Search code examples
pythonmatplotlibscaleaxislimits

Python, Matplotlib: Normalising multiple plots to fit the same arbitrary axis limits


I am trying to create a figure containing several plots, and normalising them all so that their features can all be easily distinguished. I'm having some trouble wording what it is I'm trying to do, but the example code below should help clarify.

Example plot generated by the below code.

The code creates a figure with three lines on it. The black line's data varies between -1000 and 1000, so the scale is adjusted accordingly. This means that the variation in the green data, and even moreso in the red, is hard to see. I would ideally like to enlarge the green and red data so that their variation is clearer - but hopefully without just multiplying by a constant value.

Example of the result I'm hoping for - the different lines all have different orders of magnitude but have been fit to an arbitrary y-axis scale so their shape can be demonstrated.

import numpy as np
import matplotlib.pyplot as plt

time_arr = np.arange(0, 25, 1)

dist_arr = np.zeros(len(time_arr))
speed_arr = np.zeros(len(time_arr))
energy_arr = np.zeros(len(time_arr))

for i in range(len(time_arr)):
    dist_arr[i] = np.random.randint(-10, 10)
    speed_arr[i] = np.random.randint(-50, 50)
    energy_arr[i] = np.random.randint(-1000, 1000)


fig = plt.figure(figsize=(13,13))

plt.plot(time_arr, dist_arr, 'red', linestyle='-', label='dist (m)', linewidth=5)
plt.plot(time_arr, speed_arr, 'lime', linestyle='-', label='speed (m/s)', linewidth=5)
plt.plot(time_arr, energy_arr, 'black', linestyle='--', label='E_tot (J)', linewidth=5)

plt.xlabel('Time (s)', fontsize=25)
plt.ylabel('Various Params', fontsize=25)
plt.tick_params(axis='x', labelsize=20)
plt.tick_params(axis='y', labelsize=20)
plt.title('Various VS Time', fontsize = 32, y=1.008)
plt.legend(loc='best', fontsize=25)

plt.show()

I've tried playing around with things like plt.ylim([0, 100]) for each individual line plot, but that didn't seem to work out. Any assistance here would be fantastic, cheers.

EDIT:

Problem solved in the comments thanks to ImportanceOfBeingErnest's improved normalisation technique, combined with the accepted answer. Thanks!


Solution

  • you have the option of normalizing the data and also have multiple twin y axes. See below.

    import numpy as np
    import matplotlib.pyplot as plt
    
    def norm(data):
        return (data)/(max(data)-min(data))
    
    time_arr = np.arange(0, 25, 1)
    
    dist_arr = np.zeros(len(time_arr))
    speed_arr = np.zeros(len(time_arr))
    energy_arr = np.zeros(len(time_arr))
    
    for i in range(len(time_arr)):
        dist_arr[i] = np.random.randint(-10, 10)
        speed_arr[i] = np.random.randint(-50, 50)
        energy_arr[i] = np.random.randint(-1000, 1000)
    
    # option 1
    fig = plt.figure(figsize=(10,10))
    
    plt.plot(time_arr, norm(dist_arr), 'red', linestyle='-', label='dist (m)', linewidth=5)
    plt.plot(time_arr, norm(speed_arr), 'lime', linestyle='-', label='speed (m/s)', linewidth=5)
    plt.plot(time_arr, norm(energy_arr), 'black', linestyle='--', label='E_tot (J)', linewidth=5)
    
    plt.xlabel('Time (s)', fontsize=25)
    plt.ylabel('Various Params', fontsize=25)
    plt.tick_params(axis='x', labelsize=20)
    plt.tick_params(axis='y', labelsize=20)
    plt.title('Various VS Time', fontsize = 32, y=1.008)
    plt.legend(loc='best', fontsize=25)
    
    plt.show()
    
    # option 2
    fig, ax1 = plt.subplots(1,1,figsize=(12,9))
    
    ax1.plot(time_arr, dist_arr, 'red', linestyle='-', label='dist (m)', linewidth=5)
    ax1.set_xlabel('Time (s)', fontsize=25)
    # Make the y-axis label, ticks and tick labels match the line color.
    ax1.set_ylabel('Distance(m)', color='r',fontsize=25)
    ax1.tick_params('y', colors='r', labelsize=20)
    
    ax2 = ax1.twinx()
    ax2.plot(time_arr, speed_arr, 'lime', linestyle='-', label='speed (m/s)', linewidth=5)
    ax2.set_ylabel('Speed (m/s)', color='lime',fontsize=25)
    ax2.tick_params('y', colors='lime', labelsize=20)
    
    ax3 = ax1.twinx()
    ax3.spines['right'].set_position(('axes', 1.25)) # move the right axis light bit to the right by 25 % of the axes
    ax3.plot(time_arr, norm(energy_arr), 'black', linestyle='--', label='E_tot (J)', linewidth=5)
    ax3.set_ylabel('E_tot (J)', color='black',fontsize=25)
    ax3.tick_params('y', colors='black', labelsize=20)
    
    
    fig.tight_layout()
    plt.show()
    

    results in

    enter image description here enter image description here