R — удаление обеих строк в «парном» фрейме данных, если одно значение = x

#r #dplyr

#r #dplyr

Вопрос:

У меня есть набор данных, в котором каждые две строки являются частью набора выбора, который будет использоваться для дискретного анализа выбора в mlogit. Я хочу удалить все строки, связанные с набором выбора, на основе значений в столбце.

Например, с помощью этого простого набора данных, допустим, я хотел удалить все пары, в которых в одной из строк пары было желтое значение (пары, определенные здесь с помощью «Set»).

Я предполагаю, что должен быть простой способ сказать, соответствует ли это значение = «x» , и это значение этого другого столбца соответствует значению этой строки, удалите оба. Я полагаю, что это должно быть как-то связано с ifelse или case_when в dplyr, но это часть с соответствующим значением, в которой я не уверен. В Excel я бы использовал ссылку на ячейку для выполнения простого if_then, но не уверен, что лучший способ сделать это в R. Спасибо!

 data <-  data.frame(Set = sort(rep(1:20,2)), 
                    Choice = rep(c(T,F),20),
                    Color = sample(
                      rep(c("Red","Blue","Green","Yellow"),5)))
  

Ответ №1:

Вы можете выбрать те set , которые имеют все значения в Color столбце, которых нет 'Yellow' .

 library(dplyr)
data %>% group_by(Set) %>% filter(all(Color != 'Yellow'))
  

Или другой способ написать это было бы :

 data %>% group_by(Set) %>% filter(!any(Color == 'Yellow'))
  

Это также может быть записано в базе R

 subset(data, ave(Color != 'Yellow', Set, FUN = all))
  

и data.table :

 library(data.table)
setDT(df)[, .SD[all(Color != 'Yellow')], Set]
  

Комментарии:

1. Удивительное дополнение, если бы я хотел фильтровать по двум переменным, сработало бы это data %>% group_by(Set) %>% filter(!any(Color == 'Yellow'| Color == 'Blue"))

2. Да, это сработало бы. Также вы можете использовать %in% : data %>% group_by(Set) %>% filter(!any(Color %in% c('Yellow', 'Blue')))

3. Блестяще — даже лучше, на практике я буду отфильтровывать несколько значений, так что это будет еще более лаконично, спасибо

Ответ №2:

Не должно быть необходимости выполнять вычисления для каждой группы как таковой. Вы можете найти Set содержащие s "Yellow" , а затем сохранить только те, которых нет в этих Set s. Поскольку мы говорим о множествах, давайте воспользуемся некоторой теорией множеств:

 data[data$Set %in% setdiff(data$Set, data$Set[data$Color == "Yellow"]),]
  

Вы можете легко расширить это до многих цветов:

 data[data$Set %in% setdiff(data$Set, data$Set[data$Color %in% c("Yellow","Green")]),]
  

И почему вас это должно волновать? Потому что, если вы когда-либо работали с большими наборами данных со многими группами, выполнение всех вычислений за один проход намного быстрее, чем повторять все для каждой группы:

 ## one million groups!
data <- data[rep(1:20, each=1e5),]
data$Set <- rep(1:1e6, each=2)

library(dplyr)
system.time({ 
    data %>% group_by(Set) %>% filter(all(Color != 'Yellow'))
})
## i'm still waiting for this to finish 10 minutes later

system.time({
    data[data$Set %in% setdiff(data$Set, data$Set[data$Color == "Yellow"]),]
})
#   user  system elapsed 
#  0.301   0.000   0.302
  

Комментарии:

1. Спасибо за добавленное обоснование того, почему этот метод используется вместо метода dplyr. В случаях, когда данных меньше, а код предоставляется пользователям, которые в противном случае не знакомы с процессом (и не должны быть) — мне нравится dplyr из-за простоты чтения. Но на практике, когда используются большие наборы данных, ваш метод явно превосходит.

Ответ №3:

 df[!(df$Set %in% with(df, Set[which(Color == "Yellow")])),]