matplotlib - управление capstyle коллекции строк/большое количество строк

аналогично предыдущий вопрос из моих, я хотел бы контролировать capstyle линий, рисуемых с помощью matplotlib. Тем не менее, у меня очень большое количество строк, и рисование с чем-либо, кроме коллекции строк, занимает слишком много времени. Существуют ли обходные пути для управления capstyle строк в коллекции строк в общем виде (или, альтернативно, супер быстрые способы рисования большого количества Line2D строки). Например, я попытался использовать matplotlib rc настройки через:

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

но это, похоже, не имеет никакого влияния. Из docstring для collections.py:

классы не должны быть такими гибкими, как их одноэлементные аналоги (например, вы не можете выбрать все стили линий), но они должны быть быстрыми для общих случаев использования (например, большой набор сплошных линейных сегментов)

что объясняет почему Я не могу контролировать различные параметры, но я все еще хочу это сделать! Я взглянул на код для бэкэнда AGG (_backend_agg.cpp: не то, чтобы я действительно это понимаю), и похоже, что line_cap и line_join управляются gc.cap и gc.join, где gc происходит от GCAgg класса. Кто-нибудь знает, как можно управлять этим с Python? Я задаю правильный вопрос? Возможно, это более простые способы управления этими параметрами?

любая помощь очень ценится... Я отчаянно хочу, чтобы это сработало, поэтому даже сумасшедшие хаки добро пожаловать!

спасибо,

Карсон

2 ответов


поскольку вы упоминаете в своем вопросе, что не возражаете против "грязных" решений, один из вариантов будет следующим.

"процесс рисования" конкретного LineCollection обрабатывается draw метод, определенный в Collection класс (База LineCollection). Этот метод создает экземпляр GraphicsContextBase (определена в backend_bases.py) по заявлению gc = renderer.new_gc(). Кажется, именно этот объект управляет, среди прочего, свойствами, управляющими 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')

это производит: enter image description here


Я боролся с той же проблемой. Я закончил тем, что построил график разброса поверх моей коллекции линий. Это не идеально, но это может работать для вашего приложения. Есть несколько тонкостей - ниже приведен рабочий пример.

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())