sort() сортирует значения вектора
Для обычного вектора sort() делает ровно то, что вы ожидаете, — возвращает значения по возрастанию (оригинал не трогается):
Две опции, которые стоит знать. decreasing = TRUE переворачивает направление. И sort() по умолчанию молча отбрасывает значения NA — sort(c(3, NA, 1)) возвращает просто 1 3. Если пропущенные значения должны пережить сортировку, укажите, куда их деть: sort(x, na.last = TRUE) сохранит их, поместив после упорядоченных значений.
Пока всё очевидно. Интересное начинается с того, почему sort() — неправильный инструмент для датафреймов.
order() возвращает позиции — смена мышления
sort() отвечает на вопрос «какие значения, по порядку?». order() отвечает на другой вопрос: «какие позиции мне посетить, чтобы увидеть значения по порядку?»
Читайте order(times) как инструкцию: наименьшее значение находится на позиции 2, следующее — на позиции 4, затем 1, затем 3. Индексация по этой инструкции — times[order(times)] — в точности воспроизводит sort(times).
Почему это важно? Потому что строка датафрейма — это команда значений, которые должны двигаться вместе. Сортировка одного столбца time через sort() переупорядочила бы один столбец и бросила его товарищей — все строки перемешались бы. order() вместо этого даёт позиции строк, а помещение позиций в слот строк внутри скобок двигает целые строки:
Результат Бена 9.8 приезжает вместе с его именем. В этом весь приём, и это та самая идиома сортировки датафреймов в базовом R: df[order(df$column), ]. (Напечатанные метки строк — 2 4 1 3 — это исходные номера строк, увязавшиеся следом; безвредно.)
Сортировка по убыванию и по нескольким столбцам
Для убывающей числовой сортировки поменяйте знак столбца — упорядочивание -time по возрастанию это упорядочивание time по убыванию. И order() принимает несколько столбцов: более ранние идут первыми, более поздние разрешают совпадения:
Второй вызов сортирует отделы по алфавиту и внутри каждого отдела — зарплаты от больших к меньшим. Приём со сменой знака работает только на числах: -df$name на символьном столбце даст ошибку. Для убывающей сортировки текста используйте order(df$name, decreasing = TRUE) (она переворачивает сразу все ключи сортировки).
rank() — третий родственник
Мимоходом: rank(x) отвечает ещё на один вопрос — «какое место занимает каждое значение?» — ничего не перемещая:
sort() даёт упорядоченные значения, order() даёт позиции для обхода, rank() даёт положение каждого значения на месте. order() и rank() постоянно путают; заметьте, что они являются обратными перестановками друг друга, и берите rank(), только когда вам буквально нужны ранги (турнирные таблицы, процентили).
Способ dplyr: arrange()
arrange() из dplyr оборачивает весь танец с order() в глагол — столбцы служат ключами сортировки, а desc() переворачивает один из них (сниппет статичный; песочница выполняет только базовый R):
library(dplyr)
runners |> arrange(time)
staff |> arrange(dept, desc(salary))
Ни скобок, ни $, ни приёма со сменой знака — desc() работает и на символьных столбцах. Одно поведенческое отличие стоит знать: arrange() помещает значения NA в конец независимо от направления, тогда как базовый order() тоже по умолчанию использует na.last = TRUE. О том, как arrange() сцепляется с filter() и родственниками, см. введение в dplyr.
У сортировки текста острые края
Две оговорки при сортировке текста. Во-первых, цифры, хранящиеся как текст, сортируются как текст — символьное сравнение идёт посимвольно:
"10" идёт раньше "2", потому что сравнение останавливается на первом символе: "1" < "2". Если столбец чисел сортируется странно, он почти наверняка хранится как character — сначала преобразуйте через as.numeric() (частое последствие грязного импорта CSV).
Во-вторых, упорядочивание текста зависит от системной локали — диакритика, регистр и нелатинские письменности могут сортироваться на вашем ноутбуке иначе, чем на сервере с локалью C. Отсортированный вывод, который должен быть идентичным на разных машинах, должен либо явно задавать локаль, либо сортироваться по нормализованному ключу.
Что вы уносите с собой
sort(x)упорядочивает значения вектора;decreasing = TRUEпереворачивает порядок;NAотбрасываются, если не указатьna.last = TRUE.order(x)возвращает позиции, аdf[order(df$x), ]— та самая базовая идиома сортировки датафреймов.- Несколько столбцов:
order(df$a, -df$b); смена знака для убывания работает только на числах. rank()даёт места без переупорядочивания — это не заменаorder().arrange(dept, desc(salary))из dplyr — та же сортировка с меньшим числом знаков препинания."10"идёт раньше"2"в тексте — проверяйте типы своих столбцов.
Дальше: схлопывание строк в групповые сводки через table(), tapply(), aggregate() и group_by() из dplyr.
Часто задаваемые вопросы
Как отсортировать датафрейм по столбцу в R?
Используйте order() внутри скобок для строк: df[order(df$price), ]. order() возвращает позиции строк в отсортированном порядке, а индексация ими переставляет весь датафрейм. sort() здесь не подойдёт — она сортирует значения одного вектора, теряя их связь с остальными столбцами.
В чём разница между sort() и order() в R?
sort(x) возвращает значения x, переставленные по порядку. order(x) возвращает позиции (индексы), которые расставят x по порядку, — а это и нужно, чтобы отсортировать целый датафрейм по одному из его столбцов: df[order(df$x), ].
Как отсортировать по убыванию в R?
Для вектора: sort(x, decreasing = TRUE). Для датафрейма: df[order(-df$x), ] (смена знака у числового столбца) или df[order(df$x, decreasing = TRUE), ], что работает и для символьных столбцов, где смена знака невозможна. В dplyr: arrange(df, desc(x)).
Как отсортировать по нескольким столбцам в R?
Передайте их все в order(): df[order(df$dept, -df$salary), ] сортирует по отделу, а затем по зарплате внутри каждого отдела по убыванию. Более ранние аргументы — основные ключи; более поздние разрешают совпадения. В dplyr: arrange(df, dept, desc(salary)).