dplyr: добавление подходящих имен столбцов при фильтрации по области действия (filter_all, )

У меня очень широкий и длинный набор данных, из которого мне нужно выбрать строки, в которых любая из переменных соответствует определенным условиям. Пока фильтрация по области в dplyr вместе с any_vars очень близка к тому, что мне нужно. Проиллюстрировать:

x <- tibble(v1 = c(1, 1, 5, 3, 4), v2 = c(3, 1, 2, 1, 2))
filter_all(x, any_vars( . == min(.)))

производит

# A tibble: 3 x 2
     v1    v2
  <dbl> <dbl>
1     1   3
2     1   1
3     3   1

Я хочу добавить имя «переменной фильтрации» к результирующим строкам, как показано ниже:

     v1    v2   var
  <dbl> <dbl> <chr>
1     1   3    v1
2     1   1    v1
3     1   1    v2
4     3   1    v2

Какие-либо предложения? Я подозреваю, что одна из функций map в purrr может выполнять фильтрацию по одному, а затем объединять результаты.

Когда один соответствует нескольким переменным (спасибо @Moody_Mudskipper), я хотел бы показать строку несколько раз --- в этом случае как с v1, так и с v2.


person Dong    schedule 10.08.2017    source источник
comment
что должно произойти, если v1 и v2 оба являются min в этой строке?   -  person Moody_Mudskipper    schedule 10.08.2017
comment
@Moody_Mudskipper Спасибо. Я хотел бы показать строку дважды --- сначала с v1, затем с v2.   -  person Dong    schedule 10.08.2017
comment
см. мой отредактированный ответ   -  person Moody_Mudskipper    schedule 11.08.2017


Ответы (3)


Вот и все, это должно масштабироваться для широкого набора данных.

x <- tibble(v1 = c(1, 1, 5, 3, 4), v2 = c(3, 1, 2, 1, 2))

library(dplyr)
library(tidyr)
x %>%
  mutate_all(rank,ties.method ="min") %>%
  gather(var,val) %>%
  cbind(x,.) %>%
  filter(val ==1) %>%
  select(-val)

#   v1 v2 var
# 1  1  3  v1
# 2  1  1  v1
# 3  1  1  v2
# 4  3  1  v2

чтобы избежать создания большой временной таблицы:

gathered <- x %>%
  mutate_all(rank,ties.method ="min") %>%
  gather(var,val)
rows_to_keep <- which(gathered$val == 1)
cbind(x[(rows_to_keep-1) %% nrow(x) + 1,],gathered[rows_to_keep,])

Это уродливее, но я думаю, что это самое эффективное, что я мог придумать:

log_df       <- mutate_all(x,function(x){x==min(x)}) # identify rows that contain min (no time wasted sorting here)
filter1      <- rowSums(log_df)>0  # to get rid of uninteresting rows
x2           <- x[filter1,]
log_df2      <- log_df[filter1,]
gathered     <- gather(log_df2,var,val) # put in long format
rows_to_keep <- which(gathered$val)
cbind(x2[(rows_to_keep-1) %% nrow(x2) + 1,],gathered[rows_to_keep,]) %>% select(-val)
person Moody_Mudskipper    schedule 10.08.2017
comment
очень хорошо. Мне нужно будет проверить это. Одна проблема заключается в том, что у меня более 100 переменных, и решение нужно масштабировать. - person Dong; 11.08.2017
comment
Это работает, но может оказаться непрактичным для большого набора данных — промежуточная таблица в два раза шире и в сто раз длиннее исходной. - person Dong; 15.08.2017
comment
попробуйте с новыми версиями - person Moody_Mudskipper; 15.08.2017

Попробуйте этот код:

  x%>%filter_all(., any_vars( . == min(.)))%>%
 data.frame(.,var=apply(.,1,function(i) names(.)[i==sapply(x,min)]))

Если это поможет, сообщите нам об этом. Спасибо.

Этот код не сработает при одном условии: если несколько переменных в строке являются минимальными. например, в опубликованном примере, если есть строка, в которой есть обе единицы, тогда этот код завершится ошибкой. Спасибо

person Onyambu    schedule 10.08.2017
comment
Это неверно, хотя вывод такой же, вы берете минимум между v1 и v2, а не переменную фильтрации. - person Moody_Mudskipper; 10.08.2017
comment
Помните, что после фильтрации у вас есть таблица, созданная из минимумов: так что вопрос в том, откуда взялся этот минимум? Другого минимума кроме него нет. Попробуйте использовать другую таблицу и посмотрите. Или даже попробуйте использовать максимумы вместо минимумов и использовать which.max и посмотреть, не получите ли вы правильные результаты. логика позади этого в порядке. Вот почему он дает правильное решение. - person Onyambu; 10.08.2017
comment
взглянем. в функции filter_all вы четко указали, что переменные, которые будут использоваться в фильтре, находятся между v1 и v2. Простое значение не одного из них, а любого из двух. Это означает, что минимум должен исходить либо от v1, либо от v2. Итак, вопрос в том, какой из них дал вам минимум? - person Onyambu; 10.08.2017
comment
попробуйте сделать x$v1 <- x$v1 * 10, вы увидите, что у вас будет только v2 в столбце var. Затем посмотрите ответ, который я дал, столбец var остается прежним. - person Moody_Mudskipper; 10.08.2017
comment
Спасибо. Я понимаю что ты имеешь в виду. Спасибо - person Onyambu; 10.08.2017

Спасибо за идею создания новых столбцов, мое решение ниже сохраняет имена переменных перед фильтрацией. Дайте мне знать, если вы можете улучшить это:

x %>%  
  mutate_all(funs(qual = . == min(.))) %>%
  filter_at(vars(ends_with("_qual")), any_vars(. == TRUE)) %>%
  gather(var, qual, ends_with("_qual")) %>%
  filter(qual==TRUE) %>%
  select(-qual) %>%
  extract(var, "var")

промежуточная таблица после первого шага:

     v1    v2 v1_qual v2_qual
1     1     3    TRUE   FALSE
2     1     1    TRUE    TRUE
3     5     2   FALSE   FALSE
4     3     1   FALSE    TRUE
5     4     2   FALSE   FALSE
person Dong    schedule 15.08.2017