代码之家  ›  专栏  ›  技术社区  ›  anon01

如何快速绘制数千个圆圈?

  •  9
  • anon01  · 技术社区  · 9 年前

    我正在尝试绘制几个(数千个)圆圈对象——我没有太多使用python的经验。我对指定位置、半径和颜色感兴趣。是否有更有效的方法来实现相同的结果?:

    import matplotlib.pyplot as plt
    
    xvals = [0,.1,.2,.3]
    yvals = [0,.1,.2,.3]
    rvals = [0,.1,.1,.1]
    
    c1vals = [0,.1,0..1]
    c2vals = [.1,0,.1,0]
    c3vals = [.1,.1,.1,.1]
    
    for q in range(0,4):
        circle1=plt.Circle((xvals[q], yvals[q]), rvals[q], color=[0,0,0])
        plt.gcf().gca().add_artist(circle1)
    
    4 回复  |  直到 9 年前
        1
  •  15
  •   Joe Kington    9 年前

    这里的关键是使用 Collection 。在你的情况下,你想 PatchCollection .

    Matplotlib通过使用集合优化绘制许多类似的艺术家。这比单独绘制每一个要快得多。此外,该地块不会包含数千名艺术家,只有一个收藏。这加快了每次绘制绘图时需要对每个艺术家进行操作的许多其他杂项操作。

    scatter 实际上是 比您当前的方法更快,因为它将添加一个集合而不是单独的艺术家。然而,它也会绘制大小不在数据坐标中的标记。

    为了解决这个问题,您可以使用相同的方法 分散 但手动创建集合。

    例如:

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.collections
    
    num = 5000
    sizes = 0.2 * np.random.random(num)
    xy = 50 * np.random.random((num, 2))
    
    # Note that the patches won't be added to the axes, instead a collection will
    patches = [plt.Circle(center, size) for center, size in zip(xy, sizes)]
    
    fig, ax = plt.subplots()
    
    coll = matplotlib.collections.PatchCollection(patches, facecolors='black')
    ax.add_collection(coll)
    
    ax.margins(0.01)
    plt.show()
    

    enter image description here

    这对我来说非常平滑。为了证明圆在数据坐标中,请注意如果我们放大一个窄矩形会发生什么(注意:这假设绘图的方面设置为 auto ):

    enter image description here


    如果你真的专注于速度,你可以使用 EllipseCollection 正如@tcaswell所建议的。

    椭圆集合 只会使 路径,但将在绘制时缩放并转换为您指定的位置/大小。

    缺点是,虽然大小可以在数据坐标中,但圆始终是一个圆,即使绘图的纵横比不是1。(即,圆不会像上图中那样拉伸)。

    优点是速度快。

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.collections
    
    num = 5000
    sizes = 0.4 * np.random.random(num)
    xy = 50 * np.random.random((num, 2))
    
    fig, ax = plt.subplots()
    
    coll = matplotlib.collections.EllipseCollection(sizes, sizes,
                                                    np.zeros_like(sizes),
                                                    offsets=xy, units='x',
                                                    transOffset=ax.transData,
                                                    **kwargs)
    ax.add_collection(coll)
    ax.margins(0.01)
    plt.show()
    

    enter image description here

    当我们放大与第二幅图相似的区域时,请注意差异。圆圈变大(大小以数据坐标表示),但保持圆圈而不是变长。它们不是“数据”空间中圆的精确表示。

    enter image description here

    为了给出时间差的一些概念,现在是使用三种方法中的每种方法创建并绘制具有相同5000个圆的图形的时间:

    In [5]: %timeit time_plotting(circles)
    1 loops, best of 3: 3.84 s per loop
    
    In [6]: %timeit time_plotting(patch_collection)
    1 loops, best of 3: 1.37 s per loop
    
    In [7]: %timeit time_plotting(ellipse_collection)
    1 loops, best of 3: 228 ms per loop
    
        2
  •  3
  •   danodonovan    9 年前

    scatter 可能比 plt.Circle 尽管这不会让任何东西跑得更快。

    for i in range(4):
        mp.scatter(xvals[i], yvals[i], s=rvals[i])
    

    如果你能处理相同大小的圆圈,那么 mp.plot(xvals[i], yvals[i], marker='o') 将更有表现力。

    但这可能是 matplotlib 而不是语言限制。有优秀的JavaScript库可以有效地绘制数千个数据点( d3.js ). 也许这里有人会知道你可以从Python调用的一个。

        3
  •  1
  •   Alexander    9 年前

    你肯定想搬家 ...gca() 在你的循环之外。你也可以使用列表理解。

    fig = plt.figure()
    ax = plt.gcf().gca()
    
    [ax.add_artist(plt.Circle((xvals[q],yvals[q]),rvals[q],color=[0,0,0])) 
     for q in xrange(4)]  # range(4) for Python3
    

    以下是使用各种方法生成4000个圆的一些测试:

    xvals = [0,.1,.2,.3] * 1000
    yvals = [0,.1,.2,.3] * 1000
    rvals = [0,.1,.1,.1] * 1000
    
    %%timeit -n5 fig = plt.figure(); ax = plt.gcf().gca()
    for q in range(4000):
        circle1=plt.Circle((xvals[q], yvals[q]), rvals[q], color=[0,0,0])
        plt.gcf().gca().add_artist(circle1)
    5 loops, best of 3: 792 ms per loop
    
    %%timeit -n5 fig = plt.figure(); ax = plt.gcf().gca()
    for q in xrange(4000):
        ax.add_artist(plt.Circle((xvals[q],yvals[q]),rvals[q],color=[0,0,0]))
    5 loops, best of 3: 779 ms per loop
    
    %%timeit -n5 fig = plt.figure(); ax = plt.gcf().gca()
    [ax.add_artist(plt.Circle((xvals[q],yvals[q]),rvals[q],color=[0,0,0])) for q in xrange(4000)]
    5 loops, best of 3: 730 ms per loop
    
        4
  •  1
  •   Mark Setchell    9 年前

    不知道你真正想做什么,或者你的问题或担忧是什么,但这里有一种完全不同的绘制圆的方法 SVG 像这样归档并调用它 circles.svg

    <?xml version="1.0" standalone="no"?>
    <svg width="500" height="300" version="1.1" xmlns="http://www.w3.org/2000/svg">
      <circle cx="100" cy="175" r="200" stroke="lime"   fill="coral"  stroke-width="28"/>
      <circle cx="25"  cy="75"  r="80"  stroke="red"    fill="yellow" stroke-width="5"/>
      <circle cx="400" cy="280" r="20"  stroke="black"  fill="blue"   stroke-width="10"/>
    </svg>
    

    并将其传递给ImageMagick以生成 PNG 文件如下:

    convert circles.svg result.png
    

    enter image description here