いろは3013586 views
高校生物551985 views
小学社会310647 views
世界の国564972 views
中学社会669002 views
Computer368461 views
高校化学2925825 views
高校国語788606 views
LaTeX962713 views
MathPython498072 views
Help
Tools
NewsSpreadsheetCalendarBookkeepingMarkdown TablesLanguage Model NewsLinux CommandsSlidesTier ListPen ToolIllustrationCrayonWatercolorPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchSwirl EffectLine ArtOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksCherry BlossomWater RippleWater SplashBreaking GlassGlass TextureFabric TextureWood GrainMarble TextureBrick Wall TextureMetal TextureWashi Paper TextureCardboard TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalUniversity Entrance Exam MathColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsRolling DiceCross SectionsMotion PathMechanicsWavesUniversity Entrance Exam Physics解析力学Quantum MechanicsStatistical MechanicsRelativityCelestial MechanicsAstrophysicsCosmologyElectromagnetic WavesCapacitorsLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHOrganic Reaction MapPeriodic TableComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseMuscle ContractionHormones and HomeostasisRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic TheoryShogi StrategyPiano Rhythm Game

English

NumPy と pandas で分散の答えが違う!pandas の要約統計量

同じ 10 個の数から、NumPy の np.var は 3.6 を返し、pandas の .var() は 4.0 を返します。

計算がちがうわけではありません。既定の ddof が、片方は 、もう片方は だからです[1]

pandas で要約統計量を出すときは、この既定を頭に置いておく必要があります。

Series と DataFrame

1 列ぶんのデータが Series、表になったものが DataFrame です。

import numpy as np
import pandas as pd

data = pd.Series([4, 8, 5, 9, 6, 7, 3, 8, 6, 4])

Series は値の並びに見出しがついたものと考えると分かりやすい。見出しを指定しなければ から順に振られます。

DataFrame は、その Series を列として横に並べたものです。列ごとに型が違ってよいので、数値と文字列を同じ表に置けます。

基本の統計量

メソッドとして呼びます。出力を右にコメントで添えました。

print(data.mean())              # 6.0
print(data.var())               # 4.0
print(data.var(ddof=0))         # 3.6
print(data.std())               # 2.0
print(np.var(data.to_numpy()))  # 3.6

pandas の .var() は既定で で割ります[1]。不偏分散のほうです。

最後の行で同じデータを NumPy に渡すと 3.6 が返りました。既定が で割るほうだからです。

pandas の既定

ddof=1。 で割る不偏分散

NumPy の既定

ddof=0。 で割る標本分散

どちらも引数で切りかえられます。混ぜて使うときは、どちらの既定に合わせるかを先に決めておく。

同じ集計を 2 つの道具で書き直したら値が変わった、という事故はここから起きます。

describe で一度に出す

DataFrame には、主な統計量をまとめて返すメソッドがあります。

frame = pd.DataFrame({
	'国語': [62, 72, 66, 70, 58],
	'数学': [68, 75, 64, 80, 55],
	'組': ['A', 'B', 'A', 'B', 'A'],
})

print(frame.describe())

出力はこうなります。

国語         数学
count   5.000000   5.000000
mean   65.600000  68.400000
std     5.727128   9.710819
min    58.000000  55.000000
25%    62.000000  64.000000
50%    66.000000  68.000000
75%    70.000000  75.000000
max    72.000000  80.000000

数値の列だけが選ばれ、文字列の「組」は落ちています。数値の列がまったくない場合だけ、文字列の列が対象になります[2]

count は欠測を除いた個数です。std はここでも で割った値になります。

50% の行が中央値です。国語の中央値 66.0 は平均 65.6 とほぼ同じで、大きな偏りはありません。

数学のほうは std が 9.71 と大きく、min 55 から max 80 まで開いています。同じ 5 人でも、科目によって散らばりが違う。

型を確かめる

集計の前に、各列がどう読みこまれたかを見ておきます。

print(frame.dtypes)
国語    int64
数学    int64str
dtype: object

数値の列が int64、文字列の列が str になっています。ここが意図と違っていると、集計の結果もおかしくなる。

外部のファイルから読みこんだ場合、数値のはずの列が文字列になっていることがあります。空白や単位の記号が 1 つ混じっているだけで、列全体が文字列と判定される。

平均が出ないとき、まず疑うのはこの型です。エラーにならずに文字列の連結が起きる場合もあり、そのほうがたちが悪い。

行と列のどちらへ集計するか

DataFrame のメソッドは、既定で列ごとに集計します。

print(frame[['国語', '数学']].median())
print(frame[['国語', '数学']].mean(axis=1))
国語    66.0
数学    68.0
dtype: float64
0    65.0
1    73.5
2    65.0
3    75.0
4    56.5
dtype: float64

上は科目ごとの中央値、下は 1 人ごとの 2 科目の平均です。axis=1 を渡すと横方向に集計されます。

科目ごとの平均を出したいのか、生徒ごとの平均を出したいのか。同じデータでも、集計の向きで答える問いが変わります。

群ごとに集計する

列の値でグループに分け、それぞれの統計量を出せます。

print(frame.groupby('組')['数学'].mean())
A    62.333333
B    77.500000
Name: 数学, dtype: float64

複数の統計量をまとめて出すこともできます。

print(frame.groupby('組')['数学'].agg(['count', 'mean', 'std']))
count       mean       stdA      3  62.333333  6.658328
B      2  77.500000  3.535534

A 組が 3 人、B 組が 2 人です。この人数を見ずに平均だけを比べると、判断を誤ります。

B 組の std が 3.54 と小さく見えますが、2 人しかいないので自由度は 1 しかありません。この値から母集団のばらつきを語る根拠は薄い。

groupby の出力には count を必ず添えておくと、あとで読み返したときに人数が分かります。

列どうしの関係

数値の列だけをとり出して、相関行列や共分散行列を出せます。

print(frame[['国語', '数学']].corr())
print(frame[['国語', '数学']].cov())
国語        数学
国語  1.000000  0.875661
数学  0.875661  1.000000

      国語    数学
国語  32.8  48.7
数学  48.7  94.3

対角線が になるのは、自分自身との相関だからです。共分散行列のほうは、対角線に各列の分散が並びます。

国語の分散 32.8 と数学の分散 94.3。数学のほうが 3 倍近く散らばっています。

相関 0.876 は強い正の関係ですが、5 人ぶんしかありません。この人数では、相関係数の値そのものが大きく振れます。

欠測値は既定で落ちる

pandas の集計は、既定で欠測を無視します[1]

data = pd.Series([4.0, 8.0, np.nan, 9.0, 6.0])

print(data.mean())          # 6.75
print(data.count())         # 4
print(len(data))            # 5
print(data.isna().sum())    # 1

.mean() が 6.75 を返しました。欠測を除いた 4 個で計算しています。

NumPy が nan を返したのとは反対の設計です。pandas のほうが便利ですが、欠測があることに気づかないまま進む危険もある。

.count() は欠測を除いた個数、len() はもとの長さです。この 2 つがずれていたら、欠測があるということ。

落とし穴は合計を自分で個数で割ったときです。.sum() は欠測を飛ばすのに、len() は飛ばしません。

この例では になり、正しい 6.75 とずれます。割る相手は .count() のほう。

欠測をどう扱うか

集計が通ってしまうぶん、欠測の扱いは自分で決める必要があります。

まず isna().sum() で何個あるかを数える

無作為に抜けているかどうかを考える

落とすか埋めるかを決めて、決めたことを記録する

大きい値だけが抜けている場合、残りで計算した平均は母平均の推定になりません。関数は落としてくれますが、落としてよいかは判断できない。

報告するときは、何個のうち何個で計算したかを一緒に書きます。describe の count がその役目を果たします。

pandas の Series に .var() を当てたら 4.0、np.var で同じデータを計算したら 3.6 でした。原因はどれですか。

  • 既定の ddof が pandas は 1、NumPy は 0 だから
  • pandas が欠測を落としているから
  • 計算の精度が違うから
__RESULT__

pandas は で割る不偏分散、NumPy は で割る標本分散を既定にしています。この例に欠測はなく、精度の問題でもありません。data.var(ddof=0) とすれば 3.6 で一致します。

describe を 1 度呼べば、count と mean と std がまとめて出ます。あとは ddof の既定と、count と len のずれだけ確かめる。

型を見て、向きを決めて、欠測を数える。集計そのものより、その前後のほうに手間をかける価値があります。

参考

pandas.Series.var. pandas documentation.
pandas.DataFrame.describe. pandas documentation.
numpy.var. NumPy Reference.
同じ 10 個から np.var は 3.6、pandas の .var() は 4.0。既定の ddof が $0$ と $1$ で違うためです。describe を 1 度呼べば count、mean、std、四分位数までまとまって出て、文字列の列は自動で落ちます。dtypes で型を確かめ、axis で集計の向きを決め、groupby には count を添える。A 組 3 人と B 組 2 人の平均を並べるとき、人数を見ずに比べると判断を誤ります。欠測は既定で無視されるので .mean() は通りますが、.sum() を len() で割ると 6.75 が 5.4 になる。実行して確かめた出力とともに、集計の前後で見るところを並べます。