データサイエンス100本ノック（構造化データ加工編）ー ヒント集（P-021〜P-030）



# この記事ってなに？
[データサイエンスの100本ノック](https://github.com/The-Japan-DataScientist-Society/100knocks-preprocess)に取り組んだ時、
**「答えを見るのは嫌だ!! でもヒントは欲しい...」** 的なヒトむけに向けた記事！



（ついでに私が取り組んだ時の回答も折り畳みで隠して記載しておくので、
　15分考えてダメなら、確認して次へ進もう）

| ページ内リンク |
|:-------------|
| [P-021ヒント](#p-021) |
| [P-022ヒント](#p-022) |
| [P-023ヒント](#p-023) |
| [P-024ヒント](#p-024) |
| [P-025ヒント](#p-025) |
| [P-026ヒント](#p-026) |
| [P-027ヒント](#p-027) |
| [P-028ヒント](#p-028) |
| [P-029ヒント](#p-029) |
| [P-030ヒント](#p-030) |

本記事以外のヒントをお探しの方はこちらから
|:-------------|
| [DS 100本ノック ヒント記事リスト](#ds-100本ノック-ヒント記事リスト) |

Python公式演習:
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/68c8da0e-add3-18c2-fa20-cdd7c9d7ec31.png)](https://colab.research.google.com/github/The-Japan-DataScientist-Society/100knocks-preprocess/blob/master/docker/work/preprocess_knock_Python.ipynb)

Python公式解答: 
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1990652/68c8da0e-add3-18c2-fa20-cdd7c9d7ec31.png)](https://colab.research.google.com/github/The-Japan-DataScientist-Society/100knocks-preprocess/blob/master/docker/work/answer/ans_preprocess_knock_Python.ipynb)

# P-021
### 問題
> 021: レシート明細データ（df_receipt）に対し、件数をカウントせよ

### ヒント
件数カウントにはlenを使うことで、行数（件数）や列数（カラム数）をカウントできる！

- 参考になるサイト

https://note.nkmk.me/python-pandas-len-shape-size/

<details><summary>主の答え（クリックして表示）</summary><div>

```python
len(df_receipt) # 行数をカウント
```
</div></details>

---

# P-022
### 問題
> P-022: レシート明細データ（df_receipt）の顧客ID（customer_id）に対し、ユニーク件数をカウントせよ。。

### ヒント
pandasにはユニーク値を求める関数が用意されている！
つまりこれで、ユニークな値に絞って件数を数えることで...


- 参考になるサイト

https://note.nkmk.me/python-pandas-value-counts/

<details><summary>主の答え（クリックして表示）</summary><div>

```python
len(df_receipt['customer_id'].unique()) # customer_idのユニーク値をカウント
```
</div></details>

---
# P-023
### 問題
> P-023: P-023: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）と売上数量（quantity）を合計せよ。

### ヒント

まずはdf_receiptを”store_cd”毎にグルーピングする（同じコード毎にまとめる）必要がある！
グルーピングするにはgroupby()をつかえば良い！（下記参考サイトを参照）

グルーピングしたオブジェクトに対し、”quatity”を合計するので...

- 参考になるサイト

https://note.nkmk.me/python-pandas-groupby-statistics/

<details><summary>主の答え（クリックして表示）</summary><div>

```python
df_p023 = df_receipt[['store_cd', 'amount', 'quantity']].groupby('store_cd').sum()
df_p023
# ちなみに.reset_index()を最後につけることでindexを振直し＆表示することができる！

# 公式解答　こっちの方が何をしているかわかりやすいですね
df_receipt.groupby('store_cd').agg({'amount':'sum', 
                                    'quantity':'sum'}).reset_index()

```
</div></details>

---
# P-024
### 問題
> P-024: レシート明細データ（df_receipt）に対し、顧客ID（customer_id）ごとに最も新しい売上年月日（sales_ymd）を求め、10件表示せよ。

### ヒント

P-023と同じくまずは"customer_id"でグループピング！
次に各"customer_id"毎に新しい売上年月日を求める！

ここで、今回のデータでは売上年月日を「yyyymmdd」の8桁の整数で持っている！
つまり、新しい売上年月日であるほど、大きな数字となる！
（2016/01/01 と 2018/12/31を比較すると 20160101 < 20181231 になる！）

なので、最新の日付を抽出するには**max**の値を選べば良いので...

- 参考になるサイト

https://note.nkmk.me/python-pandas-groupby-statistics/

<details><summary>主の答え（クリックして表示）</summary><div>

```python
df_p024 = df_receipt[['customer_id','sales_ymd']].groupby('customer_id').max()
df_p024.head(10)
```
</div></details>

# P-025
### 問題
> P-025: レシート明細データ（df_receipt）に対し、顧客ID（customer_id）ごとに最も古い売上年月日（sales_ymd）を求め、10件表示せよ。

### ヒント

P-024と逆に、最古の値を求めるにはminを取れば良いので...

- 参考になるサイト

https://note.nkmk.me/python-pandas-groupby-statistics/

<details><summary>主の答え（クリックして表示）</summary><div>

```python
df_p025 = df_receipt[['customer_id','sales_ymd']].groupby('customer_id').min()
df_p025.head(10)
```
</div></details>

# P-026
### 問題

> P-026: レシート明細データ（df_receipt）に対し、顧客ID（customer_id）ごとに最も新しい売上年月日（sales_ymd）と古い売上年月日を求め、両者が異なるデータを10件表示せよ

### ヒント
順に解くと以下のようになる！
・1. 最新&最古の売上年月日を持ったdataframeをそれぞれ作成（P-024とP-025で求めた！）
・2. 最新と最古のdataframeを"customer_id"をキーにして組み合わせて、1つのdataframeに！
・3. query()で最新と最古の値が一致しないレコードを抽出し、10件表示！

- 参考になるサイト
データフレームの結合

https://note.nkmk.me/python-pandas-merge-join/

データフレームから値を抽出（query）

https://note.nkmk.me/python-pandas-query/

<details><summary>主の答え（クリックして表示）</summary><div>

```python
df_newSalesymd = df_receipt[['customer_id', 'sales_ymd']].groupby('customer_id').max() # 新しい売上年月日を取得
df_oldSalesymd = df_receipt[['customer_id', 'sales_ymd']].groupby('customer_id').min() # 古い売上年月日を取得
df_p026        = pd.merge(df_newSalesymd, df_oldSalesymd, on='customer_id')              # 新古の売上年月日を結合
df_p026.query('sales_ymd_x != sales_ymd_y').head(10)                                     # 新古の売上年月日が一致しないレコードを取得
```
</div></details>

# P-027
### 問題
> P-027: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）の平均を計算し、降順でTOP5を表示せよ。

### ヒント

"store_cd"でグルーピング!
平均を出すにはmeanを利用する！

あとは降順に並替を行うと...

- 参考になるサイト

https://note.nkmk.me/python-pandas-groupby-statistics/

https://note.nkmk.me/python-pandas-sort-values-sort-index/


<details><summary>主の答え（クリックして表示）</summary><div>

```python
df_p027 = df_receipt[['store_cd', 'amount']].groupby('store_cd').mean() # store_cdでgroupby&amountで平均計算
df_p027.sort_values('amount', ascending=True).head(5)                   # 昇順（小さい順）に並び替え
```
</div></details>

# P-028
### 問題
> > P-028: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）の中央値を計算し、降順でTOP5を表示せよ。

### ヒント
P-027の中央値版や！

- 参考になるサイト

https://note.nkmk.me/python-pandas-groupby-statistics/

https://note.nkmk.me/python-pandas-sort-values-sort-index/

<details><summary>主の答え（クリックして表示）</summary><div>

```python
df_p028 = df_receipt[['store_cd', 'amount']].groupby('store_cd').median() # store_cdでgroupby&medianで中央値
df_p028.sort_values('amount', ascending=False).head(5)                    # 降順に並び替え

# 公式解答
df_receipt.groupby('store_cd').agg({'amount':'median'}).reset_index().sort_values('amount', ascending=False).head(5)
```
</div></details>


# P-029
### 問題
> P-029: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに商品コード（product_cd）の最頻値を求め、10件表示させよ。


### ヒント

わーい最頻値だ！
```python
df_receipt.groupby('store_cd').mode()
```
とやるのは間違い！

なぜなら、groupby()オブジェクトにはmode関数を持っていないから、
**'DataFrameGroupBy' object has no attribute 'mode'**
となるからだ！
（感覚的には出来そうな気もするけど、なんか出来ないみたい...罠だったよ...）

どうやらgroupbyをした後に最頻値を求めるには
lambda関数を使う必要があるようだ！
(todo:ここ詳しくわかり次第追記が必要）

- 参考になるサイト

https://qiita.com/tkkkd/items/10b831d428441ba654de

https://note.nkmk.me/python-lambda-usage/


<details><summary>主の答え（クリックして表示）</summary><div>

```python
df_p029 = df_receipt[['store_cd', 'product_cd']].groupby('store_cd') # store_cdでグルーピンググルーピング
# df_p029.mode() # これは間違い
df_p029.product_cd.apply(lambda x: x.mode()).reset_index().head(10)
```
</div></details>

# P-030
### 問題
> > P-030: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）の分散を計算し、降順で5件表示せよ。

### ヒント
分散はgroupbyにもあるのね...
P-027, P-028と同じ解き方でいけそう!

- 参考になるサイト

https://note.nkmk.me/python-pandas-groupby-statistics/

https://note.nkmk.me/python-pandas-sort-values-sort-index/

<details><summary>主の答え（クリックして表示）</summary><div>

```python
df_p030 = df_receipt[['store_cd', 'amount']].groupby('store_cd').var()
df_p030.sort_values('amount', ascending=False).head(5)
```
</div></details>


# さいごに
30%完了！お疲れ様です。
グルーピングが出てき始めたので、自分含めデータ加工に今まで触れてこなかった人にとって、徐々に問題が重くなり始めてきましたね...

でも一歩一歩進めることで強くなっている気がするので、このまま走り抜けましょう！
（~~遊びに行ったり呑みに行ったり~~問題を解きながらヒント集記事を執筆しているので、
　遅筆になることはご了承ください！）


# DS 100本ノック ヒント記事リスト

- [DS 100本ノック（構造化データ加工編）ヒント集（P-001～P010）](https://qiita.com/nonono_copen/items/50a7e66ed9496e6c9d7d)
- [DS 100本ノック（構造化データ加工編）ヒント集（P-011～P020）](https://qiita.com/nonono_copen/items/6a7cbe9dce700f1b6990)
- DS 100本ノック（構造化データ加工編）ヒント集（P-021～P030）👈 **イマココ!**
- DS 100本ノック（構造化データ加工編）ヒント集（P-031～P040）執筆予定
- DS 100本ノック（構造化データ加工編）ヒント集（P-041～P050）執筆予定
- DS 100本ノック（構造化データ加工編）ヒント集（P-051～P060）執筆予定
- DS 100本ノック（構造化データ加工編）ヒント集（P-061～P070）執筆予定
- DS 100本ノック（構造化データ加工編）ヒント集（P-071～P080）執筆予定
- DS 100本ノック（構造化データ加工編）ヒント集（P-081～P090）執筆予定
- DS 100本ノック（構造化データ加工編）ヒント集（P-091～P100）執筆予定