如何从多个带有NA的列中创建更少的列?

时间:2019-08-01 13:37:38

标签: r

我已将长格式的data.frame转换为width,以便将其与另一个数据框合并。当我将多边型转换为宽型时,我得到了很多NA,我想消除这些NA,并使用现有数据创建一些新列。

对于相同的ID,长数据可以具有多个级别。我希望所有级别都采用较宽的格式,而不是较长的格式。因为我的长数据有40多个级别,所以当我使用“ dcast”将其转换为宽级别时,我会得到很多带有大量NA的列。我尝试了许多方法来合并这些列,以消除尽可能多的NA,但这是行不通的。

我的数据如下:

ID | Date | Gender | Age | Name1 | Name2 | Name3 | Name4 | ... | NameN |
----------------------------------------------------------------------
1    1/1      F       1     NA     Name2    Name3   NA           NameN
2    2/2      M       2     NA      NA      Name3   NA             NA
3    3/3      F       3     NA     Name2    Name3   NA             NA
4    4/4      F       4    Name1    NA      Name3   NA             NA
5    5/5      F       5     NA      NA       NA    Name4           NA
6    6/6      M       6     NA      NA       NA     NA             NA
7    7/7      F       7     NA      NA       NA     NA             NA
8    8/8      F       8     NA      NA       NA     NA             NA

我想得到类似这样的东西

ID | Date | Gender | Age | Risk1 | Risk2| ...| RiskN |
------------------------------------------------------
1    1/1      F       1    Name2   Name3 NameN    
2    2/2      M       2    Name3    NA    NA     
3    3/3      F       3    Name2   Name3  NA
4    4/4      F       4    Name1   Name3  NA
5    5/5      F       5    Name4    NA    NA
6    6/6      M       6     NA      NA    NA
7    7/7      F       7     NA      NA    NA
8    8/8      F       8     NA      NA    NA

Edit1:感谢您的回答,不幸的是,它们都没有提供预期的输出。我编辑了上面的数据,以包括我的数据中另外一些条目,这些条目将被完全排除在外。我也有45个变量(Name1,Name2 ... Name45)。根据我收到的第二个答案,我应该只剩下9个Risk变量。抱歉造成混乱!

第一个答案的输出是消除所有类似于6:8行的行。另外,其余数据看起来与上面的预期不同,但更像是:

ID | Date | Gender | Age | RiskName1 | RiskName2 | RiskName3 | RiskName4 | ... | RiskNameN
------------------------------------------------------------------------------------------
4    4/4      F       4    Name1           NA       Name3         NA           NA
1    1/1      F       1     NA           Name2      Name3         NA        NameN
3    3/3      F       3     NA           Name2      Name3         NA           NA
2    2/2      M       2     NA             NA       Name3         NA           NA
5    5/5      F       5     NA             NA        NA         Name4          NA

第二个答案仍然消除了类似于6:8的数据,但是在实际上消除了现有的大量列方面表现更好,但是它用数字代替了所有行内容。例如

ID | Date | Gender | Age | Risk1 | Risk2| Risk3 |
-------------------------------------------------
1    1/1      F       1      1       1      1    
2    2/2      M       2      1       0      0    
3    3/3      F       3      1       1      0
4    4/4      F       4      1       1      0
5    5/5      F       5      1       0      0

Edit2: 数据很敏感,但是我创建了一个非常相似的结构供您使用。谢谢!

样本数据:

structure(list(Ref = c("213", "42", "512", "123","421"), Start = structure(c(1541912880, 1541912880, 1541918160,1541918160,1542024180), class = c("POSIXct", "POSIXt"), tzone = "UTC"),Age = c(1, 7, 8, 6, 3), Gender = c("Female", "Male", "Female","Female", "Female"), Ethnicity = c("E2", "E1", "E4", "E1", "E1"), Cats = c("cats", "cats", NA_character_,NA_character_, NA_character_), Dogs = c(NA_character_,NA_character_, NA_character_, "dogs", NA_character_), Iguanas = c(NA_character_, "Iguanas", NA_character_, "Iguanas", NA_character_), Coalas = c(NA_character_, NA_character_, NA_character_, NA_character_, NA_character_), Ducks = c("ducks", NA_character_,"ducks",NA_character_, NA_character_)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"))

我希望它看起来如何:

Ref | Date        | Gender | Age | Risk1 | Risk2| Risk3 |
---------------------------------------------------------
213    2018-11-11      F       1    cats   ducks     NA    
42     2018-11-11      M       7    cats   Iguanas   NA    
512    2018-11-11      F       8    ducks    NA      NA
123    2018-11-11      F       6    dogs   Iguanas   NA
421    2018-11-12      F       3     NA      NA      NA

2 个答案:

答案 0 :(得分:3)

一个选项是将“名称”列gather转换为“长”格式,同时用NA删除na.rm = TRUE,然后按“ ID”分组,将“风险”创建为序列列,然后spread回到“宽”格式

library(tidyverse)
gather(df1, Risk, val, starts_with("Name"), na.rm = TRUE) %>%
      group_by(ID) %>%
      mutate(Risk = str_c("Risk", Risk)) %>%
      spread(Risk, val)

使用新的更新数据集

df2 %>%
  gather(Risk, val,  Cats:Ducks) %>% 
  mutate(Ref = factor(Ref, levels = unique(Ref))) %>% 
  arrange(Ref, is.na(val)) %>%
  group_by(Ref) %>%
  slice(if(all(is.na(val))) 1 else which(!is.na(val))) %>% 
  mutate(Risk = str_c('Risk', row_number())) %>%
  spread(Risk, val)
# A tibble: 5 x 7
# Groups:   Ref [5]
#  Ref   Start                 Age Gender Ethnicity Risk1 Risk2  
#  <fct> <dttm>              <dbl> <chr>  <chr>     <chr> <chr>  
#1 213   2018-11-11 05:08:00     1 Female E2        cats  ducks  
#2 42    2018-11-11 05:08:00     7 Male   E1        cats  Iguanas
#3 512   2018-11-11 06:36:00     8 Female E4        ducks <NA>   
#4 123   2018-11-11 06:36:00     6 Female E1        dogs  Iguanas
#5 421   2018-11-12 12:03:00     3 Female E1        <NA>  <NA>   

答案 1 :(得分:2)

使用data.table类似的转换为长距离然后返回宽距离的方法

library(data.table)
setDT(df)

long <- melt(df, which(!names(df) %like% 'Name'), na.rm = T)

dcast(long[, -'variable'], ... ~ paste0('Risk', rowid(ID)))

#    Date Gender Age Risk1 Risk2
# 1:  1/1      F   1 Name2 Name3
# 2:  2/2      M   2 Name3  <NA>
# 3:  3/3      F   3 Name2 Name3
# 4:  4/4      F   4 Name1 Name3
# 5:  5/5      F   5 Name4  <NA>

使用的数据:

df <- fread('
ID  Date  Gender  Age  Name1  Name2  Name3  Name4 
1    1/1      F       1     NA     Name2    Name3   NA
2    2/2      M       2     NA      NA      Name3   NA
3    3/3      F       3     NA     Name2    Name3   NA
4    4/4      F       4    Name1    NA      Name3   NA
5    5/5      F       5     NA      NA       NA    Name4
')