基于计数创建一个汇总数据框。

3
我将尝试使用数据框创建第二个摘要计数数据框,原始数据的结构如下:
mydata <- read.table(header=TRUE, text="
item    type    store1  store2  store3  store4  store5
chair   timber  0   1   4   0   6
chair   metal   0   1   4   1   9
chair   upholstered 3   0   0   1   1
table   indoor  1   8   0   1   0
table   outdoor 1   12  2   1   0
bed single  0   0   2   1   0
bed double  0   1   1   1   0
bed queen   1   0   0   1   3
bed king    5   0   1   3   0")

我希望我的汇总数据框能够计算每个商店所拥有的家具类型,并给出每个商店库存的摘要(仅包含有或无的信息,而不是数量)。它应该像这样:
summary <- read.table(header=TRUE, text="
store   chair_types table_types bed_types   total_types
store1  1   2   2   5
store2  2   2   1   5
store3  2   1   3   6
store4  2   2   4   8
store5  3   0   1   4")

这个在Excel里很容易实现,但我想去学习如何用正确的方式来做这件事。如果这是一个重复问题,我道歉了,因为我没有找到类似的例子。先提前谢谢你。

2个回答

3
我们可以使用 `dplyr/tidyr` 来完成这个操作。首先按 'item' 进行分组,然后循环遍历 'store' 列(使用 `summarise_each`),获取每个 'store' 列中非零元素的数量(使用 `sum(.!=0`),转换为 'long' 格式(使用 `gather`),将子字符串 '_types' 添加到 'item' 中(使用 `paste`),将 'long' 格式转换为 'wide' 格式(使用 `spread`),并使用 `rowSums` 创建一个 'total' 列。
library(dplyr)
library(tidyr)
mydata %>% 
     group_by(item) %>%
     summarise_each(funs(sum(.!=0)), store1:store5) %>% 
     gather(store, val, store1:store5) %>% 
     mutate(item = paste0(item, "_types")) %>%
     spread(item, val) %>%
     mutate(total = rowSums(.[-1]))
#   store bed_types chair_types table_types total
#   <chr>     <int>       <int>       <int> <dbl>
#1 store1         2           1           2     5
#2 store2         1           2           2     5
#3 store3         3           2           1     6
#4 store4         4           2           2     8
#5 store5         1           3           0     4

这也可以通过先转换为“长”格式,按“项目”、“店铺”分组,获取非零元素数量(summarise),按“店铺”分组,通过将“val”相加创建“总计”列,然后进行spread操作来完成。

mydata %>% 
     gather(store, val, store1:store5) %>%
     group_by(item, store) %>% 
     summarise(val = sum(val!=0)) %>% 
     group_by(store) %>% 
     mutate(Total = sum(val)) %>% 
     spread(item, val)

我们也可以使用base R轻松完成这个任务,只需使用rowsumaddmargins函数。

addmargins(t(rowsum(+(mydata[-(1:2)]!=0), mydata[,1])), 2)
#       bed chair table Sum
#store1   2     1     2   5
#store2   1     2     2   5
#store3   3     2     1   6
#store4   4     2     2   8
#store5   1     3     0   4

我写了以下代码 group_by(mydata, item) %>% summarize_if(is.numeric, sum(.!=0)) 代替你的 summarize_each 代码。我以为这会起作用,但是我收到了以下错误信息:Error in UseMethod("as.fun_list") : no applicable method for 'as.fun_list' applied to an object of class "c('integer', 'numeric')" 有什么想法吗? - jazzurro
1
太好了,akrun。非常感谢你的帮助。 - setbackademic
@jazzurro 我发现这个代码可以运行 mydata %>% mutate_each(funs(.!=0), store1:store5) %>% group_by(item) %>% summarise_if(is.logical, sum) - akrun
@akrun 很有趣。这肯定需要更多的工作,不是吗? - jazzurro
@alistaire 我明白了。那么,在这种情况下,每次都写funs可能会避免这种问题吗? - jazzurro
显示剩余4条评论

3
你可以在R语言的基本R stats 包中使用R函数 aggregate 来实现你想要的核心操作。
> aggregated <- aggregate(mydata[grep("store",names(mydata))], 
                          by = mydata["item"], 
                          FUN = function(x) sum(x != 0))
> aggregated
   item store1 store2 store3 store4 store5
1   bed      2      1      3      4      1
2 chair      1      2      2      2      3
3 table      2      2      1      2      0

第一个参数mydata[grep("store",names(mydata))]是从您的数据框中选择“stores”。第二个参数by = mydata["item"]表示您想使用“item”来标识数据框中的组。最后,FUN = function(x) sum(x != 0)表示您想计算每个项目在每个店铺列中的非零元素数量。
这可能已经足够了,但如果您想更改格式以使其与上面的内容更相似,则可以执行以下操作:
> summary <- as.data.frame(t(aggregated[-1]))
> names(summary) <- aggregated[[1]]
> summary[["total"]] <- rowSums(summary)
> summary
       bed chair table total
store1   2     1     2     5
store2   1     2     2     5
store3   3     2     1     6
store4   4     2     2     8
store5   1     3     0     4

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