簡體   English   中英

"在讀取多個 CSV 文件時,base R 比 readr 更快"

[英]base R faster than readr for reading multiple CSV files

有很多關於如何讀取多個 CSV 並將它們綁定到一個數據框中的文檔。 我有 5000 多個 CSV 文件需要讀取並綁定到一個數據結構中。

特別是我在這里進行了討論: Issue in Loading multiple .csv files into single dataframe in R using rbind

奇怪的是,base R 比我嘗試過的任何其他解決方案都要快得多。

這是我的 CSV 的樣子:

> head(PT)
  Line          Timestamp       Lane.01 Lane.02 Lane.03 Lane.04 Lane.05 Lane.06 Lane.07 Lane.08
1    PL1    05-Jan-16 07:17:36      NA      NA      NA      NA      NA      NA      NA      NA
2    PL1    05-Jan-16 07:22:38      NA      NA      NA      NA      NA      NA      NA      NA
3    PL1    05-Jan-16 07:27:41      NA      NA      NA      NA      NA      NA      NA      NA
4    PL1    05-Jan-16 07:32:43    9.98   10.36   10.41   10.16   10.10    9.97   10.07    9.59
5    PL1    05-Jan-16 07:37:45    9.65    8.87    9.88    9.86    8.85    8.75    9.19    8.51
6    PL1    05-Jan-16 07:42:47    9.14    8.98    9.29    9.04    9.01    9.06    9.12    9.08

我創建了三種讀取和綁定數據的方法。 這些文件位於一個單獨的目錄中,我將其定義為:

dataPath <- "data"
PTfiles <- list.files(path=dataPath, full.names = TRUE)

方法 1:基礎 R

classes <- c("factor", "character", rep("numeric",8))

# build function to load data
load_data <- function(dataPath, classes) { 
   tables <- lapply(PTfiles, read.csv, colClasses=classes, na.strings=c("NA", ""))
   do.call(rbind, tables)
}

#clock
method1 <- system.time(
   PT <- load_data(path, classes)
)

方法 2: read_csv在這種情況下,我創建了一個包裝函數供 read_csv 使用

#create wrapper function for read_csv
read_csv.wrap <- function(x) { read_csv(x, skip = 1, na=c("NA", ""),
                      col_names = c("tool", "timestamp", paste("lane", 1:8, sep="")),
                      col_types = 
                         cols(
                            tool = col_character(),
                            timestamp = col_character(),
                            lane1 = col_double(),
                            lane2 = col_double(),
                            lane3 = col_double(),
                            lane4 = col_double(),
                            lane5 = col_double(),
                            lane6 = col_double(),
                            lane7 = col_double(),
                            lane8 = col_double()
                           )
                     )
}

##
# Same as method 1, just uses read_csv instead of read.csv

load_data2 <- function(dataPath) { 
   tables <- lapply(PTfiles, read_csv.wrap)
   do.call(rbind, tables)
}

#clock
method2 <- system.time(
   PT2 <- load_data2(path)
)

方法3: read_csv + dplyr::bind_rows

load_data3 <- function(dataPath) { 
   tables <- lapply(PTfiles, read_csv.wrap)
   dplyr::bind_rows(tables)
}

#clock
method3 <- system.time(
   PT3 <- load_data3(path)
)

我想不通的是,為什么 read_csv 和 dplyr 方法在它們應該更快的情況下會更 CPU時間減少了,但是為什么經過的時間(文件系統)會增加? 這里發生了什么?

編輯- 我按照評論中的建議添加了data.table方法

方法4 data.table

library(data.table)

load_data4 <- function(dataPath){
   tables <- lapply(PTfiles, fread)
   rbindlist(tables)
}

method4 <- system.time(
   PT4 <- load_data4(path)
)

從 CPU 的角度來看, data.table方法是最快的。 但是問題仍然在於使它們變得如此緩慢的read_csv方法發生了什么。

> rbind(method1, method2, method3, method4)
        user.self sys.self elapsed
method1      0.56     0.39    1.35
method2      0.42     1.98   13.96
method3      0.36     2.25   14.69
method4      0.34     0.67    1.74

我會在終端(Unix)中這樣做。 我會將所有文件放在同一個文件夾中,然后導航到該文件夾​​(在終端中),使用以下命令僅創建一個 CSV 文件:

cat *.csv > merged_csv_file.csv

關於此方法的一個觀察結果是每個文件的標題將顯示在觀察結果的中間。 為了解決這個問題,我建議你這樣做:

只從第一個文件中獲取標題

head -2 file1.csv > merged_csv_file.csv

然后使用以下命令跳過其他文件中的前“X”行,其中“X”是要跳過的行數。

tail -n +3 -q file*.csv >> merged_csv_file.csv

-n +3使尾部打印行從 3rd 到末尾, -q告訴它不打印帶有文件名的標題(read man), >>添加到文件中,而不是將其覆蓋為 >。

我可能發現了一個相關的問題。 我正在從一些模擬輸出中讀取嵌套的 CSV 數據,其中多個列具有 CSV 格式的數據作為元素,我需要對其進行取消嵌套和重塑以進行分析。

在我進行多次運行的模擬中,這導致需要解析數千個元素。 使用 map(.,read_csv) 這將需要幾個小時來轉換。 當我重寫腳本以在 lambda 函數中應用 read.csv 時,操作將在seconds 內完成。

我很好奇是否有一些中間系統 I/O 操作或錯誤處理會造成單個輸入文件不會遇到的瓶頸。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM