pandasを使ったデータの処理

# はじめに
データ分析関連の基礎を身に着けるため、本を読みながら勉強しています。
データを分析する際には便利そうなpandasについていろいろ試しながら確認したので、結果をまとめてみました。
基礎的な内容かもしれませんが、知らない事がたくさんあり、勉強になりました。

https://www.shoeisha.co.jp/book/detail/9784798158341

## pandas のデータの形　seriesとdataframe
pandasには1次元のseriesと2次元のdataframeがあります。

``` python:in
a = pd.Series([10,20,30,40])
print(a)

b = pd.DataFrame([[10,"apple",True],
                 [30,"banana",False],
                 [20,"orange",True],
                 [40,"grape",False]]
                 )
print(b)
```

``` :out
0    10
1    20
2    30
3    40
dtype: int64

    0       1      2
0  10   apple   True
1  30  banana  False
2  20  orange   True
3  40   grape  False
```

## 表示方法　tail　head
データフレームを表示させる際に先頭から表示させる場合にはheadを、末尾から表示させる場合はtailを使います。

``` python:in
df = pd.DataFrame([[10,"apple",True],
                 [30,"banana",False],
                 [20,"orange",True],
                 [40,"grape",False]]
                 )
a = df.tail(1)
b = df.head(1)
```

``` :out
    0      1      2
3  40  grape  False
    0      1     2
0  10  apple  True
```

## インデックス名(行)とカラム名(列)　index　columns
データフレームには行と列に名前を付けることができます。
行に名前を付ける場合にはindexを、列に名前を付ける場合にはcolumnsを使用します。

``` python:in
df = pd.DataFrame([[10,"apple",True],
                 [30,"banana",False],
                 [20,"orange",True],
                 [40,"grape",False]]
                 )

df.index = ["A","B","C","D"]
df.columns = ["番号","名前","真偽"] 

print(df)
```

``` :out
   番号      名前     真偽
A  10   apple   True
B  30  banana  False
C  20  orange   True
D  40   grape  False
```

## データの抽出 loc　iloc
データフレームからデータを取り出す際には、locメソッドやilocメソッドを使います。
locメソッドを使うと、インデックス名とカラム名からデータを抽出することができ、
ilocメソッドを使うと、インデックス番号とカラム番号からデータを抽出することができます。

以下はそれぞれのメソッドを使って同じデータを参照している例です。

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

df = pd.DataFrame(np.arange(12).reshape((4,3)),
                  columns=["A","B","C"],
                  index= ["1","2","3","4"])
print(df)

print(df.loc[:,"B"])
print(df.iloc[:,1])

print(df.loc["3","C"])
print(df.iloc[2,2])
```

``` :out
   A   B   C
1  0   1   2
2  3   4   5
3  6   7   8
4  9  10  11

1     1
2     4
3     7
4    10
Name: B, dtype: int32
1     1
2     4
3     7
4    10
Name: B, dtype: int32

8
8
```

## データフレームの読み書き
データフレームを作成する場合、手入力ではなく、Excelファイル、CSVファイル、Webサイトから作成する場合には以下のように行います。

・webサイトから読み出す場合
wikiediaから宮城県の自治体の情報を取得してみました。

``` python:in
url = "https://ja.wikipedia.org/wiki/%E5%AE%AE%E5%9F%8E%E7%9C%8C"
tables = pd.read_html(url)
print(tables[4]) #見たいテーブルの位置に合わせる必要あり
```

``` :out
        地域圏  市区町村    団体コード    郡  2010年国調（人）  推計人口（人）  増減率(%)
0   気仙沼・本吉圏  気仙沼市  04205-6    市       73489    59179  -19.47
1   気仙沼・本吉圏  南三陸町  04606-0  本吉郡       17429    11876  -31.86
2       登米圏   登米市  04212-9    市       83969    74132  -11.72
3       栗原圏   栗原市  04213-7    市       74932    62609  -16.45
　（省略）
38      仙南圏   蔵王町  04301-0  刈田郡       12882    11127  -13.62
39      仙南圏  七ヶ宿町  04302-8  刈田郡        1694     1201  -29.10
```

・エクセルを使う場合
以下のようにpd.read_excelを使ってデータフレームを変数に代入します。
``` python
df = pd.read_excel("XXXXXXXXX.xlsx")
```

加工したデータフレームをエクセルに保存する場合には、to_excelを使用します。
``` python
df.to_excel("XXXXXXXXX.xlsx")
```

・CSVを使う場合
以下のようにpd.read_csvを使ってデータフレームを変数に代入します。
``` python
df = pd.read_csv("XXXXXXXXXX.csv")
```
加工したデータフレームをCSVに保存する場合には、to_csvを使用します。
``` python
df.to_csv("XXXXXXXXXX.csv")
```

・pickleを使う場合
pythonのデータフレームを直列化して保存する形式として、pickle形式があります。
使い方はexcelやcsvと同じで、データフレームをファイルに出力したり読み込んだりすることができます。

ファイルから読み出す場合は以下のようにします。
``` python
df = pd.read_pickle("XXXXXXXXXX.pickle")
```

ファイルに出力する場合は以下に用にします。
``` python
df.to_pickle("XXXXXXXXXX.pickle")
```

## データを条件付きで抽出
取得したデータフレームに対して、特定のカラムの要素をチェックして真偽を返したり、条件に一致しているデータだけを抽出するには以下のようにします。

``` python:in
url = "https://ja.wikipedia.org/wiki/%E5%AE%AE%E5%9F%8E%E7%9C%8C"
tables = pd.read_html(url)
df = tables[4]

population_growth = df["増減率(%)"] > 0
print(population_growth)

population_growth_town = df[df["増減率(%)"] > 0]
print(population_growth_town)
```

``` :out
0     False
1     False
2     False
 (省略)
38    False
39    False
Name: 増減率(%), dtype: bool

      地域圏  市区町村    団体コード    郡  2010年国調（人）  推計人口（人）  増減率(%)
12  仙台都市圏   大衡村  04424-5  黒川郡        5334     5665    6.21
13  仙台都市圏   大和町  04421-1  黒川郡       24894    28651   15.09
15  仙台都市圏   富谷市  04216-1    市       47042    51542    9.57
18  仙台都市圏   利府町  04406-7  宮城郡       33994    35207    3.57
21  仙台都市圏   仙台市  04100-9    市     1045986  1093543    4.55
22  仙台都市圏  宮城野区  04102-5  行政区      190473   195568    2.67
23  仙台都市圏   若林区  04103-3  行政区      132306   141260    6.77
25  仙台都市圏   青葉区  04101-7  行政区      291436   310682    6.60
26  仙台都市圏   太白区  04104-1  行政区      220588   235634    6.82
27  仙台都市圏   名取市  04207-2    市       73134    78629    7.51
33    仙南圏  大河原町  04321-4  柴田郡       23530    23635    0.45
```

## データの並び替え　sort_values
先ほど取得した宮城県の自治体の情報の増減率を降順に並べ替えるにはsort_valuesを使います。
気まぐれでこのデータ使いましたが震災以降沿岸地域の人口が減ってる様子がわかります。

``` python:in
url = "https://ja.wikipedia.org/wiki/%E5%AE%AE%E5%9F%8E%E7%9C%8C"
tables = pd.read_html(url)
df = tables[4]

population_growth = df.sort_values(by="増減率(%)", ascending=False)
print(population_growth)
```

``` :out
        地域圏  市区町村    団体コード    郡  2010年国調（人）  推計人口（人）  増減率(%)
13    仙台都市圏   大和町  04421-1  黒川郡       24894    28651   15.09
15    仙台都市圏   富谷市  04216-1    市       47042    51542    9.57
27    仙台都市圏   名取市  04207-2    市       73134    78629    7.51
　(省略)
39      仙南圏  七ヶ宿町  04302-8  刈田郡        1694     1201  -29.10
1   気仙沼・本吉圏  南三陸町  04606-0  本吉郡       17429    11876  -31.86
5       石巻圏   女川町  04581-1  牡鹿郡       10051     6200  -38.31
```


## 時系列データをまとめる pd.date_range  groupby
2022年5月27日から始まる10日分の日付のデータをpd.date_rangeで作成します。
作成したデータに値のカラムを追加して、1~5の値をランダムに作成します。
作成したデータをgroupbyを使って月ごとの平均値を算出します。

``` python:in
dates = pd.date_range(start="2022-05-27",periods=10)
print(dates)

np.random.seed(100)
df = pd.DataFrame(np.random.randint(1,5,10),index=dates,columns=["値"])
print(df)

m_df = df.groupby(pd.Grouper(freq='M')).mean()
print(m_df)
```

``` :out
DatetimeIndex(['2022-05-27', '2022-05-28', '2022-05-29', '2022-05-30',
               '2022-05-31', '2022-06-01', '2022-06-02', '2022-06-03',
               '2022-06-04', '2022-06-05'],
              dtype='datetime64[ns]', freq='D')

            値
2022-05-27  1
2022-05-28  1
2022-05-29  4
2022-05-30  4
2022-05-31  4
2022-06-01  4
2022-06-02  1
2022-06-03  3
2022-06-04  3
2022-06-05  1

              値
2022-05-31  2.8
2022-06-30  2.4
```

ランダムな値に対して、月曜日の日付でグループ化して、週ごとの合計値(日曜から月曜まで)を出力する場合には以下のようにします。

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

np.random.seed(123)

#2020-01-01から31日分の行に対して1~10の数字をランダムに割り当て
dates = pd.date_range(start="2020-01-01", periods=31)
df = pd.DataFrame(np.random.randint(1, 10, 31), index=dates, columns=["rand"])
print(df)

#月曜日の日付で週ごとの合計値を出力
df_year = pd.DataFrame(df.groupby(pd.Grouper(freq='W-MON')).sum(), columns=["rand"] )
print(df_year) 
````

``` :out
            rand
2020-01-01     3
2020-01-02     3
2020-01-03     7
2020-01-04     2
2020-01-05     4
2020-01-06     7
2020-01-07     2
2020-01-08     1
2020-01-09     2
2020-01-10     1
2020-01-11     1
2020-01-12     4
2020-01-13     5
2020-01-14     1
2020-01-15     1
2020-01-16     5
2020-01-17     2
2020-01-18     8
2020-01-19     4
2020-01-20     3
2020-01-21     5
2020-01-22     8
2020-01-23     3
2020-01-24     5
2020-01-25     9
2020-01-26     1
2020-01-27     8
2020-01-28     4
2020-01-29     5
2020-01-30     7
2020-01-31     2
            rand
2020-01-06    26
2020-01-13    16
2020-01-20    24
2020-01-27    39
2020-02-03    18
```

## 欠損値処置 dropna fillna 
データフレームにしたとき、データが入っていないとNaNと表示されます。
このようなデータに対して欠損値処理を行うことでデータを扱いやすくすることができます。
欠損値の処理方法には以下があります。

|  処理方法 |  メソッド  |
| ---- | ---- |
|  行の削除  | df.dropna() |
|  前方の値で補完  |  df.fillna(method='ffill')  |
|  後方の値で補完  |  df.fillna(method='bfill')  |
|  0などの任意の値で補完  |  df.fillna(0)  |
|  平均値で補完  |  df.fillna(df.mean())  |
|  中央値で補完  |  df.fillna(df.median)  |
|  最頻値で補完  |  df.fillna(df.mode().iloc[0,:])  |
|  欠損値か銅貨の判定  |  df.isnull()  |

``` python:in
df = pd.DataFrame(np.arange(12).reshape((4,3)),
                  columns=["A","B","C"],
                  index= ["1","2","3","4"])
df["B"]["2"] = np.nan
df["B"]["4"] = np.nan
print(df)

df_drop = df.dropna()
print(df_drop)

df_zero = df.fillna(0)
print(df_zero)

df_mean = df.fillna(df.mean())
print(df_mean)
```

``` :out
   A    B   C
1  0  1.0   2
2  3  NaN   5
3  6  7.0   8
4  9  NaN  11

   A    B  C
1  0  1.0  2
3  6  7.0  8

   A    B   C
1  0  1.0   2
2  3  0.0   5
3  6  7.0   8
4  9  0.0  11

   A    B   C
1  0  1.0   2
2  3  4.0   5
3  6  7.0   8
4  9  4.0  11
```

## データの連結　pd.concat
データフレームを連結するにはpd.concatを使います。
・列方向(カラム方向)に連結する場合には以下のように行います。
``` python:in
dates_a = pd.date_range(start="2022-05-27",periods=10)
np.random.seed(100)
df_a = pd.DataFrame(np.random.randint(1,5,10),index=dates_a,columns=["値１"])
df_b = pd.DataFrame(np.random.randint(1,5,10),index=dates_a,columns=["値２"])
df_merged = pd.concat([df_a,df_b],axis=1)
print(df_merged)
```

``` :out
            値１  値２
2022-05-27   1   3
2022-05-28   1   2
2022-05-29   4   3
2022-05-30   4   3
2022-05-31   4   3
2022-06-01   4   3
2022-06-02   1   2
2022-06-03   3   1
2022-06-04   3   1
2022-06-05   1   4
```

・行方向(インデックス方向)に連結する場合には以下のように行います。
5/27から3日分のデータと6/27から3日分のデータを連結しています。

``` python:in
dates_a = pd.date_range(start="2022-05-27",periods=3)
np.random.seed(100)
df_a = pd.DataFrame(np.random.randint(1,5,3),index=dates_a,columns=["値"])

dates_b = pd.date_range(start="2022-06-27",periods=3)
np.random.seed(101)
df_b = pd.DataFrame(np.random.randint(1,5,3),index=dates_b,columns=["値"])

df_merged = pd.concat([df_a,df_b],axis=0)
print(df_merged)
```

``` :out
            値
2022-05-27  1
2022-05-28  1
2022-05-29  4
2022-06-27  4
2022-06-28  4
2022-06-29  2
```

## データフレームの統計量の取得 max min mode mean median std 
データフレームのカラムを指定して、その要素の情報を取得します。
以下は値の内容を見て、最大値、最小値、最頻値、平均値、中央値、標準偏差を出力しています。
describeを使って統計情報を一括表示することもできます。

``` python:in
dates = pd.date_range(start="2022-05-27",periods=10)
np.random.seed(100)
df = pd.DataFrame(np.random.randint(1,5,10),index=dates,columns=["値"])
print(df)

print(df.max()) #最大値
print(df.min()) #最小値
print(df.mode()) #最頻値
print(df.mean()) #平均値
print(df.median()) #中央値
print(df.std()) #標準偏差
print(df.describe()) #統計量を一括表示
```

``` :out
値    4
dtype: int32

値    1
dtype: int32

   値
0  1
1  4

値    2.6
dtype: float64

値    3.0
dtype: float64

値    1.429841
dtype: float64

               値
count  10.000000
mean    2.600000
std     1.429841
min     1.000000
25%     1.000000
50%     3.000000
75%     4.000000
max     4.000000
```

## データフレームをNumPy配列に変換 values
データフレームの変数に格納した宮城県の市区町村のデータをndarray形式に変換して出力します。
NumPy配列に変換するとカラムやインデックスの情報がなくなります。

``` python:in
url = "https://ja.wikipedia.org/wiki/%E5%AE%AE%E5%9F%8E%E7%9C%8C"
tables = pd.read_html(url)
df = tables[4].loc[:,["市区町村","増減率(%)"]]
print(df)

nd = df.loc[:,["市区町村","増減率(%)"]].values
print(nd)
```

``` :out
    市区町村  増減率(%)
0   気仙沼市  -19.47
1   南三陸町  -31.86
2    登米市  -11.72
　 (中略)
37   白石市  -14.80
38   蔵王町  -13.62
39  七ヶ宿町  -29.10

[['気仙沼市' -19.47]
 ['南三陸町' -31.86]
 ['登米市' -11.72]
　(中略)
 ['白石市' -14.8]
 ['蔵王町' -13.62]
 ['七ヶ宿町' -29.1]]
```