Python: найти контурные линии из matplotlib.pyplot.contour()
Я пытаюсь найти (но не рисовать!) Контурные линии для некоторых данных:
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)
я знаю cn
содержит контурные линии, которые я хочу, но я не могу добраться до них. Я пробовал несколько вещей:
print dir(cn)
pprint(cn.collections[0])
print dir(cn.collections[0])
pprint(cn.collections[0].figure)
print dir(cn.collections[0].figure)
но безрезультатно. я знаю cn
это ContourSet
, а также cn.collections
это массив LineCollection
s. Я бы подумал LineCollection
это массив отрезков, но я не могу понять, как извлечь эти отрезки.
Моя конечная цель - создать файл KML, который будет отображать данные на карте мира, а также контуры для этих данных.
Однако, поскольку некоторые из моих точек данных расположены близко друг к другу, а другие - далеко, мне нужны фактические полигоны (линии линий), которые составляют контуры, а не просто растровое изображение контуров.
Я несколько удивлен qhull
не делает что-то подобное
Используя Mathematica's ListContourPlot
и затем экспорт как SVG работает, но я хочу использовать что-то с открытым исходным кодом.
Я не могу использовать хорошо известный алгоритм CONREC, потому что мои данные не находятся в сетке (не всегда есть несколько значений y для данного значения x, и наоборот).
Решение не обязательно для Python, но должно быть с открытым исходным кодом и работоспособным в Linux.
2 ответа
Вернуть вершины можно, перебирая коллекции и пути и используя iter_segments()
метод matplotlib.path.Path
,
Вот функция, которая возвращает вершины в виде набора вложенных списков контурных линий, контурных секций и массивов вершин 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
Редактировать:
Также возможно вычислить контуры, не создавая ничего, используя недокументированные matplotlib._cntr
C module:
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()
Я бы предложил использовать scikit-image find_contours
Он возвращает список контуров для заданного уровня.
matplotlib._cntr
был удален из matplotlib начиная с версии 2.2 (см. здесь).
Кажется, что данные контура находятся в .allsegs
атрибут QuadContourSet
объект, возвращаемый plt.contour()
функция.
.allseg
Атрибут - это список всех уровней (которые можно указать при вызове plt.contour(X,Y,Z,V)
, Для каждого уровня вы получаете список numy 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')
В приведенном выше примере дается только один уровень, поэтому len(C.allsegs)
=1. Ты получаешь:
контурный сюжет
извлеченные кривые
Вершины всех путей могут быть возвращены как массив чисел типа float64 просто через:
cn.allsegs[i][j] # for element j, in level i
Более подробный:
Перебирать коллекции и извлекать пути и вершины - не самое простое и не самое быстрое занятие. Возвращенный объект Contour фактически имеет атрибуты для сегментов черезcs.allsegs
, который возвращает вложенный список фигур [level][element][vertex_coord]:
num_levels = len(cn.allsegs)
num_element = len(cn.allsegs[0]) # in level 0
num_vertices = len(cn.allsegs[0][0]) # of element 0, in level 0
num_coord = len(cn.allsegs[0][0][0]) # of vertex 0, in element 0, in level 0
См. Ссылку:https://matplotlib.org/3.1.1/api/contour_api.html