【问题标题】:matplotlib - control capstyle of line collection/large number of linesmatplotlib - 控制线条集合/大量线条的capstyle
【发布时间】:2021-06-18 00:47:35
【问题描述】:

与我的 previous question 类似,我想控制使用 matplotlib 绘制的线条的 capstyle。但是,我的线条数量非常多,使用线条集合以外的任何东西进行绘制都需要很长时间。是否有任何解决方法可以以通用方式控制线条集合中线条的大写样式(或者,绘制大量Line2D 线条的超快速方法)。例如,我尝试通过以下方式使用 matplotlib rc 设置:

import matplotlib as mpl
mpl.rcParams['lines.solid_capstyle'] = 'round'
mpl.rcParams['lines.solid_joinstyle'] = 'round'

但这似乎没有任何影响。来自collections.py 的文档字符串:

这些类并不像它们的单元素对应物那样灵活(例如,您可能无法选择所有线条样式),但它们对于常见用例(例如大量实线段)来说是快速的)

这解释了为什么我似乎无法控制各种参数,但我仍然想这样做!我查看了 AGG 后端的代码(_backend_agg.cpp:不是我真正理解它),似乎 line_cap 和 line_join 由 gc.capgc.join 控制,其中 gc 来自 @ 987654328@上课。有谁知道如何从 Python 控制它?我在这里问正确的问题吗?也许这是控制这些参数的更简单方法?

非常感谢任何帮助...我迫切希望让这项工作正常进行,所以即使是疯狂的黑客也欢迎!

谢谢,

卡森

【问题讨论】:

    标签: python matplotlib lines


    【解决方案1】:

    由于您在问题中提到您不介意“肮脏”的解决方案,因此一种选择如下。

    特定LineCollection 的“绘图过程”由Collection 类(LineCollection 的基础)中定义的draw 方法处理。此方法通过语句gc = renderer.new_gc() 创建GraphicsContextBase 的实例(在backend_bases.py 中定义)。似乎正是这个对象控制了控制capstyle(属性_capstyle)的属性。因此,可以继承GraphicsContextBase,覆盖_capstyle 属性,并在RendererBase 类中注入一个新的new_gc 方法,以便随后对new_gc 的调用返回自定义实例:

    从@florisvb 的答案中借用示例(假设是 Python3):

    #!/usr/bin/env python
    import types
    
    import numpy as np
    from matplotlib.backend_bases import GraphicsContextBase, RendererBase
    import matplotlib.pyplot as plt
    from matplotlib.collections import LineCollection
    
    class GC(GraphicsContextBase):
        def __init__(self):
            super().__init__()
            self._capstyle = 'round'
    
    def custom_new_gc(self):
        return GC()
    
    RendererBase.new_gc = types.MethodType(custom_new_gc, RendererBase)
    #----------------------------------------------------------------------
    np.random.seed(42)
    
    x = np.random.random(10)
    y = np.random.random(10)
    
    points = np.array([x, y]).T.reshape((-1, 1, 2))
    segments = np.concatenate([points[:-1], points[1:]], axis=1)
    
    fig = plt.figure()
    ax = fig.add_subplot(111)
    
    linewidth = 10
    lc = LineCollection(segments, linewidths=linewidth)
    ax.add_collection(lc)
    
    fig.savefig('fig.png')
    

    这会产生:

    【讨论】:

    • 不错。看起来很漂亮!谢谢@ewcz!
    • @ewcz 这个解决方案非常适合基于 agg 的输出,谢谢!我一直在寻找其他后端的源代码,因为我也想在 pdf 中获得这种效果。我似乎无法让“ps”或“pdf”后端接受您编写的补丁,但有趣的是“svg”后端可以处理它。关于如何调整此补丁以输出 pdf 的任何想法?
    • 谢谢你的回答,但是导出到pdf不起作用,我用这个答案使它工作stackoverflow.com/questions/49983192/…
    【解决方案2】:

    更新来自@ewcz 的答案,因为该主题仍然出现在搜索结果中。
    现在可以使用path_effects 而不是定义自己的GraphicsContextBase。

    例如

    import numpy as np
    import matplotlib.patheffects as path_effects
    from matplotlib.collections import LineCollection
    
    np.random.seed(42)
    
    x = np.random.random(10)
    y = np.random.random(10)
    
    points = np.array([x, y]).T.reshape((-1, 1, 2))
    segments = np.concatenate([points[:-1], points[1:]], axis=1)
    
    fig = plt.figure()
    ax = fig.add_subplot(111)
    
    linewidth = 10
    
    ### Stroke redraws the segment passing kwargs down to the GC renderer
    lc = LineCollection(segments, linewidths=linewidth, 
        path_effects=[path_effects.Stroke(capstyle="round")])
    
    ax.add_collection(lc)
    
    fig.show()
    

    Example png output with smooth lines 它似乎也适用于 pdf 输出

    【讨论】:

      【解决方案3】:

      我在同样的问题上苦苦挣扎。我最终在我的线条集合上绘制了一个散点图。它并不完美,但它可能适用于您的应用程序。有一些微妙之处 - 下面是一个工作示例。

      import numpy as np
      import matplotlib.pyplot as plt
      from matplotlib.collections import LineCollection
      
      x = np.random.random(10)
      y = np.random.random(10)
      z = np.arange(0,10)
      
      points = np.array([x, y]).T.reshape(-1, 1, 2)
      segments = np.concatenate([points[:-1], points[1:]], axis=1)
      
      fig = plt.figure()
      ax = fig.add_subplot(111)
      
      linewidth = 10
      cmap = plt.get_cmap('jet')
      norm = plt.Normalize(np.min(z), np.max(z))
      color = cmap(norm(z))
      
      lc = LineCollection(segments, linewidths=linewidth, cmap=cmap, norm=norm)
      lc.set_array(z)
      lc.set_zorder(z.tolist())
      ax.add_collection(lc)
      
      ax.scatter(x,y,color=color,s=linewidth**2,edgecolor='none', zorder=(z+2).tolist())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-10
        • 1970-01-01
        • 2017-01-02
        • 1970-01-01
        相关资源
        最近更新 更多