三角関数とは
ここまでは、データ分析などにおける三角関数の応用についての説明でしたが、ここからは三角関数の原点にもどって説明を進めていきます。
三角関数と聞くとsin、cos、tanなどの用語までは思い出す方も多いでしょう。しかし、それがどのようなものであったか忘れてしまった方もいると思います。そこで、簡単におさらいすると、図2の通りです。
筆者も「どちらがsinでどちらがcosだろうか」と、学生時代によく悩んだ経験があります。
その時に、図2のsin(赤線)とcos(青線)を波の図としてイメージすると、「0から始まるのがsinで、1から始まるのがcos」と思い出せるはずです。
その知識を元に再度、単位円内に描かれる角度を見てみます。角度が0の時、y=0の値になるのがsinであり、y=1の値になるのがcosなので、単位円上の角度とsinやcosの波形グラフの関係性が理解しやすくなります。
前回までは一次関数や二次関数について説明しましたが、それらが数直線上で変化するイメージだとしたら、三角関数は空間上で変化する値を見るツールと言えます。
三角形、もしくは円の特徴をこのようなイメージまで広げることができれば、これらの応用範囲も広くなるはずです。また、先ほどの「コサイン類似度」がなぜcosであって、sinではないかなどを考えてみるのもよいかもしれません。
三角関数(sin, cos)を使って円内に線を描画する
続いて、座標をcos、sinを使って円の中に直線を描画してみます。これは、cos、sinの運動としてのイメージです。
そして、任意の角度での原点から円周上の一点を結ぶ直線を示すためのコードがリスト1です。
renderLine(group,scale, deg){
// (1) 度数からラジアンに変更する
const r = Math.PI / 180 * (deg);
// (2) x, y の値を求める
const x = Math.cos(r);
const y = Math.sin(r);
// (3) 原点と(2)で求めたx,yの値
const dataset = [{
x : 0, y : 0
},{
x : x, y : y
}];
// (4) 2点間の線を描画
const line = d3.line().x(function (d){
return scale.x(d.x);
}).y(function(d){
return scale.y(d.y);
});
group.append("path")
.datum(dataset)
.attr('fill','none')
.attr('stroke',"green")
.attr('stroke-width', 2)
.attr('d',line);
}
JavaScriptの三角関数はラジアン単位で計算するため、(1)で角度をラジアンに変換しています。Math.PI (π)は180度に相当する値なので、指定した角度にπをかけて180で割ればラジアンが求まります。
そして、(2)でそれぞれのsin値(y座標)、cos値(x座標)を求めます。これを(3)のように原点と(2)で求めた値の2点間を、これまでと同様に(4)のようにD3のlineメソッドで指定すれば線が描画でき、図4のようになります。
また、数学で扱う場合や、計算上ではラジアンを用いる事も多いですが、多くの人が直感的に分かりやすいのは角度の方だと思います。本稿でも、グラフ表示は度数法を前提に進めていきます。
円内の線を動かす
続いて、先ほど固定で指定していた角度を時間とともに変化させ、動的な表示に変更します。例えば、野球やテニスなどでのスイングしている状況を簡易的に表現した場合には、このように円運動で確認することができます。
そのため、まずは直線を円周上で回転させるシンプルなアニメーションを実装したのが、リスト2です。
renderMoveLine(group,scale){
// (1) 線を描写する関数を用意する
const line = d3.line().x(function (d){
return scale.x(d.x);
}).y(function(d){
return scale.y(d.y);
});
// (2) 線を描写するためのpath要素を作成する
const path = group.append("path")
.attr('fill','none')
.attr('stroke',"orange")
.attr('stroke-width', 2);
// (3) 最初の角度は0度
let deg = 0;
// (4) 繰り返し実行する
setInterval(() => {
// (5) 新しいデータを作成
const r = Math.PI / 180 * (deg);
const x = Math.cos(r);
const y = Math.sin(r);
const dataset = [{
x: 0, y: 0
},{
x: x, y: y
}];
// (6) line表示のみを更新する
path.datum(dataset).attr('d', line);
deg++;
},100);
}
D3では、同じ要素を更新する際に、要素の変化する部分だけを更新することで動的な表示を実装できます。
そのため、まず変わらない部分を最初に記述します。(1)の線を描くための関数と(2)のpath要素を最初に作成します。
そして、初期値の角度を0度(3)として(4)でデータを繰り返し処理します。今回はsetIntervalで処理していますが、実際にはサーバーからリアルタイムデータを取得したり、ユーザー操作に応じたデータ更新を行うことが多いでしょう。
次に(5)で新しい角度に対応するデータを作成します。そして、最後に(6)でそのデータを使って線を引き直します。
これで、図5のように先ほどの線が回転するようになります。
