2014-04-30 3 views
7

RMongoDB와 함께 작업 중이며 쿼리 값이있는 빈 data.frame을 채워야합니다. 결과는 약 2 백만 개의 문서 (행)로 상당히 길다.느린 data.frame 행 지정

성능 테스트를 수행하는 동안 행에 값을 쓰는 시간이 데이터 프레임의 크기만큼 증가한다는 것을 알게되었습니다. 어쩌면 그것은 잘 알려진 이슈이고 나는 그것을 알아 차릴 마지막 사람입니다.

일부 코드 예제 : 내 컴퓨터에

set.seed(20140430) 
nreg <- 2e3 
dfres <- as.data.frame(matrix(rep(NA,nreg*7),nrow=nreg,ncol=7)) 
system.time(dfres[1e3,] <- c(1:5,"a","b")) 
summary(replicate(10,system.time(dfres[sample(1:nreg,1),] <- c(1:5,"a","b"))[3])) 

nreg <- 2e6 
dfres <- as.data.frame(matrix(rep(NA,nreg*7),nrow=nreg,ncol=7)) 
system.time(dfres[1e3,] <- c(1:5,"a","b")) 
summary(replicate(10,system.time(dfres[sample(1:nreg,1),] <- c(1:5,"a","b"))[3])) 

, 약 0.4 초 정도 data.frame 2 milion 행에 할당. 전체 데이터 집합을 채우려면 많은 시간이 필요합니다. 이 문제를 이끌어 내기 위해 두 번째 시뮬레이션이 진행됩니다.

nreg <- seq(2e1,2e7,length.out=10) 
te <- NULL 
for(i in nreg){ 
    dfres <- as.data.frame(matrix(rep(NA,i*7),nrow=i,ncol=7)) 
    te <- c(te,mean(replicate(10,{r <- sample(1:i,1); system.time(dfres[r,] <- c(1:5,"a","b"))[3]}))) 
} 
plot(nreg,te,xlab="Number of rows",ylab="Avg. time for 10 random assignments [sec]",type="o") 
#rm(nreg,dfres,te) 

enter image description here

질문 : 이런 이유는 무엇입니까? 메모리에 data.frame을 채우는 더 빠른 방법이 있습니까?

+0

워크 어라운드 : 가득 차기까지 1e4 행의 임시 data.frame에 행을 지정합니다. 그런 다음 최종 데이터로 프레임을 바인딩하고 작은 프레임을 다시 채 웁니다. – Emer

답변

11

먼저 "열"부터 시작하여 계속 진행되는 것을 확인하고 행으로 돌아가 보겠습니다.

R 버전 < 사용자가 조작 할 때 3.1.0 (불필요하게)은 data.frame 전체를 복사합니다. 예를 들면 : 당신은 "새로운"컬럼의 추가를 볼 수 있습니다

## R v3.0.3 
df <- data.frame(x=1:5, y=6:10) 
dplyr:::changes(df, transform(df, z=11:15)) ## requires dplyr to be available 

# Changed variables: 
#   old   new   
# x   0x7ff9343fb4d0 0x7ff9326dfba8 
# y   0x7ff9343fb488 0x7ff9326dfbf0 
# z   <added>  0x7ff9326dfc38 

# Changed attributes: 
#   old   new   
# names  0x7ff934170c28 0x7ff934308808 
# row.names 0x7ff934551b18 0x7ff934308970 
# class  0x7ff9346c5278 0x7ff935d1d1f8 

는 "오래된"컬럼의 사본을 가져왔다 (주소는 다릅니다). 또한 속성이 복사됩니다. 가장 많이 물리는 것은이 복사본이 깊은 복사본인데 반해 은 얕은 복사본입니다.

얕은 사본은 전체 데이터가 아닌 전체 데이터가 아닌 전체 열 포인터의 벡터 만 복사합니다. 전체 복사본으로 모든 데이터가 복사됩니다 (여기서는 불필요 함). 점에서

그러나, R의 v3.1.0에,이 있었다 좋은 환영 변화는 "이전"열은 깊은 복사되지 않습니다. R 코어 개발팀에 대한 모든 크레딧.

## R v3.1.0 
df <- data.frame(x=1:5, y=6:10) 
dplyr:::changes(df, transform(df, z=11:15)) ## requires dplyr to be available 

# Changed variables: 
#   old  new   
# z   <added> 0x7f85d328dda8 

# Changed attributes: 
#   old   new   
# names  0x7f85d1459548 0x7f85d297bec8 
# row.names 0x7f85d2c66cd8 0x7f85d2bfa928 
# class  0x7f85d345cab8 0x7f85d2d6afb8 

하면 열 xy 전혀 변경되지 (changes 및 함수 호출의 출력에 따라서 존재하지 않는) 것을 알 수있다. 이것은 거대한 (그리고 환영하는) 개선입니다! 그래서, 무엇을 "행"에 대해 :

는 질문에오고, 지금까지, 우리는 R < 3.1.0에서 추가 열 문제를 쳐다 보면서 지금

v3.1.0

? 이전 버전의 R을 먼저 고려한 다음 R v3.1.0으로 돌아가 보겠습니다.

## R v3.0.3 
df <- data.frame(x=1:5, y=6:10) 
df.old <- df 
df$y[1L] <- -6L 
dplyr:::changes(df.old, df) 

# Changed variables: 
#   old   new   
# x   0x7f968b423e50 0x7f968ac6ba40 
# y   0x7f968b423e98 0x7f968ac6bad0 
# 
# Changed attributes: 
#   old   new   
# names  0x7f968ab88a28 0x7f968abca8e0 
# row.names 0x7f968abb6438 0x7f968ab22bb0 
# class  0x7f968ad73e08 0x7f968b580828 

다시 한번 우리는 열 y을 변경하는 것은 R.의 이전 버전에서뿐만 아니라 복사 열 x 결과 것을 볼

## R v3.1.0 
df <- data.frame(x=1:5, y=6:10) 
df.old <- df 
df$y[1L] <- -6L 
dplyr:::changes(df.old, df) 

# Changed variables: 
#   old   new   
# y   0x7f85d3544090 0x7f85d2c9bbb8 
# 
# Changed attributes: 
#   old   new   
# row.names 0x7f85d35a69a8 0x7f85d35a6690 

우리는 단지y의 사본을 가져왔다 R의 v3.1.0에서 좋은 개선을 참조하십시오. 다시 한번, R v3.1.0의 큰 개선! R의 카피 - 온 - 수정 (copy-on-modify)이 현명 해졌다. 참조 의미로 data.table할당을 사용

하지만 여전히, 우리는 한 단계 더 잘 할 수 - R의 v3.1.0의 경우와 같이도 y 열을 복사 할 수 없습니다.

아이디어의 존재 :만큼 당신이 특정 인덱스에 컬럼에 할당하는 오브젝트의 유형으로 변경하지 마십시오 (여기가 열 y는 정수 - 그래서만큼 당신이 다시 y의 정수를 지정할로), 우리는 정말로을 (를) 통해 (수정)으로 수정하여 복사하지 않고도 처리 할 수 ​​있습니다.

왜? 우리는 여기에 아무것도 할당/재 할당 할 필요가 없기 때문입니다. 예를 들어 정수 열 y에 대해 4 바이트의 저장 장치가 아닌 8 바이트의 저장 장치가 필요한 double/numeric 유형을 할당 한 경우 새 열 y을 만들고 값을 다시 복사해야합니다. 인

, 우리는를 사용 data.table 참조 서브 할당 할 수있다. 이렇게하려면 := 또는 set()을 사용할 수 있습니다. 여기서 set()을 사용하여 시연 해 보겠습니다.

이제 R 배수 3.0.3 및 v3.1.0에 대해 2,000에서 20,000,000 행이 10의 배수 인 데이터의 기본 R 및 data.table과 비교해 보겠습니다. You can find the code here. R의 3.0.3, R 용 3.1.0 vs data.table

최소값, 중간 값 및 최대 : R에 대하여 비교 v3.1.0

R3.0.3 vs data.table

플롯 : R에 대하여 3.0.3 비교

플롯 10 개 복제 20 백만 행에 v3.1.0 및 data.table은 다음과 같습니다

 type min median max 
base_3.0.3 10.05 10.70 18.51 
base_3.1.0 1.67 1.97 5.20 
data.table 0.04 0.04 0.05 

참고 : 전체 타이밍은 this gist입니다. 이것은 분명히 또한 R의 v3.1.0의 향상을 보여 주지만

것을 보여준다 data.table에 참조 의해 서브 할당을 통해 극복 여전히 복사되는 변경하고 여전히 언젠가 소비되는 칼럼, .

HTH

+2

훌륭한 상세 대답. – BrodieG