Сопоставлять строки в разных фреймах данных на основе нескольких критериев без использования циклов for - PullRequest
0 голосов
/ 27 августа 2018

Мои данные содержат два разных кадра данных:

visits <- data.frame("visit_nr", "label", "degree", "code")
category <- data.frame("label", "degree", "group", "code1", "code2, "code3")

Я хотел бы назначить группу для каждого посещения в кадрах данных "посещения" на основе совпадения "метка", "степень" и "код" между двумя кадрами данных. Тем не менее, строка из определенного «visit_nr» может быть назначена только определенной группе, если «code2» и «code3» из «категории» фрейма данных также перечислены в фрейме данных «посещения». Это означает, что для того, чтобы строка была назначена определенной группе, должно быть три строки с одинаковым «visit_nr», где «label»; «Степень» и «код» совпадают с:

- "label", "degree", "code1"
- "label", "degree", "code2"
- "label", "degree", "code3" 

Поскольку оба этих фрейма данных содержат более 50 000 строк, я бы хотел избежать использования циклов для этого.

Посещения

visit_nr   | label | degree | code   |  Group
1601704801 |  171  |    1   | 354373 |   0
1601704801 |  171  |    1   | 200200 |   0
1601704801 |  171  |    1   | 973443 |   0
1601704801 |  171  |    1   | 475985 |   0
1601704801 |  171  |    1   | 994320 |   0

Категория

label | degree | group | code1 | code2 | code3
 171  |   1    |   2   | 354373| 200200| 475985 
 171  |   1    |   3   | 354373| 200200| 998282
 171  |   1    |   1   | 354373| 200200| 0

Ожидаемый результат:

visit_nr   | label | degree | code   |  Group 
1601704801 |  171  |    1   | 354373 |   2
1601704801 |  171  |    1   | 200200 |   2
1601704801 |  171  |    1   | 973443 |   2
1601704801 |  171  |    1   | 475985 |   2
1601704801 |  171  |    1   | 994320 |   2

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

Существует альтернативный подход, который изменяет category с широкого на длинный формат, объединяет с visits и подсчитывает, сколько подходящих кодов можно найти:

library(data.table)
# reshape from wide to long format
lcat <- melt(setDT(category), measure.vars = patterns("^code"),
     value.name = "code")
# join and count
tmp <- lcat[setDT(visits), on = .(label, degree, code), nomatch = 0L][
  , .N, by = .(visit_nr, label, degree, group)][
    N == 3L]
tmp[]
     visit_nr label degree group N
1: 1601704801   171      1     2 3
# update join
visits[tmp, on = .(visit_nr, label, degree), Group := group, mult = "first"][]
visits[]
     visit_nr label degree   code Group
1: 1601704801   171      1 354373     2
2: 1601704801   171      1 200200     2
3: 1601704801   171      1 973443     2
4: 1601704801   171      1 475985     2
5: 1601704801   171      1 994320     2

EDIT

В комментарии ОП раскрыл, что

не все строки в столбцах code2 и code3 в кадре данных category имеют значение. Также бывает, что только code1 имеет значение отличные от 0 и code2 и code3 имеют значение 0. В этом В случае, если в определенном visit_nr должен присутствовать только первый код назначить соответствующую группу для всего visit_nr

Таким образом, простая проверка, если есть точные 3 совпадающих кода, работает для образца набора данных, но не для производственного набора данных OP.

Я полагаю, что дополнительное требование может быть покрыто двумя модификациями:

  1. Все строки с code == 0 удалены из long
  2. Если tmp содержит несколько совпадений, выбирается тот, который имеет наибольшее значение N. Если есть связи, which.max() выбирает первую встреченную.

Итак, код становится:

library(data.table)
lcat <- melt(setDT(category), measure.vars = patterns("^code"),
             value.name = "code")[code != 0]
tmp <- lcat[setDT(visits), on = .(label, degree, code), nomatch = 0L][
  , .N, by = .(visit_nr, label, degree, group)][
    , .SD[which.max(N)], by = .(visit_nr, label, degree)]
visits[tmp, on = .(visit_nr, label, degree), Group := group]
visits[]
     visit_nr label degree   code Group
1: 1601704801   171      1 354373     2
2: 1601704801   171      1 200200     2
3: 1601704801   171      1 973443     2
4: 1601704801   171      1 475985     2
5: 1601704801   171      1 994320     2

Данные

library(data.table)

visits <- fread("
visit_nr   | label | degree | code   |  Group
1601704801 |  171  |    1   | 354373 |   0
1601704801 |  171  |    1   | 200200 |   0
1601704801 |  171  |    1   | 973443 |   0
1601704801 |  171  |    1   | 475985 |   0
1601704801 |  171  |    1   | 994320 |   0
")

category <- fread("
label | degree | group | code1 | code2 | code3
 171  |   1    |   2   | 354373| 200200| 475985 
 171  |   1    |   3   | 354373| 200200| 998282
 171  |   1    |   1   | 354373| 200200| 0
")
0 голосов
/ 27 августа 2018

Merge 2 таблицы 3 раза, а затем связать их все так:

df1 <- merge(visits, category, by.x = c("label", "degree", "code"), by.y = c("label", "degree", "code1"), all.x = TRUE)
df2 <- merge(visits, category, by.x = c("label", "degree", "code"), by.y = c("label", "degree", "code2"), all.x = TRUE)
df3 <- merge(visits, category, by.x = c("label", "degree", "code"), by.y = c("label", "degree", "code3"), all.x = TRUE)
#change the column names using names(df) here to maintain consistency
df <- rbind(df1, df2, df3)
...