matplotlib - extracting data from contour lines
Asked Answered
P

4

62

I would like to get data from a single contour of evenly spaced 2D data (an image-like data).

Based on the example found in a similar question: How can I get the (x,y) values of the line that is ploted by a contour plot (matplotlib)?

>>> 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])
>>> cs.collections[0].get_paths()

The result of this call into cs.collections[0].get_paths() is:

[Path([[ 4.          1.625     ]
 [ 3.25        2.        ]
 [ 3.          2.16666667]
 [ 2.16666667  3.        ]
 [ 2.          3.25      ]
 [ 1.625       4.        ]], None)]

Based on the plots, this result makes sense and appears to be collection of (y,x) pairs for the contour line.

Other than manually looping over this return value, extracting the coordinates and assembling arrays for the line, are there better ways to get data back from a matplotlib.path object? Are there pitfalls to be aware of when extracting data from a matplotlib.path?

Alternatively, are there alternatives within matplotlib or better yet numpy/scipy to do a similar thing? Ideal thing would be to get a high resolution vector of (x,y) pairs describing the line, which could be used for further analysis, as in general my datasets are not a small or simple as the example above.

Purgatory answered 14/4, 2011 at 15:59 Comment(0)
P
64

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]
Paraphernalia answered 14/4, 2011 at 16:31 Comment(2)
This is really useful, thanks! Do you know of any way of obtaining/interpolating equally spaced points on the contour curve? (the points returned in this way are not equally spaced)Dunton
The recommended way right now is to use cs.allsegs. It's a list that contains one element for each level. Each element is itself a list containing the polygons corresponding to that level. So if polys0 = cs.allsegs[0] is the list of polygons for the first level, x00, y00 = polys0[0].T are the x and y coordinates that will construct the first polygon for that level.Scrouge
E
10

from: http://matplotlib.org/api/path_api.html#module-matplotlib.path

Users of Path objects should not access the vertices and codes arrays directly. Instead, they should use iter_segments() to get the vertex/code pairs. This is important, since many Path objects, as an optimization, do not store a codes at all, but have a default one provided for them by iter_segments().

Otherwise, I'm not really sure what your question is. [Zip] is a sometimes useful built in function when working with coordinates. 1

Edna answered 4/2, 2013 at 5:17 Comment(0)
G
7

The vertices of an all paths can be returned as a numpy array of float64 simply via:

cs.allsegs[i][j]  # for element j, in level i

where cs is defined as in the original question as:

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])

More detailed:

Going through the collections and extracting the paths and vertices is not the most straight forward or fastest thing to do. The returned Contour object actually has attributes for the segments via cs.allsegs, which returns a nested list of shape [level][element][vertex_coord]:

num_levels = len(cs.allsegs)
num_element = len(cs.allsegs[0])  # in level 0
num_vertices = len(cs.allsegs[0][0])  # of element 0, in level 0
num_coord = len(cs.allsegs[0][0][0])  # of vertex 0, in element 0, in level 0

See reference: https://matplotlib.org/stable/api/contour_api.html

Gessner answered 26/8, 2019 at 10:8 Comment(0)
P
4

I am facing a similar problem, and stumbled over this matplotlib list discussion.

Basically, it is possible to strip away the plotting and call the underlying functions directly, not super convenient, but possible. The solution is also not pixel precise, as there is probably some interpolation going on in the underlying code.

import matplotlib.pyplot as plt
import matplotlib._cntr as cntr
import scipy as sp

data = sp.zeros((6,6))
data[2:4,2:4] = 1

plt.imshow(data,interpolation='none')
level=0.5
X,Y = sp.meshgrid(sp.arange(data.shape[0]),sp.arange(data.shape[1]))
c = cntr.Cntr(X, Y, data.T)
nlist = c.trace(level, level, 0)
segs = nlist[:len(nlist)//2]
for seg in segs:
    plt.plot(seg[:,0],seg[:,1],color='white')

plt.show()
Pertinent answered 20/5, 2015 at 13:11 Comment(1)
Note that matplotlib._cntr is no longer part of matplotlib. #48350230Diencephalon

© 2022 - 2024 — McMap. All rights reserved.