tidyr ::收集缺少数据的na.rm

时间:2017-05-25 21:06:06

标签: r tidyr tidyverse

假设我在一个数据框中有多个列来衡量相同的概念,但是在不同的方法中(例如,有多种智商测试,学生可以拥有其中任何一个,或者根本没有)。我想将各种方法组合成一个列(tidyr的明显用例)。

如果数据是这样的:

mydata <- data.frame(ID = 55:64, 
                 age = c(12, 12, 14, 11, 20, 10, 13, 15, 18, 17),
                 Test1 = c(100, 90, 88, 115, NA, NA, NA, NA, NA, NA),
                 Test2 = c(NA, NA, NA, NA, 100, 120, NA, NA, NA, NA),
                 Test3 = c( NA, NA, NA, NA, NA, NA, 110, NA, 85, 150))

我自然希望执行类似这样的操作(请注意,我使用na.rm = TRUE以便在我的数据集中没有多个NA&#39;来获取自己的行):

library(tidyr)
tests <- gather(mydata, key=IQSource, value=IQValue, c(Test1, Test2, Test3), na.rm = TRUE)
tests

给我:

ID age IQSource IQValue 1 55 12 Test1 100 2 56 12 Test1 90 3 57 14 Test1 88 4 58 11 Test1 115 15 59 20 Test2 100 16 60 10 Test2 120 27 61 13 Test3 110 29 63 18 Test3 85 30 64 17 Test3 150

问题是,我有一个学生(ID = 62),三个中的任何一个都没有任何的IQ分数,我不想失去她其他数据(ID和年龄列中的数据)。

有没有办法区分tidyr,是的,我想删除NA,我至少在一个栏目中收集数据,但同时又要防止收集的所有列都是NA时数据丢失?)

3 个答案:

答案 0 :(得分:3)

我确实没有找到直接解决方案,但您可以right_join支持原始data.frame,然后取消选择您不需要的所有列。

library(tidyr)
library(dplyr)

mydata %>% 
  gather(key, val, Test1:Test3, na.rm = T) %>%
  right_join(mydata) %>% 
  select(-contains("Test"))
#> Joining, by = c("ID", "age")
#>    ID age   key val
#> 1  55  12 Test1 100
#> 2  56  12 Test1  90
#> 3  57  14 Test1  88
#> 4  58  11 Test1 115
#> 5  59  20 Test2 100
#> 6  60  10 Test2 120
#> 7  61  13 Test3 110
#> 8  62  15  <NA>  NA
#> 9  63  18 Test3  85
#> 10 64  17 Test3 150

或者,您当然可以首先创建一个包含您要保留的所有变量的data.frame,然后加入它:

id_data <- select(mydata, ID, age)

mydata %>% 
  gather(key, val, Test1:Test3, na.rm = T) %>%
  right_join(id_data)

答案 1 :(得分:1)

我认为这会为你解决问题:

    # make another data frame which has just ID and whether or not they missed all 3 tests
    missing = mydata %>% 
      mutate(allNA = is.na(Test1) & is.na(Test2) & is.na(Test3)) %>%
      select(ID, allNA)

    # Gather and keep NAs  
    tests <- gather(mydata, key=IQSource, value=IQValue, c(Test1, Test2, Test3), na.rm = FALSE)

    # Keep the rows that have a IQValue or missed all tests
    tests = left_join(tests, missing) %>% 
      filter(!is.na(IQValue) | allNA)
    # Remove duplicated rows of individuals who missed all exams
    tests = tests[!is.na(tests$IQValue) | !duplicated(tests[["ID"]]), ]

答案 2 :(得分:1)

如果学生每人只能进行一次智商测试......

library(tidyverse)

mydata %>%
  gather(key=IQSource, value=IQValue, Test1:Test3) %>%
  group_by(ID) %>%
  arrange(IQValue) %>%
  slice(1)
      ID   age IQSource IQValue
 1    55    12    Test1     100
 2    56    12    Test1      90
 3    57    14    Test1      88
 4    58    11    Test1     115
 5    59    20    Test2     100
 6    60    10    Test2     120
 7    61    13    Test3     110
 8    62    15    Test1      NA
 9    63    18    Test3      85
10    64    17    Test3     150

如果学生每人都有多项智商测试......

mydata %>%
  # Add an ID with multiple IQ tests
  bind_rows(data.frame(ID=65, age=13, Test1=100, Test2=100, Test3=NA)) %>%
  gather(key=IQSource, value=IQValue, Test1:Test3) %>%
  group_by(ID) %>%
  filter(!is.na(IQValue) | all(is.na(IQValue))) %>%
  filter(all(!is.na(IQValue)) | !duplicated(IQValue)) %>%
  arrange(ID, IQSource)
      ID   age IQSource IQValue
 1    55    12    Test1     100
 2    56    12    Test1      90
 3    57    14    Test1      88
 4    58    11    Test1     115
 5    59    20    Test2     100
 6    60    10    Test2     120
 7    61    13    Test3     110
 8    62    15    Test1      NA
 9    63    18    Test3      85
10    64    17    Test3     150
11    65    13    Test1     100
12    65    13    Test2     100