こしあん
2018-12-10

PandasのDataFrameでグループ別にサンプルをN個抜き出す方法


「PandasでGroupbyでグルーピングしたはいんだけど、そこからグループ別にサンプルを1個、2個…と抜き出す、SQLでよくやるやつってどうやるんだっけ?」ということが気になったので、調べました。ちゃんとした方法があります。

例題

今、中国地方と四国地方の県と面積をDataFrameにしてみました。ここから、中国地方と四国地方の面積が小さい県を選んでみます。

import pandas as pd

prefs = {"pref_name":["鳥取", "島根", "岡山", "広島", "山口", "徳島", "香川", "愛媛", "高知"],
         "category":["中国", "中国", "中国", "中国", "中国", "四国", "四国", "四国", "四国"],
         "area":[3507, 6708, 7010, 8480, 6114, 4147, 1862, 5679, 7105]}
df = pd.DataFrame(prefs)
print(df)
  pref_name category  area
0        鳥取       中国  3507
1        島根       中国  6708
2        岡山       中国  7010
3        広島       中国  8480
4        山口       中国  6114
5        徳島       四国  4147
6        香川       四国  1862
7        愛媛       四国  5679
8        高知       四国  7105

グルーピングして抜き出すときはhead()

全体の流れとして、面積(area)でソートして、区分(category)でgroupbyすればいいというのはわかります。でもグルーピングした後抜き出すのはどうやるんしょう?

答えはpandas.core.groupby.GroupBy.headです。headなので、頭からサンプルをN個取ります。後ろから取りたいときはtail()を使います。

やってみましょう。

# sort
sorted = df.sort_values(["area"], 0, [True])
print(sorted)
# group by
grouped = sorted.groupby("category").head(1)
print(grouped)
  pref_name category  area
6        香川       四国  1862
0        鳥取       中国  3507
5        徳島       四国  4147
7        愛媛       四国  5679
4        山口       中国  6114
1        島根       中国  6708
2        岡山       中国  7010
8        高知       四国  7105
3        広島       中国  8480
  pref_name category  area
6        香川       四国  1862
0        鳥取       中国  3507

この通り、四国で最も狭い香川県と、中国で最も狭い鳥取県を抜き出すことができました。

後ろから抜き出したいときはtail()

さっきは面積が小さい県を抜き出しましたが、逆に最も面積が大きい県を抜き出してみましょう。sort_valuesを降順ソートにしてhead()でもいいですが、ここではtail()を使ってみます。

grouped = sorted.groupby("category").tail(1)
print(grouped)
  pref_name category  area
8        高知       四国  7105
3        広島       中国  8480

この通り、高知と広島が出てきました。

ソートをしてグルーピングすると順番がバラバラになるので、グループ間で直したい場合は再ソートが必要

ここがちょっと曲者ですが、head()に2以上を入れるとグループ別にバラバラの値が出てくるということがおきます。

# sort
sorted = df.sort_values(["area"], 0, [True])
# group by
grouped = sorted.groupby("category").head(2)
print(grouped)
  pref_name category  area
6        香川       四国  1862
0        鳥取       中国  3507
5        徳島       四国  4147
4        山口       中国  6114

なので、グループごとに一緒にして表示したいという場合はグループで再ソートする必要がありそうです(ここもっと上手いやり方あったら教えてください)。

# さらにソート
resort = grouped.sort_values(["category"], 0, [False])
print(resort)
  pref_name category  area
6        香川       四国  1862
5        徳島       四国  4147
0        鳥取       中国  3507
4        山口       中国  6114

このように、四国で面積の小さい2県:香川・徳島と、中国で面積の小さい2県:鳥取・山口を抽出することができました。愛媛県は山口県より面積が小さいですが(5679)、四国では3番目に小さいのでここには含まれていません。

実は公式ドキュメントに載ってた

この方法探してて、あんまいい方法ないなと思っていたら公式ドキュメントに載っていました。ただかなり下の方に載っている方法なので、気をつけてみないと見落とします。詳しく知りたかったら見てみてください。

Group By: split-apply-combine
https://pandas.pydata.org/pandas-docs/stable/groupby.html

Related Posts

Python(Numpy)で画像を水平反転する方法:Data Augmentation向け... OpenCVを使わずに単純に画像を左右反転(水平反転)する方法を考えます。ディープラーニングでデータのジェネレーターを自分で実装した場合、Data Augmentationを組み込む際にも必要になります。それを見ていきましょう。 左右反転自体は実は簡単 例えばNumpyの行列を左右反転させてみ...
KerasのCallbackを使って継承したImageDataGeneratorに値が渡せるか確かめ... Kerasで前処理の内容をエポックごとに変えたいというケースがたまにあります。これを実装するとなると、CallbackからGeneratorに値を渡すというコードになりますが、これが本当にできるかどうか確かめてみました。 想定する状況 例えば、前処理で正則化に関係するData Augmenta...
Pandasで複数の列を値をもとに、新しい列を任意の関数で定義する方法... Pandasで、「列A(文字列)と列B(数字)」を文字列として結合し、新しい列を定義するという操作をしたかったのですが、思ったよりも情報がなくハマったので解説していきたいと思います。 サンプルデータ 次のようなデータを定義しました。コミケのサークルスペースのデータを模したものです。 impo...
Numpyだけでサクッと画像を拡大する方法... Numpyだけで画像をサクッと拡大する方法を紹介します。OpenCVやPillowを使うまでもないな、というようなときに便利な方法です。ニューラルネットワークでインプットのサイズを調整するときも使えます。 ただのNearest Neighbor法 拡大前の1ピクセルを1つの四角形と見立てて、拡...
keras_preprocessingを使ってお手軽に画像を回転させる方法... Data Augmentationで画像を回転させたいことがあります。画像の回転は一般に「アフィン変換」と呼ばれる操作で、OpenCVやPillowのライブラリを使えば簡単にできるのですが、Numpy配列に対して1から書くとかなりめんどいのです。Kerasが裏で使っているkeras_preproc...

Add a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です