更有效地获取跨数据框列的频率计数

时间:2017-06-15 19:28:20

标签: r dplyr

我有一些调查数据,其中列对应于项目,而行对应于客户说明他们购买每件商品的可能性。看起来像这样:

item1 = c("Likely", "Unlikely", "Very Likely","Likely") 
item2 = c("Likely", "Unlikely", "Very Likely","Unlikely")
item3 = c("Very Likely", "Unlikely", "Very Likely","Likely") 
df = data.frame(item1, item2, item3) 

我想要一个汇总表,给出每个项目的每个响应的百分比。现在我在每个列上使用table()进行此过程,并且需要操作很多代码。我怎样才能使用plyr或者应用更快的东西?

目前的解决方案:

d1<-as.data.frame(table(df$item1))
d1$item1_percent<- d1$Freq/sum(d1$Freq)
names(d1)<-c("Response","item1_freqs","item1_percent")

d2<-as.data.frame(table(df$item2))
d2$item2_percent<- d2$Freq/sum(d2$Freq)
names(d2)<-c("Response","item2_freqs","item2_percent")

d3<-as.data.frame(table(df$item3))
d3$item3_percent<- d3$Freq/sum(d3$Freq)
names(d3)<-c("Response","item3_freqs","item3_percent")

results<-cbind(d1,d2[,2:3],d3[,2:3])

注意我并不需要频率计数,只需要百分比。

提前致谢!

4 个答案:

答案 0 :(得分:5)

由于每个项目中的值范围相同,您可以使用

sapply(df, function(x) prop.table(table(x)))
#             item1 item2 item3
# Likely       0.50  0.25  0.25
# Unlikely     0.25  0.50  0.25
# Very Likely  0.25  0.25  0.50

但如果它们不同,您可以将每个项目#设置为具有一组共同的级别

df[] <- lapply(df, factor, levels=unique(unlist(df)))
sapply(df, function(x) prop.table(table(x)))

答案 1 :(得分:2)

使用dplyr

results = data.frame(df %>% 
                     group_by(item1) %>% 
                             summarise(no_rows=length(item1)/nrow(df)))
results = cbind(results,
          data.frame(df %>%  
                     group_by(item2) %>%
                             summarise(no_rows=length(item2)/nrow(df))))

results = cbind(results,
          data.frame(df %>% 
                     group_by(item3) %>% 
                             summarise(no_rows=length(item3)/nrow(df))))


# > results
#        item1 no_rows       item2 no_rows       item3 no_rows
# 1      Likely    0.50      Likely    0.25      Likely    0.25
# 2    Unlikely    0.25    Unlikely    0.50    Unlikely    0.25
# 3 Very Likely    0.25 Very Likely    0.25 Very Likely    0.50

答案 2 :(得分:2)

考虑与Reduce的链合并,您首先使用lapply按数字循环遍历数据框的每一列,以构建数据框列表,然后将其传递到上的merge响应

dfList <- lapply(seq_along(df), function(i){      
  d <- as.data.frame(table(df[,i]))
  d$item1_percent <- d$Freq/sum(d$Freq)
  # PASS COLUMN NUMBER INTO DF COLUMN NAMES
  names(d) <- c("Response", paste0("item",i,"_freqs"), paste0("item",i,"_percent"))

  return(d)      
})

results2 <- Reduce(function(x,y) merge(x, y, by="Response", all.equal=TRUE), dfList)

# EQUIVALENT TO ORIGINAL results
all.equal(results, results2)
# [1] TRUE
identical(results, results2)
# [1] TRUE

答案 3 :(得分:2)

我建议使用不同的方式组织数据,使用因子级别来区分项目。这样可以轻松处理数据。我将使用收集功能转换您的数据,然后使用汇总来计算频率百分比:

library(tidyverse)

results <- df %>% 
 gather("item", "likelihood") %>% 
 group_by(item, likelihood) %>% 
 summarise(n = n() ) %>% 
 mutate(freq = n / sum(n))

# > results
#  A tibble: 9 x 4
#  Groups:   item [3]
#    item  likelihood     n  freq
#   <chr>       <chr> <int> <dbl>
# 1 item1      Likely     2  0.50
# 2 item1    Unlikely     1  0.25
# 3 item1 Very Likely     1  0.25
# 4 item2      Likely     1  0.25
# 5 item2    Unlikely     2  0.50
# 6 item2 Very Likely     1  0.25
# 7 item3      Likely     1  0.25
# 8 item3    Unlikely     1  0.25
# 9 item3 Very Likely     2  0.50

我为此使用了dplyr和broom,但我更喜欢使用tidyverse库,因为它会同时加载两个包。

编辑:如果要使用将频率保留为列,可以使用spread来执行此操作:

col_results <- results %>% 
  select(-n) %>% 
  spread(item, freq)

# > col_results
# A tibble: 3 x 4
#   likelihood item1 item2 item3
# *       <chr> <dbl> <dbl> <dbl>
# 1      Likely  0.50  0.25  0.25
# 2    Unlikely  0.25  0.50  0.25
# 3 Very Likely  0.25  0.25  0.50