(学習ログ)Python005:複数CSVの結合と基本検証(concat/merge/列演算)
1. はじめに
-
この記事の目的
取引データを「複数CSVから読み込み → 縦結合(concat)→ キーで横結合(merge)→ 列演算 → 確認」という最小ルートで扱う方法を整理します。 -
この記事の概要
pandas.read_csvでデータを読み込み、取引(ヘッダ)と明細を結合して、商品マスタや顧客マスタを突き合わせ、最後に金額列を計算します。
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[["quantity", "item_price"]].head()
行ごと・ブロックごとの簡潔な説明
-
import pandas as pd
pandasをpdという短い別名で使えるようにします。 -
取引(ヘッダ)を読み込み → 縦結合
sample_transaction1 = pd.read_csv('...transaction_data1.csv') sample_transaction2 = pd.read_csv('...transaction_data2.csv') sample_transaction = pd.concat([sample_transaction1, sample_transaction2], ignore_index=True)-
read_csv: CSVをDataFrameとして読み込む -
concat([...], ignore_index=True): 縦方向の結合。複数ファイルを1つの表にまとめます。ignore_index=Trueで新しい連番インデックスに振り直します。
-
-
取引明細を読み込み → 縦結合
sample_transaction_detail1 = pd.read_csv('...transaction_detail1.csv') sample_transaction_detail2 = pd.read_csv('...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" )-
merge(..., on='transaction_id', how="left"): 左外部結合。左側(明細)にあるtransaction_idを軸に、右側(取引ヘッダ)のpayment_date・customer_idをくっつけます。 -
sample_transaction[["transaction_id", "payment_date", "customer_id"]]の二重角括弧は「複数列をDataFrameとして取り出す」書き方です(後述)。
-
-
顧客マスタを横結合
sample_file = pd.read_csv('001_サンプル/sample_file.csv') sample_join_data = pd.merge(sample_join_data, sample_file, on="customer_id", how="left")customer_idをキーに顧客属性(例:顧客名、エリア)を付与します。 -
商品マスタを横結合
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")item_idをキーに商品名や単価(item_price)を付与します。 -
列演算で金額を計算
sample_join_data["price"] = sample_join_data["quantity"] * sample_join_data["item_price"]行ごとに「数量×単価」を計算し、
price列に保存します。 -
一部列だけ先頭を確認
sample_join_data[["quantity", "item_price"]].head()計算に使った列をサッと点検。二重角括弧で「列のリスト」を渡していることに注目。
実行結果例
実行したときに読者が再現しやすいよう、想定形を示します。
-
sample_join_data.head()のイメージ
transaction_id item_id quantity item_price payment_date customer_id customer_name item_name price
0 1001 501 2 800 2024-03-01 C001 田中太郎 ノート 1600
1 1001 502 1 1200 2024-03-01 C001 田中太郎 ボールペン 1200
2 1002 503 3 500 2024-03-05 C002 鈴木花子 付箋 1500
3 1003 501 1 800 2024-03-10 C003 佐藤健 ノート 800
4 1004 504 5 300 2024-03-12 C001 田中太郎 クリップ 1500
- 最終行
sample_join_data[["quantity", "item_price"]].head()のイメージ
quantity item_price
0 2 800
1 1 1200
2 3 500
3 1 800
4 5 300
3. エラーと確認のポイント
3-1. 代表的なエラー文
-
FileNotFoundError: [Errno 2] No such file or directory:
指定パスが間違い/ファイルが存在しない。- 対策:相対パスの起点(実行ディレクトリ)を意識。
print(os.getcwd())で確認(※使うならimport osが必要)。
- 対策:相対パスの起点(実行ディレクトリ)を意識。
-
KeyError: 'transaction_id'/KeyError: 'item_price'など
結合キーや参照列がファイルに存在しない/表記揺れ(全角・半角、大小文字、余計な空白)。- 対策:
df.columnsで列名確認。前処理でdf.rename(columns={'取引ID':'transaction_id'})など統一。
- 対策:
-
ValueError: columns overlap but no suffix specified
merge時に同名列が衝突。- 対策:
pd.merge(..., suffixes=('_det','_trx'))を指定、または事前に不要列をdrop。
- 対策:
-
TypeError: unsupported operand type(s) for *: 'str' and 'int'
quantityやitem_priceが文字列型のまま計算している。- 対策:
df['quantity'] = pd.to_numeric(df['quantity'], errors='coerce')で数値化(欠損はNaNに)。
- 対策:
3-2. 「二重角括弧 [[...]] はなぜ?」
-
df['col']はSeries(1列)、df[['col']]はDataFrame(1列でも“表”)。 -
mergeの右側に渡すとき、複数列を確実にDataFrameとして切り出すために[['transaction_id','payment_date','customer_id']]のように二重角括弧を使います。 - 「1列でよいがDataFrameのまま扱いたい」ケース(後続の
mergeやconcatの整合性)でも[['col']]は有効です。
3-3. よく使う確認メソッドと出力例
-
形と欠損のざっくり把握
sample_join_data.shape # 例: (12543, 10) sample_join_data.isna().sum() # 列ごとの欠損数 sample_join_data.duplicated().sum() # 完全重複行の数 -
列名・型の確認
sample_join_data.columns sample_join_data.info()出力イメージ(抜粋):
<class 'pandas.core.frame.DataFrame'> RangeIndex: 12543 entries, 0 to 12542 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 transaction_id 12543 non-null int64 1 item_id 12543 non-null int64 2 quantity 12543 non-null int64 3 item_price 12543 non-null float64 4 payment_date 12543 non-null object # ※後述のparse_datesで日付型に ... -
値のサンプル確認
sample_join_data.sample(3, random_state=0) sample_join_data[['customer_id','item_id','price']].head(10)
3-4. ちょい上級の実務Tips
-
読み込み時に型・日付を最適化
pd.read_csv('file.csv', dtype={'customer_id':'string'}, usecols=[...], parse_dates=['payment_date'])メモリ削減・日付処理の安定化に有効。
-
結合ミスを早めに検知(ヒット率チェック)
before = len(sample_join_data) # 例:右側キーが欠けてNaNになった件数 miss = sample_join_data['payment_date'].isna().sum() print(f'payment_date 未付与: {miss} / {before}') -
同名列の衝突は明示的に回避
pd.merge(a, b, on='id', how='left', suffixes=('_a','_b')) -
インデックスを意識して更新
concat(..., ignore_index=True)は後段のiloc系操作を安定させます。
4. まとめ
-
read_csv → concat(縦)→ merge(横)→ 列演算 → headで確認という最小ルートを理解できた。 -
[['col1','col2']]の二重角括弧は「複数列をDataFrameで渡す」ためで、mergeの相手に安全。 - 典型的なエラー(
FileNotFoundError/KeyError/ 型エラー / 列衝突)と基本の確認メソッド(shape/info/isna().sum()/duplicated().sum()/head/sample)をセットで押さえた。
まずはこの記事のコードをそのまま手元で動かし、各ステップで
head()やinfo()を挟みながら”期待どおりに結合できているか”を確認していくのがおすすめです。焦らず、1ブロックずつ進めていきましょう。
Discussion