【发布时间】:2017-10-27 22:20:59
【问题描述】:
使用以下设置将 100 个带有标题的 CSV 文件合并为一个的最快方法是什么:
- 文件的总大小为 200 MB。 (尺寸减小以使 计算时间可见)
- 文件位于最高速度为 240 MB/s 的 SSD 上。
- CPU 有 4 个内核,因此多线程和多进程是 允许。
- 只有一个节点(对 Spark 很重要)
- 可用内存为 15 GB。因此文件很容易放入内存中。
- 操作系统是 Linux (Debian Jessie)
- 计算机实际上是 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 和你现在的一样。如果您愿意,请随时将其发布为答案。