2017-05-25 94 views
3

假设我在数据框中有多个列,它们测量相同的概念,但使用不同的方法(例如,有多种智商测试,学生可以有任何一种他们,或根本没有)。我想将各种方法合并到一个列中(tidyr的明显用例)。tidyr ::与缺少数据收集na.rm

如果数据是这样的:

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在我的数据设定得到自己行):

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),其中任何一个都没有任何智商分数,我不想丢失她的其他数据(ID和年龄的数据列)。

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

回答

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 
+0

我选择这个作为正确的答案b/c简单,坚持tidyverse,并扩大超出原来的要求。所有给出的答案都很棒,但是很有帮助!谢谢大家! – Joy

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"]]), ] 
3

我did'nt找到一个直接的解决方案,但你可以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

韦尔普这比我更好了很多。谢谢! – svenhalvorson