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


219

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

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

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

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

답변:


259

다음과 같은 결과는 각 데이터 프레임을 단일 목록에서 별도의 요소로 만들어야합니다.

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

이는 CSV가 단일 디렉토리 (현재 작업 디렉토리)에 있고 모든 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)

그러나 다시 한 번 목록으로 남겨 두는 것이 좋습니다.


감사! 이것은 매우 잘 작동합니다 ... 방금 가져온 각 파일의 이름을 지정하여 쉽게 불러올 수 있습니까?
Jojo Ono

파일의 처음 몇 줄을 보여 주시면 제안 사항이있을 수 있습니다. 질문을 수정하십시오!
Spacedman

2
위의 코드는 단일 객체로 가져 오기 위해 완벽하게 작동하지만 데이터 세트에서 열을 불러 오려고 할 때 데이터 프레임이 아닌 단일 객체이므로 인식하지 못합니다. 즉, 위 코드의 내 버전은 다음과 같습니다. setwd ( 'C : / Users / new / Desktop / Dives / 0904_003') temp <-list.files (pattern = "*. csv") ddives <-lapply (temp, read.csv) 이제 각 파일을 ddives [n ]하지만 단일 객체가 아닌 모든 데이터 프레임을 만들기 위해 루프를 작성하는 방법은 무엇입니까? data.frame 연산자를 사용하여 개별적 으로이 작업을 수행 할 수 있지만 이것을 반복하는 방법에 대해서는 확실하지 않습니다. @mrdwab
조조 오노

@JosephOnoufriou, 내 업데이트를 참조하십시오. 그러나 일반적으로 모든 데이터 프레임에서 비슷한 계산을 수행하면 목록 작업이 더 쉬워집니다.
A5C1D2H2I1M1N2O1R2T1 1

2
사용이 답변의 업데이트 된 버전을 할 수있는 기능을 쓰기를 시도하는 누군가를 위해 assign... 당신이 지구 환경에 상주에 할당 된 값을 원하는 경우를, 당신이 설정하기 inherits=T.
dnlbrky

127

빠르고 간결한 tidyverse솔루션 : ( Base R 보다 두 배 이상 빠름 read.csv)

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

data.table '들 fread()도 다시 반으로 그로드 시간을 줄일 수 있습니다. ( 기본 R 시간의 1/4 )

library(data.table)

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

stringsAsFactors = FALSE인수는 데이터 프레임 팩터를 자유롭게 유지합니다 (그리고 marbel이 지적했듯이 기본 설정은fread ).

타입 캐스팅이 건방진 경우 모든 열을 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(.))
}

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

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

Middling 사용 사례

일반적인 사용 사례의 경과 시간 상자 그림 비교

더 큰 사용 사례

초대형 부하에 대한 경과 시간의 박스 플롯 비교

다양한 사용 사례

행 : 파일 수 (1000, 100, 10)
열 : 최종 데이터 프레임 크기 (5MB, 50MB, 500MB)
(원본 크기를 보려면 이미지를 클릭) 디렉토리 크기 변화의 상자 그림 비교

기본 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 파일 이름의 초기 8 자 및 밑줄 이후에 순차적으로 번호가 매겨진 파일의 100 개 사본을 만듭니다.

기여와 감사

특별한 덕분에 :

  • 마이크로 벤치 마크를 보여준 Tyler RinkerAkrun .
  • 저를 도입 제이크 KAUPP map_df() 여기 .
  • 작은 파일, 작은 데이터 프레임 분석 결과에서 관찰 된 성능 반전을 논의하고 확인하는 데 도움이되는 David McLaughlin의 David McLaughlin
  • 의 기본 동작을 지적 해 주셔서 감사합니다 fread(). (에 대해 공부해야합니다 data.table.)

1
당신은 해결책이 나를 위해 일합니다. 이 파일에서 파일 이름을 저장하여 구별하고 싶습니다. 가능합니까?
Niks

1
@ 닉크 스-확실히! 파일을 읽을뿐만 아니라 각 레코드 읽기에 파일 이름을 즉시 추가하는 작은 함수를 작성하고 바꾸십시오. 그래서 같이 readAddFilename <- function(flnm) { read_csv(flnm) %>% mutate(filename = flnm) }그런 단지에 그 드롭 map_df대신 간단한의 읽기 전용 read_csv()지금 거기입니다. 위의 항목을 업데이트하여 기능과 여전히 궁금한 점이 있거나 도움이 될 것 같으면 파이프에 어떻게 적용되는지 보여줄 수 있습니다.
leerssej

실제로 문제 read_csv는보다 훨씬 느리다는 것 fread입니다. 무언가 더 빠르다고 말하면 벤치 마크를 포함시킵니다. 한 가지 아이디어는 30 개의 1GB 파일을 만들고 읽는 것입니다. 성능이 중요한 경우입니다.
marbel

@marbel : 제안 해 주셔서 감사합니다! 530메가바이트 및 (최대 100 파일이있는) 작은 디렉토리에 나는 사이의 성능을 25 % 향상을 발견하고 data.tablefread()dplyrread_csv(): 14.2 19.9 대 초. TBH, 나는 기본 R을 dplyr과 비교하고 있었기 때문에 벤치마킹이 필요하지 않은 것 read_csv()보다 약 2-4 배 빠릅니다 read.csv(). 그러나 fread()보다 완전한 벤치 마크 결과를 확인하기 위해 소용돌이를 치고 일시 중지하는 것이 흥미로 웠습니다 . 다시 감사합니다!
leerssej

1
또 다른 좋은 점입니다. 필자는 필자가 data.table 활동이 데이터를 변경하지 못하도록 보호하는 데 너무 조심 스러웠다 고 생각합니다 (데이터에 대한 다음 및 모든 후속 실행의 성능에 영향을 미침). 물론이 경우에는 의미가 없습니다. 감사합니다. :-D 기능없이, 더 큰 기계로 더 큰 데이터 세트를 사용하여 곧 숫자를 다시 실행할 수 있기를 기대합니다.
leerssej

104

다음은 R base를 사용하여 .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.table하고readr

fread()의 함수 인 버전 data.table패키지. 이것은 R에서 가장 빠른 옵션입니다 .

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

CSV 파일을 읽기위한 또 다른 패키지 인 readr 사용 fread기본 R보다 속도는 느리지 만 기능은 다릅니다.

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

2
이것이 어떻게 Reduce (rbind, lapply (...))를 수행합니까? R 만 배우지 만 제 추측은 성능이 떨어집니다
Aaron

4
data.table성능을 향상시키는 버전을 추가했습니다 .
marbel

특정 파일 만 읽을 수 있습니까? 예 이름에 '날씨'가 포함 된 파일?
Derelict

1
여기에서 찾았습니다 : stackoverflow.com/questions/10353540/… 감사합니다.
Derelict

1
+1은 작업하기 가장 쉬운 단일 데이터 프레임 (모든 CSV 파일의 SQL UNION)을 생성하는 것처럼 보입니다. OP는 1 개의 데이터 프레임을 원하는지 또는 많은 데이터 프레임을 원하는지를 지정하지 않았기 때문에 1 개의 데이터 프레임이 가장 좋다고 가정했기 때문에 허용 된 답변이 "UNION"을 수행하지 않는다는 것에 놀랐습니다. I과 일치하는이 답변처럼 의 설명do.call
레드 완두콩

24

lapplyR에서 또는 다른 루핑 구문 을 사용 하거나 CSV 파일을 하나의 파일로 병합 할 수 있습니다.

유닉스에서 파일에 헤더가 없으면 다음과 같이 쉽습니다.

cat *.csv > all.csv

또는 헤더가 있고 헤더와 헤더 만 일치하는 문자열을 찾을 수있는 경우 (예 : 헤더 행이 모두 "Age"로 시작한다고 가정) 다음을 수행하십시오.

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

내가 Windows에서 당신이 할 수 있다고 생각 COPY하고 SEARCH(또는 FIND또는 무언가) DOS 명령 상자에서, 그런데 왜 설치하지 cygwin와 유닉스 명령 셸의 힘을 얻을?


또는 설치 와 함께 넘어 지는 Git Bash 와 함께 가십 Git니까?
leerssej

내 경험상 파일이 커지기 시작하면 이것이 가장 빠른 해결책은 아닙니다.
Amir

20

이것은 모든 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="")))
}

8

@ A5C1D2H2I1M1N2O1R2T1, @leerssej 및 @marbel의 상위 3 가지 답변은 본질적으로 동일합니다. 각 파일에 fread를 적용한 다음 결과 데이터를 rbind / rbindlist합니다. 나는 보통rbindlist(lapply(list.files("*.csv"),fread)) 양식을 .

이것은 다른 R-internal 대안보다 낫고 많은 수의 큰 csvs에는 적합하지만 속도가 중요 할 때 많은 수의 작은 csvs에는 적합하지 않습니다. 이 경우 cat@Spacedman이 4 위 답변에서 제안한 것처럼 처음 사용하는 것이 훨씬 빠릅니다 . R 내 에서이 작업을 수행하는 방법에 대해 자세히 설명하겠습니다.

x = fread(cmd='cat *.csv', header=F)

그러나 각 CSV에 헤더가 있으면 어떻게됩니까?

x = fread(cmd="awk 'NR==1||FNR!=1' *.csv", header=T)

*.csv쉘 글로브가 실패 할 파일이 너무 많으면 어떻게해야 합니까?

x = fread(cmd='find . -name "*.csv" | xargs cat', header=F)

모든 파일에 헤더가 있고 파일이 너무 많은 경우 어떻게해야합니까?

header = fread(cmd='find . -name "*.csv" | head -n1 | xargs head -n1', header=T)
x = fread(cmd='find . -name "*.csv" | xargs tail -q -n+2', header=F)
names(x) = names(header)

연결된 결과 CSV가 시스템 메모리에 비해 너무 큰 경우 어떻게해야합니까?

system('find . -name "*.csv" | xargs cat > combined.csv')
x = fread('combined.csv', header=F)

헤더가 있습니까?

system('find . -name "*.csv" | head -n1 | xargs head -n1 > combined.csv')
system('find . -name "*.csv" | xargs tail -q -n+2 >> combined.csv')
x = fread('combined.csv', header=T)

마지막으로, 디렉토리에있는 모든 .csv를 원하지 않고 특정 파일 세트를 원한다면 어떻게해야합니까? (또한 모두 헤더가 있습니다.) (이것은 나의 사용 사례입니다.)

fread(text=paste0(system("xargs cat|awk 'NR==1||$1!=\"<column one name>\"'",input=paths,intern=T),collapse="\n"),header=T,sep="\t")

그리고 이것은 일반 fread xargs cat과 거의 같은 속도입니다 :)

참고 : data.table pre-v1.11.6 (2018 년 9 월 19 일)의 경우 cmd=from을 생략하십시오 fread(cmd=.

부록 : 직렬 랩핑 대신 병렬 라이브러리의 mclapply 사용 (예 : rbindlist(lapply(list.files("*.csv"),fread)) rbindlist lapply fread보다 훨씬 빠릅니다.

121401 csvs를 단일 데이터 테이블로 읽을 시간입니다. 각 csv에는 3 개의 열, 하나의 헤더 행 및 평균 4.510 개의 행이 있습니다. 머신은 96 개의 코어가있는 GCP VM입니다.

rbindlist lapply fread   234.172s 247.513s 256.349s
rbindlist mclapply fread  15.223s   9.558s   9.292s
fread xargs cat            4.761s   4.259s   5.095s

요약하자면, 속도에 관심이 있고 많은 파일과 많은 코어를 가지고 있다면, far xargs cat은 상위 3 답변에서 가장 빠른 솔루션보다 약 50 배 빠릅니다.


6

내 생각에, 대부분의 다른 답변은 rio::import_list간결한 단일 라이너 인에 의해 사용되지 않습니다 .

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

추가 인수는로 전달됩니다 rio::import. rio읽을 수있는 거의 모든 파일 형식 R 처리, 그리고 그것을 사용 수 data.tablefread가능한 경우가 너무 빨리해야한다, 그래서.


5

사용 plyr::ldply에이블하여 50 %의 속도 증가 정도가 .parallel약 30-40메가바이트 각각 400 개 CSV 파일을 읽는 동안 옵션. 예는 텍스트 진행 표시 줄을 포함합니다.

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)

좋은 대답입니다! fread또는에 추가 인수를 어떻게 전달 user-defined functions합니까? 감사!
Tung

1
@Tung 찾고은 ?ldply...다른 인수로 전달 .fun. 중 하나를 사용 fread, skip = 100또는 function(x) fread(x, skip = 100)작동합니다
manotheshark을

사용하는 function(x) fread(x, skip = 100)것이 효과가 없었지만 베어 함수 이름 뒤에 추가 인수를 제공하는 것이 트릭입니다. 다시 감사합니다!
Tung

3

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

3

이것은 여러 파일을 읽고 하나의 데이터 프레임으로 결합하는 구체적인 예입니다.

path<- file.path("C:/folder/subfolder")
files <- list.files(path=path, pattern="/*.csv",full.names = T)
library(data.table)
data = do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))

1
당신은 사용할 수 있습니다 rbindlist()에서data.table
조고

3

다음 코드는 컴퓨터에 코어가 많은 한 빅 데이터에 가장 빠른 속도를 제공합니다.

if (!require("pacman")) install.packages("pacman")
pacman::p_load(doParallel, data.table, stringr)

# get the file name
dir() %>% str_subset("\\.csv$") -> fn

# use parallel setting
(cl <- detectCores() %>%
  makeCluster()) %>%
  registerDoParallel()

# read and bind all files together
system.time({
  big_df <- foreach(
    i = fn,
    .packages = "data.table"
  ) %dopar%
    {
      fread(i, colClasses = "character")
    } %>%
    rbindlist(fill = TRUE)
})

# end of parallel work
stopImplicitCluster(cl)

2020/04/16에서 업데이트 됨 : 병렬 계산에 사용할 수있는 새 패키지를 찾으면 다음 코드를 사용하여 대체 솔루션이 제공됩니다.

if (!require("pacman")) install.packages("pacman")
pacman::p_load(future.apply, data.table, stringr)

# get the file name
dir() %>% str_subset("\\.csv$") -> fn

plan(multiprocess)

future_lapply(fn,fread,colClasses = "character") %>% 
  rbindlist(fill = TRUE) -> res

# res is the merged data.table

1

내가 사용하는 접근 방식을 좋아한다 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.case하고 invert.


더 많은 유연성을 위해 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

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

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

이 기능을 stackoverflow R 패키지에 추가하라는 요청을 받았습니다. 그것이 초소형 패키지 (및 타사 패키지에 의존 할 수 없음)임을 감안할 때, 내가 생각해 낸 것은 다음과 같습니다.

#' Bulk import data files 
#' 
#' Read in each file at a path and then unnest them. Defaults to csv format.
#' 
#' @param path        a character vector of full path names
#' @param pattern     an optional \link[=regex]{regular expression}. Only file names which match the regular expression will be returned.
#' @param reader      a function that can read data from a file name.
#' @param ...         optional arguments to pass to the reader function (eg \code{stringsAsFactors}).
#' @param reducer     a function to unnest the individual data files. Use I to retain the nested structure. 
#' @param recursive     logical. Should the listing recurse into directories?
#'  
#' @author Neal Fultz
#' @references \url{/programming/11433432/how-to-import-multiple-csv-files-at-once}
#' 
#' @importFrom utils read.csv
#' @export
read.directory <- function(path='.', pattern=NULL, reader=read.csv, ..., 
                           reducer=function(dfs) do.call(rbind.data.frame, dfs), recursive=FALSE) {
  files <- list.files(path, pattern, full.names = TRUE, recursive = recursive)

  reducer(lapply(files, reader, ...))
}

판독기 및 감속기 기능을 매개 변수화하여 사람들은 원하는 경우 data.table 또는 dplyr을 사용하거나 더 작은 데이터 세트에 적합한 기본 R 기능을 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.