Связать строки на основе условий с данными.таблица

#r #dplyr #data.table #tidyr

#r #dplyr #данные.таблица #порядок

Вопрос:

У меня есть 2 таблицы данных, которые я хотел бы объединить, связав строки. Выходные данные dput приведены ниже:

     >dput(x)
    structure(list(NN = c("test", "test", "test", "test", "test"), 
    gpn = c("gas", "gas", "gas", "gas", "gas"), sitename = c("agip", 
    "agip", "agip", "agip", "agip"), code = c("F123", "F123", 
    "F123", "F123", "F123"), Brand = c("bp", "shell", "ionic", 
    "meta", "robo"), starttime = structure(c(1533097609, 1533118867, 
    1533183999, 1533270359, 1533356437), class = c("POSIXct", 
    "POSIXt"), tzone = ""), endtime = structure(c(1533118867, 
    1533183999, 1533270359, 1533356437, 1533443068), class = c("POSIXct", 
    "POSIXt"), tzone = ""), price = c(1.592, 1.592, 1.598, 1.598, 
    1.598), otherprice = c(1.998, 2.11, 2.114, 2.134, 1.989)), .Names = c("NN", 
"gpn", "sitename", "code", "Brand", "starttime", "endtime", "price", 
"otherprice"), row.names = c(NA, -5L), class = c("data.table", 
"data.frame"), .internal.selfref = <pointer: 0x00000000025c0788>)



>dput(y)

structure(list(NN = c("test", "test", "test", "test", "test"), 
gpn = c("gas", "gas", "gas", "gas", "gas"), sitename = c("agip", 
"agip", "agip", "agip", "agip"), code = c("F123", "F123", 
"F123", "F123", "F123"), Brand = c("dino", "dino", "dino", 
"dino", "dino"), otherprice = c(1.987, 2.456, 3.456, 1.989, 
1.99), starttime = structure(c(1533097399, 1533118999, 1533184999, 
1533271999, 1533359357), tzone = "", class = c("POSIXct", 
"POSIXt")), endtime = structure(c(1533118100, 1533182999, 
1533270000, 1533356000, 1533443000), class = c("POSIXct", 
"POSIXt"), tzone = "")), .Names = c("NN", "gpn", "sitename", 
  

«код», «Бренд», «другая цена», «время начала», «время окончания»), class = c(«data.table»,
«data.frame»), .internal.selfref = )

Я хотел бы привязать строки от y до x таким образом, чтобы соответствовало следующее:

псевдокод:

If(NN, код из ‘x’ совпадает с NN, кодом из ‘y’) amp; ‘x $starttime’ < ‘y $ starttime’ amp; ‘x $endtime’ > ‘y$endtime’

Затем получите NN, код, марку, цену из ‘y’, и результирующий фрейм станет:

 NN    gpn    sitename   code   Brand        starttime          endtime price otherprice
 test gas     agip      F123    bp    01/08/2018 05:26 01/08/2018 11:21 1.592      1.998
 test gas     agip      F123   shell 01/08/2018 11:21 02/08/2018 05:26 1.592      2.110
 test gas     agip      F123   Ionic 02/08/2018 05:26 03/08/2018 05:25 1.598      2.114
 test gas     agip      F123    meta 03/08/2018 05:25 04/08/2018 05:20 1.598      2.134
 test gas     agip      F123    robo 04/08/2018 05:20 05/08/2018 05:24 1.598      1.989
 test gas     agip      F123    dino 01/08/2018 05:26 01/08/2018 11:21 1.592      1.987
 test gas     agip      F123    dino 01/08/2018 11:21 02/08/2018 05:26 1.592      2.456
 test gas     agip      F123    dino 02/08/2018 05:26 03/08/2018 05:25 1.598      3.456
 test gas     agip      F123    dino 03/08/2018 05:25 04/08/2018 05:20 1.598      1.989
test gas     agip       F123    dino 04/08/2018 05:20 05/08/2018 05:24 1.598      1.990
  

Я проверил InsertRow и rbind , но ни то, ни другое не работает.

Есть идеи?

Спасибо.

Ответ №1:

Чтобы объединить наборы данных, кажется, что хорошим решением является использование только rbind наборов данных при использовании merge для привязки наборов данных к ограничениям времени начала / окончания. В приведенном ниже примере rbind функция вкладывает merge условия для начальной и конечной дат:

 library('data.table')
z <- rbind(
  x = x,
  y = merge(x[, .(code, starttime.x = starttime, endtime.x = endtime, price)], y, 
            by = 'code',
            allow.cartesian = TRUE,
            all.x = TRUE)[starttime.x < starttime amp; endtime.x > endtime,
                          .SD,
                          .SDcols = c(paste0(names(y)), 'price')])
z
  

Вывод:

      NN gpn sitename code Brand           starttime             endtime price otherprice
1: test gas     agip F123    bp 2018-08-01 00:26:49 2018-08-01 06:21:07 1.592      1.998
2: test gas     agip F123 shell 2018-08-01 06:21:07 2018-08-02 00:26:39 1.592      2.110
3: test gas     agip F123 ionic 2018-08-02 00:26:39 2018-08-03 00:25:59 1.598      2.114
4: test gas     agip F123  meta 2018-08-03 00:25:59 2018-08-04 00:20:37 1.598      2.134
5: test gas     agip F123  robo 2018-08-04 00:20:37 2018-08-05 00:24:28 1.598      1.989
6: test gas     agip F123  dino 2018-08-01 06:23:19 2018-08-02 00:09:59 1.592      2.456
7: test gas     agip F123  dino 2018-08-02 00:43:19 2018-08-03 00:20:00 1.598      3.456
8: test gas     agip F123  dino 2018-08-03 00:53:19 2018-08-04 00:13:20 1.598      1.989
9: test gas     agip F123  dino 2018-08-04 01:09:17 2018-08-05 00:23:20 1.598      1.990
  

Комментарии:

1. ОК. Это выглядит лучше, но все еще есть пара вещей. 1. допустим, мы переименовываем ‘y $ price’ в ‘y $ otherprice’, и ‘цена’ должна быть из ‘x’, только тогда как мы можем этого добиться? 2. Слияние должно быть соединением по левому краю, что означает, что должны быть возвращены все строки из ‘x’. Любые идеи о том, как настроить код для учета вышеупомянутых изменений. Спасибо

2. Давайте используем следующий dput для ‘y’, который я обновил в вопросе.

3. Хорошо — я скорректировал код, чтобы он соответствовал новым данным. «Цена» теперь относится только к значениям из таблицы «x». Объединение по левому краю достигается путем установки для all.x параметра значения TRUE в merge функции.

4. Это выглядит хорошо, но по-прежнему отсутствует одно наблюдение из ‘x’, которое не соответствовало наблюдению в ‘y’. Таким образом, общее количество наблюдений в результате должно составлять 9, поскольку наблюдение из ‘y’, которое не соответствует ни одному из ‘x’, должно быть удалено только при сохранении всех строк из x. Спасибо.

5. Я удалил начальное / конечное подмножество в элементе «x», которое, как я думаю, препятствовало отображению всех строк. unique Это просто гарантирует, что вы не получите повторяющиеся записи. Я думаю, вы также могли бы просто заменить первый аргумент просто «x», поскольку слияние больше ничего не делает в этом примере. Что вы думаете?

Ответ №2:

Мы можем объединить, используя dplyr , однако есть несколько предостережений из-за предоставленной структуры. Из dput() мы видим, что у y этого sitename элемента нет starttime переменной, starttime и brand являются факторами и их необходимо преобразовать в posixct, а переменная y в Brand имеет строчную букву «b», но в x «в» — заглавную «B».

Учитывая эти наблюдения, мы можем сделать следующее:

 # Change time to posixct to be consistent with x. 
# NOTE: You may have to adjust the time zone
y$starttime = as.POSIXct(y$starttime)
y$endtime = as.POSIXct(y$endtime)

# Chance 'brand' to 'Brand'
colnames(y)[colnames(y)=="brand"] = "Brand"

# Change other variabes as mentioned in commen
y$otherprice = y$price
y$price = x$price
y$sitename = x$sitename
  

Мы можем создать подмножества, которые удовлетворяют указанным критериям (т. Е. выбрать подмножество x и y , где x$starttime < y$starttime и x$endtime > y$endtime )

 # Create subsets which match criteria
x.subset = subset(x, subset = x$starttime < y$starttime amp; x$endtime > y$endtime)
y.subset = subset(y, subset = x$starttime < y$starttime amp; x$endtime > y$endtime)
  

Теперь мы можем связать подмножества вместе

 # Row bind
library(dplyr)
z = dplyr::bind_rows(x.subset, y.subset)
> z
      NN gpn sitename code Brand           starttime             endtime price otherprice
 1: test gas     agip F123    bp 2018-08-01 00:26:49 2018-08-01 06:21:07 1.592      1.998
 2: test gas     agip F123 shell 2018-08-01 06:21:07 2018-08-02 00:26:39 1.592      2.110
 3: test gas     agip F123 ionic 2018-08-02 00:26:39 2018-08-03 00:25:59 1.598      2.114
 4: test gas     agip F123  meta 2018-08-03 00:25:59 2018-08-04 00:20:37 1.598      2.134
 5: test gas     agip F123  robo 2018-08-04 00:20:37 2018-08-05 00:24:28 1.598      1.989
 6: test gas     agip F123  dino 2018-08-01 00:33:19 2018-08-01 06:08:20 1.592      1.987
 7: test gas     agip F123  dino 2018-08-01 06:23:19 2018-08-02 00:09:59 1.592      2.456
 8: test gas     agip F123  dino 2018-08-02 00:43:19 2018-08-03 00:20:00 1.598      3.456
 9: test gas     agip F123  dino 2018-08-03 00:53:19 2018-08-04 00:13:20 1.598      1.989
10: test gas     agip F123  dino 2018-08-04 01:09:17 2018-08-05 00:23:20 1.598      1.990
  

Комментарии:

1. Все выглядит хорошо, но цена из y должна быть указана в столбце «other price», а «цена» должна совпадать с ценой для сайта в «x». Для sitename мы можем считать, что это то же самое. Кроме того, условие требует проверить, является ли время начала в «x» меньше, чем время начала в «y», и «время окончания» в «x» больше, чем время окончания в «y». Как я могу определить это условие при выполнении rbind?

2. код @syebill изменен с учетом запрошенных изменений.

3. Я также изменил структуру. Важной вещью является проверка времени. Есть идеи о том, как это настроить? Новые dput отражают правильные типы данных. Также вышеупомянутое решение работает, единственное, чего не хватает, это проверки времени. Вы предлагаете поместить rbind в инструкцию «if_else»?

4. @syebill не могли бы вы, пожалуйста, подробнее рассказать о проверке времени, на которую вы ссылаетесь?

5. @syebill : Я отредактировал свой ответ и добавил больше кода. Теперь есть этап фильтрации, на котором мы выбираем подмножества x и y , которые удовлетворяют критериям, упомянутым в вашем комментарии, затем мы объединяем подмножества вместе. Это должно решить проблему. Пожалуйста, дайте мне знать.