2013-06-15 3 views
2

나는 일정 기간 동안 측정 된 일련의 측정을했습니다. 측정 횟수는 일반적으로 4입니다. 모든 측정에서 캡처 할 수있는 범위의 범위는 1-5입니다 (실생활에서는 테스트 세트가 주어지면 범위는 최대 100 또는 20만큼 작을 수 있음).R data.table : 현재 측정 전의 발생 횟수

오늘 날짜 이전에 각 값의 수를 계산하고 싶습니다.

나 일부 샘플 데이터로 설명하자

# test data creation 
d1 = list(as.Date("2013-5-4"), 4,2) 
d2 = list(as.Date("2013-5-9"), 2,5) 
d3 = list(as.Date("2013-5-16"), 3,2) 
d4 = list(as.Date("2013-5-30"), 1,4) 

d = rbind(d1,d2,d3,d4) 
colnames(d) <- c("Date", "V1", "V2") 

tt = as.data.table(d) 

내가 (가능한 값의 범위에 가능한 값 당 1) 5 열을 추가하는 함수를 실행하려면. 각 열에는 테스트 날짜 이전에 해당 값의 발생 수가 필요합니다.

예를 들어, 2013-5-30의 함수 출력은 C1=0, C2=3, C3=1, C4=1, C5=1이됩니다.

그것은 얼마나 많은 시간을 계산입니다 :

1 제로
2 세
세 이전에 나타나있다 5/30을 포함하여 이전하지 등장 5/30을 포함하여 이전하지 등장 또한 하나

입니다 5/30를 포함하지 않는, 그것은 또한 번호가 표시되는 총 측정의 비율에 대한 열을 포함해야한다. 5/30에 예를 들어, 6 개 5/30 측정 전에 있었다 그래서

PC1 = (0/6), PC2 = 3/6 = PC3/6 1 PC4 = 1/6, PC5 = 1/6

데이터 테이블 할당 표기법 (: =)을 사용하여 이러한 여러 열을 모두 한 번에 추가하고 싶습니다. 내가 찾고있는 출력 형식은 다음과 같습니다.

Date V1 V2 C1 PC1 C2 PC2 C3 PC3 C4 PC4 C5 PC5 

답변

3

1 data.table

먼저 더 일반적인 하나 질문에 t의 이상한 구조를 대체 이전 행 perm은 C 열 (nc + 1 : n)과 PC 열 (nc + n + 1 : n)의 열 번호를 다시 배열하는 데 사용되는 순열 벡터입니다.

nc <- ncol(t) # 3 
n <- t[, max(V1, V2)] # 5 

Cnames <- paste0("C", 1:n) 
PCnames <- paste0("PC", 1:n) 

perm <- c(1:nc, rbind(nc + 1:n, nc + n + 1:n)) 

t[, (Cnames) := as.list(tabulate(c(V1, V2), n)), by = 1:nrow(t)][, 
(Cnames):=lapply(.SD, function(x) cumsum(x) - x), .SDcol=Cnames][, 
(PCnames):=lapply(.SD, function(x) x/seq(0,len=.N,by=nc-1)), .SDcols=Cnames][, 
perm, with = FALSE] 

마지막 줄은 제공 :

  Date V1 V2 C1 PC1 C2 PC2 C3  PC3 C4  PC4 C5  PC5 
1: 2013-05-04 4 2 0 NaN 0 NaN 0  NaN 0  NaN 0  NaN 
2: 2013-05-09 2 5 0 0 1 0.5 0 0.0000000 1 0.5000000 0 0.0000000 
3: 2013-05-16 3 2 0 0 2 0.5 0 0.0000000 1 0.2500000 1 0.2500000 
4: 2013-05-30 1 4 0 0 3 0.5 1 0.1666667 1 0.1666667 1 0.1666667 

1a.data.table 대안

만약 그 존재하기 때문에 매우 유용하지 않습니다 첫 데이트의 행 (생략하려면 '확인' 첫 번째 날짜 이전의 날짜가없는 경우) 다음 지루하고 직선적 인 자체 조인을 수행 할 수 있습니다.

t <- data.table(
    Date = as.Date(c("2013-5-4", "2013-5-9", "2013-5-16", "2013-5-30")), 
    V1 = c(4, 2, 3, 1), 
    V2 = c(2, 5, 2, 4) 
) 
tt <- t[, one := 1] 
setkey(tt, one) 
tt[tt,,allow.cartesian=TRUE][Date > Date.1, list(
    C1 = sum(.SD == 1), PC1 = mean(.SD == 1), 
    C2 = sum(.SD == 2), PC2 = mean(.SD == 2), 
    C3 = sum(.SD == 3), PC3 = mean(.SD == 3), 
    C4 = sum(.SD == 4), PC4 = mean(.SD == 4), 
    C5 = sum(.SD == 5), PC5 = mean(.SD == 5) 
), by = list(Date, V1, V2), .SDcols = c("V1.1", "V2.1")] 

1b. data.table 대안

아니면이 같은보다 콤팩트 1A를 다시 쓸 수있다 (tt, n, CnamesPCnames 위에서부터 어디)

tt[tt,,allow.cartesian=TRUE][Date > Date.1, setNames(as.list(rbind(
    sapply(1:n, function(i, .SD) sum(.SD==i), .SD=.SD), 
    sapply(1:n, function(i, .SD) mean(.SD==i), .SD=.SD) 
)), c(rbind(Cnames, PCnames))), 
    by = list(Date, V1, V2), .SDcols = c("V1.1", "V2.1")] 

2 sqldf

대안 data.table에이 비슷하고 지루하고 직선적 인 자체 조인으로 SQL을 사용하는 것입니다 :

library(sqldf) 
sqldf("select a.Date, a.V1, a.V2, 
sum(((b.V1 = 1) + (b.V2 = 1)) * (a.Date > b.Date)) C1, 
sum(((b.V1 = 1) + (b.V2 = 1)) * (a.Date > b.Date))/
cast (2 * count(*) - 2 as real) PC1, 
sum(((b.V1 = 2) + (b.V2 = 2)) * (a.Date > b.Date)) C2, 
sum(((b.V1 = 2) + (b.V2 = 2)) * (a.Date > b.Date))/
cast (2 * count(*) - 2 as real) PC2, 
sum(((b.V1 = 3) + (b.V2 = 3)) * (a.Date > b.Date)) C3, 
sum(((b.V1 = 3) + (b.V2 = 3)) * (a.Date > b.Date))/
cast (2 * count(*) - 2 as real) PC3, 
sum(((b.V1 = 4) + (b.V2 = 4)) * (a.Date > b.Date)) C4, 
sum(((b.V1 = 4) + (b.V2 = 4)) * (a.Date > b.Date))/
cast (2 * count(*) - 2 as real) PC4, 
sum(((b.V1 = 5) + (b.V2 = 5)) * (a.Date > b.Date)) C5, 
sum(((b.V1 = 5) + (b.V2 = 5)) * (a.Date > b.Date))/
cast (2 * count(*) - 2 as real) PC5 
from t a, t b where a.Date >= b.Date 
group by a.Date") 

2a. 대안

sqldf 대안은 다음과 같이 위의 SQL 문자열을 생성하는 문자열 조작을 사용하는 것입니다 :

f <- function(i) { 
    s <- fn$identity("sum(((b.V1 = $i) + (b.V2 = $i)) * (a.Date > b.Date))") 
    fn$identity("$s C$i,\n $s /\ncast (2 * count(*) - 2 as real) PC$i") 
} 
s <- fn$identity("select a.Date, a.V1, a.V2, `toString(sapply(1:5, f))` 
    from t a, t b where a.Date >= b.Date 
    group by a.Date") 

sqldf(s) 

2B. 두 번째 sqldf 대안

첫 번째 날짜에 출력 행을 사용하지 않고 SQL 솔루션을 단순화 할 수 있습니다. 첫 데이트 등이 할 수 감각은 더 이전 날짜가 집계되지합니다 :

sqldf("select a.Date, a.V1, a.V2, 
sum((b.V1 = 1) + (b.V2 = 1)) C1, 
avg((b.V1 = 1) + (b.V2 = 1)) PC1, 
sum((b.V1 = 2) + (b.V2 = 2)) C2, 
avg((b.V1 = 2) + (b.V2 = 2)) PC2, 
sum((b.V1 = 3) + (b.V2 = 3)) C3, 
avg((b.V1 = 3) + (b.V2 = 3)) PC3, 
sum((b.V1 = 4) + (b.V2 = 4)) C4, 
avg((b.V1 = 4) + (b.V2 = 4)) PC4, 
sum((b.V1 = 5) + (b.V2 = 5)) C5, 
avg((b.V1 = 5) + (b.V2 = 5)) PC5 
from t a, t b where a.Date > b.Date 
group by a.Date") 

다시는 이전의 솔루션에서와 동일한 방식으로 repitition을 방지하기 위해 SQL 문자열을 만들 수있을 것이다.

UPDATE : 추가 PC 열 및 일부 단순화

업데이트 2 : 내 최초의 솔루션에 사용되는 어떤 용액에 추가 솔루션

+0

새 원본 데이터와 새 분석 데이터를 함께 제공하므로 초기 솔루션을 좋아합니다. 비록 첫 번째 행이 쓰레기 일지라도). 나는 '현재'행과 '이후'('before'가 아님)의 모든 행을 어떻게 고려하는지 이해하기 위해 노력하고 있습니다. 첫 번째 해결 방법은 여러 계층의 과제, 흥미로운 접근 방법을 연습합니다. – eAndy

0

아마도 %in% 연산자가 필요합니다.

> foo<-sample(1:10,4) 
> bar<-sample(1:10,3) 
> foo 
[1] 5 3 9 6 
> bar 
[1] 1 7 2 
> bar2<-sample(1:10,5) 
> bar2 
[1] 2 9 4 8 5 
> which(bar2%in%foo) 
[1] 2 5 #those are the indices of the values in bar2 which appear in foo 

> which(bar%in%foo) 
integer(0) 
+0

. 고맙습니다. – eAndy

1

여기가 시작되었습니다. 나는 이것을 "한 번에"하는 이유를 알지 못합니다. 가능할 수도 있습니다. 직접 해봐.

library(data.table) 
t <- data.table(
    Date = as.Date(c("2013-5-4", "2013-5-9", "2013-5-16", "2013-5-30")), 
    V1 = c(4, 2, 3, 1), 
    V2 = c(2, 5, 2, 4) 
) 
이제

tabulate 각 행을 축적하는 cumsum를 사용

library(data.table) 
DT = as.data.table(d) 

DT[,i:=as.numeric(Date)] 
setkey(DT,"i") 

uv <- 1:max(unlist(DT[,2:3,with=FALSE])) 
DT[,paste0("C",uv):=lapply(uv,function(x) x %in% unlist(.SD)),.SDcols=2:3,by=i] 
DT[,paste0("C",uv):=lapply(.SD,function(x) c(NA,head(cumsum(x),-1))),.SDcols=paste0("C",uv)] 
DT[,paste0("PC",uv):=lapply(.SD,function(x) x/(2*.I-2)),.SDcols=paste0("C",uv)] 

#   Date V1 V2  i C1 C2 C3 C4 C5 PC1 PC2  PC3  PC4  PC5 
# 1: 2013-05-04 4 2 15829 NA NA NA NA NA NA NA  NA  NA  NA 
# 2: 2013-05-09 2 5 15834 0 1 0 1 0 0 0.5 0.0000000 0.5000000 0.0000000 
# 3: 2013-05-16 3 2 15841 0 2 0 1 1 0 0.5 0.0000000 0.2500000 0.2500000 
# 4: 2013-05-30 1 4 15855 0 3 1 1 1 0 0.5 0.1666667 0.1666667 0.1666667 
+0

이것을 시험해보고 싶지만 오류를 반환합니다. 1. 'd'는 위의 코드에 정의되어 있지 않습니다. 2. DT [, paste0 ("PC", uv) : = lapply (.SD, function (x) x/(2 * .I-2)), SDcols = paste0 ("C", uv)] '[.data.frame' (DT,,': ='(paste0 ("PC", uv), lapply (.SD, function) 현재 사용중인 행을 무시하기 위해 head (, - 1)를 사용하고 있습니까? – eAndy

+0

-1)'x의 마지막 행/위치를 제거합니다. 올바른 길이의 벡터를 얻으려면 첫 번째 위치에 NA를 추가하십시오. – eAndy

+0

'head (x, (2) : 사용하지 않은 인수 (.SDcols = paste0 ("C", uv)) – Roland