简体   繁体   中英

R: Return rows with only 1 non-NA value for a set of columns

Suppose I have a data.table with the following data:

colA  colB  colC  result
1     2     3     231
1     NA    2     123
NA    3     NA    345
11    NA    NA    754

How would I use dplyr and magrittr to only select the following rows:

colA  colB  colC result
NA    3     NA   345
11    NA    NA   754

The selection criteria is: only 1 non-NA value for columns AC (ie colA, colB, ColC )

I have been unable to find a similar question; guessing this is an odd situation.

A base R option would be

df[apply(df, 1, function(x) sum(!is.na(x)) == 1), ]
#  colA colB colC
#3   NA    3   NA
#4   11   NA   NA

A dplyr option is

df %>% filter(rowSums(!is.na(.)) == 1)

Update

In response to your comment, you can do

df[apply(df[, -ncol(df)], 1, function(x) sum(!is.na(x)) == 1), ]
#  colA colB colC result
#3   NA    3   NA    345
#4   11   NA   NA    754

Or the same in dplyr

df %>% filter(rowSums(!is.na(.[-length(.)])) == 1)

This assumes that the last column is the one you'd like to ignore.


Sample data

df <-read.table(text = "colA  colB  colC
1     2     3
1     NA    2
NA    3     NA
11    NA    NA", header = T)

Sample data for update

df <- read.table(text =
"colA  colB  colC  result
1     2     3     231
1     NA    2     123
NA    3     NA    345
11    NA    NA    754
", header = T)

Another option is filter with map

library(dplyr)
library(purrr)
df %>% 
    filter(map(select(., starts_with('col')), ~ !is.na(.)) %>% 
              reduce(`+`) == 1)
#    colA colB colC result
#1   NA    3   NA    345
#2   11   NA   NA    754

Or another option is to use transmute_at

df %>% 
   transmute_at(vars(starts_with('col')), ~ !is.na(.)) %>% 
   reduce(`+`) %>%
   magrittr::equals(1) %>% filter(df, .)
#  colA colB colC result
#1   NA    3   NA    345
#2   11   NA   NA    754

data

df <- structure(list(colA = c(1L, 1L, NA, 11L), colB = c(2L, NA, 3L, 
NA), colC = c(3L, 2L, NA, NA), result = c(231L, 123L, 345L, 754L
)), class = "data.frame", row.names = c(NA, -4L))

I think this would be possible with filter_at but I was not able to make it work. Here is one attempt with filter and pmap_lgl where you can specify the range of columns in select or specify by their positions or use other tidyselect helper variables.

library(dplyr)
library(purrr)

df %>%
  filter(pmap_lgl(select(., colA:colC), ~sum(!is.na(c(...))) == 1))

 #  colA colB colC result
#1   NA    3   NA    345
#2   11   NA   NA    754

data

df <- structure(list(colA = c(1L, 1L, NA, 11L), colB = c(2L, NA, 3L, 
NA), colC = c(3L, 2L, NA, NA), result = c(231L, 123L, 345L, 754L
)), class = "data.frame", row.names = c(NA, -4L))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM