Filtrowanie wierszy: maska logiczna
Żeby przefiltrować ramkę danych w R, wstaw warunek na pozycję wierszy w nawiasach: df[condition, ]. Warunek daje wektor TRUE/FALSE, po jednej wartości na wiersz, a R zostawia wiersze z TRUE:
Czytaj scores[scores$score > 80, ] od środka: scores$score > 80 daje TRUE FALSE TRUE TRUE FALSE, a nawiasy zostawiają wiersze 1, 3 i 4. Końcowy przecinek nie jest opcjonalny. Nawiasy na ramce danych przyjmują [rows, columns]; puste miejsce na kolumny oznacza "wszystkie kolumny". Zapomnij o przecinku, a zamiast wierszy zaczniesz indeksować kolumny. To jeden z najczęstszych błędów początkujących w R.
Zwróć uwagę, że wypisane numery wierszy to 1 3 4, a nie 1 2 3: filtrowanie zachowuje oryginalne etykiety wierszy. Nieszkodliwe, ale zaskakuje.
Wiele warunków: & i | (nie &&)
Łącz warunki przez & (AND) i | (OR). Każdy warunek dostaje własne, pełne porównanie:
Pułapka: R ma też && i ||, a tutaj nie są one wymienne z & i |. Formy podwójne działają na pojedynczych wartościach (istnieją dla warunków if); gdy dostaną całe kolumny, współczesny R (4.3+) zatrzymuje się z błędem w rodzaju 'length = 5' in coercion to 'logical(1)', a starszy R po cichu porównywał tylko pierwszy wiersz, co jest chyba jeszcze gorsze. W nawiasach zawsze używaj pojedynczych & i |. Pełne omówienie tej różnicy znajdziesz w artykule o operatorach.
%in%: dopasowanie do zbioru wartości
Gdy kolumna ma pasować do dowolnej z kilku wartości, nie łącz == przez |, tylko użyj %in%:
x %in% set zwraca TRUE wszędzie tam, gdzie x jest jedną z wartości w set. Czyta się to lepiej niż status == "pending" | status == "shipped", działa dla dowolnej liczby wartości i łatwo to zanegować: !(orders$status %in% c("refunded")).
Wybieranie kolumn
Miejsce na kolumny w nawiasach przyjmuje nazwy lub pozycje:
Dwie uwagi. Wybór po nazwie przetrwa zmianę kolejności kolumn; wybór po pozycji (scores[, c(1, 3)]) psuje się w dniu, w którym ktoś wstawi nową kolumnę. A prośba o jedną kolumnę zwraca zwykły wektor, a nie ramkę danych, dlatego drugie print() pokazuje 91 88 bez otaczającej tabeli. Gdy chcesz zachować kształt ramki danych, dodaj drop = FALSE: scores[, "score", drop = FALSE].
subset(): przyjazna jednolinijkowa funkcja bazowego R
Bazowy R ma funkcję opakowującą, która obsługuje wiersze i kolumny jednym czytelnym wywołaniem, bez $ i bez pilnowania przecinków:
Wewnątrz subset() piszesz gołe nazwy kolumn (score, nie scores$score), a funkcja obliczy je na ramce danych. To tak zwana niestandardowa ewaluacja (non-standard evaluation) i ma udokumentowane zastrzeżenie: rozwiązuje nazwy w czasie wykonania w sposób, który źle działa wewnątrz własnych funkcji (zmienna score w twojej funkcji może zostać przesłonięta przez kolumnę score). Zasada wprost z ?subset: świetne w konsoli, unikaj w programowaniu i tam używaj indeksowania nawiasami.
Pułapka z NA
Porównanie z NA daje NA, a nie FALSE, a filtrowanie nawiasami zamienia wiersze z NA w masce na wiersze pełne NA:
Pierwszy wynik zawiera śmieciowy wiersz z NA, bo warunek dla wiersza 2 nie był ani TRUE, ani FALSE. Rozwiązaniem jest jawne wymaganie !is.na(). subset() i filter() z dplyr po cichu usuwają wiersze z warunkiem NA. To wygodne, ale warto wiedzieć, że tak się dzieje. Dlaczego NA tak się rozprzestrzenia, wyjaśnia artykuł o brakujących wartościach.
Sposób dplyr: filter() i select()
Pakiet dplyr dzieli zadanie na dwa czasowniki, filter() dla wierszy i select() dla kolumn, z gołymi nazwami kolumn i bez powtarzania df$ (statycznie, bo piaskownica uruchamia tylko bazowy R):
library(dplyr)
scores |> filter(score > 80)
scores |> filter(score > 80, team == "red") # comma = AND
scores |> filter(status %in% c("pending", "shipped"))
scores |> select(name, score)
scores |> filter(team == "red") |> select(name, score)
filter() automatycznie usuwa wiersze z warunkiem NA i zawsze zwraca ramkę danych (nigdy goły wektor), co usuwa obie pułapki bazowego R opisane wyżej. Ceną jest zależność od pakietu i te same zastrzeżenia dotyczące niestandardowej ewaluacji co przy subset(). Pełny przegląd czasowników znajdziesz we wprowadzeniu do dplyr.
Co warto zapamiętać
df[condition, ]to podstawowy idiom, a przecinek jest obowiązkowy.- Łącz warunki pojedynczymi
&i|;&&zgłasza błąd na wektorach. %in%wygrywa z łańcuchem==przy dopasowywaniu zbioru wartości.- Wybieraj kolumny po nazwie i pamiętaj, że pojedyncze kolumny spadają do wektorów, chyba że użyjesz
drop = FALSE. subset()to wygodna jednolinijkowa funkcja do konsoli; nawiasy to wersja do programowania.- Porównania z
NAtworzą w nawiasach wiersze widma; zabezpiecz się przez!is.na().
Dalej: dodawanie i przekształcanie kolumn, czyli df$new <- ..., ifelse() i mutate() z dplyr.
Najczęściej zadawane pytania
Jak przefiltrować wiersze ramki danych w R?
Wstaw warunek logiczny na pozycję wierszy w nawiasach: df[df$score > 80, ]. Warunek daje wektor TRUE/FALSE, a R zostawia wiersze z TRUE. Przecinek jest ważny: oddziela filtr wierszy od (pustego) filtra kolumn. subset(df, score > 80) robi to samo przy mniejszej ilości pisania.
Jak filtrować z wieloma warunkami w R?
Łącz warunki przez & (AND) i | (OR): df[df$score > 80 & df$team == "red", ]. Używaj pojedynczego &, nie &&: podwójna forma działa tylko na pojedynczych wartościach i we współczesnym R zgłasza błąd na wektorach.
Czym różni się subset() od filtrowania nawiasami w R?
Zostawiają te same wiersze, z dwiema różnicami: subset() po cichu usuwa wiersze, w których warunek daje NA (nawiasy zachowują je jako wiersze wypełnione NA), a subset() korzysta z niestandardowej ewaluacji: piszesz gołe nazwy kolumn, co jest wygodne w konsoli, ale zawodne wewnątrz własnych funkcji.
Jak przefiltrować wiersze, w których kolumna pasuje do listy wartości?
Użyj %in%: df[df$team %in% c("red", "blue"), ] zostawia wiersze, w których team to dowolna z wymienionych wartości. Zastępuje łańcuch porównań == połączonych przez | i w przeciwieństwie do == nigdy nie zwraca NA.