Как отсортировать фрейм данных по нескольким столбцам


1317

Я хочу отсортировать data.frame по нескольким столбцам. Например, с помощью data.frame ниже я бы хотел отсортировать по столбцу z(по убыванию), а затем по столбцу b(по возрастанию):

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
dd
    b x y z
1  Hi A 8 1
2 Med D 3 1
3  Hi A 9 1
4 Low C 9 2

Ответы:


1626

Вы можете использовать order()функцию напрямую, не прибегая к дополнительным инструментам - посмотрите на этот более простой ответ, который использует хитрость прямо в верхней части example(order)кода:

R> dd[with(dd, order(-z, b)), ]
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1

Отредактируйте примерно через 2 года: просто спросили, как это сделать по индексу столбца. Ответ заключается в том, чтобы просто передать нужные столбцы сортировки в order()функцию:

R> dd[order(-dd[,4], dd[,1]), ]
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1
R> 

вместо того, чтобы использовать имя столбца (и with()для более простого / более прямого доступа).


@Dirk Eddelbuettel Есть ли такой же простой метод для матриц?
Jota

14
Должен работать так же, но вы не можете использовать with. Попробуйте M <- matrix(c(1,2,2,2,3,6,4,5), 4, 2, byrow=FALSE, dimnames=list(NULL, c("a","b")))создать матрицу M, затем используйте ее M[order(M[,"a"],-M[,"b"]),]для упорядочения по двум столбцам.
Дирк Эддельбюттель

4
Достаточно просто:, dd[ order(-dd[,4], dd[,1]), ]но не может использоваться withдля подмножеств на основе имен.
Дирк Эддельбюттель

18
У меня ошибка «неверный аргумент к унарному оператору» при выполнении второго примера.
Nailgun

21
Ошибка «неверный аргумент для унарного оператора» возникает при использовании минуса с символьным столбцом. Решить его обертывание колонки в xtfrm, например dd[ order(-xtfrm(dd[,4]), dd[,1]), ].
Ричи Коттон

477

Ваш выбор

  • order от base
  • arrange от dplyr
  • setorderи setordervизdata.table
  • arrange от plyr
  • sort от taRifx
  • orderBy от doBy
  • sortData от Deducer

Большую часть времени вы должны использовать решения dplyrили data.table, если важно не иметь никаких зависимостей, в этом случае используйте base::order.


Недавно я добавил sort.data.frame в пакет CRAN, сделав его совместимым с классом, как обсуждалось здесь: Лучший способ создать непротиворечивость родового / метода для sort.data.frame?

Поэтому, учитывая data.frame dd, вы можете сортировать следующим образом:

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
library(taRifx)
sort(dd, f= ~ -z + b )

Если вы являетесь одним из авторов этой функции, свяжитесь со мной. Дискуссия в отношении общедоступности является здесь: http://chat.stackoverflow.com/transcript/message/1094290#1094290


Вы также можете использовать arrange()функцию, plyrуказанную Хэдли в приведенной выше теме:

library(plyr)
arrange(dd,desc(z),b)

Тесты: обратите внимание, что я загрузил каждый пакет в новом сеансе R, поскольку было много конфликтов. В частности, загрузка пакета doBy приводит sortк возврату «Следующие объекты маскируются из« x (позиция 17) »: b, x, y, z», а загрузка пакета Deducer перезаписывается sort.data.frameиз пакета Кевина Райта или пакета taRifx.

#Load each time
dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
library(microbenchmark)

# Reload R between benchmarks
microbenchmark(dd[with(dd, order(-z, b)), ] ,
    dd[order(-dd$z, dd$b),],
    times=1000
)

Среднее время:

dd[with(dd, order(-z, b)), ] +778

dd[order(-dd$z, dd$b),] +788

library(taRifx)
microbenchmark(sort(dd, f= ~-z+b ),times=1000)

Среднее время: 1,567

library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=1000)

Среднее время: 862

library(doBy)
microbenchmark(orderBy(~-z+b, data=dd),times=1000)

Среднее время: 1694

Обратите внимание, что doBy требует много времени для загрузки пакета.

library(Deducer)
microbenchmark(sortData(dd,c("z","b"),increasing= c(FALSE,TRUE)),times=1000)

Не удалось заставить дедуцера загрузиться. Требуется консоль JGR.

esort <- function(x, sortvar, ...) {
attach(x)
x <- x[with(x,order(sortvar,...)),]
return(x)
detach(x)
}

microbenchmark(esort(dd, -z, b),times=1000)

Кажется, не совместим с микробенчмарком из-за прикрепления / отсоединения.


m <- microbenchmark(
  arrange(dd,desc(z),b),
  sort(dd, f= ~-z+b ),
  dd[with(dd, order(-z, b)), ] ,
  dd[order(-dd$z, dd$b),],
  times=1000
  )

uq <- function(x) { fivenum(x)[4]}  
lq <- function(x) { fivenum(x)[2]}

y_min <- 0 # min(by(m$time,m$expr,lq))
y_max <- max(by(m$time,m$expr,uq)) * 1.05

p <- ggplot(m,aes(x=expr,y=time)) + coord_cartesian(ylim = c( y_min , y_max )) 
p + stat_summary(fun.y=median,fun.ymin = lq, fun.ymax = uq, aes(fill=expr))

участок микробенчмарка

(линии простираются от нижнего квартиля до верхнего квартиля, точка - медиана)


Учитывая эти результаты и простоту взвешивания в зависимости от скорости, я должен был бы дать согласие arrangeна plyrпакет . Он имеет простой синтаксис и, тем не менее, почти такой же быстрый, как команды base R, с их запутанными махинациями. Типично блестящая работа Хэдли Уикхем. Моя единственная неприятность в том, что она нарушает стандартную номенклатуру R, в которой вызывается сортировка объектов sort(object), но я понимаю, почему Хэдли так поступила из-за проблем, обсуждаемых в вопросе, связанном выше.


3
Функция микробенчмарка ggplot2, описанная выше, теперь доступна как taRifx::autoplot.microbenchmark.
Ари Б. Фридман

@ AriB.Friedman Каковы интервалы по оси Y / каков масштаб?
naught101

@ naught101 Ось Y начинается с 0. Масштаб должен быть микросекундами.
Ари Б. Фридман

2
@AME посмотрим, как bотсортировано в образце. По умолчанию используется сортировка по возрастанию, поэтому вы просто не включаете ее desc. По возрастанию в обоих arrange(dd,z,b). По убыванию в обоих arrange(dd,desc(z),desc(b)).
Ари Б. Фридман

2
Согласно ?arrange: «# ПРИМЕЧАНИЕ: функции plyr НЕ сохраняют row.names». Это делает превосходную arrange()функцию неоптимальной, если кто-то хочет ее сохранить row.names.
Landroni

149

Ответ Дирка великолепен. Это также подчеркивает ключевое различие в синтаксисе, используемом для индексации data.frames и data.tables:

## The data.frame way
dd[with(dd, order(-z, b)), ]

## The data.table way: (7 fewer characters, but that's not the important bit)
dd[order(-z, b)]

Разница между двумя вызовами невелика, но она может иметь важные последствия. Особенно, если вы пишете производственный код и / или обеспокоены правильностью своих исследований, лучше избегать ненужного повторения имен переменных. data.table поможет вам сделать это.

Вот пример того, как повторение имен переменных может привести к неприятностям:

Давайте изменим контекст из ответа Дирка и скажем, что это часть более крупного проекта, в котором много имен объектов, и они длинные и значимые; вместо того, чтобы ddэто называется quarterlyreport. Это становится :

quarterlyreport[with(quarterlyreport,order(-z,b)),]

Хорошо. В этом нет ничего плохого. Затем ваш начальник просит вас включить отчет за последний квартал в отчет. Вы просматриваете свой код, добавляете объект lastquarterlyreportв различные места и каким-то образом (как на земле?) Вы в конечном итоге получаете следующее:

quarterlyreport[with(lastquarterlyreport,order(-z,b)),]

Это не то, что вы имели в виду, но вы не заметили это, потому что вы сделали это быстро, и он расположен на странице с похожим кодом. Код не падает (без предупреждения и без ошибок), потому что R думает, что это то, что вы имели в виду. Вы бы надеялись, что тот, кто прочитает ваш отчет, заметит его, но, возможно, нет. Если вы много работаете с языками программирования, то эта ситуация может быть всем знакома. Это была опечатка, которую вы скажете. Я исправлю "опечатку", которую вы скажете своему боссу.

В data.tableнас беспокоят мелкие детали, подобные этому. Итак, мы сделали что-то простое, чтобы не вводить имена переменных дважды. Нечто очень простое. iоценивается в рамках ddуже, автоматически. Вам не нужно with()вообще.

Вместо

dd[with(dd, order(-z, b)), ]

это просто

dd[order(-z, b)]

И вместо

quarterlyreport[with(lastquarterlyreport,order(-z,b)),]

это просто

quarterlyreport[order(-z,b)]

Это очень маленькая разница, но она может спасти вашу шею однажды. При взвешивании различных ответов на этот вопрос рассмотрите возможность считать повторения имен переменных одним из ваших критериев при принятии решения. Некоторые ответы имеют много повторений, другие нет.


9
+1 Это замечательный момент, и он подробно описывает синтаксис R, который часто раздражал меня. Я иногда использую subset()только для того, чтобы не повторять ссылки на один и тот же объект в течение одного вызова.
Джош О'Брайен

2
@ naught101 Отвечает ли на это data.table FAQ 1.9?
Мэтт Доул

5
Полагаю, вы могли бы добавить и новую setorderфункцию здесь, так как в этом потоке мы отправляем все orderтипы dupes.
Давид Аренбург

125

Здесь есть много отличных ответов, но dplyr дает единственный синтаксис, который я могу быстро и легко запомнить (и теперь использую очень часто):

library(dplyr)
# sort mtcars by mpg, ascending... use desc(mpg) for descending
arrange(mtcars, mpg)
# sort mtcars first by mpg, then by cyl, then by wt)
arrange(mtcars , mpg, cyl, wt)

Для проблемы ОП:

arrange(dd, desc(z),  b)

    b x y z
1 Low C 9 2
2 Med D 3 1
3  Hi A 8 1
4  Hi A 9 1

2
Принятый ответ не работает, когда мои столбцы имеют тип-фактор (или что-то в этом роде), и я хочу отсортировать по убыванию этот столбец с последующим целочисленным столбцом по возрастанию. Но это работает просто отлично! Спасибо!
Сахил Годхейн

10
Почему "только"? Я считаю, что data.table dd[order(-z, b)]довольно прост в использовании и запоминается.
Мэтт Доул

2
Согласен, между этими двумя методами не так уж много, и во многих других отношениях data.tableэто огромный вклад R. Я полагаю, что для меня это может быть то, что в этом случае наличие одного меньшего набора скобок (или одного меньшего типа скобок) уменьшает когнитивную нагрузку лишь на едва заметную величину.
Бен

7
Для меня это сводится к тому, что arrange()полностью декларативно, dd[order(-z, b)]нет.
Муллефа

83

Пакет R data.tableобеспечивает быстрое и эффективное использование памяти в data.tables с простым синтаксисом (часть которого Мэтт очень хорошо подчеркнул в своем ответе ). С тех пор было довольно много улучшений и новой функции setorder(). С v1.9.5+, setorder()также работает с data.frames .

Сначала мы создадим достаточно большой набор данных и проведем сравнение различных методов, упомянутых в других ответах, а затем перечислим функции data.table .

Данные:

require(plyr)
require(doBy)
require(data.table)
require(dplyr)
require(taRifx)

set.seed(45L)
dat = data.frame(b = as.factor(sample(c("Hi", "Med", "Low"), 1e8, TRUE)),
                 x = sample(c("A", "D", "C"), 1e8, TRUE),
                 y = sample(100, 1e8, TRUE),
                 z = sample(5, 1e8, TRUE), 
                 stringsAsFactors = FALSE)

тесты:

Сообщается, что время выполнения system.time(...)этих функций показано ниже. Время приведено в таблице ниже (в порядке от самого медленного до самого быстрого).

orderBy( ~ -z + b, data = dat)     ## doBy
plyr::arrange(dat, desc(z), b)     ## plyr
arrange(dat, desc(z), b)           ## dplyr
sort(dat, f = ~ -z + b)            ## taRifx
dat[with(dat, order(-z, b)), ]     ## base R

# convert to data.table, by reference
setDT(dat)

dat[order(-z, b)]                  ## data.table, base R like syntax
setorder(dat, -z, b)               ## data.table, using setorder()
                                   ## setorder() now also works with data.frames 

# R-session memory usage (BEFORE) = ~2GB (size of 'dat')
# ------------------------------------------------------------
# Package      function    Time (s)  Peak memory   Memory used
# ------------------------------------------------------------
# doBy          orderBy      409.7        6.7 GB        4.7 GB
# taRifx           sort      400.8        6.7 GB        4.7 GB
# plyr          arrange      318.8        5.6 GB        3.6 GB 
# base R          order      299.0        5.6 GB        3.6 GB
# dplyr         arrange       62.7        4.2 GB        2.2 GB
# ------------------------------------------------------------
# data.table      order        6.2        4.2 GB        2.2 GB
# data.table   setorder        4.5        2.4 GB        0.4 GB
# ------------------------------------------------------------
  • data.table«s DT[order(...)]синтаксис был ~ 10 раз быстрее , чем самый быстрый из других методов (dplyr ), потребляя при этом один и тот же объем памяти , как dplyr.

  • data.tableЭто setorder()было в ~ 14 раз быстрее, чем самый быстрый из других методов ( dplyr), при этом потребовалось всего 0,4 ГБ дополнительной памяти .datтеперь в том порядке, в котором мы нуждаемся (так как он обновляется по ссылке).

Возможности data.table:

Скорость:

  • Порядок в data.table очень быстрый, потому что он реализует радикальное упорядочение .

  • Синтаксис DT[order(...)]внутренне оптимизирован для использования быстрого порядка в data.table . Вы можете продолжать использовать знакомый синтаксис base R, но ускорить процесс (и использовать меньше памяти).

Память:

  • В большинстве случаев нам не требуется оригинальный data.frame или data.table после переупорядочения. То есть мы обычно присваиваем результат тому же объекту, например:

    DF <- DF[order(...)]

    Проблема в том, что для этого требуется как минимум вдвое (в 2 раза) объем памяти исходного объекта. Для того, чтобы эффективно использовать память , data.table также предоставляет функциюsetorder() .

    setorder()переупорядочивает data.tables by reference ( на месте ), не делая дополнительных копий. Используется только дополнительная память, равная размеру одного столбца.

Другие особенности:

  1. Он поддерживает integer, logical, numeric, characterи даже bit64::integer64тип.

    Обратите внимание , что factor, Date, и POSIXctт.д .. классы все integer/ numericтипов под дополнительными атрибутами , и поэтому также поддерживаются.

  2. В базе R мы не можем использовать -вектор символов для сортировки по этому столбцу в порядке убывания. Вместо этого мы должны использовать-xtfrm(.) .

    Однако в data.table мы можем просто сделать, например, dat[order(-x)]или setorder(dat, -x).


Спасибо за этот очень поучительный ответ о data.table. Хотя я не понимаю, что такое «пик памяти» и как вы его рассчитали. Не могли бы вы объяснить, пожалуйста? Спасибо !
Жюльен Наварр,

Я использовал Instruments -> allocations и сообщил о размере «Все кучи и распределение VM».
Арун

2
@ Arun Ссылка на инструменты в вашем комментарии мертва. Хотите опубликовать обновление?
MichaelChirico

@MichaelChirico Вот ссылка на информацию об инструментах, изготовленных Apple: developer.apple.com/library/content/documentation/…
n1k31t4

73

С помощью этой (очень полезной) функции Кевина Райта , опубликованной в разделе советов R-wiki, это легко достигается.

sort(dd,by = ~ -z + b)
#     b x y z
# 4 Low C 9 2
# 2 Med D 3 1
# 1  Hi A 8 1
# 3  Hi A 9 1

2
Смотрите мой ответ для сравнительного анализа алгоритма, используемого в этой функции.
Ари Б. Фридман


39

Предположим, у вас есть data.frame Aи вы хотите отсортировать его, используя столбец, называемый по xубыванию. Позвоните отсортированоdata.frame newdata

newdata <- A[order(-A$x),]

Если вы хотите восходящий порядок, то замените его "-"ничем. Вы можете иметь что-то вроде

newdata <- A[order(-A$x, A$y, -A$z),]

где xи zнекоторые столбцы в data.frame A. Это означает сортировку data.frame Aпо xубыванию, yвозрастанию и zубыванию.


32

если SQL вам подходит, sqldfпакет обрабатывает так, ORDER BYкак задумал Codd.


7
MJM, спасибо за указание на этот пакет. Это невероятно гибко, и потому, что половина моей работы уже выполнена путем извлечения из баз данных sql, это проще, чем изучать большую часть R, чем интуитивный синтаксис.
Брэндон Бертельсен


19

В ответ на комментарий, добавленный в OP для того, как сортировать программно:

Использование dplyrиdata.table

library(dplyr)
library(data.table)

dplyr

Просто используйте arrange_, которая является стандартной оценочной версией для arrange.

df1 <- tbl_df(iris)
#using strings or formula
arrange_(df1, c('Petal.Length', 'Petal.Width'))
arrange_(df1, ~Petal.Length, ~Petal.Width)
    Source: local data frame [150 x 5]

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
1           4.6         3.6          1.0         0.2  setosa
2           4.3         3.0          1.1         0.1  setosa
3           5.8         4.0          1.2         0.2  setosa
4           5.0         3.2          1.2         0.2  setosa
5           4.7         3.2          1.3         0.2  setosa
6           5.4         3.9          1.3         0.4  setosa
7           5.5         3.5          1.3         0.2  setosa
8           4.4         3.0          1.3         0.2  setosa
9           5.0         3.5          1.3         0.3  setosa
10          4.5         2.3          1.3         0.3  setosa
..          ...         ...          ...         ...     ...


#Or using a variable
sortBy <- c('Petal.Length', 'Petal.Width')
arrange_(df1, .dots = sortBy)
    Source: local data frame [150 x 5]

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          (dbl)       (dbl)        (dbl)       (dbl)  (fctr)
1           4.6         3.6          1.0         0.2  setosa
2           4.3         3.0          1.1         0.1  setosa
3           5.8         4.0          1.2         0.2  setosa
4           5.0         3.2          1.2         0.2  setosa
5           4.7         3.2          1.3         0.2  setosa
6           5.5         3.5          1.3         0.2  setosa
7           4.4         3.0          1.3         0.2  setosa
8           4.4         3.2          1.3         0.2  setosa
9           5.0         3.5          1.3         0.3  setosa
10          4.5         2.3          1.3         0.3  setosa
..          ...         ...          ...         ...     ...

#Doing the same operation except sorting Petal.Length in descending order
sortByDesc <- c('desc(Petal.Length)', 'Petal.Width')
arrange_(df1, .dots = sortByDesc)

больше информации здесь: https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html

Лучше использовать формулу, поскольку она также захватывает среду для оценки выражения в

Таблица данных

dt1 <- data.table(iris) #not really required, as you can work directly on your data.frame
sortBy <- c('Petal.Length', 'Petal.Width')
sortType <- c(-1, 1)
setorderv(dt1, sortBy, sortType)
dt1
     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
  1:          7.7         2.6          6.9         2.3 virginica
  2:          7.7         2.8          6.7         2.0 virginica
  3:          7.7         3.8          6.7         2.2 virginica
  4:          7.6         3.0          6.6         2.1 virginica
  5:          7.9         3.8          6.4         2.0 virginica
 ---                                                            
146:          5.4         3.9          1.3         0.4    setosa
147:          5.8         4.0          1.2         0.2    setosa
148:          5.0         3.2          1.2         0.2    setosa
149:          4.3         3.0          1.1         0.1    setosa
150:          4.6         3.6          1.0         0.2    setosa

18

Я узнал о orderследующем примере, который долго меня смущал:

set.seed(1234)

ID        = 1:10
Age       = round(rnorm(10, 50, 1))
diag      = c("Depression", "Bipolar")
Diagnosis = sample(diag, 10, replace=TRUE)

data = data.frame(ID, Age, Diagnosis)

databyAge = data[order(Age),]
databyAge

Единственная причина, по которой работает этот пример, заключается в том, что orderсортировка выполняется vector Ageпо столбцу, указанному Ageвdata frame data .

Чтобы увидеть это, создайте идентичный фрейм данных, используя read.tableнесколько разные имена столбцов и не используя ни один из перечисленных выше векторов:

my.data <- read.table(text = '

  id age  diagnosis
   1  49 Depression
   2  50 Depression
   3  51 Depression
   4  48 Depression
   5  50 Depression
   6  51    Bipolar
   7  49    Bipolar
   8  49    Bipolar
   9  49    Bipolar
  10  49 Depression

', header = TRUE)

Вышеуказанная структура линии orderбольше не работает, потому что нет вектора с именем age:

databyage = my.data[order(age),]

Следующая строка работает, потому что orderсортирует по столбцу ageв my.data.

databyage = my.data[order(my.data$age),]

Я думал, что это стоит опубликовать, учитывая, насколько я был смущен этим примером так долго. Если это сообщение не считается подходящим для темы, я могу удалить его.

РЕДАКТИРОВАТЬ: 13 мая 2014 г.

Ниже приведен обобщенный способ сортировки фрейма данных по каждому столбцу без указания имен столбцов. Код ниже показывает, как сортировать слева направо или справа налево. Это работает, если каждый столбец числовой. Я не пробовал с добавлением столбца символов.

Я нашел do.callкод месяц или два назад в старом посте на другом сайте, но только после тщательного и сложного поиска. Я не уверен, что мог бы переместить этот пост сейчас. Настоящий поток является первым хитом для заказа data.frameв R. Итак, я подумал, что моя расширенная версия этого исходного do.callкода может быть полезна.

set.seed(1234)

v1  <- c(0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1)
v2  <- c(0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1)
v3  <- c(0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1)
v4  <- c(0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1)

df.1 <- data.frame(v1, v2, v3, v4) 
df.1

rdf.1 <- df.1[sample(nrow(df.1), nrow(df.1), replace = FALSE),]
rdf.1

order.rdf.1 <- rdf.1[do.call(order, as.list(rdf.1)),]
order.rdf.1

order.rdf.2 <- rdf.1[do.call(order, rev(as.list(rdf.1))),]
order.rdf.2

rdf.3 <- data.frame(rdf.1$v2, rdf.1$v4, rdf.1$v3, rdf.1$v1) 
rdf.3

order.rdf.3 <- rdf.1[do.call(order, as.list(rdf.3)),]
order.rdf.3

4
Этот синтаксис работает, если вы храните ваши данные в data.table вместо data.frame: он require(data.table); my.dt <- data.table(my.data); my.dt[order(age)]работает, потому что имена столбцов доступны в скобках [].
Фрэнк

Я не думаю , что downvote здесь необходима, но ни я думаю , что это добавляет много к вопросу под рукой, особенно принимая во внимание существующий набор ответов, некоторые из которых уже поражающих требование с data.frameс либо использования withили $.
A5C1D2H2I1M1N2O1R2T1

1
upvote для do.callэтого делает короткую работу по сортировке многоколоночного фрейма данных. Просто do.call(sort, mydf.obj)и красивый каскадный вид будет иметься.
AdamO

17

Ответ Дирка хорош, но если вам нужно, чтобы сортировка сохранялась, вы захотите применить сортировку обратно к имени этого фрейма данных. Используя пример кода:

dd <- dd[with(dd, order(-z, b)), ] 

13

Аранжировка () в dplyer - мой любимый вариант. Используйте оператора трубы и переходите от наименее важного к наиболее важному аспекту

dd1 <- dd %>%
    arrange(z) %>%
    arrange(desc(x))

7

Просто для полноты, поскольку о сортировке по номерам столбцов сказано немногое ... Можно, конечно, утверждать, что это часто нежелательно (поскольку порядок столбцов может измениться, что ведет к ошибкам), но в некоторых конкретных ситуациях (например, когда вам нужно выполнить быструю работу и нет риска изменить порядок столбцов), это может быть наиболее разумно, особенно при работе с большим количеством столбцов.

В таком случае do.call()на помощь приходит:

ind <- do.call(what = "order", args = iris[,c(5,1,2,3)])
iris[ind, ]

##        Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
##    14           4.3         3.0          1.1         0.1     setosa
##    9            4.4         2.9          1.4         0.2     setosa
##    39           4.4         3.0          1.3         0.2     setosa
##    43           4.4         3.2          1.3         0.2     setosa
##    42           4.5         2.3          1.3         0.3     setosa
##    4            4.6         3.1          1.5         0.2     setosa
##    48           4.6         3.2          1.4         0.2     setosa
##    7            4.6         3.4          1.4         0.3     setosa
##    (...)

6

Ради полноты: вы также можете использовать sortByCol()функцию из BBmiscпакета:

library(BBmisc)
sortByCol(dd, c("z", "b"), asc = c(FALSE, TRUE))
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1

Сравнение производительности:

library(microbenchmark)
microbenchmark(sortByCol(dd, c("z", "b"), asc = c(FALSE, TRUE)), times = 100000)
median 202.878

library(plyr)
microbenchmark(arrange(dd,desc(z),b),times=100000)
median 148.758

microbenchmark(dd[with(dd, order(-z, b)), ], times = 100000)
median 115.872

4
странно добавлять сравнение производительности, когда ваш метод самый медленный ... во всяком случае, сомнительная ценность использования эталона для 4-х рядовdata.frame
MichaelChirico

5

Точно так же, как механические сортировщики карт давным-давно, сначала сортируют по наименее значимому ключу, затем по следующему наиболее значимому и т. Д. Библиотека не требуется, работает с любым количеством клавиш и любой комбинацией восходящих и нисходящих клавиш.

 dd <- dd[order(dd$b, decreasing = FALSE),]

Теперь мы готовы сделать самый важный ключ. Сортировка стабильна, и любые связи в наиболее значимом ключе уже разрешены.

dd <- dd[order(dd$z, decreasing = TRUE),]

Это может быть не самым быстрым, но это, безусловно, просто и надежно


4

Еще одна альтернатива, используя rgrпакет:

> library(rgr)
> gx.sort.df(dd, ~ -z+b)
    b x y z
4 Low C 9 2
2 Med D 3 1
1  Hi A 8 1
3  Hi A 9 1

4

Я боролся с вышеуказанными решениями, когда хотел автоматизировать процесс заказа для n столбцов, имена столбцов которых могли каждый раз отличаться. Я нашел супер полезную функцию из psychпакета, чтобы сделать это простым способом:

dfOrder(myDf, columnIndices)

где columnIndices указатели одного или нескольких столбцов, в том порядке, в котором вы хотите их отсортировать. Больше информации здесь:

Функция dfOrder из пакета 'psych'

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.