it-swarm.dev

한 번에 여러 .csv 파일을 가져 오는 방법은 무엇입니까?

여러 개의 data.csv 파일을 포함하는 폴더가 있다고 가정합니다. 각 파일은 같은 수의 변수를 포함하지만 각기 다른 시간의 변수를 포함합니다. R에서 그들을 개별적으로 모두 가져 오지 않고 동시에 가져올 수있는 방법이 있습니까?

내 문제는 약 2000 개의 데이터 파일을 가져 와서 코드를 사용하여 개별적으로 가져올 필요가 있다는 것입니다.

read.delim(file="filename", header=TRUE, sep="\t")

매우 효율적이지 않습니다.

169
Jojo Ono

다음과 같은 것이 각 데이터 프레임을 단일 목록의 개별 요소로 가져와야합니다.

temp = list.files(pattern="*.csv")
myfiles = lapply(temp, read.delim)

여기서는 해당 CSV가 단일 디렉토리 (현재 작업 디렉토리)에 있고 그 모두에 소문자 확장명 .csv가 있다고 가정합니다.

그런 다음 해당 데이터 프레임을 단일 데이터 프레임으로 결합하려는 경우 do.call(rbind,...), dplyr::bind_rows() 또는 data.table::rbindlist()과 같은 것을 사용하여 다른 솔루션의 솔루션을 참조하십시오.

별도의 객체에 각 데이터 프레임을 실제로 넣기를 원한다면, 이것은 종종 바람직하지 않지만 assign을 사용하여 다음을 수행 할 수 있습니다.

temp = list.files(pattern="*.csv")
for (i in 1:length(temp)) assign(temp[i], read.csv(temp[i]))

또는 assign을 사용하지 않고 (1) 파일 이름을 정리하는 방법과 (2) list2env 사용 방법을 보여주기 위해 다음을 시도 할 수 있습니다.

temp = list.files(pattern="*.csv")
list2env(
  lapply(setNames(temp, make.names(gsub("*.csv$", "", temp))), 
         read.csv), envir = .GlobalEnv)

그러나 다시 한번, 그것들을 하나의리스트에 남겨 두는 것이 더 나은 경우가 있습니다.

209

.csv 파일을 하나의 data.frame으로 변환하는 또 다른 옵션은 다음과 같습니다. R 기본 함수 사용. 이것은 아래의 옵션보다 느린 순서입니다.

# Get the files names
files = list.files(pattern="*.csv")
# First apply read.csv, then rbind
myfiles = do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))

편집 : - data.tablereadr을 사용하여 몇 가지 추가 선택 사항

fread() 버전은 data.table 패키지의 기능입니다. 가장 빠른 옵션입니다.

library(data.table)
DT = do.call(rbind, lapply(files, fread))
# The same using `rbindlist`
DT = rbindlist(lapply(files, fread))

readr 를 사용하면 csv 파일을 읽을 수있는 새로운 hadley 패키지입니다. fread보다 조금 느리지 만 기능이 다릅니다.

library(readr)
library(dplyr)
tbl = lapply(files, read_csv) %>% bind_rows()
87
marbel

신속하고 간결한 tidyverse 솔루션 : (2 배 이상 Base R read.csv)

tbl <-
    list.files(pattern = "*.csv") %>% 
    map_df(~read_csv(.))

data.table fread() 은로드 시간을 반으로 줄일 수 있습니다. (1/4 베이스 R 회)

library(data.table)

tbl_fread <- 
    list.files(pattern = "*.csv") %>% 
    map_df(~fread(., stringsAsFactors = FALSE))

stringsAsFactors = FALSE 인수는 데이터 프레임 요소를 자유롭게 유지합니다.

Typecasting 건방진 경우, col_types 인수를 사용하여 모든 열을 문자로 강제 설정할 수 있습니다.

tbl <-
    list.files(pattern = "*.csv") %>% 
    map_df(~read_csv(., col_types = cols(.default = "c")))

최종적으로 바인드 할 파일 목록을 구성하기 위해 서브 디렉토리로 들어가기를 원할 경우, 경로 이름을 포함시키고 파일을 전체 이름으로리스트에 등록하십시오. 이렇게하면 바인딩 작업을 현재 디렉토리 외부로 이동할 수 있습니다. (전체 경로 이름을 여권과 같은 것으로 생각하여 디렉토리 '테두리'를 가로 질러 이동하도록 허용합니다.)

tbl <-
    list.files(path = "./subdirectory/",
               pattern = "*.csv", 
               full.names = T) %>% 
    map_df(~read_csv(., col_types = cols(.default = "c"))) 

해들리가 말한 것처럼 여기 (약 절반 정도) :

map_df(x, f)은 사실상 do.call("rbind", lapply(x, f)) ...과 동일합니다.

보너스 기능 - 아래 주석에서 Niks 기능 요청 당 레코드에 파일 이름을 추가하십시오.
* 각 레코드에 원래 filename을 추가하십시오.

코드 설명 : 테이블을 처음 읽는 동안 각 레코드에 파일 이름을 추가하는 함수를 만듭니다. 그런 다음 간단한 read_csv() 함수 대신 해당 함수를 사용하십시오.

read_plus <- function(flnm) {
    read_csv(flnm) %>% 
        mutate(filename = flnm)
}

tbl_with_sources <-
    list.files(pattern = "*.csv", 
               full.names = T) %>% 
    map_df(~read_plus(.))

(typecasting 및 하위 디렉토리 처리 방법은 위에서 제안한 두 번째 및 세 번째 변형에서 설명한 것과 같은 방식으로 read_plus() 함수 내에서 처리 될 수 있습니다.)

### Benchmark Code & Results 
library(tidyverse)
library(data.table)
library(microbenchmark)

### Base R Approaches
#### Instead of a dataframe, this approach creates a list of lists
#### removed from analysis as this alone doubled analysis time reqd
# lapply_read.delim <- function(path, pattern = "*.csv") {
#     temp = list.files(path, pattern, full.names = TRUE)
#     myfiles = lapply(temp, read.delim)
# }

#### `read.csv()`
do.call_rbind_read.csv <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))
}

map_df_read.csv <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~read.csv(., stringsAsFactors = FALSE))
}


### *dplyr()*
#### `read_csv()`
lapply_read_csv_bind_rows <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    lapply(files, read_csv) %>% bind_rows()
}

map_df_read_csv <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~read_csv(., col_types = cols(.default = "c")))
}

### *data.table* / *purrr* hybrid
map_df_fread <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~fread(., stringsAsFactors = FALSE))
}

### *data.table*
rbindlist_fread <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    rbindlist(lapply(files, function(x) fread(x, stringsAsFactors = FALSE)))
}

do.call_rbind_fread <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    do.call(rbind, lapply(files, function(x) fread(x, stringsAsFactors = FALSE)))
}


read_results <- function(dir_size){
    microbenchmark(
        # lapply_read.delim = lapply_read.delim(dir_size), # too slow to include in benchmarks
        do.call_rbind_read.csv = do.call_rbind_read.csv(dir_size),
        map_df_read.csv = map_df_read.csv(dir_size),
        lapply_read_csv_bind_rows = lapply_read_csv_bind_rows(dir_size),
        map_df_read_csv = map_df_read_csv(dir_size),
        rbindlist_fread = rbindlist_fread(dir_size),
        do.call_rbind_fread = do.call_rbind_fread(dir_size),
        map_df_fread = map_df_fread(dir_size),
        times = 10L) 
}

read_results_lrg_mid_mid <- read_results('./testFolder/500MB_12.5MB_40files')
print(read_results_lrg_mid_mid, digits = 3)

read_results_sml_mic_mny <- read_results('./testFolder/5MB_5KB_1000files/')
read_results_sml_tny_mod <- read_results('./testFolder/5MB_50KB_100files/')
read_results_sml_sml_few <- read_results('./testFolder/5MB_500KB_10files/')

read_results_med_sml_mny <- read_results('./testFolder/50MB_5OKB_1000files')
read_results_med_sml_mod <- read_results('./testFolder/50MB_5OOKB_100files')
read_results_med_med_few <- read_results('./testFolder/50MB_5MB_10files')

read_results_lrg_sml_mny <- read_results('./testFolder/500MB_500KB_1000files')
read_results_lrg_med_mod <- read_results('./testFolder/500MB_5MB_100files')
read_results_lrg_lrg_few <- read_results('./testFolder/500MB_50MB_10files')

read_results_xlg_lrg_mod <- read_results('./testFolder/5000MB_50MB_100files')


print(read_results_sml_mic_mny, digits = 3)
print(read_results_sml_tny_mod, digits = 3)
print(read_results_sml_sml_few, digits = 3)

print(read_results_med_sml_mny, digits = 3)
print(read_results_med_sml_mod, digits = 3)
print(read_results_med_med_few, digits = 3)

print(read_results_lrg_sml_mny, digits = 3)
print(read_results_lrg_med_mod, digits = 3)
print(read_results_lrg_lrg_few, digits = 3)

print(read_results_xlg_lrg_mod, digits = 3)

# display boxplot of my typical use case results & basic machine max load
par(oma = c(0,0,0,0)) # remove overall margins if present
par(mfcol = c(1,1)) # remove grid if present
par(mar = c(12,5,1,1) + 0.1) # to display just a single boxplot with its complete labels
boxplot(read_results_lrg_mid_mid, las = 2, xlab = "", ylab = "Duration (seconds)", main = "40 files @ 12.5MB (500MB)")
boxplot(read_results_xlg_lrg_mod, las = 2, xlab = "", ylab = "Duration (seconds)", main = "100 files @ 50MB (5GB)")

# generate 3x3 grid boxplots
par(oma = c(12,1,1,1)) # margins for the whole 3 x 3 grid plot
par(mfcol = c(3,3)) # create grid (filling down each column)
par(mar = c(1,4,2,1)) # margins for the individual plots in 3 x 3 grid
boxplot(read_results_sml_mic_mny, las = 2, xlab = "", ylab = "Duration (seconds)", main = "1000 files @ 5KB (5MB)", xaxt = 'n')
boxplot(read_results_sml_tny_mod, las = 2, xlab = "", ylab = "Duration (milliseconds)", main = "100 files @ 50KB (5MB)", xaxt = 'n')
boxplot(read_results_sml_sml_few, las = 2, xlab = "", ylab = "Duration (milliseconds)", main = "10 files @ 500KB (5MB)",)

boxplot(read_results_med_sml_mny, las = 2, xlab = "", ylab = "Duration (microseconds)        ", main = "1000 files @ 50KB (50MB)", xaxt = 'n')
boxplot(read_results_med_sml_mod, las = 2, xlab = "", ylab = "Duration (microseconds)", main = "100 files @ 500KB (50MB)", xaxt = 'n')
boxplot(read_results_med_med_few, las = 2, xlab = "", ylab = "Duration (seconds)", main = "10 files @ 5MB (50MB)")

boxplot(read_results_lrg_sml_mny, las = 2, xlab = "", ylab = "Duration (seconds)", main = "1000 files @ 500KB (500MB)", xaxt = 'n')
boxplot(read_results_lrg_med_mod, las = 2, xlab = "", ylab = "Duration (seconds)", main = "100 files @ 5MB (500MB)", xaxt = 'n')
boxplot(read_results_lrg_lrg_few, las = 2, xlab = "", ylab = "Duration (seconds)", main = "10 files @ 50MB (500MB)")

중간 사용 사례

Boxplot Comparison of Elapsed Time my typical use case

대규모 사용 사례

Boxplot Comparison of Elapsed Time for Extra Large Load

다양한 사용 사례

행 : 파일 수 (1000, 100, 10)
열 : 최종 데이터 프레임 크기 (5MB, 50MB, 500MB)
(이미지를 클릭하면 원래 크기로 보입니다) Boxplot Comparison of Directory Size Variations

기본 R 결과는 purrr 및 dplyr의 C 라이브러리를 가져 오는 오버 헤드가 대규모 처리 작업을 수행 할 때 관찰되는 성능 향상을 능가하는 가장 작은 사용 사례에 더 좋습니다.

자신의 테스트를 실행하고 싶다면이 bash 스크립트가 유용 할 수 있습니다.

for ((i=1; i<=$2; i++)); do 
  cp "$1" "${1:0:8}_${i}.csv";
done

bash what_you_name_this_script.sh "fileName_you_want_copied" 100는 순차적으로 번호가 매겨진 파일의 사본 100 개를 만듭니다 (파일 이름의 처음 8 자와 밑줄 뒤에).

귀속 및 감사

특별한 덕분에 :

  • Tyler RinkerAkrun 마이크로 벤치 마크 시연.
  • 저를 map_df()에 소개 한 Jake Kaupp 여기 .
  • David McLaughlin은 시각화를 개선하고 작은 파일에서 관찰 된 성능 역전, 작은 데이터 프레임 분석 결과를 논의/확인하는 데 유용한 피드백을 제공합니다.
84
leerssej

R에서 lapply 또는 다른 루핑 구문을 사용하는 것과 마찬가지로 CSV 파일을 하나의 파일로 병합 할 수 있습니다.

Unix에서 파일에 헤더가 없다면 다음과 같이 쉽게 할 수 있습니다.

cat *.csv > all.csv

또는 헤더가 있고 헤더와 헤더 만 일치하는 문자열을 찾을 수 있습니다 (예 : 헤더 행이 모두 "Age"로 시작한다고 가정 할 때).

cat *.csv | grep -v ^Age > all.csv

나는 Windows에서 COPYSEARCH (또는 FIND 또는 기타)을 DOS 명령 상자에서 사용할 수 있다고 생각하지만 cygwin을 설치하고 Unix 명령 셸의 기능을 얻지 않는 이유는 무엇입니까?

24
Spacedman

이 코드는 모든 CSV 파일을 R로 읽어 들이기 위해 개발 한 코드입니다. 각 CSV 파일에 대한 데이터 프레임을 개별적으로 만들고 그 데이터 프레임에 파일의 원래 이름을 지정합니다 (공백과 .csv 제거).

path <- "C:/Users/cfees/My Box Files/Fitness/"
files <- list.files(path=path, pattern="*.csv")
for(file in files)
{
perpos <- which(strsplit(file, "")[[1]]==".")
assign(
gsub(" ","",substr(file, 1, perpos-1)), 
read.csv(paste(path,file,sep="")))
}

plyr::ldply를 사용하면 .parallel 옵션을 활성화하여 약 50 %의 속도 향상을 얻을 수 있습니다. 각 옵션은 약 30-40MB의 400csv 파일을 읽습니다. 텍스트 진행률 표시 줄이 예제에 포함됩니다.

library(plyr)
library(data.table)
library(doSNOW)

csv.list <- list.files(path="t:/data", pattern=".csv$", full.names=TRUE)

cl <- makeCluster(4)
registerDoSNOW(cl)

pb <- txtProgressBar(max=length(csv.list), style=3)
pbu <- function(i) setTxtProgressBar(pb, i)
dt <- setDT(ldply(csv.list, fread, .parallel=TRUE, .paropts=list(.options.snow=list(progress=pbu))))

stopCluster(cl)
4
manotheshark

내 견해로는 대부분의 다른 대답은 간결한 한 줄짜리 rio::import_list에 의해 폐기되었습니다.

library(rio)
my_data <- import_list(dir("path_to_directory", pattern = ".csv", rbind = TRUE))

추가 인수는 rio::import로 전달됩니다. rio은 R이 읽을 수있는 거의 모든 파일 형식을 처리 할 수 ​​있으며 가능한 경우 data.tablefread을 사용하므로 너무 빠릅니다.

4
user3603486

Dnlbrk의 의견을 바탕으로 assign은 큰 파일의 경우 list2env보다 훨씬 빠릅니다.

library(readr)
library(stringr)

List_of_file_paths <- list.files(path ="C:/Users/Anon/Documents/Folder_with_csv_files/", pattern = ".csv", all.files = TRUE, full.names = TRUE)

Full.names 인수를 true로 설정하면 각 파일의 전체 경로를 파일 목록의 별도 문자열로 가져옵니다. 예를 들어 List_of_file_paths [1]은 "C :/Users/Anon/Documents/Folder_with_csv_files/file1.csv "

for(f in 1:length(List_of_filepaths)) {
  file_name <- str_sub(string = List_of_filepaths[f], start = 46, end = -5)
  file_df <- read_csv(List_of_filepaths[f])  
  assign( x = file_name, value = file_df, envir = .GlobalEnv)
}

Read_csv 대신에 data.table 패키지의 fread 또는 base R read.csv를 사용할 수 있습니다. file_name 단계를 사용하면 각 데이터 프레임이 파일의 전체 경로와 함께 이름으로 남지 않도록 이름을 정리할 수 있습니다. 글로벌 환경으로 전송하기 전에 데이터 테이블에 대한 추가 작업을 수행하도록 루프를 확장 할 수 있습니다. 예를 들면 다음과 같습니다.

for(f in 1:length(List_of_filepaths)) {
  file_name <- str_sub(string = List_of_filepaths[f], start = 46, end = -5)
  file_df <- read_csv(List_of_filepaths[f])  
  file_df <- file_df[,1:3] #if you only need the first three columns
  assign( x = file_name, value = file_df, envir = .GlobalEnv)
}
2
user6741397

나는 list.files(), lapply()list2env() (또는 fs::dir_ls(), purrr::map()list2env())을 사용하는 접근법을 좋아합니다. 그건 간단하고 유연한 것 같습니다.

또는 작은 패키지 { tor } ( to-R )를 시도해보십시오. 작업 디렉토리의 파일을 목록 (list_*() 변형) 또는 전역 환경 (load_*() 변형)으로 가져옵니다.

예를 들어 여기에서 작업 디렉토리의 모든 .csv 파일을 목록 tor::list_csv() 을 사용하여 읽습니다.

library(tor)

dir()
#>  [1] "_pkgdown.yml"     "cran-comments.md" "csv1.csv"        
#>  [4] "csv2.csv"         "datasets"         "DESCRIPTION"     
#>  [7] "docs"             "inst"             "LICENSE.md"      
#> [10] "man"              "NAMESPACE"        "NEWS.md"         
#> [13] "R"                "README.md"        "README.Rmd"      
#> [16] "tests"            "tmp.R"            "tor.Rproj"

list_csv()
#> $csv1
#>   x
#> 1 1
#> 2 2
#> 
#> $csv2
#>   y
#> 1 a
#> 2 b

이제는 tor::load_csv() 을 사용하여 해당 파일을 전역 환경에로드합니다.

# The working directory contains .csv files
dir()
#>  [1] "_pkgdown.yml"     "cran-comments.md" "CRAN-RELEASE"    
#>  [4] "csv1.csv"         "csv2.csv"         "datasets"        
#>  [7] "DESCRIPTION"      "docs"             "inst"            
#> [10] "LICENSE.md"       "man"              "NAMESPACE"       
#> [13] "NEWS.md"          "R"                "README.md"       
#> [16] "README.Rmd"       "tests"            "tmp.R"           
#> [19] "tor.Rproj"

load_csv()

# Each file is now available as a dataframe in the global environment
csv1
#>   x
#> 1 1
#> 2 2
csv2
#>   y
#> 1 a
#> 2 b

특정 파일을 읽어야 할 경우 파일 경로를 regexp, ignore.caseinvert과 일치시킬 수 있습니다.


유연성을 더 높이려면 list_any() 을 사용하십시오. .f 인수를 통해 독자 함수를 제공 할 수 있습니다.

(path_csv <- tor_example("csv"))
#> [1] "C:/Users/LeporeM/Documents/R/R-3.5.2/library/tor/extdata/csv"
dir(path_csv)
#> [1] "file1.csv" "file2.csv"

list_any(path_csv, read.csv)
#> $file1
#>   x
#> 1 1
#> 2 2
#> 
#> $file2
#>   y
#> 1 a
#> 2 b

추가 인수를 ... 또는 람다 함수 내부를 통해 전달하십시오.

path_csv %>% 
  list_any(readr::read_csv, skip = 1)
#> Parsed with column specification:
#> cols(
#>   `1` = col_double()
#> )
#> Parsed with column specification:
#> cols(
#>   a = col_character()
#> )
#> $file1
#> # A tibble: 1 x 1
#>     `1`
#>   <dbl>
#> 1     2
#> 
#> $file2
#> # A tibble: 1 x 1
#>   a    
#>   <chr>
#> 1 b

path_csv %>% 
  list_any(~read.csv(., stringsAsFactors = FALSE)) %>% 
  map(as_tibble)
#> $file1
#> # A tibble: 2 x 1
#>       x
#>   <int>
#> 1     1
#> 2     2
#> 
#> $file2
#> # A tibble: 2 x 1
#>   y    
#>   <chr>
#> 1 a    
#> 2 b
1
Mauro Lepore