在R中创建多个因子列的频率表

8

我是一个R语言的初学者。我正在为自己的工作编写一份有关常见函数/特性语法的单独手册。我的样本数据框如下:

x.sample <-
structure(list(Q9_A = structure(c(5L, 3L, 5L, 3L, 5L, 3L, 1L, 
5L, 5L, 5L), .Label = c("Impt", "Neutral", "Not Impt at all", 
"Somewhat Impt", "Very Impt"), class = "factor"), Q9_B = structure(c(5L, 
5L, 5L, 3L, 5L, 5L, 3L, 5L, 3L, 3L), .Label = c("Impt", "Neutral", 
"Not Impt at all", "Somewhat Impt", "Very Impt"), class = "factor"), 
Q9_C = structure(c(3L, 5L, 5L, 3L, 5L, 5L, 3L, 5L, 5L, 3L
), .Label = c("Impt", "Neutral", "Not Impt at all", "Somewhat Impt", 
"Very Impt"), class = "factor")), .Names = c("Q9_A", "Q9_B", 
"Q9_C"), row.names = c(NA, 10L), class = "data.frame")

> x.sample
          Q9_A            Q9_B            Q9_C
1        Very Impt       Very Impt Not Impt at all
2  Not Impt at all       Very Impt       Very Impt
3        Very Impt       Very Impt       Very Impt
4  Not Impt at all Not Impt at all Not Impt at all
5        Very Impt       Very Impt       Very Impt
6  Not Impt at all       Very Impt       Very Impt
7             Impt Not Impt at all Not Impt at all
8        Very Impt       Very Impt       Very Impt
9        Very Impt Not Impt at all       Very Impt
10       Very Impt Not Impt at all Not Impt at all

我的原始数据框有21列。

如果我想找到平均值(将其视为序数变量):

> sapply(x.sample,function(x) mean(as.numeric(x), na.rm=TRUE))
Q9_A Q9_B Q9_C 
 4.0  4.2  4.2

我想为数据框中的所有变量制作频率表。我在互联网和许多论坛上搜索发现,最接近实现这一功能的命令是使用 sapply。但是当我使用它时,结果都是0。

> sapply(x.sample,function(x) table(factor(x.sample, levels=c("Not Impt at all", "Somewhat Impt",            "Neutral", "Impt", "Very Impt"), ordered=TRUE)))
                Q9_A Q9_B Q9_C
Not Impt at all    0    0    0
Somewhat Impt      0    0    0
Neutral            0    0    0
Impt               0    0    0
Very Impt          0    0    0

问题 如何利用sapply将数据框中所有列(即为因子)制作类似上表的频率统计表格?

PS 如果这看起来太琐碎,我很抱歉。我已经搜索了2天,仍然找不到答案,尝试了所有可能的组合。也许我没有足够努力去寻找 =(

非常感谢。


2
sapply(x.sample, table) 行不行? - Rich Scriven
@RichardScriven - 差不多就是这样。它可以完成所有操作,但无法按照“因子”排序重新排列输出。 - thelatemail
@Richard Scriven:我尝试了你的方法,但是它返回了一个错误:“无效的大小参数”。 - Raphael Lee
@RaphaelLee - 它绝对有效。尝试使用你在问题中提到的代码,使用自己的 x.sample 进行测试。 - thelatemail
3个回答

10
你已经接近成功了。只需要在你的函数中进行一个小改动就可以了。在 function(x) ... 中的 x 需要传递到 table() 调用中:
levs <- c("Not Impt at all", "Somewhat Impt", "Neutral", "Impt", "Very Impt")
sapply(x.sample, function(x) table(factor(x, levels=levs, ordered=TRUE)))

一些代码的微调可能会使它更易于阅读:
sapply(lapply(x.sample,factor,levels=levs,ordered=TRUE), table)

#                Q9_A Q9_B Q9_C
#Not Impt at all    3    4    4
#Somewhat Impt      0    0    0
#Neutral            0    0    0
#Impt               1    0    0
#Very Impt          6    6    6

我知道我不允许说“谢谢”,但是你节省了我大量在互联网上无尽搜索的精力。无论如何,一个懒惰的替代方法是使用以下代码:levs <- levels(x.sample$Q9_A) 来代替键入所有级别。 - Raphael Lee
1
@RaphaelLee - 你肯定可以说“谢谢”。点赞好的答案并接受适合你的答案也是良好的礼仪。此外,当你接受一个答案时,你也会获得声望。 - Rich Scriven

9

稍晚了一点,不过这里提供一个 reshape2 的解决方案。如果使用 recast 将会非常简单,但是我们需要处理空的因子级别,所以需要在 melt 中同时指定 factorsAsStrings = FALSE ,在 dcast 中指定 drop = FALSE ,而 recast 无法将参数传递给 melt(仅能传递给 dcast),所以就这样操作吧。

library(reshape2)
x.sample$indx <- 1 
dcast(melt(x.sample, "indx", factorsAsStrings = FALSE), value ~ variable, drop = FALSE)
#             value Q9_A Q9_B Q9_C
# 1            Impt    1    0    0
# 2         Neutral    0    0    0
# 3 Not Impt at all    3    4    4
# 4   Somewhat Impt    0    0    0
# 5       Very Impt    6    6    6

如果我们不关心空级别,一个快速的解决方案就是:
recast(x.sample, value ~ variable, id.var = "indx")
#             value Q9_A Q9_B Q9_C
# 1            Impt    1    0    0
# 2 Not Impt at all    3    4    4
# 3       Very Impt    6    6    6

如果速度是一个问题,我们可以使用data.atble来完成同样的操作。

library(data.table)
dcast(melt(setDT(x.sample), measure.vars = names(x.sample), value.factor = TRUE), 
           value ~ variable, drop = FALSE)
#              value Q9_A Q9_B Q9_C
# 1:            Impt    1    0    0
# 2:         Neutral    0    0    0
# 3: Not Impt at all    3    4    4
# 4:   Somewhat Impt    0    0    0
# 5:       Very Impt    6    6    6

5
为什么不直接这样做:
> sapply(x.sample, table)
                Q9_A Q9_B Q9_C
Impt               1    0    0
Neutral            0    0    0
Not Impt at all    3    4    4
Somewhat Impt      0    0    0
Very Impt          6    6    6

让我们称其为“tbl”。
tbl[ order(match(rownames(tbl), c("Not Impt at all", "Somewhat Impt", 
                                  "Neutral", "Impt", "Very Impt")) )   , ]
                Q9_A Q9_B Q9_C
Not Impt at all    3    4    4
Somewhat Impt      0    0    0
Neutral            0    0    0
Impt               1    0    0
Very Impt          6    6    6

这将始终返回一个矩阵。只有在每列的级别数量和名称相同的情况下,才能正常工作。 - IRTFM
是的,是我的语法错误。非常感谢你! - Raphael Lee
我本意是写成它 不会 总是返回一个矩阵。 - IRTFM
1
当然这个方法可行,但我认为 OP 特别想要重新排列结果。至于是否应该先通过修改数据框本身来完成这一点,这是值得商榷的。 - thelatemail
我了解你的意思。也许可以通过将行名与 c("Not Impt at all", "Somewhat Impt", "Neutral", "Impt", "Very Impt") 相匹配来实现吗?只要提供级别,我认为不需要“有序”属性。 - IRTFM

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