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