我想通过附加到以其他列的值为条件的列表来创建一个新列。如果可能的话,我想在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
语句,但也无法附加到列表中。
在最后的注释中,我们显示了此处使用的输入数据。就像问题一样,除了我们在最后添加了一行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)
使用na.omit()
和paste()
与collapse
参数的解决方案:
apply(newdata, 1,
function(x) paste0("(", paste(na.omit(x), collapse = ", "), ")"))
[1] "(dog, cat)" "(cat, foo, bar)" "(dog)" "(cat)"
这看起来像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!