如何跨多个列匹配一列,以及 return 匹配新列中的 col_name
How to match a column across multiple columns and return matching col_name in a new column
假设我有一个数据集 df,其中我想在多个列 A to F
的值中匹配 col X
的值,并希望 return 匹配列名称(否则 NA ) 在新列中。
输入
df <- structure(list(A = c(4L, NA, NA, NA), B = c(NA, 5L, NA, NA),
C = c(NA, NA, NA, NA), D = c(NA, 4L, 6L, 7L), E = c(5L, NA,
NA, NA), F = c(NA, NA, NA, NA), X = 4:7), class = "data.frame", row.names = c(NA,
-4L))
> df
A B C D E F X
1 4 NA NA NA 5 NA 4
2 NA 5 NA 4 NA NA 5
3 NA NA NA 6 NA NA 6
4 NA NA NA 7 NA NA 7
我想要的输出
> df_out
A B C D E F X new
1 4 NA NA NA 5 NA 4 A
2 NA 5 NA 4 NA NA 5 B
3 NA NA NA 6 NA NA 6 D
4 NA NA NA 7 NA NA 7 D
我更喜欢 dplyr
/tidyverse
语法,我会将其集成到我现有的语法中。
一个选项可以是:
df %>%
rowwise() %>%
mutate(new = names(.)[which(c_across(-X) %in% X)])
A B C D E F X new
<int> <int> <lgl> <int> <int> <lgl> <int> <chr>
1 4 NA NA NA 5 NA 4 A
2 NA 5 NA 4 NA NA 5 B
3 NA NA NA 6 NA NA 6 D
4 NA NA NA 7 NA NA 7 D
上面的解决方案假设列名对应于which()
建立的位置。但是,如果不是这种情况(例如 c_across(-c(C, E, X)
),则结果将不正确。更复杂情况的解决方案可能是:
df %>%
mutate(new = Reduce(coalesce, across(-c(C, E, X), ~ ifelse(. == X, cur_column(), NA_character_))))
A B C D E F X new
1 4 NA NA NA 5 NA 4 A
2 NA 5 NA 4 NA NA 5 B
3 NA NA NA 6 NA NA 6 D
4 NA NA NA 7 NA NA 7 D
df %>%
pivot_longer(cols = -X) %>%
mutate(
match = if_else(X == value, name, NA_character_)
) %>%
pivot_wider() %>%
filter(!is.na(match))
将A:F
列与X
列进行比较,将NA
替换为FALSE
并使用max.col
获取[=17=的索引] 每行中的值,可用于获取列名。
library(dplyr)
df %>%
mutate(new = {
tmp <- select(., A:F) == X
names(.)[max.col(replace(tmp, is.na(tmp), FALSE))]
})
# A B C D E F X new
#1 4 NA NA NA 5 NA 4 A
#2 NA 5 NA 4 NA NA 5 B
#3 NA NA NA 6 NA NA 6 D
#4 NA NA NA 7 NA NA 7 D
在 base R 中,这可以写成:
tmp <- df[1:5] == df$X
df$new <- names(df)[max.col(replace(tmp, is.na(tmp), FALSE))]
此解决方案假设您在该行中至少有一个 X
的匹配项,如示例中所示。
假设我有一个数据集 df,其中我想在多个列 A to F
的值中匹配 col X
的值,并希望 return 匹配列名称(否则 NA ) 在新列中。
输入
df <- structure(list(A = c(4L, NA, NA, NA), B = c(NA, 5L, NA, NA),
C = c(NA, NA, NA, NA), D = c(NA, 4L, 6L, 7L), E = c(5L, NA,
NA, NA), F = c(NA, NA, NA, NA), X = 4:7), class = "data.frame", row.names = c(NA,
-4L))
> df
A B C D E F X
1 4 NA NA NA 5 NA 4
2 NA 5 NA 4 NA NA 5
3 NA NA NA 6 NA NA 6
4 NA NA NA 7 NA NA 7
我想要的输出
> df_out
A B C D E F X new
1 4 NA NA NA 5 NA 4 A
2 NA 5 NA 4 NA NA 5 B
3 NA NA NA 6 NA NA 6 D
4 NA NA NA 7 NA NA 7 D
我更喜欢 dplyr
/tidyverse
语法,我会将其集成到我现有的语法中。
一个选项可以是:
df %>%
rowwise() %>%
mutate(new = names(.)[which(c_across(-X) %in% X)])
A B C D E F X new
<int> <int> <lgl> <int> <int> <lgl> <int> <chr>
1 4 NA NA NA 5 NA 4 A
2 NA 5 NA 4 NA NA 5 B
3 NA NA NA 6 NA NA 6 D
4 NA NA NA 7 NA NA 7 D
上面的解决方案假设列名对应于which()
建立的位置。但是,如果不是这种情况(例如 c_across(-c(C, E, X)
),则结果将不正确。更复杂情况的解决方案可能是:
df %>%
mutate(new = Reduce(coalesce, across(-c(C, E, X), ~ ifelse(. == X, cur_column(), NA_character_))))
A B C D E F X new
1 4 NA NA NA 5 NA 4 A
2 NA 5 NA 4 NA NA 5 B
3 NA NA NA 6 NA NA 6 D
4 NA NA NA 7 NA NA 7 D
df %>%
pivot_longer(cols = -X) %>%
mutate(
match = if_else(X == value, name, NA_character_)
) %>%
pivot_wider() %>%
filter(!is.na(match))
将A:F
列与X
列进行比较,将NA
替换为FALSE
并使用max.col
获取[=17=的索引] 每行中的值,可用于获取列名。
library(dplyr)
df %>%
mutate(new = {
tmp <- select(., A:F) == X
names(.)[max.col(replace(tmp, is.na(tmp), FALSE))]
})
# A B C D E F X new
#1 4 NA NA NA 5 NA 4 A
#2 NA 5 NA 4 NA NA 5 B
#3 NA NA NA 6 NA NA 6 D
#4 NA NA NA 7 NA NA 7 D
在 base R 中,这可以写成:
tmp <- df[1:5] == df$X
df$new <- names(df)[max.col(replace(tmp, is.na(tmp), FALSE))]
此解决方案假设您在该行中至少有一个 X
的匹配项,如示例中所示。