#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
Ответ №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