通过将值附加到其他列的列表中,在dplyr中创建一个新列?

时间:2017-12-28 23:42:42

标签: r list if-statement dplyr case-when

我想通过附加到以其他列的值为条件的列表来创建新列。如果可能的话,我想在dplyr中这样做。样本输入和所需输出如下。

假设数据框newdata

col1 col2 col3 col4
dog  cat  NA   NA
NA   cat  foo  bar
dog  NA   NA   NA
NA   cat  NA   NA

这是我想要的输出,新列newCol

col1 col2 col3 col4 newCol
dog  cat  NA   NA   (dog, cat)
NA   cat  foo  bar  (cat, foo, bar)
dog  NA   NA   NA   (dog)
NA   cat  NA   bar  (cat, bar)

我尝试在ifelse内使用mutatecase_when内使用mutate,但两者都不允许连接到列表。以下是case_when

的尝试(不成功)
newdata = newdata %>% mutate( 
    newCol = case_when(
        col1 == "dog" ~ c("dog"),
        col2 == "cat" ~ c(newCol, "cat"),
        col3 == "foo" ~ c(newCol, "foo"),
        col4 == "bar" ~ c(newcol, "dog")
        )
    )

我尝试了一种类似的方法,每个列都有ifelse语句,但也无法附加到列表中。

3 个答案:

答案 0 :(得分:3)

使用na.omit()paste() collapse参数的解决方案:

apply(newdata, 1, 
      function(x) paste0("(", paste(na.omit(x), collapse = ", "), ")"))
[1] "(dog, cat)" "(cat, foo, bar)" "(dog)" "(cat)"

Demo

答案 1 :(得分:3)

在最后的注释中,我们显示了此处使用的输入数据。就像问题一样,除了我们在最后添加了一行NA以表明所有解决方案都适用于那种情况。

我们同时显示列表和字符列解决方案。问题具体是指列表所以这是假定的期望输出,但如果newCol是一个字符向量,那么我们也会显示它。

使用我们首先显示的基本函数很容易做到这一点;但是,我们在tidyverse中重做它,尽管它涉及更多的代码。

1)基础我们可以像这样使用apply

reduce <- function(x) unname(x[!is.na(x)])
DF$newCol <- apply(DF, 1, reduce)

给出以下内容newCol是第一个成分为c("dog", "cat")的列表,等等。

  col1 col2 col3 col4        newCol
1  dog  cat <NA> <NA>      dog, cat
2 <NA>  cat  foo  bar cat, foo, bar
3  dog <NA> <NA> <NA>           dog
4 <NA>  cat <NA> <NA>           cat
5 <NA> <NA> <NA> <NA>              

最后一行代码可以是:

DF$newCol <- lapply(split(DF, 1:nrow(DF)), reduce)

问题是指连接到列表,所以我假设newCol需要一个列表,但如果需要一个字符串,那么请使用它来代替:

reduce_ch <- function(x) sprintf("(%s)", toString(x[!is.na(x)]))
apply(DF, 1, reduce_ch)

2)tidyverse 或使用tpldyr / tidyr / tibble我们将其收集到长格式,删除NA,嵌套,将其重新排序为原始顺序并使用{{1}将其重新绑定}。

DF

,并提供:

library(dplyr)
library(tibble)
library(tidyr)

DF %>%
   rownames_to_column %>%
   gather(colName, Value, -rowname) %>%
   na.omit %>%
   select(-colName) %>%
   nest(Value, .key = newCol) %>%
   arrange(rowname) %>%
   left_join(cbind(DF %>% rownames_to_column), .) %>% 
   select(-rowname)

如果需要字符输出,请改用:

  col1 col2 col3 col4        newCol
1  dog  cat <NA> <NA>      dog, cat
2 <NA>  cat  foo  bar cat, foo, bar
3  dog <NA> <NA> <NA>           dog
4 <NA>  cat <NA> <NA>           cat
5 <NA> <NA> <NA> <NA>          NULL

,并提供:

DF %>%
   rownames_to_column %>%
   gather(colName, Value, -rowname) %>%
   select(-colName) %>%
   group_by(rowname) %>%
   summarize(newCol = sprintf("(%s)", toString(na.omit(Value)))) %>%
   ungroup %>%
   { cbind(DF, .) } %>%
   select(-rowname)

注意

输入 col1 col2 col3 col4 newCol 1 dog cat <NA> <NA> (dog, cat) 2 <NA> cat foo bar (cat, foo, bar) 3 dog <NA> <NA> <NA> (dog) 4 <NA> cat <NA> <NA> (cat) 5 <NA> <NA> <NA> <NA> () ,可重复的形式:

DF

答案 2 :(得分:1)

这看起来像tidyr::unite的用例。你最后还是需要做一些dplyr清理工作,但这应该适用于现在。

library(tibble)
library(dplyr)
library(tidyr)

df <- tribble(~col1, ~col2, ~col3, ~col4,
              "dog", "cat", NA, NA,
              NA, "cat", "foo", "bar",
              "dog", NA, NA, NA,
              NA, "cat", NA, NA)

df %>%
  unite(newCol, col1, col2, col3, col4,
        remove = FALSE,
        sep = ', ') %>%
  # Replace NAs and "NA, "s with ''
  mutate(newCol = gsub('NA[, ]*', '', newCol)) %>%
  # Replace ', ' with '' if it is at the end of the line
  mutate(newCol = gsub(', $', '', newCol)) %>%
  # Add the parentheses on either side
  mutate(newCol = paste0('(', newCol, ')'))
#> # A tibble: 4 x 5
#>            newCol  col1  col2  col3  col4
#>             <chr> <chr> <chr> <chr> <chr>
#> 1      (dog, cat)   dog   cat  <NA>  <NA>
#> 2 (cat, foo, bar)  <NA>   cat   foo   bar
#> 3           (dog)   dog  <NA>  <NA>  <NA>
#> 4           (cat)  <NA>   cat  <NA>  <NA>

另外,对于它的价值,其他人是discussing this problem