python - density - matplotlib contour plot level
Python: encuentra lĂneas de contorno desde matplotlib.pyplot.contour() (2)
Estoy tratando de encontrar (pero no dibujar) líneas de contorno para algunos datos:
from pprint import pprint
import matplotlib.pyplot
z = [[0.350087, 0.0590954, 0.002165], [0.144522, 0.885409, 0.378515],
[0.027956, 0.777996, 0.602663], [0.138367, 0.182499, 0.460879],
[0.357434, 0.297271, 0.587715]]
cn = matplotlib.pyplot.contour(z)
Sé que cn
contiene las líneas de contorno que quiero, pero parece que no puedo llegar a ellas. He intentado varias cosas:
print dir(cn)
pprint(cn.collections[0])
print dir(cn.collections[0])
pprint(cn.collections[0].figure)
print dir(cn.collections[0].figure)
en vano. Sé que cn
es un ContourSet
, y cn.collections
es una matriz de LineCollection
s. LineCollection
que un LineCollection
es una matriz de segmentos de línea, pero no puedo averiguar cómo extraer esos segmentos.
Mi objetivo final es crear un archivo KML que traza datos en un mapa del mundo y los contornos de esos datos también.
Sin embargo, dado que algunos de mis puntos de datos están muy juntos, y otros están muy lejos, necesito los polígonos reales (cadenas lineales) que forman los contornos, no solo una imagen rasterizada de los contornos.
Estoy algo sorprendido qhull
no hace algo como esto.
Utilizar ListContourPlot
de ListContourPlot
y luego exportar como SVG funciona, pero quiero usar algo de código abierto.
No puedo usar el conocido algoritmo CONREC porque mis datos no están en una malla (no siempre hay múltiples valores y para un valor x dado, y viceversa).
La solución no tiene que ser python, pero tiene que ser de código abierto y ejecutable en Linux.
Parece que los datos de contorno están en el atributo QuadContourSet
objeto QuadContourSet
devuelto por la función plt.contour()
.
El atributo .allseg
es una lista de todos los niveles (que se puede especificar al llamar a plt.contour(X,Y,Z,V)
. Para cada nivel, se obtiene una lista de arrays numpy nx2.
plt.figure()
plt.contour(X, Y, Z, [0], colors=''r'')
plt.figure()
for ii, seg in enumerate(C.allsegs[0]):
plt.plot(seg[:,0], seg[:,1], ''.-'', label=ii)
plt.legend(fontsize=9, loc=''best'')
En el ejemplo anterior, solo se da un nivel, por lo que len(C.allsegs)
= 1. Usted obtiene:
dibujo de contorno
las curvas extraídas
Puede recuperar los vértices haciendo un bucle sobre colecciones y rutas y utilizando el método iter_segments()
de matplotlib.path.Path
.
Aquí hay una función que devuelve los vértices como un conjunto de listas anidadas de líneas de contorno, secciones de contorno y matrices de vértices x, y:
import numpy as np
def get_contour_verts(cn):
contours = []
# for each contour line
for cc in cn.collections:
paths = []
# for each separate section of the contour line
for pp in cc.get_paths():
xy = []
# for each segment of that section
for vv in pp.iter_segments():
xy.append(vv[0])
paths.append(np.vstack(xy))
contours.append(paths)
return contours
Editar:
También es posible calcular los contornos sin trazar nada utilizando el módulo matplotlib._cntr
C no matplotlib._cntr
:
from matplotlib import pyplot as plt
from matplotlib import _cntr as cntr
z = np.array([[0.350087, 0.0590954, 0.002165],
[0.144522, 0.885409, 0.378515],
[0.027956, 0.777996, 0.602663],
[0.138367, 0.182499, 0.460879],
[0.357434, 0.297271, 0.587715]])
x, y = np.mgrid[:z.shape[0], :z.shape[1]]
c = cntr.Cntr(x, y, z)
# trace a contour at z == 0.5
res = c.trace(0.5)
# result is a list of arrays of vertices and path codes
# (see docs for matplotlib.path.Path)
nseg = len(res) // 2
segments, codes = res[:nseg], res[nseg:]
fig, ax = plt.subplots(1, 1)
img = ax.imshow(z.T, origin=''lower'')
plt.colorbar(img)
ax.hold(True)
p = plt.Polygon(segments[0], fill=False, color=''w'')
ax.add_artist(p)
plt.show()