【问题标题】:How to split a csv into multiple csv files using a list of keywords如何使用关键字列表将 csv 拆分为多个 csv 文件
【发布时间】:2021-03-12 23:57:36
【问题描述】:

我正在尝试从多台机器上读取性能报告,并希望解析它们并将它们组合起来,以便轻松比较单个地块上的机器性能。一旦分成多个 csv,我计划使用 pd.read_csv() 读取它们并将多个工具组合成单个 df。

但为了做到这一点,我必须首先处理并拆分带有分号分隔符的相当丑陋的 csv 文件。
CSV的结构是这样的:

KEYWORD_01;;;...;;
COL_01;COL_02;COL03;...;COL_n;
第 1 行;
Line_2;
第 3 行;
...
Line_m;
KEYWORD_02;;;...;;
COL_01;COL_02;COL03;...;COL_x;
第 1 行;
Line_2;
第 3 行;
...
Line_y;
KEYWORD_03;;;...;;
COL_01;COL_02;COL03;...;COL_f;
第 1 行;
Line_2;
第 3 行;
...
线_g;

Data csv file available here

csv 报告由多个部分组成,每个部分都以一个固定的关键字(或关键词)开头,每个部分都有固定的列数(可能因部分而异)和动态的行数,具体取决于报告的事件(上面的 CFR 结构)。

  1. 我创建了一个包含所有关键字的列表,称为 tpm_sections

    tpm_sections = ['Summary of time consumption',
        'Equipment Indicators',
        'Batch Profile',
        'Jam Profile',
        'Jam Time Profile',
        'Jam Table',
        'Handler Model profile',
        'Miscellaneous Indicators ',
        'Tape Job Profile ']
    tpm_idx = [None]*len(tpm_sections)
    
  2. 我读取了我的 csv 并使用正则表达式将我的 tpm_sections 列表中的任何元素与我的 csv 文件的行匹配,并且我使用函数 enumerate 以便我可以将行索引返回到单独的列表 tpm_idx 中:

for file in os.listdir(input_folder):
   input_file=os.path.join(input_folder, file)
   if file.endswith('.csv'):
     tpm_date=datetime.fromtimestamp(os.path.getctime(input_file)).strftime('%Y%m%d') # get TPM report date from file creation timestamp
        with open(input_file, "r") as f: 
          
            reader = csv.reader(f, delimiter=";")
            #for line in reader:
            
            for i, row in enumerate(reader):
                if r'Machine' in row:
                    mcpat = re.compile(r'\\\\7icost\d\d')
                    mcline = str(row[1])
                    mcname = mcpat.match(mcline).group(0)[2:]
                    mcid = mcname[6:]
                    print('Report date is: ' + tpm_date + "\nMachine Name: " + mcname + '\nMachine ID: ' + mcid)
                for j in range(len(tpm_sections)):
                    if tpm_sections[j] in row:
                        tpm_idx[j] = i
                        print('Section '+tpm_sections[j]+' starts at line: ' + str(tpm_idx[j]) )
            tpm_dict = {tpm_idx_names[i]: tpm_idx[i] for i in range(len(tpm_idx))}
  1. 我现在有一个关键字列表、一个匹配行索引列表和一个链接两者的字典,我应该如何继续拆分 csv 文件?我的代码为我的阅读器对象的每个部分编写 csv 文件以供将来导入熊猫,可选]为每个部分创建子文件夹以获得更多结构

    for j in range(len(tpm_idx_names))
    output_file = tpm_date + mcname + tpm_idx_name[j]
    with open(output_file, 'w', newline='') as o:
        if j+1 < len(tpm_idx):
            #for row_idx in range(tpm_idx[j]:tpm_idx[j+1]):
            for line in reader[tpm_idx[j]:tpm_idx[j+1]]:
                o.write(''.join())
        else:
            for line in reader[tpm_idx[j]:]:
                o.write(''.join())
    
  2. 有没有更简单的方法通过将关键字列表传递给 split() 函数来做到这一点?那太棒了,但我找不到任何可能的例子。或者通过更好地使用正则表达式,然后使用while“行不为空”循环?请记住,我的 csv 中的空行是由 ;;;;;

  3. 我应该改为使用 ls.append() 还是 numpy 数组创建列表列表 (LoL)?对于匹配的 tpm_section[j] 关键字之间的每一行?然后我可以轻松地为我的机器名称和 ID 添加列。我可以选择创建一个附加所有 20 台机器的 LoL/数组,或者为每台机器创建一个,然后在 pandas 中或在编写我的 csv 之前附加它们。在第 2 部分中添加的代码示例:

elif tpm_sections[j] in row: TPM_LoL.j.append(row)

【问题讨论】:

  • 请发布具有预期输出的可测试样本数据,而不是这样 ...
  • 您好,感谢您的建议,我在这个论坛上发帖和一般的编码方面都很陌生,所以我并不总是知道最好的方法是什么。话虽这么说,我想保持主题的通用性,以便其他人可以从中受益,csv 文件的结构非常丑陋,我试图将其精简为一个简单的示例,但即使这样也占用了大量空间。 . 可以附上链接吗?
  • 附上一个链接是可以的,但提供一个说明问题的数据样本仍然是一个好主意 - 这样,人们就不会浪费时间回答一个意义不大的问题如果示例不再可用(无论您打算保持它可用)
  • 不清楚您对源 .csv 的哪一部分感兴趣?您在问题中的描述表明格式相当规则,但数据中有许多行可能需要忽略,并且各个部分的格式似乎并不相同。您已经通过关键字说出了您想要哪些部分,但是您需要这些部分的哪些部分最终出现在输出中?
  • @Grismar 感谢您的清理工作。是的,格式很丑,我还附上了可以帮助可视化的 html 版本,但主要是报告中的部分由第 1 点中我的 tpm_sections 列表中的关键字分隔。在部分名称之后,与表格列和然后是可变数量的数据行。报告的每个部分都有自己的一组列,这使情况变得更糟。如果我们缩小:我想将 csv 分成由多个关键字分隔的部分,一个部分的结尾是另一个部分的开始。以后我会清理的!谢谢!

标签: python regex csv


【解决方案1】:

我认为你把问题分解成太多小问题会让事情变得更难。从原始 html 中提取数据(也是一种结构化的数据格式)并且只提取您需要的数据,这可能是最简单的。

但是,如果您正在寻找以下方法:

  • 将现有文本文件拆分为多个文本文件
  • 在关键字行之前拆分
  • 只为选定的关键字写入输出

假设文本文件是一个分号分隔的文件,其中第一列中只有一个词的任何行都是关键字行,那么这应该有效:

tpm_sections = [
    'Summary of time consumption',
    'Equipment Indicators',
    'Batch Profile',
    'Jam Profile',
    'Jam Time Profile',
    'Jam Table',
    'Handler Model profile',
    'Miscellaneous Indicators ',
    'Tape Job Profile '
]
out_f = None
with open('ICOST_19_TPM_20201124.csv') as f:
    for line in f:
        parts = line.strip().split(';')
        if parts[1] and (parts[1:].count('') == len(parts) - 1):
            # new keyword line, close previous file if any
            if out_f is not None:
                out_f.close()
            if line[1] in tpm_sections:
                # naming the new file after the section
                out_f = open(f'{line[1]}.csv', 'w')
            else:
                out_f = None
        # for any line, if an output file is open at this point, write to it
        if out_f is not None:
            out_f.write(line)
    else:
        if out_f is not None:
            out_f.close()

如果您不想将第一列中只有一个值的每一行识别为关键字行,但只希望具有可识别关键字的行导致拆分(并将其后的所有内容包含在该文件中),你可以简单地改变这个:

        if parts[1] and (parts[1:].count('') == len(parts) - 1):
            # new keyword line, close previous file if any
            if out_f is not None:
                out_f.close()
            if line[1] in tpm_sections:
                # naming the new file after the section
                out_f = open(f'{line[1]}.csv', 'w')
            else:
                out_f = None

收件人:

        if (parts[1] and (parts[1:].count('') == len(parts) - 1) and 
            (line[1] in tpm_sections)):
            # new keyword line, close previous file if any
            if out_f is not None:
                out_f.close()
            out_f = open(f'{line[1]}.csv', 'w')

但从问题或数据中并不完全清楚它应该是什么。两者都像宣传的那样。

【讨论】:

  • 谢谢,是的,我确实认为我认为这个问题比它应该的更复杂。这很有帮助,谢谢,我会反馈我最终使用了这两种方法中的哪一种!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多