【问题标题】:More efficient way to get frequency counts across columns of data frame获取跨数据框列的频率计数的更有效方法
【发布时间】:2017-06-16 04:11:47
【问题描述】:

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

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 或 apply 或更快的东西来做到这一点?

目前的解决方案:

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])

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

提前致谢!

【问题讨论】:

  • lapply(df, function(x) prop.table(table(x)))

标签: r dplyr


【解决方案1】:

因为您在每个项目中都有相同的值范围#您可以使用

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)))

【讨论】:

  • 如果您尝试在早期阶段处理这些问题,那将是最简单的。例如,在读入时,使用read.table 设置na.strings=c("", " ")。或者您可以将这些空白值设置为缺少 df[df == ""] &lt;- NA ...
  • 我删除了那个让你看起来像是在自言自语的问题,哈哈,抱歉。但基本上我做了 sapply(df, function(x) prop.table(table(x,exclude=""))) 来控制我的数据中的空白
  • 好东西。我确实认为最好在早期阶段尝试解决这些问题,因为随着您的分析变得更加复杂,处理它们将变得更加困难。
  • 非常正确。是否有一个 na.rm 类型的函数可以在所有列中从我的 df 中取出所有空白?每列都有相同数量的空白,因为每个人都被询问了相同数量的项目,但项目轮换了......我不需要为行保留用户 ID 或任何内容。
  • 啊,好吧。好吧,如果您被他们困住,请将它们转换为真正的 NA(整圈)
【解决方案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】:

    使用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
    

    【讨论】:

    • 谢谢!非常有帮助。不过,我有超过 3 列。可能会做类似“for (col in colnames(df)){results=cbind(results, data.frame(df %>% group_by(col) ..... ” 除非有更好的方法吗?
    • 马特!不要你认为 apply 会有帮助。抄送:@SarahCummings
    【解决方案4】:

    我建议使用不同的数据组织方式,使用因子水平来区分项目。这使得处理数据变得容易。我将使用gather函数转换您的数据,然后使用summary计算频率百分比:

    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 库,因为它同时加载两个包。

    编辑:如果你想使用保持频率作为列,你可以使用传播这样做:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-22
      • 1970-01-01
      • 1970-01-01
      • 2019-08-02
      相关资源
      最近更新 更多