Matplotlib - Extracting Data from Contour Lines

matplotlib - extracting data from contour lines

For a given path, you can get the points like this:

p = cs.collections[0].get_paths()[0]
v = p.vertices
x = v[:,0]
y = v[:,1]

matplotlib in python - how to extracting data from contour lines

You can plot several contours at once with plt.contour by giving a list of the values you wish to contour. Then, you can access them all from the returned ContourSet using cs.allsegs or by using get_paths on each item in the cs.collections list.

For example:

import matplotlib.pyplot as plt
x = [1,2,3,4]
y = [1,2,3,4]
m = [[15,14,13,12],[14,12,10,8],[13,10,7,4],[12,8,4,0]]
cs = plt.contour(x,y,m, [9.5, 10.5, 11.5])
plt.show()

# Option 1: use allsegs
all_coords = cs.allsegs
print(all_coords)

# Option 2: use cs.collections[X].get_paths()
coords1 = cs.collections[0].get_paths()
coords2 = cs.collections[1].get_paths()
coords3 = cs.collections[2].get_paths()
print(coords1)
print(coords2)
print(coords3)

Where the printed coords are then:

Option 1 (allsegs):

[[array([[4.        , 1.625     ],
[3.25 , 2. ],
[3. , 2.16666667],
[2.16666667, 3. ],
[2. , 3.25 ],
[1.625 , 4. ]])],
[array([[4. , 1.375 ],
[3. , 1.83333333],
[2.75 , 2. ],
[2. , 2.75 ],
[1.83333333, 3. ],
[1.375 , 4. ]])],
[array([[4. , 1.125],
[3. , 1.5 ],
[2.25 , 2. ],
[2. , 2.25 ],
[1.5 , 3. ],
[1.125, 4. ]])]]

Option 2 (get_paths()):


[Path(array([[4. , 1.625 ],
[3.25 , 2. ],
[3. , 2.16666667],
[2.16666667, 3. ],
[2. , 3.25 ],
[1.625 , 4. ]]), array([1, 2, 2, 2, 2, 2], dtype=uint8))]
[Path(array([[4. , 1.375 ],
[3. , 1.83333333],
[2.75 , 2. ],
[2. , 2.75 ],
[1.83333333, 3. ],
[1.375 , 4. ]]), array([1, 2, 2, 2, 2, 2], dtype=uint8))]
[Path(array([[4. , 1.125],
[3. , 1.5 ],
[2.25 , 2. ],
[2. , 2.25 ],
[1.5 , 3. ],
[1.125, 4. ]]), array([1, 2, 2, 2, 2, 2], dtype=uint8))]

How can I get the (x,y) values of the line that is plotted by a contour plot?

Look at the collections property of the returned ContourSet. In particular the get_paths() method of the first collection returns paired points making up each line segment.

cs.collections[0].get_paths()

To get a NumPy array of the coordinates, use the Path.vertices attribute.

p1 = cs.collections[0].get_paths()[0]  # grab the 1st path
coor_p1 = p1.vertices

matplotlib - extracting values from contour lines

There's no direct way, but cs.collections is in the exact same order as cs.levels (which is the "z" values you're after).

Therefore, it's easiest to do something like:

lookup = dict(zip(cs.collections, cs.levels))
z = lookup[line_collection_artist]

As a quick interactive example:

import numpy as np
import matplotlib.pyplot as plt

def main():
fig, ax = plt.subplots()
cs = ax.contour(np.random.random((10,10)))

callback = ContourCallback(cs)
plt.setp(cs.collections, picker=5)
fig.canvas.mpl_connect('pick_event', callback)

plt.show()

class ContourCallback(object):
def __init__(self, cs):
self.lookup = dict(zip(cs.collections, cs.levels))

def __call__(self, event):
print self.lookup[event.artist]

main()

Matplotlib contour plot does not extract exact contours

The contours are drawn using a marching squares algorithm to compute contour locations, it interpolates between the grid points.

Maybe you're looking for the discrete region boundaries: these can be retrieved like this:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

data = np.array([[ 1.46184395, 1.46184395, 1.46184395, 1. , 1. ],
[ 1.46184395, 1.46184395, 1.46184395, 1. , 1. ],
[ 1.46184395, 1.46184395, 1.46184395, 1. , 1. ],
[ 1.46184395, 1.46184395, 1.46184395, 1. , 1. ],
[ 1.46184395, 1.46184395, 1.46184395, 4.24552277, 4.24552277],
[ 1.46184395, 1.46184395, 1.46184395, 4.24552277, 4.24552277],
[ 1.46184395, 1.46184395, 1.46184395, 4.24552277, 4.24552277],
[ 1.46184395, 1.46184395, 1.46184395, 4.24552277, 4.24552277],
[ 1.46184395, 1.46184395, 1.46184395, 4.24552277, 4.24552277],
[ 1.46184395, 1.46184395, 1.46184395, 4.24552277, 4.24552277]])

def region_borders(data, value, color, **kwargs):
v = np.argwhere(np.diff((data == value).T, axis=0))
vlines = np.array(list(zip(v + [.5, -.5], v + [.5, .5])))

h = np.argwhere(np.diff((data == value).T, axis=1))
hlines = np.array(list(zip(h + [-.5, .5], h + [.5, .5])))

if len(vlines) and len(hlines):
lines = np.vstack((vlines, hlines))
elif len(vlines):
lines = vlines
else:
lines = hlines
return mpl.collections.LineCollection(lines, colors=color, **kwargs)

contours = np.unique(data)

fig, ax = plt.subplots(ncols=len(contours)+1, sharex=True, sharey=True, layout='constrained')
im = ax[0].matshow(data, cmap='jet', aspect='auto')
fig.colorbar(im, ax=ax[-1])

norm = mpl.colors.Normalize(data.min(), data.max())
for i, value in enumerate(contours, 1):
ax[i].add_collection(region_borders(data, value, mpl.cm.jet(norm(value)), lw=2))
ax[i].set_title(value)

Sample Image

Python: find contour lines from matplotlib.pyplot.contour()

You can get the vertices back by looping over collections and paths and using the iter_segments() method of matplotlib.path.Path.

Here's a function that returns the vertices as a set of nested lists of contour lines, contour sections and arrays of x,y vertices:

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

Edit:

It's also possible to compute the contours without plotting anything using the undocumented 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()

Sample Image

Extracting contours multiple times with Python

If somebody faces the same challenge, I found a suitable solution in one of the answers to this question.

It uses the marching squares method implemented in the scikit-image package. As an example:

import numpy as np
from skimage import measure

# Construct some test data
x, y = np.ogrid[-np.pi:np.pi:100j, -np.pi:np.pi:100j]
r = np.sin(np.exp((np.sin(x)**3 + np.cos(y)**2)))

# Find contours at a constant value of 0.8
contours = measure.find_contours(r, 0.8)

There are some caveats discussed in the comments, but now you have another option to matplotlib.pyplot.contour if needed.

are contour data (X,Y) ordered in a connected clockwise path in matplotlib?

I resolved this problem by the following steps:

  1. make use of the torus structure, i.e., Z[0,:] = Z[N,:], Z[:,0] = Z[:,M], where (N,M) are linear dimensions of the matrix.

  2. find allsegs from contour plot for a given level z0:

    pl = plt.contour(X,Y,Z,levels=[z0])
    segs = pl.allsegs[0]

  3. segs[i] contains the coordinates of a given contour whose two end points either: (1) are the same, then segs[i] is a closed contour in the domain set by X and Y. (2) are different and therefore must terminate at either of the four edges of the domain. In this case, there must exist at least another open contour whose end points pair-up with the current open contour. This pair identification is achieved by calculating their distance on a torus, which is defined as the smallest of |r1-r2|, |r1-r2 +/- period_along_x|, |r1-r2 +/- period_along_y|

  4. ultimately the numerical algorithm boils down to identifying closed contours as well as identifying pairs of matching end points satisfying torus topology.

Three example solutions are shown in the attached figure.

Sample Image

Four open contours, but it is one contour on a torus, where end points are identified as pairs by color

Two closed contours, where two end points of each contour are identical



Related Topics



Leave a reply



Submit