mecobalamin’s diary

人間万事塞翁が馬

https://help.hatenablog.com/entry/developer-option

CSVファイルから数の集計をする

以前人口ピラミッドのグラフを作成した
mecobalamin.hatenablog.com

同様なグラフを作成するに当たり、以下のようなリストから男女別と年代別に集計をしたい

	居住地, 年代, 性別, 死亡確認	
死亡例1例目, 非公表, 70代. 非公表, 非公表	
死亡例2例目, 非公表, 50代, 男性, 2020年4月19日, 4月7日入院。酸素吸入開始。4月19日死亡確認。死因調査中
死亡例3例目, 那覇市, 80代, 男性, 2020年4月19日, 4月9日入院。4月12日ICUに転院。4月19日死亡確認。死因調査中
死亡例4例目, 中部保健所管内, 70代, 女性, 2020年4月22日, 4月22日死亡確認。死因調査中
死亡例5例目, 中部保健所管内, 70代, 男性, 2020年4月29日, 4月10日発熱。4月12日PCR検査にて陽性を確認、入院。4月29日死亡確認。死因調査中

集計後の出力は以下の通り

man worman undisclosed
90 + 71.0 143.0 NaN
80 - 89 127.0 95.0 1.0
70 - 79 90.0 37.0 NaN
60- 69 43.0 19.0 NaN
50 - 59 15.0 6.0 NaN
40 - 49 6.0 3.0 NaN
30 - 39 3.0 1.0 NaN
20 - 29 NaN NaN NaN
10 - 19 NaN NaN NaN
0 - 9 NaN NaN NaN
undisclosed 1.0 NaN 16.0

やることは

  1. CSVファイルを読み込んでデータフレームに変換
  2. 文字の変換
  3. 性別、年齢別に集計

1. CSVファイルを読み込んでデータフレームに変換
CSVを読み込むコードはよく使うので関数にしてある
ファイルのパスとファイル名を引数にした
ファイル名はリストになっている
CSVの読み込みはpandasのread_csvを使用し、1列目をインデックスにした
読み込んだCSVを空のデータフレームに結合して変数を返す

def Read_CSV_Files(Path_CSV, List_CSV):
    df = DataFrame()
    for i in List_CSV:
        try:
            df_tmp = pd.read_csv(Path_CSV + i, index_col = 0, encoding = 'shift_jis')
        except Exception as e:
            print('cannot open file: ', i)
            print(str(e))
            sys.exit(1)
        else:
            print('we were able to open the file:', i)
            df = pd.concat([df, df_tmp])
    return df

2. 文字の変換
読み込んだCSVの3列目の型を文字列からpandasのdatetime形式に変換する
日付が"非公表"の場合変換できずにエラーになるため、nanに変換して行ごと削除(dropna())する

    df.iloc[:, 3] = df.iloc[:, 3].replace({'非公表': np.nan}).replace({'確認中': np.nan}).dropna()
    df.iloc[:, 3] = pd.to_datetime(df.iloc[:, 3], format = '%Y年%m月%d日')

3. 性別、年齢別に集計
実行環境ではグラフの出力に2バイト文字を使えないため漢字をアルファベットに変換している
まず性別だけを抜き出し、"Count_age()"で年齢ごとの数をカウントする

    df_age = pd.DataFrame()
    for i in ['man', 'woman', 'undisclosed']:
        df_tmp = df
        df_tmp = df_tmp.replace({'男性': 'man', '女性': 'woman', '非公表': 'undisclosed'})
        df_tmp = df_tmp[df_tmp['性別'].isin([i])]
        df_age = pd.concat([df_age, Count_age(df_tmp.iloc[:, 1])], axis = 1)

    df_age.columns = ['man', 'woman', 'undisclosed']

Count_age()はデータフレームを引数にして、インデックスの年代ごとに数をカウントする

def Count_age(df):
    df_index = {"90歳以上": "90 +", '80代': "80 - 89", '70代': "70 - 79", '60代': "60- 69", '50代': "50 - 59", '40代': "40 - 49", '30代': "30 - 39", '20代': "20 - 29", '10代': "10 - 19", "10歳未満": "0 - 9", "非公表": "undisclosed"}
    df = df.replace(df_index)
    df = df.dropna().value_counts()
    df = df.reindex(index = list(df_index.values()))
    return df

4. コードのまとめ
コードをまとめると次のようになる

import os, sys, datetime
from pandas import Series, DataFrame
import pandas as pd
import numpy as np

def Count_age(df):
    df_index = {"90歳以上": "90 +", '80代': "80 - 89", '70代': "70 - 79", '60代': "60- 69", '50代': "50 - 59", '40代': "40 - 49", '30代': "30 - 39", '20代': "20 - 29", '10代': "10 - 19", "10歳未満": "0 - 9", "非公表": "undisclosed"}
    df = df.replace(df_index)
    df = df.dropna().value_counts()
    df = df.reindex(index = list(df_index.values()))
    return df

def Read_CSV_Files(Path_CSV, List_CSV):
    df = DataFrame()
    for i in List_CSV:
        try:
            df_tmp = pd.read_csv(Path_CSV + i, index_col = 0, encoding = 'shift_jis')
        except Exception as e:
            print('cannot open file: ', i)
            print(str(e))
            sys.exit(1)
        else:
            print('we were able to open the file:', i)
            df = pd.concat([df, df_tmp])
    return df

if __name__ == '__main__':

    path = os.getcwd()
    path = os.chdir(os.path.dirname(os.path.abspath(__file__)))
    path = os.getcwd()

    List_DeathList = ['hogehoge.csv']
    Path_CSV = path +  '\\' + 'csv\\'

    df = Read_CSV_Files(Path_CSV, List_DeathList)

    df.iloc[:, 3] = df.iloc[:, 3].replace({'非公表': np.nan}).replace({'確認中': np.nan}).dropna()
    df.iloc[:, 3] = pd.to_datetime(df.iloc[:, 3], format = '%Y年%m月%d日')

    df_age = pd.DataFrame()
    for i in ['man', 'woman', 'undisclosed']:
        df_tmp = df
        df_tmp = df_tmp.replace({'男性': 'man', '女性': 'woman', '非公表': 'undisclosed'})
        df_tmp = df_tmp[df_tmp['性別'].isin([i])]
        df_age = pd.concat([df_age, Count_age(df_tmp.iloc[:, 1])], axis = 1)

    df_age.columns = ['man', 'woman', 'undisclosed']
    print(df_age)

いちいちコードを書いたけど何かしらもっと便利な方法が他にあるとは思う