Сопоставление людей из 2 групп на основе наибольшего количества сходств в трех категориях

#r #matching

#r #сопоставление

Вопрос:

Это мой первый вопрос о переполнении стека, так что потерпите меня.

Я надеюсь сопоставить людей из группы 1 в группу 2 на основе наибольшего количества сходств по трем категориям («Предметы», «Сектора» и «География»). Я привел пример того, что я ищу ниже:

 group   Name       Subjects                            Sectors  Geography  
1       Hannah     Science, Fisheries, Policy          F, S     North
1       Zach       Policy, Energy, Marine              S, N     South   
2       Chelsea    Energy, Marine, Fisheries           S, N     South
2       Titus      Science, Fisheries, Communication   F, S, N  West

#Matches
Hannah:Titus
Zach:Chelsea
 

Я безуспешно искал в Интернете какие-либо примеры того, как выполнить этот тип сопоставления с использованием R. Самым близким, что я нашел, был алгоритм знакомств (https://algorithmia.com/algorithms/matching/DatingAlgorithm ) но у него есть ограничения, которые не позволяют мне редактировать их примерные данные. У меня есть некоторый, но не большой опыт в R, поэтому любые рекомендации (особенно базовые) будут оценены. Рад уточнить, если это необходимо. Спасибо!

Ответ №1:

Функция r find.matches выглядит так, как будто ее можно использовать для решения проблемы этого типа: «Сравнивает каждую строку в x со всеми строками в y, находя строки в y со всеми столбцами в пределах допуска значений заданной строки x»

Документация для функции включает в себя этот пример кода … (для ваших целей вы будете указывать свои текстовые значения для x и y, а не числовые значения)

https://www.rdocumentation.org/packages/Hmisc/versions/4.4-1/topics/find.matches

 y <- rbind(c(.1, .2),c(.11, .22), c(.3, .4), c(.31, .41), c(.32, 5))
x <- rbind(c(.09,.21), c(.29,.39))
y
x
w <- find.matches(x, y, maxmatch=5, tol=c(.05,.05))


set.seed(111)       # so can replicate results
x <- matrix(runif(500), ncol=2)
y <- matrix(runif(2000), ncol=2)
w <- find.matches(x, y, maxmatch=5, tol=c(.02,.03))
w$matches[1:5,]
w$distance[1:5,]
# Find first x with 3 or more y-matches
num.match <- apply(w$matches, 1, function(x)sum(x > 0))
j <- ((1:length(num.match))[num.match > 2])[1]
x[j,]
y[w$matches[j,],]
 

Для более надежной обработки возможностей сопоставления — вы можете изучить optmatch и RITools —

https://cran.r-project.org/web/packages/optmatch/index.html

https://cran.r-project.org/web/packages/RItools/index.html

который обсуждается в этой статье

https://cran.r-project.org/web/packages/optmatch/vignettes/fullmatch-vignette.pdf

Кроме того, вы можете найти интересующие вас статьи Джасджита С. Сехона (программное обеспечение для сопоставления многомерных показателей и склонностей для причинно-следственного вывода) — используя его пакет R Matching:

https://cran.r-project.org/web/packages/Matching/Matching.pdf

http://sekhon.berkeley.edu/papers/MatchingJSS.pdf

http://sekhon.berkeley.edu/matching/Match.html

http://sekhon.berkeley.edu/matching/

Ответ №2:

Вероятно, это не самое эффективное решение, оно, вероятно, не будет работать, если количество записей станет большим.

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

 library(purrr)

distance <- function(x, y){
  dist_subjects <- length(intersect(x$lSubjects[[1]], y$lSubjects[[1]]))
  dist_sectors <- length(intersect(x$lSectors[[1]], y$lSectors[[1]]))
  dist_geography <- sum(x$Geography == y$Geography)
  sum(dist_subjects, dist_sectors, dist_geography)
}

psort <- function(a, b){
  # parallel sort each pair from 2 vectors and paste them together in order
  out <- ifelse(a < b, paste0(a,":",b), paste0(b,":",a))
  out
}


# format as list for convenience
df$lSubjects <- strsplit(df$Subjects, ", ")
df$lSectors <- strsplit(df$Sectors, ", ")



all_pairs <- expand.grid(first = transpose(df),
            second = transpose(df))

# filter out the pairs of someone with themselves
all_pairs <- all_pairs[!map2_lgl(all_pairs$first, all_pairs$second,
                                 ~ .x$Name == .y$Name),]
# filter out duplicate pairs (same names in different order)
all_pairs$pair_name <- map2_chr(all_pairs$first, all_pairs$second, ~psort(.x$Name,.y$Name))
all_pairs <- all_pairs[! duplicated(all_pairs$pair_name), ]

setNames(map2_int(all_pairs$first, all_pairs$second, distance),
          all_pairs$pair_name)
#>    Hannah:Zach Chelsea:Hannah   Hannah:Titus   Chelsea:Zach     Titus:Zach 
#>              0              0              2              2              0 
#>  Chelsea:Titus 
#>              0