データ分析で欠かせないのがCSVやExcelのような表形式データです。
pandasはPythonでこれらを柔軟に扱うための定番ライブラリで、読み込みから整形、結合、集計まで一貫して行えます。
本記事ではCSVとExcelを中心に、初心者がつまずきやすい点もカバーしながら、最短距離で使いこなすことを目標に解説します。
pandasの基本
pandasとは?できること
表形式データ処理の標準ライブラリ
pandasは、行と列からなる表(データフレーム)を高速かつ直感的に操作できるPythonライブラリです。
CSVやExcelの読み書き、欠損値処理、列の追加や変換、結合や集計など、日常的なデータ前処理を網羅します。
SQL的な結合やExcel的なピボット集計も1つのAPIで扱えるのが特徴です。
なぜpandasを使うのか
Excelだけでは自動化や再現性、規模の拡張に限界があります。
pandasならスクリプトで処理を記録し、毎回同じ結果を素早く再現できます。
さらに、NumPyやMatplotlib、scikit-learnなどのエコシステムと自然に連携できます。
インストールとimport
インストール方法とバージョン確認
一般的にはpipでインストールします。
Excelを扱う場合はopenpyxlも入れておくと安心です。
# 仮想環境を使うことを推奨します(任意)
python -m venv .venv
. .venv/bin/activate # Windowsは .venv\Scripts\activate
# pandasとExcelエンジン(openpyxl)のインストール
pip install -U pandas openpyxl
# pandasの基本的なインポートとバージョン確認
import pandas as pd
print(pd.__version__)
2.3.2
openpyxlはExcelの読み書きに必要になることが多いため、一緒に入れておくと後述のエラー回避になります。
データフレーム(DataFrame)の考え方
DataFrameとSeries
pandasの中心はDataFrame(二次元表)とSeries(一列の一次元データ)です。
行ラベル(index)と列ラベル(columns)が付与され、列ごとに型(dtype)を持ちます。
ベクトル化された演算を活用することで、forループを避けて高速に計算できます。
import pandas as pd
# 辞書からDataFrameを作成
df = pd.DataFrame({
"id": [1, 2, 3],
"product": ["P001", "P002", "P003"],
"price": [1200, 550, 980]
})
print(df)
print("\n列の型:")
print(df.dtypes)
print("\n形状(行数, 列数):", df.shape)
id product price
0 1 P001 1200
1 2 P002 550
2 3 P003 980
列の型:
id int64
product object
price int64
dtype: object
形状(行数, 列数): (3, 3)
よく使う関数一覧
以下は初心者が頻繁に使う関数とポイントの一覧です。
引数は一例です。
関数/メソッド | 用途 | 主な引数・ポイント |
---|---|---|
pd.read_csv | CSV読み込み | encoding , usecols , dtype , parse_dates , na_values |
pd.read_excel | Excel読み込み | sheet_name , usecols , header , engine |
df.head/tail/info/describe | 概観・基本統計 | head(n) , describe(include="all") |
df.loc/df.iloc | 行列選択 | ラベル/位置での抽出 |
df.query | 条件抽出 | 式で直感的にフィルタ |
df.sort_values | 並び替え | by , ascending , na_position |
df.assign/df.rename/df.drop | 列の整形 | 追加・名称変更・削除 |
pd.to_datetime/to_numeric | 型変換 | errors="coerce" で安全に変換 |
df.fillna/dropna | 欠損値処理 | 置換・削除 |
pd.merge | 横方向の結合 | on , how , indicator |
pd.concat | 縦方向の結合 | axis=0 , ignore_index=True |
df.groupby.agg | 集計 | 複数集計も可 |
df.to_csv/to_excel | 保存 | index , encoding , sheet_name |
CSVの読み込みと基本操作
read_csvの使い方とエンコーディング
サンプルCSVの準備
記事内の例がすぐ再現できるよう、まず小さなCSVを作成します。
Excelで開きやすいようにUTF-8 BOM付き(utf-8-sig)で保存します。
import pandas as pd
from datetime import date
# サンプルの売上CSVを作成して保存
sales = pd.DataFrame({
"order_id": [1001, 1002, 1003, 1004, 1005],
"date": [date(2024,4,1), date(2024,4,1), date(2024,4,2), date(2024,4,3), date(2024,4,3)],
"product_id": ["P001", "P002", "P001", "P003", "P002"],
"qty": [2, 1, 3, 5, 2],
"unit_price": [1200, 550, 1200, 980, 550],
"customer": ["佐藤", "田中", "鈴木", "山本", "佐藤"],
"prefecture": ["東京都", "大阪府", "神奈川県", "愛知県", "東京都"]
})
sales.to_csv("sales.csv", index=False, encoding="utf-8-sig")
print("sales.csv を保存しました")
sales.csv を保存しました
order_id,date,product_id,qty,unit_price,customer,prefecture
1001,2024-04-01,P001,2,1200,佐藤,東京都
1002,2024-04-01,P002,1,550,田中,大阪府
1003,2024-04-02,P001,3,1200,鈴木,神奈川県
1004,2024-04-03,P003,5,980,山本,愛知県
1005,2024-04-03,P002,2,550,佐藤,東京都
基本の読み込みと文字コード
CSVの標準的な文字コードはUTF-8です。
Windowsで作成されたCSVはShift_JIS系(cp932)のことがあります。
文字化けや読み込みエラーはencoding
でほぼ解決できます。
import pandas as pd
# UTF-8(推奨)で読み込み
df = pd.read_csv("sales.csv", encoding="utf-8")
print(df.head(3))
order_id date product_id qty unit_price customer prefecture
0 1001 2024-04-01 P001 2 1200 佐藤 東京都
1 1002 2024-04-01 P002 1 550 田中 大阪府
2 1003 2024-04-02 P001 3 1200 鈴木 神奈川県
UnicodeDecodeErrorが出る場合は、encoding="cp932"
やencoding="shift_jis"
で試してみてください。
区切りや数値表記の調整
欧州発のCSVでは小数点がカンマのことがあります。
その場合は以下のように指定します。
# セミコロン区切りや桁区切り・小数点記号の調整例
df_eu = pd.read_csv("eu_sales.csv", sep=";", decimal=",", thousands=".")
(上記は実ファイルがある場合の例です)
列名と型の指定
読み込み時に列と型を固定する
データ分析を効率的に進めるには、読み込みの段階でひと工夫するのが重要です。
特に大規模なデータでは、必要な列だけを、正しいデータ型で読み込むことで、メモリ使用量を抑え、処理速度を向上させることができます。
parse_dates
: 日付として扱いたい列名をリストで指定します。自動的に日付型(datetime64
)に変換され、時系列分析が容易になります。usecols
: 読み込みたい列名をリストで指定します。不要な列を最初から除外します。dtype
: 列ごとにデータ型を辞書形式で指定します。意図しない型変換(例: IDが数値でなく浮動小数点になる)を防ぎ、データの一貫性を保ちます。
import pandas as pd
df = pd.read_csv(
"sales.csv",
# customerとprefecture列は読み込まない
usecols=["order_id", "date", "product_id", "qty", "unit_price"],
# 各列のデータ型を明示的に指定
dtype={
"order_id": "Int64", # 欠損(NaN)を許容できる整数型
"product_id": "string", # Pandas推奨の効率的な文字列型
"qty": "Int64",
"unit_price": "float64" # 浮動小数点数
},
# date列を日付型として解析
parse_dates=["date"]
)
print(df.dtypes)
order_id Int64
date datetime64[ns]
product_id string
qty Int64
unit_price float64
dtype: object
列名がない(または途中から)場合
CSVファイルにヘッダー行(列名)がない場合や、特定の行をヘッダーとして扱いたい場合もあります。
header=None
: ファイルの1行目からデータが始まっているとみなし、列名を自動的に0, 1, 2...
と割り振ります。names
引数で任意の列名を指定するのが一般的です。header=1
: 2行目(0から数えて1番目)をヘッダー行として読み込みます。ファイルの先頭にタイトルや説明などが含まれている場合に便利です。
# 1行目に列名がない場合
# namesで自分で列名を定義
df_no_header = pd.read_csv("noheader.csv", header=None, names=["c1", "c2", "c3"])
# 2行目が列名の場合(0始まりでheader=1)
df_header_row2 = pd.read_csv("header_on_2nd_row.csv", header=1)
欠損値として扱うトークンの指定
データによっては、空欄だけでなく "NA"
や "-"
といった特定の文字列で「欠損」を表現していることがあります。
na_values
引数にこれらの文字列をリストで指定すると、Pandasはそれらを正しく欠損値 NaN
として認識してくれます。
# "", "NA", "N/A", "-" を欠損値として読み込む
df = pd.read_csv("sales.csv", na_values=["", "NA", "N/A", "-"])
行列の選択
DataFrameから必要な部分だけを取り出す操作は、データ分析の基本です。
列の選択と複数列の抽出
単一列の選択: df["列名"]
のように指定します。返されるデータは1次元の Series オブジェクトです。
複数列の選択: df[["列名1", "列名2"]]
のように、リストを二重の角括弧で囲んで指定します。こちらは2次元の DataFrame オブジェクトが返されます。
import pandas as pd
df = pd.read_csv("sales.csv", parse_dates=["date"])
# 単一列(qty)を選択 → Series
print("--- 単一列 (Series) ---")
print(df["qty"].head())
# 複数列を選択 → DataFrame
print("\n--- 複数列 (DataFrame) ---")
print(df[["date", "qty", "unit_price"]].head())
0 2
1 1
2 3
3 5
4 2
Name: qty, dtype: int64
date qty unit_price
0 2024-04-01 2 1200
1 2024-04-01 1 550
2 2024-04-02 3 1200
3 2024-04-03 5 980
4 2024-04-03 2 550
行の選択(loc/iloc)
行を選択するには、主に loc
と iloc
を使います。この2つの違いを理解することが重要です。
loc
: ラベルベースで選択します。行のインデックス名と列名を指定します。(例:df.loc[0, "product_id"]
)iloc
: 整数位置ベースで選択します。0から始まる行番号と列番号を指定します。(例:df.iloc[0:3, 0:2]
)
# loc: ラベルベースで「インデックスが0」の行の「product_id」列を選択
print(f"locの結果: {df.loc[0, 'product_id']}")
# iloc: 位置ベースで「先頭から3行 (0,1,2)」×「先頭から2列 (0,1)」を選択
print("\nilocの結果:")
print(df.iloc[0:3, 0:2])
P001
order_id date
0 1001 2024-04-01
1 1002 2024-04-01
2 1003 2024-04-02
フィルタと並び替え
条件で抽出
特定の条件に合致するデータだけを絞り込む操作は、分析で頻繁に行われます。
df["prefecture"] == "東京都"
のような条件式は、各行が条件を満たすかどうかの True
/False
のリスト(ブール値Series)を返します。
このリストをDataFrameに渡すことで、True
の行だけを抽出できます(ブールインデックス参照)。
複数の条件を組み合わせる場合は、&
(AND) や |
(OR) を使い、各条件式を ()
で囲みます。
# 「prefectureが東京都」かつ「qtyが2以上」の行を抽出
mask = (df["prefecture"] == "東京都") & (df["qty"] >= 2)
tokyo_orders = df[mask]
print(tokyo_orders)
order_id date product_id qty unit_price customer prefecture
0 1001 2024-04-01 P001 2 1200 佐藤 東京都
4 1005 2024-04-03 P002 2 550 佐藤 東京都
df.query("prefecture == '東京都' and qty >= 2")
のように query
メソッドを使うと、SQLのように直感的な文字列で条件を記述でき、コードがシンプルになります。
並び替え
sort_values()
メソッドでデータを並び替えます。
by
: 並び替えのキーとなる列名を指定します。リストで複数指定すると、優先順位をつけて並び替えられます。ascending
:True
で昇順(小さい順)、False
で降順(大きい順)を指定します。by
で複数列を指定した場合は、リストで各列の並び順を個別に設定できます。
# prefecture(昇順) → date(昇順) → qty(降順) の優先順位で並び替え
sorted_df = df.sort_values(
by=["prefecture", "date", "qty"],
ascending=[True, True, False]
)
print(sorted_df.head())
order_id date product_id qty unit_price customer prefecture
3 1004 2024-04-03 P003 5 980 山本 愛知県
1 1002 2024-04-01 P002 1 550 田中 大阪府
2 1003 2024-04-02 P001 3 1200 鈴木 神奈川県
0 1001 2024-04-01 P001 2 1200 佐藤 東京都
4 1005 2024-04-03 P002 2 550 佐藤 東京都
欠損値の扱い
欠損の検出と置換・削除
データに欠損値(NaN
)が含まれていると、計算や分析が正しく行えないことがあります。まずは欠損の状況を確認し、適切に処理(補完または削除)する必要があります。
isna().sum()
: 列ごとに欠損値の数を簡単に集計できる便利な手法です。fillna()
: 欠損値を指定した値で補完(穴埋め)します。辞書を渡せば、列ごとに異なる値で補完できます。統計量(平均値mean()
や中央値median()
)で補完することも一般的です。dropna()
: 欠損値を含む行(または列)を削除します。subset
で対象列を指定すると、その列に欠損がある行だけを削除できます。
import numpy as np
# 動作確認のため、意図的に欠損(NaN)を作成
df.loc[1, "unit_price"] = np.nan
# isna()で欠損を判定し、sum()で列ごとに集計
print("--- 列ごとの欠損数 ---")
print(df.isna().sum())
# fillna()でunit_price列の欠損を中央値で補完
df_filled = df.fillna({"unit_price": df["unit_price"].median()})
# dropna()でunit_price列に欠損を含む行を削除する場合(参考)
df_dropped = df.dropna(subset=["unit_price"])
order_id 0
date 0
product_id 0
qty 0
unit_price 1
customer 0
prefecture 0
dtype: int64
整数列に欠損がある場合はInt64
という欠損許容整数型を使うと取り回しが楽です。
CSVに保存
保存時の基本設定
処理や分析が完了したDataFrameは、to_csv()
メソッドで再びCSVファイルとして保存できます。
index=False
: これを指定しないと、DataFrameのインデックス(0, 1, 2…)がCSVの先頭列として書き出されてしまいます。通常は不要なため、指定が推奨されます。encoding="utf-8-sig"
: BOM付きUTF-8で保存します。これにより、Excelで直接ファイルを開いても文字化けするのを防げます。float_format
: 金額など、浮動小数点数の表示形式(小数点以下の桁数など)を整えたい場合に指定します。
# indexを付けず、Excelで文字化けしないように保存
df.to_csv("sales_clean.csv", index=False, encoding="utf-8-sig")
# 価格の小数点以下を2桁に揃えて保存したい場合
df.to_csv("sales_price2.csv", index=False, float_format="%.2f")
print("CSVを保存しました")
CSVを保存しました
Excelと連携する場合はutf-8-sig
が無難です。
数式や先頭ゼロ保持が必要なら文字列型を検討します。
Excelの読み込みと複数シート 📂
PandasはCSVだけでなく、Excelファイルの読み書きも強力にサポートしています。
複数のシートを扱ったり、特定のセル範囲を読み込んだりと、Excelならではの操作に対応できます。
read_excelの基本
サンプルExcelの準備と読み込み
まずは分析対象となるExcelファイルを作成します。
ここでは、製品マスタと前章で作成した売上データを、それぞれ別のシートに保存したworkbook.xlsx
というブックを準備します。
pd.read_excel()
は、ExcelファイルをDataFrameとして読み込むための基本関数です。
引数を指定しない場合、ブックの先頭にあるシートを自動的に読み込みます。
import pandas as pd
# 製品マスタのDataFrameを作成
products = pd.DataFrame({
"product_id": ["P001", "P002", "P003"],
"product_name": ["ノートPC", "マウス", "キーボード"],
"category": ["PC", "周辺機器", "周辺機器"]
})
# ExcelWriterを使い、複数のシートを持つExcelブックを作成
with pd.ExcelWriter("workbook.xlsx", engine="openpyxl") as writer:
products.to_excel(writer, sheet_name="products", index=False)
# 先ほど作ったsales.csvも読み込んで、別のシートに保存
pd.read_csv("sales.csv").to_excel(writer, sheet_name="sales", index=False)
# Excelファイルを読み込み (デフォルトでは先頭のシート"products"が対象)
df_xl = pd.read_excel("workbook.xlsx")
print(df_xl)
product_id product_name category
0 P001 ノートPC PC
1 P002 マウス 周辺機器
2 P003 キーボード 周辺機器
エラー対応:
ImportError: Missing optional dependency ‘openpyxl’
というエラーが出た場合は、Excelファイルの操作に必要なライブラリが不足しています。ターミナルやコマンドプロンプトで
pip install openpyxl
を実行してインストールしてください。
シート名(sheet_name)の指定
Excelブック内の特定のシートや、複数のシートを一度に読み込みたい場合は sheet_name
引数を使います。
シートを選ぶ・複数同時に読む
- 単一シートの指定:
sheet_name="シート名"
やsheet_name=1
(0から始まるシートの順番)で指定します。 - 複数シートの指定:
sheet_name=["シート名1", "シート名2"]
のようにリストで渡すと、指定したシートをまとめて読み込めます。この場合、戻り値はDataFrameを値に持つ辞書となり、キーはシート名になります。
# "sales"シートを名前で指定して読み込み
# CSVと同様に、日付として解析したい列も指定可能
df_sales = pd.read_excel("workbook.xlsx", sheet_name="sales", parse_dates=["date"])
# "sales"と"products"の2シートをまとめて読み込み
# 戻り値は {"sales": DataFrame, "products": DataFrame} という形式の辞書
dfs = pd.read_excel("workbook.xlsx", sheet_name=["sales", "products"])
# 戻り値の型と、辞書のキーを確認
print(type(dfs), list(dfs.keys()))
<class 'dict'> ['sales', 'products']
全シートを一括で読み込む
sheet_name=None
を指定すると、ブックに含まれるすべてのシートを一度に読み込むことができます。
これも結果はシート名をキーとする辞書形式で返されます。
# sheet_name=None ですべてのシートを読み込む
all_sheets = pd.read_excel("workbook.xlsx", sheet_name=None)
print([k for k in all_sheets.keys()])
['products', 'sales']
列名行の指定
業務で使うExcelファイルには、表のすぐ上にタイトル行が入っているなど、1行目からデータが始まっていないケースがよくあります。
header
や skiprows
引数で柔軟に対応できます。
header=1
: 2行目(0から数えて1番目)を列名として読み込みます。skiprows=2
: 先頭から2行を完全に無視して、3行目から読み込みを開始します。
# 例:2行目が列名、1行目はタイトル行などの場合
df2 = pd.read_excel("report.xlsx", header=1)
# 例:先頭の不要な2行をスキップして読み込む場合
df3 = pd.read_excel("report.xlsx", skiprows=2)
(上記は実ファイルがある場合の例です)
Excelに保存
DataFrameは to_excel()
メソッドで簡単にExcelファイルとして保存できます。
1シート・複数シートの保存
pd.ExcelWriter
を使うと、複数のDataFrameを1つのブックに別々のシートとして書き込むことができます。
with
構文を使うことで、ファイル操作を安全に完了できます。
# "products" DataFrameだけを単一シートのExcelファイルとして保存
products.to_excel("products_only.xlsx", index=False)
# 複数のDataFrameを1つのブック内の異なるシートに保存
with pd.ExcelWriter("outputs.xlsx", engine="openpyxl") as writer:
df_sales.to_excel(writer, sheet_name="Sales", index=False)
products.to_excel(writer, sheet_name="Products", index=False)
print("Excelを保存しました")
Excelを保存しました
ヒント既存のExcelブックにシートを追記したい場合は
mode="a"
や、同名シートが存在する場合の挙動を決めるif_sheet_exists
といった引数の利用を検討します。
データの整形と結合の実例 🛠️
読み込んだデータは、分析に適した形に整える「前処理」が必要です。
ここでは、列の操作、型変換、複数データの結合といった、実践で頻出する処理を見ていきます。
列の追加・削除・リネーム
計算列の追加や列名変更
既存のデータから新しい指標を作ったり、分かりにくい列名を変更したりするのは基本的な操作です。
assign()
: 計算列を追加します。元のDataFrameを変更せず、新しい列が追加されたコピーを返します。rename()
: 列名を変更します。columns
引数に{"元の列名": "新しい列名"}
の辞書を渡します。drop()
: 列を削除します。columns
引数に削除したい列名のリストを渡します。
import pandas as pd
sales = pd.read_csv("sales.csv", parse_dates=["date"])
# assignを使って、金額(=数量×単価)を計算するamount列を追加
sales = sales.assign(amount=sales["qty"] * sales["unit_price"])
# renameを使って、列名をより分かりやすい英語に変更
sales = sales.rename(columns={"qty": "quantity", "unit_price": "unit_price_jpy"})
# dropを使って、分析に不要な列(例: 顧客名)を削除
sales_drop = sales.drop(columns=["customer"])
print("--- 処理後のDataFrame ---")
print(sales.head(2))
print("\n--- 列削除後の列名リスト ---")
print(sales_drop.columns.tolist())
--- 処理後のDataFrame ---
order_id date product_id quantity unit_price_jpy customer prefecture amount
0 1001 2024-04-01 P001 2 1200 佐藤 東京都 2400
1 1002 2024-04-01 P002 1 550 田中 大阪府 550
--- 列削除後の列名リスト ---
['order_id', 'date', 'product_id', 'quantity', 'unit_price_jpy', 'prefecture', 'amount']
ポイント
assign
やrename
は、sales.assign(...).rename(...)
のようにメソッドを繋げて(メソッドチェーン)、一連の処理を流れるように記述できます。
文字列・日付の型変換
文字列整形と数値・日付への変換
手入力されたデータなどには、表記の揺れ(不要なスペース、大文字・小文字の混在)や、数値・日付が文字列として扱われていることが頻繁にあります。
これらを正しい型に変換し、データを綺麗に(クレンジング)します。
.str
アクセサ: 文字列型の列に対して、strip()
(空白除去)やupper()
(大文字化)などの文字列操作を一括適用できます。pd.to_numeric()
: 文字列を数値型に変換します。errors="coerce"
を指定すると、変換できない値をエラーにせず**欠損値(NaN
)**に置き換えてくれるため、非常に堅牢です。pd.to_datetime()
: 文字列を日付型に変換します。こちらもerrors="coerce"
が有効です。.dt
アクセサ: 日付型の列から、年・月・曜日などの要素を簡単に取り出せます。
# .strアクセサで、product_idの前後の空白を除去し、大文字に統一
sales["product_id"] = sales["product_id"].str.strip().str.upper()
# pd.to_numericで、unit_price_jpyを数値型に安全に変換
sales["unit_price_jpy"] = pd.to_numeric(sales["unit_price_jpy"], errors="coerce")
# pd.to_datetimeで、dateを日付型に変換
sales["date"] = pd.to_datetime(sales["date"], format="%Y-%m-%d", errors="coerce")
# .dtアクセサで、日付から年月(YYYY-MM)の文字列を派生させる
sales["year_month"] = sales["date"].dt.to_period("M").astype(str)
print(sales[["product_id", "unit_price_jpy", "date", "year_month"]].head(3))
product_id unit_price_jpy date year_month
0 P001 1200.0 2024-04-01 2024-04
1 P002 550.0 2024-04-01 2024-04
2 P001 1200.0 2024-04-02 2024-04
データの結合(merge)の基本
マスタ結合で情報を付与する
分析では、トランザクションデータ(売上など)に、マスタデータ(製品情報、顧客情報など)を紐づけて情報を豊かにすることが頻繁に行われます。
pd.merge()
は、このテーブル結合(SQLのJOINに相当)を実現します。
on
で指定した共通のキー列(ここでは product_id
)をもとに、2つのDataFrameを横に連結します。
import pandas as pd
sales = pd.read_csv("sales.csv")
products = pd.read_excel("workbook.xlsx", sheet_name="products")
# salesを左側(left)、productsを右側として、product_idをキーに結合
merged = pd.merge(
sales, products,
on="product_id", # 結合キー
how="left", # 結合方法: salesの全行を残す左外部結合
validate="m:1", # sales(多):products(1)の関係を検証
indicator=True, # 結合状態を示す列(_merge)を追加
suffixes=("", "_prod") # 重複列名があった場合の接尾辞
)
print(merged[["product_id", "qty", "unit_price", "product_name", "_merge"]].head())
product_id qty unit_price product_name _merge
0 P001 2 1200 ノートPC both
1 P002 1 550 マウス both
2 P001 3 1200 ノートPC both
3 P003 5 980 キーボード both
4 P002 2 550 マウス both
データ品質チェック:
indicator=True
を付けると、_merge
列が作成されます。この列が
"left_only"
になっている行は、「売上データには存在するが、製品マスタには登録がないID」を意味し、データクレンジングの対象として発見できます。
結合方法 how
は、"inner"
(両方に存在するデータのみ), "left"
, "right"
, "outer"
(どちらかにあればすべて)から目的に応じて選びます。
縦結合(concat)の基本
同じ列構造のデータを積み上げる
merge
がデータを横に広げるのに対し、pd.concat()
はデータを縦に積み上げます。
月ごとの売上ファイルや店舗ごとの日報など、同じフォーマットの複数のデータを1つにまとめる際によく使われます。
import pandas as pd
apr = pd.read_csv("sales.csv")
# 5月分のデータとして、4月データの一部をサンプリングして作成
may = apr.sample(frac=0.6, random_state=0)
may["date"] = "2024-05-01"
# 2つのDataFrameをリストで渡し、縦方向(axis=0)に連結
all_sales = pd.concat([apr, may], axis=0, ignore_index=True)
print(all_sales[["date", "product_id", "qty"]].head(7))
print("合計行数:", len(all_sales))
date product_id qty
0 2024-04-01 P001 2
1 2024-04-01 P002 1
2 2024-04-02 P001 3
3 2024-04-03 P003 5
4 2024-04-03 P002 2
5 2024-05-01 P003 5
6 2024-05-01 P002 2
合計行数: 8
ポイント
ignore_index=True
を指定すると、元のインデックスは破棄され、0から始まる新しい連番のインデックスが振り直されます。データを連結した後は、通常この指定を行うのが便利です。
集計とグループ化 📊
単純集計と複合集計
groupby()
は、データ分析の中核をなす機能です。
「~ごと」の集計(例: 都道府県ごと、製品カテゴリごと)を行いたい場合に使用します。
groupby()
でグループ化した後、.agg()
メソッドを続けることで、合計(sum
)、平均(mean
)、件数(count
)など、複数の集計を一度に実行できます。
("新しい列名", ("元の列名", "集計方法"))
という書き方をすると、結果の列名が分かりやすくなりおすすめです。
import pandas as pd
sales = pd.read_csv("sales.csv")
# 事前に金額列を計算しておく
sales["amount"] = sales["qty"] * sales["unit_price"]
# 「都道府県」と「商品ID」でグループ化し、それぞれの集計値を計算
agg_df = sales.groupby(["prefecture", "product_id"], as_index=False).agg(
total_qty=("qty", "sum"),
total_amount=("amount", "sum"),
avg_unit_price=("unit_price", "mean")
)
print(agg_df)
prefecture product_id total_qty total_amount avg_unit_price
0 大阪府 P002 1 550 550.0
1 神奈川県 P001 3 3600 1200.0
2 愛知県 P003 5 4900 980.0
3 東京都 P001 2 2400 1200.0
4 東京都 P002 2 1100 550.0
ピボットテーブル
Excelのピボットテーブルと同様のクロス集計表を作成するには pivot_table()
を使います。
データを行と列の2軸で整理し、集計結果を俯瞰したい場合に非常に強力です。
index
: 表の行に表示したい列columns
: 表の列に表示したい列values
: 集計したい数値データが含まれる列aggfunc
: 集計方法("sum"
,"mean"
,"count"
など)fill_value
: データが存在しないセルに表示する値(NaNの代わり)
# 行に「都道府県」、列に「商品ID」、値に「金額の合計」を配置したピボットテーブルを作成
pivot = sales.pivot_table(
index="prefecture",
columns="product_id",
values="amount",
aggfunc="sum",
fill_value=0 # 該当なしのセルは0で埋める
)
print(pivot)
product_id P001 P002 P003
prefecture
大阪府 0 550 0
愛知県 0 0 4900
東京都 2400 1100 0
神奈川県 3600 0 0
よくあるエラーと対処
文字コード関連
- 症状: UnicodeDecodeError: ‘utf-8’ codec can’t decode byte…
対処:encoding="cp932"
や"shift_jis"
を指定。BOM付きは"utf-8-sig"
。
区切り・フォーマット
- 症状: PARSERError: Error tokenizing data
対処: 区切り文字sep
や引用符quotechar
の見直し。実データの先頭数行を目視確認。
型の混在
- 症状: DtypeWarning: Columns have mixed types
対処:dtype
やconverters
で明示。読み込み後はto_numeric(..., errors="coerce")
で正規化。
参照とコピー
- 症状: SettingWithCopyWarning
対処: チェーン代入を避け、df.loc[mask, "col"] = value
の形に統一。必要に応じて.copy()
を明示。
# 悪い例(警告が出やすい)
tmp = sales[sales["qty"] >= 2]
tmp["flag"] = 1 # ← SettingWithCopyWarningの可能性
# 良い例
tmp = sales.loc[sales["qty"] >= 2].copy()
tmp.loc[:, "flag"] = 1
# ここでは警告は表示されません(例示)
列名やキーの不整合
- 症状: KeyError: ‘product_id’
対処: スペースや全角・半角ゆらぎをdf.columns = df.columns.str.strip()
で除去。結合時はleft_on
/right_on
でキーを合わせる。
ファイル・エンジン
- 症状: FileNotFoundError
対処: パスを確認。相対パスが不安定なら絶対パスやpathlib
を使用。 - 症状: ImportError: Missing optional dependency ‘openpyxl’
対処:pip install openpyxl
。
パフォーマンス
- 症状: メモリ不足や速度低下
対処:usecols
で列を絞る、dtype
を適切化、chunksize
で分割読み込み、pyarrow
の利用を検討。
# 大きなCSVを分割して処理する例
import pandas as pd
total = 0
for chunk in pd.read_csv("huge.csv", usecols=["qty", "unit_price"], chunksize=100_000):
chunk["amount"] = chunk["qty"] * chunk["unit_price"]
total += chunk["amount"].sum()
print("合計金額:", total)
合計金額: 1234567890.0
トラブルの多くは「文字コード・列名・型・結合キー」に集約されます。
まずはここを丁寧に整えるのが近道です。
まとめ
本記事では、pandasの基本からCSVとExcelの読み込み、列操作、フィルタや並び替え、欠損値処理、そしてマスタ結合や縦結合、グループ集計までを一通り解説しました。
特に読み込み時のencoding/usecols/dtype/parse_dates
を正しく設定するだけで、その後の整形が格段にスムーズになります。
さらにmergeでのキー管理とSettingWithCopyWarningの回避を押さえれば、実務レベルの前処理は十分にこなせます。
最後に、再利用しやすいスクリプトとして書き残すことで、再現性と作業効率が飛躍的に向上します。
まずは小さなCSVやExcelから、手を動かしながら身につけていきましょう。