总结R数据框中因素的分布

3

假设我有这样的一个数据框:

  X1   X2   X3
1 A    B    A
2 A    C    B
3 B    A    B
4 A    A    C

我想要在每一列中计算 A、B、C 等出现的次数,并返回结果。
    A_count B_count C_count
X1  3       1       0       
X2  2       1       1
X3  1       2       1

我确定这个问题有数千个重复,但是我似乎找不到适合我的答案:(
通过运行
apply(mydata, 2, table)

我理解为:我得到了类似于某物的东西。
$X1
   B     A
   1     3
$X2
   A     C     B
   2     1     1

但这不完全是我想要的,如果我试图将它重新构建为数据框,它不起作用,因为每一行的列数都不同(像上面的$X1,没有C)。
我错过了什么吗?
非常感谢!
2个回答

3

您可以重构代码,包括每个列共同的因子水平,然后进行制表。我还建议使用lapply()而不是apply(),因为apply()适用于矩阵。

df <- read.table(text = "X1   X2   X3
1 A    B    A
2 A    C    B
3 B    A    B
4 A    A    C", h=T)

do.call(
    rbind, 
    lapply(df, function(x) table(factor(x, levels=levels(unlist(df)))))
)
#    A B C
# X1 3 1 0
# X2 2 1 1
# X3 1 2 1

3
假设您的数据框是 x,我会简单地这样做:
do.call(rbind, tapply(unlist(x, use.names = FALSE),
                      rep(1:ncol(x), each = nrow(x)),
                      table))

#  A B C
#1 3 1 0
#2 2 1 1
#3 1 2 1

Benchmarking

# a function to generate toy data
# `k` factor levels
# `n` row
# `p` columns
datsim <- function(n, p, k) {
  as.data.frame(replicate(p, sample(LETTERS[1:k], n, TRUE), simplify = FALSE),
                col.names = paste0("X",1:p), stringsAsFactors = TRUE)
  }

# try `n = 100`, `p = 500` and `k = 3`
x <- datsim(100, 500, 3)

## DirtySockSniffer's answer
system.time(do.call(rbind, lapply(x, function(u) table(factor(u, levels=levels(unlist(x)))))))
#   user  system elapsed 
# 21.240   0.068  21.365 

## my answer
system.time(do.call(rbind, tapply(unlist(x, use.names = FALSE), rep(1:ncol(x), each = nrow(x)), table)))
#   user  system elapsed 
#  0.108   0.000   0.111 

Dirty的答案可以改进,方法如下:

## improved DirtySockSniffer's answer
system.time({clevels <- levels(unlist(x, use.names = FALSE));
             do.call(rbind, lapply(x, function(u) table(factor(u, levels=clevels))))})
#   user  system elapsed 
#  0.108   0.000   0.108

还需考虑user20650的回答:

## Let's try a large `n`, `p`, `k`
x <- datsim(200, 5000, 5)

system.time(t(table(stack(lapply(x, as.character)))))
#   user  system elapsed 
#  0.592   0.052   0.646 

虽然我的答案:

system.time(do.call(rbind, tapply(unlist(x, use.names = FALSE), rep(1:ncol(x), each = nrow(x)), table)))
#   user  system elapsed 
#  1.844   0.056   1.904 

改进了Dirty的答案,它能够:

system.time({clevels <- levels(unlist(x, use.names = FALSE));
             do.call(rbind, lapply(x, function(u) table(factor(u, levels=clevels))))})
#   user  system elapsed 
#  1.240   0.012   1.263 

嗨哲远,这不是很重要,但在我的笔记本电脑上,levels(u)[u]as.character 慢一些。(我认为这是有道理的,因为我确定 R 的开发人员已经对此进行了优化) - user20650
对于第二个例子,似乎更快是因为在较小的向量上调用了as.numeric,而不是整个向量。因此,如果需要转换为数字,就像你所说的那样,似乎会更快。 - user20650

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接