読者です 読者をやめる 読者になる 読者になる

グループ化集計して、上位N件の取得

python pandas

やりたいこと

データフレームをグループ化して集計(平均)し、その上で、各グループについて上位N件を取得したい。

つまり、やることを分解すると、

  • 1 グループ化して数値集計(今回は平均)
  • 2 集計した数値で、各グループごとに上位N件取得

1 グループ化して集計

pandasの場合、groupbyメソッドが用意されており、これで解決できる。

dfをデータフレームとした時、

df.groupby([列番号]).集計関数

と記述する。

戻り値は、集計済みのデータフレームである。

2 集計した上で、各グループごとに上位N件

これが頭を悩ませた。

と、いうのも、データフレームのままでやろうとすると、上位N件だけ残す。ということができないからだ。

各グループで、違う項目が上位N件に来るので、表という形式をもっている限り、どうしてもソートはできない。 (各グループごとに、数値の順位を記載した行を用意する。という解決索はあるが、スマートでない気がした。)

そこで、一度、データフレームをバラして、dictにしてから、グループごとにソートする。ということを行なった。

で、面倒なのだが、ソートしてから、またデータフレームにして、出力をした。 (こんな面倒なことやらんでも、jsonで渡せたら最良だったのだが、OUTPUTにexcelで使えるデータを求められていたので、どうしてもcsvで保存する必要があった)

まずmelt

元のデータは行がキー(グループID)、列が値のインデックス。という巨大なデータフレームなので、まずは、これを変形する。

pandas.melt(df, id_vars=['列名'])

でmeltができる

dictにする

さて、これがちょっと面倒くさい。

と、いうのも、一発で簡単にdictに変換できなかったからだ。

そこで、forループを回して、行ごとに処理をすることにした。

my_dict = {}

for x in range(len(df)):                                                                                       

    row_info = df.iloc[x]
    line_info = row_info.tolist()
    c_key = line_info[0]  # キーになる要素
    currentvalue1 = line_info[1]  #  値の要素1
    currentvalue2 = line_info[2]  #  値の要素2

    if c_key in my_dict: my_dict[c_key].append( [currentvalue1, currentvalue2] )
    else: my_dict[c_key] = [[currentvalue1, currentvalue2]]

ソートして上位N件取得

上の処理でつくったdictの各キーで、ソートをし、上位N件だけ残したdict(キー:インデックス名、値:数値)を値にして、新しくdictを作ることにした。(キーは同じ)

わざわざ2重のdict構造にするのは、この後の処理でpandasのメソッドを使って、一発で目的の表に変換できるからだ。

top_n_map = {}

for key in df_map:
    values_list = df_map[key]
    values_list.sort(key=lambda x:(x[1]), reverse = True)

    value_map = {}
    for item in values_list[0:9]:
        value_map[item[0]] = item[1]  # インデックス名をキー、数値を値にして、2次元目のdictを保存
    top_n_map[key] = value_map 

データフレームにもどして、csvに保存

{キー1:{キー2:値} }

という形のdictなら、一発で下の表に変換してくれる。

_____| キー2 | キー2 | .....
キー1 | 値     |    値     | .....
キー1 | 値     |    値     | .....
キー1 | 値     |    値     | .....

これをする命令は

pandas.DataFrame.from_dict(dict)

まとめ

グループ化集計して、上位N件だけ残す処理をした。

が、ふと思ったのは、別にmeltはしなくてもよかったような気がする。

別にmeltしなくともdict {キー:list [ list [インデックス名, 数値] ] }の形は構築できる。

無駄足を踏んだ気がするが、meltをどこかで使う機会があるかもしれないので、記録に残しておく。