(学習ログ)Python008:複数CSVを結合して月次列を作る基本ワークフロー
1. はじめに
-
この記事の目的
複数のCSVファイルを読み込み、縦結合・横結合(JOIN)し、金額列の計算や日付の整形(年月列の作成)までを身につけます。 -
この記事の概要
pandas.read_csv → concat → merge → 計算列追加 → 日付型変換 → 月次列生成
という、実務で頻出の一連の流れを解説します。
2. サンプルコード
コード全文
import pandas as pd
sample_transaction1 = pd.read_csv('001_サンプル/sample_transaction_data1.csv')
sample_transaction2 = pd.read_csv('001_サンプル/sample_transaction_data2.csv')
sample_transaction = pd.concat([sample_transaction1, sample_transaction2], ignore_index=True)
sample_transaction_detail1 = pd.read_csv('001_サンプル/sample_transaction_detail1.csv')
sample_transaction_detail2 = pd.read_csv('001_サンプル/sample_transaction_detail2.csv')
sample_transaction_detail = pd.concat([sample_transaction_detail1, sample_transaction_detail2], ignore_index=True)
sample_join_data = pd.merge(sample_transaction_detail, sample_transaction[["transaction_id", "payment_date", "customer_id"]], on='transaction_id', how="left")
sample_file = pd.read_csv('001_サンプル/sample_file.csv')
sample_join_data = pd.merge(sample_join_data, sample_file, on="customer_id", how="left")
sample_item_list = pd.read_csv('001_サンプル/sample_item_list.csv')
sample_join_data = pd.merge(sample_join_data, sample_item_list, on="item_id", how="left")
sample_join_data["price"] = sample_join_data["quantity"] * sample_join_data["item_price"]
sample_join_data.dtypes
sample_join_data["payment_date"] = pd.to_datetime(sample_join_data["payment_date"])
sample_join_data["payment_month"] = sample_join_data["payment_date"].dt.strftime("%Y%m")
sample_join_data[["payment_date", "payment_month"]].head()
ブロックごとの解説
-
import pandas as pd
- データ加工用ライブラリ pandas を読み込みます。
-
pd.read_csv(...)
×4- 取引(transaction)と明細(transaction_detail)を ファイルごとに読み込み ます。ファイルが2つずつある前提。
-
pd.concat([...], ignore_index=True)
×2- 同じ列構成のデータを縦に結合します。
ignore_index=True
で新しい連番インデックスを振り直します。 -
sample_transaction
とsample_transaction_detail
が完成。
- 同じ列構成のデータを縦に結合します。
-
pd.merge(..., on='transaction_id', how='left')
- 明細に対して、取引の一部列(
transaction_id
,payment_date
,customer_id
)だけを 左結合 します。 -
なぜ
[[...]]
で列指定?
df[["col1","col2"]]
は DataFrame(2列以上)を返すため、結合の相手として安全です。df["col"]
は Series(1列)になりがちで、複数列を渡したい今回は不適切。
- 明細に対して、取引の一部列(
-
pd.read_csv('.../sample_file.csv')
→merge(..., on="customer_id")
- 顧客マスタ(例:顧客名・属性など)を 顧客ID で横結合します。
-
pd.read_csv('.../sample_item_list.csv')
→merge(..., on="item_id")
- 商品マスタ(例:商品名・単価など)を 商品ID で横結合します。
-
sample_join_data["price"] = quantity * item_price
- 計算列(金額)を追加します。型が数値でないとエラーや不正値になるので注意。
-
sample_join_data.dtypes
- 各列のデータ型を確認します(ここでは表示だけ。実務では
print
かinfo()
を併用)。
- 各列のデータ型を確認します(ここでは表示だけ。実務では
-
to_datetime
とdt.strftime("%Y%m")
- 文字列の
payment_date
を日付型に変換し、YYYYMM
形式の 月次キー列 を作成します。
- 文字列の
-
sample_join_data[["payment_date", "payment_month"]].head()
- 変換結果を先頭5行だけ確認します。
実行結果例
dtypes の一例
transaction_id int64
item_id int64
quantity int64
payment_date datetime64[ns] # ← 後で to_datetime 済み
customer_id string
item_price float64
customer_name string
item_name string
price int64
payment_month string
dtype: object
sample_join_data[["payment_date","payment_month"]].head()
の一例
payment_date payment_month
0 2024-07-01 202407
1 2024-07-15 202407
2 2024-08-03 202408
3 2024-08-03 202408
4 2024-09-20 202409
price
の一例
0 1980 # quantity=2, item_price=990.0
1 9900 # quantity=10, item_price=990.0
2 1500 # quantity=3, item_price=500.0
3 3000 # quantity=2, item_price=1500.0
4 2480 # quantity=1, item_price=2480.0
Name: price, dtype: int64
3. エラーと確認のポイント
代表的なエラー文
FileNotFoundError: [Errno 2] No such file or directory
-
原因:パスやファイル名の誤り。相対パスの基準が想定と違う。
-
対処:
import os print(os.getcwd()) # 現在の作業ディレクトリ os.listdir('001_サンプル') # フォルダ内のファイル確認
- 必要に応じて絶対パスに変更、Colabなら
/content/drive/MyDrive/...
を確認。
- 必要に応じて絶対パスに変更、Colabなら
- 文字化け/
UnicodeDecodeError
-
原因:文字コードが合っていない(例:UTF-8想定だが実際はSHIFT-JIS)。
-
対処:
pd.read_csv('file.csv', encoding='cp932') # 例: Windows-Excel系
ValueError: unknown string format
(to_datetime
での失敗)
-
原因:日付文字列の形式が混在・不正値を含む。
-
対処:
pd.to_datetime(df['payment_date'], errors='coerce', format='%Y-%m-%d') # coerce で不正値は NaT に。format を指定して頑健化
- 結合後に想定外の NaN が大量発生(エラーにならない“事故”)
-
原因:結合キーの 型不一致(例:片方 int64、もう片方 string)。
-
確認&対処:
df1['transaction_id'].dtype, df2['transaction_id'].dtype df2['transaction_id'] = df2['transaction_id'].astype('int64') # 片方に寄せる
- 前後で
value_counts()
やnunique()
を見て、結合キーが妥当かチェック。
- 前後で
- 重複キーによる件数“爆増”(多対多結合)
-
原因:
transaction_id
やcustomer_id
が片方または双方で重複。 -
確認&対処:
sample_transaction_detail['transaction_id'].duplicated().any() # True/False sample_transaction_detail['transaction_id'].value_counts().head() # 必要なら明細のユニーク化、または集計してから結合
price
が思った型にならない / 小数が混じる
-
原因:
item_price
が float、quantity
が string など。 -
対処:
df['quantity'] = pd.to_numeric(df['quantity'], errors='coerce').fillna(0).astype('int64') df['item_price'] = pd.to_numeric(df['item_price'], errors='coerce') df['price'] = (df['quantity'] * df['item_price']).round().astype('Int64') # 必要なら丸め
よく使う確認メソッドや出力例
# 形と列
sample_join_data.shape # (行数, 列数) 例: (1250, 12)
sample_join_data.columns.tolist() # 列名一覧
# データ型・メモリ
sample_join_data.dtypes
sample_join_data.info() # 欠損や型の俯瞰に便利
# 先頭/末尾/ランダム
sample_join_data.head(3)
sample_join_data.tail(3)
sample_join_data.sample(3, random_state=0)
# 欠損・一意性
sample_join_data.isna().sum().sort_values(ascending=False)
sample_join_data['transaction_id'].nunique()
# キーの重複や結合検証
sample_join_data['transaction_id'].duplicated().any()
sample_join_data.merge(
sample_transaction[['transaction_id']].drop_duplicates(),
on='transaction_id', how='left', indicator=True
)['_merge'].value_counts()
列選択の豆知識
df["col"]
→ Series(1列)df[["col1","col2"]]
→ DataFrame(複数列)
結合の相手や複数列の前処理には 後者 を使うのが安全です。
読み込み時に品質を上げる引数(例)
pd.read_csv(
'file.csv',
dtype={'customer_id': 'string', 'transaction_id': 'Int64'},
usecols=['transaction_id', 'payment_date', 'customer_id'],
parse_dates=['payment_date'], # 可能なら読み込み時に日付化
na_values=['', 'NA', 'NULL'],
encoding='utf-8' # 必要に応じて 'cp932' など
)
4. まとめ
-
縦結合(
concat
)→ 横結合(merge
)→ 計算列 → 日付整形 の実務基本フローを一通り実装できるようになった。 -
[[...]]
での複数列選択、キーの型不一致、重複キー といった“ハマり所”を、確認メソッド(dtypes
,info
,isna
,nunique
,duplicated
)で検証する習慣が持てた。 - 読み込み時の
dtype
/usecols
/parse_dates
/encoding
指定で、後続処理の安定性とパフォーマンスを高めるコツを理解した。
Discussion