48060


以下のサンプルコードにて、5つの座標点からなるボロノイ図を`scipy.spatial.voronoi_plot_2d`を使って生成します。

このグラフ上に`sympy.plotting.plot_implicit`を使って任意の陰関数（双曲線等）を描画したいのですが、やり方が分からず困っております。

適切な方法をご存知でしたら、ご教授願います。

[code]

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.spatial import Voronoi, voronoi_plot_2d
    
    points = np.array([[10,20], [20, 75], [45, 15], [70, 53], [90, 30]])
    weights = np.array([20, 5, 5, 5, 5])
    vor = Voronoi(points)
    
    fig, ax = plt.subplots()
    voronoi_plot_2d(vor, ax = ax)
    
    for i in range(len(points)):
     c = plt.Circle(points[i], weights[i], fc="none", ec='k')
     ax.add_patch(c)
    
    for j, p in enumerate(points):
     plt.text(p[0]-5, p[1]+5, j, ha = 'right')
    
    
    plt.xlim([0, 100])
    plt.ylim([0, 100])
    
    plt.show()
    
[/code]


CC BY-SA 4.0
2018-09-03T10:49:39.847
48053
2018-09-04T23:48:30.823
2018-09-04T23:48:30.823
29802
29802
question
python matplotlib sympy scipy
SymPy と SciPy におけるグラフの統合


[`sympy/plotting/plot.py`](https://github.com/sympy/sympy/blob/master/sympy/plotting/plot.py)
の docstring に書かれているように、ある程度新しい SymPy であれば `Plot._backend` を使うことによってバックエンドの
matplotlib
オブジェクトを触ることができます。マニュアルには書かれていない汚いやり方ですが、私が調べた限りだと現状この方法しか用意されていないように見えました。

一度 `_backend` を得てしまえば、`_backend.fig` や `_backend.ax` を使うことで SymPy
が使ったプロットにアクセスできます。ですから、この Axes に新しくプロットを作れば上から別のプロットを描くことができるようです。

以下、私の環境では動作したサンプルコードです。半径50の円を `plot_implicit()` してみました。

[code]

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.spatial import Voronoi, voronoi_plot_2d
    from sympy import plot_implicit, Eq
    from sympy.abc import x, y
    
    plt.ion()
    
    splt = plot_implicit(Eq(x**2 + y**2, 2500), (x, 0, 100), (y, 0, 100))
    ax = splt._backend.ax
    
    points = np.array([[10,20], [20, 75], [45, 15], [70, 53], [90, 30]])
    vor = Voronoi(points)
    fig = voronoi_plot_2d(vor, ax=ax)
    
    for i in range(len(points)):
        c = plt.Circle(points[i], 10.0, fc="none", ec='k')
        ax.add_patch(c)
    
    for j, p in enumerate(points):
        ax.text(p[0]-5, p[1]+5, j, ha = 'right')
    
    ax.set_xlim([0, 100])
    ax.set_ylim([0, 100])
    
    fig.canvas.draw()
    #fig.canvas.flush_events()  # 必要かも？
    plt.show(block=True)
    
[/code]

いくつか注意点です。

  * `voronoi_plot_2d()` してから `plot_implicit()` する順番でも良さそうなのですが、そうすると `splt._backend.ax.get_child()` から順番にボロノイ図の方の Axes へ `add_patch()` せねばならず、またこのとき Figure が 2 つできてしまうので上手く処理する必要がありそうでした (`plot_implicit(..., show=False)` しても良いのですが、そうすると `_backend` が生成されないため [`_backend = splt.backend(splt)` する](https://github.com/sympy/sympy/blob/b69d4c27f298766f1f9c5f6bb17dcf7ae71bb67b/sympy/plotting/plot.py#L196)必要が出てきます)。このため先に `plot_implicit()` しています。
  * 詳しい理由まで調べていませんが、この方法だと matplotlib をインタラクティブにしないと追加の要素が描画されないようでした。このため `plt.ion()` を実行しており、また `show()` したときに即時終了しないよう `show(block=True)` しています。

参考: [Sympy and plotting](https://stackoverflow.com/q/40747474/5989200) --
Stack Overflow

私の環境: Python 3.6.2 :: Anaconda custom (64-bit), SymPy 1.1.1, matplotlib 2.0.2,
SciPy 0.19.1, NumPy 1.13.1


CC BY-SA 4.0
2018-09-03T16:02:42.567
48060
2018-09-03T16:02:42.567
19110
48053
answer


[「sympyで作成されるグラフについて」に対する回答](https://ja.stackoverflow.com/a/48043/16894)
と同じ手法で双曲線を描いてみました。

[code]

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.spatial import Voronoi, voronoi_plot_2d
    from sympy import *
    
    points = np.array([[10,20], [20, 75], [45, 15], [70, 53], [90, 30]])
    weights = np.array([5, 5.1, 5.2, 4.9, 4.8])
    vor = Voronoi(points)
    
    fig, ax = plt.subplots()
    voronoi_plot_2d(vor, ax = ax)
    
    for i in range(len(points)):
      c = plt.Circle(points[i], weights[i], fc="none", ec='k')
      ax.add_patch(c)
    
    for j, p in enumerate(points):
      plt.text(p[0]-5, p[1]+5, j, ha = 'right')
    
    ## Draw hyperbola curve
    x, y = symbols("x y")
    G = (x-50)**2/16 - (y-50)**2/9 - 10
    r = (0, 100)
    p = plot_implicit(G, (x,)+r, (y,)+r, show=False)[0].get_points()[0]
    px, py = [], []
    for (ix, iy) in p:
      px.extend([ix.start, ix.start, ix.end, ix.end, None])
      py.extend([iy.start, iy.end, iy.end, iy.start, None])
    ax.fill(px, py, facecolor='red')
    
    plt.xlim([0, 100])
    plt.ylim([0, 100])
    ax.set_aspect('equal')
    
    plt.show()
    
[/code]

[![Voronoi and hyperbola
curve](https://i.stack.imgur.com/nMfHD.png)](https://i.stack.imgur.com/nMfHD.png)


CC BY-SA 4.0
2018-09-04T03:40:30.410
48068
2018-09-04T03:40:30.410
48053
answer