【问题标题】:What is the fastest way to combine 100 CSV files with headers into one?将 100 个带有标题的 CSV 文件合并为一个的最快方法是什么?
【发布时间】:2017-10-27 22:20:59
【问题描述】:

使用以下设置将 100 个带有标题的 CSV 文件合并为一个的最快方法是什么:

  1. 文件的总大小为 200 MB。 (尺寸减小以使 计算时间可见)
  2. 文件位于最高速度为 240 MB/s 的 SSD 上。
  3. CPU 有 4 个内核,因此多线程和多进程是 允许。
  4. 只有一个节点(对 Spark 很重要)
  5. 可用内存为 15 GB。因此文件很容易放入内存中。
  6. 操作系统是 Linux (Debian Jessie)
  7. 计算机实际上是 Google Cloud 中的 n1-standard-4 实例。

(包含详细设置以使问题的范围更具体。更改是根据the feedback here进行的)

文件 1.csv:

a,b
1,2

文件 2.csv:

a,b
3,4

最终输出.csv:

a,b
1,2
3,4

根据我的基准测试,所有提议的方法中最快的是纯 python。有没有更快的方法?

基准(使用 cmets 和帖子中的方法更新):

Method                      Time
pure python                  0.298s
sed                          1.9s
awk                          2.5s
R data.table                 4.4s
R data.table with colClasses 4.4s
Spark 2                     40.2s
python pandas          1min 11.0s

工具版本:

sed 4.2.2
awk: mawk 1.3.3 Nov 1996
Python 3.6.1
Pandas 0.20.1
R 3.4.0
data.table 1.10.4
Spark 2.1.1

Jupyter 笔记本中的代码:

sed:

%%time
!head temp/in/1.csv > temp/merged_sed.csv
!sed 1d temp/in/*.csv >> temp/merged_sed.csv

Pure Python 所有二进制读写都带有“next”的未记录行为:

%%time
with open("temp/merged_pure_python2.csv","wb") as fout:
    # first file:
    with open("temp/in/1.csv", "rb") as f:
        fout.write(f.read())
    # now the rest:    
    for num in range(2,101):
        with open("temp/in/"+str(num)+".csv", "rb") as f:
            next(f) # skip the header
            fout.write(f.read())

awk:

%%time
!awk 'NR==1; FNR==1{{next}} 1' temp/in/*.csv > temp/merged_awk.csv

R 数据表:

%%time
%%R
filenames <- paste0("temp/in/",list.files(path="temp/in/",pattern="*.csv"))
files <- lapply(filenames, fread)
merged_data <- rbindlist(files, use.names=F)
fwrite(merged_data, file="temp/merged_R_fwrite.csv", row.names=FALSE)

带有 colClasses 的 R data.table:

%%time
%%R
filenames <- paste0("temp/in/",list.files(path="temp/in/",pattern="*.csv"))
files <- lapply(filenames, fread,colClasses=c(
    V1="integer",
    V2="integer",
    V3="integer",
    V4="integer",
    V5="integer",
    V6="integer",
    V7="integer",
    V8="integer",
    V9="integer",
    V10="integer"))
merged_data <- rbindlist(files, use.names=F)
fwrite(merged_data, file="temp/merged_R_fwrite.csv", row.names=FALSE)

火花(pyspark):

%%time
df = spark.read.format("csv").option("header", "true").load("temp/in/*.csv")
df.coalesce(1).write.option("header", "true").csv("temp/merged_pyspark.csv")

Python 熊猫:

%%time
import pandas as pd

interesting_files = glob.glob("temp/in/*.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("temp/merged_pandas.csv", index=False)

数据生成者:

%%R
df=data.table(replicate(10,sample(0:9,100000,rep=TRUE)))
for (i in 1:100){
    write.csv(df,paste0("temp/in/",i,".csv"), row.names=FALSE)
}

【问题讨论】:

  • 文件有多大?你使用的是 Python 2 还是 Python 3?
  • 尝试使用fout.write(f.read()) 代替循环,并尝试将文件作为二进制文件打开("wb" 用于写入,"rb" 用于读取)。那需要多长时间?
  • 我不确定它是否会与 Spark 有显着不同,但 Apache Drill(甚至在本地运行)非常擅长这种事情。
  • @MarkSetchell 根据我的基准测试,差异很大,并且不受 I/O 限制。目前 Python 的性能优于 sed 10 倍。如果你是对的,它应该是一样的。
  • @keiv.fly 和你现在的一样。如果您愿意,请随时将其发布为答案。

标签: python r csv


【解决方案1】:

根据问题中的基准,最快的方法是纯 Python,具有未记录的“next()”函数行为和二进制文件。该方法由Stefan Pochmann提出

基准测试:

基准(使用 cmets 和帖子中的方法更新):

Method                      Time
pure python                  0.298s
sed                          1.9s
awk                          2.5s
R data.table                 4.4s
R data.table with colClasses 4.4s
Spark 2                     40.2s
python pandas          1min 11.0s

工具版本:

sed 4.2.2
awk: mawk 1.3.3 Nov 1996
Python 3.6.1
Pandas 0.20.1
R 3.4.0
data.table 1.10.4
Spark 2.1.1

纯 Python 代码:

with open("temp/merged_pure_python2.csv","wb") as fout:
    # first file:
    with open("temp/in/1.csv", "rb") as f:
        fout.write(f.read())
    # now the rest:    
    for num in range(2,101):
        with open("temp/in/"+str(num)+".csv", "rb") as f:
            next(f) # skip the header
            fout.write(f.read())

【讨论】:

  • 纯python解决方案(顺便说一句非常好!)给了我以下错误:ValueError: Mixing iteration and read methods would lose data。我已通过跳过 next(f) 并仅使用 fout.write("".join(f.readlines()[1:])) 来修复它
  • @Gombo 我从来没有遇到过这个问题。你使用什么版本的 Python?一周前我在 Python 3.7 中使用了该代码,它可以正常工作。使用 readlines 应该会减慢执行速度,因为 python 需要找到 \n 并填充一个新的字符串列表。
【解决方案2】:

sed 可能是最快的。我还会提出一个awk 替代方案

awk 'NR==1; FNR==1{next} 1' file* > output

打印第一个文件的第一行,然后跳过其余文件的所有其他第一行。

时间: 我尝试了 10,000 行长的 100 个文件,每个文件大约 200MB(不确定)。这是我服务器上最糟糕的时间。

real    0m0.429s                                              
user    0m0.360s                                      
sys     0m0.068s 

服务器规格(小怪物)

$ lscpu                                                                                                         
Architecture:          x86_64                                                                                                             
CPU op-mode(s):        32-bit, 64-bit                                                                                                     
Byte Order:            Little Endian                                                                                                      
CPU(s):                12                                                                                                                 
On-line CPU(s) list:   0-11                                                                                                               
Thread(s) per core:    1                                                                                                                  
Core(s) per socket:    6                                                                                                                  
Socket(s):             2                                                                                                                  
NUMA node(s):          1                                                                                                                  
Vendor ID:             GenuineIntel                                                                                                       
CPU family:            6                                                                                                                  
Model:                 63                                                                                                                 
Model name:            Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz                                                                          
Stepping:              2                                                                                                                  
CPU MHz:               2394.345                                                                                                           
BogoMIPS:              4789.86                                                                                                            
Virtualization:        VT-x                                                                                                               
L1d cache:             32K                                                                                                                
L1i cache:             32K                                                                                                                
L2 cache:              256K                                                                                                               
L3 cache:              15360K                                                                                                             
NUMA node0 CPU(s):     0-11     

【讨论】:

  • sed 在他们的测试中甚至不是最快的。您是在谈论使用 sed 的不同方式吗?
  • 不,抱歉,没看到时间。令人惊喜的是 Python 更快。我的猜测是awk 会打败它。
  • 希望他们将您的解决方案纳入基准测试。但是 Python 应该比现在更快,因为目前有解码和编码,据我所知没有任何原因,而不仅仅是原始的读写。
  • 你的服务器可以在不到0.5秒的时间内读写和处理20GB?那是什么怪物?
  • 看起来不像个怪物。我不认为您的文件每个都接近 200MB。或者有其他问题。顺便说一句,OPs 文件是 200MB total(我刚刚运行了他们的创建代码,每个文件是 2MB,100000 行,每个 20 字节)。
猜你喜欢
  • 2010-11-02
  • 2013-08-11
  • 2015-10-29
  • 2016-08-11
  • 2020-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-16
相关资源
最近更新 更多