閉じる

pandas入門 CSVとExcelの読み込み・整形・結合の基本と実例

データ分析で欠かせないのが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も入れておくと安心です。

Shell
# 仮想環境を使うことを推奨します(任意)
python -m venv .venv
. .venv/bin/activate  # Windowsは .venv\Scripts\activate

# pandasとExcelエンジン(openpyxl)のインストール
pip install -U pandas openpyxl
Python
# pandasの基本的なインポートとバージョン確認
import pandas as pd

print(pd.__version__)
実行結果
2.3.2

openpyxlはExcelの読み書きに必要になることが多いため、一緒に入れておくと後述のエラー回避になります。

データフレーム(DataFrame)の考え方

DataFrameとSeries

pandasの中心はDataFrame(二次元表)Series(一列の一次元データ)です。

行ラベル(index)と列ラベル(columns)が付与され、列ごとに型(dtype)を持ちます。

ベクトル化された演算を活用することで、forループを避けて高速に計算できます。

Python
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_csvCSV読み込みencoding, usecols, dtype, parse_dates, na_values
pd.read_excelExcel読み込み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)で保存します。

Python
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 を保存しました
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でほぼ解決できます。

Python
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では小数点がカンマのことがあります。

その場合は以下のように指定します。

Python
# セミコロン区切りや桁区切り・小数点記号の調整例
df_eu = pd.read_csv("eu_sales.csv", sep=";", decimal=",", thousands=".")

(上記は実ファイルがある場合の例です)

列名と型の指定

読み込み時に列と型を固定する

データ分析を効率的に進めるには、読み込みの段階でひと工夫するのが重要です。

特に大規模なデータでは、必要な列だけを正しいデータ型で読み込むことで、メモリ使用量を抑え、処理速度を向上させることができます。

  • parse_dates: 日付として扱いたい列名をリストで指定します。自動的に日付型(datetime64)に変換され、時系列分析が容易になります。
  • usecols: 読み込みたい列名をリストで指定します。不要な列を最初から除外します。
  • dtype: 列ごとにデータ型を辞書形式で指定します。意図しない型変換(例: IDが数値でなく浮動小数点になる)を防ぎ、データの一貫性を保ちます。
Python
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番目)をヘッダー行として読み込みます。ファイルの先頭にタイトルや説明などが含まれている場合に便利です。
Python
# 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として認識してくれます。

Python
# "", "NA", "N/A", "-" を欠損値として読み込む
df = pd.read_csv("sales.csv", na_values=["", "NA", "N/A", "-"])

行列の選択

DataFrameから必要な部分だけを取り出す操作は、データ分析の基本です。

列の選択と複数列の抽出

単一列の選択: df["列名"] のように指定します。返されるデータは1次元の Series オブジェクトです。

複数列の選択: df[["列名1", "列名2"]] のように、リストを二重の角括弧で囲んで指定します。こちらは2次元の DataFrame オブジェクトが返されます。

Python
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)

行を選択するには、主に lociloc を使います。この2つの違いを理解することが重要です。

  • loc: ラベルベースで選択します。行のインデックス名と列名を指定します。(例: df.loc[0, "product_id"]
  • iloc: 整数位置ベースで選択します。0から始まる行番号と列番号を指定します。(例: df.iloc[0:3, 0:2]
Python
# 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) を使い、各条件式を () で囲みます。

Python
# 「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で複数列を指定した場合は、リストで各列の並び順を個別に設定できます。
Python
# 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 で対象列を指定すると、その列に欠損がある行だけを削除できます。
Python
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: 金額など、浮動小数点数の表示形式(小数点以下の桁数など)を整えたい場合に指定します。
Python
# 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として読み込むための基本関数です。

引数を指定しない場合、ブックの先頭にあるシートを自動的に読み込みます。

Python
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を値に持つ辞書となり、キーはシート名になります。
Python
# "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 を指定すると、ブックに含まれるすべてのシートを一度に読み込むことができます。

これも結果はシート名をキーとする辞書形式で返されます。

Python
# 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行目からデータが始まっていないケースがよくあります。

headerskiprows 引数で柔軟に対応できます。

  • header=1: 2行目(0から数えて1番目)を列名として読み込みます。
  • skiprows=2: 先頭から2行を完全に無視して、3行目から読み込みを開始します。
Python
# 例: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 構文を使うことで、ファイル操作を安全に完了できます。

Python
# "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引数に削除したい列名のリストを渡します。
Python
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']
ポイント

assignrenameは、sales.assign(...).rename(...) のようにメソッドを繋げて(メソッドチェーン)、一連の処理を流れるように記述できます。

文字列・日付の型変換

文字列整形と数値・日付への変換

手入力されたデータなどには、表記の揺れ(不要なスペース、大文字・小文字の混在)や、数値・日付が文字列として扱われていることが頻繁にあります。

これらを正しい型に変換し、データを綺麗に(クレンジング)します。

  • .strアクセサ: 文字列型の列に対して、strip()(空白除去)や upper()(大文字化)などの文字列操作を一括適用できます。
  • pd.to_numeric(): 文字列を数値型に変換します。errors="coerce" を指定すると、変換できない値をエラーにせず**欠損値(NaN)**に置き換えてくれるため、非常に堅牢です。
  • pd.to_datetime(): 文字列を日付型に変換します。こちらも errors="coerce" が有効です。
  • .dtアクセサ: 日付型の列から、年・月・曜日などの要素を簡単に取り出せます。
Python
# .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を横に連結します。

Python
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つにまとめる際によく使われます。

Python
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)など、複数の集計を一度に実行できます。

("新しい列名", ("元の列名", "集計方法")) という書き方をすると、結果の列名が分かりやすくなりおすすめです。

Python
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の代わり)
Python
# 行に「都道府県」、列に「商品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
    対処:dtypeconvertersで明示。読み込み後はto_numeric(..., errors="coerce")で正規化。

参照とコピー

  • 症状: SettingWithCopyWarning
    対処: チェーン代入を避け、df.loc[mask, "col"] = valueの形に統一。必要に応じて.copy()を明示。
Python
# 悪い例(警告が出やすい)
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の利用を検討。
Python
# 大きな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から、手を動かしながら身につけていきましょう。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!