【问题标题】:Extracting and aggregating data out of filenames in python or pandas从 python 或 pandas 中的文件名中提取和聚合数据
【发布时间】:2021-12-09 19:22:47
【问题描述】:

我有这四个列表,分别是图片的文件名,文件名的格式为:

(疾病)-(随机患者 ID)-(该患者的图像编号)

一个病人可以有多个图像。

请看下面的这些片段:

print(train_cnv_list[0:3])
print(train_dme_list[0:3])
print(train_drusen_list[0:3])
print(train_normal_list[0:3])
>>>
['CNV-9911627-77.jpeg', 'CNV-9935363-45.jpeg', 'CNV-9911627-94.jpeg']
['DME-8889850-2.jpeg', 'DME-8773471-3.jpeg', 'DME-8797076-11.jpeg']
['DRUSEN-8986660-50.jpeg', 'DRUSEN-9100857-3.jpeg', 'DRUSEN-9025088-5.jpeg']
['NORMAL-9490249-31.jpeg', 'NORMAL-9509694-5.jpeg', 'NORMAL-9504376-3.jpeg']

我想知道:

  1. 每位患者/每个列表有多少张图像?
  2. 四个列表中的“随机患者 ID”是否有任何重叠?如果是这样,我可以使用 groupby 之类的东西将其聚合成某种报告(患者、疾病、图像数量)吗?
patient - disease1 - total number of images

        - disease2 - total number of images

        - disease3 - total number of images

其中图像总数为最大值(此患者的图像数)

我确实看到这会产生一个患者 ID:

train_cnv_list[0][4:11]
>>> 9911627

在此先感谢您的任何指导。

【问题讨论】:

  • 恕我直言有点太宽泛了。查看split 函数以获取文件名的单独组成部分,然后使用字典进行计数。
  • 对于#2,当您说重叠时,您是在尝试检查重复项吗?
  • 这个问题是学习pandas库的好机会。这正是你想要做的。
  • @KyleDixon 是的,我想看看患者是否患有多种疾病 - 那么他们的 id 是否出现在四个列表中?我将编辑问题以添加更多详细信息。

标签: python pandas data-science


【解决方案1】:

首先创建一个定义良好的数据结构,使用计数器来回答您的第一个问题。

from typing import NamedTuple
from collections import Counter,defaultdict

class FileInfo(NamedTuple):
  disease:str
  patient_id:str
  image_id: str


l1 = ['CNV-9911627-77.jpeg', 'CNV-9935363-45.jpeg', 'CNV-9911627-94.jpeg']
l2 = ['DME-8889850-2.jpeg', 'DME-8773471-3.jpeg', 'DME-8797076-11.jpeg']
l3 = ['DRUSEN-8986660-50.jpeg', 'DRUSEN-9100857-3.jpeg', 'DRUSEN-9025088-5.jpeg']
l4 = ['NORMAL-9490249-31.jpeg', 'NORMAL-9509694-5.jpeg', 'NORMAL-9504376-3.jpeg']
lists = [l1,l2,l3,l4]
data_lists = []
for l in lists:
  data_lists.append([FileInfo(*f[:-5].split('-')) for f in l])
counters = []
for l in data_lists:
  counters.append(Counter(fi.patient_id for fi in l))
print(counters)
print('-----------')
cross_lists_data = dict()
for l in data_lists:
  for file_info in l:
    if file_info.patient_id not in cross_lists_data:
      cross_lists_data[file_info.patient_id] =  defaultdict(int)
    cross_lists_data[file_info.patient_id][file_info.disease] += 1  
print(cross_lists_data)
    

【讨论】:

    【解决方案2】:

    这里有一些功能可能会让您走上正轨,但正如 @rick-supports-monica 所提到的,这对于 pandas 来说是一个很好的用例。您可以更轻松地处理数据。

    def contains_duplicate_ids(img_list):
      patient_ids = []
      for image in img_list:
        patient_id = image.split('.')[0].split('-')[1]
        patient_ids.append(patient_id)
    
      if len(set(patient_ids)) == len(patient_ids):
        return False
      
      return True
    
    def get_duplicates(img_list):
      patient_ids = []
      duplicates = []
    
      for image in img_list:
        patient_id = image.split('.')[0].split('-')[1]
    
        if patient_id in patient_ids:
          duplicates.append(patient_id)
    
        patient_ids.append(patient_id)
    
      return duplicates
    
    def count_images(img_list):
      return len(set(img_list))
    

    get_duplicates,您可以使用返回的患者 ID 从那里查找您想要的任何内容。我不确定我是否完全理解列表的结构。它看起来像{disease}-{patient_id}-{some_other_int}.jpg。我不确定如何在不了解输入的情况下向功能添加额外的查找。

    我提到了 pandas,但没有提到如何使用它,这是一种可以将现有数据放入数据框的方法:

    import pandas as pd
    
    # Sample data
    train_cnv_list = ['CNV-9911627-77.jpeg', 'CNV-9935363-45.jpeg', 'CNV-9911628-94.jpeg', 'CNM-9911629-94.jpeg']
    train_dme_list = ['DME-8889850-2.jpeg', 'DME-8773471-3.jpeg', 'DME-8797076-11.jpeg']
    train_drusen_list = ['DRUSEN-8986660-50.jpeg', 'DRUSEN-9100857-3.jpeg', 'DRUSEN-9025088-5.jpeg']
    train_normal_list = ['NORMAL-9490249-31.jpeg', 'NORMAL-9509694-5.jpeg', 'NORMAL-9504376-3.jpeg']
    
    # Convert list to dataframe
    def dataframe_from_list(img_list):
      df = pd.DataFrame(img_list, columns=['filename'])
    
      df['disease'] = [filename.split('.')[0].split('-')[0] for filename in img_list]
      df['patient_id'] = [filename.split('.')[0].split('-')[1] for filename in img_list]
      df['some_other_int'] = [filename.split('.')[0].split('-')[2] for filename in img_list]
    
      return df
    
    # Generate a dataframe for each list
    cnv_df = dataframe_from_list(train_cnv_list)
    dme_df = dataframe_from_list(train_dme_list)
    drusen_df = dataframe_from_list(train_drusen_list)
    normal_df = dataframe_from_list(train_normal_list)
    
    # or combine them into one long dataframe
    df = pd.concat([cnv_df, dme_df, drusen_df, normal_df], ignore_index=True)
    

    【讨论】:

      【解决方案3】:

      您可以使用 Pandas 轻松完成:

      import pandas as pd
      
      cnv_list=['CNV-9911627-77.jpeg', 'CNV-9935363-45.jpeg', 'CNV-9911627-94.jpeg']
      dme_list=['DME-8889850-2.jpeg', 'DME-8773471-3.jpeg', 'DME-8797076-11.jpeg']
      dru_list=['DRUSEN-8986660-50.jpeg', 'DRUSEN-9100857-3.jpeg', 'DRUSEN-9025088-5.jpeg']
      nor_list=['NORMAL-9490249-31.jpeg', 'NORMAL-9509694-5.jpeg', 'NORMAL-9504376-3.jpeg']
      
      data =[]
      data.extend(cnv_list)
      data.extend(dme_list)
      data.extend(dru_list)
      data.extend(nor_list)
      
      df = pd.DataFrame(data, columns=["files"])
      df["files"]=df["files"].str.replace ('.jpeg','')
      df=df["files"].str.split('-', expand=True).rename(columns={0:"disease",1:"PatientID",2:"pictureName"})
      res = df.groupby(['PatientID','disease']).apply(lambda x: x['pictureName'].count())
      print(res)
      

      结果:

      PatientID  disease
      8773471    DME        1
      8797076    DME        1
      8889850    DME        1
      8986660    DRUSEN     1
      9025088    DRUSEN     1
      9100857    DRUSEN     1
      9490249    NORMAL     1
      9504376    NORMAL     1
      9509694    NORMAL     1
      9911627    CNV        2
      9935363    CNV        1
      

      甚至比你现在拥有的数据框还要多...

      【讨论】:

      • 这太棒了。当我检查带有图像文件的实际文件夹时,我注意到的是,您的最终“res”df 恰好计算了实际图像的一半(因为存在重复的确切文件名!)。我在 pandas 中查找了 .count() 方法,它看起来会计算重复文件名,所以我有点困惑为什么它只显示一半的计数......
      • 我在 'pictureName' 上做了 .astype(int),现在它可以工作了。
      【解决方案4】:

      首先连接您的数据

      import pandas as pd 
      import numpy as np 
      
      train_cnv_list = ['CNV-9911627-77.jpeg', 'CNV-9935363-45.jpeg', 'CNV-9911627-94.jpeg']
      train_dme_list = ['DME-8889850-2.jpeg', 'DME-8773471-3.jpeg', 'DME-8797076-11.jpeg']
      train_drusen_list = ['DRUSEN-8986660-50.jpeg', 'DRUSEN-9100857-3.jpeg', 'DRUSEN-9025088-5.jpeg']
      train_normal_list = ['NORMAL-9490249-31.jpeg', 'NORMAL-9509694-5.jpeg', 'NORMAL-9504376-3.jpeg']
      
      train_data = np.array([
          train_cnv_list, 
          train_dme_list,
          train_drusen_list,
          train_normal_list
      ])
      

      使用扁平数组创建一个系列

      >>> train = pd.Series(train_data.flat)
      >>> train
      
      0        CNV-9911627-77.jpeg
      1        CNV-9935363-45.jpeg
      2        CNV-9911627-94.jpeg
      3         DME-8889850-2.jpeg
      4         DME-8773471-3.jpeg
      5        DME-8797076-11.jpeg
      6     DRUSEN-8986660-50.jpeg
      7      DRUSEN-9100857-3.jpeg
      8      DRUSEN-9025088-5.jpeg
      9     NORMAL-9490249-31.jpeg
      10     NORMAL-9509694-5.jpeg
      11     NORMAL-9504376-3.jpeg
      dtype: object
      
      

      使用Series.str.extract 和正则表达式从文件名中提取信息并将其分成不同的列

      >>> pat = '(?P<Disease>\w+)-(?P<Patient_ID>\d+)-(?P<IMG_ID>\d+).jpeg'
      >>> train = train.str.extract(pat)
      >>> train
      
         Disease Patient_ID IMG_ID
      0      CNV    9911627     77
      1      CNV    9935363     45
      2      CNV    9911627     94
      3      DME    8889850      2
      4      DME    8773471      3
      5      DME    8797076     11
      6   DRUSEN    8986660     50
      7   DRUSEN    9100857      3
      8   DRUSEN    9025088      5
      9   NORMAL    9490249     31
      10  NORMAL    9509694      5
      11  NORMAL    9504376      3
      

      最后,聚合数据并根据最大IMG_ID数量计算每组的图像总数。

      >>> report = train.groupby(["Patient_ID","Disease"])['IMG_ID'].agg(Total_IMGs="max")
      >>> report
      
                         Total_IMGs
      Patient_ID Disease           
      8773471    DME              3
      8797076    DME             11
      8889850    DME              2
      8986660    DRUSEN          50
      9025088    DRUSEN           5
      9100857    DRUSEN           3
      9490249    NORMAL          31
      9504376    NORMAL           3
      9509694    NORMAL           5
      9911627    CNV             94
      9935363    CNV             45
      

      【讨论】:

        猜你喜欢
        • 2014-02-07
        • 1970-01-01
        • 2018-10-27
        • 1970-01-01
        • 2013-03-03
        • 2022-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多