0

我有一个 34,000 行 x 24 列的大型数据框,每个数据框都包含一个类别标签。我想有效地浏览数据框并计算每个标签在该行的一部分中列出的次数包括 0s。(我使用了一个 for 循环来驱动一个不是非常有效的 length(which) 语句)

例子:

df.test<-as.data.frame(rbind(c("A", "B", "C","B","A","A"),c("C", "C", "C","C","C","C"), c("A", "B", "B","A","A","A")))

df.res<-as.data.frame(矩阵(ncol=6, nrow=3))

假设 df.test 中的 1:3 列来自一个数据集,4:6 来自另一个数据集。生成 df.res 以显示这一点的最有效方法是什么:

ABCABC

1 1 1 2 1 0

0 0 3 0 0 3

1 2 0 3 0 0

4

1 回答 1

1

一种使用大量s的_apply方法如下:

#list with the different data frames
df_ls <- sapply(seq(1, ncol(df.test), 3), function(x) df.test[,x:(x+2)], simplify = F)

#count each category
df.res <- do.call(cbind,
   lapply(df_ls, function(df.) { t(apply(df., 1, 
      function(x) { table(factor(unlist(x), levels = c("A", "B", "C"))) })) }))

#> df.res
#     A B C A B C
#[1,] 1 1 1 2 1 0
#[2,] 0 0 3 0 0 3
#[3,] 1 2 0 3 0 0

模拟您描述的数据框:

DF <- data.frame(replicate(24, sample(LETTERS[1:3], 34000, T)), stringsAsFactors = F)
#> head(DF)
#  X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24
#1  B  C  C  C  B  A  C  B  B   A   C   C   B   C   B   B   B   C   B   C   C   B   B   C
#2  C  B  C  A  B  C  B  C  A   B   A   C   B   B   A   A   C   A   B   B   B   C   A   B
#3  B  C  C  A  A  A  C  A  C   A   A   A   B   B   A   A   A   C   B   B   A   C   C   C
#4  C  C  A  B  A  B  B  B  A   A   A   C   C   A   B   A   C   C   A   C   C   C   B   A
#5  B  B  A  A  A  A  C  A  B   B   A   B   B   A   C   A   A   A   C   A   A   C   B   C
#6  C  A  C  C  A  B  B  C  C   C   B   C   A   B   B   B   B   B   A   C   A   A   B   A
#> dim(DF)
#[1] 34000    24

DF_ls <- sapply(seq(1, ncol(DF), 3), function(x) DF[,x:(x+2)], simplify = F)

system.time(
DF.res <- do.call(cbind,
       lapply(DF_ls, function(df.) { t(apply(df., 1, 
          function(x) { table(factor(unlist(x), levels = c("A", "B", "C"))) })) })))
#user  system elapsed 
#59.84    0.07   60.73 

#> head(DF.res)
#     A B C A B C A B C A B C A B C A B C A B C A B C
#[1,] 0 1 2 1 1 1 0 2 1 1 0 2 0 2 1 0 2 1 0 1 2 0 2 1
#[2,] 0 1 2 1 1 1 1 1 1 1 1 1 1 2 0 2 0 1 0 3 0 1 1 1
#[3,] 0 1 2 3 0 0 1 0 2 3 0 0 1 2 0 2 0 1 1 2 0 0 0 3
#[4,] 1 0 2 1 2 0 1 2 0 2 0 1 1 1 1 1 0 2 1 0 2 1 1 1
#[5,] 1 2 0 3 0 0 1 1 1 1 2 0 1 1 1 3 0 0 2 0 1 0 1 2
#[6,] 1 0 2 1 1 1 0 1 2 0 1 2 1 2 0 0 3 0 2 0 1 2 1 0

编辑关于该方法的更多评论。

我会一步一步做上面的。

第一步是对绑定在一起的不同数据帧进行子集化;这些数据帧中的每一个都放在一个列表中。该函数function(x) { df.test[,x:(x+2)], simplify = F }根据x:的值对整个数据框进行子集化seq(1, ncol(df.test), 3)。扩展这一点,如果您的 4 列相距不同的数据框将按照上述顺序3进行更改。4

#> df_ls <- sapply(seq(1, ncol(df.test), 3), function(x) df.test[,x:(x+2)], simplify = F)
#> df_ls
#[[1]]
#  V1 V2 V3
#1  A  B  C
#2  C  C  C
#3  A  B  B

#[[2]]
#  V4 V5 V6
#1  B  A  A
#2  C  C  C
#3  A  A  A

下一步是lapply在 -previously made-list 中添加一个函数,该函数计算一个数据帧的每一行中的每个类别(即列表的元素)。函数是这样的:t(apply(df., 1, function(x) { table(factor(unlist(x), levels = c("A", "B", "C"))) })). 内部函数 ( function(x)) 将包含所有类别的因子中的一行转换为 ( ) 每个类别在该行中出现的数量。将此函数应用于数据帧的每一行 ( )。所以,现在,我们已经计算了一个数据帧的每一行中每个类别的频率。levelstableapplyMARGIN = 1

#> table(factor(unlist(df_ls[[1]][3,]), levels = c("A", "B", "C")))
     #df_ls[[1]][3,] is the third row of the first dataframe of df_ls 
                                  #(i.e. _one_ row of _one_ dataframe)
#A B C 
#1 2 0

#> apply(df_ls[[1]], 1, 
#+       function(x) { table(factor(unlist(x), levels = c("A", "B", "C"))) })
#  [,1] [,2] [,3]   #df_ls[[1]] is the first dataframe of df_ls (i.e. _one_ dataframe)
#A    1    0    1
#B    1    0    2
#C    1    3    0

因为, 的返回apply不是我们t用来交换行和列的想要的形式。

下一步,是对lapply每个数据框(即列表的元素)进行上述所有操作。

#> lapply(df_ls, function(df.) { t(apply(df., 1, 
#+       function(x) { table(factor(unlist(x), levels = c("A", "B", "C"))) })) })
#[[1]]
#     A B C
#[1,] 1 1 1
#[2,] 0 0 3
#[3,] 1 2 0

#[[2]]
#     A B C
#[1,] 2 1 0
#[2,] 0 0 3
#[3,] 3 0 0

最后一步是将cbind所有这些元素放在一起。按列绑定列表中所有元素的方法是do.call cbind在该列表中。

#NOT the expected, using only cbind
#> cbind(lapply(df_ls, function(df.) { t(apply(df., 1, 
#+       function(x) { table(factor(unlist(x), levels = c("A", "B", "C"))) })) }))
#     [,1]     
#[1,] Integer,9
#[2,] Integer,9

#Correct!
#> do.call(cbind, lapply(df_ls, function(df.) { t(apply(df., 1, 
#+       function(x) { table(factor(unlist(x), levels = c("A", "B", "C"))) })) }))
#     A B C A B C
#[1,] 1 1 1 2 1 0
#[2,] 0 0 3 0 0 3
#[3,] 1 2 0 3 0 0
于 2013-10-28T23:36:14.600 回答