【问题标题】:Gathering nonempty rows (POSIXct format) from diffrent columns for the same UserID in a single row在单行中为同一 UserID 从不同列收集非空行(POSIXct 格式)
【发布时间】:2019-01-06 09:35:56
【问题描述】:

我有一个带有重复 UserId 的数据框 df,其中通常对应列 A、B、C 中的至少一行是非空的。列 A、B、C 包含 NA 值和日期。我的目标是在一行中为同一 UserID 收集所有非空(如果该 UserID 的整个列为 NA,则为 NA)行。

我尝试将 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

【问题讨论】:

    标签: r dataframe filter group-by dplyr


    【解决方案1】:

    我们可以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
    

    【讨论】:

      【解决方案2】:

      我们可以从data.table使用melt/dcast

      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)的相似性而提及。

      【讨论】:

      • 两种解决方案都有效,但 melt/dcast 解决方案在没有任何警告的情况下有效。如果 tidyverse 警告是:'...属性在度量变量之间不相同;他们将被丢弃'。谢谢!
      • @Imitation 谢谢,警告只是因为 A:C 列是 factor 类并且它们具有不同的级别。如果是character,则不会有任何警告。这只是一个友好的警告。没什么好担心的
      【解决方案3】:

      你可以在两个方向填写已知值,然后在data.frame上使用distinct:

      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
      

      【讨论】:

        猜你喜欢
        • 2019-03-16
        • 1970-01-01
        • 2014-09-22
        • 2016-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多