sorting - كيفية فرز dataframe حسب العمود(الأعمدة)؟




r-faq (13)

أرغب في فرز 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

Answers

توفر R package data.table كلاً من الطلب السريع والذاكرة للبيانات. مع بنية واضحة (جزء منها قام مات بتسليط الضوء بشكل رائع على إجابته ). لقد كان هناك الكثير من التحسينات وكذلك وظيفة 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 DT[order(...)] syntax DT[order(...)] ~ 10x أسرع من أسرع الطرق الأخرى ( dplyr ) ، بينما يستهلك نفس مقدار الذاكرة مثل dplyr .

  • data.table setorder() أسرع بـ 14 مرة من أسرع الطرق الأخرى ( dplyr )، مع أخذ ذاكرة إضافية بسعة 0.4 جيجابايت فقط . dat الآن في الترتيب الذي نطلبه (كما يتم تحديثه بالرجوع إليه).

ميزات data.table:

سرعة:

  • ترتيب data.table سريع للغاية لأنه يطبق ترتيب radix .

  • يتم تحسين بناء الجملة DT[order(...)] داخليًا لاستخدام الطلب السريع لـ data.table أيضًا. يمكنك الاستمرار في استخدام بناء جملة R الأساسي المألوف ، ولكن تسريع العملية (واستخدام ذاكرة أقل).

ذاكرة:

  • في معظم الأحيان ، لا نحتاج إلى data.frame أو data.table الأصلي بعد إعادة الترتيب. أي أننا عادة نعين النتيجة مرة أخرى لنفس الكائن ، على سبيل المثال:

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

    المشكلة هي أن هذا يتطلب على الأقل مرتين (2x) ذاكرة الكائن الأصلي. لتكون ذاكرة فعالة ، لذلك يوفر data.table أيضاً setorder() .

    يعيد setorder() data.tables by reference ( في المكان ) ، دون عمل أي نسخ إضافية. ويستخدم فقط ذاكرة إضافية مساوية لحجم عمود واحد.

ميزات أخرى:

  1. وهو يدعم integer ، logical ، numeric ، character وحتى bit64::integer64 أنواع bit64::integer64 .

    لاحظ أن POSIXct ، Date ، POSIXct وما إلى ذلك .. هي جميع الفئات integer / numeric تحت سمات إضافية وبالتالي فهي مدعومة كذلك.

  2. في القاعدة R ، لا يمكننا استخدام - على متجه حرف للفرز حسب هذا العمود في تنازلي. بدلا من ذلك علينا استخدام -xtfrm(.) .

    ومع ذلك ، في data.table ، يمكننا فقط القيام ، على سبيل المثال ، dat[order(-x)] أو setorder(dat, -x) .


أو يمكنك استخدام حزمة doBy

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

اختياراتك

  • order من base
  • arrange من dplyr
  • setorder و setorderv من data.table
  • arrange من plyr
  • sort من taRifx
  • orderBy من خلال doBy
  • sortData من Deducer

في معظم الأحيان يجب عليك استخدام حلول data.table أو 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..com/transcript/message/1094290#1094290

يمكنك أيضًا استخدام وظيفة arrange() من plyr كما أشار هادلي في الموضوع أعلاه:

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

المعايير: لاحظ أنني حمّلت كل حزمة في جلسة R جديدة نظرًا لوجود الكثير من التعارضات. على وجه الخصوص ، يؤدي تحميل حزمة doBy إلى sort للرجوع "الكائن (الأقنعة) التالية مقنعة من 'x (الموضع 17)': b ، x ، y ، z" ، وتحميل حزمة sort.data.frame من Kevin رايت أو حزمة 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))

(تمتد الخطوط من الربع الأدنى إلى الربع الأعلى ، النقطة هي المتوسط)

بالنظر إلى هذه النتائج ووزن البساطة مقابل السرعة ، يجب أن أعطي الإيماءة arrange في حزمة plyr . لديها بناء الجملة بسيطة ومع ذلك هو تقريبا بسرعة مثل أوامر القاعدة R مع مكائدها المعقدة. تعمل هادلي ويكهام الرائعة عادة. إن وجعي الوحيد هو أنه يكسر المصطلحات القياسية R حيث يتم الحصول على تصنيف الكائنات sort(object) ، لكنني أفهم لماذا فعل هادلي ذلك بهذه الطريقة بسبب القضايا التي تمت مناقشتها في السؤال المرتبط أعلاه.


تمامًا مثل فارزات البطاقات الميكانيكية منذ فترة طويلة ، والفرز الأول حسب المفتاح الأقل أهمية ، ثم الأكثر أهمية ، إلخ. لا تحتاج إلى مكتبة ، تعمل مع أي عدد من المفاتيح وأي مجموعة من المفاتيح الصاعدة والنزولية.

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

نحن الآن مستعدون للقيام بالمفتاح الأكثر أهمية. هذا النوع مستقر ، وأي روابط في أهم مفتاح تم حلها بالفعل.

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

هذا قد لا يكون الأسرع ، ولكنه بالتأكيد بسيط وموثوق


افترض أن لديك 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 data.frame خلال x تنازلي ، y تصاعدي و تنازلي.


هناك الكثير من الإجابات الممتازة هنا ، ولكن 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

ردًا على تعليق تمت إضافته في 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

جواب ديرك جيد ، ولكن إذا احتجت إلى استمرار النوع ، فستحتاج إلى تطبيق الترتيب مرة أخرى على اسم إطار البيانات هذا. باستخدام رمز المثال:

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

بدلا من ذلك ، باستخدام الحزمة Deducer

library(Deducer)
dd<- sortData(dd,c("z","b"),increasing= c(FALSE,TRUE))


تعلمت عن 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 قبل شهر أو شهرين في 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

من أجل الاكتمال: يمكنك أيضًا استخدام وظيفة 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

يبدو أنه في إصدارات Pandas الأخيرة ، فإن الطريقة التي يجب assign هي استخدام assign :

df1 = df1.assign(e=np.random.randn(sLength))

انها لا تنتج SettingWithCopyWarning.





r sorting dataframe r-faq