Каков самый быстрый способ объединить 100 CSV файлов с заголовками в один?

Каков самый быстрый способ объединить 100 CSV файлов с заголовками в один со следующей настройкой:

  • Общий размер файлов - 200 МБ. (Размер уменьшен, чтобы сделать время вычисления видимо)
  • Файлы расположены на SSD с максимальной скоростью 240 МБ/с.
  • Процессор имеет 4 ядра, поэтому многопоточность и несколько процессов допускается.
  • Существует только один node (важный для Spark)
  • Доступная память - 15 ГБ. Таким образом, файлы легко вписываются в память.
  • ОС - Linux (Debian Jessie)
  • Компьютер фактически является экземпляром n1-standard-4 в облаке Google.

(Детальная настройка была включена, чтобы уточнить сферу охвата вопроса. Изменения были сделаны в соответствии с отзывами здесь)

Файл 1.csv:

a,b
1,2

Файл 2.csv:

a,b
3,4

Final out.csv:

a,b
1,2
3,4

Согласно моим критериям, самым быстрым из всех предложенных методов является чистый питон. Есть ли более быстрый метод?

Бенчмарки (Обновлены с помощью методов комментариев и сообщений):

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:

СЭД:

%%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 data.table:

%%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)

R data.table с colClasses:

%%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 pandas:

%%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)
}

Ответ 1

sed, вероятно, самый быстрый. Я также предложил бы альтернативу awk

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

выводит первую строку из первого файла, затем пропускает все остальные первые строки из остальных файлов.

Тайминги: Я пробовал 10000 строк длиной по 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