2012-07-20 8 views
7

Analogamente a uno previous question, vorrei controllare il capstyle di linee che vengono disegnate usando matplotlib. Tuttavia, ho un numero estremamente grande di linee e il disegno con qualcosa di diverso da una raccolta di linee richiede troppo tempo. Esistono soluzioni alternative per controllare il capstyle delle linee in una raccolta di linee in modo generico (o in alternativa, modi super veloci per disegnare un numero elevato di linee Line2D). Per esempio, ho provato utilizzando le impostazioni matplotlib rc via:matplotlib - capstyle di controllo della linea raccolta/numero elevato di righe

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

Ma questo non sembra avere alcun effetto. Dal docstring per collections.py:

Le classi non sono destinate ad essere flessibile come le loro controparti singolo elemento (ad esempio, non si può essere in grado di selezionare tutti gli stili di linea), ma sono destinate ad essere veloce per i casi di uso comune (ad esempio, un grande insieme di linea continua segemnts)

che spiega il motivo per cui io non riesco a controllare i vari parametri, ma ho ancora voglia di farlo! Ho dato un'occhiata al codice per il backend AGG (_backend_agg.cpp: non che lo capisco davvero), e sembra che line_cap e line_join siano controllati da gc.cap e gc.join, dove gc proviene dalla classe GCAgg. Qualcuno sa come si può controllare questo da Python? Sto ponendo la domanda giusta qui? Forse sono modi più semplici per controllare questi parametri?

Qualsiasi aiuto è molto apprezzato ... sono disperato per farlo funzionare, quindi anche gli hack sono pazzi!

Grazie,

Carson

risposta

3

Poiché nella tua domanda hai detto che non ti dispiace soluzioni "sporche", una delle opzioni sarebbe la seguente.

Il "processo di trafilatura" di un particolare LineCollection è gestita dal metodo draw definito nella classe Collection (la base di LineCollection). Questo metodo crea un'istanza di GraphicsContextBase (definita in backend_bases.py) tramite la dichiarazione gc = renderer.new_gc(). Sembra essere esattamente questo oggetto che governa tra le altre cose le proprietà che controllano lo capstyle (proprietà _capstyle). Pertanto, si potrebbe sottoclasse GraphicsContextBase, sostituire la proprietà _capstyle, e iniettare un nuovo metodo new_gc nella classe RendererBase cosicché conseguenti chiamate al new_gc restituire l'istanza di misura:

Mutuando l'esempio dalla risposta di @florisvb (supponendo 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') 

Questo produce: enter image description here

+0

Nizza. Sembra stupendo! Grazie a @ewcz! –

+0

@ewcz Questa soluzione è perfetta per l'output basato sugli agg, grazie!Ho cercato attraverso la fonte per altri backend perché mi piacerebbe ottenere questo effetto anche in un pdf. Non riesco a ottenere il backend 'ps' o 'pdf' per accettare la patch che hai scritto, ma è interessante che il backend 'svg' sia in grado di gestirlo. Qualche idea su come modificare questa patch per l'output pdf? – aorr

1

ero alle prese con lo stesso problema. Ho finito per tracciare una trama sparsa in cima alla mia collezione di linee. Non è perfetto, ma potrebbe funzionare per la tua applicazione. Ci sono alcune sottigliezze - sotto c'è un esempio funzionante.

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