Search code examples
pythonmatplotlibscipy-optimize

Creating a modulo/folded plot in Python


I am trying to "fold" an exponential plot (and a fit to it - see the first image below) around a discrete interval on the x-axis (a.k.a a "modulo plot"). The aim is that after 10 x-units the exponential is continued on the same plot from 0 for the 10 to 20 interval, as shown on a second "photoshopped" image below.

The MWE code is below:

import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt

Generate points

x=np.arange(20)
y=np.exp(-x/10)

Fit to data

def fit_func(x, t):
    return np.exp(-x/t)
par, pcov = optimize.curve_fit(f=fit_func, xdata=x, ydata=y)

Plot data and fit function

fig, ax = plt.subplots()
ax.plot(x,y, c='g', label="Data");
ax.plot(x,fit_func(x, par), c='r', linestyle=":", label="Fit");
ax.set_xlabel("x (modulo 10)")
ax.legend()
plt.savefig("fig/mod.png", dpi=300)

What I have: Origianl exponential from 0 to 20

What I want: Modulo/folded exponential in intervals of 10


Solution

  • You could try to simply write:

    ax.plot(x % 10,y, c='g', label="Data")
    ax.plot(x % 10, f, c='r', linestyle=":", label="Fit")
    

    but then you get confusing lines connecting the last point of one section to the first point of the next.

    Another idea is to create a loop to plot every part separately. To avoid multiple legend entries, only the first section sets a legend label.

    import numpy as np
    from scipy import optimize
    import matplotlib.pyplot as plt
    
    x=np.arange(40)
    y=np.exp(-x/10)
    def fit_func(x, t):
        return np.exp(-x/t)
    
    par, pcov = optimize.curve_fit(f=fit_func, xdata=x, ydata=y)
    f = fit_func(x, par)
    fig, ax = plt.subplots()
    
    left = x.min()
    section = 1
    while left < x.max():
        right = left+10
        filter = (x >= left) & (x <= right)
        ax.plot(x[filter]-left,y[filter], c='g', label="Data" if section == 1 else '')
        ax.plot(x[filter]-left, f[filter], c='r', linestyle=":", label="Fit" if section == 1 else '')
        left = right
        section += 1
    
    ax.set_xlabel("x (modulo 10)")
    ax.legend()
    #plt.savefig("fig/mod.png", dpi=300)
    plt.show()
    

    resulting plot