Сбор непустых строк (формат POSIXct) из разных столбцов для одного и того же идентификатора пользователя в одной строке - PullRequest
0 голосов
/ 06 января 2019

У меня есть фрейм данных df с дублированным UserId, где обычно по крайней мере одна строка из соответствующих столбцов A, B, C непуста. Столбцы A, B, C содержат значения NA и даты. Моя цель - собрать все непустые (и NA, если весь столбец для этого UserID равен NA) строки для одного и того же UserID в одну строку.

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

library(dplyr)

Пример кадра данных

df<-data.frame(UserID=c(1,1,1,1,1,1,1,2,2,2,2,2,4,4,4,5,5,5,5), 
            A=c(NA,'2018-09-20 18:00:55' ,NA,NA,NA,NA,NA,NA,'2018-09-2018:00:55',NA, NA,NA,'2018-09-20 18:00:49',NA,NA,NA,NA,NA,NA),
            B=c(NA,NA ,'2018-09-20 18:00:42',NA,NA,NA,NA,NA,NA,'2018-09-20 18:00:55', NA,NA,NA,'2018-09-20 18:00:49',NA,NA,NA,NA,NA), 
            C=c('2018-09-20 18:00:38', NA,NA,NA,NA,NA,NA,'2018-09-20 18:00:40',NA,NA, NA,NA,NA,NA,NA,NA,'2018-09-20 18:00:49',NA,NA))`    


df

UserID                   A                   B                   C
1       1                <NA>                <NA> 2018-09-20 18:00:38
2       1 2018-09-20 18:00:55                <NA>                <NA>
3       1                <NA> 2018-09-20 18:00:42                <NA>
4       1                <NA>                <NA>                <NA>
5       1                <NA>                <NA>                <NA>
6       1                <NA>                <NA>                <NA>
7       1                <NA>                <NA>                <NA>
8       2                <NA>                <NA> 2018-09-20 18:00:40
9       2 2018-09-20 18:00:55                <NA>                <NA>
10      2                <NA> 2018-09-20 18:00:55                <NA>
11      2                <NA>                <NA>                <NA>
12      2                <NA>                <NA>                <NA>
13      4 2018-09-20 18:00:49                <NA>                <NA>
14      4                <NA> 2018-09-20 18:00:49                <NA>
15      4                <NA>                <NA>                <NA>
16      5                <NA>                <NA>                <NA>
17      5                <NA>                <NA> 2018-09-20 18:00:49
18      5                <NA>                <NA>                <NA>
19      5                <NA>                <NA>                <NA>

Код, который я пробовал

df2<-df %>% 
group_by(UserID) %>%
 filter(!is.na(A),  !is.na(B), !is.na(C))

Текущий результат df2 пуст

Ожидаемый результат -

df2

      UserID                   A                   B                   C
    1      1 2018-09-20 18:00:55 2018-09-20 18:00:42 2018-09-20 18:00:38
    2      2 2018-09-20 18:00:55 2018-09-20 18:00:55 2018-09-20 18:00:40
    3      4 2018-09-20 18:00:49 2018-09-20 18:00:49                <NA>
    4      5                <NA>                <NA> 2018-09-20 18:00:49

Ответы [ 3 ]

0 голосов
/ 06 января 2019

Вы можете заполнить известные значения в обоих направлениях, а затем использовать их в data.frame:

library(tidyverse)

df %>% 
  group_by(UserID) %>%
  fill(A:C) %>% 
  fill(A:C, .direction = "up") %>% 
  distinct()

# A tibble: 4 x 4
# Groups:   UserID [4]
# UserID A                   B                   C                  
# <dbl> <fct>               <fct>               <fct>              
# 1      1 2018-09-20 18:00:55 2018-09-20 18:00:42 2018-09-20 18:00:38
# 2      2 2018-09-2018:00:55  2018-09-20 18:00:55 2018-09-20 18:00:40
# 3      4 2018-09-20 18:00:49 2018-09-20 18:00:49 NA                 
# 4      5 NA                  NA                  2018-09-20 18:00:49
0 голосов
/ 06 января 2019

Мы можем использовать melt/dcast от data.table

library(data.table)
dcast(melt(setDT(df), id.var = 'UserID', na.rm = TRUE), UserID ~ variable)
# UserID                   A                   B                   C
#1:      1 2018-09-20 18:00:55 2018-09-20 18:00:42 2018-09-20 18:00:38
#2:      2  2018-09-2018:00:55 2018-09-20 18:00:55 2018-09-20 18:00:40
#3:      4 2018-09-20 18:00:49 2018-09-20 18:00:49                <NA>
#4:      5                <NA>                <NA> 2018-09-20 18:00:49

Кроме того, gather может принимать na.rm в качестве аргумента. Таким образом, подобный вариант в tidyverse будет (в дополнение к подходу @Ronak Shah с tidyverse)

library(tidyverse)
df %>%
   gather(key, value, -UserID, na.rm = TRUE) %>% 
   spread(key, value)
# UserID                   A                   B                   C
#1      1 2018-09-20 18:00:55 2018-09-20 18:00:42 2018-09-20 18:00:38
#2      2  2018-09-2018:00:55 2018-09-20 18:00:55 2018-09-20 18:00:40
#3      4 2018-09-20 18:00:49 2018-09-20 18:00:49                <NA>
#4      5                <NA>                <NA> 2018-09-20 18:00:49

ПРИМЕЧАНИЕ. Подход tidyverse является лишь небольшим изменением метода @ RonakShah. Было упомянуто только, чтобы показать сходство с основным решением (melt/dcast).

0 голосов
/ 06 января 2019

Мы можем gather датафрейм, удалить все значения NA и spread вернуть

library(tidyverse)

df %>%
  gather(key, value, - UserID) %>%
  na.omit() %>%
  spread(key, value)


#  UserID                   A                   B                   C
#1      1 2018-09-20 18:00:55 2018-09-20 18:00:42 2018-09-20 18:00:38
#2      2  2018-09-2018:00:55 2018-09-20 18:00:55 2018-09-20 18:00:40
#3      4 2018-09-20 18:00:49 2018-09-20 18:00:49                <NA>
#4      5                <NA>                <NA> 2018-09-20 18:00:49
...