matplotlib - контролира capstyle на колекция от редове/голям брой редове

Подобно на предишен мой въпрос, бих искал да контролирам capstyle на линиите се чертае с помощта на 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.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 Това решение работи чудесно за agg базиран изход, благодаря! Преглеждах изходния код за други бекендове, защото бих искал да получа този ефект и в 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