データサイエンス100本ノック~初心者未満の戦いpart7

これはデータサイエンティストの卵がわけもわからないまま100本ノックを行っていく奮闘録である。
完走できるか謎。~~途中で消えてもQiitaにあげてないだけと思ってください。~~


[100本ノックの記事](https://codezine.jp/article/detail/12456)
[100本ノックのガイド](https://github.com/The-Japan-DataScientist-Society/100knocks-preprocess/blob/master/docker/doc/100knocks_guide.pdf)

**ネタバレも含みますのでやろうとされている方は注意**


悲報：問題が進まない(執筆時41本目)


コレは見づらい！この書き方は危険！等ありましたら教えていただきたいです。~~心にダメージを負いながら~~糧とさせていただきます。

この解き方は間違っている！この解釈の仕方は違う！等もありましたらコメントください。

今回は３６～４０まで。
[[前回]](https://qiita.com/sola_wing529/items/173687974b57be5d0d1c)３３～３５
[[目次付き初回]](https://qiita.com/sola_wing529/items/5fdbd97308dd88da28eb)

#36本目
>P-036: レシート明細データフレーム（df_receipt）と店舗データフレーム（df_store）を内部結合し、レシート明細データフレームの全項目と店舗データフレームの店舗名（store_name）を10件表示させよ。

ついに来ました表の結合

SQL的に言うと

```SQLteki.sql
SELECT *
FROM receipt r INNER JOIN store s 
ON r.___ = s.___ 
```

といったところでしょうか。(射影は無視)

pandasだと[（参考）](https://note.nkmk.me/python-pandas-merge-join/)
`pd.maerge(df_receipt,df_store,on='store_cd',how='inner')`

となるらしいので

```mine36.py
df=pd.merge(df_receipt,df_store[['store_cd','store_name']],on='store_cd',how='inner')
df.head(10)

'''模範解答'''
pd.merge(df_receipt, df_store[['store_cd','store_name']], how='inner', on='store_cd').head(10)
```
#37本目
>P-037: 商品データフレーム（df_product）とカテゴリデータフレーム（df_category）を内部結合し、商品データフレームの全項目とカテゴリデータフレームの小区分名（category_small_name）を10件表示させよ。

```mine37.py
df=pd.merge(df_product,df_category[['category_major_cd', 'category_medium_cd','category_small_cd','category_small_name']]
            ,on=['category_major_cd', 'category_medium_cd','category_small_cd'],how='inner')
df.head(10)

'''模範解答'''
pd.merge(df_product
         , df_category[['category_major_cd', 'category_medium_cd','category_small_cd','category_small_name']]
         , how='inner', on=['category_major_cd', 'category_medium_cd','category_small_cd']).head(10)
```

まぁ、これは、ね。

#38本目
>P-038: 顧客データフレーム（df_customer）とレシート明細データフレーム（df_receipt）から、各顧客ごとの売上金額合計を求めよ。ただし、買い物の実績がない顧客については売上金額を0として表示させること。また、顧客は性別コード（gender_cd）が女性（1）であるものを対象とし、非会員（顧客IDが'Z'から始まるもの）は除外すること。なお、結果は10件だけ表示させれば良い。

先頭｢Z｣を消すのは慣れてきました。
今回行うのは左外部結合をしてNull(Nan)値を0にするということ。

Nanを0にするには`.fillna(0)`を使えばOK

```mine38.py
df_cst=df_customer[df_customer.customer_id.str.contains('^[^Z]')].query("gender_cd == '1'")
df_rct=df_receipt.groupby('customer_id').agg({'amount':'sum'}).reset_index()
df=pd.merge(df_cst,df_rct,on='customer_id',how='left').fillna(0)
df.head(10)

'''模範解答'''
df_amount_sum = df_receipt.groupby('customer_id').amount.sum().reset_index()
df_tmp = df_customer.query('gender_cd == "1" and not customer_id.str.startswith("Z")', engine='python')
pd.merge(df_tmp['customer_id'], df_amount_sum, how='left', on='customer_id').fillna(0).head(10)
```

模範解答では計算してから選択(Z始まり除外と女性)していますが、自分は先に選択をしてから計算してます。


#39本目
>P-039: レシート明細データフレーム（df_receipt）から売上日数の多い顧客の上位20件と、売上金額合計の多い顧客の上位20件を抽出し、完全外部結合せよ。ただし、非会員（顧客IDが'Z'から始まるもの）は除外すること。

この問題、完全に間違えて時間かかりました。
最初、

```miss39.py
df=df_receipt[df_receipt.customer_id.str.contains('^[^Z]')]
df_day=df.groupby('customer_id').agg({'sales_ymd':'count'}).sort_values('sales_ymd',ascending=False).reset_index().head(20)
df_amo=df_amo.groupby('customer_id').agg({'amount':'sum'}).sort_values('amount',ascending=False).reset_index().head(20)
pd.merge(df_amo,df_day,on='customer_id',how='outer')

```
このように書いて自信満々で答え合わせしました。

内容としては

- `df_day`に売上日数の多い顧客、上位20件
- `df_amo`に売上金額合計の多い顧客、上位20件

のつもりでした。
しかし、`df_receipt`には

- 「同じ人が」「同じ日に」「違うものを」

買ったデータもあることを失念していて、これを一つの行をしてまとめないといけませんでした。
なので、[（参考）重複した行を削除](https://note.nkmk.me/python-pandas-duplicated-drop-duplicates/)する
`df_day=df[~df.duplicated(subset=['customer_id', 'sales_ymd'])]`
を追加し

```mine39.py
df=df_receipt[df_receipt.customer_id.str.contains('^[^Z]')]
df_day=df[~df.duplicated(subset=['customer_id', 'sales_ymd'])]
df_day=df_day.groupby('customer_id').agg({'sales_ymd':'count'}).sort_values('sales_ymd',ascending=False).reset_index().head(20)
df_amo=df_amo.groupby('customer_id').agg({'amount':'sum'}).sort_values('amount',ascending=False).reset_index().head(20)
pd.merge(df_amo,df_day,on='customer_id',how='outer')

'''模範解答'''
df_sum = df_receipt.groupby('customer_id').amount.sum().reset_index()
df_sum = df_sum.query('not customer_id.str.startswith("Z")', engine='python')
df_sum = df_sum.sort_values('amount', ascending=False).head(20)

df_cnt = df_receipt[~df_receipt.duplicated(subset=['customer_id', 'sales_ymd'])]
df_cnt = df_cnt.query('not customer_id.str.startswith("Z")', engine='python')
df_cnt = df_cnt.groupby('customer_id').sales_ymd.count().reset_index()
df_cnt = df_cnt.sort_values('sales_ymd', ascending=False).head(20)

pd.merge(df_sum, df_cnt, how='outer', on='customer_id')
```

と、なりました。（やった。模範解答より短くなった）

#40本目
>P-040: 全ての店舗と全ての商品を組み合わせると何件のデータとなるか調査したい。店舗（df_store）と商品（df_product）を直積した件数を計算せよ。

```mine40.py
len(df_store)*len(df_product)
```

**え、ダメ？**


```mohan40.py
```模範解答```
df_store_tmp = df_store.copy()
df_product_tmp = df_product.copy()

df_store_tmp['key'] = 0
df_product_tmp['key'] = 0
len(pd.merge(df_store_tmp, df_product_tmp, how='outer', on='key'))
```

#今回はここまで