web-dev-qa-db-ja.com

データフレームを複数の列で並べ替える方法

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
1217

アドオンツールに頼らずに 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())。

1523

あなたの選択

  • orderからbase
  • arrangeからdplyr
  • data.tableからのsetorderおよびsetorderv
  • arrangeからplyr
  • sortからtaRifx
  • orderByからdoBy
  • sortDataからDeducer

ほとんどの場合、依存関係がないことが重要でない限り、dplyrまたはdata.tableの解決策を使用する必要があります。その場合は、base::orderを使用します。


ここで説明したように、CRANパッケージにsort.data.frameを追加してクラス互換性を持たせました。 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


Hadleyが上記のスレッドで指摘したように、plyrからarrange()関数を使用することもできます。

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

ベンチマーク:多くの衝突があったので、私は新しいRセッションで各パッケージをロードしたことに注意してください。特にdoByパッケージをロードするとsortは "次のオブジェクトは 'x(位置17)'からマスクされています:b、x、y、z"を返し、DeducerパッケージをロードするとKevin WrightまたはtaRifxのsort.data.frameを上書きしますパッケージ。

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

デデューサーをロードできませんでした。 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))

microbenchmark plot

(線は下四分位から上四分位まで伸び、ドットは中央値です)


これらの結果と単純さ対スピードの重さを考えると、私はうなずきをarrangeパッケージ内のplyrに与える必要があります。それは単純な構文を持っています、そしてそれでも彼らの複雑な機械化でベースRコマンドと同じくらい速いです。通常素晴らしいHadley Wickhamの作品。私の唯一の不満は、ソートオブジェクトがsort(object)によって呼び出される標準のR命名法を破ることですが、なぜHadleyが上記の問題で議論された問題のためにそのようにしたのか理解できます。

425
Ari B. Friedman

ダークの答えは素晴らしいです。また、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)]

2つの呼び出しの違いはわずかですが、それは重要な結果をもたらす可能性があります。特にプロダクションコードを書いたり、研究の正確さに関心がある場合は、変数名の不必要な繰り返しを避けるのが最善です。 data.tableはあなたがこれをするのを助けます。

これは、変数名を繰り返すと問題が発生する可能性がある例です。

コンテキストをDirkの答えから変更してみましょう。これは、多数のオブジェクト名があり、それらが長くて意味のある、より大きなプロジェクトの一部であると言います。 ddの代わりにquarterlyreportと呼ばれます。あれは。。。になる :

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

いいよ。それは何も悪いことではありません。次にあなたの上司はあなたにレポートに前四半期のレポートを含めるように頼みます。あなたは自分のコードを読み、いろいろな場所にlastquarterlyreportというオブジェクトを追加し、そしてどういうわけか(これは一体どうやって?)

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

それはあなたが意図したことではありませんが、あなたは速くそれをしたのでそれを見つけられませんでした、そしてそれは同様のコードのページに囲まれています。 Rはそれがあなたが意図したものであると考えているので、コードは崩れません(警告もエラーもなし)。誰かがあなたのレポートを読んだ人がそれを見つけたと思いますが、多分彼らはそうしないでしょう。あなたがプログラミング言語をたくさん使っているならば、この状況はすべておなじみのものになるかもしれません。それはあなたが言う「タイプミス」でした。私はあなたが上司に言う「タイプミス」を直します。

data.table では、このような小さな詳細について心配しています。そのため、変数名を2回入力するのを避けるために、簡単なことをしました。とても簡単なものです。 iは既にddの枠内で自動的に評価されます。 with()はまったく必要ありません。

の代わりに

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

それはただ

dd[order(-z, b)]

そしての代わりに

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

それはただ

quarterlyreport[order(-z,b)]

それは非常に小さな違いですが、それはちょうど1日あなたの首を救うかもしれません。この質問に対するさまざまな答えを比較検討する際には、変数名の繰り返しを判断の基準の1つとして数えることを検討してください。いくつかの答えにはかなりの数の繰り返しがあり、他にはありません。

135
Matt Dowle

ここには素晴らしい答えがたくさんありますが、 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
113
Ben

Rパッケージdata.tableは、fastmemoryefficientの両方を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.tableDT[order(...)]構文は、〜10x他の最速のメソッド(dplyr)よりも高速でしたが、 dplyrと同じメモリ量。

  • data.tablesetorder()は、〜14xで、他の最速のメソッド(dplyr)よりも速く、ちょうど0.4GBの追加メモリdatは、必要な順序になりました(参照により更新されます)。

data.table機能:

速度:

  • data.tableの順序付けは、 基数順序付け を実装しているため、非常に高速です。

  • 構文DT[order(...)]は、data.tableの高速順序付けも使用するように内部的に最適化されています。おなじみのベースR構文を使用し続けることができますが、プロセスを高速化できます(そして、より少ないメモリを使用します)。

メモリ:

  • ほとんどの場合、並べ替え後に元のdata.frameまたはdata.tableは必要ありません。つまり、通常、結果を同じオブジェクトに割り当てます。たとえば、次のようになります。

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

    問題は、元のオブジェクトのメモリの少なくとも2倍(2x)が必要なことです。 メモリ効率data.tableとなるため、関数setorder()も提供されます。

    setorder()並べ替えdata.tablesby referencein-place)、追加のコピーは作成しません。 1つの列のサイズに等しい追加メモリのみを使用します。

その他の機能:

  1. integerlogicalnumericcharacter、さらにはbit64::integer64型までサポートします。

    factorDatePOSIXctなどのクラスであることに注意してください。クラスはすべてinteger/numericタイプであり、追加の属性を備えているため、同様にサポートされます。

  2. ベースRでは、文字列で-を使用して、その列を降順で並べ替えることはできません。代わりに、-xtfrm(.)を使用する必要があります。

    ただし、data.tableでは、たとえばdat[order(-x)]またはsetorder(dat, -x)のようにできます。

77
Arun

この(非常に役立つ)Kevin Wrightによる機能 は、R wikiのtipsセクションに掲載されており、これは簡単に達成できます。

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
64

あるいはdoByパッケージを使うこともできます。

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

data.frameAがあり、xという列を使用して降順にソートするとします。ソートされたdata.framenewdataを呼び出す

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

昇順にしたい場合は、"-"を何も置き換えないでください。あなたはのようなものを持つことができます

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

ここで、xおよびzは、data.frameA内の一部の列です。これはdata.frameAxの降順、yの昇順およびzの降順でソートすることを意味します。

33
Khayelihle

sQLが自然に利用できるなら、sqldfはCoddが意図したとおりにORDER BYを処理します。

26
malecki

あるいは、パッケージDeducerを使う

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

私は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

この例が機能する唯一の理由は、ordervector 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),]

次の行は、ordermy.data内の列ageでソートするために機能します。

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

私はこの例によって長い間混乱していたので、これは投稿する価値があると思いました。この投稿がスレッドに適していないと思われる場合は、削除できます。

編集:2014年5月13日

以下は、列名を指定せずにすべての列でデータフレームをソートする一般的な方法です。以下のコードは、左から右へ、または右から左へソートする方法を示しています。すべての列が数値の場合、これは機能します。文字列を追加してみたことはありません。

私は1、2か月前に別のサイトの古い投稿でdo.callコードを見つけましたが、それは広範囲で困難な検索の後にのみです。私はその役職を今すぐ移転できるかどうかわからない。現在のスレッドはRdata.frameを順序付けるための最初のヒットです。だから、私はそのオリジナルの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
15
Mark Miller

プログラムでソートする方法についてOPに追加されたコメントに応答して、

dplyrdata.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

式を評価するための環境も把握するため、formulaを使用することをお勧めします。

データ表

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
14
info_seekeR

Dirkの答えはいいのですが、ソートを持続させる必要がある場合は、そのデータフレームの名前にソートを適用し直す必要があります。サンプルコードを使用すると:

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

Dplyrの配列()は私のお気に入りのオプションです。パイプ演算子を使用して、最も重要度の低いものから最も重要な側面に移動する

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

完全を期すために、BBmiscパッケージからsortByCol()関数を使うこともできます。

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
5
Lars Kotthoff

昔の機械式カードソーターと同じように、最下位のキー、次に最上位のキーなどでソートします。ライブラリは不要です。任意の数のキーと、昇順および降順のキーの組み合わせで機能します。

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

これで、最も重要なキーを実行する準備が整いました。ソートは安定しており、最も重要なキーの関係はすでに解決されています。

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

これは最速ではないかもしれませんが、確かに単純で信頼性があります。

4
Rick

別の方法として、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
3

列番号によるソートについてはあまり言及されていないので、完全を期すためだけに...(列の順序が変わる可能性があるため、エラーになる可能性があるため)多くの場合望ましくないと主張できます。特定の状況(たとえば、迅速な作業が必要で列の順序が変更されるなどのリスクがない場合)では、特に多数の列を処理するときに行うのが最も賢明な方法です。

その場合、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
##    (...)
2
Dominic Comtois

私は、列名が毎回異なる可能性があるn列の順序付けプロセスを自動化したいときに、上記の解決策に苦労していました。私はpsychパッケージからこれを簡単な方法で行うための非常に役に立つ関数を見つけました。

dfOrder(myDf, columnIndices)

ここでcolumnIndicesは1つ以上の列のインデックスで、ソートしたい順に並んでいます。ここでより多くの情報:

'psych'パッケージのdfOrder関数

2
AHegde