首页 > 解决方案 > 不寻常的(对我而言)数据结构:计算两列相互指向彼此的频率

问题描述

我正在使用具有我以前从未使用过的结构的数据集。它具有这种格式(最后输入数据的代码):

df
#>   Instance Alice_prefers Bob_prefers Charlie_prefers
#> 1        1           Bob     Charlie           Alice
#> 2        2           Bob       Alice             Bob
#> 3        3           Bob     Charlie             Bob

我正在尝试创建一个变量来显示两个人何时“匹配”,即当他们都喜欢彼此时,例如,如果 Alice 更喜欢 Bob,而 Bob 也更喜欢 Alice——这是一个相互匹配:Alice 和 Bob 匹配。

所以我想要的输出是:

output
#>   Instance Alice_prefers Bob_prefers Charlie_prefers      Match
#> 1        1           Bob     Charlie           Alice       <NA>
#> 2        2           Bob       Alice             Bob   AliceBob
#> 3        3           Bob     Charlie             Bob BobCharlie

使用case_when()似乎可以解决问题,但是有没有比列出每个案例更简单的方法,就像我在下面开始做的那样?在我的真实数据集中,我希望有更多的人,而不仅仅是 Alice、Bob 和 Charlie。

第二个问题:

当我有更多的人时,例如。Alice、Bob、Charlie 和 Denise,该Match列可能包含多个匹配项(例如,Alice 和 Bob 匹配,Charlie 和 Denise 匹配)-- 整洁的解决方案是什么?我是否应该为每个可能的匹配设置一个指标变量,例如 AliceBob、AliceCharlie 等,它的值为 1 或 0?

我这样做的原因是:我希望能够快速查看比赛的数量以及参加比赛的人。

#df <- df %>% mutate(Match = ifelse(Alice_prefers=="Bob" & Bob_prefers =="Alice", "AliceBob", NA))
df <- df %>% mutate(
      Match = case_when(
        (Alice_prefers=="Bob" & Bob_prefers=="Alice") ~  "AliceBob",
        (Charlie_prefers=="Bob" & Bob_prefers=="Charlie") ~  "BobCharlie"
                       )
                    )
df

数据输入代码:

df <- data.frame(stringsAsFactors=FALSE,
          Instance = c(1, 2, 3),
     Alice_prefers = c("Bob", "Bob", "Bob"),
       Bob_prefers = c("Charlie", "Alice", "Charlie"),
   Charlie_prefers = c("Alice", "Bob", "Bob")
)

标签: rdplyrtidyverse

解决方案


你有一个图形问题,这通常意味着你应该使用 igraph。您正在寻找有向图中的相互边。

我使用 data.table 进行分组,但如果您愿意,也可以使用 tidyverse。

首先,您应该将数据重塑为整洁(长)格式。这也是整洁的输出格式。

names(df) <- gsub("_prefers", "", names(df), fixed = TRUE)
library(reshape2)
mdf <- melt(df, id.vars = "Instance")
#  Instance variable   value
#1        1    Alice     Bob
#2        2    Alice     Bob
#3        3    Alice     Bob
#4        1      Bob Charlie
#5        2      Bob   Alice
#6        3      Bob Charlie
#7        1  Charlie   Alice
#8        2  Charlie     Bob
#9        3  Charlie     Bob

library(data.table)
setDT(mdf) #for group-by

library(igraph)
mdf[, Match := {
  #turn subsets into graphs
  g <- graph_from_data_frame(.SD[, .(variable, value)]) 
  #initialize result
  res <- character(.N)
  #find reciproc pairs
  m <- which_mutual(g)
  #I'd probably just return m
  #but just for fun,
  #get names of reciproc pairs
  res[m] <- attr(E(g)[m], "vnames")
  res
}, by = Instance]

#   Instance variable   value       Match
#1:        1    Alice     Bob            
#2:        2    Alice     Bob   Alice|Bob
#3:        3    Alice     Bob            
#4:        1      Bob Charlie            
#5:        2      Bob   Alice   Bob|Alice
#6:        3      Bob Charlie Bob|Charlie
#7:        1  Charlie   Alice            
#8:        2  Charlie     Bob            
#9:        3  Charlie     Bob Charlie|Bob

如果你只想要计数,那就更容易了:

mdf[, .(count = {
  #turn subsets into graphs
  g <- graph_from_data_frame(.SD[, .(variable, value)]) 
  reciprocity(g) * .N
}), by = Instance]
#   Instance count
#1:        1     0
#2:        2     2
#3:        3     2

推荐阅读