Python

Python statisticsモジュールの使い方|平均・中央値・分散・標準偏差とNumPyとの違い

Python statisticsモジュールの使い方|平均・中央値・分散・標準偏差とNumPyとの違い

statistics は Python に標準で付いている統計モジュールです。import statistics と書くだけで、平均は mean、中央値は median、最頻値は mode、標準偏差は stdev で求まります。pip でのインストールは要りません。この記事では関数を「何を求めたいか」で引けるように並べ、NumPy や pandas と値が食い違う理由、欠損値(NaN)で結果が狂う条件、100万件での速度まで、Python 3.15.0 で実行した出力付きで示します。

まとめ:statisticsの関数早見表と値が合わない3つの原因

求めたい値から関数を選びます。追加バージョンは公式ドキュメント(statistics — 数理統計関数)の記載です。

求めたい値 関数 追加バージョン
平均(算術平均) mean / fmean 3.4 / 3.8
幾何平均・調和平均 geometric_mean / harmonic_mean 3.8 / 3.6
中央値 median / median_low / median_high 3.4
階級データの中央値 median_grouped 3.4
最頻値 mode / multimode 3.4 / 3.8
分散 variance(標本)/ pvariance(母集団) 3.4
標準偏差 stdev(標本)/ pstdev(母集団) 3.4
四分位数・パーセンタイル quantiles 3.8
正規分布の確率 NormalDist 3.8
共分散・相関・単回帰 covariance / correlation / linear_regression 3.10
カーネル密度推定 kde / kde_random 3.13

他のツールと値がずれたときは、次の3点を疑ってください。

  • 標準偏差:statistics.stdev は n−1 で割る標本標準偏差です。numpy.std は既定で n で割るため、同じデータでも小さく出ます。
  • 四分位数:statistics.quantiles の既定は exclusive 方式です。numpy.percentile と pandas の quantile の既定は inclusive 方式と同じ値になるため、第1・第3四分位がずれます。
  • 欠損値:NaN が混じると median は並び順しだいで別の値を返します。呼ぶ前に NaN を除いてください。

速度にも差があります。100万件の平均は mean が約1.2秒、fmean が約21ミリ秒、NumPy 配列の mean() が約0.8ミリ秒でした。NumPy への移行は、配列への変換費用と集計の反復回数を含めて判断してください。リストの平均を一度だけ求めるなら、今回の実測では fmean が np.mean(list) より短時間でした。

statisticsモジュールの読み込みとインストールの要否

statistics は PEP 450 で提案され、Python 3.4 で標準ライブラリに入りました。仮想環境を作り直しても、追加の依存なしで使えます。

import statistics

scores = [62, 75, 75, 80, 88, 91, 94]
print(statistics.mean(scores))    # 80.71428571428571
print(statistics.median(scores))  # 80
print(statistics.mode(scores))    # 75

mean や median はリスト・タプル・イテレータを受け付けますが、kde や相関・回帰の関数などはシーケンスが必要です。int と float のほか、Fraction や Decimal を扱える関数もありますが、戻り値の型は関数によって異なります。金額計算では mean など対応する関数を選び、Decimal の精度と丸め規則も設定してください。mean や median などは空のデータで statistics.StatisticsError を送出しますが、multimode は空リストを返します。標本標準偏差の stdev と標本分散の variance は2件以上が必要です。

import statistics

try:
    statistics.mean([])
except statistics.StatisticsError as e:
    print(e)  # mean requires at least one data point

try:
    statistics.stdev([5])
except statistics.StatisticsError as e:
    print(e)  # stdev requires at least two data points

平均の求め方と使い分け(mean・fmean・幾何平均・調和平均)

mean と fmean の違い:戻り値の型と速度

mean は Decimal や Fraction の入力型を保ちますが、整数入力でも平均が整数でなければ float を返します。float や Decimal の結果には表現精度による丸めがあり得ます。fmean はすべてを float に変換して計算し、常に float を返します。公式ドキュメントも fmean を「mean より速い」と説明しており、後述の実測では約60倍の差がありました。3.11 からは weights 引数で加重平均も求められます。

import statistics
from fractions import Fraction
from decimal import Decimal

print(statistics.mean([Fraction(1, 3), Fraction(1, 6)]))  # 1/4
print(statistics.mean([Decimal("0.1"), Decimal("0.2")]))  # 0.15
print(statistics.fmean([0.1, 0.2]))                       # 0.15000000000000002

# 加重平均(3.11以降):小テスト・中間・期末を1:2:3で重み付け
print(statistics.fmean([70, 80, 90], weights=[1, 2, 3]))  # 83.33333333333333

Decimal の金額や Fraction の比率を扱うなら mean、float の測定値を大量に扱うなら fmean、と型で決めると迷いません。

幾何平均・調和平均:成長率と速度の平均

前年比の倍率のように掛け合わせる正の値には幾何平均を使います。速度は同じ距離ごとなら調和平均、同じ時間ごとなら算術平均で平均します。単価も同じ支出額ごとなら調和平均、同じ購入数量ごとなら算術平均です。算術平均で代用すると、成長率は過大に、往復の平均時速は高めに出ます。

import statistics

# 3年間の前年比:+10%、+20%、−10%
print(statistics.geometric_mean([1.10, 1.20, 0.90]))  # 1.059104500597819
print(statistics.mean([1.10, 1.20, 0.90]))            # 1.0666666666666667

# 行き40km/h・帰り60km/hで同じ距離を往復したときの平均時速
print(statistics.harmonic_mean([40, 60]))             # 48.0

3年間の年平均成長率は約5.9%で、算術平均の6.7%ではありません。往復の平均時速も50ではなく48です。

中央値の求め方と補間方式(median・median_low・median_high・median_grouped)

偶数件の中央値:median と median_low・median_high

median は件数が奇数なら真ん中の値、偶数なら中央2値の平均を返します。偶数件でデータに実在する値を返したいときは、小さい側を median_low、大きい側を median_high で取ります。並べ替えはせずに渡して構いません。

import statistics

data = [7, 1, 5, 3]
print(statistics.median(data))       # 4.0
print(statistics.median_low(data))   # 3
print(statistics.median_high(data))  # 5

階級データの中央値:median_grouped

「15歳以上25歳未満が1人、25歳以上35歳未満が2人、35歳以上45歳未満が3人、45歳以上55歳未満が1人」のように階級でまとめた集計表しか残っていない場合は、median_grouped で中央値を推定します。各値を階級の中点として渡し、interval に階級幅を指定します(既定は1)。

import statistics

# 階級の中点(20, 30, 40, 50)を人数分並べる。階級幅は10
ages = [20, 30, 30, 40, 40, 40, 50]
print(statistics.median(ages))                       # 40
print(statistics.median_grouped(ages, interval=10))  # 36.666666666666664

median は40を返しますが、median_grouped は「40の階級(35〜45)の中で、下から何人目か」を比例配分して約36.7と推定します。階級内で一様に分布すると仮定し、累積度数の半分である3.5を使います。35未満の累積度数3との差は0.5なので、35 + 10 × 0.5 ÷ 3 ≒ 36.7 です。

最頻値の求め方と同数時の扱い(mode・multimode)

最頻値は血液型やアンケートの選択肢のように、平均が意味を持たないデータの代表値に使います。mode は 3.8 以降、最頻値が複数あっても例外を出さず最初に現れた値を返します。全部ほしいときは multimode を使います。

import statistics

blood = ["A", "O", "A", "B", "O"]
print(statistics.mode(blood))       # A
print(statistics.multimode(blood))  # ['A', 'O']

3.7 以前の mode は同数の最頻値があると StatisticsError を送出していました。古い環境で動かすコードは multimode が使えない点も含めて注意が要ります。

分散・標準偏差の求め方と分母の違い(variance・pvariance・stdev・pstdev)

分散と標準偏差は、標本用(variance・stdev)と母集団用(pvariance・pstdev)で関数が分かれます。標本用は n−1、母集団用は n で割ります。

import statistics

d = [2, 4, 4, 4, 5, 5, 7, 9]
print(statistics.pvariance(d))  # 4
print(statistics.pstdev(d))     # 2.0
print(statistics.variance(d))   # 4.571428571428571
print(statistics.stdev(d))      # 2.138089935299395

計算式そのものや手計算の手順は標準偏差の求め方(計算表・公式とSTDEV.S/STDEV.P)で解説しています。

標本と母集団を選ぶ基準

手元のデータが調べたい対象の全部(クラス全員の点数、ある月の全注文)なら pstdev・pvariance を使います。全体の一部を抜き出したデータから全体のばらつきを推定する(抜き取り検査、アンケート回答者)なら stdev・variance です。迷ったときは、記録されたデータ自体のばらつきを記述するのか、その背後の母集団のばらつきを推定するのかを先に決めてください。平均を計算済みなら、stdev・variance には第2引数 xbar、pstdev・pvariance には mu として渡せます。標本平均のばらつき(標準誤差)を出したい場合は、stdev を √n で割ります。考え方は標準誤差の求め方と標準偏差との違いにまとめています。

NumPy・pandas・Excelとの既定の違い(ddof)

「stdev と np.std の値が合わない」の原因は、割る数の既定値の違いです。numpy.std は「By default ddof is zero」で n で割り、pandas.Series.std は「Normalized by N-1 by default」で n−1 で割ります。上と同じデータ d で比べると次のとおりです。

書き方 割る数 dの結果
statistics.stdev(d) n−1 2.138…
statistics.pstdev(d) n 2.0
np.std(a) n(ddof=0) 2.0
np.std(a, ddof=1) n−1 2.138…
pd.Series(d).std() n−1(ddof=1) 2.138…
Excel STDEV.S / STDEV.P n−1 / n 2.138… / 2.0
import statistics
import numpy as np
import pandas as pd

d = [2, 4, 4, 4, 5, 5, 7, 9]
a = np.array(d)
print(np.std(a))              # 2.0
print(np.std(a, ddof=1))      # 2.138089935299395
print(pd.Series(d).std())     # 2.138089935299395
print(statistics.stdev(d))    # 2.138089935299395

この比較では np.std の既定が pstdev・STDEV.P に、pd.Series(d).std() の既定が stdev・STDEV.S に対応します。statistics・pandas・Excel の結果と突き合わせるときは、NumPy 側に ddof=1 を付けて揃えてください。分散の np.var も同じで、既定では pvariance と一致します。

四分位数・パーセンタイルの計算方式(quantiles)

quantiles はデータを n 等分する境界値を返します。既定の n=4 で四分位数、n=100 でパーセンタイルです。公式ドキュメントによると、既定の method=”exclusive” は母集団に標本より極端な値がありうるデータ向けで、method=”inclusive” は母集団そのものや最小・最大を含むと分かっている標本向けです。

import statistics
import numpy as np
import pandas as pd

q = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(statistics.quantiles(q, n=4))                      # [2.75, 5.5, 8.25]
print(statistics.quantiles(q, n=4, method="inclusive"))  # [3.25, 5.5, 7.75]
print(np.percentile(q, [25, 50, 75]))                    # [3.25 5.5  7.75]
print(pd.Series(q).quantile([0.25, 0.5, 0.75]).tolist()) # [3.25, 5.5, 7.75]
print(np.percentile(q, [25, 50, 75], method="weibull"))  # [2.75 5.5  8.25]

NumPy の numpy.percentile(既定 method=”linear”)と pandas の Series.quantile(既定 interpolation=”linear”)は、statistics の inclusive と同じ値になりました。この例では NumPy 側を method="weibull" にすると statistics の既定と一致します。ただし、少数データの端の分位点では statistics が外挿し、NumPy は最小値・最大値に制限するため、一致しない場合があります。箱ひげ図を別ツールで描くなら、どちらの方式で計算したかを図の注記に残してください。3.13 からは1件だけのデータでも例外を出さず、quantiles([5]) は [5, 5, 5] を返します。

正規分布の確率と境界値の計算(NormalDist)

NormalDist は平均 mu と標準偏差 sigma から正規分布を作り、累積確率(cdf)、その逆(inv_cdf)、標準得点(zscore)、2つの分布の重なり(overlap)を計算します。平均60点・標準偏差10点の試験を例にします。

import statistics

nd = statistics.NormalDist(mu=60, sigma=10)
print(round(1 - nd.cdf(75), 4))  # 0.0668  75点以上は上位約6.7%
print(round(nd.inv_cdf(0.9), 2)) # 72.82   上位10%の境目
print(nd.zscore(75))             # 1.5     偏差値なら50+10×1.5=65
print(round(nd.overlap(statistics.NormalDist(70, 10)), 4))  # 0.6171

# 手元のデータから平均と標本標準偏差を推定して作る
fs = statistics.NormalDist.from_samples([2, 4, 4, 4, 5, 5, 7, 9])
print(fs.mean, fs.stdev)         # 5.0 2.138089935299395

from_samples は fmean と stdev で推定するため、標準偏差は n−1 基準になります。管理図のように平均±3σで異常を判定する用途の実装例は統計的工程管理(SPC)の実装手順で扱っています。

相関・回帰・密度推定の計算(correlation・linear_regression・kde)

correlation と linear_regressionによる相関・単回帰の計算(3.10以降)

2変数の関係は covariance(共分散)、correlation(ピアソンの相関係数)、linear_regression(単回帰の傾きと切片)で求めます。3.12 で method="ranked" によるスピアマンの順位相関、3.11 で原点を通る回帰(proportional=True)が加わりました。

import statistics

x = [1, 2, 3, 4, 5]
y = [2, 4, 5, 4, 5]
print(statistics.covariance(x, y))                         # 1.5
print(round(statistics.correlation(x, y), 4))              # 0.7746
print(round(statistics.correlation(x, y, method="ranked"), 4))  # 0.7379
print(statistics.linear_regression(x, y))
# LinearRegression(slope=0.6, intercept=2.2)
print(statistics.linear_regression(x, y, proportional=True))
# LinearRegression(slope=1.2, intercept=0.0)

説明変数が2つ以上になる重回帰は statistics では扱えません。その段階で NumPy や scikit-learn、statsmodels に移ります。

kde と kde_randomによる密度推定と乱数生成(3.13以降)

kde はデータからなめらかな確率密度関数を推定し、関数として返します。h は平滑化の幅で、小さいほどデータ点ごとの山が立ちます。cumulative=True で累積分布関数、kde_random で推定した分布からの乱数生成になります。

import statistics

sample = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2]
f_hat = statistics.kde(sample, h=1.5)
print(round(f_hat(2.5), 4))  # 0.0619

F_hat = statistics.kde(sample, h=1.5, cumulative=True)
print(round(F_hat(2.5), 4))  # 0.6117  2.5以下になる確率の推定

推定元のデータを変更した場合は、kde を呼び直して推定関数を作り直してください。ヒストグラムの代わりに分布の形をざっと掴む用途なら、matplotlib や SciPy を入れずにここまで済みます。

NaN(欠損値)を含むデータの落とし穴

公式ドキュメントは、NaN を含むデータでは median・median_low・median_high・median_grouped・mode・multimode・quantiles が「予期しない、または未定義の動作」をすると明記しています。NaN は大小比較が常に False になるため、並べ替えが崩れるからです。実際に並び順を変えるだけで中央値が変わります。

import math
import statistics
from itertools import filterfalse

nan = float("nan")
print(statistics.median([nan, 1, 2, 3, 4]))  # 2
print(statistics.median([1, 2, 3, 4, nan]))  # 3
print(statistics.mean([1, 2, nan]))          # nan

clean = list(filterfalse(math.isnan, [1, 2, nan]))
print(statistics.mean(clean))                # 1.5

NaN が混じると mean は nan を返しますが、median は並び順によって nan を返したり、もっともらしい数値を返したりします。なお、CSV の空欄をそのまま float() に通すと NaN ではなく ValueError になります。pandas の read_csv などで欠損値を NaN に変換した場合は、statistics に渡す前に math.isnan で除くのを習慣にしてください。欠損の扱いを列ごとに決めたいなら、既定で NaN を飛ばす pandas(skipna=True)や NumPy の np.nanmean のほうが安全です。

statisticsとNumPy・pandasの使い分け(速度と精度の実測)

random.seed(1) で固定した random.random() の float 乱数100万件を使い、データ生成と配列作成は計測から外しました。各処理は1回の実行時間を timeit で mean・stdev は3回、その他は10回測り、最小値を採っています。環境は Python 3.15.0・NumPy 2.5.4・Intel Core i9-9880H(macOS)です。

処理 100万件の時間
statistics.mean(list) 約1,219ミリ秒
statistics.fmean(list) 約20.7ミリ秒
np.mean(list)(配列への変換込み) 約108.7ミリ秒
NumPy配列.mean() 約0.8ミリ秒
statistics.stdev(list) 約1,901ミリ秒
NumPy配列.std(ddof=1) 約3.3ミリ秒
statistics.median(list) 約680ミリ秒
np.median(配列) 約23.3ミリ秒

mean と stdev が遅いのは、float を正確な分数に変換してから足し合わせているためです。次の例では statistics.mean と fmean が小さい項の寄与を保持し、np.mean は加算時の丸めでその寄与を失います。精度の差は入力値・データ型・計算方法によって変わります。

import statistics
import numpy as np

p = [1e16, 1.0, -1e16]
print(statistics.mean(p))   # 0.3333333333333333
print(statistics.fmean(p))  # 0.3333333333333333
print(np.mean(p))           # 0.0

判断は次の順で行えば足ります。

  • statistics のままでよい:数千件までの1次元データ、依存を増やせない配布スクリプトや Lambda 関数、Decimal・Fraction で誤差を出したくない金額計算。
  • fmean に替える:float の数万〜数十万件で mean が遅いとき。コードの変更は関数名だけです。
  • NumPy に移る:数十万件以上、多次元配列、行列演算が必要なとき。リストを毎回 np.mean に渡すと変換に時間を取られるので、最初に配列にして持ち回します。詳しくはNumPyの基本的な使い方を参照してください。
  • pandas を使う:CSV や表を列ごとに集計する、欠損値やグループ集計を伴うとき。pandasでできることにまとめています。

検定や分布のあてはめまで進むなら SciPy の scipy.stats が受け皿になります。集計処理を既存の業務システムへ組み込む場合は、フルスクラッチのシステム開発としてご相談いただけます。

平均値と中央値の使い分け(外れ値があるとき)

外れ値が混じるデータでは、平均値より中央値が多数派の実態に近くなります。年収(万円)が 300・350・400・450・3000 の5人なら、mean は900、median は400です。5人中4人が平均を下回る900を「平均的な年収」と呼ぶのは無理があります。左右対称で外れ値の少ない測定値なら平均、年収・価格・アクセス数のように右に裾が長い分布なら中央値、カテゴリデータなら最頻値、と分布の形で決めてください。

よくある質問

statisticsモジュールはインストールが必要ですか?

不要です。Python 3.4 以降に標準で含まれているため、import statistics だけで使えます。pip install statistics で入る PyPI のパッケージは標準ライブラリとは別物なので、入れないでください。

statistics.stdevとnp.stdの値が違うのはなぜですか?

割る数が違うためです。stdev は n−1 で割る標本標準偏差、np.std は既定で n で割ります。np.std(a, ddof=1) にすると stdev と一致し、ddof を付けない np.std は pstdev と一致します。

statistics.meanとfmeanはどちらを使えばよいですか?

float のデータなら fmean です。100万件で mean の約60倍速く、加重平均(weights、3.11以降)も使えます。Decimal や Fraction の型を保ったまま正確に計算したいときだけ mean を使います。

Pythonで中央値を求めるにはどうしますか?

statistics.median(データ) で求まります。並べ替えは不要で、偶数件なら中央2値の平均を返します。データに実在する値がほしいときは median_low か median_high、NumPy 配列なら np.median を使います。NaN が混じると結果が並び順で変わるので、先に除いてください。

どのPythonのバージョンで使えますか?

モジュール自体は 3.4 以降で使えます。fmean・geometric_mean・multimode・quantiles・NormalDist は 3.8、covariance・correlation・linear_regression は 3.10、kde・kde_random は 3.13 で追加されました。2026年10月時点の最新は Python 3.15.2 です。3.15 で追加された機能全般はPython 3.15の新機能で紹介しています。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  2. 2026.10.28 テックブログ リンツ不正アクセス:314万件の対象確認とパスワード強制リセット後の対処
  3. 2026.10.29 テックブログ So-net情報漏洩:258万件の対象確認とメールのパスワード再設定、窓口の番号
  4. 2026.10.27 テックブログ Joshin(ジョーシン)不正アクセス:最大600万件の対象とサブスク初期パスワードの対処
  5. 2026.10.23 テックブログ Renta!はいつ再開?不正アクセスでメールアドレス等が流出したおそれ・再開時のパスワード再設定と運営側の対応

RELATED POSTS 関連記事

目次