赤飯にかかったアレ

雑多なメモ帳

Pythonで書く、へっぽこ社会人の統計学習[平均と分散]

 

 

 

 

何故必要になったのか

2020年1月から転職をした結果、データを使った占い師になってしまい逃げ続けていた統計をとうとう使うことになってしまいました。

こうなったら一緒に使うpythonも使いながら統計を使いつつ備忘録を残そうと思います。

※ jupyter notebookって色々な形式に変換できるんですね

 

平均値と分散

e-stat 男女別人口-全国,都道府県(大正9年~平成27年)の国勢調査データから 平均と分散を求めるところから始めたいと思います

In [1]:
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
%matplotlib inline
# 有効桁数を3桁にする
pd.set_option("precision", 3)
In [2]:
# データの読み込み
data = pd.read_csv("data/Census.csv", encoding="shift-jis")
In [3]:
data.head()
Out[3]:
 
  都道府県コード 都道府県名 元号 和暦(年) 西暦(年) 人口(総数) 人口(男) 人口(女)
0 00 全国 大正 9.0 1920.0 NaN 55963053 28044185 27918868
1 01 北海道 大正 9.0 1920.0 NaN 2359183 1244322 1114861
2 02 青森県 大正 9.0 1920.0 NaN 756454 381293 375161
3 03 岩手県 大正 9.0 1920.0 NaN 845540 421069 424471
4 04 宮城県 大正 9.0 1920.0 NaN 961768 485309 476459
In [4]:
data.dtypes
Out[4]:
都道府県コード     object
都道府県名       object
元号          object
和暦(年)      float64
西暦(年)      float64
注           object
人口(総数)      object
人口(男)       object
人口(女)       object
dtype: object
In [5]:
# カラム名の変更
df = data.rename(columns={"人口(総数)": "全人口", "人口(男)": "男", "人口(女)": "女"})
df.head()
Out[5]:
 
  都道府県コード 都道府県名 元号 和暦(年) 西暦(年) 全人口
0 00 全国 大正 9.0 1920.0 NaN 55963053 28044185 27918868
1 01 北海道 大正 9.0 1920.0 NaN 2359183 1244322 1114861
2 02 青森県 大正 9.0 1920.0 NaN 756454 381293 375161
3 03 岩手県 大正 9.0 1920.0 NaN 845540 421069 424471
4 04 宮城県 大正 9.0 1920.0 NaN 961768 485309 476459
In [6]:
# objectをfloatに変換
for i in ["都道府県コード", "全人口", "男", "女"]:
    df[i] = pd.to_numeric(df[i], errors="coerce")
In [7]:
df.dtypes
Out[7]:
都道府県コード    float64
都道府県名       object
元号          object
和暦(年)      float64
西暦(年)      float64
注           object
全人口        float64
男          float64
女          float64
dtype: object
In [8]:
# 人口の抽出
populations = np.array(df["全人口"])[1:11]
populations
Out[8]:
array([2359183.,  756454.,  845540.,  961768.,  898537.,  968925.,
       1362750., 1350400., 1046479., 1052610.])
In [9]:
df["全人口"].head(12)
Out[9]:
0     5.596e+07
1     2.359e+06
2     7.565e+05
3     8.455e+05
4     9.618e+05
5     8.985e+05
6     9.689e+05
7     1.363e+06
8     1.350e+06
9     1.046e+06
10    1.053e+06
11    1.320e+06
Name: 全人口, dtype: float64
In [10]:
# 都道府県名の抽出(最初の全国は除いて抜き出す)
prefectures = df["都道府県名"][1:11]
prefectures
Out[10]:
1     北海道
2     青森県
3     岩手県
4     宮城県
5     秋田県
6     山形県
7     福島県
8     茨城県
9     栃木県
10    群馬県
Name: 都道府県名, dtype: object
In [11]:
# 結合
data=pd.DataFrame({"人口":populations},
                 index=pd.Index(prefectures,
                               name="都道府県名"))
data
Out[11]:
 
  人口
都道府県名  
北海道 2.359e+06
青森県 7.565e+05
岩手県 8.455e+05
宮城県 9.618e+05
秋田県 8.985e+05
山形県 9.689e+05
福島県 1.363e+06
茨城県 1.350e+06
栃木県 1.046e+06
群馬県 1.053e+06
 

平均値

データを全て足し、データ数で割る。 $$\overline{x} = \frac{1}{n} \sum_{i=1}^n{x_i}$$

In [12]:
# 平均値
sum(data["人口"])/len(data["人口"])
Out[12]:
1160264.6
In [13]:
# numpyで平均
np.mean(data)
Out[13]:
人口    1.160e+06
dtype: float64
In [14]:
data.mean()
Out[14]:
人口    1.160e+06
dtype: float64
 

中央値

データを大きさの順に並べたときにちょうど中央に位置するデータを中央値(median)といい、

データ数$n$が偶数の時はデータ数の真ん中に当たる値が二つでき、 それを足して$2$で割ったもの$\frac{n}{2}$と$\frac{n+1}{2}$の平均を中央値として求める

データ数$n$が奇数の時は$\frac{(n+1)}{2}$で求める

In [15]:
# 中央値
np.median(data)
Out[15]:
1007702.0
In [16]:
data.median()
Out[16]:
人口    1.008e+06
dtype: float64
In [17]:
sort_populat = np.sort(np.array([1, 1, 2, 2, 3, 3, 4]))
# sort_populat = np.sort(data["人口"])
n_len = len(sort_populat)

if (n_len % 2) == 0:
    m0 = sort_populat[n_len//2-1]
    m1 = sort_populat[n_len//2]
    median = (m0 + m1)/2
else:
    median = sort_populat[(n_len+1)//2 -1]

median
Out[17]:
2
 

最頻値(mode)

データの中でもっとも多く出現するデータを最頻値という

In [18]:
pd.Series([1,1,1,2,2,3]).mode()
Out[18]:
0    1
dtype: int64
 

分散と標準偏差

平均値や中央値はそのデータ全体の性質を判断するために用いられ、 代表値とも呼ばれる。

この代表値と各データを比較した時のバラツキの尺度を比較することで、

データの特殊性が判断できる。

偏差(deviation)

偏差は各データが平均からどれだけ離れているかを示し、 その平均的な大きさで数値の分散度を測定できる。

各データを平均値で引くことで求める $$ \sum_{i=1}^n{x_i-\overline{x} }$$

また偏差の平均は0になる。

In [19]:
mean = np.mean(data["人口"])
deviation = data["人口"] - mean
deviation
Out[19]:
都道府県名
北海道    1.199e+06
青森県   -4.038e+05
岩手県   -3.147e+05
宮城県   -1.985e+05
秋田県   -2.617e+05
山形県   -1.913e+05
福島県    2.025e+05
茨城県    1.901e+05
栃木県   -1.138e+05
群馬県   -1.077e+05
Name: 人口, dtype: float64
In [20]:
np.mean(deviation)
Out[20]:
-9.313225746154786e-11
In [21]:
mean = np.mean(populations)
deviation = populations - mean
print(deviation)
print(np.mean(deviation))
 
[1198918.4 -403810.6 -314724.6 -198496.6 -261727.6 -191339.6  202485.4
  190135.4 -113785.6 -107654.6]
-9.313225746154786e-11
In [22]:
# Dataframeに偏差を追加
summary_df = data.copy()
summary_df["偏差"] = deviation
summary_df
Out[22]:
 
  人口 偏差
都道府県名    
北海道 2.359e+06 1.199e+06
青森県 7.565e+05 -4.038e+05
岩手県 8.455e+05 -3.147e+05
宮城県 9.618e+05 -1.985e+05
秋田県 8.985e+05 -2.617e+05
山形県 9.689e+05 -1.913e+05
福島県 1.363e+06 2.025e+05
茨城県 1.350e+06 1.901e+05
栃木県 1.046e+06 -1.138e+05
群馬県 1.053e+06 -1.077e+05
 

標準偏差と分散

平均とのデータの散らばりの尺度は、偏差の平均の大きさで測定できる。

偏差の平均は0だが、偏差の大きさがわかればよく、正負は考えなくて良い。

絶対値を使う方法もあるが、便利ではなく2乗値を用いた 標準偏差を用いる。

標準偏差(s)は以下のように表し、偏差の大きさの平均を表す。 $$ s = \sqrt{\frac{1}{n} \sum_{i=1}^n({x_i-\overline{x} })^2}$$

また、計算上$s^2$を使用することもあり、 $s^2$を分散という 分散の定義は平均値からの偏差の2乗の平均だが、 計算上、2乗の平均値引く平均の2乗で求められる。

In [23]:
# 標準偏差
print(np.sqrt(np.var(data["人口"])))
print(np.std(data["人口"]))
 
441103.32881677506
441103.32881677506
In [24]:
# 分散
print(np.mean(summary_df["偏差"]**2))
print(np.var(data["人口"]))
 
194572146693.24
194572146693.24
 

範囲と四分位偏差

標準偏差以外の分散度の表し方としてデータの最大値と最小値の差を比較してその範囲から分散度を確認する方法があり、 これを範囲(range)という $$R = x_{max} - x_{min}$$

最大値と最小値だけで分散度を確認する方法は、 最大最小が中間のデータとかけ離れている時、 この値は分散度として必ずしも適切ではない。

そこで、極端なデータの影響を受けない測度として、 四分位数(quartile)を利用した四分位偏差(quartile deviation)という。

四分位数は全体のデータを小さい方から大きい方へ並べた時、 データ数を4等分する位置の値で、 データの下位25%、50%、75%に位置する値を第1四分位数、第2四分位数、第3四分位数と呼び、 $Q_1, Q_2, Q_3$で表す。

四分位偏差は$QD$と書ける $$QD = \frac{Q_3-Q_1}{2}$$

In [25]:
# 範囲(Range)
np.max(data["人口"])-np.min(data["人口"])
Out[25]:
1602729.0
In [26]:
# 四分位偏差
pre_Q1 = np.percentile(data["人口"], 25)
pre_Q3 = np.percentile(data["人口"], 75)

QD = (pre_Q3 - pre_Q1) / 2
QD
Out[26]:
180803.875
 

箱ひげ図(box plot)

四分位数を図示するときには箱ひげ図を使用し図示する。

平均値ではなく中央値を用いることで、 データの真ん中を表現でき、複数のデータ(母集団)を同時に扱える。

中央値を箱の中の横線として引く。

四分位数のうち、箱の上辺を第1四分位数、 箱の底辺を第3四分位数として線を引き箱を完成させる。

第1四分位数と第3四分位数の長さの1.5倍の範囲を把握し、 その範囲より外側にある数値を外れ値として点を記入する。

In [27]:
plt.boxplot(data["人口"], labels=["population"])
Out[27]:
{'whiskers': [<matplotlib.lines.Line2D at 0x10f5c5828>,
  <matplotlib.lines.Line2D at 0x10f5c5cc0>],
 'caps': [<matplotlib.lines.Line2D at 0x10f5d7128>,
  <matplotlib.lines.Line2D at 0x10f5d7550>],
 'boxes': [<matplotlib.lines.Line2D at 0x10f5c56d8>],
 'medians': [<matplotlib.lines.Line2D at 0x10f5d7978>],
 'fliers': [<matplotlib.lines.Line2D at 0x10f5d7da0>],
 'means': []}
 
In [28]:
# データの指標のまとめ
pd.Series(data["人口"]).describe()
Out[28]:
count    1.000e+01
mean     1.160e+06
std      4.650e+05
min      7.565e+05
25%      9.143e+05
50%      1.008e+06
75%      1.276e+06
max      2.359e+06
Name: 人口, dtype: float64
In [29]:
data["人口"].describe()
Out[29]:
count    1.000e+01
mean     1.160e+06
std      4.650e+05
min      7.565e+05
25%      9.143e+05
50%      1.008e+06
75%      1.276e+06
max      2.359e+06
Name: 人口, dtype: float64
 

正規化(normalization)

標準偏差は平均から求めることができるが、この指標は平均が異なるようなデータ間では比較することができない。 これを統一的な指標に変換することを正規化という

標準化(standardization)と標準化変量(standardized data)

正規化のうち、特にデータを平均から標準偏差の何倍離れているかという値で表し、そのデータの分布上の相対的な位置の比較を可能にする事を標準化という $$z = \frac{x - \overline{x}}{s}$$

データxをzに変換する事で異なるクラス間で比較可能になる。 この標準化されたzを標準化変量、またはzスコアという

In [30]:
z = (data["人口"] - np.mean(data["人口"])) / np.std(data["人口"])
z
Out[30]:
都道府県名
北海道    2.718
青森県   -0.915
岩手県   -0.713
宮城県   -0.450
秋田県   -0.593
山形県   -0.434
福島県    0.459
茨城県    0.431
栃木県   -0.258
群馬県   -0.244
Name: 人口, dtype: float64
 

偏差値

標準偏差10、平均が50になるように正規化した値を偏差値という $$z_i = 50+10 \times \frac{x_i - \overline{x}}{S}$$

In [31]:
# 偏差値
z = 50 + 10 * (data["人口"] - np.mean(data["人口"])) / np.std(data["人口"])
z
Out[31]:
都道府県名
北海道    77.180
青森県    40.845
岩手県    42.865
宮城県    45.500
秋田県    44.067
山形県    45.662
福島県    54.590
茨城県    54.310
栃木県    47.420
群馬県    47.559
Name: 人口, dtype: float64