2016-08-17 1 views
6

data.table 구문을 사용하여 원래의 data.table 열과 다음 열의 차이점이있는 data.table을 어떻게 만들 수 있습니까?data.table에서 열 단위 차이 가져 오기

예 : I는 각 행의 그룹 인 data.table을 가지며, 각 컬럼은 이와 같이 등 년 1,2 후 0 년 후 인구 살아남은 :

pop <- data.table(group_id = c(1, 2, 3), 
        N = c(4588L, 4589L, 4589L), 
        N_surv_1 = c(4213, 4243, 4264), 
        N_surv_2 = c(3703, 3766, 3820), 
        N_surv_3 = c(2953, 3054, 3159)) 
# group_id N N_surv_1 N_surv_2 N_surv_3 
#  1 4588  4213  3703  2953 
#  2 4589  4243  3766  3054 
#  3 4589  4264  3820  3159 

(N 진정한 정수 수와 N_surv_1 등 소수가 될 수 전망입니다 때문에 데이터 유형이 다를 수)

내가 무슨 짓을 :.은 기본 diff 및 매트릭스 전위를 사용하여, 우리는 할 수 있습니다

,451,515,
diff <- data.table(t(diff(t(as.matrix(pop[,-1,with=FALSE]))))) 
setnames(diff, paste0("deaths_",1:ncol(diff))) 
cbind(group_id = pop[,group_id],diff) 
# produces desired output: 
# group_id deaths_1 deaths_2 deaths_3 
#   1  -375  -510  -750 
#   2  -346  -477  -712 
#   3  -325  -444  -661 

은 내가 melt.data.table에 의해 생성 된 단일 컬럼에 그룹에 의해 기본 diff를 사용할 수 있다는 것을 알고, 그래서 이것은 작동하지만 꽤되지 않습니다 :

melt(pop, 
    id.vars = "group_id" 
    )[order(group_id)][, setNames(as.list(diff(value)), 
            paste0("deaths_",1:(ncol(pop)-2))), 
          keyby = group_id] 

은 그게 가장 data.table - riffic 방식 이 작업을 수행하거나 data.table에서 다중 열 작업으로 처리하는 방법이 있습니까?

답변

6

을, 당신은 하위 집합을 뺄 수 :

ncols = grep("^N(_surv_[0-9]+)?", names(pop), value=TRUE) 
pop[, Map(
    `-`, 
    utils:::tail.default(.SD, -1), 
    utils:::head.default(.SD, -1) 
), .SDcols=ncols] 

# N_surv_1 N_surv_2 N_surv_3 
# 1:  -375  -510  -750 
# 2:  -346  -477  -712 
# 3:  -325  -444  -661 

:=이있는 새 열에이 값을 할당 할 수 있습니다. tailhead을 @akrun이 지적한대로 pop[, .SD[, -1, with=FALSE] - .SD[, -ncol(.SD), with=FALSE], .SDcols=ncols]과 같이 with=FALSE 대신 사용할 수 있습니다.

어쨌든,이 단순히 재편에 비해 꽤 복잡하다 :

melt(pop, id="group_id")[, tail(value, -1) - head(value, -1), by=group_id] 
# group_id V1 
# 1:  1 -375 
# 2:  1 -510 
# 3:  1 -750 
# 4:  2 -346 
# 5:  2 -477 
# 6:  2 -712 
# 7:  3 -325 
# 8:  3 -444 
# 9:  3 -661 
2

고유 ID와 데이터 및 각 행을 재편하지 않으면, 당신은 id 컬럼에 의해 그룹은 다음 즉 unlist(.SD), 각 행에 diff와의 차이를 계산할 수 있습니다

기본적으로
pop[, setNames(as.list(diff(unlist(.SD))), paste0("deaths_", 1:(ncol(pop)-2))), group_id] 

# group_id deaths_1 deaths_2 deaths_3 
# 1:  1  -375  -510  -750 
# 2:  2  -346  -477  -712 
# 3:  3  -325  -444  -661 

,이 같은 당신이 경우 열 이름 설정 무시 : 음

pop[, as.list(diff(unlist(.SD))), group_id] 
2

여기 빠르게 만들 수있는 재편 또는 그룹화하지 않고 그것을 할 수있는 또 다른 방법입니다. 행 수가 적 으면 눈에 띄는 차이가 없을 것입니다.

cols<-names(pop)[-1] 
combs<-list() 
for(i in 2:length(cols)) { 
    combs[[length(combs)+1]]<-c(cols[i-1], cols[i]) 
} 
newnames<-sapply(combs,function(x) gsub('N_surv','death',x[2])) 
deathpop<-copy(pop) 
deathpop[,(newnames):=lapply(combs,function(x) get(x[2])-get(x[1]))] 
deathpop[,(cols):=NULL] 

내가했던 일부

rows<-10000000 
pop <- data.table(group_id = 1:rows, 
        N = runif(rows,3000,4000), 
        N_surv_1 = runif(rows,3000,4000), 
        N_surv_2 = runif(rows,3000,4000), 
        N_surv_3 = runif(rows,3000,4000)) 
system.time({ 
    cols<-names(pop)[-1] 
    combs<-list() 
    for(i in 2:length(cols)) { 
     combs[[length(combs)+1]]<-c(cols[i-1], cols[i]) 
    } 
    newnames<-sapply(combs,function(x) gsub('N_surv','death',x[2])) 
    deathpop<-copy(pop) 
    deathpop[,(newnames):=lapply(combs,function(x) get(x[2])-get(x[1]))] 
    deathpop[,(cols):=NULL]}) 

를 벤치마킹 그리고 내가

system.time(pop[, as.list(diff(unlist(.SD))), group_id]) 

을 한 반면

user system elapsed 
0.192 0.808 1.003 

를 반환하며

0을 반환
user system elapsed 
169.836 0.428 170.469 

는 또한

system.time(melt(pop, id="group_id")[, tail(value, -1) - head(value, -1), by=group_id]) 

반환

user system elapsed 
223.360 1.736 225.315 
을하고

user system elapsed 
0.044 0.044 0.089 

마지막으로 반환

system.time({ 
    ncols = grep("^N(_surv_[0-9]+)?", names(pop), value=TRUE) 
    pop[, Map(
    `-`, 
    utils:::tail.default(.SD, -1), 
    utils:::head.default(.SD, -1) 
), .SDcols=ncols] 
}) 

을했다

Frank의 Map 솔루션이 가장 빠릅니다. 당신이 복사를 꺼내면 프랭크의 시간에 훨씬 가까워 지지만이 테스트 케이스에서 여전히 승리합니다.

+0

흥미롭게도 용융 속도가 느리지 만 놀라운 것은 아닙니다. 나는 여전히 데이터를 저장하는 "올바른"방법이라고 주장 할 것입니다. 즉, 각 그룹에 대한 모집단 측정 순서를 캡처하는 "시간"열이있는 긴 형식이어야합니다. 나는 akrun의 해결책을 추측하고있다. (내 대답은 :'pop [, .SD [, -1, = FALSE] - .SD [, -ncol (.SD), = FALSE], .SDcols = ncols]')도 상당히 빠릅니다. – Frank