GCIデータサイエンティスト育成講座の演習問題を解く Chapter7

# GCIデータサイエンティスト育成講座
「[GCIデータサイエンティスト育成講座](http://weblab.t.u-tokyo.ac.jp/gci_contents/)」は、東京大学(松尾研究室)が開講している"*<u>実践型のデータサイエンティスト育成講座およびDeep Learning講座</u>*"で、演習パートのコンテンツがJupyterNoteBook形式で公開(CC-BY-NC-ND)されています。
Chapter7は「**Matplotlibを使ったデータ可視化**」で、Chapter2で学んだ散布図、折れ線グラフ、ヒストグラムに加えて、棒グラフ、円グラフ、バブルチャート、ローソクチャートを学習していきます。
日本語で学べる貴重で素晴らしい教材を公開いただいていることへの「いいね！」ボタンの代わりに、解いてみた解答を載せてみます。間違っているところがあったらご指摘ください。

章末の総合問題がやけに難しかったです。そもそも問題の意図を理解できているのやら……。


# Chapter7 Matplotlibを使ったデータ可視化

## 7.1 データの可視化

### 7.1.1 データ可視化の基礎

> **<練習問題 1>**
> 以前扱った学生のデータ（student-mat.csv）を使って、学校を選んだ理由（reason）を円グラフ化して、それぞれの割合を出してください。

```py3
import matplotlib.pyplot as plt
student_data_math = pd.read_csv("student-mat.csv",sep=";")

allCount = len(student_data_math["reason"])
reasonCount = student_data_math["reason"].value_counts()
reasonRatio = reasonCount / allCount
plt.pie(reasonRatio.values, labels=reasonRatio.index, autopct='%1.1f%%', startangle=90, counterclock=False)
plt.axis('equal')
```

![711-1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/58c3606e-8b04-8d6e-3c15-a85d9e51a8c7.png)

> **<練習問題 2>**
> 上記と同じデータで、higher - 高い教育を受けたいかどうか(binary: yes or no)を軸にして、それぞれの数学の最終成績G3の平均値を棒グラフで表示してください。ここから何か推測できることはありますか？

```py3
import matplotlib.pyplot as plt
student_data_math = pd.read_csv("student-mat.csv",sep=";")

x = student_data_math["higher"].unique()
y = []
for x_ in x:
    y.append(student_data_math["G3"][student_data_math["higher"] == x_].mean())

plt.bar(x, y)
```

![711-2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/926dc87d-085a-b01c-94a0-1b9af0f37fb2.png)

> **<練習問題 3>
> 上記と同じデータで、通学時間（traveltime）を軸にして、それぞれの数学の最終成績G3の平均値を横棒グラフで表示してください。何か推測できることはありますか？

```py3
import matplotlib.pyplot as plt
student_data_math = pd.read_csv("student-mat.csv",sep=";")

x = student_data_math["traveltime"].unique()
y = []
for x_ in x:
    y.append(student_data_math["G3"][student_data_math["traveltime"] == x_].mean())

plt.barh(x,y)
```

![711-3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/152bd5bd-9e04-5075-c763-a11e6a29044a.png)

### 7.1.2 応用：金融データの可視化

## 7.2 分析結果の見せ方を考えよう

### 7.2.1 資料作成のポイントについて

## 7.3 総合問題

> **7.3.1 時系列データ分析**
> ここでは、本章で身に付けたpandasやscipyなどを使って、時系列データついて扱っていきましょう。
> 
> （1）（データの取得と確認）下記のサイトより、dow_jones_index.zipをダウンロードし、中にあるdow_jones_index.dataを使って、データを読み込み、はじめの5行を表示してください。またデータのそれぞれのカラム情報等を見て、nullなどがあるか確認してください。　　
> 
> https://archive.ics.uci.edu/ml/machine-learning-databases/00312/dow_jones_index.zip　　

```py3
# (1)
# import requests, zipfile
# from io import StringIO
# import io
# zip_file_url = "https://archive.ics.uci.edu/ml/machine-learning-databases/00312/dow_jones_index.zip"
# r = requests.get(zip_file_url, stream=True)
# z = zipfile.ZipFile(io.BytesIO(r.content))
# z.extractall()

data = pd.read_csv("dow_jones_index.data")
data[:5]
```

![731-1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/69945fd9-cfdb-6680-c597-99e06e03c015.png)

> （2）（データの加工）カラムのopen、high、low、close等のデータは数字の前に$マークが付いているため、これを取り除いてください。また、日時をdate型で読み込んでいない場合は、date型に変換しましょう。

```py3
# (2)
data.open = data.open.str.strip("$").astype(float)
data.high = data.high.str.strip("$").astype(float)
data.low = data.low.str.strip("$").astype(float)
data.close = data.close.str.strip("$").astype(float)
data.date = pd.to_datetime(data.date)
data[:5]
```

![731-2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/fc576069-92ee-8162-1e1b-bd5d6f6d1cb2.png)

> （3）カラムのcloseについて、各stockごとの要約統計量を算出してください。

```py3
# (3)
data.groupby("stock").close.describe()
```

![731-3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/4be5612b-783b-9cd1-b8fc-fc59d94bfc01.png)

> （4）カラムのcloseについて、各stockの相関を算出する相関行列を出してください。また、seabornのheatmapを使って、相関行列のヒートマップを描いてみましょう。（ヒント：pandasのcorr()を使います。）

```py3
# (4) 
# 突然、設問が不親切に…。
# "stock"毎に"date(説明変数)"に対する"close(目的変数)"の値を見たとき、
# "close"の推移が相関する"stock"を探すという意味？
import seaborn as sns
pv = data.pivot(index="date", columns="stock", values="close")
cr = pv.corr()
sns.heatmap(cr, square=True, vmax=1, vmin=-1, center=0)
```

![731-4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/89495bb5-d3bf-e914-2e02-ae06a479f9bb.png)

> （5）(4)で算出した相関行列の中で一番相関係数が高いstockの組み合わせを抽出してください。さらに、その中でもっとも相関係数が高いペアを抜き出し、それぞれの時系列グラフを書いてください。

```py3
# (5)
# 相関係数が高い組み合わせの中の、さらに相関係数が高いペア…とは？
# 組み合わせ = ペア？
cr = data.pivot(index="date", columns="stock", values="close").corr()
cr[cr==cr[cr<1].max().max()]
# --
plt.plot(pv.index, pv.CSCO, label="CSCO")
plt.plot(pv.index, pv.MSFT, label="MSFT")
plt.xlabel("date")
plt.ylabel("close")
plt.legend()
```
![731-5-1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/cda191b1-d90b-b4eb-cd23-2729b0f8e461.png)

![731-5-2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/1ca6ae75-e890-2b50-ead1-44ea7dd58a38.png)

> （6） pandasのrollingとwindow関数（窓関数）を使って、上記で使った各stockごとに、closeの過去5期（5週間）移動平均時系列データを計算してください。

```py3
# (6)
pv.rolling(5).mean().dropna()
```

![731-6](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/e0c82d5c-d562-f62a-0ecc-4db1fa0841ea.png)

> （7） pandasのshift()を使って、上記で使った各stockごとに、closeの前期（1週前）との比の対数時系列データを計算してください。さらに、この中で、一番ボラティリティ（標準偏差）が一番大きいstockと小さいstockを抜き出し、その対数変化率グラフを書いてください。

```py3
# (7)
pv_lgrt = np.log(pv/pv.shift()).dropna()
print(pv_lgrt.std()[pv_lgrt.std()==pv_lgrt.std().max()])
print(pv_lgrt.std()[pv_lgrt.std()==pv_lgrt.std().min()])
# --
plt.plot(pv_lgrt.index, pv_lgrt.CSCO)
plt.plot(pv_lgrt.index, pv_lgrt.KO)
```

```
> Max stock
> CSCO    0.041205
> dtype: float64
> Min stock
> KO    0.01623
> dtype: float64
```

![731-7](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/1bde0645-3e89-00cf-112f-9d204f10e451.png)

> **7.3.2 マーケティング分析**
> 次は、マーケティング分析でよく扱われる購買データです。一般ユーザーとは異なる法人の購買データですが、分析する軸は基本的に同じです。
> 
> （1）下記のURLよりデータをpandasで読み込んでください（件数50万以上のデータで比較的大きいため、少し時間がかかります。）
> 
> "http://archive.ics.uci.edu/ml/machine-learning-databases/00352/Online%20Retail.xlsx"
> 
> (ヒント)pd.ExcelFileを使って、シートを.parse('Online Retail')で指定してください。
> 
> また、今回の分析対象は、CustomerIDにデータが入っているレコードのみ対象にするため、そのための処理をしてください。さらに、カラムのInvoiceNoには数字の前にCがあるものはキャンセルのため、このデータを取り除いてください。他にもデータとして取り除く必要なものがあれば、適宜処理してください。以下、このデータをベースに分析していきます。

```py3
# (1)
import requests
import pandas as pd

# xlsx_file_url = "http://archive.ics.uci.edu/ml/machine-learning-databases/00352/Online%20Retail.xlsx"
# r = requests.get(xlsx_file_url, stream=True)
# with open("Online%20Retail.xlsx", 'wb') as saveFile:
#     saveFile.write(r.content)

book = pd.ExcelFile("Online%20Retail.xlsx")
data = book.parse("Online Retail")
# --
data_ = data.dropna(subset = ["CustomerID"])
mask = data_['InvoiceNo'].str.startswith('C', na=False)
data_ = data_[~mask]

data_.head()
```

![732-1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/300269ab-7cd9-d2c9-15ea-ec319b412d7d.png)

> （2）このデータのカラムには、購買日時や商品名、数量、回数、購買者のIDなどがあります。ここで、購買者（CustomerID）のユニーク数、バスケット数（InvoiceNoのユニーク数）、商品の種類（StockCodeベースとDescriptionベースのユニーク数）を求めてください。

```py3
# (2)
print("CustomerID unique:", data_["CustomerID"].nunique())
print("InvoiceNo unique:", data_["InvoiceNo"].nunique())
print("StockCode unique:", data_["StockCode"].nunique())
print("Description unique:", data_["Description"].nunique())
```

```
> CustomerID unique: 4339
> InvoiceNo unique: 18536
> StockCode unique: 3665
> Description unique: 3877
```

> （3）このデータのカラムには、Countryがあります。このカラムを軸に、それぞれの国の購買合計金額（単位あたりの金額×数量の合計）を求め、降順にならべて、上位5つの国の結果を表示してください。

```py3
# (3)
data_["price"] = data_["Quantity"] * data_["UnitPrice"]
data_[["price", "Country"]].groupby("Country").sum().sort_values("price").iloc[-1:-6:-1]
```

![732-3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/3eb8ba16-cd3b-6364-de6f-5ff5f3ce3928.png)


> （4）上の上位5つの国について、それぞれの国の商品売り上げ（合計金額）の月別の時系列推移をグラフにしてください。ここで、グラフは分けて表示してください。

```py3
# (4)
top5countries = data_[["price", "Country"]].groupby("Country").sum().sort_values("price").iloc[-1:-6:-1].index.to_list()
data_top5countries = data_[data_["Country"].isin(top5countries)]

data_dict={}
i=0
f, axs = plt.subplots(5,1,figsize=(6,10))
for country, df in data_top5countries.groupby("Country"):
    df.index = df.InvoiceDate
    df_ = df.resample("M").sum()
    axs[i].plot(df_.index, df_.price, label=country)
    axs[i].legend(loc="best")
    data_dict[country] = df
    i+=1
```

![732-4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/3e557ba4-6a09-6f66-632d-17bd1da5ae73.png)

> （5）上の上位5つの国について、それぞれの国における商品の売り上げTOP5の商品を抽出してください。また、それらを国ごとに円グラフにしてください。なお、商品は「Description」ベースで集計してください。

```py3
# (5)
i=0
f, axs = plt.subplots(5,1,figsize=(5,15))
for country in top5countries:
    top5descriptionData = data_dict[country][["price", "Description"]].groupby("Description").sum().sort_values("price").iloc[-1:-6:-1]
    print(country, "\n", top5descriptionData, "\n")
    axs[i].pie(top5descriptionData["price"], labels=top5descriptionData.index.to_list(), autopct='%1.1f%%', startangle=90, counterclock=False)
    axs[i].title.set_text(country)
    i+=1
```

```
> United Kingdom 
>                                          price
> Description                                  
> PAPER CRAFT , LITTLE BIRDIE         168469.60
> REGENCY CAKESTAND 3 TIER            110990.20
> WHITE HANGING HEART T-LIGHT HOLDER   94858.60
> MEDIUM CERAMIC TOP STORAGE JAR       80291.44
> JUMBO BAG RED RETROSPOT              77371.57 
> 
> Netherlands 
>                                         price
> Description                                 
> RABBIT NIGHT LIGHT                   9568.48
> ROUND SNACK BOXES SET OF4 WOODLAND   7991.40
> SPACEBOY LUNCH BOX                   7485.60
> DOLLY GIRL LUNCH BOX                 6828.60
> ROUND SNACK BOXES SET OF 4 FRUITS    4039.20 
> 
> EIRE 
>                                   price
> Description                           
> REGENCY CAKESTAND 3 TIER       7388.55
> CARRIAGE                       4875.00
> 3 TIER CAKE TIN RED AND CREAM  4235.65
> Manual                         3374.34
> JAM MAKING SET WITH JARS       2976.00 
> 
> Germany 
>                                          price
> Description                                  
> POSTAGE                              21001.00
> REGENCY CAKESTAND 3 TIER              9061.95
> ROUND SNACK BOXES SET OF4 WOODLAND    3598.95
> Manual                                2296.25
> ROUND SNACK BOXES SET OF 4 FRUITS     1982.40 
> 
> France 
>                                    price
> Description                            
> POSTAGE                        15454.00
> Manual                          9492.37
> RABBIT NIGHT LIGHT              7234.24
> REGENCY CAKESTAND 3 TIER        2816.85
> RED TOADSTOOL LED NIGHT LIGHT   2130.15 
```

![732-5](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55744/b3dbc5f0-137f-5088-6fee-724e3be2b3e3.png)