【问题标题】:Merging multiple CSV files without headers being repeated (using Python)合并多个 CSV 文件而不重复标题(使用 Python)
【发布时间】:2015-07-31 20:42:05
【问题描述】:

我是 Python 的初学者。我有多个 CSV 文件(超过 10 个),并且它们都有相同数量的列。我想将它们全部合并到一个 CSV 文件中,我不会在其中重复标题。

所以基本上我只需要包含所有标题的第一行,然后我需要合并所有 CSV 文件中的所有行。我该怎么做?

这是我目前尝试过的。

import glob
import csv



with open('output.csv','wb') as fout:
    wout = csv.writer(fout,delimiter=',') 
    interesting_files = glob.glob("*.csv") 
    for filename in interesting_files: 
        print 'Processing',filename 
    # Open and process file
        h = True
        with open(filename,'rb') as fin:
                fin.next()#skip header
        for line in csv.reader(fin,delimiter=','):
                wout.writerow(line)

【问题讨论】:

  • 我已经更新了到目前为止我尝试过的内容。 @MarcB
  • 那么问题出在哪里?看起来它应该可以工作,除了不保留第一个文件的标题行。
  • csv.reader(fin,delimiter=',') 中 for 行的缩进:看起来不正确。
  • 代替最后一个 for 循环,您可以使用 line-liner wout.writerows(csv.reader(fin,delimiter=',')) 复制每个 fin 文件的其余部分。

标签: python csv


【解决方案1】:

如果您使用的是 linux 系统:

head -1 director/one_file.csv > output csv   ## writing the header to the final file
tail -n +2  director/*.csv >> output.csv  ## writing the content of all csv starting with second line into final file

【讨论】:

  • 我需要在 tail 命令中添加 '-q' (--quiet) 以避免结果文件中包含额外的文本。
  • 根据@ElMesa,我必须在 Windows 上使用“tail -q -n +2 *.csv”
  • @jean-françois-fabre 您可以调整 head 和 tail 的参数以使用/跳过 x 行
  • 是的,但我的意思是:python csv 模块/熊猫能够正确检测到一行。无需知道有多少行。我管理如果你想用一个已知的标题来做,你可以做到(即使它不是一个通用的解决方案)
【解决方案2】:

虽然我认为最好的答案是来自 @valentin 的答案,但您完全可以不使用 csv 模块来做到这一点:

import glob

interesting_files = glob.glob("*.csv") 

header_saved = False
with open('output.csv','wb') as fout:
    for filename in interesting_files:
        with open(filename) as fin:
            header = next(fin)
            if not header_saved:
                fout.write(header)
                header_saved = True
            for line in fin:
                fout.write(line)

【讨论】:

  • @m.wasowski 这个方法有效,但它所做的只是合并文件。甚至标题也被合并了。我有 10 个 CSV 文件,它们都有相同的标题。所以基本上我用这段代码在输出文件中获得了 10 次标题行。有什么修改建议吗?
  • 我的错,我将阅读标题移动到 if 块中,这是不必要的。见固定版本; @PadraicCunningham 其他想法的剩余部分,已修复,谢谢。
  • 如果 csv 文件的标题行多于一行则不起作用
【解决方案3】:

如果您不介意开销,您可以使用普通 python 发行版附带的 pandas。如果您打算使用电子表格做更多事情,我建议您使用 pandas 而不是尝试编写自己的库。

import pandas as pd
import glob
interesting_files = glob.glob("*.csv")
df_list = []
for filename in sorted(interesting_files):
    df_list.append(pd.read_csv(filename))
full_df = pd.concat(df_list)

full_df.to_csv('output.csv')

关于熊猫的更多信息。因为它是用来处理类似数据的电子表格的,所以它知道第一行是标题。读取 CSV 时,它将数据表与标题分开,该标题作为 dataframe 的元数据保存,这是 pandas 中的标准数据类型。如果您连接其中的几个dataframes,它只会连接数据部分(如果它们的标题相同)。如果标题不同,它会失败并给你一个错误。如果您的目录被其他来源的 CSV 文件污染,这可能是一件好事。

另一件事:我刚刚在interesting_files 周围添加了sorted()。我假设你的文件是按顺序命名的,应该保持这个顺序。我不确定 glob,但 os 函数不一定返回按名称排序的文件。

【讨论】:

  • 我试过这个,但我收到以下错误:导入错误:没有名为 Pandas 的模块。
  • 似乎没有安装熊猫。在终端pip install pandas 中尝试。如果您使用的是简单的 python 安装,您可能会受益于安装 python 发行版,例如 anaconda (continuum.io/downloads) 以安装最常见的软件包。
  • 非常感谢!我会尝试熊猫。看起来它应该让我的工作更轻松。
  • 在这里小心,我刚刚调试了一个脚本一个小时,因为 glob 决定将我的字符串格式化为整数,并从我的一堆字符串中删除了一个零填充。
  • 你的意思是你从sorted得到的文件名顺序错误?
【解决方案4】:

您的尝试几乎成功了,但问题是:

  • 您正在打开文件进行读取,但在写入行之前将其关闭。
  • 你永远不会写标题。你必须写一次
  • 您还必须从“glob”中排除 output.csv,否则输出也在输入中!

这是更正后的代码,将 csv 对象直接传递给 csv.writerows 方法以获得更短、更快的代码。还将第一个文件的标题写入输出文件。

import glob
import csv

output_file = 'output.csv'
header_written = False

with open(output_file,'w',newline="") as fout:  # just "wb" in python 2
    wout = csv.writer(fout,delimiter=',')
    # filter out output
    interesting_files = [x for x in glob.glob("*.csv") if x != output_file]
    for filename in interesting_files:
        print('Processing {}'.format(filename))
        with open(filename) as fin:
            cr = csv.reader(fin,delmiter=",")
            header = cr.next() #skip header
            if not header_written:
                wout.writerow(header)
                header_written = True
            wout.writerows(cr)

请注意,使用原始逐行处理的解决方案错过了一个重要的点:如果标题是多行的,它们会惨遭失败,破坏标题行/重复标题行的一部分,从而有效地破坏文件。

csv 模块(或 pandas,也是)优雅地处理这些情况。

【讨论】:

    【解决方案5】:

    您的缩进错误,您需要将循环放在 with 块内。您还可以将文件对象传递给 writer.writerows。

    import csv
    with open('output.csv','wb') as fout:
        wout = csv.writer(fout)
        interesting_files = glob.glob("*.csv")
        for filename in interesting_files:
            print 'Processing',filename
            with open(filename,'rb') as fin:
                    next(fin) # skip header
                    wout.writerows(fin)
    

    【讨论】:

    • 这是不正确的,因为您不是将输入读取为 csv,而是读取为原始行
    猜你喜欢
    • 2017-12-26
    • 2020-09-23
    • 2021-05-31
    • 1970-01-01
    • 1970-01-01
    • 2020-02-05
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    相关资源
    最近更新 更多