Выберите строки, только если они соответствуют определенной строке - PullRequest
2 голосов
/ 22 апреля 2019

У меня есть набор данных с 5 числовыми столбцами, скажем, {A, B, C, D, E}, в котором значение любого столбца может варьироваться от 1 до 100. т. Е.

1 <=все значения в A / B / C / D / E <= 100 </p>

и нашем наборе данных выглядят так:

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, высоко ценится, но любое другое творческое решение, которое может придумать кто-либо, пожалуйста, поделитесь.

Ответы [ 6 ]

2 голосов
/ 22 апреля 2019

Использование 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.

Редактировать

На основании комментария ОП относительно ситуации, в которой правильные строки выбираются, когда 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
2 голосов
/ 22 апреля 2019
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,]
1 голос
/ 22 апреля 2019

Может быть, что-то вроде этого?

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)

Примечание: я не уверен, будет ли это хорошим подходом для расширения вашего "более сложная проблема, когда они могут не быть определенным числом столбцов или даже определенным набором данных «lookup» , поскольку это несколько неопределенно определено.Если у вас есть более сложная проблема, я настоятельно рекомендую вам сформулировать свой вопрос, чтобы отразить ее.

0 голосов
/ 23 апреля 2019

Вот еще один вариант без сортировки каждой строки.

Идея состоит в том, чтобы объединить каждый столбец набора данных со значениями поиска, столбец за столбцом. Например, для столбца 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 
0 голосов
/ 22 апреля 2019

Вот решение для 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)]
0 голосов
/ 22 апреля 2019

Вот метод преобразования в длинный формат, фильтрации и преобразования обратно в широкий:

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, если он вам не нужен.)

...