LoginSignup
1
4

More than 5 years have passed since last update.

Qiitaで数式がつかえるみたいなので三角関数とか内積とか相関係数について書いて、Pythonで計算してみた

Last updated at Posted at 2018-12-12

お、Qiitaって数式つかえるんですね。。ってことで、数式をつかっていろいろ書いてみます。。

三角関数の定義

$\cos\theta ,\sin\theta $を以下のように定義します。
$x$軸正方向と角$\theta$ をなす 長さ1のベクトルを

\left(
\begin{array}{c}
\cos\theta\\
\sin\theta
\end{array}
\right) 

とします。

ようするに、単位円 $ x^2 + y^2 = 1 $ 上のある点 $P$ が $x$軸正方向と角$\theta$ をなすとき、その$x$座標、$y$座標を
$\left(
\begin{array}{c}
\cos\theta,
\sin\theta
\end{array}
\right)
$ とするってことですね。

image.png

内積の定義

あるベクトル$\boldsymbol{a}$,$\boldsymbol{b}$ に対して、内積 $\boldsymbol{a}\cdot\boldsymbol{b}$ を以下のように定義します。

\boldsymbol{a}\cdot\boldsymbol{b}  := \|\boldsymbol{a}\|\|\boldsymbol{b}\|\ \cos\theta

(ただし$\theta$は二つのベクトルのなす角)

この図形的な意味って、

\begin{align}
\boldsymbol{a}\cdot\boldsymbol{b} &= \|\boldsymbol{a}\|\|\boldsymbol{b}\|\ \cos\theta\\
&=\|\boldsymbol{a}\|\times( \|\boldsymbol{b}\|\ \cos\theta)\\
\end{align}

とみると「ベクトル$\boldsymbol{a}$の長さ」と、「ベクトル$\boldsymbol{b}$をベクトル$\boldsymbol{a}$へ正射影したときの有向長」をかけたモノ、と考えることができます。

image.png

内積の分配法則

内積をこのように定義すると、内積には分配法則が成り立ちます。つまり、

\boldsymbol{a}\cdot(\boldsymbol{b}+\boldsymbol{c}) = 
\boldsymbol{a}\cdot\boldsymbol{b}+\boldsymbol{a}\cdot\boldsymbol{c}

ということです。

なぜならば

$\boldsymbol{b} + \boldsymbol{c} =:\boldsymbol{d}$ とすると

\begin{align}
\boldsymbol{a}\cdot(\boldsymbol{b}+\boldsymbol{c}) &=\boldsymbol{a}\cdot\boldsymbol{d}\\
&=\|\boldsymbol{a}\|\times( \boldsymbol{d}を\boldsymbol{a}へ射影したときの長さ)\\
&=\|\boldsymbol{a}\|\times( \boldsymbol{b}を\boldsymbol{a}へ射影したときの長さ +\boldsymbol{c}を\boldsymbol{a}へ射影したときの長さ )\\
&=\|\boldsymbol{a}\|\times( \boldsymbol{b}を\boldsymbol{a}へ射影したときの長さ) +\|\boldsymbol{a}\|\times(\boldsymbol{c}を\boldsymbol{a}へ射影したときの長さ )\\
&=\boldsymbol{a}\cdot\boldsymbol{b}+\boldsymbol{a}\cdot\boldsymbol{c}\\
(Q.E.D.)
\end{align}

図で表すと、

image.png

こういうことですね。。

内積の成分表示の有名なヤツ。

分配法則をつかうと、2次元の場合を例にしますが

\boldsymbol{a} = 
\left(
\begin{array}{c}
a_1\\
a_2
\end{array}
\right) 
,
\boldsymbol{b} = 
\left(
\begin{array}{c}
b_1\\
b_2
\end{array}
\right) 
\boldsymbol{e_1} = 
\left(
\begin{array}{c}
1\\
0
\end{array}
\right) 
,
\boldsymbol{e_2} = 
\left(
\begin{array}{c}
0\\
1
\end{array}
\right) 

としたとき

\begin{align}
\boldsymbol{a}\cdot\boldsymbol{b} &=(a_1\boldsymbol{e_1}+a_2\boldsymbol{e_2})\cdot(b_1\boldsymbol{e_1} +b_2\boldsymbol{e_2})\\
&=a_1 b_1 \boldsymbol{e_1}\cdot\boldsymbol{e_1}+a_2b_2\boldsymbol{e_2}\cdot\boldsymbol{e_2}\\
&\qquad + a_1 b_2 \boldsymbol{e_1}\cdot\boldsymbol{e_2}+a_2 b_1 \boldsymbol{e_2}\cdot\boldsymbol{e_1}\\
&= a_1b_1 + a_2b_2\\
&=\|\boldsymbol{a}\|\|\boldsymbol{b}\|\ \cos\theta
\end{align}

が成立します。
(内積の$\cos\theta$をかけるという定義により、$\boldsymbol{e_1}\cdot\boldsymbol{e_1} =\boldsymbol{e_2}\cdot\boldsymbol{e_2} =1,\quad\boldsymbol{e_1}\cdot\boldsymbol{e_2} =\boldsymbol{e_2}\cdot\boldsymbol{e_1} =0$であるため)

コレ、基底ベクトルを $\boldsymbol{e_1}..\boldsymbol{e_n}$と $n$次元で考えた場合、各基底ベクトルの内積が、自分以外との内積は0,自分との内積は1であることに注意すると、$n$次元でも成立します。つまり、

$n$次元のベクトル $\boldsymbol{a} ,\boldsymbol{b}$

\boldsymbol{a} = 
\left(
\begin{array}{c}
a_1\\
a_2\\
\vdots\\
a_n\\
\end{array}
\right) 
,
\boldsymbol{b} = 
\left(
\begin{array}{c}
b_1\\
b_2\\
\vdots\\
b_n\\
\end{array}
\right) 

について、

\begin{align}
\boldsymbol{a}\cdot\boldsymbol{b} &=\|\boldsymbol{a}\|\|\boldsymbol{b}\|\ \cos\theta\\
&= a_1b_1 + a_2b_2 ... +a_nb_n\\
&= \sum_{i=1}^{n} a_ib_i
\end{align}

となります。

ベクトルから cos θを求める

$\boldsymbol{a}$,$\boldsymbol{b}$ 二つのベクトルのなす角を $\theta$としたとき、

\begin{align}
\cos\theta &= \frac{\boldsymbol{a}\cdot\boldsymbol{b}}{\|\boldsymbol{a}\|\|\boldsymbol{b}\|}\\
&= \frac{\sum_{i=1}^{n} a_ib_i}{\sqrt{\sum_{i=1}^{n} a_i^2} \cdot \sqrt{\sum_{i=1}^{n} b_i^2}}
\end{align}

これを計算することでなす角(の $\cos\theta$ )が求まるって事ですねー。。この $\cos\theta$ですが、統計ででてくるいわゆる相関係数です。

統計の話

相関係数

\boldsymbol{x} = 
\left(
\begin{array}{c}
x_1 -m_x\\
x_2 -m_x\\
\vdots\\
x_n -m_x\\
\end{array}
\right) 
,
\boldsymbol{y} = 
\left(
\begin{array}{c}
y_1 -m_y\\
y_2 -m_y\\
\vdots\\
y_n -m_y\\
\end{array}
\right) 

という$n$次元ベクトル$\boldsymbol{x},\boldsymbol{y}$ について、

\begin{align}
r_{xy} :&= \frac{\boldsymbol{x}\cdot\boldsymbol{y}}{\|\boldsymbol{x}\|\|\boldsymbol{y}\|}\\
&=\frac{\sum_{i=1}^{n} (x_i -m_x)(y_i -m_y)}{\sqrt{\sum_{i=1}^{n} (x_i -m_x)^2} \cdot \sqrt{\sum_{i=1}^{n} (y_i -m_y)^2}}\\
&= (\boldsymbol{x}と\boldsymbol{y}のなす角\theta の \cos\theta )

\end{align}

と定義します。ただし

m_x:= \frac{1}{n} \sum_{i=1}^{n}x_i \\
m_y:= \frac{1}{n} \sum_{i=1}^{n}y_i 

つまり$m_x,m_y$ は、各成分の平均で定数値です。

この値$r_{xy} $は $\cos\theta$ なので $-1 \leq r_{xy} \leq 1$ の値をとり、0だと直交、1だと平行(向きも同じ)、-1だと平行(向きが逆) ってことになります。

$(x_1,y_1),(x_2,y_2), \dots (x_n,y_n)$ のデータの関係を見るときに、$\boldsymbol{x},\boldsymbol{y}$ ふたつのベクトルの $\cos\theta$ を測ることで相関をみるってことで、$r_{xy} $ を相関係数ってよんでます。

標準偏差と共分散

\begin{align}
r_{xy} 
&=\frac{\sum_{i=1}^{n} (x_i -m_x)(y_i -m_y)}{\sqrt{\sum_{i=1}^{n} (x_i -m_x)^2} \cdot \sqrt{\sum_{i=1}^{n} (y_i -m_y)^2}}\\
&=\frac{\frac{1}{n}\sum_{i=1}^{n} (x_i -m_x)(y_i -m_y)}{\sqrt{\frac{1}{n}\sum_{i=1}^{n} (x_i -m_x)^2} \cdot \sqrt{\frac{1}{n}\sum_{i=1}^{n} (y_i -m_y)^2}}\\
&=\frac{S_{xy}}{S_xS_y}
\end{align}

ただし

\begin{align}

S_{xy}&:=\frac{1}{n}\sum_{i=1}^{n} (x_i -m_x)(y_i -m_y)\\
S_x&:=\sqrt{\frac{1}{n}\sum_{i=1}^{n} (x_i -m_x)^2}\\
S_y&:=\sqrt{\frac{1}{n}\sum_{i=1}^{n} (y_i -m_y)^2}

\end{align}

$S_x,S_y$は、一次元のときにもよく出てくる$\boldsymbol{x},\boldsymbol{y}$ の標準偏差、$S_{xy}$を$\boldsymbol{x}$と$\boldsymbol{y}$の共分散と呼びます。

相関係数を計算するときに、標準偏差と共分散を計算して算出しますが、これってつまるところ二つのベクトルのなす角を調べることで、データに相関があるかなーってのを $-1 \leq r_{xy} \leq 1$ で計算してるって事が分かります。

Pythonでやってみる

ついでに、これらの値をPythonのnumpyライブラリをつかって実際に計算してみます。。。

以下はとあるヒトの体重と体脂肪、BMIデータです。

実際のデータ:

$ head weight.csv 
key,体重,体脂肪率,BMI
2017-12-10,58.5750,14.2425,23.4000
2017-12-11,58.6333,14.1255,23.4333
2017-12-12,58.3250,14.4700,23.3500
2017-12-13,58.4000,14.8275,23.3500
2017-12-14,57.2000,14.1450,22.9000
2017-12-15,57.9500,13.6900,23.2000
2017-12-16,58.0000,14.3400,23.2000
2017-12-17,58.6167,14.0995,23.4333
2017-12-18,58.7500,14.0150,23.4500
.....

BMIは身長と体重から計算されるので、相関は極めて高いはずです(というかほぼ1になるはず?)。体脂肪は、どうでしょうか。。

環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.1 (Mojaveです)
BuildVersion:   18B75

Pythonの実行環境は適宜ご用意してもらえば結構ですが、以下、venvで環境を構築する手順を書いておきます。

$ python3 -m venv ./venv
$ source ./venv/bin/activate
(venv) $ which python3
/xxx/venv/bin/python3
(venv) $  python3 --version
Python 3.7.1
(venv) $ 

また、グラフを表示するために、Matplotlib を使用しますが、Macの環境構築に戸惑ったらこちらをご参照ください。

実行してみる

(venv) $ cat requirement.txt
matplotlib==3.0.2
numpy==1.15.4
pandas==0.23.4
PyQt5==5.11.3
(venv) $ pip install -r requirement.txt
... 割愛
(venv) $ cat weight.py
# !/usr/bin/env python
# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sys


def main(args):
    # データ読み込み
    df = pd.read_csv('./weight.csv', index_col='key')
    print(df.head(10))
    print(df.shape)

    # データ列取り出し
    data1 = df['体重']
    data2 = df['体脂肪率']
    data3 = df['BMI']


    # 平均
    mean1 = np.mean(data1)  # mx
    mean2 = np.mean(data2)  # my

    # 分散と、共分散
    v1 = np.var(data1, ddof=0)  # 1/n * Σ (X-mx)^2
    v2 = np.var(data2, ddof=0)  # 1/n * Σ (Y-my)^2
    cov = np.cov(data1, data2, ddof=0)[1, 0]  # 1/n * Σ (X-mx)*(Y-my)
    # 共分散は共分散行列で返ってくるので、[1,0],[0,1] をとればいい

    # 標準偏差
    std_dev1 = np.sqrt(v1)
    std_dev2 = np.sqrt(v2)

    # 相関係数
    cor = np.corrcoef(data1, data2)[1, 0]
    cor2 = cov / (std_dev1 * std_dev2)  # もしくは計算で。

    print('--- 体重と体脂肪率 ----')
    print(f'  平均: {mean1:.3f}, {mean2:.3f}')
    print(f'標準偏差: {std_dev1:.3f}, {std_dev2:.3f}')
    print(f'  分散: {v1:.3f}, {v2:.3f}')
    print(f' 共分散: {cov:.10f}')
    print(f'相関係数: {cor:.10f}')
    print(f'相関係数: {cor2:.10f}')

    print('-------')

    # ちなみに体重とBMIの相関係数
    cor3 = np.corrcoef(data1, data3)[1, 0]
    print(f'体重とBMIの相関係数: {cor3:.10f}')

    plot(df)


def plot(df):
    xorg = df['体重']
    yorg = df['BMI']

    mx = np.mean(xorg)
    my = np.mean(yorg)

    fig = plt.figure()

    ax1 = fig.add_subplot(2, 1, 1)
    ax2 = fig.add_subplot(2, 1, 2)

    ax1.scatter(xorg, yorg, label='体重とBMI', s=5)
    ax1.scatter(mx, my, label='平均')

    ax1.set_xlabel('体重')
    ax1.set_ylabel('BMI')
    ax1.grid(True)  # グリッド線

    plot_poly_1d(ax1, xorg, yorg)
    ax1.legend(loc='upper left')

    yorg2 = df['体脂肪率']
    my2 = np.mean(yorg2)
    ax2.scatter(xorg, yorg2, label='体重と体脂肪率', s=5)
    ax2.scatter(mx, my2, label='平均')

    ax2.set_xlabel('体重')
    ax2.set_ylabel('体脂肪率')
    ax2.grid(True)  # グリッド線

    plot_poly_1d(ax2, xorg, yorg2)
    ax2.legend(loc='lower left')

    plt.tight_layout()  # タイトルの被りを防ぐ

    # グラフに情報を表示
    plt.show()


# 線形回帰直線
def plot_poly_1d(ax, xorg, yorg):
    # y = ax + b のa ,b を配列で返す
    poly_fit = np.polyfit(xorg, yorg, 1)

    # 関数を作成
    poly_1d = np.poly1d(poly_fit)

    xs = np.linspace(xorg.min(), xorg.max())
    ys = poly_1d(xs)

    ax.plot(xs, ys, label=f'{poly_fit[1]:.2f}+{poly_fit[0]:.2f}x')


if __name__ == "__main__":
    main(sys.argv)


(venv) $ python3 weight.py
                 体重     体脂肪率      BMI
key                                  
2017-12-10  58.5750  14.2425  23.4000
2017-12-11  58.6333  14.1255  23.4333
2017-12-12  58.3250  14.4700  23.3500
2017-12-13  58.4000  14.8275  23.3500
2017-12-14  57.2000  14.1450  22.9000
2017-12-15  57.9500  13.6900  23.2000
2017-12-16  58.0000  14.3400  23.2000
2017-12-17  58.6167  14.0995  23.4333
2017-12-18  58.7500  14.0150  23.4500
2017-12-19  58.5875  14.2555  23.4250
(356, 3)
--- 体重と体脂肪率 ----
  平均: 59.935, 14.514
標準偏差: 0.835, 0.478
  分散: 0.698, 0.229
 共分散: -0.0193105736
相関係数: -0.0483200035
相関係数: -0.0483200035
-------
体重とBMIの相関係数: 0.9980663360

image.png

体重とBMIは当然ながら、なす角がほぼゼロであることが分かりました。体重と体脂肪はどうやら相関はなさそうってコトが分かります。。

おつかれさまでした。

まとめ

うーん、Qiitaで数式がつかえるって事でつかってみましたが、気がついたら統計とPythonのはなしになってました。また、$\rm\LaTeX$ で論文を書いてた学生時代を思い出しました。

そして数式かくのはメンドクサイけど、さすがにキレイです。。でもめちゃメンドイ。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4