共通数量化Pythonプログラム
カテゴリ項目のデータ前処理
2024.5 Jupyter
Notebook版へのリンク
2023.11更新:Excelファイルへの出力スクリプトの更新。説明の増補。
カテゴリ変数に数値を与える方法として機械学習ではOne Hot Encodingと呼ばれている処理がある。各カテゴリ値に対応してダミー変数を用意する方法である。これに対して共通数量化では各カテゴリ値を数値で置き換える。数値に置き換えたときの分散が最大(極大)になるように数値が決められる。indicator 行列を
とおけば、
である。
制約条件
のもとで、分散
が最大になるようにカテゴリに割り当てる数値のベクトル を求める。これは、の中心化行列
の固有ベクトルとして得ることができる。固有分解については、岡本(2009)、pp. 41-45で説明している。
詳しくは、岡本(2019)第8章、あるいはOkamoto (2015)を参照されたい。
<岡本安晴「いまさら聞けないPythonでデータ分析」丸善出版>の第8章では、質問紙データの共通数量化による前処理に基づいて主成分分析、回帰分析を行い、主成分分析の結果を利用してクロス表の作成を行っている。作成したクロス表は第12章および第15章でベイズ分析を行っている。
One Hot Encodingと共通数量化との比較を行動計量学会大会2020で報告した。
以下に共通数量化のPythonプログラム例の使用法について説明する。
Jupyter Notebook版も用意したが、このウェブサイトを参照されたい。
プログラムの使用法
共通数量化を行うプログラムとサンプルデータのファイルを圧縮ファイルcommonqfiles.zipにまとめた。ダウンロード・解凍すれば、自由に利用できる。
プログラムでは、数量化はモジュールmymodule.pyで行っているが、このスクリプトは本サイト後半のリスト1に示した。メインプログラムは本サイト後半のリスト2に示すMainCommonQ.pyである。リスト2のプログラムを実行すると、入力データファイル名の設定が求められる。
Data File
Name (*.xlsx) =
データファイルは、Excelで図1のように用意する。
図1 ファイル名Data.xlsx
1行目に変数名を書く。2行目は、それぞれの変数の処理法、なにもしないときは0、標準化するときは1、数量化を行うときは、そのカテゴリ変数のカテゴリ数を書く。カテゴリ数がKのときはカテゴリ値は1からKまでの整数値で表す。データは3行目以降に書く。
図1のExcelファイルを入力ファイルとするときは、次のようにファイル名を設定する。
Data File
Name (*.xlsx) = Data.xlsx
入力データファイル名を設定すると、計算が始まるが、モジュールxlrdあるいはopenpyxlがないというようなメッセージが出るときがある。このときは、pipやcondaコマンドでモジュールをインストールしてから、あらためてリスト2のプログラムを実行すればよい。
入力データファイル名の設定後、プログラムの実行が終了すると、端末には図3のような出力が表示されている。
Quantified Data was saved in cq_Data.xlsx
Log file = q_Data.txt
数量化の処理後のデータファイルは、cq_Data.xlsxに出力されている。これは入力データファイル名の先頭に「cq_」が付いたものである。このファイルをExcelで開くと、図2のようになっている。
図2
図1のデータの2行目で指定された通りに処理されていることがわかる。標準化を行った変数は、変数名の先頭に「z_」を付けて表されている。数量化を行った変数は、変数名の先頭に「q_」が付けられ、さらに同じカテゴリ変数における異なる数量化は変数名の末尾に「_0」などと通し番号を付けて区別されている。
Log fileの名前「q_Data.txt」は、入力ファイルの先頭に「q_」を付け、ファイル拡張子を「.txt」に替えたものである。開くと以下のようになっている。
Data file = Data.xlsx
Quantification of CA
eigen values = [8.88178420e-16
4.56642909e+00 6.49239444e+00]
Created...q_CA_0 for lambda = 6.4924
with omega = 0.74653
-0.65965 -0.08688
Created...q_CA_1 for lambda = 4.5664
with omega = -0.33069
-0.48117 0.81186
Quantification of CB
eigen values = [8.88178420e-16
4.56642909e+00 6.49239444e+00]
Created...q_CB_0 for lambda = 6.4924
with omega = 0.74653
-0.65965 -0.08688
Created...q_CB_1 for lambda = 4.5664
with omega = -0.33069
-0.48117 0.81186
Output file = cq_Data.xlsx
数量化を行った変数について、固有値、固有ベクトルが出力されている。
なお、固有ベクトル、それから導かれた数量化の値の符号が実行環境によって反転することがあるが、これは式(1)の解が固有ベクトルの符号を反転したものも解であることによる。いずれの解も正しい。
数量化の結果は図2のようにExcelファイルとして得られるので、他の量的変数と同じように分析すればよい。岡本安晴(2015、行動計量学会大会発表要旨)、岡本安晴(2019、「いまさら聞けないPythonでデータ分析」丸善出版、8章)などを参照されたい。
import numpy as np
import scipy as sc
def common_q(K, vname, X, fout):
fout.write('\n\nQuantification of {}'.format(vname))
nq = K - 1
n = len(X)
G = np.zeros((n, K))
for i in range(n):
G[i,
X[i] - 1] = 1.0
f = G.sum(axis = 0)
for k in range(K):
if
f[k] == 0:
print('Category-{0} of variable {1} is not used.'.format(k+1, vname))
fout.write('\n\nCategory-{0} of variable {1} is not used.'.format(k+1,
vname))
fout.close()
raise Exception('Category Error !')
One_n = np.ones((n, 1))
f = np.reshape(f, (1, K))
H = G - One_n @ f / n
HpH = H.T @ H
wt, vctr =
np.linalg.eig(HpH)
idx = np.argsort(wt)
wt = wt[idx]
vctr = vctr.T[idx].T
fout.write('\neigen values =
{}'.format(wt))
nq = K - 1
qnames = []
qvars = []
for h in range(K - 1):
qnames.append('q_' + vname + '_{}'.format(h))
z =
H @ np.reshape(vctr[:, K-1-h], (K, 1)) / ((wt[K-1-h]/n) ** 0.5)
qvars.append(z)
fout.write('\nCreated...{0:s}
for lambda = {1:.5}\n'.format(qnames[-1], wt[K-1-h]))
fout.write('
with omega = ')
for
j in range(K):
fout.write('{0:.5}
'.format(vctr[j][K-1-h]))
return nq, qnames, qvars
import pandas as pd
import scipy.stats as ss
import numpy as np
import mymodule as mm
flnm = input('Data File Name (*.xlsx)
= ')
fout_nm = 'q_' + flnm[:-4] + 'txt'
fout = open(fout_nm, 'w')
fout.write('Data file = ' + flnm)
xlsx = pd.ExcelFile(flnm)
data = pd.read_excel(xlsx)
print(data)
print(data.columns)
print(data.columns.values)
print(data.values)
X = data.values[1:]
print('X =\n', X)
print('X[:,0] =\n', X[:,0])
ndata = len(X)
print('ndata = ', ndata)
indx = data.columns.values
cndtn = data.values[0]
nvar = len(indx)
print('nvar = ', nvar)
print('indx =\n', indx)
print('cndtn =\n', cndtn)
temp_df = pd.DataFrame()
for i in range(nvar):
if (cndtn[i] < 0):
print('condition for var-{} < 0'.format(i))
raise 'var_cond error.'
else:
if
(cndtn[i] == 0):
temp_df[indx[i]] = X[:, i]
elif
(cndtn[i] == 1):
temp_df[indx[i] + '_z'] = ss.zscore([x for x in X[:, i]])
else:
nq, varnames, qvars = mm.common_q(cndtn[i], indx[i], X[:, i], fout)
for j in range(nq):
temp_df[varnames[j]] = qvars[j]
with pd.ExcelWriter('cq_' + flnm) as
xl_writer:
temp_df.to_excel(xl_writer,
index = False)
print('\nQuantified Data was saved in
' + 'cq_' + flnm)
fout.write('\n\nOutput file = ' +
'cq_' + flnm)
fout.close()
print('\nLog file = ' + fout_nm +
'\n')