Как создать `df2` с четырьмя числовыми переменными, используя три основные переменные и одну числовую переменную из `df`

#r #dplyr

#r #dplyr

Вопрос:

У меня есть следующий фрейм данных:

   df <- data.frame(ID=c(rep(c("A"),8),rep(c("B"),8)),
                   Datetime=c("2020-08-05 12:00:00","2020-08-05 17:00:00","2020-08-05 18:03:00","2020-08-05 22:54:00","2020-08-06 01:08:00","2020-08-06 13:26:00","2020-08-06 19:04:00","2020-08-08 11:00:00",
                              "2020-08-04 03:00:00","2020-08-04 15:00:00","2020-08-04 23:00:00","2020-08-06 14:00:00","2020-08-06 17:00:00","2020-08-06 20:00:00","2020-08-07 04:00:00","2020-08-07 16:00:00"),
                   Period=c("Day","Day","Day","Night","Night","Day","Night","Day","Night","Day","Night","Day","Day","Night","Night","Day"),
                   State=c(1,2,1,1,1,1,2,2,1,1,1,2,2,1,1,1),
                   Acc=c(1.1,2.3,1.7,1.4,0.1,1.9,2.9,2.3,1.1,0.1,1.4,0.2,2.6,1.3,1.7,1.0))
  
  df$Datetime <- as.POSIXct(df$Datetime,format="%Y-%m-%d %H:%M:%S", tz="UTC")
  
  df

   ID            Datetime Period State Acc
1   A 2020-08-05 12:00:00    Day     1 1.1
2   A 2020-08-05 17:00:00    Day     2 2.3
3   A 2020-08-05 18:03:00    Day     1 1.7
4   A 2020-08-05 22:54:00  Night     1 1.4
5   A 2020-08-06 01:08:00  Night     1 0.1
6   A 2020-08-06 13:26:00    Day     1 1.9
7   A 2020-08-06 19:04:00  Night     2 2.9
8   A 2020-08-08 11:00:00    Day     2 2.3
9   B 2020-08-04 03:00:00  Night     1 1.1
10  B 2020-08-04 15:00:00    Day     1 0.1
11  B 2020-08-04 23:00:00  Night     1 1.4
12  B 2020-08-06 14:00:00    Day     2 0.2
13  B 2020-08-06 17:00:00    Day     2 2.6
14  B 2020-08-06 20:00:00  Night     1 1.3
15  B 2020-08-07 04:00:00  Night     1 1.7
16  B 2020-08-07 16:00:00    Day     1 1.0
  

Мне нужно оценить среднее значение Acc per ID , day , Period и State . Мне понадобится такой фрейм данных:

   ID   Datetime State.1_day State.1_night State.2_day State.2_night
1  A 2020-08-04          NA            NA          NA            NA # This day there was no data for `A`, but there was for `B`.
2  A 2020-08-05         1.4           1.4         2.3            NA
.  .      .              .             .           .              .
.  .      .              .             .           .              .
.  .      .              .             .           .              .

  

Кто-нибудь знает, как это получить? Я не знаю, как вычислить среднее значение, используя все эти переменные сразу, а затем создать столбцы, которые я показал.

Заранее спасибо

Ответ №1:

Вот один из подходов с использованием tidyverse. Если вы хотите показать все дни, даже если они отсутствуют для некоторых идентификаторов, тогда используйте второй подход.

 library(tidyverse)

# your data
df <- data.frame(ID=c(rep(c("A"),8),rep(c("B"),8)),
                 Datetime=c("2020-08-05 12:00:00","2020-08-05 17:00:00","2020-08-05 18:03:00","2020-08-05 22:54:00","2020-08-06 01:08:00","2020-08-06 13:26:00","2020-08-06 19:04:00","2020-08-08 11:00:00",
                            "2020-08-04 03:00:00","2020-08-04 15:00:00","2020-08-04 23:00:00","2020-08-06 14:00:00","2020-08-06 17:00:00","2020-08-06 20:00:00","2020-08-07 04:00:00","2020-08-07 16:00:00"),
                 Period=c("Day","Day","Day","Night","Night","Day","Night","Day","Night","Day","Night","Day","Day","Night","Night","Day"),
                 State=c(1,2,1,1,1,1,2,2,1,1,1,2,2,1,1,1),
                 Acc=c(1.1,2.3,1.7,1.4,0.1,1.9,2.9,2.3,1.1,0.1,1.4,0.2,2.6,1.3,1.7,1.0))

df$Datetime <- as.POSIXct(df$Datetime,format="%Y-%m-%d %H:%M:%S", tz="UTC")

# first approach
df %>% 
  group_by(ID, Day = as.Date(Datetime), Period, State) %>% 
  summarise(Acc = mean(Acc, na.rm = TRUE)) %>% 
  pivot_wider(names_from = c(State, Period),
              values_from = Acc,
              names_prefix = "State.")

#> `summarise()` regrouping output by 'ID', 'Day', 'Period' (override with `.groups` argument)
#> # A tibble: 6 x 6
#> # Groups:   ID, Day [6]
#>   ID    Day        State.1_Day State.2_Day State.1_Night State.2_Night
#>   <chr> <date>           <dbl>       <dbl>         <dbl>         <dbl>
#> 1 A     2020-08-05         1.4         2.3          1.4           NA  
#> 2 A     2020-08-06         1.9        NA            0.1            2.9
#> 3 A     2020-08-08        NA           2.3         NA             NA  
#> 4 B     2020-08-04         0.1        NA            1.25          NA  
#> 5 B     2020-08-06        NA           1.4          1.3           NA  
#> 6 B     2020-08-07         1          NA            1.7           NA

# second approach
df %>% 
  group_by(ID, Day = as.factor(as.Date(Datetime)), Period, State, .drop = FALSE) %>% 
  summarise(Acc = mean(Acc, na.rm = TRUE)) %>% 
  pivot_wider(names_from = c(State, Period),
              values_from = Acc,
              names_prefix = "State.") %>% 
 select(!State.NA_NA)

#> `summarise()` regrouping output by 'ID', 'Day', 'Period' (override with `.groups` argument)
#> # A tibble: 10 x 6
#> # Groups:   ID, Day [10]
#>    ID    Day        State.1_Day State.2_Day State.1_Night State.2_Night
#>    <chr> <fct>            <dbl>       <dbl>         <dbl>         <dbl>
#>  1 A     2020-08-04        NA          NA           NA             NA  
#>  2 A     2020-08-05         1.4         2.3          1.4           NA  
#>  3 A     2020-08-06         1.9        NA            0.1            2.9
#>  4 A     2020-08-07        NA          NA           NA             NA  
#>  5 A     2020-08-08        NA           2.3         NA             NA  
#>  6 B     2020-08-04         0.1        NA            1.25          NA  
#>  7 B     2020-08-05        NA          NA           NA             NA  
#>  8 B     2020-08-06        NA           1.4          1.3           NA  
#>  9 B     2020-08-07         1          NA            1.7           NA  
#> 10 B     2020-08-08        NA          NA           NA             NA
  

Создано 2020-11-10 пакетом reprex (версия 0.3.0)

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

1. Спасибо @TimTeaFan, вы знаете, как включить тот же диапазон дат для A и B ? Я хочу построить график этих значений сверхурочно для разных людей, и было бы полезно, если бы для всех из них даты были одинаковыми. В этом примере мне нужно было бы иметь для каждой ID даты между 2020-08-04 и 2020-08-08 . Возможно ли это?

2. Вы видели мой второй подход? В нем должны быть указаны все даты для каждой ID . Я преобразую дату в коэффициент, а затем использую .drop = FALSE аргумент in group_by .

3. Извините, я этого не видел! Теперь это выглядит идеально. Спасибо за ваше время и этот замечательный ответ.

4. Нет проблем, я обновил сразу после моего первоначального сообщения 😉

5. Привет @TimTeaFan, теперь я использую предложенный вами код. Вот почему я еще не поставил вам зеленую отметку. Однако я обнаружил проблему. При использовании вашего второго подхода я получаю сообщение об ошибке, в нем говорится: «Ошибка в map_lgl (.x, .p, …): состояние объекта «не найдено». Вы знаете, почему? Я предполагаю, что это должно быть что-то глупое, но я не понимаю, что.