Pandas Concat:PythonでDataFrameを連結する方法
Updated on
現実のデータが単一のファイルに収まっていることはほとんどありません。1月の売上データをあるCSVから、2月のデータを別のCSVから、Q1の目標を3つ目のCSVから取得します。複数のWebページをスクレイピングして別々のDataFrameに格納します。大きなデータセットを並列処理のために分割し、後でピースを再組み立てする必要があります。いずれの場合も、行を失ったり、列を混乱させたり、インデックスを壊したりせずにDataFrameを結合する信頼性の高い方法が必要です。
**pandas concat**関数(pd.concat())は、この作業の標準ツールです。DataFrameを縦方向(行の追加)または横方向(列の追加)に積み重ね、異なる列を適切に処理し、1回の呼び出しで任意の数のDataFrameに対応します。このガイドでは、必要なすべてのパラメータを、ノートブックに直接貼り付けられる実動コード例とともに解説します。
pd.concat()の機能 -- 基本構文
pd.concat()はDataFrameのリスト(または辞書)を受け取り、指定された軸に沿って結合します。ブロックを積み重ねるようなイメージです -- 縦方向に積めば行が追加され、横に並べれば列が追加されます。
import pandas as pd
result = pd.concat(
objs, # list or dict of DataFrames
axis=0, # 0 = vertical (rows), 1 = horizontal (columns)
join='outer', # 'outer' or 'inner'
ignore_index=False,
keys=None,
sort=False,
verify_integrity=False
)主要パラメータ一覧
| パラメータ | 説明 | デフォルト |
|---|---|---|
objs | 連結するDataFrame(またはSeries)のリストまたは辞書 | 必須 |
axis | 0で行を積み重ね(縦方向)、1で列を積み重ね(横方向) | 0 |
join | 'outer'はすべての列を保持、'inner'は共通の列のみ保持 | 'outer' |
ignore_index | Trueの場合、結果のインデックスを0, 1, 2, ...にリセット | False |
keys | 各行がどの元のDataFrameから来たかを識別するラベル | None |
sort | 非連結軸をソート(axis=0の場合は列名) | False |
verify_integrity | 結果に重複するインデックス値がある場合にエラーを発生 | False |
すべての例で使用するサンプルデータ
以下のすべての例では、これらのDataFrameを使用します:
import pandas as pd
df_jan = pd.DataFrame({
'product': ['Widget', 'Gadget', 'Sprocket'],
'units_sold': [150, 200, 80],
'revenue': [1500, 3000, 960]
})
df_feb = pd.DataFrame({
'product': ['Widget', 'Gadget', 'Sprocket'],
'units_sold': [170, 180, 95],
'revenue': [1700, 2700, 1140]
})
print(df_jan)
print(df_feb)出力:
product units_sold revenue
0 Widget 150 1500
1 Gadget 200 3000
2 Sprocket 80 960
product units_sold revenue
0 Widget 170 1700
1 Gadget 180 2700
2 Sprocket 95 1140DataFrameを縦方向に連結する(axis=0)
縦方向の連結は最も一般的なユースケースです。あるDataFrameを別のDataFrameの上に積み重ね、行を追加します。月次ファイル、バッチ結果、または同じ列を持つ複数のテーブルに分割されたデータがある場合に使用します。
combined = pd.concat([df_jan, df_feb])
print(combined)出力:
product units_sold revenue
0 Widget 150 1500
1 Gadget 200 3000
2 Sprocket 80 960
0 Widget 170 1700
1 Gadget 180 2700
2 Sprocket 95 1140インデックスに注目してください。両方のDataFrameが元のインデックス値(0, 1, 2)を保持しているため、結果には重複するインデックス値があります。これは通常望ましくありません。解決策は次に説明するignore_indexパラメータです。
ignore_indexパラメータ -- インデックスのリセット
ignore_index=Trueを設定すると、元のインデックスを破棄し、0から始まる新しい連番インデックスを割り当てます:
combined = pd.concat([df_jan, df_feb], ignore_index=True)
print(combined)出力:
product units_sold revenue
0 Widget 150 1500
1 Gadget 200 3000
2 Sprocket 80 960
3 Widget 170 1700
4 Gadget 180 2700
5 Sprocket 95 1140使用するタイミング: 縦方向の連結ではほぼ常に使用します。インデックスに意味のある情報(タイムスタンプやユニークIDなど)が含まれていない限り、後の混乱を避けるためにリセットしましょう。
keysパラメータ -- 階層インデックスの作成
keysパラメータは、各行がどのソースから来たかをラベル付けするレベルをインデックスに追加します。これによりMultiIndex(階層インデックス)が作成されます:
combined = pd.concat([df_jan, df_feb], keys=['January', 'February'])
print(combined)出力:
product units_sold revenue
January 0 Widget 150 1500
1 Gadget 200 3000
2 Sprocket 80 960
February 0 Widget 170 1700
1 Gadget 180 2700
2 Sprocket 95 1140.locを使用して特定のソースのデータを選択できます:
# Get only January data
jan_data = combined.loc['January']
print(jan_data)出力:
product units_sold revenue
0 Widget 150 1500
1 Gadget 200 3000
2 Sprocket 80 960使用するタイミング: 各行がどの元のDataFrameに属するかを追跡する必要がある場合にkeysを使用します -- 例えば、異なる実験、期間、またはデータソースからのデータです。
横方向の連結(axis=1) -- 横に並べる
axis=1を設定すると、DataFrameを横に並べて列を追加します。Pandasはインデックス値によって行を揃えます。
targets = pd.DataFrame({
'target_units': [160, 190, 90],
'target_revenue': [1600, 2850, 1080]
})
result = pd.concat([df_jan, targets], axis=1)
print(result)出力:
product units_sold revenue target_units target_revenue
0 Widget 150 1500 160 1600
1 Gadget 200 3000 190 2850
2 Sprocket 80 960 90 1080これは両方のDataFrameが同じインデックス(0, 1, 2)を共有しているため、きれいに動作します。インデックスが一致しない場合、一致しない行にはNaN値が入ります:
df_a = pd.DataFrame({'value_a': [10, 20, 30]}, index=[0, 1, 2])
df_b = pd.DataFrame({'value_b': [40, 50, 60]}, index=[1, 2, 3])
result = pd.concat([df_a, df_b], axis=1)
print(result)出力:
value_a value_b
0 10.0 NaN
1 20.0 40.0
2 30.0 50.0
3 NaN 60.0行0にはvalue_bがありません(df_bに一致するインデックスがないため)。行3にはvalue_aがありません(df_aに一致するインデックスがないため)。
joinパラメータ -- Inner vs Outer
joinパラメータは、DataFrameが異なる列(axis=0の場合)または異なるインデックス値(axis=1の場合)を持つ場合の動作を制御します。
outer join(デフォルト) -- すべてを保持
df_with_extra = pd.DataFrame({
'product': ['Widget', 'Gadget'],
'units_sold': [200, 250],
'region': ['East', 'West']
})
result = pd.concat([df_jan, df_with_extra], join='outer', ignore_index=True)
print(result)出力:
product units_sold revenue region
0 Widget 150 1500.0 NaN
1 Gadget 200 3000.0 NaN
2 Sprocket 80 960.0 NaN
3 Widget 200 NaN East
4 Gadget 250 NaN West両方のDataFrameのすべての列が表示されます。欠損値はNaNで埋められます。
inner join -- 共通の列のみ保持
result = pd.concat([df_jan, df_with_extra], join='inner', ignore_index=True)
print(result)出力:
product units_sold
0 Widget 150
1 Gadget 200
2 Sprocket 80
3 Widget 200
4 Gadget 250両方のDataFrameに存在する列のみが残ります。revenue列(df_with_extraにない)とregion列(df_janにない)は両方とも削除されます。
inner joinを使用するタイミング: NaN値のないクリーンな結果が欲しく、すべてのDataFrameに存在しない列を失っても構わない場合に使用します。
DataFrameのリストを連結する
pd.concat()が他の結合方法に比べて持つ最大の利点の1つは、1回の呼び出しで任意の数のDataFrameを処理できることです。これはループで読み込んだファイルを結合するための標準パターンです:
import pandas as pd
# Simulate loading monthly CSV files
months = {
'Jan': {'product': ['Widget', 'Gadget'], 'units': [150, 200]},
'Feb': {'product': ['Widget', 'Gadget'], 'units': [170, 180]},
'Mar': {'product': ['Widget', 'Gadget'], 'units': [190, 210]},
}
dfs = []
for month, data in months.items():
df = pd.DataFrame(data)
df['month'] = month
dfs.append(df)
all_data = pd.concat(dfs, ignore_index=True)
print(all_data)出力:
product units month
0 Widget 150 Jan
1 Gadget 200 Jan
2 Widget 170 Feb
3 Gadget 180 Feb
4 Widget 190 Mar
5 Gadget 210 Marこのパターン -- DataFrameのリストを作成し、最後に一度だけconcatを呼び出す -- は、ループ内で1つずつDataFrameを追加するよりもはるかに高速です。各appendは完全なコピーを作成しますが、1回のpd.concat()呼び出しはメモリを1回だけ確保します。
concat vs merge vs append -- 比較表
Pandasには DataFrameを結合する方法がいくつかあります。正しいものを選ぶには、どのように結合したいかによります:
| 機能 | pd.concat() | pd.merge() | DataFrame.append() |
|---|---|---|---|
| 主な用途 | DataFrameの積み重ね(行または列) | 共有列の値で結合(SQLのように) | DataFrameに行を追加 |
| 入力数 | 任意の数を一度に | 一度に2つ | 一度に2つ |
| マッチングロジック | インデックス(または列名)で揃える | キー列の値でマッチング | 列名で揃える |
| 結合タイプ | outer、inner | inner、left、right、outer、cross | outerのみ |
| デフォルト動作 | outer join、縦方向に積み重ね | inner join、共有列で結合 | outer join、行を追加 |
| 最適な用途 | 月次ファイルの結合、バッチ結果、同じスキーマのテーブル | リレーショナル結合(顧客+注文) | pandas 2.0で非推奨 |
| パフォーマンス | 多数のDataFrameに対して高速 | 2テーブル結合に最適化 | 低速(毎回データをコピー) |
それぞれの使い分け
pd.concat()を使用する -- DataFrameが同じ構造(同じ列)を持ち、積み重ねたい場合。インデックスで揃える横方向の連結にも使用します。pd.merge()を使用する -- 列の値に基づいて行をマッチングする必要がある場合 -- 例えば、売上テーブルと製品テーブルをproduct_idで結合する場合。詳細はpandas mergeガイドをご覧ください。DataFrame.append()は避ける -- pandas 1.4で非推奨となり、pandas 2.0で削除されました。代わりにpd.concat([df1, df2])を使用してください。
よくあるエラーと対処法
1. 列が一致しない
異なる列名のDataFrameを連結する場合、デフォルトのouter joinは欠損値をNaNで埋めます。予期しない場合は、列名を確認してください:
# Diagnose: compare column names
print(df1.columns.tolist())
print(df2.columns.tolist())
# Fix: rename columns to match before concatenating
df2 = df2.rename(columns={'sales': 'revenue', 'qty': 'units_sold'})
combined = pd.concat([df1, df2], ignore_index=True)2. 連結後のデータ型の不一致
あるDataFrameが列をint64として保存し、別のDataFrameがfloat64として保存している場合、pandasはfloatにアップキャストします。さらに悪いことに、一方が文字列として保存している場合、object型の列が作成されます:
# Check dtypes after concat
combined = pd.concat([df1, df2], ignore_index=True)
print(combined.dtypes)
# Fix: cast before concatenating
df2['units_sold'] = df2['units_sold'].astype(int)
combined = pd.concat([df1, df2], ignore_index=True)3. 重複するインデックス値
ignore_index=Trueを使用しないと、縦方向の連結は元のインデックスを保持し、重複値が発生します。これは.locでのルックアップで問題を引き起こします:
combined = pd.concat([df1, df2])
# combined.loc[0] returns TWO rows, not one
# Fix option 1: use ignore_index
combined = pd.concat([df1, df2], ignore_index=True)
# Fix option 2: use verify_integrity to catch the issue early
combined = pd.concat([df1, df2], verify_integrity=True) # raises ValueError4. 誤った軸での連結
結果が行の2倍ではなく列の2倍になっている場合(またはその逆)、axisパラメータを確認してください:
# Wrong: this adds columns side by side
wrong = pd.concat([df1, df2], axis=1)
# Right: this stacks rows vertically
right = pd.concat([df1, df2], axis=0)PyGWalkerで連結したDataFrameを可視化する
複数のソースからデータを連結した後、結果を検証し、結合されたデータセット内のパターンを探索する必要がよくあります。matplotlibやseabornで手動のプロットコードを書く代わりに、PyGWalker (opens in a new tab)を使用できます -- これは任意のpandas DataFrameをJupyter Notebook内で直接、インタラクティブなTableauライクの視覚的探索インターフェースに変換するオープンソースPythonライブラリです。
import pandas as pd
import pygwalker as pyg
# Combine monthly sales data
df_jan = pd.DataFrame({
'product': ['Widget', 'Gadget', 'Sprocket'],
'units_sold': [150, 200, 80],
'revenue': [1500, 3000, 960],
'month': ['Jan', 'Jan', 'Jan']
})
df_feb = pd.DataFrame({
'product': ['Widget', 'Gadget', 'Sprocket'],
'units_sold': [170, 180, 95],
'revenue': [1700, 2700, 1140],
'month': ['Feb', 'Feb', 'Feb']
})
combined = pd.concat([df_jan, df_feb], ignore_index=True)
# Launch interactive visualization
walker = pyg.walk(combined)PyGWalkerでは、productをx軸に、revenueをy軸にドラッグし、monthで分割することで、期間間の収益トレンドを即座に比較できます -- チャートコードは不要です。フィールドをドラッグするだけで、棒グラフ、散布図、折れ線グラフなどを作成できます。連結が正しく機能したか、異なるソースからのデータが期待通りに揃っているかを検証するのに特に便利です。
pip install pygwalkerでPyGWalkerをインストールするか、Google Colab (opens in a new tab)またはKaggle (opens in a new tab)でお試しください。
FAQ
pandas concatとmergeの違いは何ですか?
pd.concat()はインデックスに基づいてDataFrameを縦方向(行の追加)または横方向(列の追加)に積み重ねます。pd.merge()は特定の列の値をマッチングして2つのDataFrameを結合します(SQL JOINのように)。DataFrameが同じ列を持ち、行を結合したい場合はconcatを使用してください。共有キー列に基づいて行をマッチングする必要がある場合はmergeを使用してください。
pd.concat()は元のDataFrameを変更しますか?
いいえ。pd.concat()は常に新しいDataFrameを返します。元のDataFrameは変更されません。これは、操作がデータをその場で変更するのではなく、新しいオブジェクトを返すというpandasの設計原則に一致しています。
異なる列を持つDataFrameをどのように連結しますか?
デフォルトのjoin='outer'でpd.concat()を使用してください -- すべてのDataFrameのすべての列を保持し、欠損値をNaNで埋めます。すべてのDataFrameに存在する列のみが必要な場合は、join='inner'を設定してください。連結前に列名を変更して揃えることもできます。
pd.concat()はDataFrame.append()より高速ですか?
はい。DataFrame.append()はpandas 1.4で非推奨となり、pandas 2.0で削除されました。内部的にpd.concat()を呼び出していましたが、毎回コピーを作成していました。多数のDataFrameを結合する場合、リストに集めて一度だけpd.concat()を呼び出す方が、メモリを1回だけ確保するため大幅に高速です。
連結後にインデックスをリセットするにはどうすればよいですか?
pd.concat()にignore_index=Trueを渡してください:pd.concat([df1, df2], ignore_index=True)。これにより、元のインデックス値が0から始まる新しい連番インデックスに置き換えられます。または、結果に対して.reset_index(drop=True)を呼び出すこともできます。
まとめ
pandas concat()関数は、同じ構造を共有するDataFrameを結合するための定番ツールです。主なポイントは以下の通りです:
- 縦方向の連結(
axis=0)は行を積み重ねる最も一般的なユースケースです -- 月次ファイル、バッチ結果、分割データセットの結合に最適です。 - 横方向の連結(
axis=1)はDataFrameを横に並べ、インデックスで揃えます。 - **
ignore_index=True**を使用して、クリーンな連番インデックスを取得します(ほとんどの場合に推奨)。 - **
keys**を使用して、各行がどのソースから来たかを追跡する階層インデックスを作成します。 - **
join**パラメータは、一致しない列の処理方法を制御します:'outer'はすべてを保持、'inner'は共通の列のみ保持。 - 常にDataFrameをリストに集めて、ループ内でappendする代わりに、
pd.concat()を一度だけ呼び出してください。 - 列の値でSQL風の結合が必要な場合は、代わりに
pd.merge()を使用してください。
データを連結した後、PyGWalker (opens in a new tab)などのツールを使えば、チャートコードを書くことなく結合結果を視覚的に探索でき、データパイプラインの検証やソース間のパターン発見がより迅速になります。