🐍

(学習ログ)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_datecustomer_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'
    quantityitem_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のまま扱いたい」ケース(後続のmergeconcatの整合性)でも [['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