生成所有可能的对并计算R中的频率

时间:2017-10-03 02:05:26

标签: r dplyr

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

我想知道任何一对产品在任何类别中出现的次数。

这是我正在努力实现此目的的示例数据集:

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&amp; banana”一起出现在“las vegas,NV”的“食物”类别中,但也出现在“charlotte,NC”的“食物”类别中。因此,“apple&amp; banana”对的计数为2。

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

(无序)计数 apple&amp;香蕉

2

(无序)计数 apple&amp;梨

4

任何人都知道如何实现这一目标?相对较新的R并且已经混淆了一段时间。

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

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

3 个答案:

答案 0 :(得分:7)

以下是使用tidyversecrossprod的一种方法;通过使用spread,它将所有 item / fruit 从同一类别 - 位置组合转换为一行,其中作为标题(这要求您没有重复每个类别 - 国家/地区,否则您需要预先汇总步骤),表示存在的值; crossprod基本上评估了列对的内积,并给出了共生次数。

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

答案 1 :(得分: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

答案 2 :(得分:1)

这是一个可以满足您需求的小功能。它可以推广到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")