質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

3回答

4383閲覧

pandas dataframeにおける複数カラムによる順位付け

kiritanpo

総合スコア4

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

0クリップ

投稿2020/07/24 09:11

前提・実現したいこと

pandas dataframeにおける複数カラムによる順位付けについて。
pandas dataframeにおいて、あるカラムの値ごとに分類したのち、複数のカラムの値を使って順位付けし、順位が1かどうかをbooleanからなるseriesで抽出したいです。

(例)
下記のdata frameは、idが1,2の生徒がそれぞれ2回、数学と理科のテストを受けた結果です。
ここから、生徒1,2それぞれについて、数学の得点が最高のレコード、ただし同点のレコードがある場合には理科の得点が最低のレコードを抽出したい。

発生している問題・エラーメッセージ

エラーは表示されないですが、以下の2点の問題があります。 ・カラムごとにTrue, Flaseが返り値になってしまう。(2カラムを持つdata frameが返ってくる。) ・rankの引数として、ascendingにリストを渡すと、Falseが機能しない。

該当のソースコード

python

1# データ 2df_test = pd.DataFrame(data={ 3 'id': [1, 2, 1, 2], 4 'math': [60, 80, 60, 70], 5 'science': [70, 80, 80, 70] 6}) 7 8# 実行した処理 9df_test.groupby('id')[['math', 'science']].rank(ascending=[False, True], method='min') == 1 10 11# 処理結果 12math science 130 True True 141 False False 152 True False 163 True True 17 18# 返ってきてほしい結果 190 True 201 True 212 False 223 False 23Name: flag, dtype: bool 24

試したこと

次に試すことが思いつかない状況です。

補足情報(FW/ツールのバージョンなど)

Anaconda3-2019.03
pandas.version : 0.24.2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kirara0048

2020/07/24 12:14

数学理科がどちらも2回とも同じ点数だった場合はどうなりますか?
kiritanpo

2020/07/24 14:38

説明不足で申し訳ありません。どちらか一方のみを残したいです。
guest

回答3

0

ベストアンサー

下記のような方法はどうでしょうか?

  1. 点数でソートする(mathは高い順、scienceは低い順)
  2. idでグループ化
  3. グループ化されたテーブルから先頭1行を取り出す(目的のデータを取り出し)

取り出すだけなら以上で終了ですが、欲しいのはSeriesによるTrue/Falseとのことですので、元のデータフレームdf_testのインデックスを残して、それをもとにTrue/Falseを作ります。

これをソースコードにすると下記のようになります。

python

1df_head = ( 2 df_test.reset_index() 3 .sort_values(["math", "science"], ascending=[False, True]) 4 .groupby(["id"], sort=False) 5 .head(1) 6 .set_index("index") 7) 8 9s = pd.Series(df_test.index) 10print(s.isin(df_head.index))

投稿2020/07/24 11:10

yymmt

総合スコア1615

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kiritanpo

2020/07/24 17:38

早速ご回答いただきありがとうございます!大変助かりました。いち早くご回答いただき、使いやすいコードでした。ベストアンサーにさせていただきます!
guest

0

np.lexsort()を用いた方法です。考え方はおおむねyymmt氏と同じです。

python

1df_test.index.to_series().isin( 2 np.lexsort([df_test['science'], -df_test['math'], df_test['id']])[::2] 3) 4 5# 0 True 6# 1 True 7# 2 False 8# 3 False 9# dtype: bool

np.lexsort()によって、以下のソートを行ったときのindexの並び順を得ています。

  1. idでソート
  2. idが同じものは数学点数高い順でソート
  3. 数学点数が同じものは理科点数低い順でソート

全員2回テストを受けているので、[::2]をつけて2行ずつ(1行飛ばしということ)取得すると、「今回Trueをつけたい行番号」が得られますので、これをdf_test.index.isin()に渡すことでブーリアン配列を得ることができます。

投稿2020/07/24 16:59

kirara0048

総合スコア1399

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kiritanpo

2020/07/24 17:38

ご回答ありがとうございます!numpyにはあるんですね、シンプルに書けて可読性も高く便利です!テストの回数が人によって違う場合には少し工夫が必要になりそうですが…
guest

0

もっとスマートに出来るかと思うのですが、泥臭く一応出来ました。

Python

1df_test = pd.DataFrame(data={ 2 'id': [1, 2, 1, 2], 3 'math': [60, 80, 60, 70], 4 'science': [70, 80, 80, 70] 5}) 6 7# 降順、昇順の組み合わせはたしかラムダ式でも出来たと思いますが単純にscieceを負にして逆のランクとする 8df_test2 = df_test.copy() 9df_test2['science'] = df_test2['science'] * -1 10df_test2
id math science 0 1 60 -70 1 2 80 -80 2 1 60 -80 3 2 70 -70

tmp1:同着ありのランク
tmp2:同着を判定するため同着なしのランク
同着1位が2件ならば1.5となるが、3件以上であれば値が変わるので2つランクを作成し比較するため。
tmp3:一つにまとめたDataFrame

Python

1tmp1 = df_test2.groupby('id')[['math', 'science']].rank(ascending=False) 2tmp2 = df_test2.groupby('id')[['math', 'science']].rank(ascending=False, method='min') 3tmp3 = pd.concat([df_test['id'], tmp1, tmp2], axis = 1) 4tmp3.columns = ['id', 'math', 'science', 'math2', 'science2'] 5tmp3
id math science math2 science2 0 1 1.5 1.0 1.0 1.0 1 2 1.0 2.0 1.0 2.0 2 1 1.5 2.0 1.0 2.0 3 2 2.0 1.0 2.0 1.0

mathが1.0ならば即True、mathの最高点が同着ならば、その時scienceの最低点(負にしているので1位)をTrue

Python

1(tmp3['math'] == 1.0) | ((tmp3['math'] != 1.0) & (tmp3['math2'] == 1.0) & (tmp3['science'] == 1.0))
0 True 1 True 2 False 3 False dtype: bool

投稿2020/07/24 11:16

aokikenichi

総合スコア2235

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kiritanpo

2020/07/24 17:33

ご回答ありがとうございます!たしかに少し強引ですが、面白い発想ですね!勉強になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問