Home

2次元配列データの特異値分解による内積表現

 

 

クロス表(対応表)は2次元配列データの特殊な場合(セル値が非負整数値)である。2次元配列データは、行列として特異値分解を適用することができる。特異値分解から内積による所与の行列の表現が導かれ、これによりデータの直感的把握、情報の視覚化が容易になる(岡本、2023)。特異値分解から内積表現の導き方情報教育調査の分析例色と象徴語の関係の分析例セマンティックディファレンシャルデータの分析例の説明を行った後、内積表現の解釈について論じる。ファイルは、圧縮ファイルinner_product.zipにまとめている。

 

 

 

特異値分解から内積表現

 

 

 型行列のランクがであるとき、次のように特異値分解(singular value decomposition (SVD) (Magnus & Neudecker, 1999)ができる。

 

 

ここで、

 

 

であり、はランクの単位行列、は次式の対角行列である。 

 

.

 

内積表現は、(1)式を次のように書き直すことにより得られる。

 

, 

 

上式より

 

 

すなわち、次の内積表現を得る。

 

 

内積は、ベクトルの長さの積にその挟む角のコサインを掛けたものである(Figure 1).  

 

 

 


Figure 1  Vectors  and , drawn from the origin .

 

 

式(2)のは、座標の平均2乗和のバランスがとれるように決める、すなわち、

 

 

である。次式

 

 

より、

 

 

となり、次式を得る。

 

 

が式 (2)を満たすことより、次式を得る。

 

 

これらのに対して、次式が成り立つ。

 

 

 

布置の距離とデータの距離

 

 

データの第行をで表す。

 

 

すなわち、

 

である。このとき、ベクトルの距離は次式で与えられる。

 

 

ここで、

=

 

である。すなわち、の距離は、座標ベクトルの重み付距離で与えられる。.

 

列ベクトルの場合も同様に考える。

 

 

と置けば、

 

 

より、次式を得る。

 

 

ここで、

=

 

である。列ベクトルの距離は、布置ベクトルの重み付距離で表される。.

 

 

 

布置の直交回転

 

内積は、布置の直交回転に対して不変であるので、得られた布置を見やすく直交回転することができる。

いま、Table 1のデータについて考える。行項目がR1,,R3,であり、列項目がC1,,C3.である。

 

Table 1  Frequencies for rows R1,..,R3 and

        columns C1,,C3 (artificial data).

 

C1

C2

C3

R1

10

1

0

R2

0

10

1

R3

0

0

10

 

Table 1のデータに対して、特異値分解を行い、布置を求めるとFigure 2のような布置を得る。

 


Figure 2

 

座標軸1上の値が重なっていて、座標軸1は項目間の差を表す情報が含まれていない。

Figure 2の布置を回転すると、Figure 3のようになる。

 


Figure 3

 

布置を回転することにより、座標軸1が項目間の差異を表すことが期待される。

 

直交回転を求めるために、求めた布置をまとめてとする。

 

 

の主要な分散の方向を求めるために中心化する。

 

 

の分散を極大化する方向,, を次式で与える。

 

 

これらの,, は、次式を満たす(Timm, 1975)

 

 

すなわち、の固有ベクトルであり、は固有値である (Magnus & Neudecker, 1999)

次式

 

 

より、方向の分散に対応している。

 

分散の方向を最大(極大)にするベクトル,, により、直交回転を次式で与える。

 

 

回転後の座標は次式で与えられる。

 

 

回転後の布置は、Figure 4のようになる。ベクトルの座標がプロットされているので、ベクトルは原点(星印)から座標点までの矢印で表される。ただし、矢印は描画されていない。


Figure 4

 

 

 

クラスター分析

 

内積を与えるベクトルの布置が得られると、クラスター分析を行うことができる。布置の座標に対してはWard法を適用することができる。この場合、ベクトルの向きと長さの観点からのクラスターが得られる。ベクトルの挟む角度に基づいて分析すると、向きのみの観点からのクラスター分析となる。

 

 

 

分析例

 

情報教育調査結果色と象徴語の適合度データセマンティックディファレンシャルデータの分析例を示す。

データはCSV形式のファイルでプログラムファイルと同じフォルダ内に用意されているものとする。CSV形式のファイルについては、このウェブサイトで説明している。

分析は、リスト1に示すスクリプトで行った。実行は、Anaconda/Windows10Python3.12.4仮想環境で行った。Windows10にインストールしたPython3.12.7で実行すると、モジュールjapanize_matplotlibに関係したエラーが出た(2024.10.19)。モジュールjapanize_matplotlibimportを行わなければエラーは出ない。このとき、日本語を用いるときは、rcParamsを用いればよい。すなわち、

 

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

#import japanize_matplotlib

from scipy.cluster.hierarchy import linkage

from scipy.cluster.hierarchy import dendrogram

 

plt.rcParams['font.family'] = 'Microsoft Yahei'  # フォント名'Microsoft Yahei'は、使用PC依存

 

とする。この方法を用いれば、Python 3.12.7でも実行可能であった(2024.10.19)。

ファイルは、圧縮ファイルinner_product.zipにまとめた。

 

 

 

情報教育調査

 

 

入力データファイルはCSV形式のファイルの形式で用意する。図I.1は、政府統計ポータルサイトe-Statからダウンロードした「学校における教育の情報化の実態等に関する調査(令和3年度)」のファイルをリスト1のスクリプト用に編集したものである。

 

I.1 「学校における教育の情報化の実態等に関する調査(令和3年度)」(e-Statより)を編集

 

1行目に列項目名、1列目に行項目名が設定されている。セル内の数値はPC(デバイス)の設置台数である。

リスト1のスクリプトを実行すると、入力データファイル名の設定が求められる。

I.1CSVファイル名情報調査結果.csvを設定する。

 

CSV File(*.csv) = 情報調査結果.csv

 

入力ファイル名を設定すると計算が始まり、まず特異値分解の特異値のスクリープロットが表示される(図I.2)。

I.2

 

布置の分布は、ほぼ1次元であることが分かる

I.2Windowを閉じると、図I.3のグラフが表示される。

I.3

 

回転を求めるための固有分解の固有値のスクリープロットである。このスクリープロットから、布置はほぼ1次元空間に分布していることが分かる。

I.3Windowを閉じると、図I.4のマップが表示される。

I.4

 

小学校が、iOSChromeWindows 10と、中学校と高等学校がChromeWindiows 10と近いことが表されている。

I.4Windowを閉じると、次に表示するマップの座標軸の設定が求められる。

0以下の値を設定すると、マップの表示が終わり、次に進む。

 

Maximum dimension = 7

Dimension for x-axis = 0

 

マップの表示が終わると、クラスター分析が行われる。まず、布置座標によるクラスター分析の結果が表示される(図I.5)。

I.5

 

小学校、中学校、ChromeiOSWindows 10がクラスターとしてまとまっている。

I.5Windowを閉じると、図I.6のグラフが表示される。

I.6

 

ベクトルの角度によるクラスター分析である。長さは無視される。小学校、中学校、ChromeWindows 10iOSがクラスターとしてまとまっている。高等学校がWindows 11とまとまっているが、これは角度によるクラスターリングなので、絶対数ではなく相対度数によるまとまりと考えられる。

I.6Windowを閉じると、スクリプトの実行終了である。

 

 

 

色と象徴語の関係

 

C1に示すデータは、大山(1994に説明されている色と象徴語の関係を、適合するものを1で、そうでないものを0で表した岡本(2014の表14.1.1CSV形式のファイルとしたものである。

 

C1 

 

C1では、適合するかどうかが1と0で表されているが、データが適合すると答えた人の人数、あるいは適合度を100点満点で評価したときの平均値であれば、図C1の数値は0以上の実数となる。また、適合しないを負の数で表すデータも考えられる。いずれにせよ、1と0で適合するかしないかを表すという大まかなデータでどのような分析結果となるかは興味深いと思われる。

C1のデータをリスト1のプログラムで分析する。

プログラムを実行すると、入力ファイル名の設定が求められる。

C1のファイル名を設定する。

 

CSV File(*.csv) = CncptClr.csv

 

ファイル名を設定すると計算が始まる。

先ず、特異値のグラフ(スクリープロット)が表示される(図C2)。

 

C2

 

3つ目までの特異値が大きいが、1番目の特異値はデータの全体的な様子を表すので、2番目と3番目の特異値が個々の変数の関係を表していると予想できる。

C2Windowを閉じると、図C3のグラフが表示される。

 

C3

 

布置の回転を行うときの主成分分析の固有値のグラフである。1番目と2番目の固有値が大きいので、布置はほぼ2次元空間に配置されていると考えられる。

C3Windowを閉じると、回転後の布置が表示される(図C4)。

 

C4

 

色が色相環に沿う形で配置され、それぞれの色に適合する象徴語が色に沿う形で配置されている。原点(星印)から色(列項目:下向き3角形)あるいは象徴語(行項目:上向き3角形)の位置までがベクトルを表す。

C5Windowを閉じると、次の描画における次元の設定が求められるので、次元の他の組み合わせにおける布置が得られる。次元として0以下の値を設定すると、クラスター分析に進む。

まず、座標値によるクラスター分析の結果が表示される(図C5)。

C5

 

C5Windowを閉じると、角度によるクラスター分析の結果が示される(図C6)。

C6

 

C6Windowを閉じると、プログラムは終了する。

 

 

 

セマンティックディファレンシャル

 

S1は、大山ら(2005の図4-4に示されているセマンティック・ディファレンシャルのデータ(「幸福」のイメージに対する評定形容詞対の平均値)を読んで、CSV形式のファイルとしたものである。形容詞対は、対の一方のみを示している。

 

S1 「幸福」のイメージに対する平均値

 

S1のデータをリスト1のプログラムで分析する。

プログラムを実行すると、入力データファイル名の設定が求められる。

S1のファイル名を設定する。

 

CSV File(*.csv) = SDHp.csv

 

 

入力データファイル名を設定すると、データは読み込まれて計算が始まる。

まず、特異値のグラフ(スクリープロット)が表示される(図S2)。

 

S2

 

1番目と2番目の特異値が大きいので、データ配列はほぼ2次元の布置で表されると考えられる。

S2のグラフを閉じると、固有値のスクリープロットが表示される(図S3)。

 

S3

 

固有値のスクリープロットから、第1固有値と第2固有値が大きく、布置はほぼ2次元空間に配置されていると考えられる。図S1のデータの場合、データ値が正の値と負の値の量符号があるので、ベクトルの位置関係は同方向(内積が正)と反対方向(内積が負)の2通りがあるので、分布は原点を中心にして両方向に広がっていると考えられる。このため、特異値分解の特異値のスクリープロットの反映する次元数は固有分解の固有値のスクリープロットが反映する次元数と同じになっていると考えられる。

S3Windowを閉じると図S4の布置のグラフが表示される。

 

S4

 

原点(星印)から国(行項目:上向き3角形)あるいは形容詞対(列項目:下向き3角形)の位置までがベクトルを表す。

「幸福」のイメージとして、「美しい」、「好きな」、「良い」、「明るい」、「陽気な」があげられ、「緊張した」とは反対のイメージであることが表されている。「日本」と「台湾」のグループと、「米国」と「セルビア」のグループにまとまっていて、「米国」と「セルビア」のグループでは「幸福」は「動的な」と「派手な」のイメージに近いが、「日本」と「台湾」ではそうではないことが表されている。

S4Windowを閉じると、次に表示する布置の次元の設定が求められる。

 

Dimension for x-axis = 0

 

0以下の数値を設定すると、布置の描画は終わり、クラスター分析が行われる。

まず、座標によるクラスター分析が行われる(図S5)。

S5

 

日本と台湾が同じクラスタ―、米国とセルビアが同じクラスターにまとめられている。

S5Windowを閉じると、挟む角によるクラスター分析が行われる(図S6)。

S6

 

S6では、米国はセルビアから離れて別々のクラスターにまとめられている。

布置を、第1次元を横軸に、第3次元を縦軸にとって描いてみると、図S7のようになる。

S7

 

「動的な」などに、米国とセルビアに違いがあるようである。

 

S6Windowを閉じると、プログラムの終了である。

 

 

 

 

注意点など

 

特異値分解は、適用できる2次元配列に制約はない。そこから内積表現が導かれるが、これは数学的に表現されるということであり、心理学的に自然な解釈ができることとは別である。例えば、色と象徴語の適合度のデータにおいて、不適合である方に注目して不適合であれば1とコーディングした場合、不適合であればベクトルの内積は大きくなりベクトルは近付くという表現が予想されるが、これは不自然であろう。また、セマンティックディファレンシャル法では双極性の尺度を用いることが標準であるが、尺度値をー3から3までの数値で表すとき、合う関係は内積が正になりベクトルは近付き、合わないときは内積は負になりベクトルは反対方向に向くので、自然な解釈になる。しかし、これを尺度値として1から7までの数値を用いると、反対方向の関係が内積が0に近い値で表されるので不都合と考えられる。単極性の尺度のとき、その尺度に合う場合に7,関係がない場合に0を与えるときは、尺度に合うときは同じ向きになり、無関係のときは内積が0、すなわち直交関係になるので、自然な表現となる。

特異値分解とそこから導かれる内積表現はデータを選ばないが、得られた解の解釈はデータに依存している。

 

 

 

 

関連文献

 

Borg, U. & Groenen, P. J. F. (2005) Modern multidimensional scaling: Theory and applications, second edition. Springer.

Greenacre, M. (2017) Correspondence analysis in practice, third edition. CRC Press.

Magnus, J. R. & Neudecker, H. (1999) Matrix differential calculus: with applications in statistics and econometrics, revised edition. John Wiley & Sons.

岡本安晴(2014) 心理学データ分析と測定.勁草書房

岡本安晴(2023)「クロス表のSVDおよび内積による表現と座標変換――グラフ図示による例示――」日本行動計量学会第51回大会発表.

https://doi.org/10.20742/pbsj.51.0_58

Okamoto, Y.(2024) Inner Product Representation of Two-dimensional Array of Data Derived from Singular Value Decomposition.

https://osf.io/ye5jf/    DOI 10.17605/OSF.IO/YE5JF

大山 正(1994) 色彩心理学入門――ニュートンとゲーテの流れを追って――.中公新書.

大山 正・岩脇三良・宮埜嘉夫(2005)「心理学研究法」サイエンス社

Timm, N. H. (1975) Multivariate analysis: with applications in education and psychology. Brooks/Cole Publishing Company.

 

 

 

 

リスト1 2次元配列データの分析スクリプト例

 

 

"""

         Yasuharu Okamoto, 2024.08,2024.10

"""

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

import japanize_matplotlib

from scipy.cluster.hierarchy import linkage

from scipy.cluster.hierarchy import dendrogram

 

#         Read CSV file

fl_nm = input('CSV File(*.csv) = ')

df = pd.read_csv(fl_nm, encoding='shift-jis')

 

#       Names of column items

col_names = df.keys()   

col_names = col_names[1:]  

#        Names of row items

row_names = df.values.T[0]

#

#       Values in the two dimensional array data

#

df_data = df.values   

F = df_data[:,1:]         

data = np.array(F, dtype=float)

 

print(data)

#       Singular Value Decomposition of data

u, s, vh = np.linalg.svd(data, full_matrices = False)

p = sum([1 if v > 1.0E-9 else 0 for v in s])

s = s[:p]

print('\ns =', s)

print('p =', p)

u = u[:,:p]

vh = vh[:p]

#

#         Scree plot of the singular values

#

plt.title('Scree Plot of Singular Values', fontsize=14)

plt.plot(range(1, p+1), s, c = 'k')

plt.xticks(np.arange(1, p+1, 1))

plt.ylabel('Value', fontsize = 14)

plt.tight_layout()

plt.show()

plt.close()

#

#            Weighting

#

M = len(data)

N = len(data.T)

s1 = np.empty(p)

s2 = np.empty(p)

for t in range(p):

    s1[t] = (s[t]**0.5) * ((M/N)**0.25) 

    s2[t] = (s[t]**0.5) * ((N/M)**0.25) 

 

row_coord = u @ np.diag(s1)

col_coord = np.diag(s2) @ vh

 

#

#          Concatenation of the row and column coordinates

#

mrg_coord = np.concatenate((row_coord, col_coord.T), axis = 0)

mrg_label = np.concatenate((row_names, col_names), axis = 0)

 

#

#           Eigendecomposition with centering

#

mean_X = np.mean(mrg_coord, axis = 0)

Z = mrg_coord - mean_X

w, v = np.linalg.eigh(Z.T @ Z)

 

sidx = np.argsort(-w)

w = w[sidx]

v = v.T[sidx].T

#      Scree plot of the eigen values

plt.title('Scree Plot for w', fontsize=16)

plt.plot(np.arange(len(w))+1,w, c='k')

plt.xticks(np.arange(len(w))+1)

plt.ylabel('Eigen value', fontsize=14)

plt.tight_layout()

plt.show()

plt.close()

#

#       Rotation of the coordinates

#

Proj = v.T

Proj_mrg_coord = (Proj @ mrg_coord.T).T

#

#       Output the coordinates to the file Coord.txt

#

p_dim = 5 if p > 5 else p

fout = open('Coord.txt', 'w')

fout.write(f'\n\n{"Names":<15s}')

for i in range(p_dim):

    fout.write(f'{f"Dim-{i+1}":<15s}')

fout.write('\n')

for i in range(M):

    fout.write(f'{row_names[i]:<15s}')

    for j in range(p_dim):

        fout.write(f'{Proj_mrg_coord[i][j]:<15.5f}')

    fout.write('\n')

 

fout.write('\n')

for i in range(N):

    fout.write(f'{col_names[i]:<15s}')

    for j in range(p_dim):

        fout.write(f'{Proj_mrg_coord[M+i][j]:<15.5f}')

    fout.write('\n')

 

fout.close()

print('\n', 'Coord.txt', 'was saved.')

 

#

#        Plot of coordinates

#

max_coord = np.max(np.abs(Proj_mrg_coord))

 

dimx = 0

dimy = 1

while True:

    plt.close()

    plt.figure(figsize=(7,7))  

    plt.xticks([-max_coord, max_coord])

    plt.yticks([-max_coord, max_coord])

    #      Plot the row items

    plt.plot(Proj_mrg_coord.T[dimx][:M], Proj_mrg_coord.T[dimy][:M],

             '^', c = 'k', label = 'Row')

    #      Plot the column itens

    plt.plot(Proj_mrg_coord.T[dimx][M:], Proj_mrg_coord.T[dimy][M:],

             'v', c = 'k', label = 'Col')

    #      Write the row item names

    for i in range(M):

        plt.text(Proj_mrg_coord.T[dimx][i], Proj_mrg_coord.T[dimy][i] - 0.05,

                 row_names[i], rotation = 0, fontsize = 14, color='k')

    #      Write the column item names

    for i in range(N):

        plt.text(Proj_mrg_coord.T[dimx][M + i], Proj_mrg_coord.T[dimy][M + i] + 0.05,

                 col_names[i], rotation = 0, fontsize = 14, color='k')

    plt.plot([0], [0], '*', c = 'k', markersize = 12, label = 'Origin')

    plt.plot([-max_coord, max_coord], [0, 0], c = 'k', ls = ':')

    plt.plot([0, 0], [-max_coord, max_coord], c = 'k', ls = ':')

    plt.legend()

    plt.title('Orthogonally Rotated Configulation', fontsize = 16)

    plt.xlabel(f'Axis-{dimx+1}', fontsize = 14)

    plt.ylabel(f'Axis-{dimy+1}', fontsize = 14)

    plt.tight_layout()

    plt.show()

    #

    #      Selection of the axes for the next plot

    #

    print(f'\nMaximum dimension = {p}')

    dimx = int(input('Dimension for x-axis = '))

    if dimx < 1:    #    End this plotting

        break

    if dimx > p:

        dimx = p

    dimx -= 1

    dimy = int(input('Dimension for y-axis = '))

    if dimy < 1:    #     End this plotting

        break

    if dimy > p:

        dimy = p

    dimy -= 1

 

#

#     Concatenate the row and column coordinates for clustering

#

mrg_coord = np.concatenate((row_coord, col_coord.T), axis = 0)

#

#     Concatenate the row and column names for clustering

#

mrg_label = np.concatenate((row_names, col_names), axis = 0)

#

#        Clustering by the coordinates

#

row_clusters = linkage(mrg_coord, method = 'ward')

fig = plt.figure(figsize = (7, 5), facecolor = 'white')

row_dendr = dendrogram(row_clusters, labels = mrg_label,

                       orientation = 'right', leaf_font_size = 14,

                       link_color_func = lambda k: 'k')

plt.title('Clustering by Coordinates', fontsize=14)

plt.tight_layout()

plt.show()

 

#

#      Calculate the angle between two vectors

#

def calc_angle(x, y):

    v = np.dot(x, y) / ((np.dot(x,x))*np.dot(y,y))**0.5

    v1 = 1.0 if v > 1.0 else -1.0 if v < -1.0 else v

    return np.arccos(v1)

 

#

#      Calculation of angles between vectors of items

#

 

Angles = np.empty((M+N, M+N))

for i in range(M+N):

    for j in range(M+N):

        if i > j:

            Angles[i][j] = Angles[j][i]

        elif i == j:

            Angles[i][j] = 0.0

        else:

            Angles[i][j] = calc_angle(mrg_coord[i], mrg_coord[j])   

 

#

#       Cluster Analysis by angles

#

dists = []

for i in range(len(Angles)-1):

    for j in range(i+1, len(Angles)):

        dists.append(Angles[i][j])

 

row_clusters = linkage(dists, method = 'complete')

fig = plt.figure(figsize = (7, 5), facecolor = 'white')

row_dendr = dendrogram(row_clusters, labels = mrg_label,

                       orientation = 'right', leaf_font_size = 14, link_color_func = lambda k: 'k')

plt.title('Clustering by Angles', fontsize = 14)

plt.tight_layout()

plt.show()

 

 

 

Home