LoginSignup
1
0

More than 5 years have passed since last update.

シェルスクリプトで距離行列を内積の値に変換

Last updated at Posted at 2016-06-22

距離行列が与えられている時に、その行列を内積の値に変換する。

そもそも距離行列とは

a b c d e
a 0,4,6,4,4
b 4,0,5,3,3
c 6,5,0,3,1
d 4,3,3,0,3
e 4,3,1,3,0

というような形で与えられ、上の例ではa~eのお互いの距離が記された行列である。

この距離行列が用いられる場面では、ここから多次元尺度構成法などで、二次元にプロットなどが行われる。実際にプロットを行うためには、この距離行列を内積の値に変換し、固有ベクトルを求めることで座標を得る必要があるが、シェルスクリプトで内積の値への変換を行ってみる。

内積への変換は、まず、距離行列のそれぞれの成分を2乗して2で割ったものをそれぞれの成分に置き換え、それを新たな距離行列とし、距離行列の名前をmatrix、各行の平均をそれぞれ、ave_1,ave_2,ave_3....、matrix全ての値の平均をall_ave、matrixの成分をmatrix[1,2]などとした時に、各行列の成分を以下のように変換することでなされる。
なお、以下の "=" は代入

matrix[x,y]=ave_x + ave_y - all_ave - matrix[x,y]

標準入力として、以下のようなカンマ区切りの行列を想定して、内積値への変換スクリプトを書いてみる。
0,4,6,4,4
4,0,5,3,3
6,5,0,3,1
4,3,3,0,3
4,3,1,3,0

change.sh
#!/bin/bash
#標準入力を変数へ
data=$(cat -)
#全値の平均を変数へ、各行の平均を配列へ
all=$(echo "$data" | tr ',' '\n' | awk '{sum+=$1*$1/2}END{print sum/NR}')
temp=$(echo "$data" | awk -F, '{sum=0;for(i=1;i<=NF;i++){sum+=$i*$i/2};print sum/NF}' | sed '/^$/d' | grep -n ".")
#上記の値を用いてデータを変更
row=1
col=1
echo "$data" |
while read line_1;do
  echo "$line_1" |
  tr ',' '\n' |
  sed '/^$/d' |
  while read line_2;do
    m1=$(echo "$temp" | awk -F : -v num="$row" '$1==num{print $2}')
    m2=$(echo "$temp" | awk -F : -v num="$col" '$1==num{print $2}')
    val=$(echo "$m1 + $m2 - $all - ${line_2} * ${line_2} / 2" | bc)
    echo "$val"
    col=$(($col + 1))
  done | tr '\n' ','
  echo ""
  row=$(($row + 1))
done | sed 's/,$//g' | sed -e 's/-\./-0\./g' -e 's/^\./0\./g' -e 's/,\./,0\./g'

シェルスクリプトでやる意味があるのか?(無いと思う)
そもそもこのシェルスクリプト、無駄が多くね?(かなり適当に書いてる)
awkで計算ってことはでかい数字だと計算ズレね?(だよねー!)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0