Создание переменной в кадре данных 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 закрыты справа, а не слева (по умолчанию) и не видели разницы.