Вот мои данные:
df1 <- fread('
id , date1 , date2
id_0001 , 2017-01-01, 2017-01-05
id_0002 , 2017-01-02, 2017-01-08
id_0003 , 2017-01-04, 2017-01-07
')
df2<- fread('
date , value
2017-01-01, 1
2017-01-02, 2
2017-01-03, 5
2017-01-04, 5
2017-01-05, 5
2017-01-06, 3
2017-01-07, 4
2017-01-08, 7
2017-01-09, 5
2017-01-10, 1
2017-01-11, 5
')
Я хочу обобщить (получить среднее значение) value
из df2
по каждому id
из df1
в диапазоне между рядами date1
и date2
.
Результат такой:
mean(c(1,2,5,5,5))
id_0002
2017-01-02
2017-01-08
mean(c(2,5,5,5,3,4,7))
id_0003
2017-01-04
2017-01-07
mean(c(5,5,3,4))
Я знаю, что могу расширить id
на date1
и date2
в df1
и выполнить left_join
на dates
до df2
, а затем summarize
. Однако по мере увеличения объема данных r не может обрабатывать векторы определенного размера, когда требуется дальнейший анализ. Есть ли data.table
способ сделать эту сводку между кадрами данных?
Да, я тоже использовал mean(). Извините за грубый расчет.
С pmap
и between
:
library(purrr)
library(dplyr)
df1 %>%
mutate(mean = pmap(across(date1:date2), ~ mean(df2$value[between(df2$date, ..1, ..2)])))
# id date1 date2 mean
#1: id_0001 2017-01-01 2017-01-05 3.6
#2: id_0002 2017-01-02 2017-01-08 4.428571
#3: id_0003 2017-01-04 2017-01-07 4.25
Если средства неточны, кажется, вам нужен подход, как показано ниже:
library(data.table)
df1[, value := df2[.SD, on = .(date >= date1, date <= date2), mean(value), by = .EACHI]$V1]
Вывод:
df1
id date1 date2 value
1: id_0001 2017-01-01 2017-01-05 3.600000
2: id_0002 2017-01-02 2017-01-08 4.428571
3: id_0003 2017-01-04 2017-01-07 4.250000
Я думаю, магия — это by=.EACHI
, верно?
Да, это один из самых быстрых подходов к агрегированию (.EACHI
) и обновлению ($V1
) в соединении. Он рассчитывается для каждой строки индекса i
, поэтому нет необходимости в последующих вызовах агрегации/обновления через by
.
Не data.table, но этот подход dplyr избегает объединения и должен (?) Требовать меньше памяти:
library(dplyr)
df1 %>%
group_by(id) %>%
mutate(value = mean(df2$value[df2$date >= date1 & df2$date <= date2])) %>%
ungroup()
# A tibble: 3 × 4
id date1 date2 value
<chr> <date> <date> <dbl>
1 id_0001 2017-01-01 2017-01-05 3.6
2 id_0002 2017-01-02 2017-01-08 4.43
3 id_0003 2017-01-04 2017-01-07 4.25
Или аналогичный базовый подход R с использованием sapply()
по строкам вместо группировки по id
:
df1$value <- sapply(
seq(nrow(df1)),
\(i) mean(df2$value[df2$date >= df1$date1[[i]] & df2$date <= df1$date2[[i]]])
)
id date1 date2 value
1: id_0001 2017-01-01 2017-01-05 3.600000
2: id_0002 2017-01-02 2017-01-08 4.428571
3: id_0003 2017-01-04 2017-01-07 4.250000
Два способа решить вашу проблему с помощью пакета data.table:
# Method 1
df1[, value := df2[date>=date1 & date<=date2, mean(value)], by=.(date1, date2)]
# Method 2
df1[, value := df2[.BY, mean(value), on=.(date>=date1, date<=date2)], by=.(date1, date2)]
id date1 date2 value
<char> <IDat> <IDat> <num>
1: id_0001 2017-01-01 2017-01-05 3.600000
2: id_0002 2017-01-02 2017-01-08 4.428571
3: id_0003 2017-01-04 2017-01-07 4.250000
Вот еще один non-equi join
вариант внутри data.table
df2[df1,
on = .(date >= date1, date <= date2)
][
,
.(value = mean(value)),
.(id, date1 = date, date2 = date.1)
]
который дает
id date1 date2 value
1: id_0001 2017-01-01 2017-01-05 3.600000
2: id_0002 2017-01-02 2017-01-08 4.428571
3: id_0003 2017-01-04 2017-01-07 4.250000
Вы можете сделать что-то вроде
df1[, value := df2[.SD, on = .(date >= date1, date <= date2), mean(value), by = .EACHI]$V1 ]
, но средние значения, похоже, не соответствуют вашим, можете ли вы поделиться точным расчетом, например, для первый диапазон?