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

问题描述 投票:0回答:3

我想通过附加到以其他列的值为条件的列表来创建一个新列。如果可能的话,我想在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中使用mutate,在case_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语句,但也无法附加到列表中。

r list if-statement dplyr case-when
3个回答
3
投票

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

我们显示列表和字符列解决方案。这个问题具体是指列表,所以这是假设的期望输出,但如果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,嵌套它,将其排序回原始顺序并用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可重复的形式:

Lines <- "col1 col2 col3 col4
dog  cat  NA   NA
NA   cat  foo  bar
dog  NA   NA   NA
NA   cat  NA   NA
NA   NA   NA   NA"
DF <- read.table(text = Lines,  header = TRUE, as.is = TRUE)

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
投票

这看起来像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

© www.soinside.com 2019 - 2024. All rights reserved.