Python - Pandas、データフレームを使ったデータ分析

Odyssey CBTのPython3 エンジニア認定データ分析試験の学習を始めました。今回はPandasを使ったデータ処理について学習しました。
https://pandas.pydata.org/docs/user_guide/index.html

# Pandasについて
PandasはNumpyを基盤に1次元データを扱う`Series型`、2次元データを扱う`DataFrame型`を提供するライブラリーです。DataFrameを用いるとTidyverseを使ったR言語環境のように、データ分析のフレームワークを使った便利機能を色々使うことができます。（大雑把）

```python
import numpy as np
import pandas as pd

# Series型
series = pd.Series([10,20,30,40,50])
print(series)
```
```python
0    10
1    20
2    30
3    40
4    50
dtype: int64
```

```python
# DataFrame型
df = pd.DataFrame([[10,"a",True],[20,"b",False],[30,"c",False],[40,"d",True]])
df
```
```python

    0	1	2
0	10	a	True
1	20	b	False
2	30	c	False
3	40	d	True
```
# arrange, reshape - データの作成と変形変形
DataFrame型はNumpyと同様、データ型であるndarray型を生成・変形することでデータの内容や行列方向の形状を変更できます。
```python
# reshape - データを作成・変形してDataFrame型にする
df = pd.DataFrame(np.arange(100).reshape((25, 4)))
print(df)
```
```python
     0   1   2   3
0    0   1   2   3
1    4   5   6   7
2    8   9  10  11
3   12  13  14  15
4   16  17  18  19
5   20  21  22  23
6   24  25  26  27
7   28  29  30  31
8   32  33  34  35
9   36  37  38  39
10  40  41  42  43
11  44  45  46  47
12  48  49  50  51
13  52  53  54  55
14  56  57  58  59
15  60  61  62  63
16  64  65  66  67
17  68  69  70  71
18  72  73  74  75
19  76  77  78  79
20  80  81  82  83
21  84  85  86  87
22  88  89  90  91
23  92  93  94  95
24  96  97  98  99
```

# head, tail, shape -データの先頭・後方、形状を確認
データの先頭・後方5列を確認するには`head`, `tail`というメソッドを使うことができます。`shape`では形状の確認ができます。
```python
df.head()
df.tail()
df.shape
```

```python
	0	1	2	3
0	0	1	2	3
1	4	5	6	7
2	8	9	10	11
3	12	13	14	15
4	16	17	18	19

	0	1	2	3
20	80	81	82	83
21	84	85	86	87
22	88	89	90	91
23	92	93	94	95
24	96	97	98	99

(25, 4)
```

# コラム名・インデックス名を指定・参照
```python
# コラム名・インデックス名を指定・参照
dframe = pd.DataFrame(np.arange(12).reshape((4,3)), columns=["A","B","C"], index=["01","02","03","04"])
print(dframe)
```
```python
    A   B   C
01  0   1   2
02  3   4   5
03  6   7   8
04  9  10  11
```

```python
# コラム名・インデックス名を指定すると参照できる
dframe["A"]
dframe[["A", "B"]]
```
```python
01    0
02    3
03    6
04    9
Name: A, dtype: int64

	A	B
01	0	1
02	3	4
03	6	7
04	9	10
```

```python
# インデックスを使った参照 - 最初の3行をスライスする
dframe[:3]
```
```python
	A	B	C
01	0	1	2
02	3	4	5
03	6	7	8
```
# read_csv, read_excel - データの読み込み
Pandasではcsv・excel形式のファイルを読み込んでデータフレームにすることができます。サンプルデータとして「Pythonによるあたらしいデータ分析の教科書 第2版」のデータを使います。

```python
import pandas as pd
df = pd.read_csv("202204health.csv", encoding="utf-8")
df.head()
```
```python
        日付	歩数	摂取カロリー
0	2022-04-01	5439	2500
1	2022-04-02	2510	2300
2	2022-04-03	10238	1950
3	2022-04-04	8209	1850
4	2022-04-05	9434	1930
```

```python
df = pd.read_excel("202204health.xlsx")
df.head()
```
```python
        日付	歩数	摂取カロリー
0	2022-04-01	5439	2500
1	2022-04-02	2510	2300
2	2022-04-03	10238	1950
3	2022-04-04	8209	1850
4	2022-04-05	9434	1930
```
# read_html - WebサイトのHTML要素をスクレイピングする
urlの直接指定と`read_html`メソッドを組み合わせると簡単なWebスクレイピングを行う事ができます。
```python
url = "https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%83%E3%83%97%E3%83%AC%E3%83%99%E3%83%AB%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E4%B8%80%E8%A6%A7"
tables = pd.read_html(url, flavor = "html5lib")

len(tables)
#国別のテーブルはインデックスの4番目に入っている
df=tables[4]
df
```
```python
	Name	Entity	Explanation	Notes	IDN	DNSSEC	SLD	IPv6
0	.ac	アセンション島	NaN	広く学術的なサイト（大学など）のために用いられる。アセンション島はイギリス領であるが、イギリ...	Yes	Yes	Yes	Yes
1	.ad	アンドラ	NaN	アンドラにおける商標または市民権が必要となる[7][8]。	NaN	Yes	Yes	NaN
2	.ae	アラブ首長国連邦	NaN	NaN	NaN	No	Yes	NaN
3	.af	アフガニスタン	NaN	NaN	NaN	Yes	Yes	NaN
4	.ag	アンティグア・バーブーダ	NaN	AGがドイツの株式会社(Aktiengesellschaft)の略称であることから、非公式に...	NaN	Yes	Yes	NaN
...	...	...	...	...	...	...	...	...
248	.ye	イエメン	NaN	NaN	NaN	No	No	NaN
249	.yt	マヨット島	NaN	欧州連合・スイス・ノルウェー・アイスランド・リヒテンシュタインの個人・企業に限る[cctld...	Yes[cctld 12]	Yes	Yes	NaN
250	.za	南アフリカ	Zuid-Afrika (オランダ語)	NaN	NaN	No	No	NaN
251	.zm	ザンビア	NaN	NaN	NaN	Yes	Yes[cctld 28]	NaN
252	.zw	ジンバブエ	NaN	NaN	NaN	No	No	NaN
253 rows × 8 columns
```
# to_csv, to_excel, to_pickle - データをcsv・excel・DataFrameとして出力する
```python
#データをcsv・excelとして出力する
df.to_csv("data/write_data.csv")
df.to_excel("data/write_data.xlsx")

#DataFrame形式で出力→次回利用時にそのままデータフレームとして使用できる
df.to_pickle("data/write_df.pickle")
df = pd.read_pickle("data/write_df.pickle")
```

# データの条件抽出
if文のように条件分岐を行う事で、特定の条件を満たすデータか否かを`bool型`で判定できます。また、条件に合致するデータのみを別のデータフレームに代入することができます。

```python
df = pd.read_excel("202204health.xlsx")
df.loc[:, "歩数"] >= 10000
```
```python
0     False
1     False
2      True
3     False
4     False
5     False
6     False
7     False
8      True
9     False
10    False
11    False
12     True
13    False
14    False
15    False
16    False
17    False
18    False
19     True
20     True
21    False
22    False
23    False
24    False
25    False
26    False
27    False
28    False
29    False
Name: 歩数, dtype: bool
```

```python
#条件で抽出→別のデータフレームを作成する
df_selected = df.loc[df.loc[:, "歩数"] >= 10000,:]
df_selected
```
```python

        日付	歩数	摂取カロリー
2	2022-04-03	10238	1950
8	2022-04-09	12045	1950
12	2022-04-13	10287	1800
19	2022-04-20	15328	1800
20	2022-04-21	12849	1940
```

# データの整形・前処理
日付データ等は`object型`となっていることが多いので、`to_datetime`メソッドで日付型に整形します。
その他細かい修正として、摂取カロリーなどの連続値をint型からfloat型に変換します。
```python
#データフレームの方式を確認する
df.dtypes
```
```python
日付        object
歩数         int64
摂取カロリー     int64
dtype: object
```

```python
#日付データはobject型になっていることが多いので、to_datetimeメソッドで日付型にする
df.loc[:, "date"]=df.loc[:,"日付"].apply(pd.to_datetime)
df.loc[:, "date"]
```
```python
<bound method NDFrame.head of 0    2022-04-01
1    2022-04-02
2    2022-04-03
3    2022-04-04
4    2022-04-05
5    2022-04-06
6    2022-04-07
7    2022-04-08
8    2022-04-09
9    2022-04-10
10   2022-04-11
11   2022-04-12
12   2022-04-13
13   2022-04-14
14   2022-04-15
15   2022-04-16
16   2022-04-17
17   2022-04-18
18   2022-04-19
19   2022-04-20
20   2022-04-21
21   2022-04-22
22   2022-04-23
23   2022-04-24
24   2022-04-25
25   2022-04-26
26   2022-04-27
27   2022-04-28
28   2022-04-29
29   2022-04-30
Name: date, dtype: datetime64[ns]>
```

```python
#摂取カロリーをintからfloat型にする。データの型の微妙な違いに気を付ける
df.loc[:, "摂取カロリー"]=df.loc[:, "摂取カロリー"].astype(np.float32)
df.head()
```
```python
            	日付	歩数	摂取カロリー	date
date				
2022-04-01	2022-04-01	5439	2500.0	2022-04-01
2022-04-02	2022-04-02	2510	2300.0	2022-04-02
2022-04-03	2022-04-03	10238	1950.0	2022-04-03
2022-04-04	2022-04-04	8209	1850.0	2022-04-04
2022-04-05	2022-04-05	9434	1930.0	2022-04-05
```

# ソート - ort_valuesメソッドで並べ替え
int・float型はsort_valuesメソッドで並べ替えができます。
```python
df.sort_values(by="歩数").head()
```
```python
    	日付	歩数	摂取カロリー	date
1	2022-04-02	2510	2300	2022-04-02
22	2022-04-23	3890	1950	2022-04-23
21	2022-04-22	4029	2300	2022-04-22
29	2022-04-30	4093	1950	2022-04-30
7	2022-04-08	4873	2300	2022-04-08
```

```python
#並べ替え -　int・float型を降順で並べ替える
df.sort_values(by="歩数", ascending=False).head()
```
```python
        日付	歩数	摂取カロリー	date
date				
2022-04-20	2022-04-20	15328	1800.0	2022-04-20
2022-04-21	2022-04-21	12849	1940.0	2022-04-21
2022-04-09	2022-04-09	12045	1950.0	2022-04-09
2022-04-13	2022-04-13	10287	1800.0	2022-04-13
2022-04-03	2022-04-03	10238	1950.0	2022-04-03
```
# drop - 不要カラムの削除
`日付`と`date`でデータ内容が重複しているので、変換前の`日付`を削除します。

```python
# 日付とdateでデータ内容が重複しているので、`drop`メソッドで変換前の"日付"を削除する
df = df.drop("日付", axis=1)
df.head()
```
```python

    歩数	摂取カロリー	date
0	5439	2500	2022-04-01
1	2510	2300	2022-04-02
2	10238	1950	2022-04-03
3	8209	1850	2022-04-04
4	9434	1930	2022-04-05
```

## 計算結果の挿入
歩数当たりのカロリーをデータフレームに挿入します。
```python
df.loc[:,"歩数/カロリー"] = (df.loc[:, "歩数"]/df.loc[:, "摂取カロリー"])
df
```
```python

歩数	摂取カロリー	date	歩数/カロリー
0	5439	2500.0	2022-04-01	2.175600
1	2510	2300.0	2022-04-02	1.091304
2	10238	1950.0	2022-04-03	5.250256
3	8209	1850.0	2022-04-04	4.437297
4	9434	1930.0	2022-04-05	4.888083
5	7593	1800.0	2022-04-06	4.218333
.
.
.
```
## 計算の関数化
数/カロリーをもとに運動指数を関数で作成します
```python
##計算の関数化 - 歩数/カロリーをもとに運動指数を関数で作成する
def exercise_judge(ex):
    if ex <= 3.0:
        return "Low"
    elif 3.0 < ex <= 6.0:
        return "Mid"
    else:
        return "High"

df.loc[:,"運動指数"] = df.loc[:, "歩数/カロリー"].apply(exercise_judge)
df
```
```python
	歩数	摂取カロリー	date	歩数/カロリー	運動指数
0	5439	2500.0	2022-04-01	2.175600	Low
1	2510	2300.0	2022-04-02	1.091304	Low
2	10238	1950.0	2022-04-03	5.250256	Mid
3	8209	1850.0	2022-04-04	4.437297	Mid
4	9434	1930.0	2022-04-05	4.888083	Mid
5	7593	1800.0	2022-04-06	4.218333	Mid
.
.
.
```

#ワンホットベクトル化
#定性データをワンホットベクトルを使ったデータに変換することができます。教師あり学習などの機械学習に利用するデータにしたい時に役立ちます。
```python
#get_dummies - 定性データをワンホットベクトルを使ったデータに変換する
df_moved = pd.get_dummies(df.loc[:,"運動指数"], prefix="運動")
df_moved
```
```python
運動_High	運動_Low	運動_Mid
0	0	1	0
1	0	1	0
2	0	0	1
3	0	0	1
4	0	0	1
5	0	0	1
.
.
.

```

# 時系列データを生成
時系列データを作成したい時等に、必要な分の日付の配列を開始・終了日を指定して作成することができます。
```python
import pandas as pd
import numpy as np

# 1ヶ月分の日付の配列を開始・終了日を指定して作成する
dates = pd.date_range(start="2022-04-01", end="2022-04-30")
dates
```
```python
DatetimeIndex(['2022-04-01', '2022-04-02', '2022-04-03', '2022-04-04',
               '2022-04-05', '2022-04-06', '2022-04-07', '2022-04-08',
               '2022-04-09', '2022-04-10', '2022-04-11', '2022-04-12',
               '2022-04-13', '2022-04-14', '2022-04-15', '2022-04-16',
               '2022-04-17', '2022-04-18', '2022-04-19', '2022-04-20',
               '2022-04-21', '2022-04-22', '2022-04-23', '2022-04-24',
               '2022-04-25', '2022-04-26', '2022-04-27', '2022-04-28',
               '2022-04-29', '2022-04-30'],
              dtype='datetime64[ns]', freq='D')
```

作成した日付をインデックスにすることができます。

```python
#作成した日付をインデックスにし、対応する値を乱数にする
mg = np.random.default_rng(123)
df = pd.DataFrame(mg.integers(1,31,size=30),index=dates,columns=["乱数値"])
df
```
```python
	乱数値
2022-04-01	1
2022-04-02	21
2022-04-03	18
2022-04-04	2
2022-04-05	28
2022-04-06	7
.
.
.
```

## 1年の日付の配列を作成
```python
# 1年の日付の配列を作成する
dates = pd.date_range(start="2022-01-01", periods=365)
dates
```
```python
DatetimeIndex(['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04',
               '2022-01-05', '2022-01-06', '2022-01-07', '2022-01-08',
               '2022-01-09', '2022-01-10',
               ...
               '2022-12-22', '2022-12-23', '2022-12-24', '2022-12-25',
               '2022-12-26', '2022-12-27', '2022-12-28', '2022-12-29',
               '2022-12-30', '2022-12-31'],
              dtype='datetime64[ns]', length=365, freq='D')
```

```python
#作成した日付をインデックスにし、対応する値を乱数にする
mg = np.random.default_rng(123)
df = pd.DataFrame(mg.integers(1,31,size=365),index=dates,columns=["乱数値"])
df
```
```python

乱数値
2022-01-01	1
2022-01-02	21
2022-01-03	18
2022-01-04	2
2022-01-05	28
...	...
2022-12-27	23
2022-12-28	17
2022-12-29	2
2022-12-30	7
2022-12-31	23
```
## 月平均のデータフレームを作成

```python
# Grouperを使うと月ごと・週ごとの平均などが出力できる
df.groupby(pd.Grouper(freq="M")).mean()
```
```python
乱数値
2022-01-31	14.516129
2022-02-28	12.892857
2022-03-31	16.935484
2022-04-30	12.933333
2022-05-31	16.612903
2022-06-30	14.200000
2022-07-31	14.161290
2022-08-31	15.935484
2022-09-30	13.500000
2022-10-31	13.483871
2022-11-30	14.300000
2022-12-31	17.741935
```

# 欠損値の処理
ミスや計測値がない場合、データ型に`NaN`型(欠損値)が挿入されていることがあります。これらの値・行列を削除する方法・値を埋め合わせる方法を考えます。
```python
#欠損値-NaNを処理する方法を覚える
import numpy as np
import pandas as pd

df_202205 = pd.read_csv("202205health.csv", encoding="utf-8", index_col="日付", parse_dates=True)
df_202205
```
```python
	歩数	摂取カロリー
日付		
2022-05-01	1439.0	4500.0
2022-05-02	8120.0	2420.0
2022-05-03	NaN	NaN
2022-05-04	2329.0	1500.0
2022-05-05	NaN	NaN
2022-05-06	3233.0	1800.0
.
.
.
```
## dropna - 欠損値を含む行を削除
```python
df_202205_dropped = df_202205.dropna()
df_202205_dropped
```
```python
歩数	摂取カロリー
日付		
2022-05-01	1439.0	4500.0
2022-05-02	8120.0	2420.0
2022-05-04	2329.0	1500.0
2022-05-06	3233.0	1800.0
2022-05-07	9593.0	2200.0
2022-05-08	9213.0	1800.0
2022-05-09	5593.0	2500.0
```

```python
# fillna - 欠損値を含む行に値を挿入する
df_202205_filled = df_202205.fillna(0)
df_202205_filled
```
```python
歩数	摂取カロリー
日付		
2022-05-01	1439.0	4500.0
2022-05-02	8120.0	2420.0
2022-05-03	0.0	0.0
2022-05-04	2329.0	1500.0
2022-05-05	0.0	0.0
2022-05-06	3233.0	1800.0
2022-05-07	9593.0	2200.0
2022-05-08	9213.0	1800.0
2022-05-09	5593.0	2500.0
```

```python
# fillna, method = "ffill",値を前の行で保管する
df_202205_fill = df_202205.fillna(method = "ffill")
df_202205_fill
```
```python
歩数	摂取カロリー
日付		
2022-05-01	1439.0	4500.0
2022-05-02	8120.0	2420.0
2022-05-03	8120.0	2420.0
2022-05-04	2329.0	1500.0
2022-05-05	2329.0	1500.0
2022-05-06	3233.0	1800.0
2022-05-07	9593.0	2200.0
2022-05-08	9213.0	1800.0
2022-05-09	5593.0	2500.0
```

```python
# 平均値、中央値、最頻値で保管する
# mean-平均、median-中央値、mode().iloc[0,:]-最頻値
df_202205_fillmean = df_202205.fillna(df_202205.mean())
df_202205_fillmean
```
```python
歩数	摂取カロリー
日付		
2022-05-01	1439.000000	4500.000000
2022-05-02	8120.000000	2420.000000
2022-05-03	5645.714286	2388.571429
2022-05-04	2329.000000	1500.000000
2022-05-05	5645.714286	2388.571429
2022-05-06	3233.000000	1800.000000
2022-05-07	9593.000000	2200.000000
2022-05-08	9213.000000	1800.000000
2022-05-09	5593.000000	2500.000000
```

# 統計量を表示

```python
import numpy as np
import pandas as pd

df = pd.read_excel("202204health.xlsx")

#最頻値
df.loc[:, "摂取カロリー"].mode()
#平均値
df.loc[:, "摂取カロリー"].mean()
中央値
df.loc[:, "摂取カロリー"].median()
#標準偏差(母分散)
df.loc[:, "摂取カロリー"].std()
#標準偏差(標本分散)
df.loc[:, "摂取カロリー"].std(ddof=0)
#要約統計量をまとめて表示
df.describe()
```
```python
0    2300
Name: 摂取カロリー, dtype: int64
2026.6666666666667
1945.0
205.54944590692452
202.09458731549992

歩数	摂取カロリー
count	30.000000	30.000000
mean	7766.366667	 2026.666667
std	    2689.269308	 205.549446
min	    2510.000000	 1800.000000
25%	    6661.500000	 1870.000000
50%	    7561.000000	 1945.000000
75%	    8408.500000	 2300.000000
max	    15328.000000 2500.000000
```

```python
df.loc[:,"歩数/カロリー"] = (df.loc[:, "歩数"]/df.loc[:, "摂取カロリー"])
df
```
```python
日付	歩数	摂取カロリー	歩数/カロリー
0	2022-04-01	5439	2500	2.175600
1	2022-04-02	2510	2300	1.091304
2	2022-04-03	10238	1950	5.250256
3	2022-04-04	8209	1850	4.437297
4	2022-04-05	9434	1930	4.888083
5	2022-04-06	7593	1800	4.218333
6	2022-04-07	9320	1940	4.804124
.
.
.
```

```python
#相関係数 - カラム間のデータの相関係数を確認する
df.corr()
```
```python
        歩数	摂取カロリー	歩数/カロリー
歩数	          1.000000	-0.498703	0.982828
摂取カロリー	 -0.498703	1.000000   -0.636438
歩数/カロリー     0.982828	-0.636438	1.000000
```

```python
#散布図行列 - カラム間のデータの分布を表示する
from pandas.plotting import scatter_matrix
plot=scatter_matrix(df)
```
![スクリーンショット 2023-05-09 182938.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337578/cc5b39c2-af6e-89c1-bc9f-0a9de3acbae6.png)