Czy mutatu można użyć, gdy mutacja jest warunkowa (w zależności od wartości określonych wartości kolumn)?
Ten przykład pomaga pokazać, o co mi chodzi.
structure(list(a = c(1, 3, 4, 6, 3, 2, 5, 1), b = c(1, 3, 4,
2, 6, 7, 2, 6), c = c(6, 3, 6, 5, 3, 6, 5, 3), d = c(6, 2, 4,
5, 3, 7, 2, 6), e = c(1, 2, 4, 5, 6, 7, 6, 3), f = c(2, 3, 4,
2, 2, 7, 5, 2)), .Names = c("a", "b", "c", "d", "e", "f"), row.names = c(NA,
8L), class = "data.frame")
a b c d e f
1 1 1 6 6 1 2
2 3 3 3 2 2 3
3 4 4 6 4 4 4
4 6 2 5 5 5 2
5 3 6 3 3 6 2
6 2 7 6 7 7 7
7 5 2 5 2 6 5
8 1 6 3 6 3 2
Miałem nadzieję, że znajdę rozwiązanie mojego problemu za pomocą pakietu dplyr (i tak, wiem, że to nie kod, który powinien działać, ale wydaje mi się, że wyjaśnia cel) do utworzenia nowej kolumny g:
library(dplyr)
df <- mutate(df,
if (a == 2 | a == 5 | a == 7 | (a == 1 & b == 4)){g = 2},
if (a == 0 | a == 1 | a == 4 | a == 3 | c == 4) {g = 3})
Wynik szukanego kodu powinien mieć następujący wynik w tym konkretnym przykładzie:
a b c d e f g
1 1 1 6 6 1 2 3
2 3 3 3 2 2 3 3
3 4 4 6 4 4 4 3
4 6 2 5 5 5 2 NA
5 3 6 3 3 6 2 NA
6 2 7 6 7 7 7 2
7 5 2 5 2 6 5 2
8 1 6 3 6 3 2 3
Czy ktoś ma pomysł jak to zrobić w dplyr? Ta ramka danych to tylko przykład, ramki danych, z którymi mam do czynienia, są znacznie większe. Ze względu na jego szybkość próbowałem użyć dplyr, ale może są inne, lepsze sposoby rozwiązania tego problemu?
dplyr::case_when()
jest znacznie jaśniejsze niżifelse
,Odpowiedzi:
Posługiwać się
ifelse
Dodanej - if_else: Zauważ, że w dplyr 0.5 istnieje
if_else
funkcja zdefiniowana tak alternatywą byłoby wymienićifelse
zif_else
; należy jednak pamiętać, że ponieważif_else
jest bardziej rygorystyczne niżifelse
(obie nogi warunku muszą mieć ten sam typ), więcNA
w tym przypadku należałoby zastąpićNA_real_
.Dodano - case_when Od czasu wysłania tego pytania dplyr dodał,
case_when
więc inną alternatywą byłoby:Dodane - arithmetic / na_if Jeśli wartości są liczbowe, a warunki (z wyjątkiem domyślnej wartości NA na końcu) wzajemnie się wykluczają, tak jak ma to miejsce w pytaniu, możemy użyć wyrażenia arytmetycznego, w którym każdy termin jest pomnożony przez pożądany wynik, używając
na_if
na końcu, aby zamienić 0 na NA.źródło
NA
chcę, aby wiersze, które nie spełniają warunków, pozostały takie same?mutate(g = ifelse(condition1, 2, ifelse(condition2, 3, g))
Ponieważ pytasz o inne lepsze sposoby rozwiązania problemu, oto inny sposób
data.table
:Zauważ, że kolejność instrukcji warunkowych jest odwrócona, aby uzyskać
g
poprawne wyniki. Nie ma kopiig
, nawet podczas drugiego zadania - jest wymieniana na miejscu .W przypadku większych danych miałoby to lepszą wydajność niż użycie zagnieżdżonych
if-else
, ponieważ może oceniać przypadki „tak” i „nie” , a zagnieżdżanie może być trudniejsze do odczytania / utrzymania IMHO.Oto test porównawczy dla stosunkowo większych danych:
Nie jestem pewien, czy jest to alternatywa, o którą prosiłeś, ale mam nadzieję, że pomoże.
źródło
DT_fun
modyfikuje swoje dane wejściowe w miejscu, benchmark może nie być całkiem sprawiedliwy - oprócz tego, że nie otrzymuje tych samych danych wejściowych z drugiej iteracji do przodu (co może wpływać na czas, ponieważDT$g
jest już przydzielony?), Wynik również propaguje się z powrotem do,ans1
a zatem może ( jeśli uzna to za optymalizatora R jest konieczne? Nie wiesz na ten temat ...) uniknąć kolejną kopię, któraDPLYR_fun
iBASE_fun
trzeba dokonać?data.table
rozwiązanie jest świetne i używamdata.table
wszędzie tam, gdzie naprawdę potrzebuję szybkości do operacji na tabelach i nie chcę przechodzić aż do C ++. Wymaga to jednak dużej ostrożności podczas wprowadzania modyfikacji!dplyr ma teraz funkcję,
case_when
która oferuje wektoryzację if. Składnia jest trochę dziwna w porównaniu z tym,mosaic:::derivedFactor
że nie możesz uzyskać dostępu do zmiennych w standardowy sposób dplyr i musisz zadeklarować tryb NA, ale jest znacznie szybszy niżmosaic:::derivedFactor
.EDYCJA: Jeśli używasz
dplyr::case_when()
pakietu sprzed wersji 0.7.0, musisz poprzedzić nazwy zmiennych znakiem „.$
” (np. Wpisz.$a == 1
wewnątrzcase_when
).Benchmark : W przypadku testu porównawczego (ponowne wykorzystanie funkcji z postu Arun) i zmniejszenie wielkości próby:
To daje:
źródło
case_when
można też zapisać jako:df %>% mutate(g = with(., case_when(a %in% c(2,5,7) | (a==1 & b==4) ~ 2L, a %in% c(0,1,3,4) | c==4 ~ 3L, TRUE ~ NA_integer_)))
.$
już jej w nowej wersji dplyrderivedFactor
Funkcji zmosaic
pakietu wydaje się być tak zaprojektowane, aby sobie z tym poradzić. Korzystając z tego przykładu, wyglądałoby to tak:(Jeśli chcesz być wynik numeryczny zamiast czynnika, można owinąć
derivedFactor
was.numeric
zaproszeniu).derivedFactor
może być również użyty do dowolnej liczby warunków.źródło
.asFactor = F
opcji lub używając (podobnej)derivedVariable
funkcji w tym samym pakiecie.recode
dplyr 0.5 to zrobi. Jednak jeszcze tego nie zbadałem. Zobacz blog.rstudio.org/2016/06/27/dplyr-0-5-0case_when
jest teraz całkiem czystą implementacją przypadku w stylu SQL, gdy:Korzystanie z dplyr 0.7.4
Podręcznik: http://dplyr.tidyverse.org/reference/case_when.html
źródło