【解説】副問合せ(サブクエリ)の設定方法を学ぶ | データサイエンス100本ノック【問34〜問35 回答】

【解説】副問合せ(サブクエリ)の設定方法を学ぶ | データサイエンス100本ノック【問34〜問35 回答】



当ページのリンクには広告が含まれています。



目次

この記事の対象者


・ データサイエンティストを目指している人

・ Pythonで副問合せ(サブクエリ)の設定方法を学びたい人

以降はデータサイエンス100本ノックの問題を題材に、副問合せ(サブクエリ)の設定方法について学んでいきます。

データサイエンス100本ノックの始め方は、以下の記事を参考にしていただければと思います。

>>データサイエンス100本ノックの始め方

第34問目: 検索結果に対するサブクエリ

P-034: レシート明細データフレーム(df_receipt)に対し、顧客ID(customer_id)ごとに売上金額(amount)を合計して全顧客の平均を求めよ。ただし、顧客IDが”Z”から始まるのものは非会員を表すため、除外して計算すること。

まずは、queryメソッドを使って、customer_idが”Z”から始まる行以外の行を抽出します。

>>queryメソッドを復習する

その後、customer_id列をグルーピングし、amount列の合計を、sumメソッドを使って計算します。

最後にmeanメソッドを用いて全顧客の平均値を求めていきます。

1
df_receipt.query('not customer_id.str.startswith("Z")', engine='python').groupby('customer_id').amount.sum().mean()
出力
1
2547.742234529256

これが答えとなります。

groupbyメソッドは必要な列を抽出してから適用しなければいけません。


ちなみに、以下のようにgroupbyメソッドを適用してからqueryメソッドを用いても、AttributeError: 'DataFrameGroupBy' object has no attribute 'query'というエラーが出力されて実行できません。

エラーコード
1
df_receipt.groupby('customer_id').query('not customer_id.str.startswith("Z")', engine='python').amount.sum().mean()
エラー
1
2
3
4
5
6
7
8
9
10
11
12
13
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-11-9b5c34b46732> in <module>
----> 1 df_receipt.groupby('customer_id').query('not customer_id.str.startswith("Z")', engine='python').amount.sum().mean()

/opt/conda/lib/python3.8/site-packages/pandas/core/groupby/groupby.py in __getattr__(self, attr)
750 return self[attr]
751
--> 752 raise AttributeError(
753 f"'{type(self).__name__}' object has no attribute '{attr}'"
754 )

AttributeError: 'DataFrameGroupBy' object has no attribute 'query'

groupbyメソッドは、必要な列を抽出してから適用しなければいけません。

第35問目: 条件指定でのサブクエリ

P-035: レシート明細データフレーム(df_receipt)に対し、顧客ID(customer_id)ごとに売上金額(amount)を合計して全顧客の平均を求め、平均以上に買い物をしている顧客を抽出せよ。ただし、顧客IDが”Z”から始まるのものは非会員を表すため、除外して計算すること。なお、データは10件だけ表示させれば良い。

まずは、第34問のときと同様に、df_receiptに対するcustomer_idごとにamountを合計し、全顧客の平均を求めます。

1
2
df_amount_mean = df_receipt.query('not customer_id.str.startswith("Z")', engine='python').groupby('customer_id').amount.sum().mean()
df_amount_mean

次に、顧客ID毎の売上金額の合計のデータを抽出します。

1
2
df_customerid_amount_list = df_receipt.groupby('customer_id').amount.sum().reset_index()
df_customerid_amount_list
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
	customer_id	amount
0 CS001113000004 1298
1 CS001114000005 626
2 CS001115000010 3044
3 CS001205000004 1988
4 CS001205000006 3337
... ... ...
8302 CS051513000004 551
8303 CS051515000002 265
8304 CS052212000002 192
8305 CS052514000001 178
8306 ZZ000000000000 12395003
8307 rows × 2 columns

各顧客の売上金額が、平均以上か否かを判定するには、以下のようにすればOKです。

1
df_customerid_amount_list['amount'] >= df_amount_mean
出力
1
2
3
4
5
6
7
8
9
10
11
12
0       False
1 False
2 True
3 False
4 True
...
8302 False
8303 False
8304 False
8305 False
8306 True
Name: amount, Length: 8307, dtype: bool

このbool型の判定を、df_customerid_amount_listのindexとして指定すれば答えが出力されます。

1
df_customerid_amount_list[df_customerid_amount_list['amount']>=df_amount_mean]
出力
1
2
3
4
5
6
7
8
9
10
11
12
13
	customer_id	amount
2 CS001115000010 3044
4 CS001205000006 3337
13 CS001214000009 4685
14 CS001214000017 4132
17 CS001214000052 5639
... ... ...
8286 CS048415000020 2995
8287 CS048513000001 2570
8292 CS049513000008 4450
8300 CS050415000007 2770
8306 ZZ000000000000 12395003
2997 rows × 2 columns

このあたり少しわかりにくいかと思いますので、簡単な例を用いて解説します。

データフレームに対してBool型のフィルタを適用する際の注意点


例えば以下のようなデータフレームに対して、bool型でフィルタしてみたいと思います。

1
2
df_temp = df_customerid_amount_list.head(4)
df_temp
出力
1
2
3
4
5
customer_id	amount
0 CS001113000004 1298
1 CS001114000005 626
2 CS001115000010 3044
3 CS001205000004 1988

2つのフィルタを用意します。

1
2
filter_4 = [True, False, True, False]
filter_3 = [True, False, True]

この2つのフィルタをそれぞれ適用してみます。

filter_4を適用
1
df_temp[filter_4]
出力
1
2
3
	customer_id	amount
0 CS001113000004 1298
2 CS001115000010 3044

このようにTrueと指定されたindexのみが抽出されました。

では、filter_3を適用してみます。

filter_3を適用
1
df_temp[filter_3]
エラーが出力される
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-9-cbf35eb15a30> in <module>
----> 1 df_temp[filter_3]

/opt/conda/lib/python3.8/site-packages/pandas/core/frame.py in __getitem__(self, key)
3013 # Do we have a (boolean) 1d indexer?
3014 if com.is_bool_indexer(key):
-> 3015 return self._getitem_bool_array(key)
3016
3017 # We are left with two options: a single key, and a collection of keys,

/opt/conda/lib/python3.8/site-packages/pandas/core/frame.py in _getitem_bool_array(self, key)
3060 )
3061 elif len(key) != len(self.index):
-> 3062 raise ValueError(
3063 f"Item wrong length {len(key)} instead of {len(self.index)}."
3064 )

ValueError: Item wrong length 3 instead of 4.

このように、フィルターを書けられる側のデータの数とbool型のフィルタの数は、一致していないといけません。



まとめ: 副問合せ(サブクエリ)の設定方法を学びました。

本記事は、「【Python】副問合せ(サブクエリ)の設定方法を学ぶ | データサイエンス100本ノック【問34〜問35 回答】」というテーマでまとめました。

groupbyメソッドは必要な列を抽出してから適用しなければいけない点や、データフレームに対してBool型のフィルタを適用する際の注意点も補足としてまとめました。

>> 続きはこちら

コメント