9

与我之前的问题类似,我想控制使用 matplotlib 绘制的线条的 capstyle。但是,我的线条数量非常多,使用线条集合以外的任何东西进行绘制都需要很长时间。是否有任何解决方法可以以通用方式控制线条集合中线条的 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.capand控制gc.join,其中 gc 来自GCAgg类。有谁知道如何从 Python 控制它?我在这里问正确的问题吗?也许这是控制这些参数的更简单方法?

非常感谢任何帮助......我迫切希望得到这个工作,所以即使是疯狂的黑客也是受欢迎的!

谢谢,

卡森

4

3 回答 3

5

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

特定的“绘图过程”由类中定义LineCollection的方法(的基础)处理。此方法通过语句创建(defined in ) 的实例。似乎正是这个对象控制了控制(property ) 的属性。因此,可以子类化、覆盖属性并将新方法注入到类中,以便后续调用返回自定义实例:drawCollectionLineCollectionGraphicsContextBasebackend_bases.pygc = renderer.new_gc()capstyle_capstyleGraphicsContextBase_capstylenew_gcRendererBasenew_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')

这会产生: 在此处输入图像描述

于 2017-05-14T09:58:06.693 回答
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()

带有平滑线条的示例 png 输出, 它似乎也适用于 pdf 输出

于 2021-06-17T16:47:35.773 回答
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())
于 2016-08-29T20:03:24.073 回答