🤳
教師なし学習
[!abstract]+ Curriculum
1.教師なし学習の基礎
2.非階層的クラスタリング
3.主成分分析
- 添削問題
- 自作応用問題
教師なし学習の基礎
- 答えが与えられていないデータを利用して、AIが自ら判断して答えを決める。
種類
クラスタリング
#クラスター化
↑ k-mean 法を利用したクラスタリング(紫色の点:データの中心)
- k-mean法 : データ中心の最適位置を学習してクラスタリングを行う。
- クラスターの数 : 手動と自動がある。k-mean は手動。階層的は自動。
- 階層的は比較的計算量が多い。
主成分分析
#PCA
- Principal Component Analysis
- データ次元削減のための手法
事前知識
ユークリッド距離
#np/standard
import numpy as np
vec_a = np.array([1, 2, 3])
vec_b = np.array([2, 3, 4])
print(np.linalg.norm(vec_a - vec_b))
Cos類似度
- 二つのベクトルが成す各
の\theta をデータ類似度の指標としたもの。\cos{\theta}
#np/cos_similarity
import numpy as np
vec_a = np.array([1, 2, 3])
vec_b = np.array([2, 3, 4])
print(np.dot(vec_a, vec_b) / (np.linalg.norm(vec_a) * np.linalg.norm(vec_b)))
非階層的クラスタリング
クラスタリング手法
階層的クラスタリング
↑ デンドログラム
#HC
- Hierarchical Clustering : データの中から似たものを見つけて順番にクラスタリングする。
非階層的クラスタリング
#non-HC
- 人がクラスタリング数を決める
- 最適なクラスタリング数は決まっていない。
- 計算量が少なくデータ量が多い場合に有効
- 代表的にはk-means法
K-means 법
#ノンエイチシー/k-means
データの集合
#sk/make/blobs
#sk/make/blobs
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
# Xには1つのプロットの(x,y)が、Yにはそのプロットの所属するクラスター番号が入る
X, Y = make_blobs(n_samples=150, # サンプル点の総数
n_features=2, # 特徴量(次元数)の指定 default:2
centers=10, # ここを変えてください # クラスタの個数
cluster_std=0.5, # クラスタ内の標準偏差
shuffle=True, # サンプルをシャッフル
random_state=0) # 乱数生成器の状態を指定
plt.scatter(X[:, 0], X[:, 1], c="black", marker="*", s=50)
plt.grid()
plt.show()
K-means 法について
- データを分散が同じ
個のクラスターに分けます。n - 各クラスターごとに中心
(centroid) を割り当てます。\mu_{i} - 指標 SSE
- k-means法:全クラスタのSSEが同じで最小化されるように。
アルゴリズム
1.データセット内で
2.すべてのデータを最も近い centroid に割り当てます。
3.割り当てられたデータ群の中心を計算し、新しいcentroidに割り当てる。
4.前の centroid と新しい centroid の距離を計算
- 基準値より大きい場合、2,3回繰り返す。小さい場合は計算終了
Sklearn の KMeans ライブラリ、SSE について
#sk/cluster/k-means #sk/cluster/k-means/SSE
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
# サンプルデータの生成
X, Y = make_blobs(n_samples=150, n_features=2, centers=3,
cluster_std=0.5, shuffle=True, random_state=0)
# KMeansクラスからkmインスタンスを作成
km = KMeans(n_clusters=3, # クラスターの個数 # 変更してみてください
init="random", # セントロイドの初期値をランダムに設定 default: "k-means++"
n_init=1, # 異なるセントロイドの初期値を用いたk-meansの実行回数
max_iter=300, # k-meansアルゴリズムを繰り返す最大回数
tol=1e-04, # 収束と判定するための相対的な許容誤差
random_state=0) # 乱数発生初期化
# fit_predictメソッドによりクラスタリングを行う
Y_km = km.fit_predict(X)
# SSE値を出力
print("Distortion: %.2f" % km.inertia_)
# プロット
for n in range(len(Y_km)):
plt.scatter(X[Y_km == n, 0], X[Y_km == n, 1], s=50, c=cm.hsv(
float(n) / 10), marker="*", label="cluster"+str(n+1))
plt.scatter(km.cluster_centers_[:, 0], km.cluster_centers_[
:, 1], s=250, marker="*", c="black", label="centroids")
plt.grid()
plt.show()
>>> Distortion: 72.48
エルボ法
- k-means法でクラスターの数はどうやって決めるのか。
- k$ の値を増やしたときの SSE の変動をプロットしてクラスター数を決める。
- グラフの形が腕に似ていて、肘が最適。
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
# サンプルデータの生成
X, Y = make_blobs(n_samples=150, n_features=2, centers=3,
cluster_std=0.5, shuffle=True, random_state=0)
distortions = []
for i in range(1, 11): # クラスター数1~10を一気に計算
km = KMeans(n_clusters=i,
init="k-means++", # k-means++法によりクラスタ中心を選択
n_init=10,
max_iter=300,
random_state=0)
km.fit(X) # クラスタリングを実行
distortions.append(km.inertia_) # km.fitするとkm.inertia_が得られる
# グラフのプロット
plt.plot(range(1, 11), distortions, marker="o")
plt.xticks(np.arange(1, 11, 1))
plt.xlabel("Number of clusters")
plt.ylabel("Distortion")
plt.show()
DBSCAN
- ノイズを含むアプリケーションの密度に基づく空間的クラスタリング
DBSCAN のアルゴリズム
- k-means法は必然的にクラスターが円形に近い形になる。
- クラスターの形や大きさが均一でないと良いクラスタリングが難しい。
- DBSCAN : クラスターの高密集場所を低密集場所から分離して認識する。
- クラスターの形態やサイズが均一でないデータの場合に真価を発揮。
- 二つの指標、eps(epsilon)とmin_samplesを使用してデータを三種類に分類。
- core point : あるデータの半径 eps の中に min_samples だけデータが存在する場合。
- border point : コアポイントではないが、半径epsの中に存在する点。
- noise point : どちらでもない場合
#sk/cluster/dbscan
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.cluster import KMeans
from sklearn.cluster import DBSCAN
# 月型のデータを生成
X, Y = make_moons(n_samples=200, noise=0.05, random_state=0)
# グラフと2つの軸を定義 左のax1はk-means法用、右のax2はDBSCAN用
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 3))
# k-means法
km = KMeans(n_clusters=2, random_state=0)
Y_km = km.fit_predict(X)
ax1.scatter(X[Y_km == 0, 0], X[Y_km == 0, 1], c="lightblue",
marker="o", s=40, label="cluster 1")
ax1.scatter(X[Y_km == 1, 0], X[Y_km == 1, 1], c="red",
marker="s", s=40, label="cluster 2")
ax1.set_title("K-means clustering")
ax1.legend()
# DBSCANでクラスタリング # コードを完成してください
db = DBSCAN(eps=0.3)
Y_db = db.fit_predict(X)
ax2.scatter(X[Y_db == 0, 0], X[Y_db == 0, 1], c="lightblue",
marker="o", s=40, label="cluster 1")
ax2.scatter(X[Y_db == 1, 0], X[Y_db == 1, 1], c="red",
marker="s", s=40, label="cluster 2")
ax2.set_title("DBSCAN clustering")
ax2.legend()
plt.show()
主成分分析
#PCA
↑ 主成分分析のイメージ
- 主成分分析**:データ要約(少ないデータ数で原データを表現すること、特徴変換)の強力な手法の一つ。
- 実用性が高く、MLの重要なテーマの一つ。
- 実用的な例:製品やサービスのスコアリングや比較(1次元に圧縮)、データの可視化(2、3次元に圧縮)、回帰分析の前処理など。
特徴変換までの流れ
- データの標準化
- 特徴間の相関行列の計算
- 相関行列の固有ベクトルと固有値の計算
- 固有値が大きい順に
個(圧縮したい次元数)を選び、それに対応する固有ベクトルを選択する。k - 固有ベクトルで特徴変換行列
を作成するW - 元のデータ
(d 次元) と特徴変換行列X を乗算し、W 次元に変換されたデータk を得る。X'
データ標準化
#ピーディーエスディー
import pandas as pd
import matplotlib.pyplot as plt
df_wine = pd.read_csv("./5030_unsupervised_learning_data/wine.csv", header=None)
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
# 標準化前のデータを可視化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 3))
ax1.set_title('before')
ax2.set_title('before')
ax1.scatter(X[:, 0], X[:, 1])
ax2.scatter(X[:, 5], X[:, 6])
plt.show()
print("before")
print("mean: ", X.mean(axis=0), "\nstd: ", X.std(axis=0))
# Xに、Xを標準化したデータを代入してください
X = (X-X.mean(axis=0)) / X.std(axis=0)
# 標準化後のデータを可視化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 3))
ax1.set_title('after')
ax2.set_title('after')
ax1.scatter(X[:, 0], X[:, 1])
ax2.scatter(X[:, 5], X[:, 6])
plt.show()
print("after")
print("mean: ", X.mean(axis=0), "\nstd: ", X.std(axis=0))
相関行列の計算
#np/corrcoef
-
np.corrcoef()
関数は行同士の相関を表す。-
np.corrcoef(X.T)
のように 転置して使いましょう。
-
import pandas as pd
import numpy as np
df_wine = pd.read_csv("./5030_unsupervised_learning_data/wine.csv", header=None)
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
# 標準化
X = (X - X.mean(axis=0)) / X.std(axis=0)
# 相関行列(13x13)を作成してください
R = np.corrcoef(X.T)
# 対角成分を0にしてください
_R = R - np.identity(13)
# 最大相関係数をとるインデックスを取得してください
index = np.where(_R == _R.max())
print(_R[index[0][0], index[0][1]])
print(index)
>>> 0.8645635000951152
>>> (array([5, 6]), array([6, 5]))
固有値と固有ベクトルに分解
#np/linalg/eigh #np/eigen
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df_wine = pd.read_csv("./5030_unsupervised_learning_data/wine.csv", header=None)
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
# 標準化
X = (X - X.mean(axis=0)) / X.std(axis=0)
# 相関行列(13x13)を作成
R = np.corrcoef(X.T)
# 固有値分解
eigvals, eigvecs = np.linalg.eigh(R)
# 可視化
plt.bar(range(13), eigvals)
plt.title("distribution of eigvals")
plt.xlabel("index")
plt.ylabel("eigvals")
plt.show()
# 消さないでください。実行結果の確認に使います。
print(eigvals)
>>> [0.10337794 0.16877023 0.22578864 0.25090248 0.28887994 0.34849736 0.55102831 0.64165703 0.85322818 0.91897392 1.44607197 2.49697373 4.70585025]
特徴変換
#np/c_
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df_wine = pd.read_csv("./5030_unsupervised_learning_data/wine.csv", header=None)
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
# 標準化
X = (X - X.mean(axis=0)) / X.std(axis=0)
# 相関行列の取得
R = np.corrcoef(X.T)
# 固有値分解
eigvals, eigvecs = np.linalg.eigh(R)
# 変換行列の取得
W = np.c_[eigvecs[:,-1], eigvecs[:,-2]]
# 特徴変換
X_pca = X.dot(W)
# 可視化
color = ["r", "b", "g"]
marker = ["s", "x", "o"]
for label, color, marker in zip(np.unique(y), color, marker):
plt.scatter(X_pca[y == label, 0], X_pca[y == label, 1],
c=color, marker=marker, label=label)
plt.xlabel("PC 1")
plt.ylabel("PC 2")
plt.legend(loc="lower left")
plt.show()
Scikit-learn を使った主成分分析
#sk/分解/PCA #sk/decomposition/PCA/fit_transform
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# PCAをインポートしてください
# ---------------------------
from sklearn.decomposition import PCA
# ---------------------------
df_wine = pd.read_csv("./5030_unsupervised_learning_data/wine.csv", header=None)
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X = (X - X.mean(axis=0)) / X.std(axis=0)
# 主成分分析のインスタンスを生成。主成分数は2としてください。
pca =PCA(n_components=2)
# データから変換モデルを学習し、変換する。
X_pca = pca.fit_transform(X)
# 可視化
color = ["r", "b", "g"]
marker = ["s", "x", "o"]
for label, color, marker in zip(np.unique(y), color, marker):
plt.scatter(X_pca[y == label, 0], X_pca[y == label, 1],
c=color,, label=label)
plt.xlabel("PC 1")
plt.ylabel("PC 2")
plt.legend(loc="lower left")
plt.show()
print(X_pca) # 消さないでください。実行結果の確認に使います。
前処理としての主成分分析
- トレーニングデータと検証データ、両方に同じ標準化や特徴量変換を行う必要がある。
- データ標準化後の主成分分析
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
df_wine = pd.read_csv("./5030_unsupervised_learning_data/wine.csv", header=None)
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.4, random_state=0)
# 標準化のためのインスタンスを生成
sc = StandardScaler()
# トレーニングデータから変換モデルを学習し、テストデータに適用
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)
# 主成分分析のインスタンスを生成
pca = PCA(n_components=2)
# トレーニングデータから変換モデルを学習し、テストデータに適用
X_train_pca =pca.fit_transform(X_train_std)
X_test_pca =pca.transform(X_test_std)
# ロジスティック回帰のインスタンスを生成
lr = LogisticRegression()
# 次元削減後のトレーニングデータで分類モデルを学習
lr.fit(X_train_pca, y_train)
# スコアの表示
print(lr.score(X_train_pca, y_train))
print(lr.score(X_test_pca, y_test))
>>> 0.9716981132075472
>>> 0.9583333333333334
カーネル主成分分析
↑ 2次元データをカーネルトリックを利用して特徴量を増やした後、PCAで特徴量を2つに戻します。
- 現実的に非線形分離する必要があるデータがほとんど。
- このようなデータに対応可能なカーネルPCAについて説明します。
- 線形分離不可能なデータを可能なデータに変換
- カーネルトリック :
。X\;(N\times M)\quad \quad \rightarrow \quad X'\;(N\times M') - 一般的に特徴量が多くなり(特徴量が展開され)、線形分離が容易になる。
カーネルトリック
K の計算
カーネル行列 -
: カーネル関数。数種類存在。k(x_{i},\,y_{i}) - rbf : Radiam Basis Function、ガウスカーネル。フェイザーのような感じ。次数無限大の多項式カーネル。正解率が高い。
import numpy as np
np.random.seed(39)
X = np.random.rand(8, 3)
# ペアごとの平方ユークリッド距離を計算
M = np.sum((X - X[:, np.newaxis])**2, axis=2)
# カーネル行列を計算
gamma = 15
K = np.exp(-gamma * M)
# ---------------------------
print(K.shape)
# ---------------------------
print(M) # 消さないでください。実行結果の確認に使います。
print(K) # 消さないでください。実行結果の確認に使います。
特徴変換
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_circles
# データが円状に分布するデータを取得
X, y = make_circles(n_samples=1000, random_state=123, noise=0.1, factor=0.2)
# ペアごとの平方ユークリッド距離を計算してください。
M = np.sum((X-X[:,np.newaxis])**2, axis=2)
# 対称カーネル行列を計算。gammaの値は10にしてください。
gamma = 10
K = np.exp(-gamma * M)
# カーネル行列から固有対を取得。 numpy.linalg.eighにより、それらを固有値の昇順で返す
eigvals, eigvecs = np.linalg.eigh(K)
# 固有値の値が大きい順に2つ、対応する固有ベクトルを抽出
W = np.c_[eigvecs[:, -1], eigvecs[:, -2]]
# KとWの内積を求めて線形分離可能なデータを獲得してください。
X_kpca = K.dot(W)
# 可視化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 3))
ax1.scatter(X[y == 0, 0], X[y == 0, 1], color="r", marker="^")
ax1.scatter(X[y == 1, 0], X[y == 1, 1], color="b", marker="o")
ax2.scatter(X_kpca[y == 0, 0], X_kpca[y == 0, 1], color="r", marker="^")
ax2.scatter(X_kpca[y == 1, 0], X_kpca[y == 1, 1], color="b", marker="o")
ax1.set_title("circle_data")
ax2.set_title("kernel_pca")
plt.show()
print(M) # 消さないでください。実行結果の確認に使います。
print(K) # 消さないでください。実行結果の確認に使います。
print(W) # 消さないでください。実行結果の確認に使います。
print(X_kpca) # 消さないでください。実行結果の確認に使います。
Scikit-learn を利用したカーネル主成分分析
#sk/分解/カーネルPCA
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
# KernelPCAをインポート
# ---------------------------
from sklearn.decomposition import KernelPCA
# ---------------------------
# 月形データを取得
X, y = make_moons(n_samples=100, random_state=123)
# KernelPCAクラスをインスタンス化
kpca = KernelPCA(n_components=2, kernel="rbf", gamma=15)
# データXをKernelPCAを用いて変換
X_kpca =kpca.fit_transform(X)
# 可視化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 3))
ax1.scatter(X[y == 0, 0], X[y == 0, 1], c="r")
ax1.scatter(X[y == 1, 0], X[y == 1, 1], c="b")
ax1.set_title("moon_data")
ax2.scatter(X_kpca[y == 0, 0], X_kpca[y == 0, 1], c="r")
ax2.scatter(X_kpca[y == 1, 0], X_kpca[y == 1, 1], c="b")
ax2.set_title("kernel_PCA")
plt.show()
print(X_kpca) # 消さないでください。実行結果の確認に使います。
添削問題
基本 : ラット実験データの次元縮小 (20 to 2) by Class PCA
# 初めの一回だけこのセルを実行してください、データセットをダウンロードして展開します
# 一回実行すれば、データセットはダウンロードされたままなので、再起動後等再び実行する必要はありません
import urllib.request
import zipfile
# URLを指定
url = "https://storage.googleapis.com/tutor-contents-dataset/5030_unsupervised_learning_data.zip"
save_name = url.split('/')[-1]
# ダウンロードする
mem = urllib.request.urlopen(url).read()
# ファイルへ保存
with open(save_name, mode='wb') as f:
f.write(mem)
# zipファイルをカレントディレクトリに展開する
zfile = zipfile.ZipFile(save_name)
zfile.extractall('.')
# 必要なライブラリのインポート
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# PCAクラスを定義します
class PCA:
def __init__(self):
pass
# 以下のfitメソッドを完成させてください
def fit(self, X):
# 引数Xをインスタンス変数Xに代入します
self.X = X
# 受け取ったデータXを標準化してください
X = (X - X.mean(axis=0)) / X.std(axis=0)
# 標準化したデータの相関行列を計算してください
R = np.corrcoef(X.T)
# 相関行列を固有値分解し、固有値と固有ベクトルを求めてください
eigvals, eigvecs = np.linalg.eigh(R)
# 2次元に圧縮する特徴変換行列を作成してください
W = np.c_[eigvecs[:,-1], eigvecs[:,-2]]
# データXを特徴変換して得たデータをインスタンス変数dataに代入してください
self.data = X.dot(W)
# 実験データを読み込み、DataFrameを作成します
df = pd.read_csv("./5030_unsupervised_learning_data/Data_Cortex_Nuclear.csv")
# 今回は使用しないタンパク質のデータである、21列目〜80列目を削除してください
df = df.drop(df.columns[range(21,81)],axis = 1)
# 今回使用するクラスのマウスは計29匹なので、29×15=435個のデータを用います
# 435行目以降は使わないので434行目までを抽出してください、なおdf.iloc[:n]とするとn-1行目までしか抽出できない点に注意してください
df = df.iloc[:435]
# 欠損値nanを含む行をリストワイズ削除してください
df = df.dropna()
# 最終列のclassを抽出し、ラベルyとして定義してください
y = df.iloc[:,-1]
# 0列目のMouseIDと目的変数であるclassを削除し、特徴量Xとして定義してくだい
X = df.iloc[:,1:-1]
# PCAクラスを用いてデータを分析してください
pca = PCA()
pca.fit(X)
# 圧縮したデータを取得し、matplotlibで表示してください
X_pca = pca.data
colors = ["r", "g", "b"]
marker = ["s", "x", "o"]
for label, color, marker in zip(y.unique(), colors,marker):
# 横軸(第一引数)にはX_pcaの0列目を、縦軸(第二引数)にはX_pcaの1列目を表示します
plt.scatter(X_pca.loc[y == label, 0], X_pca.loc[y == label, 1],c=color, label=label, marker=marker)
plt.legend()
plt.show()
自作アプリケーション:同じ問題 by SVM and Kernel SVM of Sklearn
import matplotlib.pyplot as plt
import pandas as pd
import matplotlib.pyplot as plt
# KernelPCAをインポート
from sklearn.decomposition import KernelPCA
from sklearn.decomposition import PCA
# 実験データを読み込み、DataFrameを作成します
df = pd.read_csv("./5030_unsupervised_learning_data/Data_Cortex_Nuclear.csv")
# 今回は使用しないタンパク質のデータである、21列目〜80列目を削除してください
df = df.drop(df.columns[range(21,81)],axis = 1)
# 今回使用するクラスのマウスは計29匹なので、29×15=435個のデータを用います
# 435行目以降は使わないので434行目までを抽出してください、なおdf.iloc[:n]とするとn-1行目までしか抽出できない点に注意してください
df = df.iloc[:435]
# 欠損値nanを含む行をリストワイズ削除してください
df = df.dropna()
# 最終列のclassを抽出し、ラベルyとして定義してください
y = df.iloc[:,-1]
# 0列目のMouseIDと目的変数であるclassを削除し、特徴量Xとして定義してくだい
X = df.iloc[:,1:-1]
# KernelPCAクラスをインスタンス化
kpca = KernelPCA(n_components=2, kernel="rbf", gamma=15)
# データXをKernelPCAを用いて変換
X_kpca =kpca.fit_transform(X)
X_kpca = pd.DataFrame(X_kpca)
pca=PCA(n_components=2)
X_pca=pca.fit_transform(X)
X_pca = pd.DataFrame(X_pca)
colors = ["r", "g", "b"]
marker = ["s", "x", "o"]
marker2 = ["|", ".", "_"]
# 可視化
for label, color, marker in zip(y.unique(), colors, marker):
# 横軸(第一引数)にはX_pcaの0列目を、縦軸(第二引数)にはX_pcaの1列目を表示します
plt.scatter(X_pca.loc[y == label, 0], X_pca.loc[y == label, 1],
c=color, label=label, marker=marker)
plt.legend()
plt.show()
for label, color, marker2 in zip(y.unique(), colors, marker2):
# 横軸(第一引数)にはX_pcaの0列目を、縦軸(第二引数)にはX_pcaの1列目を表示します
plt.scatter(X_kpca.loc[y == label, 0], X_kpca.loc[y == label, 1],
c=color, label=label, marker=marker2)
plt.legend()
plt.show()
結果考察
- 自作SVMもsklearnのSVMもc-CS-mとc-CS-sのクラスタリングがうまくいかない。
- Kernel SVM を用いて主成分分析をした結果、2つのデータ群は同じ主成分を持つことが明らかになった。
Discussion