Matplotlib - 控制线集合的端点样式/大量线条

11
与我之前的一个问题相似,我想使用matplotlib控制正在绘制的线段的capstyle。但是,我有大量的线段,使用除了线集合以外的任何东西进行绘制都需要太长时间。是否有任何解决方法以一般方式控制线集合中线段的capstyle(或者,绘制大量Line2D线段的超快速方法)。例如,我尝试使用matplotlib rc设置:
import matplotlib as mpl
mpl.rcParams['lines.solid_capstyle'] = 'round'
mpl.rcParams['lines.solid_joinstyle'] = 'round'

但这似乎没有任何影响。从collections.py的docstring中可以看出:

这些类的灵活性不如它们的单个元素对应物(例如,您可能无法选择所有线条样式),但是它们旨在快速处理常见用例(例如,大量实线段)。

这解释了为什么我似乎无法控制各种参数,但我仍然想要做到!我已经查看了AGG后端的代码(_backend_agg.cpp:虽然我真的不太理解它),并且看起来line_cap和line_join由gc.capgc.join控制,其中gc来自GCAgg类。有人知道如何从Python控制这个吗?我在这里问的问题正确吗?也许有更容易控制这些参数的方法?
非常感谢任何帮助……我迫切希望使它工作,因此即使是疯狂的黑客也受欢迎!
谢谢,
卡森
3个回答

6

更新@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()

平滑线条的示例 png 输出,它似乎也可以与 pdf 输出良好配合使用。


6

既然您在问题中提到您不介意“肮脏”的解决方案,那么一个选项是:

特定LineCollection的“绘制过程”由Collection类(LineCollection的基础)中定义的draw方法处理。该方法通过语句gc = renderer.new_gc()创建GraphicsContextBase的实例(在backend_bases.py中定义)。似乎正是这个对象控制了包括capstyle(属性_capstyle)在内的一些属性。因此,可以子类化GraphicsContextBase,覆盖_capstyle属性,并将一个新的new_gc方法注入到RendererBase类中,以便后续对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! - Carson Farmer
@ewcz 这个解决方案对于基于agg的输出非常有效,谢谢!我一直在查看其他后端的源代码,因为我也想在pdf中获得这种效果。但是我似乎无法让“ps”或“pdf”后端接受您编写的补丁,但有趣的是,“svg”后端可以处理它。您有没有关于如何调整此补丁以实现pdf输出的想法? - aorr
谢谢您的回答,但是导出为pdf不起作用,我使用了这个答案来使其工作 https://stackoverflow.com/questions/49983192/how-to-plot-and-export-multi-colored-line-with-smooth-edges?noredirect=1&lq=1 - BaptisteL

1
我曾经也遇到过同样的问题。最终,我在线条集合上绘制了一个散点图。虽然不完美,但可能适用于您的应用程序。有一些微妙之处 - 下面是一个可行的示例。
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())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接