【问题标题】:Merge CSVs in Python with different columns在 Python 中将 CSV 与不同的列合并
【发布时间】:2014-12-23 07:33:27
【问题描述】:

我有数百个大型 CSV 文件,我想将它们合并为一个。但是,并非所有 CSV 文件都包含所有列。因此,我需要根据列名而不是列位置合并文件。

明确一点:在合并的 CSV 中,来自没有该单元格列的行的单元格的值应该为空。

我不能使用 pandas 模块,因为它会让我内存不足。

有没有可以做到这一点的模块,或者一些简单的代码?

【问题讨论】:

    标签: python csv merge


    【解决方案1】:

    csv.DictReadercsv.DictWriter 类应该可以正常工作(请参阅Python docs)。像这样的:

    import csv
    inputs = ["in1.csv", "in2.csv"]  # etc
    
    # First determine the field names from the top line of each input file
    # Comment 1 below
    fieldnames = []
    for filename in inputs:
      with open(filename, "r", newline="") as f_in:
        reader = csv.reader(f_in)
        headers = next(reader)
        for h in headers:
          if h not in fieldnames:
            fieldnames.append(h)
    
    # Then copy the data
    with open("out.csv", "w", newline="") as f_out:   # Comment 2 below
      writer = csv.DictWriter(f_out, fieldnames=fieldnames)
      for filename in inputs:
        with open(filename, "r", newline="") as f_in:
          reader = csv.DictReader(f_in)  # Uses the field names in this file
          for line in reader:
            # Comment 3 below
            writer.writerow(line)
    

    以上评论:

    1. 您需要提前将所有可能的字段名称指定给DictWriter,因此您需要遍历所有CSV 文件两次:一次查找所有标题,一次读取数据。没有更好的解决方案,因为在DictWriter 可以写第一行之前需要知道所有的标题。这部分使用集合而不是列表会更有效(列表上的in 运算符相对较慢),但对于几百个标题不会有太大区别。集合也会失去列表的确定性排序 - 每次运行代码时,列都会以不同的顺序出现。
    2. 以上代码适用于 Python 3,在没有 newline="" 的情况下,CSV 模块中会发生奇怪的事情。在 Python 2 中删除它。
    3. 此时,line 是一个以字段名称为键,列数据为值的字典。您可以在 DictReaderDictWriter 构造函数中指定如何处理空白或未知值。

    此方法不应耗尽内存,因为它永远不会一次加载整个文件。

    【讨论】:

    • 谢谢!这可行,但输入的 CSV 都有标题,并且它们在合并文件中与上面的代码重复。除了第一个文件之外,如何为每个文件删除这一行?
    • 实际上我的列在合并文档中没有对齐。试图找出原因。
    • out.csv 中有 223 列,但我的字段名长度是 368...?
    • 非常感谢,这行得通!我做了两个小调整:1) 用 open("out.csv", "w") 更改为 open("out.csv", "wb"),否则由于某种原因我每隔一行空白 2) 添加f_out.write(str(fieldnames)+"\n" 获取合并后的 csv 中的标题。
    • 我需要对代码进行一些补充。我添加了“writer.writeheader()”。也单独写为下面的解决方案。
    【解决方案2】:

    对于我们这些使用 2.7 的人来说,这会在“out.csv”中的记录之间添加一个额外的换行符。要解决此问题,只需将文件模式从“w”更改为“wb”即可。

    【讨论】:

    • 什么增加了额外的换行符?接受的答案?如果是这样,这应该是对已接受答案的评论,而不是单独的答案。
    【解决方案3】:

    您可以使用 pandas 模块轻松完成此操作。这个 sn-p 假设您所有的 csv 文件都在当前文件夹中。

    import pandas as pd
    import os
    
    all_csv = [file_name for file_name in os.listdir(os.getcwd()) if '.csv' in file_name]
    
    li = []
    
    for filename in all_csv:
        df = pd.read_csv(filename, index_col=None, header=0, parse_dates=True, infer_datetime_format=True)
        li.append(df)
    
    frame = pd.concat(li, axis=0, ignore_index=True)
    frame.to_csv('melted_csv.csv', index=False)
    

    【讨论】:

      【解决方案4】:

      @Aaron Lockey 的解决方案,这是公认的答案,对我来说效果很好,除了文件没有标题。输出没有标题,只有行数据。每列都没有标题(键)。所以我插入了以下内容:

      writer.writeheader()
      

      它对我来说非常好用!所以现在整个代码如下所示:

          import csv
          ``inputs = ["in1.csv", "in2.csv"]  # etc
      
          # First determine the field names from the top line of each input file
      
      `# Comment 1 below
      
          `fieldnames = []
      
      
        with open(filename, "r", newline="") as f_in:
          reader = csv.reader(f_in)
          headers = next(reader)
          for h in headers:
            if h not in fieldnames:
              fieldnames.append(h)
      
      # Then copy the data
      with open("out.csv", "w", newline="") as f_out:   # Comment 2 below
        writer = csv.DictWriter(f_out, fieldnames=fieldnames)
      writer.writeheader() #this is the addition.       
      for filename in inputs:
              with open(filename, "r", newline="") as f_in:
                reader = csv.DictReader(f_in)  # Uses the field names in this file
                for line in reader:
                  # Comment 3 below
                  writer.writerow(line)
      

      【讨论】:

      • 您的缩进已关闭,但这非常有用,谢谢!
      猜你喜欢
      • 2014-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-24
      • 2020-02-10
      • 1970-01-01
      • 2021-10-04
      • 1970-01-01
      相关资源
      最近更新 更多