У меня есть фрейм данных, в котором я хотел бы определить случаи (строки), в которых заданное условие выполняется как минимум определенное количество раз в наборе столбцов. В приведенном ниже игрушечном примере я хотел бы определить случаи, когда «А» — это выбор для двух из трех столбцов (от «Выбор_1» до «Выбор_3»). Мне все равно, в каких двух из трех столбцов находится «А». В моем примере будут идентифицированы ID = 1 и ID = 4.
Это должно работать с любым желаемым количеством «A» в любом количестве столбцов (например, если бы я хотел определить случаи, когда «A» является выбором в трех из четырех столбцов «Выбор», будет идентифицирован только ID = 1).
ID <- 1:4
Choice_1 <- c("A", "B", "C", "D")
Choice_2 <- c("A", "D", "C", "A")
Choice_3 <- c("A", "C", "A", "A")
Choice_4 <- c("B", "B", "A", "B")
df <- data.frame(ID, Choice_1, Choice_2, Choice_3, Choice_4)
> df
ID Choice_1 Choice_2 Choice_3 Choice_4
1 A A A B
2 B D C B
3 C C A A
4 D A A B
Одним из окольных способов сделать это было бы преобразовать «A» в 1, а все остальное в 0, просуммировать интересующие меня столбцы «Выбор» и проверить, что сумма равна или превышает мой порог, но я чувствую, что должен быть лучшим способом.
Как я себе это представляю, это будет какая-то форма оператора if_else, включенного в мутацию, чтобы строки, соответствующие условию, были идентифицированы с 1, а те, которые не соответствуют 0:
df %>% mutate(cond_matched = if_else( two of (Choice_1, Choice_2, Choice_3) == "A", 1, 0))
ID Choice_1 Choice_2 Choice_3 Choice_4 cond_matched
1 A A A B 1
2 B D C B 0
3 C C A A 0
4 D A A B 1
Я надеюсь, что я только что искал с неправильными ключевыми словами. Спасибо за любую помощь!
@Jon Spring, потому что я тестирую только Choice_1 - Choice_3.
Базовый вариант R будет состоять в том, чтобы создать логическую матрицу из выбранных столбцов (df[2:4] == "A"
), получить построчную сумму ИСТИННЫХ элементов и проверить, больше ли она или равна 2, привести логический вектор к двоичному с помощью as.integer
или +
(хаки)
df$cond_matched <- +(rowSums(df[2:4] == "A") >= 2)
df$cond_matched
#[1] 1 0 0 1
Или с помощью tidyverse
(с аналогичной логикой из базового решения R, но не совсем с таким же синтаксисом)
library(tidyverse)
df %>%
mutate(cond_matched = select(., 2:4) %>%
map(~ .x == 'A') %>%
reduce(`+`) %>%
`>=`(2) %>%
as.integer)
# ID Choice_1 Choice_2 Choice_3 Choice_4 cond_matched
#1 1 A A A B 1
#2 2 B D C B 0
#3 3 C C A A 0
#4 4 D A A B 1
Спасибо @akrun! Base R кажется намного проще ... Мне нужно будет изучить, что карта и сокращение делают в решении tidyverse, мне никогда не приходилось использовать их в прошлом.
Одна из возможностей dplyr
и tidyr
может быть:
df %>%
gather(var, val, -c(ID, Choice_4)) %>%
group_by(ID) %>%
summarise(cond_matched = as.integer(sum(val == "A") >= 2)) %>%
ungroup() %>%
left_join(df, by = c("ID" = "ID"))
ID cond_matched Choice_1 Choice_2 Choice_3 Choice_4
<int> <int> <chr> <chr> <chr> <chr>
1 1 1 A A A B
2 2 0 B D C B
3 3 0 C C A A
4 4 1 D A A B
Или просто с dplyr
(используя в основном ту же логику, что и @akrun):
df %>%
mutate(cond_matched = as.integer(rowSums(.[-ncol(.)] == "A") >= 2))
Чтобы явно назвать столбцы:
df %>%
mutate(cond_matched = as.integer(rowSums(.[grepl("Choice_1|Choice_2|Choice_3", colnames(.))] == "A") >= 2))
Спасибо! Просто чтобы уточнить, я думаю, вы использовали -ncol(.), чтобы избавиться от Choice_4. Каким был бы синтаксис, если бы я хотел явно назвать Choice_1, Choice_2 и Choice_3? В моем реальном фрейме данных это будет три столбца из 150+, поэтому проще всего использовать имя столбца.
Почему ID 3 получает 0?