LoginSignup
1
2

先頭行がそのままヘッダーにならない、若干エクセル風味の CSV ファイルを Python で前処理する

Last updated at Posted at 2023-12-17

一行まとめ

先頭行をそのままヘッダー(列名)として扱えない CSV データの前処理をした。

背景

Python でデータ分析をする練習用のデータとして、独立行政法人統計センターが提供している SSDSE(教育用標準データセット)を利用したい。

しかし、SSDSE-市区町村(SSDSE-A)をダウンロードしたところ、そのままでは扱いづらいデータであることがわかった。

image.png

(CSV データなんだけど、若干のエクセル味を感じる…!)

解決したい問題

  • 先頭行が正しい項目名になっていない
  • 年度が一つの行になっており、そのままでは集計できない

対応(2023-12-23 更新)

いただいたコメントを参考にしてコードをコンパクトにできたので更新分を載せます。

import pandas as pd

id_cols = ['地域コード', '都道府県', '市区町村']

df = pd.read_csv('SSDSE-A-2023.csv', encoding='cp932', header=[1,2])
df = pd.melt(
    df, id_vars=list(df.columns[:3]), 
    var_name=['年度', '項目'], value_name='')
df.columns = [*id_cols, *df.columns[3:]]

image.png

header をリストで指定することで列を MultiIndex にしています。
MultiIndex はそのままだとちょっと扱いづらいので、id_varsなどは少しまどろこっしい書き方になってます。

感想

だいぶスッキリしました。
マルチカラムの扱いは相変わらず自信ないですが。


以下、アーカイブとして.

対応(過去分)

解決したい問題

  • 先頭行が正しい項目名になっていない
  • 年度が一つの行になっており、そのままでは集計できない

戦略

  • 列の階層構造を分解して縦持ちデータ(地域-項目-年度ごとに一行)にすることを目指す

  • 必要な情報を一旦文字列で結合して、あとで分解する

対応(2023-12-23 更新)

いただいたコメントを参考にしてコードをコンパクトにできたので更新分を載せます。

単純にデータを読み込んだ場合の結果

image.png

データの前処理をした結果

image.png

実行した Python のコード

import pandas as pd

FILE_PATH = 'あなたの/ファイルパス/を入れてください'

LOCAL_CODE = '地域コード'
PREFECTURE = '都道府県'
MUNICIPALITY = '市区町村'
ITEM_YEAR = '項目名(年度)'
ITEM_CODE = '項目コード'
ITEM_NAME = '項目名'
YEAR = '年度'
AMOUNT = ''

df = pd.read_csv(FILE_PATH, encoding='shift-jis', header=None)
df.iloc[0:2,:3] = '' # 表の左上ゾーンの NaN が邪魔なので '' で埋めておく

# ヘッダーとしてまとめたい情報を抜き出す
item_code_row = df.iloc[0,:]
year_row = df.iloc[1,:]
item_name_row = df.iloc[2,:]

# 一旦文字列として結合してあとで分解する
_header = item_code_row + ',' + item_name_row + ',' + year_row
_header = _header.str.strip(',') # 地域コードなどは分割しないので , を削っておく
df.columns = _header

# ヘッダーの素材になった行を削除しておく
df = df.iloc[3:,]

# [地域-項目名-年度] が一行ずつになるようにデータを変換する
id_cols = [LOCAL_CODE, PREFECTURE, MUNICIPALITY]
_var_col = 'variable' # データ加工のための作業列

df = pd.melt(df, id_vars=id_cols, var_name=_var_col, value_name=AMOUNT)

df[ITEM_CODE] = df[_var_col].apply(lambda x: x.split(',')[0])
df[ITEM_NAME] = df[_var_col].apply(lambda x: x.split(',')[1])
df[YEAR] = df[_var_col].apply(lambda x: x.split(',')[2])
df = df[[*id_cols, ITEM_CODE, ITEM_NAME, YEAR, AMOUNT]] # 列の並び替え
df

感想

単純な問題のようで、実際には前処理にかなり手こずった。
とはいえ、利用可能なデータが公的に供給されていることはありがたい。
先頭行をそのままヘッダーに使えないデータは今後もしばしば出くわしそうなので、
今後こういうケースがあったら今回の知見を活かしたい。
(もうちょとスマートな解決方法もありそうだけど、まあ前処理は多少泥臭くてもやるものなので…)

Appendix

データの変形過程

実行環境は VSCode の Interactive Window.

df = pd.read_csv(FILE_PATH, encoding='shift-jis', header=None)
df.iloc[0:2,:3] = '' # 表左上ゾーンの NaN が邪魔なので '' で埋めておく
df

image.png

# ヘッダーとしてまとめたい情報を抜き出す
item_code_row = df.iloc[0,:]
year_row = df.iloc[1,:]
item_name_row = df.iloc[2,:]

# 一旦文字列として結合して、あとで分解する戦略を取る.
_header = item_code_row + ',' + item_name_row + ',' + year_row
_header = _header.str.strip(',') # 地域コードなどは分割しないので , を削っておく
df.columns = _header
df

image.png

# header の素材になった行を削除しておく
df = df.iloc[3:,]

# [地域 - 項目名 - 年度] が一行ずつになるようにデータを変換する
id_cols = [LOCAL_CODE, PREFECTURE, MUNICIPALITY]
_var_col = 'variable'

df = pd.melt(df, id_vars=id_cols, var_name=_var_col, value_name=AMOUNT)
df

image.png

# 文字列で結合された列を分解してごにょごにょする
df[ITEM_CODE] = df[_var_col].apply(lambda x: x.split(',')[0])
df[ITEM_NAME] = df[_var_col].apply(lambda x: x.split(',')[1])
df[YEAR] = df[_var_col].apply(lambda x: x.split(',')[2])
df = df[[*id_cols, ITEM_CODE, ITEM_NAME, YEAR, AMOUNT]] # 列の並び替え
df

image.png

1
2
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2