paste column elements with condition in r

Viewed 294

I have a data frame and I want to paste elements in name1, name2 and name3 which do not contain NA.

c <- data.frame(name1 = letters[1:3],
                name2 = c('A', NA, 'C'),
                name3 = c('pig', 'cow', NA)
                )

The result should like this:

c %>% mutate(new_name = c('a&A&pig', 'b&cow', 'c&C'))

When I use paste0() it binds all the elements including NA. I do not want this.

c %>% mutate(new_name = paste0(name1,'&', name2, '&', name3))

Then I tried another two method. One is split the data frame into list with group_split(), the other is nest the data frame by index. And then use map() and select() to select the column that do not contain NA after the two methods but all failed.

 c %>% 
  mutate(index = row_number()) %>% 
  group_split(index) %>% 
  map(select(~where(~!any(is.na(.)))))

c %>% 
  mutate(index = row_number()) %>% 
  nest(data = name1:name3) %>% 
  mutate(without_NA_data = map(data, select(~where(~!any(is.na(.))))))

Is there any way I can get what I want? Any help will be highly appreciated!

2 Answers

We can use rowwise with c_across by loading only dplyr package

library(dplyr)    
c %>% 
   rowwise %>% 
   mutate(new_name = paste(na.omit(c_across(everything())), collapse="&")) %>%
   ungroup
# A tibble: 3 x 4
# name1 name2 name3 new_name
#  <chr> <chr> <chr> <chr>   
#1 a     A     pig   a&A&pig 
#2 b     <NA>  cow   b&cow   
#3 c     C     <NA>  c&C    

Or with pmap

library(purrr)
c %>% 
     mutate(new_name = pmap_chr(., ~ paste(na.omit(c(...)), collapse="&")))
#  name1 name2 name3 new_name
#1     a     A   pig  a&A&pig
#2     b  <NA>   cow    b&cow
#3     c     C  <NA>      c&C

Or using base R with paste and replace

trimws(do.call(paste, c(replace(c, is.na(c), ''), sep="&")), whitespace = "&")
#[1] "a&A&pig" "b&&cow"  "c&C"  

Or using apply

apply(c, 1, function(x)  paste(na.omit(x), collapse="&"))
#[1] "a&A&pig" "b&cow"   "c&C"  

Or paste first and remove the NA substring

gsub("&NA|NA&|NA$", "", do.call(paste, c(c, sep="&")))
#[1] "a&A&pig" "b&cow"   "c&C"    

We can use unite from tidyr by using na.rm = TRUE to remove NA values

tidyr::unite(c, new_name, starts_with('name'), 
                sep = '&', na.rm = TRUE, remove = FALSE)

#  new_name name1 name2 name3
#1  a&A&pig     a     A   pig
#2    b&cow     b  <NA>   cow
#3      c&C     c     C  <NA>
Related