相関係数Rと決定係数R2の違いが！「言葉」でなく「心」で理解できた!

どうも、昨夜９時頃Twitterアカウントが永久凍結した [Ikemen Mas Kot](https://twitter.com/maskot1977) です。過去記事「相関係数が0.63の散布図を作成する」の続編を思いついたので、やってみました。今回のテーマは

相関係数Rと決定係数R2の違いが！「言葉」でなく「心」で理解できた！

で行きたいと思います。それぞれの定義については、ググって調べてください（放置系）。「言葉」でなく「心」で理解するために、今回もOptunaでプロットを自作します。

# 目的関数の設計


```python
import numpy as np
from scipy.stats import pearsonr
from sklearn.metrics import r2_score


class Objective:  # Optuna で最適化する目的関数
    def __init__(
        self,
        n_data=20,  # データ数
        target_r=0.95,  # 目標とするピアソン相関係数の値
        delta=0.005,  # 許容する誤差の最大値
        direction="minimize",  # 最適化する方向
    ):
        self.n_data = n_data  # データ数
        self.target_r = target_r  # 目標とするピアソン相関係数の値
        self.delta = delta  # 許容する誤差の最大値
        self.direction = direction  # 最適化する方向

        # 最大化・最小化の目的に合わせて初期値を設定する
        if self.direction == "maximize":
            self.best_score = -530000
            self.penalty = -530000
        else:
            self.best_score = 530000  # フリーザ（初期形態）の戦闘力
            self.penalty = 530000  # フリーザ（初期形態）の戦闘力

        self.best_X = None  # ベストX座標を記録する場所
        self.best_Y = None  # ベストY座標を記録する場所

    def __call__(self, trial):  # Optuna に呼ばれるときはこれが実行される

        # X座標は均等に決め打ちする
        self.X = np.linspace(0.05, 0.95, self.n_data)

        # Y座標を Optuna に最適化してもらう
        self.Y = []
        for i in range(self.n_data):
            self.Y.append(trial.suggest_float("y{}".format(i), 0, 1))

        # ピアソンの相関係数を r とする
        r, p = pearsonr(self.X, self.Y)

        # 決定係数を r2 とする
        r2 = r2_score(self.X, self.Y)

        # ピアソンの相関係数が目標値に十分近いとき、決定係数をスコアとする
        if self.target_r - self.delta < r and r < self.target_r + self.delta:
            score = r2
        else:  # ピアソンの相関係数が目標値から離れすぎているとき、その距離に応じてペナルティを与える
            score = abs(self.target_r - r) * self.penalty

        # 今までの記録よりも良いものが得られたとき、ベスト結果を更新する
        if (self.direction == "maximize" and self.best_score < score) or (
            self.direction == "minimize" and self.best_score > score
        ):
            self.best_score = score
            self.best_X = self.X
            self.best_Y = self.Y
        return score
```

# R2最大化

ピアソンの相関係数 R = 0.9 を目標とし、決定係数 R2 をできるだけ最大化した計算例がこちらです。


```python
import optuna

optuna.logging.set_verbosity(optuna.logging.WARN)  # 途中経過メッセージを省略する

n_trials = 1000  # 試行回数
objective = Objective(target_r=0.90, direction="maximize")  # 目的関数を設定
study = optuna.create_study(directions=["maximize"])  # Optuna の設定
study.optimize(objective, n_trials=n_trials, show_progress_bar=True)  # 最適化計算実行
```

    /usr/local/lib/python3.9/site-packages/optuna/progress_bar.py:49: ExperimentalWarning: Progress bar is experimental (supported from v1.2.0). The interface can change in the future.
      self._init_valid()



      0%|          | 0/1000 [00:00<?, ?it/s]



```python
import matplotlib.pyplot as plt

plt.title(
    "R={:.3f}, R2={:.3f}".format(
        pearsonr(objective.best_X, objective.best_Y)[0],
        r2_score(objective.best_X, objective.best_Y),
    )
)
plt.scatter(objective.best_X, objective.best_Y)
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.grid()
plt.savefig("0.png")
```


       
![output_5_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/21af7d80-b59c-d19a-d4ba-cc67743c1830.png)


なるほど。続いて、ピアソンの相関係数 R = 0.9 を目標とし、決定係数 R2 をできるだけ最小化した計算例がこちらです。


```python
import optuna

optuna.logging.set_verbosity(optuna.logging.WARN)  # 途中経過メッセージを省略する

objective = Objective(target_r=0.90, direction="minimize")
study = optuna.create_study(directions=["minimize"])
study.optimize(objective, n_trials=n_trials, show_progress_bar=True)
```

    /usr/local/lib/python3.9/site-packages/optuna/progress_bar.py:49: ExperimentalWarning: Progress bar is experimental (supported from v1.2.0). The interface can change in the future.
      self._init_valid()



      0%|          | 0/1000 [00:00<?, ?it/s]



```python
import matplotlib.pyplot as plt

plt.title(
    "R={:.3f}, R2={:.3f}".format(
        pearsonr(objective.best_X, objective.best_Y)[0],
        r2_score(objective.best_X, objective.best_Y),
    )
)
plt.scatter(objective.best_X, objective.best_Y)
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.grid()
plt.savefig("1.png")
```


    
    
![output_8_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/c438fa88-f723-f8a7-5444-2bb70cc6dea9.png)


# データ数を増やしていくぜ

それでは、データ数を増やしていきたいと思います。計算に時間がかかるので、途中で何かの原因で計算が止まってしまうようなことがあれば、再計算するのが面倒です。そのようなときに備えて、Optunaに計算履歴を残してもらうと大変便利です。


```python
for n_data in [10, 20, 100, 200, 1000]:  # データ数を増やしていく

    # データ数ごとに結果表示をする
    fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(20, 8))

    # 目標とするピアソン相関係数Rの値を変えていく
    for j, target_r in enumerate([0.9, 0.7, 0.5, 0.25, 0.0]):

        # 決定係数R2を最大化した計算例、最小化した計算例をそれぞれ算出する
        for i, direction in enumerate(["maximize", "minimize"]):

            # 目的関数を設定する
            objective = Objective(n_data=n_data, target_r=target_r, direction=direction)

            # 計算履歴を残すデータベースの名前を決める
            study_name = "{}-{}-{}".format(n_data, target_r, direction)

            # 計算履歴を残せる形で、Optuna の study を設定する
            study = optuna.create_study(
                directions=[direction],
                study_name=study_name,
                storage="sqlite:///" + study_name + ".sql",
                load_if_exists=True,
            )

            # 最適化計算
            study.optimize(objective, n_trials=n_trials, show_progress_bar=True)

            # １つのパネルに計算結果を描画する
            axes[i][j].set_title(
                "n_data={}, R={:.3f}, R2={:.3f}".format(
                    n_data,
                    pearsonr(objective.best_X, objective.best_Y)[0],
                    r2_score(objective.best_X, objective.best_Y),
                )
            )
            axes[i][j].scatter(objective.best_X, objective.best_Y)
            axes[i][j].set_xlim([0, 1])
            axes[i][j].set_ylim([0, 1])
            axes[i][j].grid()

    # データ数ごとに結果表示をして画像を保存する
    plt.savefig("{}.png".format(n_data))
    plt.show()  # 画像を表示する
```

## n_data = 10

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/d80afe8f-cfa4-2599-35d3-cb0fb8356990.png)



## n_data = 20

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/fbe66dc9-fc0a-2ad7-6a32-0d9f399c64c1.png)

わかるか？え？RとR2の関係が...



## n_data = 100

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/577ddec4-a4db-711b-27e4-486220c23d44.png)

さすがに n_data = 100 ともなると最適化が難しくなってきましたね...試行回数がまだ足りないかな...もっとデータ数が大きいときはもっと酷い結果だったので省略ッ


# 見つけたぜ 関連記事だ

* [相関係数が0.63の散布図を作成する](https://qiita.com/maskot1977/items/7526f6eb6edf5b92d9d9)