วิธีจัดเรียงดาต้าเฟรมโดยใช้หลายคอลัมน์


1316

ฉันต้องการเรียงลำดับ 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

คำตอบ:


1625

คุณสามารถใช้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"]),]ในการสั่งซื้อสองคอลัมน์
Dirk Eddelbuettel

4
ง่ายพอ: dd[ order(-dd[,4], dd[,1]), ]แต่ไม่สามารถใช้withสำหรับการย่อยตามชื่อ
Dirk Eddelbuettel

18
ฉันมีข้อผิดพลาด "การโต้เถียงที่ไม่ถูกต้องกับผู้ประกอบการ unary" ในขณะที่รันตัวอย่างที่สอง
Nailgun

21
ข้อผิดพลาด "อาร์กิวเมนต์ที่ไม่ถูกต้องไปยังผู้ประกอบการ unary" เกิดขึ้นเมื่อคุณใช้ลบด้วยคอลัมน์อักขระ แก้ได้โดยการตัดคอลัมน์ในตัวอย่างเช่นxtfrm dd[ order(-xtfrm(dd[,4]), dd[,1]), ]
Richie Cotton

477

ทางเลือกของคุณ

  • order จาก base
  • arrange จาก dplyr
  • setorderและsetordervจากdata.table
  • arrange จาก plyr
  • sort จาก taRifx
  • orderBy จาก doBy
  • sortData จาก Deducer

ส่วนใหญ่เวลาที่คุณควรใช้dplyrหรือการแก้ปัญหาเว้นแต่มีไม่มีการอ้างอิงเป็นสิ่งสำคัญซึ่งในกรณีการใช้งานdata.tablebase::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ที่ Hadley ชี้ให้เห็นในหัวข้อด้านบน:

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

มาตรฐาน: โปรดทราบว่าฉันโหลดแต่ละแพ็คเกจในเซสชัน R ใหม่เนื่องจากมีข้อขัดแย้งมากมาย โดยเฉพาะอย่างยิ่งการโหลดแพคเกจ doBy ทำให้เกิดsortการส่งคืน "วัตถุต่อไปนี้ถูกปิดบังจาก 'x (ตำแหน่ง 17)': b, x, y, z" และการโหลดแพ็คเกจ Deducer เขียนทับsort.data.frameจาก Kevin Wright หรือแพ็คเกจ 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)

เวลาเฉลี่ย: 1,694

โปรดทราบว่า doBy ใช้เวลาพอสมควรในการโหลดแพ็กเกจ

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

ไม่สามารถโหลด Deducer ได้ ต้องการคอนโซล JGR

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

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

ดูเหมือนจะไม่เข้ากันได้กับ microbenchmark เนื่องจากการแนบ / แยก


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

(เส้นขยายจากควอไทล์ล่างถึงควอไทล์ตอนบนจุดคือค่ามัธยฐาน)


ได้รับผลเหล่านี้และมีน้ำหนักความเร็วกับความเรียบง่ายฉันต้องผงกหัวไปarrangeในplyrแพคเกจ มันมีไวยากรณ์ที่เรียบง่าย แต่ก็เกือบจะเร็วเหมือนคำสั่งพื้นฐาน R พร้อมการตัดเฉือนที่ซับซ้อน โดยทั่วไปแล้ว Hadley Wickham จะทำงานได้อย่างยอดเยี่ยม สิ่งเดียวที่ฉันจับได้คือมันทำลายระบบการตั้งชื่อมาตรฐานที่เรียกใช้การเรียงลำดับวัตถุsort(object)แต่ฉันเข้าใจว่าทำไม Hadley ทำแบบนั้นเนื่องจากปัญหาที่กล่าวถึงในคำถามที่เชื่อมโยงข้างต้น


3
ฟังก์ชั่น microbenchmark ggplot2 ด้านบนมีให้ใช้งานในขณะtaRifx::autoplot.microbenchmarkนี้
Ari B. Friedman

@ AriB.Friedman ช่วงเวลา y แกนคืออะไร / ขนาดคืออะไร?
naught101

@ naught101 แกน y เริ่มต้นที่ 0. สเกลควรเป็นไมโครวินาที
Ari B. Friedman

2
@AME ดูวิธีbเรียงลำดับในตัวอย่าง descเริ่มต้นคือการจัดเรียงจากน้อยไปหามากดังนั้นคุณก็ไม่ห่อไว้ใน Ascending arrange(dd,z,b)ทั้งใน: arrange(dd,desc(z),desc(b))น้อยไปมากทั้งใน:
Ari B. Friedman

2
ตาม?arrange: "# NOTE: ฟังก์ชั่น plyr ไม่ควรเก็บ row. ชื่อ" นี้จะทำให้ดีด้อยฟังก์ชั่นถ้าใครต้องการที่จะให้arrange() row.names
landroni

148

คำตอบของเดิร์คนั้นยอดเยี่ยม นอกจากนี้ยังเน้นถึงความแตกต่างที่สำคัญในไวยากรณ์ที่ใช้สำหรับการทำดัชนี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()เพื่อหลีกเลี่ยงการอ้างถึงวัตถุเดียวกันซ้ำ ๆ ในการโทรครั้งเดียว
Josh O'Brien

2
@ naught101 data.table คำถามที่พบบ่อย 1.9 ตอบว่า?
Matt Dowle

5
ฉันเดาว่าคุณสามารถเพิ่มsetorderฟังก์ชั่นใหม่ได้ที่นี่เนื่องจากเธรดนี้เป็นที่ที่เราส่งงานorderพิมพ์ทุกประเภท
David Arenburg

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)

สำหรับปัญหาของ 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

2
คำตอบที่ได้รับการยอมรับไม่ทำงานเมื่อคอลัมน์ของฉันเป็นหรือพิมพ์ปัจจัย (หรืออะไรทำนองนั้น) และฉันต้องการเรียงลำดับจากมากไปน้อยสำหรับคอลัมน์ปัจจัยนี้ตามด้วยคอลัมน์จำนวนเต็มในรูปแบบจากน้อยไปมาก แต่มันใช้งานได้ดี! ขอบคุณ!
Saheel Godhane

10
ทำไม "เท่านั้น" ฉันพบว่า data.table dd[order(-z, b)]ใช้งานง่ายและจดจำได้ง่าย
Matt Dowle

2
เห็นด้วยมีไม่มากระหว่างสองวิธีและdata.tableมีส่วนร่วมอย่างมากRในวิธีอื่น ๆ ด้วย ฉันคิดว่าสำหรับฉันมันอาจเป็นไปได้ว่าการมีวงเล็บน้อยกว่าหนึ่งชุด (หรือหนึ่งวงเล็บประเภทน้อยกว่า) ในกรณีนี้จะลดภาระการรับรู้โดยจำนวนที่รับรู้เพียงเล็กน้อย
Ben

7
สำหรับฉันมันลงมาจากความจริงที่ว่าarrange()เป็นการประกาศอย่างสมบูรณ์dd[order(-z, b)]ไม่ใช่
Mullefa

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's setorder()เป็น~ 14 เท่าเร็วกว่าเร็วที่สุดของวิธีการอื่น ๆ ( dplyr) ในขณะที่การเพียงแค่ 0.4GB หน่วยความจำเสริม datขณะนี้อยู่ในลำดับที่เราต้องการ (เนื่องจากมีการอัปเดตโดยการอ้างอิง)

คุณสมบัติ data.table:

ความเร็ว:

  • data.tableของการสั่งซื้อเป็นไปอย่างรวดเร็วมากเพราะมันดำเนินการสั่งซื้อกี่

  • ไวยากรณ์ได้DT[order(...)]รับการปรับปรุงภายในเพื่อใช้data.tableของการสั่งซื้อที่รวดเร็วเช่นกัน คุณสามารถใช้ไวยากรณ์ R พื้นฐานที่คุ้นเคยต่อไป แต่เร่งกระบวนการ (และใช้หน่วยความจำน้อยกว่า)

หน่วยความจำ:

  • ส่วนใหญ่เราไม่ต้องการdata.frameดั้งเดิมหรือdata.tableหลังจากจัดลำดับใหม่ นั่นคือเรามักจะกำหนดผลลัพธ์กลับไปที่วัตถุเดียวกันตัวอย่างเช่น:

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

    ปัญหาคือต้องใช้หน่วยความจำอย่างน้อยสองครั้ง (2x) ของวัตถุต้นฉบับ จะเป็นหน่วยความจำที่มีประสิทธิภาพ , data.tablesetorder()จึงนอกจากนี้ยังมีฟังก์ชั่น

    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 แม้ว่าฉันจะไม่เข้าใจว่า "หน่วยความจำสูงสุด" คืออะไรและคุณคำนวณอย่างไร คุณช่วยอธิบายได้มั้ย ขอบคุณ !
Julien Navarre

ฉันใช้เครื่องมือ -> การจัดสรรและรายงานขนาด "heap และการจัดสรร VM ทั้งหมด"
อรุณ

2
@ ลิงก์เครื่องมือในความคิดเห็นของคุณตายแล้ว สนใจโพสต์การอัปเดตหรือไม่
MichaelChirico

@MichaelChirico นี่คือลิงค์ไปยังข้อมูลเกี่ยวกับเครื่องมือที่ทำโดย Apple: developer.apple.com/library/content/documentation/…
n1k31t4

73

ด้วยฟังก์ชั่นนี้ (มีประโยชน์มาก) โดย Kevin Wrightโพสต์ในส่วนเคล็ดลับของ 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
ดูคำตอบของฉันสำหรับการเปรียบเทียบอัลกอริทึมที่ใช้ในฟังก์ชั่นนี้
Ari B. Friedman


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 ที่น้อยกว่าสัญชาตญาณ
Brandon Bertelsen


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ไม่ตามคอลัมน์ที่มีชื่อในAgedata 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ทุกประเภทในคอลัมน์ในagemy.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)]สิ่งนี้จะทำงานได้เนื่องจากชื่อคอลัมน์ถูกทำให้พร้อมใช้งานภายในวงเล็บ []
Frank

ฉันไม่คิดว่า 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

ฉันกำลังดิ้นรนกับวิธีแก้ไขปัญหาข้างต้นเมื่อฉันต้องการทำให้กระบวนการสั่งซื้อของฉันเป็นอัตโนมัติสำหรับคอลัมน์ n คอลัมน์ซึ่งชื่อคอลัมน์อาจแตกต่างกันไปในแต่ละครั้ง ฉันพบฟังก์ชั่นที่มีประโยชน์สุด ๆ จากpsychแพ็คเกจเพื่อทำสิ่งนี้ในลักษณะที่ตรงไปตรงมา:

dfOrder(myDf, columnIndices)

โดยที่columnIndicesดัชนีของคอลัมน์หนึ่งคอลัมน์ขึ้นไปเรียงตามลำดับที่คุณต้องการเรียงลำดับ ข้อมูลเพิ่มเติมที่นี่:

ฟังก์ชัน dfOrder จากแพ็คเกจ 'psych'

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.