静かなる名辞

pythonとプログラミングのこと


【python】numpyで楕円形のデータを生成する

 楕円形のデータを生成したいと思った。

理論

 楕円の式は、基本的に次の定義でいく。

  x=a\cos (\theta)\\y=b\sin (\theta)
  0 < \theta < 2\piで、a,\ bが楕円のパラメタである(長半径と短半径)。

 よって、適当なスパンで0~2\piのデータを生成して定義通りxy座標を求めれば良い。簡単そう。

 データを生成する観点からは色々な楕円がほしいので、ちょっと余計な真似をする。

  • 回転角を指定して回転できるようにする

 線形代数の基本。回転行列をかけるだけ。参考:回転行列 - Wikipedia

  • 中心を指定できるようにする

 xyそれぞれの座標に足すだけ。回転させた後に足さないと変なところに飛ばされるので、そこだけ注意する

  • ノイズを乗せる

 理論的には二次元正規分布にするべき・・・とか考えると面倒くさいので、とりあえず各点のXY座標にそれぞれ独立なノイズを乗せて誤魔化すことにする。手抜き。二次元正規分布にするのも然るべき分散共分散行列を与えればそう難しくないはずだけど、やらない(必要な人は自分でやってみて)

実装

# coding: UTF-8

import numpy as np
from matplotlib import pyplot as plt

def make_circle(a=1, b=1, xy=(0,0), phi=0, n=100, random_s=0.1):
    theta = np.arange(0, 2*np.pi, 2*np.pi/n)
    X = a*np.cos(theta)
    Y = b*np.sin(theta)
    data_mat = np.matrix(np.vstack([X, Y]))
    phi_d = np.deg2rad(phi)
    rot = np.matrix([[np.cos(phi_d), -np.sin(phi_d)],
                     [np.sin(phi_d), np.cos(phi_d)]])
    rot_data = rot*data_mat
    X = rot_data[0].A
    Y = rot_data[1].A

    rand1 = np.random.normal(scale=random_s, size=theta.shape)
    rand2 = np.random.normal(scale=random_s, size=theta.shape)

    return X+rand1+xy[0], Y+rand2+xy[1]

def main():
    plt.figure()
    X, Y = make_circle(a=3, b=2, xy=(0,0), phi=0, n=100, random_s=0.2)
    plt.scatter(X, Y, c="r")
    X, Y = make_circle(a=10, b=4, xy=(2,2), phi=45, n=100, random_s=0.5)
    plt.scatter(X, Y, c="g")
    X, Y = make_circle(a=20, b=2, xy=(-3,-5), phi=-70, n=100, random_s=0.5)
    plt.scatter(X, Y, c="b")
    plt.savefig("circle.png")

if __name__ == "__main__":
    main()

 結果は、
f:id:hayataka2049:20180307220454p:plain
 こんな感じで、とりあえず不満のないものが得られました。

 ただ、媒介変数(角度)の変化=一定とし、楕円の円弧の長さの変化=一定としなかったので、割と楕円の長半径近くに点がまとまる。これを改善するのは大変そうだけど、やってみるのも面白そうだ。