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

Подобно моему предыдущему вопросу, я хотел бы контролировать стиль заглавных строк рисуется с помощью matplotlib. Однако у меня чрезвычайно большое количество линий, и рисование чем-либо, кроме набора линий, занимает слишком много времени. Существуют ли какие-либо обходные пути для управления шапкой строк в коллекции строк общим способом (или, альтернативно, сверхбыстрые способы рисования большого количества 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.cap и gc.join, где gc происходит из класса GCAgg. Кто-нибудь знает, как можно управлять этим из Python? Я задаю правильный вопрос здесь? Возможно, это более простые способы управления этими параметрами?

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

Спасибо,

Карсон


person Carson Farmer    schedule 20.07.2012    source источник


Ответы (3)


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

«Процесс рисования» конкретного 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')

Получается: введите здесь описание изображения

person ewcz    schedule 14.05.2017
comment
Хороший. Это выглядит великолепно! Спасибо @ewcz! - person Carson Farmer; 14.05.2017
comment
@ewcz Это решение отлично работает для вывода на основе gg, спасибо! Я просматривал источник для других бэкэндов, потому что я хотел бы получить этот эффект и в формате PDF. Кажется, я не могу заставить серверные части «ps» или «pdf» принять написанный вами патч, но, что интересно, серверная часть «svg» может с этим справиться. Любые мысли о том, как настроить этот патч для вывода в формате PDF? - person aorr; 09.07.2017
comment
Спасибо за ответ, но экспорт в pdf не работает, я использовал этот ответ, чтобы заставить его работать stackoverflow.com/questions/49983192/ - person BaptisteL; 07.12.2020

Чтобы обновить ответ от @ewcz, поскольку эта тема все еще появляется в результатах поиска.
Теперь можно использовать path_effects вместо определения собственной GraphicsContextBase.

e.g.

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

person beez    schedule 17.06.2021

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

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())
person florisvb    schedule 29.08.2016