sort() sortuje wartości wektora
Dla zwykłego wektora sort() robi dokładnie to, czego się spodziewasz: zwraca wartości w kolejności rosnącej (oryginał pozostaje nietknięty):
Dwie opcje warte poznania. decreasing = TRUE odwraca kierunek. A sort() domyślnie po cichu usuwa wartości NA: sort(c(3, NA, 1)) zwraca samo 1 3. Jeśli brakujące wartości muszą przetrwać sortowanie, powiedz, gdzie mają trafić: sort(x, na.last = TRUE) je zachowuje i umieszcza za posortowanymi wartościami.
Na razie wszystko oczywiste. Ciekawe jest to, dlaczego sort() to złe narzędzie dla ramek danych.
order() zwraca pozycje: zmiana sposobu myślenia
sort() odpowiada na pytanie „jakie są wartości, po kolei?”. order() odpowiada na inne pytanie: „które pozycje muszę odwiedzić, żeby zobaczyć wartości po kolei?”
Czytaj order(times) jak instrukcję: najmniejsza wartość jest na pozycji 2, następna na pozycji 4, potem 1, potem 3. Indeksowanie według tej instrukcji, times[order(times)], odtwarza dokładnie sort(times).
Dlaczego to ważne? Bo wiersz ramki danych to drużyna wartości, które muszą przesuwać się razem. Posortowanie samej kolumny time przez sort() przestawiłoby jedną kolumnę, a jej towarzyszy zostawiło w miejscu, więc każdy wiersz by się pomieszał. order() daje zamiast tego pozycje wierszy, a wstawienie pozycji w miejsce wierszy w nawiasach przesuwa całe wiersze:
9.8 Bena trafia na miejsce razem z jego imieniem. To cała sztuczka i to ten idiom sortowania ramek danych w bazowym R: df[order(df$column), ]. (Wypisane etykiety wierszy, 2 4 1 3, to oryginalne numery wierszy, które wędrują razem z nimi; to nieszkodliwe).
Sortowanie malejące i po wielu kolumnach
Przy sortowaniu malejącym liczb zaneguj kolumnę: rosnące sortowanie -time to malejące sortowanie time. A order() przyjmuje wiele kolumn: najpierw liczą się wcześniejsze, a późniejsze rozstrzygają remisy:
Drugie wywołanie sortuje działy alfabetycznie, a w obrębie każdego działu pensje od najwyższej do najniższej. Sztuczka z negacją działa tylko na liczbach: -df$name na kolumnie tekstowej to błąd. Do malejącego sortowania tekstu użyj zamiast tego order(df$name, decreasing = TRUE) (co odwraca naraz wszystkie klucze sortowania).
rank() to trzecie rodzeństwo
Przy okazji: rank(x) odpowiada na jeszcze inne pytanie, „które miejsce zajmuje każda wartość?”, i niczego nie przesuwa:
sort() daje uporządkowane wartości, order() daje pozycje do odwiedzenia, a rank() daje miejsce każdej wartości bez jej przesuwania. Ludzie ciągle mylą order() i rank(); zauważ, że są one wzajemnie odwrotnymi permutacjami, i sięgaj po rank() tylko wtedy, gdy dosłownie chcesz miejsc w rankingu (tabele wyników, percentyle).
Sposób z dplyr: arrange()
arrange() z dplyr opakowuje cały taniec z order() w jeden czasownik: kolumny są kluczami sortowania, a desc() odwraca kierunek jednego z nich (kod statyczny; piaskownica uruchamia tylko bazowy R):
library(dplyr)
runners |> arrange(time)
staff |> arrange(dept, desc(salary))
Bez nawiasów kwadratowych, bez $, bez sztuczki z negacją: desc() działa też na kolumnach tekstowych. Jedna różnica w zachowaniu warta poznania: arrange() umieszcza wartości NA na końcu niezależnie od kierunku, a bazowe order() też domyślnie ma na.last = TRUE. Zajrzyj do wprowadzenia do dplyr, żeby zobaczyć, jak arrange() łączy się z filter() i resztą.
Sortowanie tekstu ma ostre krawędzie
Dwa zastrzeżenia przy sortowaniu tekstu. Po pierwsze, cyfry zapisane jako tekst sortują się jak tekst, bo porównanie napisów idzie znak po znaku:
"10" trafia przed "2", bo porównanie kończy się na pierwszym znaku: "1" < "2". Jeśli kolumna liczb sortuje się dziwnie, prawie na pewno jest zapisana jako tekst. Najpierw przekształć ją przez as.numeric() (to częsty skutek chaotycznego importu CSV).
Po drugie, kolejność tekstu zależy od ustawień regionalnych (locale) systemu: akcenty, wielkość liter i alfabety inne niż łaciński mogą sortować się inaczej na twoim laptopie niż na serwerze z locale C. Posortowany wynik, który musi być identyczny na różnych komputerach, powinien albo jawnie ustawiać locale, albo sortować według znormalizowanego klucza.
Najważniejsze informacje
sort(x)porządkuje wartości wektora;decreasing = TRUEodwraca kierunek; wartościNAsą usuwane, chyba że użyjeszna.last = TRUE.order(x)zwraca pozycje, adf[order(df$x), ]to ten bazowy idiom sortowania ramek danych.- Wiele kolumn:
order(df$a, -df$b); negacja do sortowania malejącego działa tylko na liczbach. rank()podaje miejsca bez przestawiania i nie zastępujeorder().arrange(dept, desc(salary))z dplyr to to samo sortowanie z mniejszą ilością interpunkcji.- W tekście
"10"trafia przed"2": sprawdzaj typy kolumn.
Dalej: zwijanie wierszy w podsumowania grup przez table(), tapply(), aggregate() i group_by() z dplyr.
Najczęściej zadawane pytania
Jak posortować ramkę danych według kolumny w R?
Użyj order() w nawiasach wierszy: df[order(df$price), ]. order() zwraca pozycje wierszy w posortowanej kolejności, a indeksowanie nimi przestawia całą ramkę danych. sort() tutaj nie zadziała: sortuje wartości jednego wektora, gubiąc ich powiązanie z pozostałymi kolumnami.
Czym różni się sort() od order() w R?
sort(x) zwraca wartości x ułożone po kolei. order(x) zwraca pozycje (indeksy), które ułożyłyby x po kolei, a właśnie tego potrzebujesz, żeby posortować całą ramkę danych według jednej z jej kolumn: df[order(df$x), ].
Jak sortować malejąco w R?
Dla wektora: sort(x, decreasing = TRUE). Dla ramki danych: df[order(-df$x), ] (zaneguj kolumnę liczbową) albo df[order(df$x, decreasing = TRUE), ], co działa też dla kolumn tekstowych, gdzie negacja nie działa. W dplyr: arrange(df, desc(x)).
Jak sortować po kilku kolumnach w R?
Przekaż je wszystkie do order(): df[order(df$dept, -df$salary), ] sortuje według działu, a potem malejąco według pensji w każdym dziale. Wcześniejsze argumenty to klucze główne; późniejsze rozstrzygają remisy. W dplyr: arrange(df, dept, desc(salary)).