Создание переменной в кадре данных R в зависимости от другого кадра данных

Я ищу помощь, потратив почти день. У меня есть большой фрейм данных (bdf) и небольшой фрейм данных (sdf). Я хочу добавить переменную z в ​​bdf в зависимости от значения sdf $y (которое изменяется как функция переменной времени).

Вот пример воспроизводимости:

bdf <- data.frame(tb = seq(as.POSIXct("2013-05-19 17:11:22 GMT", tz="GMT"), by=5624*24, length.out=10))

bdf
                tb
1  2013-05-19 17:11:22
2  2013-05-21 06:40:58
3  2013-05-22 20:10:34
4  2013-05-24 09:40:10
5  2013-05-25 23:09:46
6  2013-05-27 12:39:22
7  2013-05-29 02:08:58
8  2013-05-30 15:38:34
9  2013-06-01 05:08:10
10 2013-06-02 18:37:46


sdf <- data.frame(ts = as.POSIXct(c("2013-05-22", "2013-05-25", "2013-05-30"), tz="GMT"), y = c(0.2, -0.1, 0.3))

> sdf
      ts    y
1 2013-05-22  0.2
2 2013-05-25 -0.1
3 2013-05-30  0.3

Я хочу создать переменную z в ​​bdf со следующими значениями sdf $y:

  • 0.2 для строк, где bdf $tb варьируется от первого значения bdf $tb до середины между 1-м и 2-м значением sdf $ts. В этом простом примере это относится к строкам с 1 по 3 из dbf, которые имеют время bdf $tb ниже "2013-05-23 12:00:00 GMT" .

  • -0.1 для строк, где bdf $tb варьируется от середины между 1-м и 2-м значениями sdf $ts до середины между 2-м и 3-м значениями sdf $ts. В этом простом примере это относится к строкам 4 и 5 dbf, которые имеют время bdf $tb между "2013-05-23 12:00:00 GMT" и "2013-05-27 12:00:00 GMT",.

  • 0.3 для всех строк, где bdf $tb варьируется от середины между вторым и третьим значением sdf $ts до последнего значения bdf $tb. В этом простом примере это относится к строкам от 1 до 6 до 10 из dbf, которые имеют времена, превышающие "2013-05-23 12:00:00 GMT" .

Следовательно, в конце, большой фрейм данных bdf должен выглядеть следующим образом:

                 tb    z
1  2013-05-19 17:11:22  0.2
2  2013-05-21 06:40:58  0.2
3  2013-05-22 20:10:34  0.2
4  2013-05-24 09:40:10 -0.1
5  2013-05-25 23:09:46 -0.1
6  2013-05-27 12:39:22  0.3
7  2013-05-29 02:08:58  0.3
8  2013-05-30 15:38:34  0.3
9  2013-06-01 05:08:10  0.3
10 2013-06-02 18:37:46  0.3

Я не смог бы использовать dplyr:: mutate и не получал нигде с помощью циклов... Любая помощь была бы высоко оценена. Надеюсь, что я четко описал проблему как придерживающуюся этикета (это мой первый вопрос).

Ответ 1

Теперь это абсолютно не нужно, но в базе R

bdf$z <- numeric(nrow(bdf))
for(i in seq_along(bdf$z)){
  ind <- which.min(abs(bdf$tb[i] - sdf$ts))
  bdf$z[i] <- sdf$y[ind]
}

Несмотря на то, что он немного неуклюжий, он имеет преимущество в ясности, что позволяет легко адаптироваться к dplyr

library(dplyr)
bdf %>% rowwise() %>% 
  mutate(z= sdf$y[which.min(abs(as.numeric(tb)-as.numeric(sdf$ts)))])

#Source: local data frame [10 x 2]
#Groups: <by row>

#                    tb    z
#1  2013-05-19 17:11:22  0.2
#2  2013-05-21 06:40:58  0.2
#3  2013-05-22 20:10:34  0.2
#4  2013-05-24 09:40:10 -0.1
#5  2013-05-25 23:09:46 -0.1
#6  2013-05-27 12:39:22  0.3
#7  2013-05-29 02:08:58  0.3
#8  2013-05-30 15:38:34  0.3
#9  2013-06-01 05:08:10  0.3
#10 2013-06-02 18:37:46  0.3

Ответ 2

Здесь решение с использованием data.table катящихся соединений:

require(data.table)
setkey(setDT(sdf), ts)
sdf[bdf, roll = "nearest"]
#                      ts    y
#  1: 2013-05-19 17:11:22  0.2
#  2: 2013-05-21 06:40:58  0.2
#  3: 2013-05-22 20:10:34  0.2
#  4: 2013-05-24 09:40:10 -0.1
#  5: 2013-05-25 23:09:46 -0.1
#  6: 2013-05-27 12:39:22  0.3
#  7: 2013-05-29 02:08:58  0.3
#  8: 2013-05-30 15:38:34  0.3
#  9: 2013-06-01 05:08:10  0.3
# 10: 2013-06-02 18:37:46  0.3
  • setDT преобразует data.frame в data.table по ссылке.

  • setkey сортирует таблицу данных по ссылке в порядке возрастания по предоставленным столбцам и помещает эти столбцы в качестве ключевых столбцов (так что мы можем присоединиться к этим ключевым столбцам позже.

  • В таблице данных x[i] выполняется объединение, когда i является таблицей данных. Я передам вам этот ответ, чтобы догнать соединения data.table, если вы еще не знакомы.

  • x[i] выполняет equi-join. То есть он находит соответствующие индексы строк в x для каждой строки в i, а затем извлекает эти строки из x, чтобы вернуть результат объединения вместе с соответствующей строкой из i. Если строка в i не находит соответствующие индексы строк в x, эта строка имела бы NA для x по умолчанию.

    Однако x[i, roll = .] выполняет скользящее соединение. Если нет совпадения, то последнее наблюдение переносится вперед (roll = TRUE или -Inf), или следующее наблюдение может быть перенесено назад (roll = Inf) или свернуто до ближайшего значения (roll = "nearest"). И в этом случае вам требуется roll = "nearest" IIUC.

НТН

Ответ 3

Здесь мой подход:

library(zoo)
m <- c(rollmean(as.POSIXct(sdf$ts), 2), Inf)
transform(bdf, z = sdf$y[sapply(tb, function(x) which.max(x < m))])
#                    tb    z
#1  2013-05-19 17:11:22  0.2
#2  2013-05-21 06:40:58  0.2
#3  2013-05-22 20:10:34  0.2
#4  2013-05-24 09:40:10 -0.1
#5  2013-05-25 23:09:46 -0.1
#6  2013-05-27 12:39:22  0.3
#7  2013-05-29 02:08:58  0.3
#8  2013-05-30 15:38:34  0.3
#9  2013-06-01 05:08:10  0.3
#10 2013-06-02 18:37:46  0.3

Обновление: удаленное преобразование в числовое (не обязательно)

Краткое объяснение:

  • as.POSIXct(sdf$ts) преобразует даты в даты даты POSIXct
  • rollmean(as.POSIXct(sdf$ts), 2) вычисляет среднее значение прокатки для каждой из двух последовательных строк. Это происходит именно в тот момент, когда вы хотите использовать для разделения наблюдений. rollmean из пакета zoo. Вычисление a rollmean(..,2) означает, что выходной вектор сокращается на 1 по сравнению с входным вектором.
  • Вот почему я завершаю результат rollmean в c(.., Inf), что означает, что значение бесконечности добавляется к вектору rollmean в качестве последнего значения. Это гарантирует, что будут возвращены последние записи z в sdf (0,3 в конкретном примере).
  • Я использую transform, чтобы добавить столбец z в bdf
  • sapply(tb, function(x) which.max(x < m)) прокручивает записи в bdf$tb и для каждой записи вычисляет максимальный индекс, для которого bdf$tb меньше (раньше), чем m (который содержит вектор записей rollmean). Для каждой записи bdf$tb возвращается только максимальный (последний) индекс.
  • Этот вектор индексов используется в sdf$y[sapply(tb, function(x) which.max(x < m))] для извлечения соответствующих элементов sdf$y, которые затем будут сохранены/скопированы в новый столбец z в bdf

Надеюсь, что поможет

Ответ 4

Отредактируйте примечание. Первоначально я получаю немного другой результат, чем вы, который, как я теперь думаю, был связан с моим недостатком понимания R-разностных объектов. Временные метки в объектах POSIXt также остаются для меня загадкой, но теперь я вижу, что, когда я принуждал объект "diffftime" к "числовому", я получил значение в "дни".

Функция findInterval очень полезна в качестве функции создания индекса, которая отображает вектор значений, где один имеет несколько соседних неперекрывающихся интервалов. У вас действительно есть только два момента времени, разделенных на три интервала.

bdf$z <- c(0.2,-0.1,0.3)[findInterval(bdf$tb, 
                c(-Inf, 
  sdf$ts[2] - 0.5*as.numeric(difftime(sdf$ts[2], sdf$ts[1], units="secs")), 
  sdf$ts[3] - 0.5*as.numeric(difftime(sdf$ts[3], sdf$ts[2],units="sec")), 
                 Inf))]

> bdf
                    tb    z
1  2013-05-19 17:11:22  0.2
2  2013-05-21 06:40:58  0.2
3  2013-05-22 20:10:34  0.2
4  2013-05-24 09:40:10 -0.1
5  2013-05-25 23:09:46 -0.1
6  2013-05-27 12:39:22  0.3
7  2013-05-29 02:08:58  0.3
8  2013-05-30 15:38:34  0.3
9  2013-06-01 05:08:10  0.3
10 2013-06-02 18:37:46  0.3

Я также проверил, не повлияет ли мой результат на то, были ли интервалы в findIntervals закрыты справа, а не слева (по умолчанию) и не видели разницы.