我在R中有一个data.table,它包含在不同时间点收集的每个用户的多个状态值。我想比较连续时间点的状态值,并在状态发生变化时用标志更新行。请参阅下面的示例
DT_A <- data.table(sid=c(1,1,2,2,2,3,3), date=as.Date(c("2014-06-22","2014-06-23","2014-06-22","2014-06-23", "2014-06-24","2014-06-22","2014-06-23")), Status1 = c("A","B","A","A","B","A","A"), Status2 = c("C","C","C","C","D","D","E"))
DT_A_Final <- data.table(sid=c(1,1,2,2,2,3,3), date=as.Date(c("2014-06-22","2014-06-23","2014-06-22","2014-06-23", "2014-06-24","2014-06-22","2014-06-23")), Status1 = c("0","1","0","0","1","0","0"), Status2 = c("0","0","0","0","1","0","1"))
原始数据表DT_A是
sid date Status1 Status2
1 1 2014-06-22 A C
2 1 2014-06-23 B C
3 2 2014-06-22 A C
4 2 2014-06-23 A C
5 2 2014-06-24 B D
6 3 2014-06-22 A D
7 3 2014-06-23 A E
最终所需的数据表是DT_A_final
sid date Status1 Status2
1 1 2014-06-22 0 0
2 1 2014-06-23 1 0
3 2 2014-06-22 0 0
4 2 2014-06-23 0 0
5 2 2014-06-24 1 1
6 3 2014-06-22 0 0
7 3 2014-06-23 0 1
请帮助我如何实现这一目标?
答案 0 :(得分:7)
这是一个选项:
DT_A[,
c("S1Change", "S2Change") :=
lapply(.SD, function(x) c(0, head(x, -1L) != tail(x, -1L))),
.SDcols=c("Status1", "Status2"), # .SD contains just these columns
by=sid
]
在这里,我们创建了两个新列,我们按lapply
填充.SD
(定义为仅包含Status1
和Status2
)。该函数将除列的第一个值之外的所有值与除了同一列的最后一个列之外的所有值进行比较。只要列中发生更改,这将返回TRUE。我们在开头添加0,因为第一个值永远不会改变;这也会将结果强制转换为数字向量(感谢eddi)。
然后,我们只是by
sid
,瞧:
sid date Status1 Status2 S1Change S2Change
1: 1 2014-06-22 A C 0 0
2: 1 2014-06-23 B C 1 0
3: 2 2014-06-22 A C 0 0
4: 2 2014-06-23 A C 0 0
5: 2 2014-06-24 B D 1 1
6: 3 2014-06-22 A D 0 0
7: 3 2014-06-23 A E 0 1
如果需要,您可以轻松对其进行子集以删除原始状态列。无法重复使用它们,因为结果的数据类型与原始数据类型不同(数字与字符)。
答案 1 :(得分:3)
dplyr
方法也适用于此。首先创建一个函数,将向量中的所有元素与第一个元素进行比较,然后将其应用于所有&#34;状态&#34;变量:
library(dplyr)
library(magrittr)
equal_first <- function(x) {
x %>% equals(x[1]) %>% not %>% as.numeric
}
DT_A %>%
group_by(sid) %>%
mutate_each(funs(equal_first),starts_with("Status"))
sid date Status1 Status2
1 1 2014-06-22 0 0
2 1 2014-06-23 1 0
3 2 2014-06-22 0 0
4 2 2014-06-23 0 0
5 2 2014-06-24 1 1
6 3 2014-06-22 0 0
7 3 2014-06-23 0 1
如果每个用户有多个状态更改,则需要与之前的值进行比较,而不是第一个:
equal_prev <- function(x) {
x %>% equals(lag(x, default = x[1])) %>% not %>% as.numeric
}
DT_A %>%
group_by(sid) %>%
mutate_each(funs(equal_prev),starts_with("Status"))
答案 2 :(得分:1)
使用set
for(col in c('Status1','Status2')){
ones <- DT_A[, .I[1L] ,by=c('sid',col)][,V1[-1L],by=sid][['V1']]
set(DT_A, j=col,value='0')
set(DT_A, j=col,i=ones,value='1')
}
注意我保留Status1
/ Status2
作为字符变量,创建整数变量,使用
for(col in c('Status1','Status2')){
ones <- DT_A[, .I[1L] ,by=c('sid',col)][,V1[-1L],by=sid][['V1']]
set(DT_A, j=col, value=NULL)
set(DT_A, j=col,value=0L)
set(DT_A, j=col,i=ones,value=1L)
}