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




r-faq (13)

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

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

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

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


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

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

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

اختياراتك

  • 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) ، لكنني أفهم لماذا فعل هادلي ذلك بهذه الطريقة بسبب القضايا التي تمت مناقشتها في السؤال المرتبط أعلاه.


جواب ديرك رائع. كما أنه يسلط الضوء على الاختلاف الرئيسي في بناء الجملة المستخدم لفهرسة data.frame s و data.table s:

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

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


يمكنك استخدام الدالة 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() للوصول بشكل أسهل / أكثر مباشرة.


إذا SQL يأتي بشكل طبيعي لك ، يعالج sqldf ORDER BY كما المقصود Codd.


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

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

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

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

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


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

توفر 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) .


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

في Python 2.7 الحديثة ، لدينا نوع OrderedDict الجديد ، الذي يتذكر ترتيب إضافة العناصر.

>>> d = {"third": 3, "first": 1, "fourth": 4, "second": 2}

>>> for k, v in d.items():
...     print "%s: %s" % (k, v)
...
second: 2
fourth: 4
third: 3
first: 1

>>> d
{'second': 2, 'fourth': 4, 'third': 3, 'first': 1}

لإنشاء قاموس مرتب جديد من النص الأصلي ، وفرز حسب القيم:

>>> from collections import OrderedDict
>>> d_sorted_by_value = OrderedDict(sorted(d.items(), key=lambda x: x[1]))

يتصرف OrderedDict مثل dict عادي:

>>> for k, v in d_sorted_by_value.items():
...     print "%s: %s" % (k, v)
...
first: 1
second: 2
third: 3
fourth: 4

>>> d_sorted_by_value
OrderedDict([('first': 1), ('second': 2), ('third': 3), ('fourth': 4)])






r sorting dataframe r-faq