python - graficos - matplotlib title position
Matplotlib: trazar numerosos segmentos de lĂnea desconectados con diferentes colores (4)
El plot
función permite dibujar múltiples líneas en una llamada, si sus datos están solo en una lista, simplemente descomprímalo al pasarlo a la plot
:
In [315]: data=[(1, 1), (2, 3), ''r'', #assuming points are (1,2) (1,3) actually and,
#here they are in form of (x1, x2), (y1, y2)
...: (2, 2), (4, 5), ''g'',
...: (5, 5), (6, 7), ''b'',]
In [316]: plot(*data)
Out[316]:
[<matplotlib.lines.Line2D at 0x8752870>,
<matplotlib.lines.Line2D at 0x8752a30>,
<matplotlib.lines.Line2D at 0x8752db0>]
Tengo un conjunto de registros de datos como este:
(s1, t1), (u1, v1), color1
(s2, t2), (u2, v2), color2
.
.
.
(sN, tN), (uN, vN), colorN
En cualquier registro, los primeros dos valores son los puntos finales de un segmento de línea, el tercer valor es el color de ese segmento de línea. Más específicamente, (sn, tn)
son las coordenadas xy del primer punto final, (un, vn)
son las coordenadas xy del segundo punto final. Además, el color es un rgb con valor alfa.
En general, dos segmentos de línea están desconectados (lo que significa que sus puntos finales no coinciden necesariamente).
Cómo trazar estos datos usando matplotlib con una sola llamada de plot
(o la menor cantidad posible) ya que podría haber potencialmente miles de registros.
Intenta
Preparar los datos en una gran lista y llamar a la plot
contra de ella es demasiado lenta. Por ejemplo, el siguiente código no pudo finalizar en un tiempo razonable:
import numpy as np
import matplotlib.pyplot as plt
data = []
for _ in xrange(60000):
data.append((np.random.rand(), np.random.rand()))
data.append((np.random.rand(), np.random.rand()))
data.append(''r'')
print ''now plotting...'' # from now on, takes too long
plt.plot(*data)
print ''done''
#plt.show()
Pude acelerar la representación de la trama utilizando el truco de inserción Ninguno de la siguiente manera:
import numpy as np
import matplotlib.pyplot as plt
from timeit import timeit
N = 60000
_s = np.random.rand(N)
_t = np.random.rand(N)
_u = np.random.rand(N)
_v = np.random.rand(N)
x = []
y = []
for s, t, u, v in zip(_s, _t, _u, _v):
x.append(s)
x.append(u)
x.append(None)
y.append(t)
y.append(v)
y.append(None)
print timeit(lambda:plt.plot(x, y), number=1)
Esto se ejecuta en menos de un segundo en mi máquina. Todavía tengo que descubrir cómo incrustar los valores de color (RGB con canal alfa).
He probado algunos buenos motores de renderizado 2D disponibles en Python 3, mientras busco una solución rápida para una etapa de salida en Deep Learning & GAN orientado a la imagen.
Uso de la siguiente referencia: tiempo para procesar 99 líneas en una imagen fuera de pantalla de 256x256 (o lo que sea más efectivo) con y sin anti-alias.
Los resultados, en orden de eficiencia en mi laptop oldish x301:
- PyGtk2: ~ 2500 FPS, (Python 2, GTK 2, no estoy seguro de cómo obtener AA)
- PyQt5: ~ 1200 FPS, ~ 350 con Antialias
- PyQt4: ~ 1100 FPS, ~ 380 con AA
- El Cairo: ~ 750 FPS, ~ 250 con AA (solo un poco más rápido con ''AA RÁPIDO'')
- PIL: ~ 600 FPS
La línea de base es un ciclo que toma ~ 0.1 ms (10,000 FPS) recuperando números aleatorios y llamando a los primitivos.
Código básico para PyGtk2:
from gtk import gdk
import random
WIDTH = 256
def r255(): return int(256.0*random.random())
cmap = gdk.Colormap(gdk.visual_get_best_with_depth(24), True)
black = cmap.alloc_color(''black'')
white = cmap.alloc_color(''white'')
pixmap = gdk.Pixmap(None, WIDTH, WIDTH, 24)
pixmap.set_colormap(cmap)
gc = pixmap.new_gc(black, line_width=2)
pixmap.draw_rectangle(gc, True, -1, -1, WIDTH+2, WIDTH+2);
gc.set_foreground(white)
for n in range(99):
pixmap.draw_line(gc, r255(), r255(), r255(), r255())
gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, WIDTH, WIDTH
).get_from_drawable(pixmap, cmap, 0,0, 0,0, WIDTH, WIDTH
).save(''Gdk2-lines.png'',''png'')
Y aquí está para PyQt5:
from PyQt5.QtCore import Qt
from PyQt5.QtGui import *
import random
WIDTH = 256.0
def r255(): return WIDTH*random.random()
image = QImage(WIDTH, WIDTH, QImage.Format_RGB16)
painter = QPainter()
image.fill(Qt.black)
painter.begin(image)
painter.setPen(QPen(Qt.white, 2))
#painter.setRenderHint(QPainter.Antialiasing)
for n in range(99):
painter.drawLine(WIDTH*r0to1(),WIDTH*r0to1(),WIDTH*r0to1(),WIDTH*r0to1())
painter.end()
image.save(''Qt5-lines.png'', ''png'')
Y aquí está Python3-Cairo para completar:
import cairo
from random import random as r0to1
WIDTH, HEIGHT = 256, 256
surface = cairo.ImageSurface(cairo.FORMAT_A8, WIDTH, HEIGHT)
ctx = cairo.Context(surface)
ctx.scale(WIDTH, HEIGHT) # Normalizing the canvas
ctx.set_line_width(0.01)
ctx.set_source_rgb(1.0, 1.0, 1.0)
ctx.set_antialias(cairo.ANTIALIAS_NONE)
#ctx.set_antialias(cairo.ANTIALIAS_FAST)
ctx.set_operator(cairo.OPERATOR_CLEAR)
ctx.paint()
ctx.set_operator(cairo.OPERATOR_SOURCE)
for n in range(99):
ctx.move_to(r0to1(), r0to1())
ctx.line_to(r0to1(), r0to1())
ctx.stroke()
surface.write_to_png(''Cairo-lines.png'')
OK, terminé rasterizando las líneas en una imagen PIL antes de convertirla en una matriz numpy:
from PIL import Image
from PIL import ImageDraw
import random as rnd
import numpy as np
import matplotlib.pyplot as plt
N = 60000
s = (500, 500)
im = Image.new(''RGBA'', s, (255,255,255,255))
draw = ImageDraw.Draw(im)
for i in range(N):
x1 = rnd.random() * s[0]
y1 = rnd.random() * s[1]
x2 = rnd.random() * s[0]
y2 = rnd.random() * s[1]
alpha = rnd.random()
color = (int(rnd.random() * 256), int(rnd.random() * 256), int(rnd.random() * 256), int(alpha * 256))
draw.line(((x1,y1),(x2,y2)), fill=color, width=1)
plt.imshow(np.asarray(im),
origin=''lower'')
plt.show()
Esta es, de lejos, la solución más rápida y se adapta perfectamente a mis necesidades en tiempo real. Una advertencia es que las líneas se dibujan sin anti-aliasing.
use LineCollection
:
import numpy as np
import pylab as pl
from matplotlib import collections as mc
lines = [[(0, 1), (1, 1)], [(2, 3), (3, 3)], [(1, 2), (1, 3)]]
c = np.array([(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1)])
lc = mc.LineCollection(lines, colors=c, linewidths=2)
fig, ax = pl.subplots()
ax.add_collection(lc)
ax.autoscale()
ax.margins(0.1)
aquí está el resultado: