堆叠 CSV 文件的最快方法

Posted

技术标签:

【中文标题】堆叠 CSV 文件的最快方法【英文标题】:Fastest way to stack CSV files 【发布时间】:2017-07-08 20:13:47 【问题描述】:

我有 10 个具有相同列和数据类型的 csv 文件。堆叠它们最快/最有效的方法是什么?

CSV1:

col1 | col2 | col3
  1  |  'a' |  0.1
  2  |  'b' |  0.8

CSV2:

col1 | col2 | col3
  3  |  'c' |  0.4
  4  |  'd' |  0.3

我可以用Pandas 读入它们并重复df.append 但这似乎很慢,因为我必须将所有内容读入内存,如果文件非常大,可能需要很长时间。想知道我是否可以使用 bash 命令或其他 Python 包更快地做到这一点。

我不希望使用具有任何严重依赖关系或需要编译的东西。

附:如果解决方案还可以自动处理存在于一个数据集中而不是另一个数据集中的列,则可以加分。

【问题讨论】:

你想创建 1 个包含所有数据的大 csv 文件吗? 这里有类似的东西(不使用熊猫):***.com/questions/41982238/… 【参考方案1】:

正如@zwer 在comment to another answer 中正确指出的那样,如果输入 CSV 错过了最后一行的换行符,此解决方案将无法正常工作。


使用bashsed 的解决方案(假设所有文件都具有相同的列/分隔符并且都包含标题行):

concat_csv_files

#!/usr/bin/env bash

head -n1 "$1"
for f do
    sed -e 1d "$f" # or: tail -n+2 "$f"
done

例子:

concat_csv_files csv* > stacked.csv

【讨论】:

【参考方案2】:

使用headtail 的解决方案

head -n1 a.log > output.log
for f in a.log b.log; do tail -n+2 $f; done >> output.log

如果您的输入文件末尾可能没有换行符,您必须按照@zwar 的说明手动添加它。这个问题的许多解决方案都给出了in this thread。我最喜欢在这种情况下工作的是

head -n1 a.log > output.log
for f in a.log b.log
do
  tail -n+2 $f
  [ -n "$(tail -c1 $f)" ] && echo ""
done >> output.log

【讨论】:

小心这个(就像 head/sed 一样) - 如果您的任何 CSV 文件(最后一个除外)在最后一行没有换行符(对于CSV),这会弄乱输出。 @zwer 感谢您的提示!我为该问题添加了解决方案 tail -c1 "$f" 总是会输出一个字节(除非文件完全为空),无论该字节是否(部分)行尾序列。 @chepner 是的,确实如此,但测试会以不同的方式处理这些结果 @Chris 您不应该将标头通过管道传输到 csv2.csv,而是将 out_csv.csv 传输到【参考方案3】:

纯 Python 解决方案:

csv_in = ["csv1.csv", "csv2.csv"]  # paths of CSVs to 'concentrate'
csv_out = "output.csv"

skip_header = False
with open(csv_out, "w") as dest:
    for csv in csv_in:
        with open(csv, "r") as src:
            if skip_header:  # skip the CSV header in consequent files
                next(src)
            for line in src:
                dest.write(line)
                if line[-1] != "\n":  # if not present, write a new line after each row
                    dest.write("\n")
            skip_header = True  # make sure only the first CSV header is included

要合并具有不同列数的数据,您必须至少部分解析 CSV。

【讨论】:

【参考方案4】:

如果你想做一个 python 解决方案

import csv

my_files = ['file_one.csv', 'file_two.csv']
final_file = []
for fi in files:
     with open(fi, r) as f:
          reader = csv.reader(f, delimiter='|')
          for row in reader:
               final_file.append(row)

#write out final file
with open('final_file.csv', 'w') as out:
    for line in final_file:
         out.write('|'.join(line))
         out.write('\n')

【讨论】:

【参考方案5】:

这是另一个纯 Python 解决方案。这个想法是使用 glob 来构建要处理的文件列表,然后将它们单独导入到单独的 pandas 数据帧中(并将每个数据帧添加到列表中)。然后将数据帧列表连接成一个。您只想这样做一次,而不是使用重复的 df.append 调用(它太慢了)。我发现为每一列指定数据类型有助于加快速度。

import os
import glob
import numpy as np
import pandas as pd

def process_csv_file(f):

    print("Processing file ".format(f))

    # check if it's an empty file (have to be able to append an empty dataframe)
    # specifying the datatypes speeds up the process because pandas doesn't have to guess.
    if os.stat(f).st_size > 0:
        df = pd.read_csv(f, sep = ',', dtype = 'col1' : str, 'col2' : float, memory_map=True)
    else:
        df = pd.DataFrame()

    return(df)

csv_files = glob.glob(indir +'/**/' + '*.csv', recursive = True)
print ("Found  files to parse.".format(len(csv_files)))
frames = [process_csv_file(f) for f in csv_files]

csv_df = pd.concat(frames)

【讨论】:

如果整个数据不适合内存,而其他数据流式传输数据,则此问题是一个问题。我没有意识到迭代追加比 concat 慢! 非常正确。当我第一次写这篇文章时,我的目标是构建一个包含所有内容的数据框,这样我就可以做进一步的 pandas 操作。仅与您的请求无关。

以上是关于堆叠 CSV 文件的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

读取 .csv 文件时在 Python 中解析日期的最快方法是啥?

将大型 CSV 文件加载到核心数据中的最快方法是啥

在 MATLAB 中导入 CSV 文件的最快方法

将数据加载到 Cassandra 列族的最快方法是啥

在文本文件 Java 中写入大量数据的最快方法

从多个文件中读取大数据并在python中聚合数据的最快方法是啥?