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
アドオンツールに頼らずに 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()
)。
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))
(線は下四分位から上四分位まで伸び、ドットは中央値です)
これらの結果と単純さ対スピードの重さを考えると、私はうなずきをarrange
パッケージ内のplyr
に与える必要があります。それは単純な構文を持っています、そしてそれでも彼らの複雑な機械化でベースRコマンドと同じくらい速いです。通常素晴らしいHadley Wickhamの作品。私の唯一の不満は、ソートオブジェクトがsort(object)
によって呼び出される標準のR命名法を破ることですが、なぜHadleyが上記の問題で議論された問題のためにそのようにしたのか理解できます。
ダークの答えは素晴らしいです。また、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)]
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つとして数えることを検討してください。いくつかの答えにはかなりの数の繰り返しがあり、他にはありません。
ここには素晴らしい答えがたくさんありますが、 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
Rパッケージdata.table
は、fastとmemoryefficientの両方を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
のDT[order(...)]
構文は、〜10x他の最速のメソッド(dplyr
)よりも高速でしたが、 dplyr
と同じメモリ量。
data.table
のsetorder()
は、〜14xで、他の最速のメソッド(dplyr
)よりも速く、ちょうど0.4GBの追加メモリ。 dat
は、必要な順序になりました(参照により更新されます)。
速度:
data.tableの順序付けは、 基数順序付け を実装しているため、非常に高速です。
構文DT[order(...)]
は、data.tableの高速順序付けも使用するように内部的に最適化されています。おなじみのベースR構文を使用し続けることができますが、プロセスを高速化できます(そして、より少ないメモリを使用します)。
メモリ:
ほとんどの場合、並べ替え後に元のdata.frameまたはdata.tableは必要ありません。つまり、通常、結果を同じオブジェクトに割り当てます。たとえば、次のようになります。
DF <- DF[order(...)]
問題は、元のオブジェクトのメモリの少なくとも2倍(2x)が必要なことです。 メモリ効率、data.tableとなるため、関数setorder()
も提供されます。
setorder()
並べ替えdata.tablesby reference
(in-place)、追加のコピーは作成しません。 1つの列のサイズに等しい追加メモリのみを使用します。
その他の機能:
integer
、logical
、numeric
、character
、さらにはbit64::integer64
型までサポートします。
factor
、Date
、POSIXct
などのクラスであることに注意してください。クラスはすべてinteger
/numeric
タイプであり、追加の属性を備えているため、同様にサポートされます。
ベースRでは、文字列で-
を使用して、その列を降順で並べ替えることはできません。代わりに、-xtfrm(.)
を使用する必要があります。
ただし、data.tableでは、たとえばdat[order(-x)]
またはsetorder(dat, -x)
のようにできます。
この(非常に役立つ)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
あるいはdoByパッケージを使うこともできます。
library(doBy)
dd <- orderBy(~-z+b, data=dd)
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
の降順でソートすることを意味します。
sQLが自然に利用できるなら、sqldfはCoddが意図したとおりにORDER BYを処理します。
あるいは、パッケージ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
がmy.data
内の列age
でソートするために機能します。
databyage = my.data[order(my.data$age),]
私はこの例によって長い間混乱していたので、これは投稿する価値があると思いました。この投稿がスレッドに適していないと思われる場合は、削除できます。
編集:2014年5月13日
以下は、列名を指定せずにすべての列でデータフレームをソートする一般的な方法です。以下のコードは、左から右へ、または右から左へソートする方法を示しています。すべての列が数値の場合、これは機能します。文字列を追加してみたことはありません。
私は1、2か月前に別のサイトの古い投稿でdo.call
コードを見つけましたが、それは広範囲で困難な検索の後にのみです。私はその役職を今すぐ移転できるかどうかわからない。現在のスレッドはR
のdata.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
プログラムでソートする方法についてOPに追加されたコメントに応答して、
dplyr
とdata.table
を使う
library(dplyr)
library(data.table)
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
Dirkの答えはいいのですが、ソートを持続させる必要がある場合は、そのデータフレームの名前にソートを適用し直す必要があります。サンプルコードを使用すると:
dd <- dd[with(dd, order(-z, b)), ]
Dplyrの配列()は私のお気に入りのオプションです。パイプ演算子を使用して、最も重要度の低いものから最も重要な側面に移動する
dd1 <- dd %>%
arrange(z) %>%
arrange(desc(x))
完全を期すために、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
昔の機械式カードソーターと同じように、最下位のキー、次に最上位のキーなどでソートします。ライブラリは不要です。任意の数のキーと、昇順および降順のキーの組み合わせで機能します。
dd <- dd[order(dd$b, decreasing = FALSE),]
これで、最も重要なキーを実行する準備が整いました。ソートは安定しており、最も重要なキーの関係はすでに解決されています。
dd <- dd[order(dd$z, decreasing = TRUE),]
これは最速ではないかもしれませんが、確かに単純で信頼性があります。
別の方法として、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
列番号によるソートについてはあまり言及されていないので、完全を期すためだけに...(列の順序が変わる可能性があるため、エラーになる可能性があるため)多くの場合望ましくないと主張できます。特定の状況(たとえば、迅速な作業が必要で列の順序が変更されるなどのリスクがない場合)では、特に多数の列を処理するときに行うのが最も賢明な方法です。
その場合、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
## (...)
私は、列名が毎回異なる可能性があるn列の順序付けプロセスを自動化したいときに、上記の解決策に苦労していました。私はpsych
パッケージからこれを簡単な方法で行うための非常に役に立つ関数を見つけました。
dfOrder(myDf, columnIndices)
ここでcolumnIndices
は1つ以上の列のインデックスで、ソートしたい順に並んでいます。ここでより多くの情報: