【问题标题】:Frequency count for Comma delimited categories within a column列中以逗号分隔的类别的频率计数
【发布时间】:2017-12-06 20:24:36
【问题描述】:

我在 R 中有一个数据框。该数据框有多行多列。其中一列具有不同汽车制造商(如丰田、本田等)的逗号分隔值。我想计算逗号分隔值的频率,并根据每行的出现频率输出前三个值。 这是数据集的样子:

Zip                   Make
12325 Toyota, Honda, Toyota, Mitsubishi, Mercedes
85271 Toyota,Honda,Toyota,Honda,Toyota,Toyota,Volvo,Nissan,Nissan,Nissan, Nissan 
56098 Toyota,Honda,Toyota,Mitsubishi,Chevrolet,Acura,Chevrolet,Chevrolet, Honda

这是我想要的输出

Output appended in the image

任何人都可以根据示例提供实际的 R 代码吗?

【问题讨论】:

  • 这不是这样做的方法。您希望您的数据格式整洁,以便您可以轻松地对其进行操作和分析。无法按照您的数据集当前的方式进行分析,尤其是您希望它出现的方式。请先提供您的数据集的dput() 并在此处发布

标签: r count frequency


【解决方案1】:

在进行任何操作和求和之前,您需要先整理数据。

您的数据集:

df <- data.frame(Zip =c(12325, 85271, 56098), Make = c("Toyota,Honda,Toyota,Mitsubishi,Mercedes", "Toyota,Honda,Toyota,Honda,Toyota,Toyota,Volvo,Nissan,Nissan,Nissan,Nissan", "Toyota,Honda,Toyota,Mitsubishi,Chevrolet,Acura,Chevrolet,Chevrolet,Honda"))

把 Make 转成 Character,这样我们就可以拆分它了

df$Make <- as.character(df$Make)

导入库

library(stringr)
library(tidyverse)

用逗号分割每一列,并创建一个不同品牌的矩阵,然后将其转换为数据框。然后命名列,并将数据框中的 zip 列分配给我们的新清理器。

df2 <- as.data.frame(str_split_fixed(df$Make, ",", max(unlist(lapply(strsplit(df$Make, ","), length)))))


names(df2) <- paste0("car_", 1:ncol(df2))

df2$zip <- df$Zip

检查:

> df2
   car_1 car_2  car_3      car_4     car_5  car_6     car_7     car_8  car_9 car_10 car_11   zip
1 Toyota Honda Toyota Mitsubishi  Mercedes                                                 12325
2 Toyota Honda Toyota      Honda    Toyota Toyota     Volvo    Nissan Nissan Nissan Nissan 85271
3 Toyota Honda Toyota Mitsubishi Chevrolet  Acura Chevrolet Chevrolet  Honda               56098

现在我们要整理一下。我们可以收集列以将其从宽格式变为长格式。然后我们用 NA 替换空,并将它们过滤掉。然后我们删除我们创建的car 列。之后,我们将按zipMakesummarise 进行分组,这将按zip 显示每个Make 的计数。最后我们按数字降序排列。

df3 <- df2 %>% 
  gather(car, Make, -zip) %>%
  replace(. == "", NA) %>%
  filter(!is.na(Make)) %>%
  select(-car) %>%
  group_by(zip, Make) %>%
  summarise(number = n()) %>%
  arrange(zip, desc(number)) 

我们要做的最后一件事是我们可以创建一个名为top3 的新df,我们可以对其进行切片以仅从每个分组(zipMake)中获取前3 行。由于我们已将其按降序排列,因此我们将使用此数据框获得按邮政编码排列的前 3 个品牌。

top3 <- df3 %>%
  slice(1:3)

【讨论】:

    【解决方案2】:

    如果您坚持使用屏幕截图所示的输出格式,这里有一个基本的 R 解决方案(无需额外的 R 库):

    df <- read.table(text =
        "Zip Make
         12325 Toyota,Honda,Toyota,Mitsubishi,Mercedes
         85271 Toyota,Honda,Toyota,Honda,Toyota,Toyota,Volvo,Nissan,Nissan,Nissan,Nissan
         56098 Toyota,Honda,Toyota,Mitsubishi,Chevrolet,Acura,Chevrolet,Chevrolet,Honda", 
         header = T, stringsAsFactors = F)
    
    df$Frequency <- sapply(lapply(strsplit(df$Make, ","), function(x)
        paste0(apply(as.data.frame(table(x)), 1, paste, collapse = "("), ")")),
        paste, collapse = ",");
    
    df$top <- sapply(lapply(strsplit(df$Make, ","), function(x)
        names(table(x)[order(-table(x))][1:3])),
        paste, collapse = ",");
    
    df;
    #    Zip
    #1 12325
    #2 85271
    #3 56098
    #                                                                       Make
    #1                                   Toyota,Honda,Toyota,Mitsubishi,Mercedes
    #2 Toyota,Honda,Toyota,Honda,Toyota,Toyota,Volvo,Nissan,Nissan,Nissan,Nissan
    #3  Toyota,Honda,Toyota,Mitsubishi,Chevrolet,Acura,Chevrolet,Chevrolet,Honda
    #                                               Frequency                    top
    #1           Honda(1),Mercedes(1),Mitsubishi(1),Toyota(2)  Toyota,Honda,Mercedes
    #2                  Honda(2),Nissan(4),Toyota(4),Volvo(1)    Nissan,Toyota,Honda
    #3 Acura(1),Chevrolet(3),Honda(2),Mitsubishi(1),Toyota(2) Chevrolet,Honda,Toyota
    

    【讨论】:

    • 我们得到了相同的结果,除了最后一列。我认为即使有平局,OP 也希望保留所有前 3 名,因此仅对表格输出进行切片可能是不够的。
    • @www 是的,你是对的。处理关系需要更多的工作。我在那里偷工减料,在代码简单性和重现 OP 的确切预期输出之间取得平衡。
    • 我认为您提供了一个很好的解决方案,我同意保持代码简单性很重要。
    【解决方案3】:

    使用tidyverse 的解决方案。 dt_final 是最终输出。

    library(tidyverse)
    
    # Separate the comma
    dt2 <- dt %>% separate_rows(Make)
    
    # Calculate the frequency
    dt3 <- dt2 %>% count(Zip, Make) 
    
    # Prepare the Frequency column
    dt4 <- dt3 %>%
      mutate(n = paste0("(", n, ")")) %>%
      unite(Frequency, Make, n, sep = " ") %>%
      group_by(Zip) %>%
      summarise(Frequency = paste0(Frequency, collapse = ", "))
    
    # Prepare the Top 3 Make column
    dt5 <- dt3 %>%
      group_by(Zip) %>%
      mutate(Rank = dense_rank(n)) %>%
      filter(Rank <= 3) %>%
      arrange(Zip, Rank, Make) %>%
      select(Zip, Make) %>%
      summarise(`Top 3 Make (per frequency)` = paste0(Make, collapse = ", "))
    
    # Join the results
    dt_final <- reduce(list(dt, dt4, dt5), left_join, by = "Zip")
    
    dt_final
    #     Zip                                                                       Make
    # 1 12325                                Toyota, Honda, Toyota, Mitsubishi, Mercedes
    # 2 85271 Toyota,Honda,Toyota,Honda,Toyota,Toyota,Volvo,Nissan,Nissan,Nissan, Nissan
    # 3 56098  Toyota,Honda,Toyota,Mitsubishi,Chevrolet,Acura,Chevrolet,Chevrolet, Honda
    #                                                         Frequency
    # 1             Honda (1), Mercedes (1), Mitsubishi (1), Toyota (2)
    # 2                    Honda (2), Nissan (4), Toyota (4), Volvo (1)
    # 3 Acura (1), Chevrolet (3), Honda (2), Mitsubishi (1), Toyota (2)
    #                    Top 3 Make (per frequency)
    # 1         Honda, Mercedes, Mitsubishi, Toyota
    # 2                Volvo, Honda, Nissan, Toyota
    # 3 Acura, Mitsubishi, Honda, Toyota, Chevrolet
    

    数据

    dt <- read.table(text = "Zip                   Make
    12325 'Toyota, Honda, Toyota, Mitsubishi, Mercedes'
                     85271 'Toyota,Honda,Toyota,Honda,Toyota,Toyota,Volvo,Nissan,Nissan,Nissan, Nissan' 
                     56098 'Toyota,Honda,Toyota,Mitsubishi,Chevrolet,Acura,Chevrolet,Chevrolet, Honda'",
                     header = TRUE, stringsAsFactors = FALSE)
    

    【讨论】:

      【解决方案4】:

      首先,请考虑@Matt W. 关于以更恰当的方式组织数据框的评论,在单元格中包含列表通常是糟糕设计的征兆。

      尽管如此,让我们假设您的数据框是 df,那么:

      # iterate over all the cells in the Make column of the dataframe
      top3 <- unlist(lapply(df$Make, function(cell) {
        # convert the cell string to a vector
        cars <- unlist(strsplit(cell, ','))
        # count how many of each different type are
        carFreq <- unlist(lapply(unique(cars), function(c) sum(c == cars)))
        # fix the name of carFreq
        names(carFreq) <- unique(cars)
        # return a string with the top 3
        paste0(names(sort(carFreq, decreasing = TRUE)[1:3]), collapse = ', ')
      }))
      
      # finally put the extra column in the data frame    
      df <- cbind(df, top3)
      

      不需要图书馆。

      【讨论】:

      • 谢谢大家。我实际上也能够仅使用 sqldf 包来解决它。从我这里添加代码:
      【解决方案5】:

      感谢大家分享答案。实际上,我也可以通过使用 sqldf 包来解决它。 BB2 是包含 zip 和 Concatenated make 列的原始数据集。这是代码

      D1 查看(BB2)
      D2 查看(D2)
      D3 查看(D3)
      D4 D4 D4_x 查看(D4_x)
      Final

      【讨论】:

      • 如果您将每行缩进 4 个空格,那么 SO 将为您设置格式。
      猜你喜欢
      • 2020-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-24
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2020-04-26
      相关资源
      最近更新 更多