DataFrameやSeriesのrank()
を使うと、順位を計算できます。
具体的な例を見てみましょう。次のような、あるゲームのユーザーごとのスコアについて考えます。スコアは、値が小さいほど良いものとします。
ユーザーID | スコア |
---|---|
user1 | 5 |
user2 | 50 |
user3 | 10 |
user4 | 100 |
次のように書くと、指定した列を昇順で並び替えた順で順位付けした結果が得られます。
# 列スコアを昇順で順位付け
df["スコア"].rank()
ユーザーID
user1 1.0
user2 3.0
user3 2.0
user4 4.0
Name: スコア, dtype: float64
最もスコアが小さいuser1が1位、最もスコアが大きいuser4が4位になっていることがわかります。
引数ascending
を使って、順位付けを昇順でするか降順でするか指定できます。
- True: 昇順(デフォルト)。最も小さい値が1位になる。
- False: 降順。最も大きい値が1位になる。
rank()
で便利なのは、同一順位の扱いを指定できる点です。
具体的な例を見てみましょう。次のデータでは、user2
とuser3
が同点(50点)なので同一順位になります。
ユーザーID | スコア |
---|---|
user1 | 5 |
user2 | 50 |
user3 | 50 |
user4 | 100 |
同一順位がある場合、順位付けを考える上でのポイントは次の2つです。
- (1) 同一順位である
user2
とuser3
は何位になるか - (2)
user2
とuser3
の次に点数が大きいuser4
は何位になるか
どう計算したいかは状況によって変わります。
(1)のuser2
とuser3
の順位については、「user2
もuser3
も1位の次なのだから2人とも2位」と考えることもできれば「2位と3位の間だと考えて2.5位」という考え方もあります。
また、(2)のuser4
の順位については「user2
とuser3
が2位なら、2位の次であるuser4
は3位」と考えることもできれば「2位が2人いて4人目に相当するからuser4
は4位」という考え方もできます。
rank()
では、引数method
を使って同一順位の計算方法を指定できます。
値 | 説明 | 例(今回のケース) |
---|---|---|
"average" | 平均値(デフォルト) | 2位と3位の平均なので、user2もuser3も2.5位 |
"min" | 同一のデータの中で最小の順位。次の順位は同一データの数だけカウントアップする | user2とuser3は2位。user4は 4位 |
"max" | 同一のデータの中で最大の順位 | user2とuser3は3位。user4は 4位 |
"dense" | 同一のデータの中で最小の順位。次の順位は+1した値 | user2とuser3は2位。user4は3位 |
DataFrameのrank()
を使うと、デフォルトではすべての列について順位づけした結果を得られます。cumsum()
同様、引数axis
で順位を求める方向を指定可能です。
0
: 行方向の処理。列ごとに各行の順位を計算。デフォルト。1
: 列方向の処理。行ごとに各列の順位を計算。
演習
import pandas as pd
# テストの成績を読み込み
df = pd.read_csv("dataset/score_rank.csv", index_col="生徒番号")
df
国語 | 数学 | |
---|---|---|
生徒番号 | ||
user1 | 90 | 100 |
user2 | 92 | 90 |
user3 | 60 | 90 |
user4 | 78 | 79 |
国語の点数で順位付けした結果を列国語の順位
に格納しましょう。
点数が高いほど良い順位(小さい順位)にしたい、つまり降順で並び替えた順位にしたいので、引数ascending
でFalse
を指定します。 実行すると、1番国語の点数が高いuser2
が1位、次に点数が高いuser1
が2位になっていることがわかります。
# 国語の点数で順位付けした結果を新しい列として追加
df["国語の順位"] = df["国語"].rank(ascending=False)
df
国語 | 数学 | 国語の順位 | |
---|---|---|---|
生徒番号 | |||
user1 | 90 | 100 | 2.0 |
user2 | 92 | 90 | 1.0 |
user3 | 60 | 90 | 4.0 |
user4 | 78 | 79 | 3.0 |
次に、数学の点数で順位づけした結果を列数学の順位
に格納しましょう。
数学の点数は、user2
とuser3
が90点で同じ順位です。デフォルトでは、平均をとって2人とも2.5位になります(2位と3位の平均)。
# 数学の点数で順位付けした結果を新しい列として追加
# デフォルトでは、同点の場合は平均をとった順位になる
df["数学の順位"] = df["数学"].rank(ascending=False)
df
国語 | 数学 | 国語の順位 | 数学の順位 | |
---|---|---|---|---|
生徒番号 | ||||
user1 | 90 | 100 | 2.0 | 1.0 |
user2 | 92 | 90 | 1.0 | 2.5 |
user3 | 60 | 90 | 4.0 | 2.5 |
user4 | 78 | 79 | 3.0 | 4.0 |
引数method
を使って、同一順位の計算方法を変えてみましょう。
引数method
に”min”を指定すると、user2もuser3も2位になります。これは、「2位と3位の内の最小値」が採用されるためです。 また、user4の順位が4位になっています。
# "min": 同点の場合は、最小の順位になるように指定
df["数学の順位_min"] = df["数学"].rank(method="min", ascending=False)
df
国語 | 数学 | 国語の順位 | 数学の順位 | 数学の順位_min | |
---|---|---|---|---|---|
生徒番号 | |||||
user1 | 90 | 100 | 2.0 | 1.0 | 1.0 |
user2 | 92 | 90 | 1.0 | 2.5 | 2.0 |
user3 | 60 | 90 | 4.0 | 2.5 | 2.0 |
user4 | 78 | 79 | 3.0 | 4.0 | 4.0 |
引数method
に指定する値を”dense”に変えてみましょう。
実行すると、user2とuser3が2位である点は”min”と同じですが、user4の順位が3位になることがわかります。 「4番目の人だから4位」とするか「2位の次だから3位」とするかによって使い分けましょう。
# "dense": 同一順位の次の順位が"min"とは変わる
df["数学の順位_dense"] = df["数学"].rank(method="dense", ascending=False)
df
国語 | 数学 | 国語の順位 | 数学の順位 | 数学の順位_min | 数学の順位_dense | |
---|---|---|---|---|---|---|
生徒番号 | ||||||
user1 | 90 | 100 | 2.0 | 1.0 | 1.0 | 1.0 |
user2 | 92 | 90 | 1.0 | 2.5 | 2.0 | 2.0 |
user3 | 60 | 90 | 4.0 | 2.5 | 2.0 | 2.0 |
user4 | 78 | 79 | 3.0 | 4.0 | 4.0 | 3.0 |
DataFrameのrank()
を使うと、各行または各列について順位を一括で求められます。
デフォルトでは行方向に沿って順位を計算するため、「各科目ごとに誰が何位だったか」がわかります。
# テストの成績を読み込み
df = pd.read_csv("dataset/score_rank.csv", index_col="生徒番号")
# 列ごとに順位を計算(=各科目ごとに誰が何位だったか)
df.rank(ascending=False)
生徒番号 | 国語 | 数学 |
---|---|---|
user1 | 2.0 | 1.0 |
user2 | 1.0 | 2.5 |
user3 | 4.0 | 2.5 |
user4 | 3.0 | 4.0 |
引数axis
で1
を指定すると行方向に沿って順位が計算されるので、「各生徒ごとにどの科目が何位だったか」 がわかります。
たとえばuser1
は国語が90点、数学が100点なので、国語が2位、数学が1位になります。
# 行ごとに順位を計算(=各生徒ごとにどの科目が何位だったか)
df.rank(ascending=False, axis=1)
生徒番号 | 国語 | 数学 |
---|---|---|
user1 | 2.0 | 1.0 |
user2 | 1.0 | 2.0 |
user3 | 2.0 | 1.0 |
user4 | 2.0 | 1.0 |
コメント