Pythonを使ったテーブルデータの集計作業メモ

テーブルデータに対する、Pythonを使った基本的な作業のメモを書いていきます。

---
以下の様な何の変哲もないsample_result.csvというCSVファイルに対し、基本的な集計・可視化作業を行っていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/d52521e4-3d92-1385-f96b-eb3baa0205a9.png)


---

```python:
#ライブラリをインポート。
import pandas as pd
import matplotlib.pyplot as plt
import os
```


```python:
#CSVファイルを読み込み。
df = pd.read_csv('data/sample_result.csv')
#エンコーディングスタイルを指定する時はdf = pd.read_csv('data/sample_result.csv', encoding="SHIFT-JISx0213")の様に書く。
display(df)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/2b1f6a8e-ed9e-00ec-8583-3f1a51b21d42.png)

元のcsvファイルのクラス名が001などになっている場合は、0落ち防止のため以下の様に書きます。

```python:
df = pd.read_csv('data/sample_result.csv',dtype= {'クラス':str})
display(df)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/dc14ffbe-5e49-a197-7c29-544da2d5215e.png)


今回の場合はたった5列しかありませんが、大抵表示しきれないくらい沢山の列がありますのでどんな列があるのかチェックしておきます。

```python:
#列名チェック
df.columns
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/b69a5e9c-c814-8861-da82-74757227a7bb.png)
これでも全部の列名が表示されない時は以下の様に書きます。

```python:
list(df.head(0))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/e1072da6-3075-7caf-7e57-ccc83f732e69.png)

```python:
#読み込んだファイル名を表示
name = os.path.basename('data/sample_result.csv')
print(name)
```
これは特に意味のない作業の様に見えますが、新しくcsvファイルを作成する時、その名前をつける際にこのnameを元にすることもあります。


```python:
x = df['受験者']
y = df['数学']
```

```python:
print("数学の平均点は"+str(round(sum(y))/len(y))+"です。")
```
このコードを実行すると、下の様な文章が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/c183af81-2144-b6a9-6fff-1b740a65d373.png)

元のcsvファイルにある列をindexとして使う時は以下の様に書きます。

```python:
df = pd.read_csv('data/sample_result.csv', index_col='受験者')
display(df)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/b32ef1f4-425c-e675-c372-c2516c4b741d.png)

数学の点数の一部だけを取り出し、再びテーブルにしてみます。
```python:
math_scores = np.array(df['数学'])[:3]
math_scores_df = pd.DataFrame({'数学の点数':math_scores},
                             index = pd.Index(['A','B','C'],
                                             name = '受験者'))
math_scores_df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/36183795-1b2e-44d1-693d-578a6aa9d402.png)

標準化や偏差値を求めてみましょう。
```python:
#標準化
Z = (math_scores - np.mean(math_scores))/np.std(math_scores)
Z
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/87ebd387-d8fd-15b4-e479-17088665e3b5.png)


```python:
#偏差値
Deviation_value = 50 + 10 * Z
Deviation_value
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/0c03f9cb-b888-d93d-af20-34b6dbda91d6.png)


## 基本的な可視化

さて、次に棒グラフを描いて見ましょう。

```python:
plt.bar(x,y)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/146fa8a3-4b1e-ab60-798e-69a526b38e72.png)

そうすると、皆さんご存知の通り(?)文字化けします。
そこで以下の様なコードを使います。

```python:
!pip install japanize-matplotlib
import japanize_matplotlib
```
※自分の場合はこの方法で上手くいきましたが、他の方法もネットで検索すると色々出てきます。

これで先ほどのコードをもう一度実行すると、今度はきちんと文字が現れました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/602a0c8f-f011-c8a1-26b9-19f6b202773a.png)

タイトルや軸名を加えて、図のサイズも変えてみましょう。

```python:
plt.figure(figsize=(15,6))
plt.bar(x,y)
plt.title("数学の成績")
plt.xlabel("受験者")
plt.ylabel("点数")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/772c18a5-5d03-c619-8c41-8326ca0d9cf6.png)

ヒストグラムも描いてみましょう。

```python:
plt.hist(df["英語"])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/e641fbaf-697c-555f-f058-d072fdd5b7aa.png)

散布図も描いてみましょう。

```python:
z = df['英語']
plt.scatter(y,z)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/b951dd63-c966-cd01-d10a-81938b21a97a.png)

グリッドを表示してみましょう。

```python:
z = df['英語']
plt.grid(True)
plt.scatter(y,z)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/2ee32945-5664-35f3-7279-0cc9d253bff3.png)


回帰直線を描いてみましょう。

```python:
import numpy as np

y = df['数学']
z = df['英語']

poly_fit=np.polyfit(y,z,1)
poly_1d=np.poly1d(poly_fit)

plt.scatter(y,z)
plt.plot(y,poly_1d(y))

#ついでに画像ファイルを保存。
plt.savefig("test.png")
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/a7298a1c-6e0e-f687-ff15-d784c63d81bf.png)


因みに以下の様に書くと背景が黒くなります。

```python:
z = df['英語']
#先に書かないとダメ。
plt.style.use("dark_background")
plt.scatter(y,z)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/5a7d10a6-ba1d-4fa4-df28-03ce6c81f87e.png)


plt.xkcd()を使えば手書き風の味のあるグラフになりますが、元に戻すのが凄い大変なようなのでおススメはしません。

```python:
z = df['英語']
plt.xkcd()
plt.scatter(y,z)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/9811b671-e189-078b-bab9-aeb4afa6d349.png)

## 重回帰分析
いきなり話がちょっと高度になりますが、以下の様に書けばお手軽に重回帰分析が出来ます。
```python:
import statsmodels.formula.api as smf
formula = '国語 ~ 英語 + 数学'
result = smf.ols(formula, df).fit()
result.summary()
```


## 文字型を指定したいとき

例えば実数型にしたい時は以下の様に書きます。

```python:
X=df['英語']
X=X.astype('float')
X
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/dfbdc345-7171-94e7-8631-2edc662ccb9e.png)

整数を実数に直す時より、どう見ても数なのにPythonが文字だと言い張る時によくこのコードを使います。

## データの抽出や整理

特定の列だけ取り出すには、以下の様に書きます。

```python:
A = df.loc[:,['受験者','英語']]
display(A)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/92f60861-33f1-084a-5f52-bd55684145a2.png)


ここでちょっと保存してみましょう。最初の方で読み込んでおいたファイル名のnameを使います。

```python:
name_split = name.split(".")
name_1 = name_split[0]
A.to_csv('data/'+name_1+'_A.csv', encoding="SHIFT-JISx0213", index=False)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/298940c5-6e41-186d-045a-f4157f4ab213.png)


特定の行だけ取り出すのは以下の様になります。

```python:
P = df.loc[[1,3],:]
display(P)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/aea70ce9-d09e-fbc5-87c3-186d8dc2d7ad.png)


ある行からある行までをまとめて取り出すには以下の様にします。

```python:
display(df[2:5])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/a7a41f13-2eb0-d4d9-5522-c19e016415ab.png)


次は国語の成績順に並び替えてみましょう。

```python:
#B=を忘れずに。
B = df.sort_values(['国語'],ascending=[False])
display(B)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/ab0e56ef-d38b-348b-06c9-18f470687d10.png)


「1組で、国語が70点より上」のデータを取り出す場合は以下の様になります。

```python:
C = df.query('クラス == ["1組"] & 国語 > 70')
display(C)
#あくまで同じテーブル内の情報しか使えません。他のテーブル内のカラム名をクエリ内に書いてもダメ。
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/c923451c-966d-1b7d-fc86-eab90ea1f588.png)

以下の様に書くこともできます。

```python:
C=df[(df['クラス']=='1組')&(df['国語']>70)]
display(C)
```
## 配列の結合

以下の様なsample2.csvというファイルの列を結合させてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/e6aee3c3-f0af-0c04-dfed-3cc2b5e1e42a.png)


```python:
#CSVファイルを読み込み。
df2 = pd.read_csv('data/sample2.csv',encoding="SHIFT-JISx0213")
#display(df2)
#デフォルトはhow='inner'。
X = pd.merge(df,df2, left_on=df['受験者'], right_on=df2['受験者名'],how="left")
X=X.drop(columns=['受験者名'])
X
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/ef310105-a3ac-9aa7-7187-55bc2a3e40cc.png)

次は以下の様なsample_result_2.csvというテーブルの情報を追加しましょう。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/f3eb652b-b5d8-ae14-283b-4bde259d2f6e.png)

```python:
#CSVファイルを読み込み。
df2 = pd.read_csv('data/sample_result_2.csv')
df.append(df2)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/8855c227-e766-5eb7-a2ba-1d7e2f0732d2.png)

---

カテゴリ変数の変換は以下の様に書きます。

```python:
X = pd.merge(df,pd.get_dummies(df['クラス']), on=df['受験者'], how="inner")
X = X.drop(columns=['クラス'])
X
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/d20b3e68-03e5-4ab8-067b-6f0f8f1a4b17.png)

クラスごとの平均点を見るには以下の様に書きます。このテーブルをcsvファイルで出力する時にindex=Falseとしてしまうとクラスの列が消えてしまいます。

```python:
df.groupby('クラス').mean()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/d45611dd-7ac7-0f27-a9d9-792ab5a53490.png)

小数点以下の桁数を制限するには以下の様に書きます。

```python:
pd.set_option('precision',3)
```

---

教科ごとの相関係数を見てみましょう。

```python:
import seaborn as sns
#cmap ='coolwarm'などもアリ。
sns.heatmap(df.corr(), cmap='Blues',annot = True)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/52de0171-3a1c-61c5-554c-498cd7ae643c.png)

seabornを使えばいっぺんに全部の組み合わせの散布図も描けます。

```python:
sns.pairplot(df)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/fccc4249-d77c-8204-80d7-131b24376247.png)



全体の基本的な統計量を表示してみましょう。

```python:
df.describe()
#引数を指定することも可能。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/d6402493-981e-7490-396a-f251f61671b5.png)


意外とあまり本に載っていませんが、空欄の列を作るには以下の様に書きます。

```python:
df['合計'] = ''
display(df)

#T = df['合計'] = '',print(T)と書いてもダメ。
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/4d3c1ac5-d306-a0d5-720f-7e058d2d0ba0.png)


ついでに合計点の計算。

```python:
#合計点の計算
i = 0
while i < len(df):
    df['合計'][i] = df['国語'][i] + df['英語'][i] + df['数学'][i]
    i = i + 1
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/61f2e91a-5d18-238f-a6d6-95040ca0476d.png)

と思ったら以下の様な単純なコードで大丈夫でした。

```python:
df['合計'] = df['国語'] + df['英語'] + df['数学']
df
```

列名の変更は以下の様に書きます。

```python:
df = df.rename(columns={'合計':'合計点'})
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/b251e13c-91e9-7006-7f07-1c72443dcfb8.png)


## 複数のcsvファイルをまとめて処理するとき

以下の様にglobを使えばOKです。

```python:
#ライブラリをインポート。
import pandas as pd
import matplotlib.pyplot as plt
import os
import glob

#dataというフォルダ内にsample_result_1というcsvファイルとsample_result_2というcsvファイルがあるとする。
for doc in glob.glob('test/sample_result_*.csv'):

    #CSVファイルを読み込み。
    df = pd.read_csv(doc)
    display(df)

    #列名チェック
    print(df.columns)

    #読み込んだファイル名を表示
    name = os.path.basename(doc)
    print(name)

    x = df['受験者']
    y = df['数学']

    print("数学の平均点は"+str(round(sum(y))/len(y))+"です。")
```
## 欠損値があるとき

下のテーブルをdfとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/4c15d567-6636-5eeb-813d-df5923caa04b.png)

欠損値を含む行全てを削除するとき。

```python:
#ドロップエヌエーと読む。
X = df.dropna()
X
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/16dea617-06e6-121f-7c21-cd2902f6816c.png)

特定の列に欠損値を含む行を削除するとき。

```python:
X = df.dropna(subset=['国語'])
X
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/01b9c5f4-f207-ebf1-d907-2090acc27a46.png)

ただしこのような操作を行うと、見てわかる通りindexもそれに合わせて欠けてしまい、例えばこの後にX['国語'][1]などと入力してもエラーになってしまいます。ですので、以下の様にindexを振り直します。

```python:
X=X.reset_index()
```

特定の列に欠損値を含む行だけ抽出するとき。

```python:
X=df[df['英語'].isnull()]
X
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359600/cae28a72-6c79-96b2-db1b-aae052e89f28.png)