Необычная (для меня) структура данных: посчитайте, как часто два столбца взаимно указывают друг на друга - PullRequest
2 голосов
/ 17 октября 2019

Я работаю с набором данных, у которого есть структура, с которой я раньше не работал. У него есть этот формат (код для ввода данных в конце):

df
#>   Instance Alice_prefers Bob_prefers Charlie_prefers
#> 1        1           Bob     Charlie           Alice
#> 2        2           Bob       Alice             Bob
#> 3        3           Bob     Charlie             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(), кажется, делаетхитрость, но есть ли более простой способ, чем перечисление каждого отдельного случая, как я начал делать ниже? В моем реальном наборе данных я буду искать, чтобы у меня было намного больше людей, чем просто Алиса, Боб и Чарли.

Второй вопрос:

Когда у меня будет больше людей,например. Алиса, Боб, Чарли и Дениз, столбец Match может содержать несколько совпадений (например, совпадение Алисы и Боба и совпадение Чарли и Дениз) - каково это решение? Должен ли я иметь индикаторную переменную для каждого возможного совпадения, например, 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")
)

Ответы [ 2 ]

3 голосов
/ 17 октября 2019

У вас проблема с графиком, и это обычно означает, что вы должны использовать 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
2 голосов
/ 17 октября 2019

Вот один из подходов, когда столбцы преобразуются в более длинную форму, чтобы мы могли соединить двух людей. Это должно работать с любым количеством совпадений в одной строке.

library(tidyverse)
df %>%
  pivot_longer(-Instance, "A", values_to = "B") %>%
  mutate(A = str_remove(A, "_prefers"),
         match = if_else(A < B, paste(A,B), paste(B,A))) %>%
  count(Instance, match) %>%
  filter(n > 1)

## A tibble: 2 x 3
#  Instance match           n
#     <dbl> <chr>       <int>
#1        2 Alice Bob       2
#2        3 Bob Charlie     2
...