Вычислите среднее значение между несколькими столбцами df2, которое может варьироваться в зависимости от переменной `var1` df1, и добавьте значение к новой переменной в df1 - PullRequest
1 голос
/ 17 мая 2019

У меня есть фрейм данных df1, который суммирует глубину разных рыб с течением времени в разных местах.

С другой стороны, у меня есть df2, который суммирует интенсивность течений во времени (КАЖДЫЙ ТРИ ЧАСА) от поверхности до глубины 39 метров с интервалами в 8 метров (m0-7, m8-15, * 1007). *, m24-31 и m32-39) в определенном месте. Как пример:

df1<-data.frame(Datetime=c("2016-08-01 15:34:07","2016-08-01 16:25:16","2016-08-01 17:29:16","2016-08-01 18:33:16","2016-08-01 20:54:16","2016-08-01 22:48:16"),Site=c("BD","HG","BD","BD","BD","BD"),Ind=c(16,17,19,16,17,16), Depth=c(5.3,24,36.4,42,NA,22.1))
df1$Datetime<-as.POSIXct(df1$Datetime, format="%Y-%m-%d %H:%M:%S",tz="UTC")


> df1
             Datetime Site Ind Depth
1 2016-08-01 15:34:07   BD  16   5.3
2 2016-08-01 16:25:16   HG  17  24.0
3 2016-08-01 17:29:16   BD  19  36.4
4 2016-08-01 18:33:16   BD  16  42.0
5 2016-08-01 20:54:16   BD  17    NA
6 2016-08-01 22:48:16   BD  16  22.1

df2<-data.frame(Datetime=c("2016-08-01 12:00:00","2016-08-01 15:00:00","2016-08-01 18:00:00","2016-08-01 21:00:00","2016-08-02 00:00:00"), Site=c("BD","BD","BD","BD","BD"),var1=c(2.75,4,6.75,2.25,4.3),var2=c(3,4,4.75,3,2.1),var3=c(2.75,4,5.75,2.25,1.4),var4=c(3.25,3,6.5,2.75,3.4),var5=c(3,4,4.75,3,1.7))
df2$Datetime<-as.POSIXct(df2$Datetime, format="%Y-%m-%d %H:%M:%S",tz="UTC")
colnames(df2)<-c("Datetime","Site","m0-7","m8-15","m16-23","m24-31","m32-39")

> df2
             Datetime Site m0-7 m8-15 m16-23 m24-31 m32-39
1 2016-08-01 12:00:00   BD 2.75  3.00   2.75   3.25   3.00
2 2016-08-01 15:00:00   BD 4.00  4.00   4.00   3.00   4.00
3 2016-08-01 18:00:00   BD 6.75  4.75   5.75   6.50   4.75
4 2016-08-01 21:00:00   BD 2.25  3.00   2.25   2.75   3.00
5 2016-08-02 00:00:00   BD 4.30  2.10   1.40   3.40   1.70

Я хочу создать переменную в df1, которая отражает средний ток для глубинных слоев, в которых рыба НЕ БЫЛА. Например, если рыба находится на глубине 20 метров, что соответствует слою m16-23, я хочу знать средний ток для слоев m0-7, m8-15, m24-31 и m32-39.

Примечание 1: если моя рыба была на глубине более 39 метров, я считаю, что она была на самом глубоком слое (m32-39). Пример этого в строке 4 df1.

Примечание2: поскольку текущие записи выполняются каждые три часа, каждый час, указанный в df2$Datetime, означает на полтора часа больше и на полтора часа меньше. То есть сила тока, указанная в df2 при 21:00:00, отражает токи между 19:30:00 и 22:30:00. То же самое с остальными часами.

Я бы ожидал этого:

> df1
             Datetime Site Ind Depth current.Mean
1 2016-08-01 15:34:07   BD  16   5.3         3.75
2 2016-08-01 16:25:16   HG  17  24.0           NA
3 2016-08-01 17:29:16   BD  19  36.4         5.94
4 2016-08-01 18:33:16   BD  16  42.0         5.94
5 2016-08-01 20:54:16   BD  17    NA           NA
6 2016-08-01 22:48:16   BD  16  22.1         2.87

Кто-нибудь знает, как это сделать?

Ответы [ 3 ]

2 голосов
/ 17 мая 2019

Я бы подошел к этому в два этапа:

  1. создать таблицу поиска с avg_speed_elsewhere для каждой даты, времени и глубины в df2.
  2. Присоединиться к df1.

Вот таблица соответствия:

library(tidyverse)
df2_long <- df2 %>%
  gather(depth_rng, speed, `m0-7`:`m32-39`) %>%
  separate(depth_rng, c("min_depth", "max_depth")) %>%
  mutate_at(vars(matches("depth")), parse_number) %>%
  # EDIT -- added to make deep category cover >39 too
  mutate(max_depth = if_else(max_depth == 39, 10000, max_depth)) %>%
  group_by(Datetime, Site) %>%
  # Avg Speed elsewhere is the sum of all speeds, minus this speed, all divided by 4.
  mutate(avg_speed_elsewhere = (sum(speed) - speed) / 4)

> df2_long
# A tibble: 25 x 6
# Groups:   Datetime, Site [5]
   Datetime            Site  min_depth max_depth speed avg_speed_elsewhere
   <dttm>              <fct>     <dbl>     <dbl> <dbl>               <dbl>
 1 2016-08-18 12:00:00 BD            0         7  2.75                3   
 2 2016-08-18 15:00:00 BD            0         7  4                   3.75
 3 2016-08-18 18:00:00 BD            0         7  6.75                5.44
 4 2016-08-18 21:00:00 BD            0         7  2.25                2.75
 5 2016-08-19 00:00:00 BD            0         7  4.3                 2.15
 6 2016-08-18 12:00:00 BD            8        15  3                   2.94
 7 2016-08-18 15:00:00 BD            8        15  4                   3.75
 8 2016-08-18 18:00:00 BD            8        15  4.75                5.94
 9 2016-08-18 21:00:00 BD            8        15  3                   2.56
10 2016-08-19 00:00:00 BD            8        15  2.1                 2.7 
# ... with 15 more rows

Я ожидаю, что это будет работать, но ваши данные не перекрываются, поэтому я не уверен:

df1 %>%
  # EDIT - replaced floor_date with round_date
  mutate(Datetime_3hr = lubridate::round_date(Datetime, "3 hour")) %>%
  left_join(df2_long, by = c("Site", "Datetime_3hr" = "Datetime")) %>%
  filter(Depth >= min_depth & Depth < max_depth + 1 | is.na(Depth))
1 голос
/ 19 мая 2019

Этот вопрос содержит интересные задачи:

  1. ОП запрашивает "частичное анти-объединение" , т. Е. ОП хочет объединить текущие данные в df2где Datetime и Site совпадают, но слой глубины не .
  2. Текущие данные df2 приведены в справочной таблице, где каждое значение связано с диапазоном глубины (уровень глубины) и интервалом времени из3 часа.Таким образом, измеренные значения Depth и Datetime в df1 должны быть сопоставлены с соответствующими диапазонами.

Я пробовал разные подходы, но в итоге я выбрал следующий, который не даетпредположения о агрегатной функции.Таким образом, mean() можно вызвать напрямую.

library(data.table)
library(magrittr)

# reshape df2 from wide to long format
currents <- melt(setDT(df2), id.vars = c("Datetime", "Site"),
                 variable.name = "layer", value.name = "current")

# create columns to join on
labels <- names(df2) %>% stringr::str_subset("^m")
breaks <- c(seq(0, 32, 8), Inf)
setDT(df1)[, layer := cut(Depth, breaks = breaks, labels = labels)]
df1[, current.dt := df2[df1, on = .(Site, Datetime), 
                      roll = "nearest", x.Datetime]]

# "partial anti-join" to compute mean of other layers
currents_other_layers <- 
  currents[df1, on = .(Site, Datetime = current.dt)][
    layer != i.layer, mean(current), by = .(i.Datetime, Site)]

# append result column
df1[currents_other_layers, on = .(Site, Datetime = i.Datetime), current.mean := i.V1]
df1
               Datetime Site Ind Depth  layer          current.dt current.mean
1: 2016-08-01 15:34:07   BD  16   5.3   m0-7 2016-08-01 15:00:00       3.7500
2: 2016-08-01 16:25:16   HG  17  24.0 m16-23                <NA>           NA
3: 2016-08-01 17:29:16   BD  19  36.4 m32-39 2016-08-01 18:00:00       5.9375
4: 2016-08-01 18:33:16   BD  16  42.0 m32-39 2016-08-01 18:00:00       5.9375
5: 2016-08-01 20:54:16   BD  17    NA   <NA> 2016-08-01 21:00:00           NA
6: 2016-08-01 22:48:16   BD  16  22.1 m16-23 2016-08-02 00:00:00       2.8750

Воспроизводит ожидаемый результат OP.

Пояснение

df2изменен с широкого на длинный формат.Это позволяет объединять / не объединять столбец layer.

currents
               Datetime Site  layer current
 1: 2016-08-01 12:00:00   BD   m0-7    2.75
 2: 2016-08-01 15:00:00   BD   m0-7    4.00
 3: 2016-08-01 18:00:00   BD   m0-7    6.75
 4: 2016-08-01 21:00:00   BD   m0-7    2.25
 5: 2016-08-02 00:00:00   BD   m0-7    4.30
 6: 2016-08-01 12:00:00   BD  m8-15    3.00
 7: 2016-08-01 15:00:00   BD  m8-15    4.00
 8: 2016-08-01 18:00:00   BD  m8-15    4.75
 9: 2016-08-01 21:00:00   BD  m8-15    3.00
10: 2016-08-02 00:00:00   BD  m8-15    2.10
11: 2016-08-01 12:00:00   BD m16-23    2.75
12: 2016-08-01 15:00:00   BD m16-23    4.00
13: 2016-08-01 18:00:00   BD m16-23    5.75
14: 2016-08-01 21:00:00   BD m16-23    2.25
15: 2016-08-02 00:00:00   BD m16-23    1.40
16: 2016-08-01 12:00:00   BD m24-31    3.25
17: 2016-08-01 15:00:00   BD m24-31    3.00
18: 2016-08-01 18:00:00   BD m24-31    6.50
19: 2016-08-01 21:00:00   BD m24-31    2.75
20: 2016-08-02 00:00:00   BD m24-31    3.40
21: 2016-08-01 12:00:00   BD m32-39    3.00
22: 2016-08-01 15:00:00   BD m32-39    4.00
23: 2016-08-01 18:00:00   BD m32-39    4.75
24: 2016-08-01 21:00:00   BD m32-39    3.00
25: 2016-08-02 00:00:00   BD m32-39    1.70
               Datetime Site  layer current

Теперь необходимо изменить df1, добавив в него столбцы, соответствующие layer и Datetime в currents.

Для Depth используется функция cut().Уровень последнего слоя m32-39 расширен до Inf, поэтому все глубины, превышающие 32 м, включены в этот уровень в соответствии с требованием OP.

Для Datetime - подвижное соединение с ближайшим Datetime в df2 используется.Это возможно, потому что df2$Datetime обозначает середину 3-часового временного диапазона.

После того, как df1 было подготовлено, мы можем выполнить «частичное анти-объединение».К сожалению, неуравновешенные объединения data.table не принимают оператор !=.Таким образом, мы не можем написать

currents[df1, on = .(Datetime = current.dt, Site, layer != layer)]

напрямую, но должны использовать обходной путь, в котором мы сначала выбираем строки, в которых ожидаем совпадения, а затем выполняем анти-объединение:

 currents[df1, on = .(Datetime = current.dt, Site)][
    !df1, on = .(Datetime = current.dt, Site, layer)]
               Datetime Site  layer current          i.Datetime Ind Depth i.layer
 1: 2016-08-01 15:00:00   BD  m8-15    4.00 2016-08-01 15:34:07  16   5.3    m0-7
 2: 2016-08-01 15:00:00   BD m16-23    4.00 2016-08-01 15:34:07  16   5.3    m0-7
 3: 2016-08-01 15:00:00   BD m24-31    3.00 2016-08-01 15:34:07  16   5.3    m0-7
 4: 2016-08-01 15:00:00   BD m32-39    4.00 2016-08-01 15:34:07  16   5.3    m0-7
 5: 2016-08-01 18:00:00   BD   m0-7    6.75 2016-08-01 17:29:16  19  36.4  m32-39
 6: 2016-08-01 18:00:00   BD  m8-15    4.75 2016-08-01 17:29:16  19  36.4  m32-39
 7: 2016-08-01 18:00:00   BD m16-23    5.75 2016-08-01 17:29:16  19  36.4  m32-39
 8: 2016-08-01 18:00:00   BD m24-31    6.50 2016-08-01 17:29:16  19  36.4  m32-39
 9: 2016-08-01 18:00:00   BD   m0-7    6.75 2016-08-01 18:33:16  16  42.0  m32-39
10: 2016-08-01 18:00:00   BD  m8-15    4.75 2016-08-01 18:33:16  16  42.0  m32-39
11: 2016-08-01 18:00:00   BD m16-23    5.75 2016-08-01 18:33:16  16  42.0  m32-39
12: 2016-08-01 18:00:00   BD m24-31    6.50 2016-08-01 18:33:16  16  42.0  m32-39
13: 2016-08-01 21:00:00   BD   m0-7    2.25 2016-08-01 20:54:16  17    NA    <NA>
14: 2016-08-01 21:00:00   BD  m8-15    3.00 2016-08-01 20:54:16  17    NA    <NA>
15: 2016-08-01 21:00:00   BD m16-23    2.25 2016-08-01 20:54:16  17    NA    <NA>
16: 2016-08-01 21:00:00   BD m24-31    2.75 2016-08-01 20:54:16  17    NA    <NA>
17: 2016-08-01 21:00:00   BD m32-39    3.00 2016-08-01 20:54:16  17    NA    <NA>
18: 2016-08-02 00:00:00   BD   m0-7    4.30 2016-08-01 22:48:16  16  22.1  m16-23
19: 2016-08-02 00:00:00   BD  m8-15    2.10 2016-08-01 22:48:16  16  22.1  m16-23
20: 2016-08-02 00:00:00   BD m24-31    3.40 2016-08-01 22:48:16  16  22.1  m16-23
21: 2016-08-02 00:00:00   BD m32-39    1.70 2016-08-01 22:48:16  16  22.1  m16-23
22:                <NA>   HG   <NA>      NA 2016-08-01 16:25:16  17  24.0  m16-23
               Datetime Site  layer current          i.Datetime Ind Depth i.layer

Может быть агрегировано произвольно с помощью произвольной функции агрегирования (не нужно вручную добавлять отдельные столбцы):

currents_other_layers <- 
  currents[df1, on = .(Datetime = current.dt, Site)][
    !df1, on = .(Datetime = current.dt, Site, layer)][
      !is.na(Depth), mean(current), by = .(i.Datetime, Site)]

currents_other_layers
            i.Datetime Site     V1
1: 2016-08-01 15:34:07   BD 3.7500
2: 2016-08-01 17:29:16   BD 5.9375
3: 2016-08-01 18:33:16   BD 5.9375
4: 2016-08-01 22:48:16   BD 2.8750
5: 2016-08-01 16:25:16   HG     NA

ЭтоРезультат содержит средние токи всех других слоев, кроме наблюдаемого слоя.Обратите внимание, что группировка производится по i.Datetime, что относится к df1$Datetime и Site.Строки, в которых Depth отсутствует в df1, не соответствуют ожидаемому результату OP.

Финальное объединение обновлений добавляет столбец результатов к df1.

1 голос
/ 17 мая 2019

Используя data.table, вы можете выполнить скользящее соединение между двумя вашими базами данных, чтобы связать ваши переменные глубины с текущими переменными, даже если времена не совпадают.То, что делает скользящее объединение, это связывает одну таблицу с другой, по которой время является самым близким (согласно выбранным параметрам).Я немного изменил ваши данные так, чтобы дни совпадали

library(data.table)

df1<-data.frame(Datetime=c("2016-08-01 15:34:07","2016-08-01 16:25:16","2016-08-01 17:29:16","2016-08-01 18:33:16","2016-08-01 20:54:16","2016-08-01 22:48:16"),Site=c("BD","HG","BD","BD","BD","BD"),Ind=c(16,17,19,16,17,16), Depth=c(5.3,24,36.4,42,NA,22.1))
df1$Datetime<-as.POSIXct(df1$Datetime, format="%Y-%m-%d %H:%M:%S",tz="UTC")

df2<-data.frame(Datetime=c("2016-08-01 12:00:00","2016-08-01 15:00:00","2016-08-01 18:00:00","2016-08-01 21:00:00","2016-08-02 00:00:00"), Site=c("BD","BD","BD","BD","BD"),var1=c(2.75,4,6.75,2.25,4.3),var2=c(3,4,4.75,3,2.1),var3=c(2.75,4,5.75,2.25,1.4),var4=c(3.25,3,6.5,2.75,3.4),var5=c(3,4,4.75,3,1.7))
df2$Datetime<-as.POSIXct(df2$Datetime, format="%Y-%m-%d %H:%M:%S",tz="UTC")
colnames(df2)<-c("Datetime","Site","m0-7","m8-15","m16-23","m24-31","m32-39")

setDT(df1)
setDT(df2)

setkey(df1, Site, Datetime)
setkey(df2, Site, Datetime)

df_merge = df2[df1, roll = Inf]

Затем я использую case_wp drlyr для вычисления токов для других глубин

library(dplyr)

df_merge[, current_elsewhere := case_when(
  is.na(Depth) ~ NA_real_,
  Depth < 7 ~ (`m8-15` + `m16-23` + `m24-31` + `m32-39`)/4,
  Depth < 15 ~ (`m0-7` + `m16-23` + `m24-31` + `m32-39`)/4,
  Depth < 23 ~ (`m0-7` + `m8-15` + `m24-31` + `m32-39`)/4,
  Depth < 31 ~ (`m0-7` + `m8-15` + `m16-23` + `m32-39`)/4,
  T ~ (`m0-7` + `m8-15` + `m16-23` + `m24-31`)/4)]

df_merge
              Datetime Site m0-7 m8-15 m16-23 m24-31 m32-39 Ind Depth current_elsewhere
1: 2016-08-01 15:34:07   BD 4.00  4.00   4.00   3.00   4.00  16   5.3            3.7500
2: 2016-08-01 17:29:16   BD 4.00  4.00   4.00   3.00   4.00  19  36.4            3.7500
3: 2016-08-01 18:33:16   BD 6.75  4.75   5.75   6.50   4.75  16  42.0            5.9375
4: 2016-08-01 20:54:16   BD 6.75  4.75   5.75   6.50   4.75  17    NA                NA
5: 2016-08-01 22:48:16   BD 2.25  3.00   2.25   2.75   3.00  16  22.1            2.7500
6: 2016-08-01 16:25:16   HG   NA    NA     NA     NA     NA  17  24.0                NA
...