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



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



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

| ページ内リンク |
|:-------------|
| [P-011ヒント](#p-011) |
| [P-012ヒント](#p-012) |
| [P-013ヒント](#p-013) |
| [P-014ヒント](#p-014) |
| [P-015ヒント](#p-015) |
| [P-016ヒント](#p-016) |
| [P-017ヒント](#p-017) |
| [P-018ヒント](#p-018) |
| [P-019ヒント](#p-019) |
| [P-020ヒント](#p-020) |

本記事以外のヒントをお探しの方はこちらから
|:-------------|
| [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-011
### 問題
> P-011: 顧客データ（df_customer）から顧客ID（customer_id）の末尾が1のものだけ全項目抽出し、10件表示せよ。

### ヒント
抽出にはdf.query()を使おう！
どうやらqueryの指定条件には後方一致検索用の書き方があるようだが...


- 参考になるサイト

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

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

```python
df_customer.query('customer_id.str.endswith("1")', engine='python').head(10) # 後方一致検索
```
</div></details>

---

# P-012
### 問題
> P-012: 店舗データ（df_store）から、住所 (address) に"横浜市"が含まれるものだけ全項目表示せよ。

### ヒント
P-011の抽出条件を変更！
部分一致の抽出条件も書き方があるようだ！

- 参考になるサイト

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

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

```python
df_customer.query('address.str.contains("横浜市")', engine='python') # 部分一致検索
```
</div></details>

---
# P-013
### 問題
> P-013: 顧客データ（df_customer）から、ステータスコード（status_cd）の先頭がアルファベットのA～Fで始まるデータを全項目抽出し、10件表示せよ。

### ヒント
queryの抽出条件には、正規表現が使えるようだ！
df.query(【カラム名】.str.contains(【正規表現】) )

また正規表現には先頭文字を示す記号があるようだが、、、



- 参考になるサイト

https://www.mlab.im.dendai.ac.jp/~yamada/java/regex/


ちなみにPythonでは正規表現を使用する場合、r'(raw文字列の指定）をつけることが推奨されている！
ex) r'[0-9] → 0～9の任意の文字を表す。

https://blog.pyq.jp/entry/python_kaiketsu_190613

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

```python
df_customer.query('status_cd.str.contains(r"^[A-F]", regex=True)', engine='python').head(10) # 正規表現で前方一致検索 ^は先頭を示す
```
</div></details>

---
# P-014
### 問題
> P-014: 顧客データ（df_customer）から、ステータスコード（status_cd）の末尾が数字の1～9で終わるデータを全項目抽出し、10件表示せよ。

### ヒント
P-013と同様に正規表現を使おう！
先頭を表す記号同様に、末尾を表す記号もあるようだが。。。

- 参考になるサイト

https://www.mlab.im.dendai.ac.jp/~yamada/java/regex/

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

```python
df_customer.query('status_cd.str.contains(r"[1-9]$", regex=True)', engine='python').head(10) # 正規表現で後方一致検索 $は末尾を示す
```
</div></details>

# P-015
### 問題
> P-015: 顧客データ（df_customer）から、ステータスコード（status_cd）の先頭がアルファベットのA～Fで始まり、末尾が数字の1～9で終わるデータを全項目抽出し、10件表示せよ。

### ヒント
P-013とP-014のを組合せだ！
先頭と末尾以外の文字は任意文字（ワイルドカード）みたいに表現したいが...どう表現する...?


- 参考になるサイト

https://www.javadrive.jp/python/regex/index13.html#:~:text=%E4%BE%8B%E3%81%88%E3%81%B0%E4%BB%BB%E6%84%8F%E3%81%AE%E6%96%87%E5%AD%97%E3%82%92,%E5%88%97%E3%81%AB%E3%83%9E%E3%83%83%E3%83%81%E3%81%97%E3%81%BE%E3%81%99%E3%80%82

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

```python
df_customer.query('status_cd.str.contains("^[A-F].*[1-9]$", regex=True)', engine='python').head(10) # 正規表現で部分一致検索 P013とP014の組み合わせ 「.*は」任意文字の繰り返し
```
</div></details>

# P-016
### 問題

> P-016: 店舗データ（df_store）から、電話番号（tel_no）が3桁-3桁-4桁のデータを全項目表示せよ。

### ヒント

"."には任意の1文字を表すと言う正規表現がある。
今回の対象は電話番号なので、ほぼ確実に数字が入ってくると考えれば...

- 参考になるサイト

https://www.mlab.im.dendai.ac.jp/~yamada/java/regex/

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

```python
df_store.query('tel_no.str.contains(r"...-...-....", regex=True)', engine='python') # 正規表現で桁数分の任意文字で一致検索
# 別解
df_store.query('tel_no.str.contains(r"^[0-9]{3}-[0-9]{3}-[0-9]{4}$")', engine='python') 
```
</div></details>

# P-017
### 問題
> P-017: 顧客データ（df_customer）を生年月日（birth_day）で高齢順にソートし、先頭から全項目を10件表示せよ。

### ヒント

データフレーム型にはソートを指定できる書き方がある！
また引数で昇順か降順を指定することができる！
今回は生年月日を高齢順に並べるので、生年月日が小さい順になるから...
（年齢をソートするわけではないことに注意）

- 参考になるサイト

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


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

```python
df_customer.sort_values('birth_day', ascending=True).head(10) # ソート ascending=Trueで昇順設定
```
</div></details>

# P-018
### 問題
> P-018: 顧客データ（df_customer）を生年月日（birth_day）で若い順にソートし、先頭から全項目を10件表示せよ。

### ヒント
P-018のソート順を逆（降順）にするから...

- 参考になるサイト

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

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

```python
df_customer.sort_values('birth_day', ascending=False).head(10) # ソート ascending=Falseで降順設定
```
</div></details>


# P-019
### 問題
> P-019: レシート明細データ（df_receipt）に対し、1件あたりの売上金額（amount）が高い順にランクを付与し、先頭から10件表示せよ。項目は顧客ID（customer_id）、売上金額（amount）、付与したランクを表示させること。なお、売上金額（amount）が等しい場合は同一順位を付与するものとする。


### ヒント
データフレーム型には値に応じてランク付けができる
そのため、問題を解く考え方の1例として、
1. 表示に必要なカラムだけのデータフレームを用意
1. amountをランク付したデータフレームを用意（同値は同順位でランク付）
1. 1と2のデータフレームを結合し、ソートして表示

が挙げられる
ランク付は以下を参考にすると...

- 参考になるサイト

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

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

```python
df_p019 = df_receipt[['customer_id', 'amount']]                      # 該当カラムをコピー
rank_df = df_receipt[['amount']].rank(method='min', ascending=False) # amountをランク付け（method=min : 1位、2位タイ、2位タイ、4位...)
rank_df.rename(columns={"amount": "rank"}, inplace=True)             # rank_dfのカラム名を変更
df_p019 = df_p019.join(rank_df)                                      # 表示したい表示したいdfにランク情報を付与
df_p019.sort_values('amount', ascending=False).head(10)              # amountが高い順に表示
```
</div></details>

# P-020
### 問題
> P-020: レシート明細データ（df_receipt）に対し、1件あたりの売上金額（amount）が高い順にランクを付与し、先頭から10件表示せよ。項目は顧客ID（customer_id）、売上金額（amount）、付与したランクを表示させること。なお、売上金額（amount）が等しい場合でも別順位を付与すること。

### ヒント
P-020のランク付け機能には、同値を同順位で表示するのか、別順位（早いもの順）でランク付するのかを引数で決めることができる。
そのため、P-019の引数を変えると？

- 参考になるサイト

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

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

```python
df_p020 = df_receipt[['customer_id', 'amount']]                        # 該当カラムをコピー
rank_df = df_receipt[['amount']].rank(method='first', ascending=False) # amountをランク付け（method='first'で同一値（重複値）は登場順に順位付け）
rank_df.rename(columns={"amount": "rank"}, inplace=True)               # rank_dfのカラム名を変更
df_p020 = df_p020.join(rank_df)                                        # 表示したい表示したいdfにランク情報を付与
df_p020.sort_values('rank', ascending=True).head(10)                   # amountが高い順に表示(=ランクが高い順に並び替え)
```
</div></details>


# さいごに
これで20%完了！お疲れ様です。
今回はqueryでの条件指定やソート、ランク付など値がどのようなものか確認する上で重要な方法を学べたと思います！
次もよろしくお願いいたします！

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

- [DS 100本ノック（構造化データ加工編）ヒント集（P-001～P010）](https://qiita.com/nonono_copen/items/50a7e66ed9496e6c9d7d)
- DS 100本ノック（構造化データ加工編）ヒント集（P-011～P020）👈 **イマココ!**
- [DS 100本ノック（構造化データ加工編）ヒント集（P-021〜P030）](https://qiita.com/nonono_copen/items/5e0a911bdaaa6e3ca994)
- 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）執筆予定