![](/img/trans.png)
[英]How can I make file parsing and I/O faster in python when working with huge files (20GB+)
[英]How can I read lines from a csv file only if a variable equals to a certain value (20Gb+ csv file)
我有一個大約 25Gb 的 csv 文件。 我有 64GB 的內存。 雖然我的 ram 可以處理這個大文件,但它需要的時間太長了。 此外,我不需要數據中的每一行。 所以我想知道
我更喜歡使用Stata。 R 和 python 也不錯。
R 的data.table::fread
非常適合這個。 讓我們編寫一個示例文件:
library(data.table)
set.seed(39439)
NN = 3e8
DT = data.table(
ID1 = sample(LETTERS, NN, TRUE),
ID2 = sample(letters, NN, TRUE),
V1 = rnorm(NN)
)
DT
# ID1 ID2 V1
# 1: O h 0.1580064
# 2: K l -2.4281532
# 3: F z 1.7353759
# 4: B f -1.0911407
# 5: M w 0.7187998
# ---
# 299999996: D u -0.8221716
# 299999997: F f -2.4881300
# 299999998: W t 0.0371132
# 299999999: I h -1.2020380
# 300000000: L s -2.2284455
# smaller than your data, but still large
format(object.size(DT), 'Gb')
# [1] "6.7 Gb"
# write to test file
fwrite(DT, tmp <- tempfile())
# size on disk about the same
file.info(tmp)$size/1024^3
# [1] 6.191435
兩個選項:(1)在R中讀取然后過濾:
rm(DT)
system.time({
DT = fread(tmp)
DT = DT[ID2 == 'a']
})
# user system elapsed
# 50.390 25.662 40.004
約40秒
(2)使用awk
進行過濾,然后讀取:
rm(DT)
system.time({
DT = fread(cmd = paste('awk -F, \'$2 == "a"\'', tmp))
})
# user system elapsed
# 350.170 3.775 354.638
后者要慢得多,因為前者是並行運行的。 優點是第一種方法不節省內存——在過濾到較小的表之前,您首先占用了整個文件的所有 memory。 awk
方法僅將過濾后的文件加載到 memory 中。
(2*) 在這種情況下,您實際上也可以使用grep
,但請注意,這僅適用,因為此文件中只有一列可以包含a
:
rm(DT)
system.time({
DT = fread(cmd = paste('grep -F ",a,"', tmp))
})
# user system elapsed
# 164.587 2.500 167.165
PS要注意vroom
的“標價”——如前所述,它只索引你的數據,所以比較讀取數據的時間可能會產生誤導——你必須計算實際處理數據需要多長時間,因為觸發數據加載。 這是一個比較:
# to offset some re-reading optimizations in fread
file.copy(tmp, tmp <- tempfile())
rm(DT)
system.time({
DT = fread(tmp)
DT = DT[ID2 == 'a']
DT[ , .(mean(V1)), by = .(ID1, ID2)]
})
# user system elapsed
# 61.930 31.740 52.958
library(dplyr)
rm(DT)
system.time({
DT = vroom::vroom(tmp)
DT = DT %>% filter(ID2 == 'a')
DT %>% group_by(ID1, ID2) %>% summarize(mean(V1))
})
# user system elapsed
# 122.605 56.562 129.957
(大致相同的比較適用於跳過第三步)
讀取所有行和將整個內容實際加載到位於 memory 中的數據結構中是有區別的。
在 R 中, vroom
package 將對列進行索引,然后如果您過濾第二列中的值,它將僅讀取第二列以找出何時滿足條件,然后僅從其他列中讀取相關值。 在這里閱讀。
一般來說,像 SED 或 AWK 這樣的命令行工具將非常擅長預處理您的數據。 它們通過一次一行地流式傳輸文件來工作,因此整個文件永遠不會在 memory 中。 您可以使用它來創建一個僅包含您感興趣的行的較小文件,然后使用您選擇的程序正常使用它。
我和 Gregor Thomas 一樣傾向於使用awk
,但它實際上似乎比 Stata 的import delimited
慢。 這是一個模擬顯示:
#delimit;
version 16.1;
set more off;
clear all;
timer clear;
/* Fake CSV Data */
set seed 1234;
set obs 1000000;
gen id = _n;
gen keeper = mod(id,10);
forvalues i=1/2000 {;
gen x`i' = rnormal();
};
export delimited using "big_file.csv", replace;
!ls -lh "big_file.csv";
/* (1) import delimited */
timer on 1;
import delimited "big_file.csv", clear;
keep if keeper == 5;
timer off 1;
/* (2) awk + import Delimited */
timer on 2;
/* Grab all the data for obs where the second column equal to 5 */
!awk -F, '$2 ~ /5/' big_file.csv > smaller_file.csv;
import delimited "smaller_file.csv", clear;
timer off 2;
timer list;
!rm "big_file.csv" "smaller_file.csv";
這產生了一個 20G csv 文件,但import
需要 622.3250 秒和 awk + import
需要 1193.1510。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.