有条件地对 R 中的列进行分类的有效方法?
Efficient way to conditionally categorize across columns in R?
我正在尝试对数据框进行分类。类似于您需要的维恩图,但我想 'grepl' 以某种方式对列 headers 进行采样。意思是,只要一个模式被共享并且它满足数字阈值,它就可以被分类到所述类别中。
示例数据:
df <- data.frame(a_b_c = c(1,3,5,0,0), a_b=c(0,0,4,0,0), a_b_c_d=c(1,2,2,3,0),
b_d=c(0,0,3,2,3), a_c = c(1,5,1,0,0))
df
> a_b_c a_b a_b_c_d b_d a_c
> 1 0 1 0 1
> 3 0 2 0 5
> 5 4 2 3 1
> 0 0 3 2 0
> 0 0 0 3 0
期望输出
df_final <- data.frame(df, category = c("Other", "Shared c", "Shared all", "Shared d", "Appears once"))
df_final
> a_b_c a_b a_b_c_d b_d a_c category
> 1 0 1 0 1 Other
> 3 0 2 0 5 Shared c
> 5 4 2 3 1 Shared all
> 0 0 3 2 0 Shared d
> 0 0 0 3 0 Appears once
我认为它涉及一个带有 case_when()
and/or ifelse()
语句的整洁的 verse mutate 语句,但我无法正确理解逻辑。这是一个示例测试数据集,我的实际数据有 >20 列。所以这就是为什么我想用通配符对列 headers 进行分类。
接受任何和所有建议。
我建议 "pivot"、case_when
和 "join" 为您提供结果。
library(dplyr)
# library(tidyr) # pivot_longer
df <- data.frame(a_b_c = c(1,3,5,0,0), a_b=c(0,0,4,0,0), a_b_c_d=c(1,2,2,3,0),
b_d=c(0,0,3,2,3), a_c = c(1,5,1,0,0)) %>%
mutate(row = row_number())
df %>%
tidyr::pivot_longer(-row) %>%
group_by(row) %>%
summarize(
category = case_when(
all(value > 0) ~ "Shared all",
sum(value > 0) == 1L ~ "Appears once",
all(value == 0 | grepl("c", name)) ~ "Shared c",
all(value == 0 | grepl("d", name)) ~ "Shared d",
TRUE ~ "Other"
)
) %>%
left_join(df, ., by = "row")
# a_b_c a_b a_b_c_d b_d a_c row category
# 1 1 0 1 0 1 1 Shared c
# 2 3 0 2 0 5 2 Shared c
# 3 5 4 2 3 1 3 Shared all
# 4 0 0 3 2 0 4 Shared d
# 5 0 0 0 3 0 5 Appears Once
我必须添加一个 row
列,以确保类别将配对回原始行,否则行不是唯一标识的(除了完全唯一性,我不是指望)。我 pivot
ed 这样我们就不会依赖于特定的列名或恰好五列的存在(这同样适用于 3 和 300,给出或接受你的逻辑规则。最后,使用 !grepl(...) | value>0
是一个特定的反转,以确保所有 c
-包括(和 d
)名称的值都大于 0;它很容易扩展,但根据您的实际用例,您可能需要更强大的正则表达式(例如,单词边界)。
在base R
中,我们可以根据逻辑创建一个数值索引,然后将该索引用作位置索引来更改值
i1 <- apply(df > 0, 1, function(x) {
x1 <- x[x]
c1 <- all(grepl('c', names(x1)))
d1 <- all(grepl('d', names(x1)))
all(x) + 2 * c1 + 4 * d1 + 8 * (length(x1) == 1)})
df$category <- c('Shared all', 'Shared c', 'Shared d',
'Appears once', 'other')[as.integer(factor(i1))]
df$category
#[1] "Shared c" "Shared c" "Shared all" "Shared d" "Appears once"
我正在尝试对数据框进行分类。类似于您需要的维恩图,但我想 'grepl' 以某种方式对列 headers 进行采样。意思是,只要一个模式被共享并且它满足数字阈值,它就可以被分类到所述类别中。
示例数据:
df <- data.frame(a_b_c = c(1,3,5,0,0), a_b=c(0,0,4,0,0), a_b_c_d=c(1,2,2,3,0),
b_d=c(0,0,3,2,3), a_c = c(1,5,1,0,0))
df
> a_b_c a_b a_b_c_d b_d a_c
> 1 0 1 0 1
> 3 0 2 0 5
> 5 4 2 3 1
> 0 0 3 2 0
> 0 0 0 3 0
期望输出
df_final <- data.frame(df, category = c("Other", "Shared c", "Shared all", "Shared d", "Appears once"))
df_final
> a_b_c a_b a_b_c_d b_d a_c category
> 1 0 1 0 1 Other
> 3 0 2 0 5 Shared c
> 5 4 2 3 1 Shared all
> 0 0 3 2 0 Shared d
> 0 0 0 3 0 Appears once
我认为它涉及一个带有 case_when()
and/or ifelse()
语句的整洁的 verse mutate 语句,但我无法正确理解逻辑。这是一个示例测试数据集,我的实际数据有 >20 列。所以这就是为什么我想用通配符对列 headers 进行分类。
接受任何和所有建议。
我建议 "pivot"、case_when
和 "join" 为您提供结果。
library(dplyr)
# library(tidyr) # pivot_longer
df <- data.frame(a_b_c = c(1,3,5,0,0), a_b=c(0,0,4,0,0), a_b_c_d=c(1,2,2,3,0),
b_d=c(0,0,3,2,3), a_c = c(1,5,1,0,0)) %>%
mutate(row = row_number())
df %>%
tidyr::pivot_longer(-row) %>%
group_by(row) %>%
summarize(
category = case_when(
all(value > 0) ~ "Shared all",
sum(value > 0) == 1L ~ "Appears once",
all(value == 0 | grepl("c", name)) ~ "Shared c",
all(value == 0 | grepl("d", name)) ~ "Shared d",
TRUE ~ "Other"
)
) %>%
left_join(df, ., by = "row")
# a_b_c a_b a_b_c_d b_d a_c row category
# 1 1 0 1 0 1 1 Shared c
# 2 3 0 2 0 5 2 Shared c
# 3 5 4 2 3 1 3 Shared all
# 4 0 0 3 2 0 4 Shared d
# 5 0 0 0 3 0 5 Appears Once
我必须添加一个 row
列,以确保类别将配对回原始行,否则行不是唯一标识的(除了完全唯一性,我不是指望)。我 pivot
ed 这样我们就不会依赖于特定的列名或恰好五列的存在(这同样适用于 3 和 300,给出或接受你的逻辑规则。最后,使用 !grepl(...) | value>0
是一个特定的反转,以确保所有 c
-包括(和 d
)名称的值都大于 0;它很容易扩展,但根据您的实际用例,您可能需要更强大的正则表达式(例如,单词边界)。
在base R
中,我们可以根据逻辑创建一个数值索引,然后将该索引用作位置索引来更改值
i1 <- apply(df > 0, 1, function(x) {
x1 <- x[x]
c1 <- all(grepl('c', names(x1)))
d1 <- all(grepl('d', names(x1)))
all(x) + 2 * c1 + 4 * d1 + 8 * (length(x1) == 1)})
df$category <- c('Shared all', 'Shared c', 'Shared d',
'Appears once', 'other')[as.integer(factor(i1))]
df$category
#[1] "Shared c" "Shared c" "Shared all" "Shared d" "Appears once"