Python:データ分析基礎(pandas,matplotlib)

# はじめに

「Python2年生 データ分析のしくみ 体験してわかる! 会話でまなべる!」で学習したので
備忘録として記載しています。

# データ分析とは
客観的な事実をもとにして、よりよい解決方法を見つけること。

統計学とは、大量のデータから傾向を見つけ出して、法則を発見するための技術。

データ分析で必要なのは、データから推測する力と、分析結果を使って説明できる力。

# データ分析の手順(PPDAC)
①Problem(問題の把握)
②Plan(調査の計画)
③Data(データの収集)
④Analysis(データの分析)
⑤Conclusion(結論を考える)⇒①へ戻る

# 表データ
横方向・・・1行、レコード、ロウ(row)
縦方法・・・1列、カラム(columns)

# データフレーム
データフレームとは`pandas`が用意するデータ形式で、Excelのように行と列で管理できる。

<font color="Red">**「データフレーム = pd.DataFrame(data)」**</font>のように指定する。

# データフレームの作成

●行データからデータフレームを作る

```行データからデータフレームを作る.py
data = [[1行目データ], [2行目データ], [3行目データ]]
データフレーム = pd.DataFrame(data)
```

```データフレームに列名とインデックス名を設定する.py
データフレーム.columns = [列名のリスト]
データフレーム.index = [インデックス名のリスト]
```

※始めから列名とインデックス名を設定しておく場合は、下記を記載する。
<font color="Red">**col = [列名のリスト]**</font>
<font color="Red">**idx = [インデックス名のリスト]**</font>

```データフレームに列名とインデックス名を設定する.py
import pandas as pd

# 行データからデータフレームを作る
data = [
    [60, 65, 66], # 国語:60、数学:65、英語:66
    [80, 85, 88],
    [100, 100, 100]
]
col = ["国語", "数学", "英語"] # カラム名の作成
idx = ["A", "B", "C"] # インデックス名の作成
df = pd.DataFrame(data, columns=col, index=idx)
df
```
●列データからデータフレームを作る

```列データからデータフレームを作る.py
data = {"列名":[列データ], "列名":[列データ], "列名":[列データ]}
idx = [インデックス名のリスト]
データフレーム = pd.DataFrame(data, index=idx)
```

```列データからデータフレームを作る.py
import pandas as pd

# 列データからデータフレームを作る
data = {
    "国語" : [60, 80, 100],
    "数学" : [65, 85, 100],
    "英語" : [66, 88, 100]
}
idx = ["A", "B", "C"] # インデックスを作成
df = pd.DataFrame(data, columns=col, index=idx)
df
```

# CSVファイルを読み込む

CSVファイルには、<font color="Red">**「UTF-8形式」**</font>と<font color="Red">**「Shift-JIS形式」**</font>がある。

```CSVファイル(UTF-8形式)を読み込む.py
DataFrame = pd.read_csv("ファイル名.csv")
```

```CSVファイル(Shift-JIS形式)を読み込む.py
DataFrame = pd.read_csv("ファイル名.csv", encoding="Shift_JIS")
```

●0列目をインデックスを読み込む

```CSVファイル(0列目をインデックス)を読み込む.py
DataFrame = pd.read_csv("ファイル名.csv", index_col=0)
```

●ヘッダーがない状態で読み込む

```CSVファイル(ヘッダーがない)を読み込む.py
DataFrame = pd.read_csv("ファイル名.csv", header=None)
```

# 列(columns)、インデックスの確認

●列(columns)の確認

```列(columns)の確認.py
df.columns
```

●インデックスの確認

```インデックスの確認.py
df.index
```

●列(columns)、インデックスをリストに変換する

```リスト変換.py
# 列名をリスト変換
list1 = [i for i in df.columns]
print(list1)

# インデックス名をリスト変換
list2 = [i for i in df.index]
print(list2)
```

●各列のデータ種類の確認

```データ種類の確認.py
df.dtypes
```

●データ個数の確認

```データ個数の確認.py
len(df)
```

# 列データや行データを取り出す

●列データを取り出す

```列データを取り出す.py
df["列名"]
```

```複数の列データを取り出す.py
df[["列名", "列名"]]
```

●行データを取り出す

```行データを取り出す.py
df.iloc[行番号]
```

```複数の行データを取り出す.py
df.iloc[行番号, 行番号]
```

●要素データを取り出す

```要素データを取り出す.py
df.iloc[行番号]["列名"]
```

# 列データや行データを追加する

●空のデータフレームを作る

```空のデータフレームを作る.py
データフレーム = pd.DataFrame()
```

●列データを追加する

```列データを追加する.py
データフレーム["新列名"] = 列データ
```

●プログラム例

```プログラム例.py
import pandas as pd

dfA = pd.read_csv("test.csv", index_col=0)

dfB = pd.DataFrame() # 空のデータフレームを作成
dfB["国語"] = dfA["国語"]  # dfAをdfBへ代入
dfB # dfBを表示
```

●行データを追加する

```列データを追加する.py
データフレーム = データフレーム.append(列データ)
```

●プログラム例

```プログラム例.py
import pandas as pd

dfA = pd.read_csv("test.csv", index_col=0)

dfB = pd.DataFrame()
dfB = dfB.append(dfA.iloc[0])
dfB
```

:::note warn
appendメソッドを実行すると、下記のアラートが出る
The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead.
:::

# 列データや行データを削除する

●列データを削除する

```列データを削除する.py
データフレーム.drop("列名", axis=1)
```

●プログラム例

```プログラム例.py
import pandas as pd

dfA = pd.read_csv("test.csv", index_col=0)
dfA = dfA.drop("国語", axis=1)
dfA
```

●行データを削除する

```行データを削除する.py
データフレーム.drop("行名")
```

●プログラム例

```プログラム例.py
import pandas as pd

dfA = pd.read_csv("test.csv", index_col=0)
dfA = dfA.drop(dfA.index[3])
dfA
```

# 条件でデータを抽出する

●列データから「ある条件に合うデータを抽出する」

```条件に合うかどうか調べる.py
データフレーム["列名"] = 値
データフレーム["列名"] > 値
データフレーム["列名"] < 値
```

```条件でデータを抽出.py
データフレーム = データフレーム[条件]
```

●プログラム例

```プログラム例.py
import pandas as pd

dfA = pd.read_csv("test.csv", index_col=0)
dfA["国語"] > 80 # 国語が80より大きいか、True、Falseで表示
```

●プログラム例（抽出）

```プログラム例（抽出）.py
import pandas as pd

dfA = pd.read_csv("test.csv", index_col=0)
dfB = dfA[dfA["国語"] > 80] # 国語が80より大きいか抽出し、dfBへ代入
dfB
```

●プログラム例（複数の条件で抽出）

```プログラム例（複数の条件で抽出）.py
import pandas as pd

dfA = pd.read_csv("test.csv", index_col=0)
# 国語が80より大きいかつ数学が80より大きい条件で抽出し、dfBへ代入
dfB = dfA[(dfA["国語"] > 80) & (dfA["数学"] > 80)] 
dfB
```

# データの欠損値をチェックする

`pandas`では「データが抜けていること」を<font color="Red">**NaN(ナン)**</font>と表示する。
これを<font color="Red">**欠損値**</font>という。

●プログラム例（NaNと表示）

```プログラム例（NaNと表示）.py
import pandas as pd

# 列データからデータフレームを作成
data = {
    "国語" : [90, 50, None, 40],
    "数学" : [80, None, None, 50]
}
# インデックス名を作成
idx = ["A", "B", "C", "D"]
dfA = pd.DataFrame(data, index=idx)
dfA
```

●欠損値の個数

```欠損値の個数.py
データフレーム.isnull().sum()
```

●プログラム例

```プログラム例.py
dfA.isnull().sum() # 列データから個数を数える
```

●欠損値がある行を削除
**※値がある行も合わせて削除される場合がある**

```欠損値がある行を削除.py
データフレーム = データフレーム.dropna()
```

●プログラム例

```プログラム例.py
dfB = dfA.dropna() # 欠損値がある行を削除し、dfBへ代入
dfB # dfBを表示
```

●欠損値がある行だけを削除

```欠損値がある行を削除.py
データフレーム = データフレーム.dropna(subset=["列名"])
```

●プログラム例

```プログラム例.py
dfB = dfA.dropna(subset=["国語"]) # 国語データで欠損値がある行のみを削除し、dfBへ代入
dfB # dfBを表示
```

●欠損値を平均値で埋める

```欠損値を平均値で埋める.py
データフレーム = データフレーム.fillna(データフレーム.mean())
```

●プログラム例

```プログラム例.py
dfB = dfA.fillna(dfA.mean())
dfB
```

●欠損値を1つ前の値で埋める

```欠損値を平均値で埋める.py
データフレーム = データフレーム.fillna(method='ffill')
```

●プログラム例

```プログラム例.py
dfB = dfA.fillna(method='ffill')
dfB
```

# 重複したデータを削除する

●プログラム例

```プログラム例.py
import pandas as pd

data = [
    [10, 30, 40],
    [20, 30, 40],
    [20, 30, 40],
    [30, 30, 50],
    [20, 30, 40]
]
dfA = pd.DataFrame(data)
dfA
```

●重複データの個数

```重複データの個数.py
データフレーム.duplicated().value_counts()
```

●プログラム例

```プログラム例.py
dfA.duplicated().value_counts()
# False    3
# True     2 ※重複するデータが2つある
# dtype: int64
```

●重複データの2つ目以降を削除する

```重複データの2つ目以降を削除する.py
データフレーム.drop_duplicates()
```

●プログラム例

```プログラム例.py
dfB = dfA.drop_duplicates()
dfB
```

# 文字列型のデータを数値に変換する

●プログラム例

```プログラム例.py
import pandas as pd

# 列データからデータフレームを作成
data = {
    "A" : ["100", "300"],
    "B" : ["500", "1,500"]
}
dfA = pd.DataFrame(data)
dfA
```

```プログラム例.py
dfA.dtypes # データの種類を調べる
# A    object
# B    object
# dtype: object
```

●文字列の列データを整数に変換する

```文字列の列データを整数に変換する.py
データフレーム["列名"] = データフレーム["列名"].astype(int)
```

●プログラム例

```プログラム例.py
dfA["A"] = dfA["A"].astype(int) # A列を整数へ変換
dfA.dtypes # データの種類を調べる
# A     int32
# B    object
# dtype: object
```

●カンマ付き文字列の列データのカンマを削除する

```カンマ付き文字列の列データのカンマを削除する.py
データフレーム["列名"] = データフレーム["列名"].str.replace(",", "")
```

●プログラム例

```プログラム例.py
dfA["B"] = dfA["B"].str.replace(",", "").astype(int) # カンマ付きのB列を整数に変換
dfA.dtypes # データの種類を調べる
# A    int32
# B    int32
# dtype: object
```

# 平均値を求める

代表値の一つに平均値があります。
`平均値`とは、凹凸のあるデータの、凹凸を平らに均した値のこと。

<font color="Red">**「平均値 = データの合計 / データの個数」**</font>

●プログラム例

```プログラム例.py
import pandas as pd

# 列データからデータフレームを作成
data = {
    "A" : [82, 89, 93, 85, 76],
    "B" : [100, 62, 82, 70, 86]
}
df = pd.DataFrame(data)
df
```

●列データの平均値を求める

```列データの平均値を求める.py
df["列名"].mean()
```

●プログラム例

```プログラム例.py
# A、Bの平均値を求める
print("A =", df["A"].mean())
print("B =", df["B"].mean())
# A = 85.0
# B = 80.0
```

# 代表値は、「データの比較」に使う

このように求めた代表値は、<font color="Red">**「データの比較」**</font>に使います。
「代表値と、代表値を比較」すると「グループとグループの差」がわかります。

●各列データの平均値を求める

```各列データの平均値を求める.py
df.mean()
```

●プログラム例

```プログラム例.py
print(df.mean())
# A    85.0
# B    80.0
# dtype: float64
```

●プログラム例（代表値と、1つのデータを比較）

```プログラム例（代表値と、1つのデータを比較）.py
# 「A」の「0番目」と「A」の平均値を比較する
print(df.iloc[0]["A"])
print("A =", df["A"].mean())
# 82
# A = 85.0
# この結果から、「0番目」はAの平均値以下であることがわかる
```

:::note info
代表値は「データの比較」に使う
・ 代表値と、別の代表値を比較：グループとグループの差がわかる
・ 昔の代表値と、今の代表値を比較：グループの変化がわかる
・ 代表値と、1つのデータを比較：1つのデータが全体のどのあたりなのかがわかる
:::

# 平均値を代表としていいか調べる

平均値は、**外れ値の影響を受けやすい**という性質がある。
「平均値より外れ値の影響を受けにくい」代表値が必要となる場合もある。
それが、**中央値**と**最頻値**です。

<font color="Red">**・ 中央値：データを順番に並べたとき、ちょうど真ん中に来る値**</font>

<font color="Red">**・ 最頻値：データの中で一番多く現れる（最頻）値**</font>

●プログラム例

```プログラム例.py
import pandas as pd

# 列データからデータフレームを作成
data = {
    "予想価格" : [240, 250, 150, 240, 300, 5000]
}
df = pd.DataFrame(data)
df
```

●プログラム例（平均値を求める）

```プログラム例（平均値を求める）.py
print(df.mean())
# 予想価格    1030.0
# dtype: float64
```

●各列データの中央値を求める

```各列データの中央値を求める.py
df.median()
```

●プログラム例（中央値を求める）

```プログラム例（中央値を求める）.py
print(df.median())
# 予想価格    245.0
# dtype: float64
```

●各列データの最頻値を求める

```各列データの最頻値を求める.py
df.mode()
```

●プログラム例（最頻値を求める）

```プログラム例（最頻値を求める）.py
print(df.mode())
#    予想価格
# 0   240
```

:::note info
平均値、中央値、最頻値の違い
**平均値**
・ すべてのデータを考慮した値
・ 外れ値の影響を受けやすい
・ 標準偏差との相性がいいのでよく使われる

**中央値**
・ データを順番に並べたとき、ちょうど真ん中に来る値
・ 外れ値の影響をあまり受けにくい

**最頻値**
・ データの中で一番多く現れる（最頻）値
・ 外れ値の影響をあまり受けにくい
・ サンプル数が少ないと使えない
:::

# データのばらつきを調べる

データのばらつき方を表にして調べるときは、<font color="Red">**度数分布表**</font>を使います。

<font color="Red">**・ 階級：たくさんのデータをいくつかの範囲で区切った際の、区切った範囲のこと。**</font>

<font color="Red">**・ 度数：区切った範囲内にデータがいくつ入っているか、個数のこと。**</font>

<font color="Red">**・ 度数分布表：度数を表にして、データの分布をわかるようにしたもの。度数分布表を見ると、「データはどこからどこまであって」「全体が均等にばらついているのか、どこかだけに集中しているのか」などがわかる。**</font>

●プログラム例

```プログラム例.py
import pandas as pd

# 列データからデータフレームを作成
data = {
    "A" : [1, 10, 1, 10, 1, 10, 1, 10],
    "B" : [5, 5, 5, 5, 6, 6, 6, 6],
    "C" : [1, 2, 3, 4, 7, 8, 9, 10]
}
df = pd.DataFrame(data)
df
```

●プログラム例（平均値を求める）

```プログラム例（平均値を求める）.py
print(df.mean())
# A    5.5
# B    5.5
# C    5.5
# dtype: float64
```

●プログラム例（中央値を求める）

```プログラム例（中央値を求める）.py
print(df.median())
# A    5.5
# B    5.5
# C    5.5
# dtype: float64
```

●プログラム例（最頻値を求める）

```プログラム例（最頻値を求める）.py
print(df.mode())
#       A    B   C
# 0   1.0  5.0   1
# 1  10.0  6.0   2
# 2   NaN  NaN   3
# 3   NaN  NaN   4
# 4   NaN  NaN   7
# 5   NaN  NaN   8
# 6   NaN  NaN   9
# 7   NaN  NaN  10
```

●列データの度数分布表を表示する

```列データの度数分布表を表示する.py
# pd.cut()でいくつかの範囲に区切る
cut = pd.cut(df["列名"], bins=区切る範囲, right=False)
# cut.value_counts()でそれぞれの範囲にいくつデータが入っているかカウントする
cut.value_counts(sort=False)
```

●プログラム例（Aの度数分布表を表示）

```プログラム例（Aの度数分布表を表示）.py
# 「1~3, 3~5, 5~7, 7~9, 9~11」の範囲を作成
bins = [1, 3, 5, 7, 9, 11]
# 「right=False」で「範囲の左側は含まれるが、右側は含まれない」という設定にする。
# 「1以上3未満、3以上5未満、5以上7未満、7以上9未満、9以上11未満」の範囲を作成している。
cut = pd.cut(df["A"], bins=bins, right=False)
cut.value_counts(sort=False)
# [1, 3)     4　# 1以上3未満
# [3, 5)     0  # 3以上5未満
# [5, 7)     0  # 5以上7未満
# [7, 9)     0  # 7以上9未満
# [9, 11)    4  # 9以上11未満
# Name: A, dtype: int64
```

# データのばらつきがわかる

**matplotlibの使い方について**

グラフを表示するときは、**matplotlib**ライブラリを使います。
<font color="Red">**「import matplotlib.pyplot as plt」**</font>と命令すると、matplotlibを「plt」という省略形で扱えます。

●グラフは主に3段階で命令します。
１．<font color="Red">**「どんなデータで、何のグラフを表示するか」**</font>を決める。
２．必要なときは、<font color="Red">**「タイトルや線などの追加情報」**</font>を指定する。
３．最後に<font color="Red">**「plt.show()」**</font>と命令すると、指定したグラフが表示される。

matplotlibのグラフは、Pythonでは別ウィンドウで表示されるのですが、
Jupter Notebookでは、<font color="Red">**「%matplotlib inline」**</font>と指定して、Jupter Notebook中に
グラフを表示させます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import matplotlib.pyplot as plt

plt.plot([0, 100, 200], [100, 0, 200])
plt.show()
# グラフが表示される
```

**seabornの使い方について**

さらにグラフをきれいに描写するライブラリです。
seabornライブラリは、matplotlibを拡張するライブラリなので、使うときは両方をimportします。
<font color="Red">**「import seaborn as sns」**</font>と命令すると、「sns」という省略形で扱えます。

seabornは、最初に<font color="Red">**「sns.set()」**</font>と命令しておくだけで
これ以降に表示されるグラフがきれいに表示されます。

さらに、フォント指定も簡単で、<font color="Red">**「sns,set(font=["フォント名"])」**</font>と命令するだけで設定できます。<font color="Red">**「sns,set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])」**</font>と命令するだけで、日本語が使えるようになります。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

plt.plot([0, 100, 200], [100, 0, 200])
plt.title("タイトル")
plt.show()
# グラフが表示される
```

:::note info
seabornのスタイルの変更
<font color="Red">**「sns.set(style="スタイル", font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])」**</font>と命令します。
スタイルには、"dark"(暗色),"darkgrid"(暗い色で線あり),
"white"(白),"whitegrid"(白で線あり),"ticks"(目盛りあり)などを指定できます。
:::

# データのばらつきがわかる：ヒストグラム

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

# 列データからデータフレームを作成
data = {
    "A": [1, 10, 1, 10, 1, 10, 1, 10],
    "B": [5, 5, 5, 5, 6, 6, 6, 6],
    "C": [1, 2, 3, 4, 7, 8, 9, 10]
}

df = pd.DataFrame(data)
df
# 表が作成される
```

◆データ分析の命令：列データのヒストグラムを表示する
・　必要なライブラリ：pandas、matplotlib、(seaborn)
・　命令
　　<font color="Red">**df["列名"].plot.hist(bins=区切る範囲)**</font>
　　plt.show()
・　出力：列データのばらつきがわかるヒストグラム

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
# 区切る範囲を指定
bins = [1,3,5,7,9,11]

df.plot.hist(bins=bins)
# タイトルを作成
plt.title("ケーキの感想はどのように違うか？")
plt.show()
# グラフが表示される
```

◎ヒストグラムは度数分布表をグラフ化したもの。
グラフを作る前に<font color="Red">**「何のためのグラフなのか」**</font>を意識して、グラフが表示されたら
<font color="Red">**「ここから何が読み取れるか」**</font>を考える。この積み重ねで、ただの客観的なデータが、
人間にとって意味のあるデータになってくる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
# Aのグラフを作成
df["A"].plot.hist(bins=bins)
plt.title("Aのケーキの感想")
plt.show()

# Bのグラフを作成
df["B"].plot.hist(bins=bins)
plt.title("Bのケーキの感想")
plt.show()

# Cのグラフを作成
df["C"].plot.hist(bins=bins)
plt.title("Cのケーキの感想")
plt.show()
# グラフが表示される
```

# 基本的なグラフを作る

**大小を比較できる：棒グラフ**

棒グラフは<font color="Red">**「数量の大小を比較できるグラフ」**</font>です。
「棒の高さ」がそれぞれの量を表しています。
<font color="Red">**「それぞれ独立した値を比較するとき」**</font>に使います。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

# 列データからデータフレームを作成
data = {
    "名前": ["A", "B", "C"],
    "国語": [60, 80, 100],
    "数学": [65, 85, 100],
    "英語": [66, 88, 100]
}

df = pd.DataFrame(data)
df
# 表が作成される
```

このデータを棒グラフで表示してみます。
棒グラフにするには、<font color="Red">**「データフレーム.plt.bar()」**</font>と命令します。

◆データ分析の命令（列データの棒グラフを表示する）
・　必要なライブラリ：pandas、matplotlib、(seaborn)
・　命令
　　<font color="Red">**df.plot.bar()**</font>
　　plt.show()
・　出力：列データの棒グラフ

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
# 棒グラフで表示
df.plot.bar()
# タイトルを作成
plt.title("3名の成績")
plt.show()
# 棒ブラフが作成される
```

数値データの「国語」「数学」「英語」の列がグラフ化されました。ただし、
文字列データの「名前」の列はグラフ化できないので、自動的に表示対象から外されています。
グラフの横軸をみると、インデックス番号が表示されています。
「名前」の列を、インデックスに使う。<font color="Red">**「df.set_index("列名", inplace=True)」**</font>で指定します。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
# インデックスの置き換え
df.set_index("名前", inplace=True)
df
# 表が作成される
```

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df.plot.bar()
plt.title("3名の成績")
plt.show()
# 棒ブラフの横軸が変更される
```

<font color="Red">**「df["列名"]」**</font>と指定すると、特定の列データのみだけでグラフを表示できます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df["国語"].plot.bar()
plt.title("国語の成績")
plt.show()
# 国語のみの棒ブラフが表示される
```

**変化がわかる：折れ線グラフ**

折れ線グラフは<font color="Red">**「時間的な値の変化がわかるグラフ」**</font>です。
時間で変化するデータに使うので、項目名が「年、月、日、時、分」になっているなど
<font color="Red">**「時系列に並んでいるデータ」**</font>に使います。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

# 列データからデータフレームを作成
data = {
    "月": [1,2,3,4,5,6,7,8,9,10,11,12],
    "東京": [5.6, 7.2, 10.6, 13.6, 20.0, 21.8, 24.1, 28.4, 25.1, 19.4, 13.1, 8.5],
    "那覇": [18.1, 20.0, 19.9, 22.3, 24.2, 26.5, 28.9, 29.2, 28.0, 26.0, 23.1, 20.0],
    "札幌": [-3.0, -2.6, 2.5, 8.0, 15.7, 17.4, 21.7, 22.5, 19.3, 13.3, 3.9, -0.8]
}

df = pd.DataFrame(data)
df
# 表が表示される
```

インデックスと月の数字がずれているので、「月」の列を、インデックスとして指定します。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df.set_index("月", inplace=True)
df.head()
# 表が表示される
```

このデータを折れ線グラフで表示してみます。
折れ線グラフにするには、<font color="Red">**「データフレーム.plot()」**</font>と命令します。

◆データ分析の命令：列データの折れ線グラフを表示する
・　必要なライブラリ：pandas、matplotlib、(seaborn)
・　命令
　　<font color="Red">**df["列名"].plot()**</font>
　　plt.show()
・　出力：列データの折れ線グラフ

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df.plot()
plt.title("日本の気温の変化")
plt.show()
# 折れ線グラフが表示される
```

<font color="Red">**「df["列名"]」**</font>と指定すると、特定の列データのみだけでグラフを表示できます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df["東京"].plot()
plt.title("東京の気温の変化")
plt.show()
# 東京の折れ線グラフが表示される
```

**要素の割合を比較できる：円グラフ**

円グラフは<font color="Red">**「全体に対する要素の割合がわかるグラフ」**</font>です。
<font color="Red">**「すべての要素の値を合計して100%になるデータ」**</font>に使います。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

data = {
    "クッキー": [35,47,18],
    "ケーキ": [62,26,12]
}

idx = ["好き", "普通", "嫌い"]
df = pd.DataFrame(data, idx)
df
# 表が作成される
```

このデータを円グラフで表示してみます。
円グラフにするには、<font color="Red">**「データフレーム.plot.pie()」**</font>と命令します。

◆データ分析の命令：列データの円グラフを表示する
・　必要なライブラリ：pandas、matplotlib、(seaborn)
・　命令
　　<font color="Red">**df["列名"].plot.pie()**</font>
　　plt.show()
・　出力：列データの円グラフ

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df["クッキー"].plot.pie()
plt.title("お菓子の好き嫌いは、どのような割合か？")
plt.show()
# 円グラフの表示
```

matplotlibの円グラフは、「右側から始まって反時計回り」に描かれます。
一般的な円グラフのように「真上から始まって時計回り」に描かれるように変更します。
オプションで<font color="Red">**「startangle=90,counterclock=False」**</font>と指定します。

文字を円グラフの内側に表示させるには、<font color="Red">**「labeldistance=0.5」**</font>を追加します。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df["クッキー"].plot.pie(startangle=90, counterclock=False, labeldistance=0.5)
plt.title("お菓子の好き嫌いは、どのような割合か？")
plt.show()
# 円グラフの表示
```

:::note info
棒グラフ・折れ線グラフ・円グラフの見分け方
データ->合計すると100%か?->円グラフ
　　　->時系列に進んだデータか?->折れ線グラフ
　　　->独立した値の比較か?->棒グラフ
:::

# ばらつきのわかるグラフ

**データのばらつきを比較できる：箱ひげ図**

箱ひげ図は<font color="Red">**「データのばらつきを比較できるグラフ」**</font>です。
<font color="Red">**複数のデータのばらつきを比較する**</font>ときに使用します。

![スクリーンショット 2023-07-19 132432.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3468559/01702fc6-3e7b-fde2-3f6f-3cfa965a8eff.png)

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

data = {
    "A": [163.6, 172.6, 163.7, 167.1, 169.9, 173.9, 170.1, 166.2, 176.7, 165.4],
    "B": [166.9, 172.7, 166.4, 173.4, 169.6, 171.8, 166.9, 168.2, 166.7, 169.8]
}

df = pd.DataFrame(data)
df
# 表が表示される
```

このデータを箱ひげ図で表示します。
箱ひげ図は、matplotlibにも<font color="Red">**「df.boxplot()」**</font>という命令がありますが、
seabornの<font color="Red">**「sns.boxplot(data=データフレーム, width=幅)」**</font>という命令のほうが、色付きで
きれいに表示されます。

◆データ分析の命令：列データの箱ひげ図を表示する
・　必要なライブラリ：pandas、matplotlib、seaborn
・　命令
　　<font color="Red">**sns.boxplot(data=df, width=幅)**</font>
　　plt.show()
・　出力：各列データの箱ひげ図

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.boxplot(data=df, width=0.2)
plt.title("身長のばらつきに違いはあるか？")
plt.show()
# 箱ひげ図の表示
```

※出力結果から、「Aのほうがばらつきが大きく、Bのほうがまとまっている」、
「Aのほうが身長の高い生徒がいるが、中央値はBのほうが高い」とわかる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
print(df.median()) # 中央値を出力
# A    168.5
# B    168.9
# dtype: float64
```

**2種類のデータの関係性がわかる：散布図**

散布図は<font color="Red">**「2種類のデータの関係性がわかるグラフ」**</font>です。
<font color="Red">**「あるデータと別のデータに関係性があるかを目で見て確認するとき」**</font>に使います。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

data = {
    "身長": [163.6, 172.6, 163.7, 169.9, 173.9, 166.2, 176.7, 165.4],
    "体重": [50.5, 63.3, 48.5, 59.8, 69.8, 53.7, 70.3, 51.2]
}

df = pd.DataFrame(data)
df
# 表が作成される
```
このデータを散布図で表示します。
散布図を表示させるときは、<font color="Red">**「データフレーム.plot.scatter(x="横軸の列名", y="縦軸の列名", c="色")」**</font>と命令します。

◆データ分析の命令：散布図を表示する
・　必要なライブラリ：pandas、matplotlib
・　命令
　　<font color="Red">**df.plot.scatter(x="横軸の列名", y="縦軸の列名", c="色")**</font>
　　plt.show()
・　出力：指定した列データの散布図

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df.plot.scatter(x="身長", y="体重", c="b")
plt.title("身長と体重に関係はあるか?")
plt.show()
# 散布図の表示
```

※出力結果から、点が右上がりのため、「身長が高いと、体重が重くなるので、関係がありそう」
とわかります。

# グラフをわかりやすく調整する

**グラフのある点を目立たせる**

<font color="Red">**マーカーを表示する**</font>には、グラフを表示させた後に、<font color="Red">**「plt.plot(x座標, y座標, c="色", marker="マーカー", markersize=サイズ)」**</font>と命令して、マーカーを追加表示させることができます。

マーカーは、"o"(丸)、"X"(バツ)、"V"、"^"、"<"、">"などがあります。

◆データ分析の命令：グラフに目立つ1つの点を追加する
・　必要なライブラリ：pandas、matplotlib
・　命令
　　# グラフを表示するplotの命令
　　<font color="Red">**plt.plot(X座標, Y座標, c="色", marker="X", markersize=サイズ)**</font>
　　plt.show()
・　出力：グラフに目立つ1つの点を追加する

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.set_context("talk")
df.plot.scatter(x="身長", y="体重", c="b", figsize=(12, 8))

# 「私」のデータが「3行目」にあるとする。
x = df.iloc[3]["身長"]
y = df.iloc[3]["体重"]

plt.plot(x, y, c="r", marker="X", markersize=15)

plt.title("私はどこにいるか")
plt.show()
```

**グラフに線を引く**

グラフを表示させたあとに、線を引く命令を追加します。
垂直線は<font color="Red">**「plt.axvline(x=X座標, c="色", linestyle="線の種類")」**</font>と、
水平線は<font color="Red">**「plt.axhline(y=Y座標, c="色", linestyle="線の種類")」**</font>と命令します。

◆データ分析の命令：グラフに垂直線、水平線を追加する
・　必要なライブラリ：pandas、matplotlib
・　命令
　　# グラフを表示するplotの命令
　　<font color="Red">**plt.axvline(x=X座標, c="色", linestyle="--")**</font>
　　<font color="Red">**plt.axhline(y=Y座標, c="色", linestyle="--")**</font>
　　plt.show()
・　出力：グラフに垂直線（axvline）、水平線（axhline）を追加する

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.set_context("talk")
df.plot.scatter(x="身長", y="体重", c="b", figsize=(12, 8))

x = df.iloc[3]["身長"]
y = df.iloc[3]["体重"]
plt.plot(x, y, c="r", marker="^", markersize=15)

# 縦、横に線を引く
plt.axvline(x=x, c="r", linestyle="--")
plt.axhline(y=y, c="r", linestyle="--")

plt.show()
```

# データのばらつきを数値で表す

<font color="Red">**「データのばらつき」**</font>は、「表」（度数分布表）や「グラフ」（ヒストグラム）で
見ることができましたが、<font color="Red">**「1つの数値」**</font>で知ることもできます。
それが<font color="Red">**「標準偏差」**</font>です。

「平均値が同じで、ばらつきが違う2種類のデータ」があります。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
import pandas as pd

data = {
    "ID": [0,1,2,3,4,5,6,7,8,9],
    "A": [59,24,62,48,58,19,32,88,47,63],
    "B": [49,50,49,54,45,52,56,48,45,52]
}

df = pd.DataFrame(data)
# 平均値を出力
print(df["A"].mean())
print(df["B"].mean())
# 50.0
# 50.0
```

<font color="Red">**「散布図」**</font>を使って、データが散らばっている様子を点で表示します。
横軸に番号のID、縦軸に列データ名を指定して表示します。
比較したいので、<font color="Red">**「ylim=(0, 100)」**</font>と設定して、どちらも縦軸をそろえます。
さらに、平均値(50)に水平線を引いておきます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

df.plot.scatter(x="ID", y="A", color="b", ylim=(0, 100))
plt.axhline(y=50, c="Magenta")
plt.title("Aのばらつき：大きい", fontsize=24)
plt.show()

df.plot.scatter(x="ID", y="B", color="b", ylim=(0, 100))
plt.axhline(y=50, c="Magenta")
plt.title("Bのばらつき：小さい", fontsize=24)
plt.show()
```

出力結果から<font color="Red">**「上下にばらついている様子」**</font>がわかります。
これを<font color="Red">**「1つの数値」**</font>で表現するにはどうすればいいか。

上下のばらつきは、「各データと平均値の差」ですから、これらを合計して平均してみたいと
思います。つまり、<font color="Red">**「このデータ全体は、平均値からどれだけ上下にばらついているか」**</font>を「1つの数値」で表そうというわけです。

ただし、そのまま「差を合計」すると、0になってしまいます。
もともと<font color="Red">**「平均値は上下のばらつきが0になるように平らに均した値」**</font>だからです。

プラス部分とマイナス部分を打ち消しあって0になるのが問題なので、
マイナス部分をプラスにしようというアイデアが出できました。
「各データと平均値の差を2乗」すれば、マイナスがプラスになります。
これを<font color="Red">**「分散」**</font>といいます。
<font color="Red">**「データが分かれて散らばっている様子を表している」**</font>ので「分散」です。

計算では、<font color="Red">**「分散 = 平均値からの差の2乗の合計 ÷ データの個数」**</font>で求めます。
pandasで分散を求めるときは、<font color="Red">**「データフレーム.var()」**</font>と命令します。

◆データ分析の命令：各列データの分散を求める
・　必要なライブラリ：pandas
・　命令
　　<font color="Red">**df.var()**</font>
・　出力：各列データの分散

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
print(df.var())
# ID      9.166667
# A     430.666667
# B      12.888889
# dtype: float64
```

「2乗したのだから、平方根でもとの単位に戻す」⇒<font color="Red">**「標準偏差」**</font>
計算では、<font color="Red">**「分散の平方根」**</font>で求めます。
pandasでは、<font color="Red">**「データフレーム.std()」**</font>と命令します。

◆データ分析の命令：各列データの標準偏差を求める
・　必要なライブラリ：pandas
・　命令
　　<font color="Red">**df.std()**</font>
・　出力：各列データの標準偏差

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
print(df.var())
# ID     3.02765
# A     20.75251
# B      3.59011
# dtype: float64
```

出力結果から<font color="Red">**「0~100の間のデータで、Aは20.8もあるのだから、ばらつきがそこそこあり、Bは3.6なのでばらつきが小さい」**</font>とわかります。このように、標準偏差は
<font color="Red">**「もとの単位に合わせた1つの数値でデータのばらつきを表す」**</font>ことができます。

**ある範囲にどのくらいデータがあるかがわかる**

標準偏差は、<font color="Red">**「どのくらいばらついているか」**</font>がわかるだけでなく、分布がある形をしている場合には、<font color="Red">**「ある範囲にどのくらいデータがあるか」**</font>までわかります。

先ほどのAのデータの「平均値」と「標準偏差」を求めて、Aの範囲を求めてみます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
meanA = df["A"].mean()
stdA = df["A"].std()
print(meanA - stdA, "～", meanA + stdA)
# 29.247490111635493 ～ 70.7525098883645
```

<font color="Red">**「Aのデータは約68%が、29.2～70.8の範囲にある」**</font>と結果が出ました。

Bのデータの「平均値」と「標準偏差」も求めて、Bの範囲を求めてみます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
meanB = df["B"].mean()
stdB = df["B"].std()
print(meanB - stdB, "～", meanB + stdB)
# 46.409890128577 ～ 53.590109871423
```

<font color="Red">**「Bのデータは約68%が、46.4～53.6の範囲にある」**</font>と結果が出ました。

「ヒストグラム」の中では標準偏差はどのようになるのか。
まず、このデータをヒストグラムで表示してみます。
比較をしたいので、「ylim=(0, 6)」と設定して、どちらも縦軸をそろえます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
bins = [10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100]

df["A"].plot.hist(bins=bins, color="c", ylim=(0, 6))
plt.title("Aのばらつき：大きい")
plt.show()

df["B"].plot.hist(bins=bins, color="c", ylim=(0, 6))
plt.title("Bのばらつき：小さい")
plt.show()
# ヒストグラムが表示される
```

ヒストグラムは、<font color="Red">**「横軸が階級で、縦軸が度数」**</font>です。
横軸は、そのデータが取り得る値なので、「平均値」も横軸のどこかにあります。
また、「約68%の範囲を表す値」も横軸のどこかにあるはずです。

<font color="Red">**「平均値」「平均値ー標準偏差」「平均値＋標準偏差」**</font>の位置に垂直線を引いてみます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
bins = [10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100]

df["A"].plot.hist(bins=bins, color="c", ylim=(0, 6))
plt.axvline(x=meanA, color="magenta")
plt.axvline(x=meanA - stdA, color="blue", linestyle="--")
plt.axvline(x=meanA + stdA, color="red", linestyle="--")
plt.title("Aのばらつき：大きい")
plt.show()

df["B"].plot.hist(bins=bins, color="c", ylim=(0, 6))
plt.axvline(x=meanB, color="magenta")
plt.axvline(x=meanB - stdB, color="blue", linestyle="--")
plt.axvline(x=meanB + stdB, color="red", linestyle="--")
plt.title("Bのばらつき：小さい")
plt.show()
# ヒストグラムが表示される
```

ヒストグラムで見ても<font color="Red">**「標準偏差の線ではさまれた範囲が、データの約68%の範囲を示している」**</font>ことがわかります。

# 自然なばらつき

<font color="Red">**「正規分布」**</font>とは、<font color="Red">**「自然界でごく普通の、よくある分布」**</font>という意味。

●なぜ正規分布は、自然界でごく普通の分布か？

「平均値付近のことはよく起こり、平均値から遠いとめったに起こらなくなる」
「自然界のものは正規分布になることが多い」ということと「カーブ（ガウス分布）は、計算で求めることができる」
「自然界のばらつきとは、誤差の積み重ねでできているのではないか」＝「中心極限定理」

●ゴルトンボードをシュミレート

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import random
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

def galton(steps, count):
    ans = []
    for i in range(count):
        val = 50
        for j in range(steps):
            if random.randint(0, 1) == 0:
                val = val - 1
            else:
                val = val + 1
        ans.append(val)
    
    df = pd.DataFrame(ans)
    df[0].plot.hist()
    plt.title(str(steps)+"段："+str(count)+"個")
    plt.ylabel("")
    plt.show()

galton(1, 10000)
# 結果表示
```
●正規分布は計算で求めることができる

<font color="Red">**「平均値」**</font>がわかれば、<font color="Red">**「正規分布の左右の位置」**</font>がわかります。

<font color="Red">**「標準偏差」**</font>がわかれば、<font color="Red">**「正規分布のカーブとがり具合」**</font>がわかります。
標準偏差が小さければ急なカーブに、大きければゆるやかなカーブになります。

つまり、<font color="Red">**「平均値と標準偏差がわかれば、正規分布の形がわかる」**</font>ということです。

<font color="Red">**計算でカーブの形がわかります。**</font>
<font color="Red">**そのカーブで囲まれた「ある範囲の面積」を計算で求めることもできます。**</font>

ヒストグラムは、<font color="Red">**面積がデータ量**</font>を表していますから、
この<font color="Red">**「面積の割合」はつまり「データの割合」**</font>です。
「真ん中（平均値）から、前後に標準偏差までの範囲に、全体のxx%のデータがある」など。

# この値は普通なこと？珍しいこと？

<font color="Red">**scipy.statsライブラリ**</font>に入っている正規分布用の関数を使うと、難しい計算が必要な数値も、
簡単に求めることができます。

例えば、「norm.cdf（<font color="Red">**正規分布の累積分布関数**</font>）」を使うと、
<font color="Red">**「ある値が、全体の下から何%のことなのか」**</font>を求めることができます。
用意するのは「調べたい値」「平均値」「標準偏差」の3つだけです。

<font color="Red">**「norm.cdf(x=調べたい値, loc=平均値, scale=標準偏差)」**</font>と命令すると、ある値が全体の下から何%のことなのかを0～1の値で返してくれます。

◆データ分析の命令：ある値は、下から何%に該当するか？
・　必要なライブラリ：scipy.stats
・　命令
　　<font color="Red">**cdf = norm.cdf(x=調べたい値, loc=平均値, scale=標準偏差)**</font>
・　出力：ある値は、下から何%に該当するか？

例：「平均値が166.8cm、標準偏差が5.8cm」の正規分布のデータで
「160.0cmは下から何%の位置にあるか」

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
from scipy.stats import norm

mean = 166.8
std = 5.8
value = 160.0

cdf = norm.cdf(x=value, loc=mean, scale=std)
print(value, "は、下から", cdf*100, "%")
# 160.0 は、下から 12.051548220947089 %
```

逆に、<font color="Red">**「ある値は、上から何%なのか」**</font>を調べるには、norm.cdfの値を
1（100%）から引けばわかります。

例：「平均値が166.8cm、標準偏差が5.8cm」の正規分布のデータで
「170.0cmは上から何%の位置にあるか」を<font color="Red">**「1-cdf」**</font>で求めてみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
from scipy.stats import norm

mean = 166.8
std = 5.8
value = 178.0

cdf = norm.cdf(x=value, loc=mean, scale=std)
print(value, "は、上から", (1-cdf)*100, "%")
# 178.0 は、上から 2.6739394108996173 %
```

「norm.ppf（<font color="Red">**正規分布のパーセント点関数**</font>）」を使うと、
<font color="Red">**「全体の下から〇〇%にあたる値は何か？」**</font>を求めることができます。
用意するのは「下から何%を調べたいか（0～1）」「平均値」「標準偏差」の3つだけです。

<font color="Red">**「norm.ppf(q=パーセント値, loc=平均値, scale=標準偏差)」**</font>と命令すると、
全体の下から〇〇%にあたる値は何かを返してくれます。

◆データ分析の命令：下から〇〇%にあたる値は何か？
・　必要なライブラリ：scipy.stats
・　命令
　　<font color="Red">**ppf = norm.ppf(q=パーセント値, loc=平均値, scale=標準偏差)**</font>
・　出力：下から〇〇%にあたる値は何か？

例：「平均値が166.8cm、標準偏差が5.8cm」の正規分布のデータで
「下から20%は、何cmなのか」

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
from scipy.stats import norm

mean = 166.8
std = 5.8
per = 0.20

ppf = norm.ppf(q=per, loc=mean, scale=std)
print("下から", per * 100, "%の値は、", ppf, "です。")
# 下から 20.0 %の値は、 161.9185968452771 です。
```

逆に、<font color="Red">**「上から〇〇%に該当する値は何なのか」**</font>を調べるには、norm.ppfに渡す確率を
1（100%）から引けばわかります。

例：「平均値が166.8cm、標準偏差が5.8cm」の正規分布のデータで
「上から1%は、何cmなのか」を<font color="Red">**「1-per」**</font>で求めてみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
from scipy.stats import norm

mean = 166.8
std = 5.8
per = 0.01

ppf = norm.ppf(q=(1-per), loc=mean, scale=std)
print("上から", per * 100, "%の値は、", ppf, "です。")
# 上から 1.0 %の値は、 180.2928176694369 です。
```

●違うばらつきのデータでの比較

「norm.cdf」を使うと、「違うばらつきのデータ」で<font color="Red">**どちらがより珍しいか**</font>を
調べることができます。

例：あるテストで数学60点、英語80点を取ったとします。
どちらががんばったでしょうか。

|  | 数学 | 英語 |
|:-----------|------------:|:------------:|
| 点数        | 60        | 80         |
| 平均値      | 50        | 70         |
| 標準偏差    | 5         | 8          |

平均値や標準偏差が違います。このようなとき<font color="Red">**「2つの点が上から何%の珍しいことか」**</font>を比べることで、どちらがよりがんばったのかを調べることができます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
from scipy.stats import norm

scoreM= 60
meanM = 50
stdM = 5

scoreE = 80
meanE = 70
stdE = 8

cdf = norm.cdf(x=scoreM, loc=meanM, scale=stdM)
print("数学の", scoreM, "点は、上から", (1-cdf)*100, "%")

cdf = norm.cdf(x=scoreE, loc=meanE, scale=stdE)
print("英語の", scoreE, "点は、上から", (1-cdf)*100, "%")
# 数学の 60 点は、上から 2.275013194817921 %
# 英語の 80 点は、上から 10.564977366685536 %
```
この結果を見ると、英語は80点ですが上から10.6%でした。
数学は60点ですが上から2.3%なので、より珍しい点だったと分かります。これは数学ががんばったと考えられそうです。

# このデータは自然なばらつき？

ヒストグラムは、matplotlibの<font color="Red">**「データフレーム.plot.hist(bins=bins)」**</font>で表示できましたが、seabornの<font color="Red">**「sns.distplot(データフレーム)」**</font>を使うとさらに便利な機能が付いてきます。

<font color="Red">**「もしもデータがもっとたくさんあったらどうなるか（カーネル密度推定）」**</font>という予測カーブを重ねて表示させることができます。

さらに<font color="Red">**「fit=norm」**</font>というオプションをつけると<font color="Red">**「もし、正規分布になるとしたらどんなカーブになるのだろう」**</font>というところまで推定して重ね合わせることまでできます。

◆データ分析の命令：列データのヒストグラムを表示する（正規分布、推定カーブ付き）
・　必要なライブラリ：pandas、matplotlib、seaborn
・　命令
　　<font color="Red">**sns.distplot(df["列名"], fit=norm, fit_kws={"color":"色"})**</font>
　　<font color="Red">**plt.show()**</font>
・　出力：列データのヒストグラム

例として、<font color="Red">**Numpy**</font>ライブラリを使ってデータを自動生成してみます。
<font color="Red">**「random.randit(最小値, 最大値, 最大値, 個数)」**</font>命令を使うと、
<font color="Red">**「最小値から最大値までのかたよりのない、ばらけたランダムな値」**</font>を作ることができます。<font color="Red">**「random.normal(平均値, 標準偏差, 個数)」**</font>命令を使うと、<font color="Red">**「中央が一番多い、正規分布になるようなランダムな値」**</font>を作ることができます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.stats import norm

sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

df = pd.DataFrame({
    "A": np.random.randint(0, 100, 1500),
    "B": np.random.normal(50, 10, 1500)
})

sns.distplot(df["A"], fit=norm, fit_kws={"color": "red"})
plt.title("かたよりのないランダムな値")
plt.show()

sns.distplot(df["B"], fit=norm, fit_kws={"color": "red"})
plt.title("正規分布になるようなランダムな値")
plt.show()
# グラフ表示
```
Aは正規分布とかなり違う。逆にBは、正規分布に近くなりました。
ただ正規分布はあくまで理想形なので、現実のデータはズレがあることを忘れずに!!

# 違うばらつきのデータでの比較ができる

●偏差値：真ん中は50

あらかじめ<font color="Red">**「平均値は50、標準偏差は10」**</font>とそろえることで、
比較しやすくする方法が考えられました。それが<font color="Red">**「偏差値」**</font>です。
<font color="Red">**「(自分の得点-平均点)÷標準偏差×10+50」**</font>で自分の偏差値を求めることができる。

例：偏差値60、70、80が上から何%となるか調べてみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
from scipy.stats import norm

scorelist = [60, 70, 80]
for score in scorelist:
    cdf = norm.cdf(x=score, loc=50, scale=10)
    print("偏差値", score, "は、上から", (1-cdf)*100, "%")
# 偏差値 60 は、上から 15.865525393145708 %
# 偏差値 70 は、上から 2.275013194817921 %
# 偏差値 80 は、上から 0.13498980316301035 %
```

逆に、<font color="Red">**上から何%に入るのに必要な偏差値**</font>は、<font color="Red">**「norm.ppf」**</font>で調べることができます。

例：上から、15.86%、2.2275%、0.134%に入るのに必要な偏差値を調べてみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
perlist = [0.1586, 0.02275, 0.00134]
for per in perlist:
    ppf = norm.ppf(q=(1-per), loc=50, scale=10)
    print("上から", per*100, "%以上に入るには、偏差値", ppf, "以上が必要")
# 上から 15.86 %以上に入るには、偏差値 60.002283757327085 以上が必要
# 上から 2.275 %以上に入るには、偏差値 70.00002443899604 以上が必要
# 上から 0.134 %以上に入るには、偏差値 80.02240904267309 以上が必要
```

●IQ：真ん中は100

あらかじめ<font color="Red">**「平均値は100、標準偏差は15（または24）」**</font>とそろえることで、
比較しやすくする方法が考えられました。それが<font color="Red">**「IQ（知能指数）」**</font>です。

例：IQ110、130、148が上から何%になるのか調べる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
from scipy.stats import norm

std = 15
IQlist = [110, 130, 148]

for IQ in IQlist:
    cdf = norm.cdf(IQ, loc=100, scale=std)
    print("IQ", IQ, "は、上から", (1-cdf)*100, "%")
# IQ 110 は、上から 25.24925375469229 %
# IQ 130 は、上から 2.275013194817921 %
# IQ 148 は、上から 0.06871379379158604 %
```

今度は標準偏差24の場合を調べる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
from scipy.stats import norm

std = 24
IQlist = [110, 130, 148]

for IQ in IQlist:
    cdf = norm.cdf(IQ, loc=100, scale=std)
    print("IQ", IQ, "は、上から", (1-cdf)*100, "%")
# IQ 110 は、上から 33.84611195106897 %
# IQ 130 は、上から 10.564977366685536 %
# IQ 148 は、上から 2.275013194817921 %
```

# 2種類のデータの関係性の強さ：相関係数

●散布図

これまでは、データの集まりを、1つの代表値にまとめたり、ばらつき方に注目してきました。
それぞれのデータを<font color="Red">**「切り離して、比較」**</font>することで調べましたが、
今度は<font color="Red">**「つながり（関係性）があるか」**</font>について見ていきます。

2種類のデータの関係性を見るには、「散布図」を使います。
横軸と縦軸に関係性を見たいデータを割り当てることで<font color="Red">**「この2種類のデータに、どのくらい関係性があるか」**</font>を見ることができます。

例：「数学と理科と社会」の成績データで、「数学と理科」「数学と社会」の関係性はあるか調べる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(font=["Meiryo", "Yu Gothic", "Hiragino Maru Gothic Pro"])

data = {
    "数学": [100, 85, 90, 95, 80, 80, 75, 65, 65, 60, 55, 45, 45],
    "理科": [94, 90, 95, 90, 85, 80, 75, 70, 60, 60, 50, 50, 48],
    "社会": [80, 88, 70, 62, 86, 70, 79, 65, 75, 67, 75, 68, 60]
}

df = pd.DataFrame(data)
df.head()
# 表が表示される
```

このデータを、散布図で表示するには、
<font color="Red">**「データフレーム.plot.scatter(x="横軸の列名", y="縦軸の列名", c="色")」**</font>と命令します。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df.plot.scatter(x="数学", y="理科", c="b")
plt.title("数学と理科の関係性")
plt.show()

df.plot.scatter(x="数学", y="社会", c="b")
plt.title("数学と社会の関係性")
plt.show()
# 散布図が表示される
```

この結果を見ると、数学と理科は<font color="Red">**「数学の点が高いほど、理科の点も高くなっている」**</font>
ので関係性がありそうです。一方、数学と社会は<font color="Red">**「数学の点が高いほど、社会の点が高くなっているといえない」**</font>ようなので、関係性はあまりなさそうに見えます。

このように散布図は、<font color="Red">**「点のまとまり具合」**</font>を見ることで<font color="Red">**「関係性の強さ」**</font>がわかります。
このときの<font color="Red">**「傾き方」**</font>については、
<font color="Red">**「右肩上がり」**</font>の傾きは、「ある値が大きくなると、別の値も同じように大きくなる」ことを表していて<font color="Red">**「正の相関」**</font>といいます。
<font color="Red">**「右肩下がり」**</font>の傾きは、「ある値が大きくなると、別の値は逆に小さくなる」ことを表していて<font color="Red">**「負の相関」**</font>といいます。

●相関係数

この<font color="Red">**「関係性の強さと傾き」**</font>を「1つの数値」で表したものがあります。
それが<font color="Red">**「相関係数」**</font>です。

相関係数は「-1～+1」で表されます。+1に近ければ「強い正の相関」があり、-1に近ければ「強い負の相関」があり、0に近ければ「相関がない」ことを表します。

![スクリーンショット 2023-07-29 141630.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3468559/b4824680-98a5-70ec-79c3-3886524b2661.png)

相関係数は<font color="Red">**「df.corr()["横の列名"]["縦の列名"]」**</font>で求めることができます。

◆データ分析の命令：列データの相関係数を求める
・　必要なライブラリ：pandas
・　命令
　　<font color="Red">**df.corr()["横の列名"]["縦の列名"]**</font>
・　出力：列データの相関係数

例：「数学と理科」「数学と社会」の、2つの相関係数を表示してみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
print("数学と理科=", df.corr()["数学"]["理科"])
print("数学と社会=", df.corr()["数学"]["社会"])
# 数学と理科= 0.9688434503857298
# 数学と社会= 0.39425173157746296
```

<font color="Red">**「数学と理科には強い相関があり、数学と社会には相関はあまりなさそう」**</font>だとわかります。

<font color="Red">**「df.corr()」**</font>は、列名を指定しなければ、列の組み合わせを総当たりで調べて並べてくれます。
相関係数の行列なので、<font color="Red">**「相関行列」**</font>といいます。

◆データ分析の命令：相関行列を求める
・　必要なライブラリ：pandas
・　命令
　　<font color="Red">**df.corr()**</font>
・　出力：データの相関行列

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
print(df.corr())
#          数学        理科        社会
# 数学  1.000000  0.968843  0.394252
# 理科  0.968843  1.000000  0.413466
# 社会  0.394252  0.413466  1.000000
```

●散布図の上に線を引いて予想

データにはばらつきがありますが、誤差が最小になるように線を引くことができれば、<font color="Red">**「横軸のある値（説明変数）のとき、縦軸はどんな値（目的変数）になるか」**</font>を予測できます。
これを<font color="Red">**「回帰直線」**</font>といいます。

散布図の上に回帰直線を引くには、seabornの<font color="Red">**「sns.regplot()」**</font>という命令が使えます。

◆データ分析の命令：散布図+回帰直線を表示する
・　必要なライブラリ：pandas、matplotlib、seaborn
・　命令
　　<font color="Red">**sns.regplot(data=df, x="横軸", y="縦軸", line_kws={"color": "色"})**</font>
　　<font color="Red">**plt.show()**</font>
・　出力：散布図の上に回帰直線

例：「数学と理科」「数学と社会」の、2つの散布図の上に回帰直線を表示してみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.regplot(data=df, x="数学", y="理科", line_kws={"color":"red"})
plt.show()

sns.regplot(data=df, x="数学", y="社会", line_kws={"color":"red"})
plt.show()
# 散布図の上に回帰直線が表示される
```

相関が強ければはっきりした線が引けますが、相関が弱ければはっきりした線は引けません。薄い赤い範囲がそれを表していて、<font color="Red">**「95%の確率でこの範囲に入るだろう」**</font>という範囲です。
これを<font color="Red">**「信頼区間」**</font>といいます。

seabornには、散布図+回帰直線に、さらにヒストグラムを一緒に表示させる命令もあります。<font color="Red">**「sns.jointplot()」**</font>です。

◆データ分析の命令：ヒストグラム付き散布図+回帰直線を表示する
・　必要なライブラリ：pandas、matplotlib、seaborn
・　命令
　　<font color="Red">**sns.jointplot(data=df, x="横軸", y="縦軸", kind="reg", line_kws={"color": "色"})**</font>
　　<font color="Red">**plt.show()**</font>
・　出力：ヒストグラム付き散布図+回帰直線

例：「数学と理科」「数学と社会」の、2つを表示してみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.jointplot(data=df, x="数学", y="理科", kind="reg", line_kws={"color":"red"})
plt.show()

sns.jointplot(data=df, x="数学", y="社会", kind="reg", line_kws={"color":"red"})
plt.show()
# ヒストグラム付き散布図+回帰直線
```

# 総当たりで表示させる散布図

●相関行列を色の熱さで表現する：ヒートマップ

<font color="Red">**「熱そうな色ほど大きい値」**</font>と感覚的に見ることができます。
seabornの<font color="Red">**「sns.heatmap()」**</font>という命令で表示できます。

◆データ分析の命令：相関行列を色分けして表示する
・　必要なライブラリ：pandas、matplotlib、seaborn
・　命令
　　<font color="Red">**sns.heatmap(df.corr())**</font>
　　<font color="Red">**plt.show()**</font>
・　出力：ヒートマップ

例：ヒートマップを表示してみる。相関係数の数値も一緒に表示させたいので<font color="Red">**「annot=True」**</font>と指定し、最大値を1、最小値を-1、中央の相関なしを0にして、
色分けしたいので、<font color="Red">**「vmax=1, vmin=-1, center=0」**</font>と指定します。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.heatmap(df.corr(), annot=True, vmax=1, vmin=-1, center=0)
plt.show()
# ヒートマップ
```

斜めの部分は1なので一番明るい色になっていますが、その次に明るいのは「数学と理科」で、相関が強いことがわかります。
「数学と社会」「理科と社会」は暗く、相関が弱いと感覚的にわかります。

●総当たりで表示させる散布図：散布図行列

「散布図のすべての組み合わせを行列で表示させたもの」を
<font color="Red">**「散布図行列」**</font>といいます。seabornの<font color="Red">**「sns.pairplot(data=df)」**</font>という命令を使います。

◆データ分析の命令：散布図のすべての組み合わせを行列で表示させる
・　必要なライブラリ：pandas、matplotlib、seaborn
・　命令
　　<font color="Red">**sns.pairplot(data=df)**</font>
　　<font color="Red">**plt.show()**</font>
・　出力：散布図行列

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.pairplot(data=df)
plt.show()
# 散布図行列
```

「相関行列」と同じように斜め部分は同じデータな同士なので
散布図の代わりにヒストグラムが表示されています。

さらにこの上に、回帰直線を表示させてみます。
オプションで<font color="Red">**「kind="reg"」**</font>と指定します。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.pairplot(data=df, kind="reg")
plt.show()
# 散布図行列 + 回帰直線
```

# アヤメのデータを見てみよう

アヤメの「がくの長さ」「がくの幅」「花びらの長さ」「花びらの幅」は品種と関係があるのではないか。（英）ロナルド・フィッシャー

※この論文データが機械学習の分類のサンプルデータとして使われている。

このデータには、<font color="Red">**「setosa（セトサ：ヒオウギアヤメ）」「versicolor（バージカラー）」「versinica（バージニカ）」**</font>という3種類のアヤメの品種データが入っていて、
それぞれの<font color="Red">**「がくの長さ（sepal length）」「がくの幅（sepal width）」「花びらの長さ（petal length）」「花びらの幅（petal width）」**</font>の測定値が入っています。

◆データ分析の命令：アヤメの品種データを読み込む
・　必要なライブラリ：pandas、matplotlib、seaborn
・　命令
　　<font color="Red">**sns.load_dataset("iris")**</font>
・　出力：列データの平均値

例：アヤメの品種データを読み込んで表示させてみる

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

df = sns.load_dataset("iris")
df.head()
# アヤメの品種データの表示
```

例：「がくの長さ・幅」「花びらの長さ・幅」の、どの関係性が強いのか、相関係数行列を表示してみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df.corr()
# 相関係数行列の表示
```

例：ヒートマップを表示してみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.heatmap(df.corr(), annot=True, vmax=1, vmin=-1, center=0)
plt.show()
# ヒートマップの表示
```

例：散布図行列を表示してみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.pairplot(data=df)
plt.show()
# 散布図行列の表示
```

表示結果から、<font color="Red">**何となく大小2つのかたまりに分かれている**</font>ことはわかります。
このデータは「3つの品種」が入ったデータですが、その3つの品種が混じったまま調べているからです。
このデータを品種ごとに分けて調べてみます。

<font color="Red">**ある列データで使われているデータの種類を取り出す**</font>には、
<font color="Red">**「df["列名"].unique()」**</font>という命令が使えます。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
df["species"].unique()
# array(['setosa', 'versicolor', 'virginica'], dtype=object)
```

3つの名前が使われていました。
この品種をそれぞれ別々に調べてみます。
列データの中から、「ある条件に合うデータを抽出」するには、
<font color="Red">**「データフレーム = データフレーム[条件]」**</font>を使います。
例えば、「speciesの値が、setosaのものだけを抽出する」には、
<font color="Red">**「df[df["species"]=="setosa"]」**</font>と命令します。

例：それぞれのヒートマップを表示してみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
onespecies = "setosa"

one = df[df["species"] == onespecies]
sns.heatmap(one.corr(), annot=True, vmax=1, vmin=-1, center=0)
plt.title(onespecies, fontsize=18)
plt.show()
# setosaのヒートマップ
```

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
onespecies = "versicolor"

one = df[df["species"] == onespecies]
sns.heatmap(one.corr(), annot=True, vmax=1, vmin=-1, center=0)
plt.title(onespecies, fontsize=18)
plt.show()
# versicolorのヒートマップ
```

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
onespecies = "virginica"

one = df[df["species"] == onespecies]
sns.heatmap(one.corr(), annot=True, vmax=1, vmin=-1, center=0)
plt.title(onespecies, fontsize=18)
plt.show()
# virginicaのヒートマップ
```

例：setosaの散布図行列と回帰直線を表示してみる。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
onespecies = "setosa"

one = df[df["species"] == onespecies]
sns.pairplot(data=one, kind="reg")
plt.show()
# setosaの散布図行列と回帰直線を表示
```

結果から、「がくの長さ（sepal_length）」が長くなると、「がくの幅（sepal_width）も広くなる」という相関が強くて、それ以外の相関は弱そうだとわかります。

次に、<font color="Red">**品種で分類わけ**</font>して表示してみます。
<font color="Red">**「ある列の値で分類わけして表示させる」**</font>には、<font color="Red">**「hue="列名"」**</font>というオプションを指定します。

●プログラム例（入力プログラム）

```プログラム例（入力プログラム）.py
sns.pairplot(data=df, hue="species")
plt.show()
# 分類わけの結果表示
```
versicolor（オレンジ）とvirginica（緑）は近い位置にいて、
setosa（青）は少し離れた位置にいるようです。

# 終了