Сопоставление двух R фреймов данных по близости - PullRequest
0 голосов
/ 16 января 2019

У меня есть два следующих фрейма данных:

df1 <- data.frame(group = rep("A", 5),
                  name = c("Brandon",
                           "Kyler",
                           "Trent",
                           "Lesa",
                           "Michael"),
                  gender = c("M", "F", "M", "F", "M"),
                  days = c(50, 45, 32, 60, 48))

df2 <- data.frame(group = rep("B", 10),
                  name = c("Erica", 
                           "Jared",
                           "Sara",
                           "Helen",
                           "Tom",
                           "Ron",
                           "Cy",
                           "Lynn",
                           "Ken",
                           "Judy"),
                  gender = c("F", "M", "F", "F", "M", "M", "M", "F", "M", "F"),
                  days = c(47, 49, 62, 80, 74, 30, 55, 58, 63, 25))

Я хочу отфильтровать df2, чтобы включить только самое близкое соответствие каждой строке в фрейме данных df1 на основе gender и days, с приоритетом gender.

Например, в df1 «Брэндон» имеет gender == M и days == 50.Когда мы смотрим только gender == M в df2, мы видим, что «Джаред» является самым близким к «Брэндону» в днях, поэтому «Джаред» будет выбран для матча «Брэндон».В итоге результирующий фрейм данных будет выглядеть следующим образом:

# group  name gender days
#     B Jared      M   49
#     B Erica      F   47
#     B   Ron      M   30
#     B  Lynn      F   58
#     B    Cy      M   55

Дополнительные правила:

  • Это иерархическое объединение, где gender match имеет приоритет надdays близость.

  • Обратите внимание, что есть два равных удаленных параметра, которые соответствуют «Lesa» в df1 («Sara» и «Lynn»).Произвольно выберите один из двух, чтобы соответствовать "Lesa".В последнем кадре данных, приведенном выше, в примере был выбран «Линн».

  • «Джаред» в df2 равно расстоянию от «Брэндона» и «Майкла» в df1.Поскольку «Джаред» уже соответствует «Брэндону», он также не может быть сопоставлен с «Майклом».Таким образом, матч с "Майклом" переходит к "Сай", который является следующим лучшим оставшимся матчем с точки зрения gender и days.

1 Ответ

0 голосов
/ 16 января 2019

Данные

Прежде всего, я добавил stringsAsFactors = FALSE к вашим входным фреймам данных, потому что с моим решением работать со строками легче, чем с факторами.

df1 <- data.frame(group = rep("A", 5),
              name = c("Brandon",
                       "Kyler",
                       "Trent",
                       "Lesa",
                       "Michael"),
              gender = c("M", "F", "M", "F", "M"),
              days = c(50, 45, 32, 60, 48),
              stringsAsFactors = FALSE)

df2 <- data.frame(group = rep("B", 10),
                  name = c("Erica", 
                           "Jared",
                           "Sara",
                           "Helen",
                           "Tom",
                           "Ron",
                           "Cy",
                           "Lynn",
                           "Ken",
                           "Judy"),
                  gender = c("F", "M", "F", "F", "M", "M", "M", "F", "M", "F"),
                  days = c(47, 49, 62, 80, 74, 30, 55, 58, 63, 25),
                  stringsAsFactors = FALSE)

Решение

library(tidyverse)

# empty dataframe for the output
df2_new <- data.frame(group = character(),
                      name = character(),
                      gender = character(),
                      days = numeric(),
                      stringsAsFactors = FALSE)

for(i in 1:nrow(df1)){

  # add the row of interest to the output dataframe
  df2_new[i,] <- df2 %>% 
    mutate(day_diff = abs(days - df1$days[i])) %>%
    filter(gender == df1$gender[i]) %>% 
    slice(which.min(day_diff)) %>%
    select(-day_diff)

  # remove the newly added row from the original dataset
  df2 <- df2 %>%
    filter(!(name %in% df2_new$name))

}

Это первое решение, которое пришло в голову. В этом случае строки удаляются из df2 во время цикла for, поскольку вы сказали, что вы

хотите отфильтровать df2, чтобы включить только самое близкое соответствие каждой строке в df1

выход

df2_new

  group  name gender days
1     B Jared      M   49
2     B Erica      F   47
3     B   Ron      M   30
4     B  Sara      F   62
5     B    Cy      M   55

В моем случае код выбрал Сару вместо Линн, но это выбор 50/50.

...