我正在尝试为我相对较大的数据集实现data.table,而我无法弄清楚如何在同一行中的多个列上操作函数。具体来说,我想创建一个新列,其中包含列子集中值(即直方图)的特定格式的计数。它有点像table(),但也包含0个条目并进行排序 - 所以,如果你知道更好/更快的方法,我也会很感激!
简化测试用例:
DF<-data.frame("A"=c("a","d","a"),"B"=c("b","a","a"),"C"=c("c","a","a"),"D"=c("a","b","c"),"E"=c("a","a","c"))
DT<-as.data.table(DF)
> DT
A B C D E
1: a b c a a
2: d a a b a
3: a a a c c
我的klunky直方图功能:
histo<-function(vec){
foo<-c("a"=0,"b"=0,"c"=0,"d"=0)
for(i in vec){foo[i]=foo[i]+1}
return(foo)}
>histo(unname(unlist(DF[1,])))
a b c d
3 1 1 0
>histo(unname(unlist(DF[2,])))
a b c d
3 1 0 1
>histo(unname(unlist(DF[3,])))
a b c d
3 0 2 0
所需功能和输出的pseduocode
>DT[,his:=some_func_with_histo(A:E)]
>DT
A B C D E his
1: a b c a a (3,1,1,0)
2: d a a b a (3,1,0,1)
3: a a a c c (3,0,2,0)
答案 0 :(得分:1)
注意:答案已更新为OP的请求和mnel的评论
好的,你喜欢这个解决方案:
library(data.table)
DT <- data.table(A=c("a","d","a"),
B=c("b","a","a"),
C=c("c","a","a"),
D=c("a","b","c"),
E=c("a","a","c"))
fun <- function(vec, char) {
sum(vec==char)
}
DT[, Vec_Nr:= paste(Vectorize(fun, 'char')(.SD, letters[1:4]), collapse=","),
by=1:nrow(DT),
.SDcols=LETTERS[1:5]]
A B C D E Vec_Nr
1: a b c a a 3,1,1,0
2: d a a b a 3,1,0,1
3: a a a c c 3,0,2,0
我基本上将你的问题分成几个步骤:
首先,我定义了一个函数fun
,它给出了一个字符的出现次数。看看如何
该功能有效,只需调用
fun(c("a", "a", "b"), "b")
[1] 1
接下来,我向量化这个函数,因为你不想知道只有一个字符“b”,但对于很多人来说。要将参数向量传递给函数,
使用Vectorize
。要查看其工作原理,只需输入
Vectorize(fun, "char")(c("a", "a", "b"), c("a", "b"))
a b
2 1
接下来,我将结果折叠为一个字符串并将其保存为新列。请注意,我在此处使用了letters
和LETTERS
来向您展示如何使其更具动态性。
答案 1 :(得分:1)
编辑(另见下文):首先将列类转换为字符,例如使用DT <- DT[,lapply(.SD,as.character)]
...
使用factor
,您可以转换vec
并一步传递值(a,b,c,d):
histo2 <- function(x) table(factor(x,levels=letters[1:4]))
然后,您可以通过传递by=1:nrow(DT)
来迭代行。
DT[,as.list(histo2(.SD)),by=1:nrow(DT)]
这给...
nrow a b c d
1: 1 3 1 1 0
2: 2 3 1 0 1
3: 3 3 0 2 0
此外,这会迭代列。这是有效的,因为.SD
是一个特殊变量,包含与by
调用相关的数据子集。在这种情况下,该子集是由其中一行组成的data.table
。 histo2(DT[1])
的工作方式相同。
DF<-data.frame("A"=c("a","d","a"),"B"=c("b","a","a"),"C"=c("c","a","a"),"D"=c("a","b","c"),"E"=c("a","a","c")
,stringsAsFactors=FALSE)
因为我不喜欢使用除制表之外的因素。如果您不希望以这种方式将因子列转换为字符列,则可以使用:
histo3 <- function(x) table(factor(sapply(x,as.character),levels=letters[1:4]))
要将输出放入单个列,请按照建议使用:=
...
DT[,hist:=list(list(histo3(.SD))),by=1:nrow(DT)]
list(list())
部分是关键;我总是通过反复试验来解决这个问题。现在DT看起来像这样:
A B C D E hist
1: a b c a a 3,1,1,0
2: d a a b a 3,1,0,1
3: a a a c c 3,0,2,0
您可能会发现直接从新列访问信息很麻烦。例如,要访问“直方图”的“a”列,我认为最快的路线是......
DT[,hist[[1]][["a"]],by=1:nrow(DT)]
我的初步建议创建了一个只有计数的辅助data.table。我认为,对data.etable中的计数做任何你想做的事情都比较清楚,然后再cbind
。如果选择将其存储在列中,则可以随后使用
DT[,as.list(hist[[1]]),by=1:nrow(DT)]
您使用.SDcols
是正确的。对于你的例子,......
cols = c("A","C")
histname = paste(c("hist",cols),collapse="")
DT[,(histname):=list(list(histo3(.SD))),by=1:nrow(DT),.SDcols=cols]
这给出了
A B C D E hist histAC
1: a b c a a 3,1,1,0 1,0,1,0
2: d a a b a 3,1,0,1 1,0,0,1
3: a a a c c 3,0,2,0 2,0,0,0
答案 2 :(得分:1)
df <- data.table(DF)
df$hist <- unlist(apply(df, 1, function(x) {
list(
sapply(letters[1:4], function(d) {
b <- sum(!is.na(grep(d,x)))
assign(d, b)
}))
}), recursive=FALSE)
您的df $ hist列是一个列表,每个值都名为:
> df
A B C D E hist
1: a b c a a 3,1,2,0
2: d a a b a 3,1,1,1
3: a a a c c 3,0,3,0
> df$hist
[[1]]
a b c d
3 1 2 0
[[2]]
a b c d
3 1 1 1
[[3]]
a b c d
3 0 3 0