通过将值附加到其他列的列表来在 dplyr 中创建一个新列?
Create a new column in dplyr by appending values to a list from other columns?
我想通过附加到以其他列的值为条件的列表来创建一个新列。如果可能的话,我想在 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)
我曾尝试在 mutate
中使用 ifelse
并在 mutate
中使用 case_when
,但两者都不允许连接到列表。这是我对 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
语句,但也无法附加到列表中。
使用 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!
在最后的注释中,我们显示了此处使用的输入数据。就像问题中一样,只是我们在末尾添加了一行 NA 以表明所有解决方案也适用于这种情况。
我们展示了列表和字符列解决方案。这个问题具体指的是列表,所以这是假定的所需输出,但如果打算 newCol
是一个字符向量,那么我们也会显示它。
使用基本函数很容易做到这一点,我们首先展示了这一点;然而,我们在 tidyverse 中重做它,尽管它涉及更多的代码。
1) base 我们可以这样使用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)
我想通过附加到以其他列的值为条件的列表来创建一个新列。如果可能的话,我想在 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)
我曾尝试在 mutate
中使用 ifelse
并在 mutate
中使用 case_when
,但两者都不允许连接到列表。这是我对 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
语句,但也无法附加到列表中。
使用 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!
在最后的注释中,我们显示了此处使用的输入数据。就像问题中一样,只是我们在末尾添加了一行 NA 以表明所有解决方案也适用于这种情况。
我们展示了列表和字符列解决方案。这个问题具体指的是列表,所以这是假定的所需输出,但如果打算 newCol
是一个字符向量,那么我们也会显示它。
使用基本函数很容易做到这一点,我们首先展示了这一点;然而,我们在 tidyverse 中重做它,尽管它涉及更多的代码。
1) base 我们可以这样使用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)