Câu hỏi Làm thế nào để sắp xếp một dataframe bởi nhiều cột (s)?


Tôi muốn sắp xếp một data.frame bởi nhiều cột. Ví dụ: với data.frame bên dưới, tôi muốn sắp xếp theo cột z (giảm dần) rồi theo cột b (tăng dần):

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

1130
2017-08-18 21:33


gốc




Các câu trả lời:


Bạn có thể dùng order() hoạt động trực tiếp mà không cần sử dụng các công cụ bổ trợ - xem câu trả lời đơn giản này sử dụng mẹo ngay từ đầu example(order) mã:

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

Chỉnh sửa khoảng hơn 2 năm sau:  Nó chỉ được hỏi làm thế nào để làm điều này bằng chỉ số cột. Câu trả lời là chỉ cần chuyển (các) cột sắp xếp mong muốn đến order() chức năng:

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> 

thay vì sử dụng tên cột (và with() để truy cập trực tiếp dễ dàng hơn / nhiều hơn).


1420
2017-08-18 21:51



@ Dave Eddelbuettel là có một phương pháp tương tự đơn giản cho ma trận? - Jota
Nên làm việc theo cùng một cách, nhưng bạn không thể sử dụng with. Thử M <- matrix(c(1,2,2,2,3,6,4,5), 4, 2, byrow=FALSE, dimnames=list(NULL, c("a","b"))) tạo ma trận M, sau đó sử dụng M[order(M[,"a"],-M[,"b"]),] để đặt nó trên hai cột. - Dirk Eddelbuettel
Vừa đủ dễ: dd[ order(-dd[,4], dd[,1]), ], nhưng không thể sử dụng with cho việc đặt tên dựa trên tên miền. - Dirk Eddelbuettel
Tôi có lỗi "đối số không hợp lệ đối với toán tử đơn nhất" khi chạy ví dụ thứ hai. - Nailgun
Lỗi "đối số không hợp lệ đối với toán tử đơn nhất" xảy ra khi bạn sử dụng dấu trừ với một cột ký tự. Giải quyết nó bằng cách gói cột vào xtfrm, ví dụ dd[ order(-xtfrm(dd[,4]), dd[,1]), ]. - Richie Cotton


Lựa chọn của bạn

  • order từ base
  • arrange từ dplyr
  • setorder và setorderv từ data.table
  • arrange từ plyr
  • sort từ taRifx
  • orderBy từ doBy
  • sortData từ Deducer

Hầu hết thời gian bạn nên sử dụng dplyr hoặc là data.table các giải pháp, trừ khi không có phụ thuộc là quan trọng, trong trường hợp sử dụng base::order.


Gần đây tôi đã thêm sort.data.frame vào một gói CRAN, làm cho nó tương thích với lớp như được thảo luận ở đây: Cách tốt nhất để tạo tính nhất quán chung / phương thức cho sort.data.frame?

Do đó, với data.frame dd, bạn có thể sắp xếp như sau:

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 )

Nếu bạn là một trong những tác giả gốc của chức năng này, vui lòng liên hệ với tôi. Thảo luận về miền công cộng ở đây: http://chat.stackoverflow.com/transcript/message/1094290#1094290


Bạn cũng có thể sử dụng arrange() chức năng từ plyr như Hadley đã chỉ ra trong luồng trên:

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

Điểm chuẩn: Lưu ý rằng tôi đã nạp từng gói trong phiên R mới vì có rất nhiều xung đột. Đặc biệt tải doBy gói nguyên nhân sort để trả về "(Các) đối tượng sau được che dấu từ 'x (vị trí 17)': b, x, y, z" và tải gói Deducer ghi đè lên sort.data.frame từ Kevin Wright hoặc gói 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
)

Thời gian trung bình:

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

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

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

Thời gian trung bình: 1.567

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

Thời gian trung bình: 862

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

Thời gian trung bình: 1.694

Lưu ý rằng doBy mất một chút thời gian để tải gói.

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

Không thể thực hiện tải Deducer. Cần giao diện điều khiển JGR.

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

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

Dường như không tương thích với microbenchmark do gắn / tháo rời.


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))

microbenchmark plot

(các dòng mở rộng từ tứ phân vị xuống phần tư trên, dấu chấm là trung vị)


Với những kết quả này và cân nhắc sự đơn giản so với tốc độ, tôi sẽ phải gật đầu arrange bên trong plyr gói. Nó có một cú pháp đơn giản và gần như nhanh như các lệnh R cơ bản với các machination phức tạp của chúng. Điển hình là công việc Hadley Wickham rực rỡ. Nắm tay duy nhất của tôi với nó là nó phá vỡ danh pháp R tiêu chuẩn, nơi sắp xếp các đối tượng được gọi bằng sort(object)nhưng tôi hiểu tại sao Hadley lại làm theo cách đó do các vấn đề được thảo luận trong câu hỏi được liên kết ở trên.


382
2017-07-29 10:48



1 cho sự thông suốt, mặc dù tôi thừa nhận rằng tôi tìm thấy microbenchmark đầu ra khá khó đọc ... - Ben Bolker
Hàm ggplot2 microbenchmark ở trên hiện khả dụng dưới dạng taRifx::autoplot.microbenchmark. - Ari B. Friedman
@ AriB.Friedman Khoảng thời gian trục y là bao nhiêu? - naught101
@AME xem cách b được sắp xếp trong mẫu. Mặc định là sắp xếp theo tăng dần, vì vậy bạn chỉ cần không quấn nó vào desc. Tăng dần trong cả hai: arrange(dd,z,b) . Giảm dần trong cả hai: arrange(dd,desc(z),desc(b)). - Ari B. Friedman
Theo ?arrange: "# LƯU Ý: các hàm plyr KHÔNG bảo toàn hàng.names". Điều này làm cho tuyệt vời arrange() chức năng suboptimal nếu một người muốn giữ row.names. - landroni


Câu trả lời của Dirk là tuyệt vời. Nó cũng làm nổi bật sự khác biệt chính trong cú pháp được sử dụng để lập chỉ mục data.framecát 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)]

Sự khác biệt giữa hai cuộc gọi là nhỏ, nhưng nó có thể có những hậu quả quan trọng. Đặc biệt nếu bạn viết mã sản xuất và / hoặc có liên quan đến tính chính xác trong nghiên cứu của bạn, tốt nhất là tránh lặp lại không cần thiết các tên biến. data.tablegiúp bạn làm điều này.

Dưới đây là ví dụ về cách lặp lại các tên biến có thể khiến bạn gặp rắc rối:

Hãy thay đổi bối cảnh từ câu trả lời của Dirk, và nói rằng đây là một phần của một dự án lớn hơn, nơi có rất nhiều tên đối tượng và chúng dài và có ý nghĩa; thay vì dd nó được gọi là quarterlyreport. No trở nên :

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

Ok, được rồi. Không có gì sai với điều đó. Tiếp theo sếp yêu cầu bạn đưa báo cáo của quý trước vào báo cáo. Bạn đi qua mã của bạn, thêm một đối tượng lastquarterlyreport ở những nơi khác nhau và bằng cách nào đó (làm thế nào trên trái đất?) bạn kết thúc với điều này:

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

Đó không phải là những gì bạn có nghĩa là nhưng bạn đã không phát hiện ra nó bởi vì bạn đã làm nó nhanh và nó nép mình trên một trang của mã tương tự. Mã không rơi xuống (không có cảnh báo và không có lỗi) vì R nghĩ đó là ý của bạn. Bạn hy vọng bất cứ ai đọc báo cáo của bạn sẽ phát hiện ra nó, nhưng có lẽ họ không làm như vậy. Nếu bạn làm việc với các ngôn ngữ lập trình rất nhiều thì tình huống này có thể quen thuộc. Đó là một "lỗi đánh máy" bạn sẽ nói. Tôi sẽ sửa chữa "typo" bạn sẽ nói với sếp của bạn.

Trong data.table chúng tôi lo lắng về những chi tiết nhỏ như thế này. Vì vậy, chúng tôi đã làm một cái gì đó đơn giản để tránh gõ tên biến hai lần. Một cái gì đó rất đơn giản. i được đánh giá trong khung dd đã, tự động. Bạn không cần with() ở tất cả.

Thay vì

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

nó chỉ

dd[order(-z, b)]

Và thay vì

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

nó chỉ

quarterlyreport[order(-z,b)]

Đó là một sự khác biệt rất nhỏ, nhưng nó có thể chỉ tiết kiệm cổ của bạn một ngày. Khi cân nhắc các câu trả lời khác nhau cho câu hỏi này, hãy xem xét đếm số lần lặp lại các tên biến là một trong các tiêu chí của bạn khi quyết định. Một số câu trả lời có khá nhiều lần lặp lại, những câu trả lời khác không có.


128
2018-05-25 16:25



+1 Đây là một điểm tuyệt vời, và nhận được một chi tiết về cú pháp của R thường gây khó chịu cho tôi. Đôi khi tôi sử dụng subset() chỉ để tránh phải liên tục tham chiếu đến cùng một đối tượng trong một cuộc gọi duy nhất. - Josh O'Brien
Bất kỳ ý tưởng tại sao những công việc này theo những cách khác nhau? - naught101
@ naught101 Liệu data.table FAQ 1.9 trả lời rằng? - Matt Dowle
Tôi đoán bạn có thể thêm mới setorder cũng ở đây, vì chủ đề này là nơi chúng tôi gửi tất cả order loại dupes. - David Arenburg


Có rất nhiều câu trả lời tuyệt vời ở đây, nhưng dplyr cung cấp cú pháp duy nhất mà tôi có thể nhanh chóng và dễ nhớ (và vì vậy bây giờ sử dụng rất thường xuyên):

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)

Đối với vấn đề của OP:

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

106
2018-02-18 21:29



Câu trả lời được chấp nhận không hoạt động khi các cột của tôi là hoặc hệ số kiểu (hoặc cái gì đó tương tự) và tôi muốn sắp xếp theo kiểu giảm dần cho cột yếu tố này theo sau là cột số nguyên theo chiều tăng dần. Nhưng điều này hoạt động tốt! Cảm ơn bạn! - Saheel Godhane
Tại sao chỉ"? Tôi tìm thấy data.table's dd[order(-z, b)] khá dễ sử dụng và ghi nhớ. - Matt Dowle
Đồng ý, không có nhiều giữa hai phương pháp đó và data.table là một đóng góp to lớn cho R theo nhiều cách khác nữa. Tôi cho rằng đối với tôi, nó có thể là có ít hơn một tập hợp các dấu ngoặc đơn (hoặc một loại ít dấu ngoặc đơn) trong trường hợp này làm giảm tải nhận thức bởi một số lượng vừa đủ nhận thức được. - Ben
Đối với tôi, thực tế là arrange() hoàn toàn khai báo, dd[order(-z, b)] không phải là. - Mullefa


Gói R data.table cung cấp cả hai Nhanh và bộ nhớ hiệu quả đặt hàng của data.tables với một cú pháp đơn giản (một phần mà Matt đã đánh dấu khá độc đáo trong câu trả lời của mình). Đã có khá nhiều cải tiến và cũng là một chức năng mới setorder() kể từ đó. Từ v1.9.5+, setorder() cũng làm việc với data.frames.

Đầu tiên, chúng tôi sẽ tạo một tập dữ liệu đủ lớn và đánh giá các phương pháp khác nhau được đề cập từ các câu trả lời khác và sau đó liệt kê các tính năng của bảng dữ liệu.

Dữ liệu:

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)

Điểm chuẩn:

Thời gian được báo cáo là do chạy system.time(...) trên các chức năng này được hiển thị bên dưới. Thời gian được lập bảng dưới đây (theo thứ tự từ chậm nhất đến nhanh nhất).

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(...)] cú pháp là ~ 10x nhanh hơn so với các phương pháp nhanh nhất khác (dplyr), trong khi tiêu thụ cùng một lượng bộ nhớ như dplyr.

  • data.table'S setorder() là ~ 14x nhanh hơn so với các phương pháp nhanh nhất khác (dplyr), trong khi tham gia chỉ cần thêm 0.4GB bộ nhớ. dat hiện đang theo thứ tự chúng tôi yêu cầu (vì nó được cập nhật theo tham chiếu).

data.table tính năng:

Tốc độ:

  • bảng dữ liệuđặt hàng là cực kỳ nhanh chóng bởi vì nó thực hiện đặt hàng radix.

  • Cú pháp DT[order(...)] được tối ưu hóa nội bộ để sử dụng bảng dữ liệucũng nhanh chóng đặt hàng. Bạn có thể tiếp tục sử dụng cú pháp R cơ sở quen thuộc nhưng tăng tốc quá trình (và sử dụng ít bộ nhớ hơn).

Ký ức:

  • Hầu hết thời gian, chúng tôi không yêu cầu bản gốc khung dữ liệu hoặc là bảng dữ liệu sau khi sắp xếp lại. Tức là, chúng ta thường gán kết quả trở lại cùng một đối tượng, ví dụ:

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

    Vấn đề là điều này đòi hỏi ít nhất hai lần (2x) bộ nhớ của đối tượng gốc. Được bộ nhớ hiệu quả, bảng dữ liệu do đó cũng cung cấp một hàm setorder().

    setorder() sắp xếp lại data.tables  by reference (tại chỗ), mà không thực hiện bất kỳ bản sao bổ sung nào. Nó chỉ sử dụng thêm bộ nhớ bằng với kích thước của một cột.

Các tính năng khác:

  1. Nó hỗ trợ integer, logical, numeric, character và ngay cả bit64::integer64 loại.

    Lưu ý rằng factor, Date, POSIXct vv .. tất cả các lớp học integer/numeric các loại bên dưới với các thuộc tính bổ sung và do đó được hỗ trợ.

  2. Trong cơ sở R, chúng ta không thể sử dụng - trên một vector ký tự để sắp xếp theo cột đó theo thứ tự giảm dần. Thay vào đó chúng ta phải sử dụng -xtfrm(.).

    Tuy nhiên, trong bảng dữ liệu, chúng ta chỉ có thể làm, ví dụ, dat[order(-x)] hoặc là setorder(dat, -x).


69
2018-03-29 15:52



Cảm ơn câu trả lời rất hữu ích này về data.table. Mặc dù, tôi không hiểu "bộ nhớ đỉnh" là gì và cách bạn tính toán nó. Bạn có thể giải thích được không? Cảm ơn bạn ! - Julien Navarre
Tôi đã sử dụng Dụng cụ -> phân bổ và báo cáo kích thước "Tất cả đống và phân bổ VM". - Arun
@Arun the Instruments link trong bình luận của bạn đã chết. Bạn có muốn đăng cập nhật không? - MichaelChirico
@MichaelChirico Đây là một liên kết đến thông tin về các công cụ do Apple tạo ra: developer.apple.com/library/content/documentation/… - n1k31t4


Với chức năng này (rất hữu ích) của Kevin Wright, được đăng trong phần mẹo của wiki R, điều này có thể dễ dàng đạt được.

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

57
2017-08-18 21:37



Xem câu trả lời của tôi cho điểm chuẩn của thuật toán được sử dụng trong chức năng này. - Ari B. Friedman


hoặc bạn có thể sử dụng gói doBy

library(doBy)
dd <- orderBy(~-z+b, data=dd)

33
2018-01-19 20:44





Giả sử bạn có một data.frame  A và bạn muốn sắp xếp nó bằng cách sử dụng cột được gọi là x Thứ tự giảm dần. Gọi cho sắp xếp data.frame  newdata

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

Nếu bạn muốn thứ tự tăng dần thì hãy thay thế "-" không có gì. Bạn có thể có một cái gì đó như

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

Ở đâu x và z là một số cột trong data.frame  A. Điều này có nghĩa là sắp xếp data.frame  A bởi x giảm dần, y tăng dần và z giảm dần.


31
2018-01-25 13:10