【问题标题】:Generate all possible pairs and count frequency in R在 R 中生成所有可能的对和计数频率
【发布时间】:2017-10-03 02:05:26
【问题描述】:

我有一个产品数据框(苹果、梨、香蕉),在不同类别(食品和食品)的不同地点(城市)销售。

我想计算任何给定的一对产品在任何类别中一起出现的次数。

这是我正在尝试使用的示例数据集:

category <- c('food','food','food','food','food','food','edibles','edibles','edibles','edibles', 'edibles')
location <- c('houston, TX', 'houston, TX', 'las vegas, NV', 'las vegas, NV', 'philadelphia, PA', 'philadelphia, PA', 'austin, TX', 'austin, TX', 'charlotte, NC', 'charlotte, NC', 'charlotte, NC')
item <- c('apple', 'banana', 'apple', 'pear', 'apple', 'pear', 'pear', 'apple', 'apple', 'pear', 'banana')

food_data <- data.frame(cbind(category, location, item), stringsAsFactors = FALSE)

例如,“apple & banana”这对组合出现在“las vegas, NV”的“food”类别中,但也出现在“charlotte, NC”的“edibles”类别中。因此,“苹果和香蕉”对的计数为 2。

我想要的输出是这样的对数:

苹果和香蕉的(无序)计数

2

apple & pear

的(无序)计数

4

有人知道如何实现这一点吗?对 R 来说相对较新,已经困惑了一段时间。

我正在尝试使用它来计算不同项目之间的亲和力。

关于输出的额外说明: 我的完整数据集包含数百个不同的项目。想要得到一个数据框,其中第一列是对,第二列是每对的计数。

【问题讨论】:

  • 避免所有重塑这可以简化为crossprod(table(food_data[-1]))。然后,如果需要,您可以使用 diag 将对角线设置为零

标签: r dplyr


【解决方案1】:

这是使用tidyversecrossprod 的一种方式;通过使用spread,它将同一类别位置组合中的所有 item/fruit 转换为以 item 作为标题的一行(这要求您没有重复的 item 在每个类别国家中,否则您需要一个预聚合步骤),值表示存在; crossprod 本质上评估成对的 items 列的内积并给出共现的数量。

library(tidyverse)
food_data %>% 
    mutate(n = 1) %>% 
    spread(item, n, fill=0) %>% 
    select(-category, -location) %>% 
    {crossprod(as.matrix(.))} %>% 
    `diag<-`(0)

#       apple banana pear
#apple      0      2    4
#banana     2      0    1
#pear       4      1    0

要将其转换为数据框:

food_data %>% 
    mutate(n = 1) %>% 
    spread(item, n, fill=0) %>% 
    select(-category, -location) %>% 
    {crossprod(as.matrix(.))} %>% 
    replace(lower.tri(., diag=T), NA) %>%
    reshape2::melt(na.rm=T) %>%
    unite('Pair', c('Var1', 'Var2'), sep=", ")

#           Pair value
#4 apple, banana     2
#7   apple, pear     4
#8  banana, pear     1

【讨论】:

  • 这看起来很整洁!在我的实际数据集中,我有数百个不同的项目。有没有一种简单的方法可以将此矩阵折叠到一个数据框中,其中第一列是该对的名称,第二列是该对的计数?
  • 链接%&gt;% as.data.frame.table() 应该这样做,如果您不关心以交换顺序重复的对。
  • 我认为链接%&gt;% as.data.frame.table() 只会将矩阵转换为数据框。我想要第 1 行的输出:apple,banana,2;第 2 行:苹果、梨、4
  • 我最初弄错了。应该是 as.data.frame.table 而不是 as.data.frame
【解决方案2】:

来自tidyverse 的解决方案。这个想法是创建food_data2,这是food_data 的宽格式。之后,创建每个唯一项目之间的组合并使用map2_int 循环遍历每个项目组合以计算数量。此解决方案适用于任意数量的项目。

library(tidyverse)

food_data2 <- food_data %>%
  mutate(count = 1) %>%
  spread(item, count, fill = 0) 

food_combination <- food_data %>%
  pull(item) %>%
  unique() %>%
  combn(2) %>%
  t() %>%
  as_data_frame() %>%
  mutate(count = map2_int(V1, V2, 
                         ~sum(apply(food_data2 %>% select(.x, .y), 1, sum) == 2)))

# View the result
food_combination
# A tibble: 3 x 3
      V1     V2 count
   <chr>  <chr> <int>
1  apple banana     2
2  apple   pear     4
3 banana   pear     1

如果你只想一栏显示最后的项目组合,你可以进一步使用unite函数。

food_combination2 <- food_combination %>%
  unite(Pair, V1, V2)

# View the result
food_combination2
# A tibble: 3 x 2
          Pair count
*        <chr> <int>
1 apple_banana     2
2   apple_pear     4
3  banana_pear     1

【讨论】:

  • 在我的真实数据集中,我有数百个不同的项目,所以我无法像这样手动生成对名称:/
  • @HarryM 感谢您的评论。我已经更新了我的答案,它应该适用于任意数量的项目,而无需手动指定列名。
【解决方案3】:

这是一个小功能,可以满足您的需求。它可以通过dplyr:: 评估系统described here 推广到任意分组列。可能是更好的方法,但这有效:p

评论/解释是内联的~~

library("dplyr")

# a function to apply to `food_data` from the original post 
count_combos <- function(df, group_col1, group_col2, count_col){ 

  # use `combn()` to get all the unique pairs from the `$items` col
  combos <- t(combn(sort(unique(df[[count_col]])), 2)) %>% 
    as_data_frame() %>% 
    # initialize an empty column to catch the counts 
    mutate(count=NA)

  # create a new df from the colnames passed as args, 
  # (it would be more general to just use the dplyr evaluation system (@_@))
  df <- data_frame(
    group_col1 = df[[group_col1]],
    group_col2 = df[[group_col2]],
    count_col  = df[[count_col]]
  )
  # for each combo of the grouping vars, get a pipe-seperated string of items
  df <- df %>% 
    group_by(group_col1, group_col2) %>% summarize(
      items = paste(unique(count_col), collapse="|")
    ) %>% ungroup()

  # for each item pair/combo, get the number of rows of `df` with both items 
  combos$count <- sapply(1:nrow(combos), function(x){
    sum(grepl(combos$V1[x], df$items) & grepl(combos$V2[x], df$items))
  })
  # and return it in a nice df
  return(combos)
}

# apply the function 
count_combos(food_data, 
             group_col1="category", group_col2="location", count_col="item")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-24
    • 1970-01-01
    • 2022-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多