Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Plotting a curve with equidistant (arc-length) markers

I would like to plot a graph of some experimental data which is sampled at a relatively high rate, but approximates a smooth curve using markers spaced at equal arc-length intervals as shown in the graph below:

Graph with equal arc-length markers

I know about the markevery argument to plot, but that would bunch up the markers to the right of the plot and probably have quite few markers on the left. The solution should be independent of the scales on the x and y axes. I am open to installing additional modules, but it should be a python+matplotlib solution.

like image 492
chthonicdaemon Avatar asked Jul 01 '13 14:07

chthonicdaemon


2 Answers

I think I have put together a relatively good solution. The only problem is taking the data ratio into account in a way that also uses information about the aspect ratio of the final plot. I've not found a reliable way to do this, although this function will accept a data ratio so you can play until the output looks right:

def spacedmarks(x, y, Nmarks, data_ratio=None):
    import scipy.integrate

    if data_ratio is None:
        data_ratio = plt.gca().get_data_ratio()

    dydx = gradient(y, x[1])
    dxdx = gradient(x, x[1])*data_ratio
    arclength = scipy.integrate.cumtrapz(sqrt(dydx**2 + dxdx**2), x, initial=0)
    marks = linspace(0, max(arclength), Nmarks)
    markx = interp(marks, arclength, x)
    marky = interp(markx, x, y)
    return markx, marky

Example of use (this is suitable for pylab mode in iPython):

x = linspace(0, 10*pi, 1000)
y = sin(x*2) + sin(x+1)

plot(x, y)
markx, marky = spacedmarks(x, y, 80)
plot(markx, marky, 'o', color='blue')

Result:

Sample output showing equally spaced markers

like image 134
2 revs Avatar answered Nov 04 '22 14:11

2 revs


Since matplotlib 1.4, you can use markevery with real numbers to achieve this.

Documentation: http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D.set_markevery

Example:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10*np.pi, 1000)
y = np.sin(x*2) + np.sin(x + 1)

plt.plot(x, y, marker='o', markevery=0.05)

plt.show()
like image 43
crabman Avatar answered Nov 04 '22 15:11

crabman