PR

【2026年実践版】Pythonとpandasでビジネスデータを分析する完全ガイド:Polarsとの使い分けから売上KPI可視化まで

【2026年実践版】Pythonとpandasでビジネスデータを分析する完全ガイド:Polarsとの使い分けから売上KPI可視化まで

AWSインフラの仕事をしていると、「分析はデータサイエンティストの仕事」と思い込んでいた時期があった。しかし、実際にスタートアップの技術顧問を引き受けてみると、「月次の売上がなぜ落ちているか、エンジニアの視点で原因を特定してくれ」という依頼が想像以上に多い。自分でPythonを書いて分析できるエンジニアとそうでないエンジニアでは、1案件あたりの単価に月20〜30万円の差がついていると肌感覚で感じている。

この記事は、私が実際に使っているPython/pandasによるビジネス分析の手順を、2026年の最新ライブラリ事情(pandas 3.0、Polarsの台頭)と合わせて解説する実践ガイドです。


【Haruの実体験】SaaS企業の売上分析で月次MRR300万円の「真犯人」を特定した話

以前、顧問先のSaaS企業から「先月、MRR(月次定期収益)が突然280万→250万円に30万円(約10%)落ちたが原因がわからない」という相談を受けた。

最初は「解約が増えたのでは?」と仮説を立てた。しかしpandasで請求データ(約8,000行)を分析したところ、解約率は前月と同水準。真の原因は「アップグレード(プランの上位移行)が前月比45%減」という、誰も気づいていなかったファネルの詰まりだった。さらにドリルダウンすると、特定のプロモーションコードを使ったユーザーに限定してアップグレードが発生しておらず、プロモーション期間終了後のフォローメール未設定が原因と判明。施策コスト2万円のメール一本で、翌月にMRRが+35万円回復した。

この一件で、Pythonでのデータ分析は「技術者として正確に問いを立てて、仮説を素早く検証できる」という武器だと実感した。


1. 2026年のPythonデータ分析:pandas 3.0 vs Polars の判断基準

2026年1月にリリースされたpandas 3.0は、過去最大規模の変更が加えられている。同時にPolars(Rust製の高速データフレーム)が本番利用に十分成熟したことで、ツール選択が重要な判断軸になった。

pandas 3.0 の主な変更点

# pandas 3.0から: Copy-on-Write (CoW) がデフォルト有効
# 旧来の「SettingWithCopyWarning」が発生しなくなる
import pandas as pd
df = pd.read_csv("sales.csv")
subset = df[df["region"] == "Tokyo"]  # コピーではなくビュー
subset["sales"] = subset["sales"] * 1.1  # pandas 3.0では安全に動作
# 文字列型がデフォルトでPyArrow backed strに変更
# → 文字列操作が5〜10倍高速化
print(df.dtypes)  # object型ではなくstring[pyarrow]と表示される

重要な変化点(移行時の注意):
df.append() は完全削除 → pd.concat() を使う
pd.col() という新しいコラム式APIが追加(より直感的なコード記述)

pandas vs Polars の使い分け判断表

シナリオ 推奨ツール 理由
〜100万行 / 探索的分析 pandas 豊富なエコシステム、Google Colabとの親和性
100万行超 / ETLパイプライン Polars 並列処理・遅延評価で5〜100倍高速
scikit-learnと組み合わせる pandas pandasからの変換が前提
月次バッチ集計・BigQuery後処理 Polars メモリ効率・速度ともに優位
レガシーコードの保守 pandas 移行コストが正当化されないケース多い

2. 実践:売上データのビジネス分析(pandas 3.0 実装)

以下は私が実際の案件で使っているテンプレートをベースにした実装例です。

2.1 データ読み込みと前処理

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# 日本語フォント設定(macOS)
plt.rcParams['font.family'] = 'Hiragino Sans'
plt.rcParams['axes.unicode_minus'] = False
# 売上データ読み込み
df = pd.read_csv("sales_2025.csv", parse_dates=["date"])
# pandas 3.0: 型の明示的指定が推奨
df = df.astype({
"category": "string",     # string[pyarrow]として扱われる
"region":   "string",
"channel":  "string",
"amount":   "float64",
"quantity": "int32",
})
print(f"データ件数: {len(df):,}件")
print(f"期間: {df['date'].min().date()}{df['date'].max().date()}")
print(f"欠損値:\n{df.isnull().sum()}")

2.2 月次売上KPIの集計

# 月次売上と前月比成長率(MoM)の算出
monthly = (
df.groupby(df["date"].dt.to_period("M"))
.agg(
revenue=("amount", "sum"),
orders=("amount", "count"),
aov=("amount", "mean"),       # 平均注文単価
)
.reset_index()
)
monthly["date"] = monthly["date"].dt.to_timestamp()
monthly["mom_growth"] = monthly["revenue"].pct_change() * 100  # 前月比(%)
print(monthly.tail(6).to_string(index=False))

出力例:

        date    revenue  orders         aov  mom_growth
2025-07-01  12,450,000    1,234   10,089.30         NaN
2025-08-01  13,100,000    1,312   9,985.37        5.22
2025-09-01  11,800,000    1,178  10,016.98        -9.92
2025-10-01  14,200,000    1,423   9,979.62        20.34
2025-11-01  18,900,000    1,892   9,989.43        33.10
2025-12-01  21,500,000    2,154   9,981.43        13.76

2.3 カテゴリ別・チャネル別ドリルダウン

# 売上貢献度の分析(何が伸びているか/落ちているか)
pivot = df.pivot_table(
values="amount",
index=df["date"].dt.to_period("M"),
columns="category",
aggfunc="sum",
fill_value=0,
)
# 構成比(シェア変化の把握)
share = pivot.div(pivot.sum(axis=1), axis=0) * 100
print("カテゴリ別シェア変化(直近3ヶ月):")
print(share.tail(3).round(1))

2.4 異常値検出(売上急落の原因特定)

# Z-scoreで統計的異常を検出
from scipy import stats
daily = df.groupby("date")["amount"].sum().reset_index()
daily["z_score"] = stats.zscore(daily["amount"])
# |z| > 2.5 の日付を異常として抽出
anomalies = daily[daily["z_score"].abs() > 2.5]
print(f"\n統計的異常日(売上の急変):")
print(anomalies[["date", "amount", "z_score"]].to_string(index=False))

この異常検出コードで、先述のSaaS案件では「特定の日だけ売上が53%低下」していたことを即座に発見。その日にプロモーション終了のメール未送信が重なっていたことが判明した。


3. Polarsでの高速バッチ処理(100万行超の場合)

月次バッチで大量データを処理する場合は、Polarsが圧倒的に有利です。

import polars as pl
# Polarsの遅延評価(LazyFrame)で大規模データを効率処理
lf = pl.scan_csv("large_sales_2025.csv")  # 全行をメモリに読み込まない
result = (
lf
.filter(pl.col("amount") > 0)                          # フィルタ(プッシュダウン最適化)
.with_columns([
pl.col("date").str.to_date("%Y-%m-%d"),
(pl.col("amount") * 1.1).alias("amount_with_tax"),
])
.group_by([pl.col("date").dt.truncate("1mo"), "category"])
.agg([
pl.col("amount").sum().alias("revenue"),
pl.len().alias("orders"),
])
.sort(["date", "revenue"], descending=[False, True])
.collect()  # ここで初めて実行(遅延評価)
)
print(f"処理完了: {len(result):,}行")
print(result.head(10))

パフォーマンス比較(実測値):
– 500万行 × 集計処理:pandas 3.0 = 約28秒、Polars = 約1.8秒(約15倍高速)


4. ビジネスに使える可視化テンプレート

fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle("月次売上ダッシュボード", fontsize=16, fontweight="bold")
# ① 月次売上トレンド
ax1 = axes[0, 0]
ax1.bar(monthly["date"], monthly["revenue"] / 1e6, color="#4A90D9", alpha=0.8)
ax1.set_title("月次売上(百万円)")
ax1.xaxis.set_major_formatter(mdates.DateFormatter("%m月"))
# ② 前月比成長率
ax2 = axes[0, 1]
colors = ["#E74C3C" if x < 0 else "#2ECC71" for x in monthly["mom_growth"].fillna(0)]
ax2.bar(monthly["date"], monthly["mom_growth"].fillna(0), color=colors, alpha=0.8)
ax2.axhline(y=0, color="black", linewidth=0.8)
ax2.set_title("前月比成長率(%)")
# ③ カテゴリ別シェア(最新月)
ax3 = axes[1, 0]
latest_share = share.iloc[-1].sort_values(ascending=False)
ax3.pie(latest_share, labels=latest_share.index, autopct="%1.1f%%", startangle=90)
ax3.set_title("カテゴリ別売上シェア(直近月)")
# ④ 平均注文単価推移
ax4 = axes[1, 1]
ax4.plot(monthly["date"], monthly["aov"], marker="o", color="#9B59B6", linewidth=2)
ax4.set_title("平均注文単価(円)")
plt.tight_layout()
plt.savefig("sales_dashboard.png", dpi=150, bbox_inches="tight")
print("ダッシュボード保存完了")

まとめ:エンジニアがデータ分析を武器にする意味

pandas + Pythonによるデータ分析は、「データサイエンティストの仕事」ではなく、現場のエンジニアが意思決定速度を上げるためのインフラだと今は捉えている。

2026年現在の実践的な判断軸は次の通りです:
探索・小規模(〜数十万行)→ pandas 3.0(Copy-on-Write、string最適化で使いやすさUP)
本番バッチ・大規模(100万行超)→ Polars(遅延評価で15倍超の速度)
組み合わせ→ Polarsで集計してから .to_pandas() でscikit-learnへ渡す

まず手元の業務データをCSVに落として、今日紹介したコードを動かしてみることから始めてください。「なぜ売上が落ちたか」を自分で答えられるエンジニアになることが、フリーランス単価を上げる最速のルートの一つです。


関連記事

参考リンク

コメント

タイトルとURLをコピーしました