2016-03-10 14 views
5

To musi być duplikat, ale nie mogę go znaleźć. Więc idzie.Dopasuj/znajdź wiersze na podstawie wielu wymaganych wartości w jednym wierszu w R

Mam data.frame z dwiema kolumnami. Jedna zawiera grupę, a druga zawiera kryterium. Grupa może zawierać wiele różnych kryteriów, ale tylko jedną na wiersz. Chcę zidentyfikować grupy, które zawierają trzy określone kryteria (ale będą one wyświetlane w różnych wierszach.W moim przypadku chcę zidentyfikować wszystkie grupy, które zawierają kryteria "I", "E", "C." Grupy mogą zawierać dowolną liczbę i połączenie tych i kilku innych pism.

test <- data.frame(grp=c(1,1,2,2,2,3,3,3,4,4,4,4,4),val=c("C","I","E","I","C","E","I","A","C","I","E","E","A")) 

> test 
    grp val 
1 1 C 
2 1 I 
3 2 E 
4 2 I 
5 2 C 
6 3 E 
7 3 I 
8 3 A 
9 4 C 
10 4 I 
11 4 E 
12 4 E 
13 4 A 

W powyższym przykładzie, chcę zidentyfikować GRP 2 i 4, ponieważ każdy z nich zawiera litery E, i, i C.

Dzięki!

+0

Przypuszczam trzy najgłębsze unikatowe() wywołuje są całkiem zbędne i niepotrzebne. Mój aktualny zestaw danych składa się z kilku milionów wierszy i nie byłem pewien, czy byłby on szybszy. – Jordan

+0

Interesujący problem! Jest znany jako ** i/lub dylemat zapytania ** w kontekście bazy danych SQL, ponieważ ograniczenie ** i ** wymaga ograniczenia w więcej niż jednym wierszu (co nie jest mocą SQL). Chciałbym zobaczyć (lepsze) rozwiązanie R! –

+0

Ile unikalnych 'val' znajduje się w zestawie danych? W zależności od liczby informacje te mogą być podstawą niektórych rozwiązań. – JasonAizkalns

Odpowiedz

2

Oto rozwiązanie dplyr. %in% jest wektoryzowany, więc c("E", "I", "C") %in% val zwraca wektor logiczny o długości trzeciej. Dla grup docelowych przekazanie tego wektora do all() zwraca TRUE. To jest nasz filtr i uruchamiamy go w każdej grupie przy użyciu group_by().

library(dplyr) 
test %>% 
    group_by(grp) %>% 
    filter(all(c("E", "I", "C") %in% val)) 
# Source: local data frame [8 x 2] 
# Groups: grp [2] 
# 
#  grp val 
# (dbl) (fctr) 
# 1  2  E 
# 2  2  I 
# 3  2  C 
# 4  4  C 
# 5  4  I 
# 6  4  E 
# 7  4  E 
# 8  4  A 

Lub jeśli ten wynik byłby bardziej poręczne (dzięki @Frank)

test %>% 
    group_by(grp) %>% 
    summarise(matching = all(c("E", "I", "C") %in% val)) 
# Source: local data frame [4 x 2] 
# 
#  grp matching 
# (dbl) (lgl) 
# 1  1 FALSE 
# 2  2  TRUE 
# 3  3 FALSE 
# 4  4  TRUE 
+2

Oto podobna opcja w bazie: 'tapply (test $ val, test $ grp, funkcja (x) all (myvals% in% x))' gdzie 'myvals = c (" E "," I "," C ") '.Myślę, że "podsumowanie" lepiej pasuje do "filtru", jeśli OP chce tylko wiedzieć, które grupy spełniają kryterium. – Frank

+0

Nie wiedziałem o wszystkich(). Świetny dodatek od was obojga. – Jordan

+0

Dobra myśl. Nie byłem pewien, czy OP naprawdę chciał grupy lub wiersze. – effel

2
library(data.table) 

test <- data.frame(grp=c(1,1,2,2,2,3,3,3,4,4,4,4,4),val=c("C","I","E","I","C","E","I","A","C","I","E","E","A")) 

setDT(test)  # convert the data.frame into a data.table 
group.counts <- dcast(test, grp ~ val) # count number of same values per group and create one column per val with the count in the cell 
group.counts[I>0 & E>0 & C>0,]   # now filtering is easy 

Wyniki w:

grp A C E I 
1: 2 0 1 1 1 
2: 4 1 1 2 1 

Zamiast zwrotu numery grup tylko można także „join” wynikające numerów grupowych z oryginalnych danych, aby pokazać „raw” wierszy danych z każdej grupy, która odpowiada:

test[group.counts[I>0 & E>0 & C>0,], .SD, on="grp" ] 

to pokazuje :

grp val 
1: 2 E 
2: 2 I 
3: 2 C 
4: 4 C 
5: 4 I 
6: 4 E 
7: 4 E 
8: 4 A 

PS: Żeby zrozumieć łatwiejsze rozwiązanie: liczy się dla wszystkich grup to:

> group.counts 
    grp A C E I 
1: 1 0 1 0 1 
2: 2 0 1 1 1 
3: 3 1 0 1 1 
4: 4 1 1 2 1 
Powiązane problemy