R:在data.table中的列子集上运行

时间:2013-05-30 00:59:10

标签: r data.table multiple-columns

我正在尝试为我相对较大的数据集实现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)

3 个答案:

答案 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

接下来,我将结果折叠为一个字符串并将其保存为新列。请注意,我在此处使用了lettersLETTERS来向您展示如何使其更具动态性。

答案 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.tablehisto2(DT[1])的工作方式相同。

编辑(回应OP的评论):哦,对不起,我本能地用你的第一行替换

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。如果选择将其存储在列中,则可以随后使用

创建辅助data.table
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