如何对未指定的列执行条件变异(例如匹配正则表达式)?
How to perform conditional mutate on unspecified columns (e.g. matching regex)?
我有一个嵌套的 data.frame - df_nested
,其中一列包含 df
:
df <- tibble(ID_Value = 1:8,
xyz001 = c("text4", NA, NA, NA, NA, NA, NA, "text2"),
xyz002 = c(NA, NA, NA, "text3", "text1", NA, NA, NA),
xyz003 = c(NA, "text1", NA, NA, "text2", NA, "text2", NA))
我想找到一种方法,如何根据这些要求改变这个 df:
mutate(across(matches("\d")
- 有 4 个案例 - 4 个优先级。文本 4 <- 文本 3 <- 文本 2 <- 文本 1:
我需要查找并保留仅包含最高级别文本的列值。例如如果列包含 text4,我想删除 text3、text2、text1 并将它们替换为 NA。
如果它包含多个最高阶文本,我们应该保留所有这些值(例如列 xyz003)。
- 如何在不指定列名的情况下应用这些条件,因为列名中可以有任何数字。
- 如果列包含所有 NA,则什么也不做。
我的尝试:
df_nested <- df_nested %>%
mutate(df = map(data, ~.x %>%
mutate(across(matches("\dd"), function (x) {
conditions (ifelse, case_when or other)
...}
此外,我们应该更好地使用 across()
,还是 vars()
仍然是一个很好的方法?
提前谢谢你。
预期输出
df <- tibble(ID_Value = 1:8,
xyz001 = c("text4", NA, NA, NA, NA, NA, NA, NA),
xyz002 = c(NA, NA, NA, "text3", NA, NA, NA, NA),
xyz003 = c(NA, NA, NA, NA, "text2", NA, "text2", NA))
您可以将 rowwise
与 c_across
一起使用:
library(dplyr)
library(tidyr)
df %>%
rowwise() %>%
mutate(col = suppressWarnings(max(c_across(matches('\d+')), na.rm = TRUE)))
# ID_Value xyz001 xyz002 xyz003 col
# <int> <chr> <chr> <chr> <chr>
#1 1 tier4 NA NA tier4
#2 2 NA NA tier1 tier1
#3 3 NA NA NA NA
#4 4 NA tier3 NA tier3
#5 5 NA tier1 tier2 tier2
#6 6 NA NA NA NA
#7 7 NA NA tier2 tier2
#8 8 tier2 NA NA tier2
对字符值取 max
没有意义(并产生警告)但在这里我们可以使用它直接获取输出。
为了仅保留每行中的最大值,我们可以重塑数据:
df %>%
pivot_longer(cols = -ID_Value) %>%
group_by(ID_Value) %>%
mutate(value = replace(value, -which.max(readr::parse_number(value)), NA)) %>%
pivot_wider()
# ID_Value xyz001 xyz002 xyz003
# <int> <chr> <chr> <chr>
#1 1 tier4 NA NA
#2 2 NA NA tier1
#3 3 NA NA NA
#4 4 NA tier3 NA
#5 5 NA NA tier2
#6 6 NA NA NA
#7 7 NA NA tier2
#8 8 tier2 NA NA
- 使用
factor
类型指定您想要的顺序。
- 对匹配项执行行或 column-wise 操作。
考虑这个功能
max_only <- function(x, lvls) {
fct <- droplevels(factor(x, lvls))
`[<-`(x, as.integer(fct) != length(levels(fct)), NA_character_)
}
然后您可以指定任何顺序
> max_only(c("apple", "banana", NA_character_), c("banana", "apple"))
[1] "apple" NA NA
> max_only(c("apple", "banana", NA_character_), c("apple", "banana"))
[1] NA "banana" NA
案例 1:column-wise 操作
df %>%
mutate(across(matches("\d"), max_only, c("tier1", "tier2", "tier3", "tier4")))
输出(这个看起来更像你预期的输出)
# A tibble: 8 x 4
ID_Value xyz001 xyz002 xyz003
<int> <chr> <chr> <chr>
1 1 tier4 NA NA
2 2 NA NA NA
3 3 NA NA NA
4 4 NA tier3 NA
5 5 NA NA tier2
6 6 NA NA NA
7 7 NA NA tier2
8 8 NA NA NA
案例 2:row-wise 操作
df %>%
mutate(as.data.frame(t(apply(
across(matches("\d")), 1L,
max_only, c("tier1", "tier2", "tier3", "tier4")
))))
输出
# A tibble: 8 x 4
ID_Value xyz001 xyz002 xyz003
<int> <chr> <chr> <chr>
1 1 tier4 NA NA
2 2 NA NA tier1
3 3 NA NA NA
4 4 NA tier3 NA
5 5 NA NA tier2
6 6 NA NA NA
7 7 NA NA tier2
8 8 tier2 NA NA
解释
[<-
几乎等同于 x[...] <- y; x
。如果 ...
是逻辑向量(即 TRUE/FALSE),则 x
中索引为 TRUE 的值将被 y
替换。例如,
> x <- c("a", "b" ,"c")
> `[<-`(x, c(FALSE, TRUE, TRUE), NA_character_)
[1] "a" NA NA
> x[c(FALSE, TRUE, TRUE)] <- NA_character_; x
[1] "a" NA NA
NA_character_
是字符类型的NA值。
as.integer(fct) != length(levels(fct))
returns 与 fct
长度相同的逻辑向量。 TRUE 索引 fct
的值不是最高级别的位置,FALSE 索引相反,NA 索引 NAs。例如,假设 fct
看起来像这样
> x <- c("apple", "banana", NA)
> fct <- droplevels(factor(x, c("apple", "banana", "pear")))
> fct
[1] apple banana <NA>
Levels: apple banana
那么,你可以看到
> as.integer(fct) != length(levels(fct))
[1] TRUE FALSE NA
总而言之,就是把NA_character_
赋值给不等于最高层的值,但保持NA不变。
[<-(x, as.integer(fct) != length(levels(fct)), NA_character_)
我有一个嵌套的 data.frame - df_nested
,其中一列包含 df
:
df <- tibble(ID_Value = 1:8,
xyz001 = c("text4", NA, NA, NA, NA, NA, NA, "text2"),
xyz002 = c(NA, NA, NA, "text3", "text1", NA, NA, NA),
xyz003 = c(NA, "text1", NA, NA, "text2", NA, "text2", NA))
我想找到一种方法,如何根据这些要求改变这个 df:
mutate(across(matches("\d")
- 有 4 个案例 - 4 个优先级。文本 4 <- 文本 3 <- 文本 2 <- 文本 1: 我需要查找并保留仅包含最高级别文本的列值。例如如果列包含 text4,我想删除 text3、text2、text1 并将它们替换为 NA。 如果它包含多个最高阶文本,我们应该保留所有这些值(例如列 xyz003)。
- 如何在不指定列名的情况下应用这些条件,因为列名中可以有任何数字。
- 如果列包含所有 NA,则什么也不做。
我的尝试:
df_nested <- df_nested %>%
mutate(df = map(data, ~.x %>%
mutate(across(matches("\dd"), function (x) {
conditions (ifelse, case_when or other)
...}
此外,我们应该更好地使用 across()
,还是 vars()
仍然是一个很好的方法?
提前谢谢你。
预期输出
df <- tibble(ID_Value = 1:8,
xyz001 = c("text4", NA, NA, NA, NA, NA, NA, NA),
xyz002 = c(NA, NA, NA, "text3", NA, NA, NA, NA),
xyz003 = c(NA, NA, NA, NA, "text2", NA, "text2", NA))
您可以将 rowwise
与 c_across
一起使用:
library(dplyr)
library(tidyr)
df %>%
rowwise() %>%
mutate(col = suppressWarnings(max(c_across(matches('\d+')), na.rm = TRUE)))
# ID_Value xyz001 xyz002 xyz003 col
# <int> <chr> <chr> <chr> <chr>
#1 1 tier4 NA NA tier4
#2 2 NA NA tier1 tier1
#3 3 NA NA NA NA
#4 4 NA tier3 NA tier3
#5 5 NA tier1 tier2 tier2
#6 6 NA NA NA NA
#7 7 NA NA tier2 tier2
#8 8 tier2 NA NA tier2
对字符值取 max
没有意义(并产生警告)但在这里我们可以使用它直接获取输出。
为了仅保留每行中的最大值,我们可以重塑数据:
df %>%
pivot_longer(cols = -ID_Value) %>%
group_by(ID_Value) %>%
mutate(value = replace(value, -which.max(readr::parse_number(value)), NA)) %>%
pivot_wider()
# ID_Value xyz001 xyz002 xyz003
# <int> <chr> <chr> <chr>
#1 1 tier4 NA NA
#2 2 NA NA tier1
#3 3 NA NA NA
#4 4 NA tier3 NA
#5 5 NA NA tier2
#6 6 NA NA NA
#7 7 NA NA tier2
#8 8 tier2 NA NA
- 使用
factor
类型指定您想要的顺序。 - 对匹配项执行行或 column-wise 操作。
考虑这个功能
max_only <- function(x, lvls) {
fct <- droplevels(factor(x, lvls))
`[<-`(x, as.integer(fct) != length(levels(fct)), NA_character_)
}
然后您可以指定任何顺序
> max_only(c("apple", "banana", NA_character_), c("banana", "apple"))
[1] "apple" NA NA
> max_only(c("apple", "banana", NA_character_), c("apple", "banana"))
[1] NA "banana" NA
案例 1:column-wise 操作
df %>%
mutate(across(matches("\d"), max_only, c("tier1", "tier2", "tier3", "tier4")))
输出(这个看起来更像你预期的输出)
# A tibble: 8 x 4
ID_Value xyz001 xyz002 xyz003
<int> <chr> <chr> <chr>
1 1 tier4 NA NA
2 2 NA NA NA
3 3 NA NA NA
4 4 NA tier3 NA
5 5 NA NA tier2
6 6 NA NA NA
7 7 NA NA tier2
8 8 NA NA NA
案例 2:row-wise 操作
df %>%
mutate(as.data.frame(t(apply(
across(matches("\d")), 1L,
max_only, c("tier1", "tier2", "tier3", "tier4")
))))
输出
# A tibble: 8 x 4
ID_Value xyz001 xyz002 xyz003
<int> <chr> <chr> <chr>
1 1 tier4 NA NA
2 2 NA NA tier1
3 3 NA NA NA
4 4 NA tier3 NA
5 5 NA NA tier2
6 6 NA NA NA
7 7 NA NA tier2
8 8 tier2 NA NA
解释
[<-
几乎等同于x[...] <- y; x
。如果...
是逻辑向量(即 TRUE/FALSE),则x
中索引为 TRUE 的值将被y
替换。例如,> x <- c("a", "b" ,"c") > `[<-`(x, c(FALSE, TRUE, TRUE), NA_character_) [1] "a" NA NA > x[c(FALSE, TRUE, TRUE)] <- NA_character_; x [1] "a" NA NA
NA_character_
是字符类型的NA值。as.integer(fct) != length(levels(fct))
returns 与fct
长度相同的逻辑向量。 TRUE 索引fct
的值不是最高级别的位置,FALSE 索引相反,NA 索引 NAs。例如,假设fct
看起来像这样> x <- c("apple", "banana", NA) > fct <- droplevels(factor(x, c("apple", "banana", "pear"))) > fct [1] apple banana <NA> Levels: apple banana
那么,你可以看到
> as.integer(fct) != length(levels(fct)) [1] TRUE FALSE NA
总而言之,就是把
NA_character_
赋值给不等于最高层的值,但保持NA不变。[<-(x, as.integer(fct) != length(levels(fct)), NA_character_)