Присоединяйся к фреймам данных и выбирай случайную строку, когда есть несколько совпадений - PullRequest
3 голосов
/ 12 июня 2019

У меня есть справочный фрейм данных (df1) с тремя столбцами «характеристик» (пол, год, код) и двумя столбцами «значений» (сумма, статус).Это выглядит так, но со многими строками:

gender    year    code    amount   status
     M    2011       A        15      EMX
     M    2011       A       123      NOX
     F    2015       B         0      MIX
     F    2018       A        12      NOX
     F    2015       B        11      NOX

У меня есть еще один фрейм данных (df2), в котором только три столбца «характеристики».Например:

gender    year   code
     M    2011      A
     M    2011      A
     F    2018      A
     F    2015      B

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

Например, последняя строка в 'df2' (пол = F, год = 2015, код = B)соответствует двум строкам в 'df1': третьей строке (amont = 0, status = MIX) и пятой строке (сумма = 11, status = NOX).Затем одна из этих совпадающих строк должна быть выбрана случайным образом.Для всех таких случаев множественных совпадений между 'df2' и 'df1' на основе пола, года и кода следует выбирать случайную строку.


До сих пор мой подход состоял в том, чтобы начать с использованияdplyr, чтобы сделать left_join между двумя кадрами данных.Однако это обеспечивает все возможные «значения» для каждой строки в df2, а не выбирает одно случайное.Поэтому я должен сгруппировать по характеристикам и выбрать один.В результате получается очень большая промежуточная таблица, и она не кажется очень эффективной.

Интересно, есть ли у кого-нибудь предложения по более эффективному методу?Ранее я обнаружил, что объединение с пакетом data.table происходит быстрее, но в действительности у него нет хорошего понимания пакета.Мне также интересно, должен ли я вообще делать объединения или просто использовать функцию sample?

Любая помощь, высоко ценимая.

Ответы [ 4 ]

4 голосов
/ 12 июня 2019

Используйте 'd2' для поиска строк в 'd1' на основе совпадений в 'пол', 'год', 'код' (d1[d2, on = .(gender, year, code), ...]). Для каждого совпадения (by = .EACHI) выберите одну строку (sample(.N, 1L)). Используйте это, чтобы индексировать «количество» и «статус».

d1[d2, on = .(gender, year, code),
  {ri <- sample(.N, 1L)
  .(amount = amount[ri], status = status[ri])}, by = .EACHI]

# sample based on set.seed(1)
#    gender year code amount status
# 1:      M 2011    A     15    EMX
# 2:      M 2011    A     15    EMX
# 3:      F 2018    A     12    NOX
# 4:      F 2015    B     11    NOX

Обратите внимание, что существует проблема Расширенная функциональность mult аргумент , т. Е. Как обрабатывать случаи, когда несколько строк в x соответствуют строке в i. В настоящее время допустимыми являются "all" (по умолчанию), "first" или "last". Но если / когда проблема реализована, mult = "random" (sample(.N, size = 1L)) может использоваться для выбора случайной строки (строк) среди совпадений.

1 голос
/ 12 июня 2019
df2 %>%
  mutate(
    amount = pmap_chr(
      .l = df2,
      .f = ~ df1 %>%
        filter(gender == ..1, year == ..2, code == ..3) %>%
        select(amount) %>%
        sample_n(1) %>%
        pull(amount)
    ),
    status = pmap_chr(
      .l = df2,
      .f = ~ df1 %>%
        filter(gender == ..1, year == ..2, code == ..3) %>%
        select(status) %>%
        sample_n(1) %>%
        pull(status)
    )
  )

Это довольно медленно, и я бы лично избегал этого, но это способ сделать это.

1 голос
/ 12 июня 2019

Я ожидаю, что это будет эффективно:

df1[, row := .I]
keys <- c("year", "gender", "code")
setkeyv(df1, keys)
setkeyv(df2, keys)

for (rowdf2 in seq_len(nrow(df2))) {
  set(df2, i = rowdf2, j = "rowindf1", value = df1[df2[rowdf2], x.row[sample(.N, 1)]])
}

setkeyv(df1, "row")
df1[df2[, .(rowindf1)]]

Пример вывода:

#    gender year code amount status row
# 1:      M 2011    A    123    NOX   2
# 2:      M 2011    A     15    EMX   1
# 3:      F 2015    B     11    NOX   5
# 4:      F 2018    A     12    NOX   4
1 голос
/ 12 июня 2019

Моя data.table игра довольно слабая, но вот потенциальное решение, использующее подход, аналогичный описанному выше.Сначала я определяю фреймы данных.

# Define data frames
df1 <- read.table(text= "gender    year    code    amount   status
M    2011       A        15      EMX
M    2011       A       123      NOX
F    2015       B         0      MIX
F    2018       A        12      NOX
F    2015       B        11      NOX", header = TRUE)

df2 <- read.table(text = "gender    year   code
     M    2011      A
     M    2011      A
     F    2018      A
     F    2015      B", header = TRUE)

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

# Set RNG seed
set.seed(4)

# Load library
library(data.table)

Далее я конвертирую фреймы данных в таблицы данных.

# Convert to data tables
dt1 <- data.table(df1) 
dt2 <- data.table(df2) 

Здесь я делаю фактические объединения и т. Д. Я сделал это 5 раз в цикле, чтобы показатьслучайность результатов.

for(i in c(1:5)){
  # Add row numbers
  dt3 <- dt2[, rn :=.I
             ][dt1,on = .(gender, year, code)
               ][, .SD[sample(.N)[1]], .(gender, year, code, rn)
                 ][, rn := NULL]

  # Check results
  print(dt3)
}
#>    gender year code amount status
#> 1:      M 2011    A    123    NOX
#> 2:      M 2011    A     15    EMX
#> 3:      F 2015    B      0    MIX
#> 4:      F 2018    A     12    NOX
#>    gender year code amount status
#> 1:      M 2011    A    123    NOX
#> 2:      M 2011    A    123    NOX
#> 3:      F 2015    B     11    NOX
#> 4:      F 2018    A     12    NOX
#>    gender year code amount status
#> 1:      M 2011    A    123    NOX
#> 2:      M 2011    A    123    NOX
#> 3:      F 2015    B     11    NOX
#> 4:      F 2018    A     12    NOX
#>    gender year code amount status
#> 1:      M 2011    A     15    EMX
#> 2:      M 2011    A     15    EMX
#> 3:      F 2015    B     11    NOX
#> 4:      F 2018    A     12    NOX
#>    gender year code amount status
#> 1:      M 2011    A    123    NOX
#> 2:      M 2011    A     15    EMX
#> 3:      F 2015    B      0    MIX
#> 4:      F 2018    A     12    NOX

Создано в 2019-06-12 с помощью представительного пакета (v0.3.0)

ЧтоНа самом деле я добавляю номера строк в таблицу данных, что поможет мне сократить итоговую таблицу данных.Я присоединяюсь к таблицам данных, а затем группирую все строки из одной строки в dt2 и вытаскиваю одну наугад, используя sample.(Этот бит кода заимствован из @akrun здесь .) Наконец, я опускаю столбец с номером строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...