相関のある疑似データを生成し，因子分析をしてみる試み

# 背景・目的
因子分析や共分散構造分析などマーケティングリサーチで使える手法は，多々あります．今回対象としている因子分析は，多変量間に共通する因子を探り出して，それらの共通因子を使ってデータを評価するための技法です（y_itoh, 2020）．因子分析では，得られた結果を解釈するのは人間がやります．そのため，試しに因子分析をやりたい！と思って何も考えずに乱数生成したデータでは結果の解釈が容易ではなく，適切な分析が出来ているのか分からないという事態になります．
そこで，**今回は相関のある疑似乱数を生成し，そのデータを使って因子分析する手法を実装してみたいと思います**．結論から申し上げますと，相関のあるn個の疑似乱数を生成する際に相関行列を適切に設定できれば自身が望む傾向のあるデータを生成できると思います（今回は，うまくできませんでした:cry:）．

# 相関のある疑似データの生成
相関のある疑似データの生成は，先行事例（horiem, 2017）があるコレスキー分解を使用した手法で行います．詳しい理論的な説明は，先行事例の記事がとても分かりやすいので，そちらを参考にしていただければと思います．
先行事例では3変数を扱っていましたが，本記事では12変数の相関のある疑似データを生成してみます．この相関行列をチューニングするのにとても時間がかかりました．個人的には，変数ごとにより差のあるデータを生成したかったのですが，（1）対称行列であること，（2）正定値行列であることというコレスキー分解が可能な2つの条件をなかなか満たすことができず，最終的に下記の結果に落ち着きました．
```python
import numpy as np

np.random.seed(seed=314)

n = 12 # The number of random numbers
size = 1000 # Size of the vector
corr_matrix = np.matrix([
    [1.00, 0.65, 0.60, 0.61, 0.64, 0.66, 0.58, 0.55, 0.65, 0.60, 0.66, 0.60],
    [0.65, 1.00, 0.68, 0.65, 0.65, 0.66, 0.45, 0.44, 0.65, 0.60, 0.60, 0.61],
    [0.60, 0.68, 1.00, 0.66, 0.51, 0.61, 0.45, 0.45, 0.66, 0.50, 0.65, 0.70],
    [0.61, 0.65, 0.66, 1.00, 0.70, 0.69, 0.42, 0.45, 0.65, 0.65, 0.57, 0.55],
    [0.64, 0.65, 0.51, 0.70, 1.00, 0.69, 0.46, 0.40, 0.54, 0.57, 0.67, 0.69],
    [0.66, 0.66, 0.61, 0.69, 0.69, 1.00, 0.44, 0.45, 0.26, 0.45, 0.44, 0.46],
    [0.58, 0.45, 0.45, 0.42, 0.46, 0.44, 1.00, 0.49, 0.45, 0.46, 0.48, 0.49],
    [0.55, 0.44, 0.45, 0.45, 0.40, 0.45, 0.49, 1.00, 0.44, 0.46, 0.44, 0.45],
    [0.65, 0.65, 0.66, 0.65, 0.54, 0.46, 0.45, 0.44, 1.00, 0.49, 0.48, 0.45],
    [0.60, 0.60, 0.50, 0.65, 0.57, 0.45, 0.46, 0.46, 0.49, 1.00, 0.48, 0.49],
    [0.66, 0.60, 0.65, 0.57, 0.67, 0.44, 0.48, 0.44, 0.48, 0.48, 1.00, 0.48],
    [0.60, 0.61, 0.70, 0.55, 0.69, 0.46, 0.49, 0.45, 0.45, 0.49, 0.48, 1.00]
]) # Correlation matrix

w, v = np.linalg.eig(corr_matrix)

# Generate correlated random numbers
l = np.linalg.cholesky(corr_matrix)
x = np.random.randn(n, size)
z = l * x

df = pd.DataFrame(z.T, columns=['Wine_1','Wine_2','Wine_3','Beer_1','Beer_2','Beer_3','Sushi_1','Sushi_2','Beef_1','Beef_2','Beef_3','Chicken_1'])
```
今回は分かりやすく飲食店のメニューを変数としてみました．
実際にデータを表示してみると下記のような感じになります．
```python
import matplotlib.pyplot as plt
import seaborn as sns

plt.rcParams['xtick.direction'] = 'in'
plt.rcParams['ytick.direction'] = 'in'
plt.rcParams['xtick.minor.visible'] = False
plt.rcParams['ytick.minor.visible'] = False
plt.rcParams['font.family'] = 'Arial'

fig, ax = plt.subplots()
ax.scatter([z[0, :]], [z[1, :]], s=2, color='red', label='Z_2')
ax.scatter([z[0, :]], [z[2, :]], s=2, color='blue', label='Z_3')
ax.set_xlabel('Z_1')
ax.set_ylabel('Z_2, Z_3')
ax.legend()
plt.show()

pg = sns.pairplot(df)
```
![Dammy_data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133910/777973f3-b435-a72f-95b8-3ba8cc841628.png)
![Dammy_data_pairplot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133910/94238d69-3e7e-3b67-f2df-d66dcaf853b7.png)
データも用意できたので因子分析をしていきます．

# 因子分析
因子分析（Factor Analysis）は，計測や観測により得られた多くの変数（データ）の変動をできるだけ少数個の仮説的な変量（因子）で記述しようとする統計的な分析手法です．主成分分析と因子分析は，因果の方向が逆という明確な違いがあります．今回は，因子数を5つに設定して分析をしています．
```python
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import FactorAnalysis as FA

sc = StandardScaler()
sc.fit(df)

z = sc.transform(df)

n_components=5

fa = FA(n_components, max_iter=5000)
fitted = fa.fit_transform(z)
Factor_loading_matrix = fa.components_.T

pd.DataFrame(Factor_loading_matrix, 
             columns=["Factor_1", "Factor_2", "Factor_3","Factor_4", "Factor_5"], 
             index=[df.columns])
```
最終的な結果は，以下になります．以下の数値は，因子負荷量です．
```text
Factor_1	Factor_2	Factor_3	Factor_4	Factor_5
Wine_1	0.733008	-0.189831	0.001429	-0.132055	-0.632595
Wine_2	0.791074	-0.080618	-0.077347	0.059139	-0.090933
Wine_3	0.857967	0.282146	-0.400960	0.150323	0.000318
Beer_1	0.757614	-0.178737	-0.071235	0.169814	-0.057495
Beer_2	0.843292	-0.284081	0.404086	0.209029	0.009824
Beer_3	0.781827	-0.541437	-0.250740	-0.173385	0.039368
Sushi_1	0.527489	-0.035283	0.056556	-0.099774	-0.267874
Sushi_2	0.512643	-0.066197	0.034048	-0.140894	-0.232295
Beef_1	0.633002	-0.031116	-0.128842	0.327757	-0.306124
Beef_2	0.599210	-0.029141	0.079172	0.142376	-0.277486
Beef_3	0.695714	-0.015949	0.033198	0.457954	-0.322328
Chicken_1	0.862141	0.390898	0.258097	-0.189611	0.019999
```
因子負荷量とは，因子分析において得られた共通因子が分析に用いた各変数に与える影響の強さを表現する値です．つまり，得られた共通因子と各変数の関係性を示します．-1から1までの値をとります．
また，各因子負荷量の2乗和から共通性が算出可能です．共通性は，0～1の値となり，1から各変数の共通性の和を減すると独自性が分かります．
因子分析では，最終的には人間が結果を解釈する必要があります．例えば，上記の結果では因子1は全体的な好感度を示し，因子2や因子3は特定のアイテムに対する好みの差異を示し，因子4は牛肉に特化した評価を示し，因子5は特定の否定的な評価を持つアイテムに関連していると解釈できます．一方で，前述と異なる解釈も人によっては出てくるため，仮説を立てた分析が重要になってきます．

# まとめ
今回は，相関のある疑似データを生成し，因子分析をしてみました．相関のある疑似データの生成は，変数の数が多くなるほど人手でやるのは大変に感じました．生成AIにでも任せたいと思います．なお，こんな良いやり方あるよ！と教えていただける心根の優しい方がおりましたらコメントなどいただけますと幸いです．

# 参考文献やサイトなど
1. y_itoh. 2020. 2. Pythonで綴る多変量解析　4-1. 因子分析(scikit-learn). https://qiita.com/y_itoh/items/227cb33317ceb09199c2, accessed on 22 Apr. 2024.
2. horiem. 2017. 相関のある n 個の擬似乱数の生成（Python サンプルつき）. https://qiita.com/horiem/items/e3dfe0a0f0ac3a66f509, accessed on 18 May 2024.