RデータフレームでOracleROW_NUMBER()、RANK()、またはDENSE_RANK()関数( http://www.orafaq.com/node/55 を参照)などの分析関数を実行するにはどうすればよいですか。 ? CRANパッケージの「plyr」は非常に近いですが、それでも異なります。
各機能の機能は、アドホックな方法で実現できる可能性があることに同意します。しかし、私の主な関心事はパフォーマンスです。メモリと速度のために、結合またはインデックスアクセスの使用を避けることをお勧めします。
_data.table
_パッケージは、特にバージョン1.8.1以降、SQL用語でパーティションの機能の多くを提供します。 Rのrank(x, ties.method = "min")
はOracleRANK()
に似ており、ファクター(以下で説明)を使用してDENSE_RANK()
関数を模倣する方法があります。 _ROW_NUMBER
_を模倣する方法は、最後には明らかなはずです。
次に例を示します。R-Forgeから最新バージョンの_data.table
_をロードします。
_install.packages("data.table",
repos= c("http://R-Forge.R-project.org", getOption("repos")))
library(data.table)
_
いくつかのサンプルデータを作成します。
_set.seed(10)
DT<-data.table(ID=seq_len(4*3),group=rep(1:4,each=3),value=rnorm(4*3),
info=c(sample(c("a","b"),4*2,replace=TRUE),
sample(c("c","d"),4,replace=TRUE)),key="ID")
> DT
ID group value info
1: 1 1 0.01874617 a
2: 2 1 -0.18425254 b
3: 3 1 -1.37133055 b
4: 4 2 -0.59916772 a
5: 5 2 0.29454513 b
6: 6 2 0.38979430 a
7: 7 3 -1.20807618 b
8: 8 3 -0.36367602 a
9: 9 3 -1.62667268 c
10: 10 4 -0.25647839 d
11: 11 4 1.10177950 c
12: 12 4 0.75578151 d
_
ID
内でvalue
を減らすことにより、各group
をランク付けします(降順を示すためにvalue
の前の_-
_に注意してください):
_> DT[,valRank:=rank(-value),by="group"]
ID group value info valRank
1: 1 1 0.01874617 a 1
2: 2 1 -0.18425254 b 2
3: 3 1 -1.37133055 b 3
4: 4 2 -0.59916772 a 3
5: 5 2 0.29454513 b 2
6: 6 2 0.38979430 a 1
7: 7 3 -1.20807618 b 2
8: 8 3 -0.36367602 a 1
9: 9 3 -1.62667268 c 3
10: 10 4 -0.25647839 d 3
11: 11 4 1.10177950 c 1
12: 12 4 0.75578151 d 2
_
ランク付けされている値が同数のDENSE_RANK()
の場合、値を係数に変換してから、基になる整数値を返すことができます。たとえば、ID
内のinfo
に基づいて各group
をランク付けします(infoRank
とinfoRankDense
を比較してください)。
_DT[,infoRank:=rank(info,ties.method="min"),by="group"]
DT[,infoRankDense:=as.integer(factor(info)),by="group"]
R> DT
ID group value info valRank infoRank infoRankDense
1: 1 1 0.01874617 a 1 1 1
2: 2 1 -0.18425254 b 2 2 2
3: 3 1 -1.37133055 b 3 2 2
4: 4 2 -0.59916772 a 3 1 1
5: 5 2 0.29454513 b 2 3 2
6: 6 2 0.38979430 a 1 1 1
7: 7 3 -1.20807618 b 2 2 2
8: 8 3 -0.36367602 a 1 1 1
9: 9 3 -1.62667268 c 3 3 3
10: 10 4 -0.25647839 d 3 2 2
11: 11 4 1.10177950 c 1 1 1
12: 12 4 0.75578151 d 2 2 2
_
p.s.こんにちはマシューダウル。
LEADおよびLAG
LEADとLAGを模倣するには、提供された答えから始めます ここ 。グループ内のIDの順序に基づいてランク変数を作成します。上記のような偽のデータではこれは必要ありませんが、IDがグループ内で順番に並んでいない場合、これは生活を少し難しくします。したがって、非シーケンシャルIDを持ついくつかの新しい偽のデータがあります。
_set.seed(10)
DT<-data.table(ID=sample(seq_len(4*3)),group=rep(1:4,each=3),value=rnorm(4*3),
info=c(sample(c("a","b"),4*2,replace=TRUE),
sample(c("c","d"),4,replace=TRUE)),key="ID")
DT[,idRank:=rank(ID),by="group"]
setkey(DT,group, idRank)
> DT
ID group value info idRank
1: 4 1 -0.36367602 b 1
2: 5 1 -1.62667268 b 2
3: 7 1 -1.20807618 b 3
4: 1 2 1.10177950 a 1
5: 2 2 0.75578151 a 2
6: 12 2 -0.25647839 b 3
7: 3 3 0.74139013 c 1
8: 6 3 0.98744470 b 2
9: 9 3 -0.23823356 a 3
10: 8 4 -0.19515038 c 1
11: 10 4 0.08934727 c 2
12: 11 4 -0.95494386 c 3
_
次に、前の1レコードの値を取得するには、group
変数とidRank
変数を使用し、idRank
から_1
_を減算し、_multi = 'last'
_を使用します。引数。上記の2つのエントリのレコードから値を取得するには、_2
_を減算します。
_DT[,prev:=DT[J(group,idRank-1), value, mult='last']]
DT[,prev2:=DT[J(group,idRank-2), value, mult='last']]
ID group value info idRank prev prev2
1: 4 1 -0.36367602 b 1 NA NA
2: 5 1 -1.62667268 b 2 -0.36367602 NA
3: 7 1 -1.20807618 b 3 -1.62667268 -0.3636760
4: 1 2 1.10177950 a 1 NA NA
5: 2 2 0.75578151 a 2 1.10177950 NA
6: 12 2 -0.25647839 b 3 0.75578151 1.1017795
7: 3 3 0.74139013 c 1 NA NA
8: 6 3 0.98744470 b 2 0.74139013 NA
9: 9 3 -0.23823356 a 3 0.98744470 0.7413901
10: 8 4 -0.19515038 c 1 NA NA
11: 10 4 0.08934727 c 2 -0.19515038 NA
12: 11 4 -0.95494386 c 3 0.08934727 -0.1951504
_
LEADの場合、適切なオフセットをidRank
変数に追加し、_multi = 'first'
_に切り替えます。
_DT[,nex:=DT[J(group,idRank+1), value, mult='first']]
DT[,nex2:=DT[J(group,idRank+2), value, mult='first']]
ID group value info idRank prev prev2 nex nex2
1: 4 1 -0.36367602 b 1 NA NA -1.62667268 -1.2080762
2: 5 1 -1.62667268 b 2 -0.36367602 NA -1.20807618 NA
3: 7 1 -1.20807618 b 3 -1.62667268 -0.3636760 NA NA
4: 1 2 1.10177950 a 1 NA NA 0.75578151 -0.2564784
5: 2 2 0.75578151 a 2 1.10177950 NA -0.25647839 NA
6: 12 2 -0.25647839 b 3 0.75578151 1.1017795 NA NA
7: 3 3 0.74139013 c 1 NA NA 0.98744470 -0.2382336
8: 6 3 0.98744470 b 2 0.74139013 NA -0.23823356 NA
9: 9 3 -0.23823356 a 3 0.98744470 0.7413901 NA NA
10: 8 4 -0.19515038 c 1 NA NA 0.08934727 -0.9549439
11: 10 4 0.08934727 c 2 -0.19515038 NA -0.95494386 NA
12: 11 4 -0.95494386 c 3 0.08934727 -0.1951504 NA NA
_
_data.table v1.9.5+
_から、関数frank()
(forfastrank)が実装されました。 frank()
はインタラクティブなシナリオで役立ちますが、frankv()
では簡単にプログラミングできます。
_base::rank
_で使用可能なすべての操作を実装します。さらに、利点は次のとおりです。
frank()
はlist、data.framesおよびdata.tables原子ベクトルに加えて。
列ごとに、rankを昇順または降順のどちらで計算するかを指定できます。
また、dense
の他のタイプに加えて、ランクタイプbase
も実装します。
文字列で_-
_を使用して、降順でランク付けすることもできます。
これは、@ BenBarnesの(優れた)投稿からの同じdata.tableDT
を使用した上記のすべてのポイントの図です。
_require(data.table)
set.seed(10)
sample_n <- function(x, n) sample(x, n, replace=TRUE)
DT <- data.table(
ID = seq_len(4*3),
group = rep(1:4,each=3),
value = rnorm(4*3),
info = c(sample_n(letters[1:2], 8), sample_n(letters[3:4], 4)))
_
dense
ランクを計算します:
_DT[, rank := frank(value, ties.method="dense"), by=group]
_
他のメソッドmin
、max
、random
、average
、およびfirst
を使用することもできます。
降順:
_DT[, rank := frank(-value, ties.method="dense"), by=group]
_
frankv
と同様に、frank
を使用します。
_# increasing order
frankv(DT, "value", ties.method="dense")
# decreasing order
frankv(DT, "value", order=-1L, ties.method="dense")
_
_.SD
_を使用できます。これは、データのサブセットを表し、thatグループに対応するデータを含みます。 _.SD
_の詳細については、 data.table HTMLビネットの概要 を参照してください。
group
でグループ化しながら、_info, value
_列でランク付けします。
_DT[, rank := frank(.SD, info, value, ties.method="dense"), by=group]
_
_-
_を使用して、降順を指定します。
_DT[, rank := frank(.SD, info, -value, ties.method="dense"), by=group]
_
文字列で直接_-
_を使用することもできます
_DT[, rank := frank(.SD, -info, -value, ties.method="dense"), by=group]
_
同様にfrankv
を使用して、列をcols
引数に指定し、列をorder
引数を使用してランク付けする順序を指定できます。
_base::rank
_と比較するための小さなベンチマーク:
_set.seed(45L)
x = sample(1e4, 1e7, TRUE)
system.time(ans1 <- base::rank(x, ties.method="first"))
# user system elapsed
# 22.200 0.255 22.536
system.time(ans2 <- frank(x, ties.method="first"))
# user system elapsed
# 0.745 0.014 0.762
identical(ans1, ans2) # [1] TRUE
_
私は次の人と同じくらいdata.tableが好きですが、必ずしも必要というわけではありません。 data.tableは常に高速ですが、groupsの数がかなり少ない場合は、適度に大きなデータセットでも- plyrは引き続き適切に実行されます。
data.table
sを使用してBenBarnesが行ったことは、plyrを使用して同じようにコンパクトに実行できます(ただし、前に述べたように、多くの場合、おそらく遅くなります)。
library(plyr)
ddply(DT,.(group),transform,valRank = rank(-value))
ddply(DT,.(group),transform,valRank = rank(info,ties.method = "min"),
valRankDense = as.integer(factor(info)))
また、追加のパッケージを1つもロードしなくても、次のようになります。
do.call(rbind,by(DT,DT$group,transform,valRank = rank(-value)))
do.call(rbind,by(DT,DT$group,transform,valRank = rank(info,ties.method = "min"),
valRankDense = as.integer(factor(info))))
ただし、その最後のケースでは、構文上の優れた点のいくつかが失われます。