У меня есть набор данных с 5 числовыми столбцами, например {A,B,C,D,E}, в котором значение любого столбца может варьироваться от 1 до 100, т. е.
1 <= все значения в A/B/C/D/E <= 100
и наш набор данных выглядит так:
A B C D E
1 5 7 19 2
90 12 8 45 30
30 10 20 50 40 #need this row
33 11 22 55 44
50 40 10 20 30 #and this row
40 40 10 20 30 #not this one
и я хочу отфильтровать только те строки, которые содержат каждое из следующих 5 значений, например: {10,20,30,40,50}. порядок не имеет значения, но 5 столбцов должны содержать все 5 значений.
Таким образом, вывод должен быть таким:
A B C D E
30 10 20 50 40
50 40 10 20 30
Я пытался использовать много ifelse
для фильтрации всех условий из 5 столбцов, но дело в том, что мне нужно применить эту концепцию к более сложной проблеме, где они могут не быть определенными нет. столбцов или даже определенный набор данных «поиск». поэтому любое решение с использованием dplyr, data.table, tidyverse высоко ценится, но любое другое творческое решение, которое кто-либо может придумать, пожалуйста, поделитесь.
Может быть, что-то вроде этого?
library(dplyr)
dat %>%
rowwise() %>%
filter(paste(sort(c(A, B, C, D, E)), collapse = ".") == "10.20.30.40.50") %>%
ungroup()
# A tibble: 2 x 5
A B C D E
<int> <int> <int> <int> <int>
1 30 10 20 50 40
2 50 40 10 20 30
Данные:
dat <- read.table(text = "A B C D E
1 5 7 19 2
90 12 8 45 30
30 10 20 50 40
33 11 22 55 44
50 40 10 20 30
40 40 10 20 30", header = TRUE)
Примечание. Я не уверен, что это будет хорошим подходом для расширения вашего «более сложная проблема, когда они могут не быть определенным количеством столбцов или даже определенным набором данных для поиска», так как это несколько расплывчато определено. Если у вас есть более сложная проблема, я настоятельно рекомендую вам сформулировать свой вопрос так, чтобы отразить ее.
Вот метод преобразования в длинный формат, фильтрация и изменение формы обратно в широкий:
my_vals = c(10, 20, 30, 40, 50)
library(tidyr)
library(dplyr)
df %>% mutate(id = row_number()) %>%
gather("col", "val", -id) %>%
group_by(id) %>%
filter(all(my_vals %in% val)) %>%
spread(col, val)
# A tibble: 2 x 6
# Groups: id [2]
id A B C D E
<int> <int> <int> <int> <int> <int>
1 3 30 10 20 50 40
2 5 50 40 10 20 30
(Конечно, вы можете убрать столбец id
, если он вам не нужен.)
tb <- data.frame(A = c(1, 90, 30 ,33,50,40),
B = c(5,12,10,11,40,40),
C = c(7,8,20,22,10,10),
D = c(19,45,50,55,20,20),
E = c(2,30,40,44,30,30))
cols <- paste0(c(10,20,30,40,50), collapse = "_")
index <- apply(tb, 1, function(x) paste0(sort(x), collapse = "_") == cols)
tb[index,]
Вот решение data.table:
library(data.table)
dt <- setDT(read.table(text = "A B C D E
1 5 7 19 2
90 12 8 45 30
30 10 20 50 40
33 11 22 55 44
50 40 10 20 30
40 40 10 20 30", header = TRUE))
dt = dt[, .SD[all(seq(10, 50, 10) %in% .SD)], by = 1:nrow(dt)]
Используя apply
, sum
и %in%
из базы R
my_vals = c(10, 20, 30, 40, 50)
df[apply(df, 1, function(row) all(my_vals %in% row)), ]
A B C D E
3 30 10 20 50 40
5 50 40 10 20 30
Это может быть расширено на любое количество столбцов, и все, что вам нужно сделать, это обновить my_vals
.
Основываясь на комментарии OP относительно ситуации, когда нужно выбрать правильные строки, когда my_vals
может иметь повторяющиеся элементы, приведенный выше код можно изменить примерно так:
my_vals = sort(c(10, 20, 30, 40, 40))
df[apply(df, 1, function(row) all(my_vals == sort(row))), ]
A B C D E
6 40 40 10 20 30
к вашему сведению, когда my_vals = c(10, 20, 30, 40, 40)
, это решение тоже подхватит c(10, 20, 30, 40, 50)
Ах, да, потому что это не было перечисленным вариантом использования. Итак, в таком случае вам не нужна строка с 50? Потому что, учитывая ваше условие в вопросе, если в строке есть 10,20,30,40,50, в ней все еще есть все элементы, указанные в my_vals = c(10, 20, 30, 40, 40)
, плюс дополнительные 50.
ты прав. с my_vals = c(10, 20, 30, 40, 40) он также выберет 5-ю строку в моем наборе данных. и я не хочу этого, так как важно, чтобы выбирались только строки, которые также имеют столько же экземпляров всех значений в my_vals. Я имею в виду, что если my_vals = c(10, 20, 30, 40, 40), то будет выбрана только строка с двумя 40, одной 30, одной 20 и одной 10... надеюсь, это достаточно ясно.
Я обновил ответ, чтобы учесть ваше новое условие
@nsinghs Спасибо за редактирование. Это работает лучше для меня сейчас.
Вот еще вариант без сортировки каждой строки.
Идея состоит в том, чтобы соединить каждый столбец набора данных со значениями поиска, столбец за столбцом. Например, для столбца A используйте все 5 значений для фильтрации исходного набора данных.
Затем для столбца B выполните соединение, используя то, что не использовалось в столбце A для каждого подмножества набора данных из предыдущего шага.
Затем для столбца C выполните соединение, используя то, что не использовалось в столбцах A и B для каждого подмножества набора данных из предыдущего шага.
Затем для столбца D выполните соединение, используя то, что не использовалось в столбцах A, B и C для каждого подмножества набора данных из предыдущего шага.
Так далее и тому подобное.
Вот реализация вышеупомянутой идеи в data.table
:
v <- c(10, 20, 30, 40, 40)
nm <- names(dat)
dat <- dat[.(A=unique(v)), on=.(A), nomatch=0L]
for (k in seq_along(nm)[-1L]) {
dat <- dat[, .SD[.(unique(v[-match(.BY, v)])),
on=eval(nm[k]),
nomatch=0L],
by=eval(nm[seq_len(k)[-k]])]
}
dat
вывод для v <- c(10, 20, 30, 40, 40)
:
A B C D E
1: 10 40 40 20 30
2: 40 40 10 20 30
3: 40 40 10 20 30
вывод для v <- c(10, 20, 30, 40, 50)
:
A B C D E
1: 30 10 20 50 40
2: 50 40 10 20 30
данные:
library(data.table)
dat <- fread("A B C D E
1 5 7 19 2
90 12 8 45 30
30 10 20 50 40
33 11 22 55 44
50 40 10 20 30
40 40 10 20 30
40 40 10 20 30
10 40 40 20 30") #2 dupe rows to demonstrate edge case
Спасибо! изменение строк в строки, а затем их сравнение, что на самом деле помогает мне больше.