pythonpandasmatplotlibyticks

How to make primary and secondary y-axes ticks look parallel and change the color of the border of the figure?


I am trying make a plot with subplot for a list of pandas dataframes. Each dataframe-plot consists of a primary y-axis and a secondary y-axis. The limiting values are different for both axes. I want to keep the y-ticks on both axes such that they will look parallel to each other i.e. the starting tick 0 will be at the same location on both primary and secondary axes. Also, I want to change the color of left and right spine for each plot, but using ax.spines['left'].set_color('red') is not working. The Plot Outline still remains black.

dataframes = [df1, df2, df3]
fig, axs = plt.subplots(1, 3, figsize=(16,4))
axs = axs.flatten()

primary_ticks_locations = []  # to store the locations of primary y-axis ticks
secondary_ticks_locations = []  # to store the locations of secondary y-axis ticks

for idx, (df, ax) in enumerate(zip(dataframes, axs)):

    # Plot Data-A on Primary y-axis 
    primary_plot = ax.errorbar(df.index, df[df.columns[0]], 
                           yerr=df[df.columns[1]], label='A', color='firebrick',capsize =5.5,
                           linewidth=2.0, marker='o', ms=5.0, mew=1.5, elinewidth=1.0, ls='solid')
    
    ax.spines['left'].set_color('red') # Not working 
    ax.spines['right'].set_color('blue') # Not working
    
    ax.set_ylim(-300, 4001) ## Set the y-limit for A-Data
    
    # Collect primary y-axis tick locations
    primary_ticks_locations.append(ax.yaxis.get_ticklocs())
    
    ## Set the x-axis label
    ax.set_xlabel('saturation Level', fontsize=12, fontweight='bold')
    
    ## Create a secondary y-axis to plot AF
    ax1 = ax.twinx()
    
    ## Plot B-data on the secondary y-axis
    af_plot = ax1.errorbar(df.index, df[df.columns[2]], 
                           yerr=df[df.columns[3]], label='B', color= 'royalblue',capsize =5.5,
                           linewidth=2.0, marker='D', ms=5.0, mew=1.5, elinewidth=1.0, ls='dashed')

    ax1.set_ylim(-0.06, 1.01) ## Set the secondary y-axis limit

    # Collect secondary y-axis tick locations
    secondary_ticks_locations.append(ax1.yaxis.get_ticklocs())
    
    # Customize the Primary y-axis ticks
    ax.tick_params(axis='y',which = 'major', colors='firebrick')
    
    # Customize the Secondary y-axis ticks
    ax1.tick_params(axis='y', which = 'major', colors='royalblue')
    
    # Customize the x-ticks
    ax.tick_params(axis='x',which = 'major', colors='black')

plt.show()

primary_ticks_locations shows: [array([-500., 0., 500., 1000., 1500., 2000., 2500., 3000., 3500.,4000., 4500.]) for each iteration.

secondary_ticks_locations shows: [array([-0.2, 0. , 0.2, 0.4, 0.6, 0.8, 1. , 1.2]) for each iteration.

I don't understand how to make the number and location of the ticks on both axes the same and parallel. I also don't understand why ax.spines['left'].set_color('red') is not producing the desired result.


Solution

  • This post explains how to change axis color: https://stackoverflow.com/a/12059429/23322818

    To align the zero values, you could calculate the data range in each iteration of the for loop and manually set both axis to that range. If the two axis have different ranges, you can align the locations of the zero by aligning the relative position of zero along the two axis:

    import numpy as np
    import matplotlib.pyplot as plt
    
    # Create some mock data
    t = np.arange(0.01, 10.0, 0.01)
    data1 = t**2 - 50 + 20
    data2 = np.sin(2 * np.pi * t) + 0.4
    
    fig, ax1 = plt.subplots()
    ax1.set_xlabel('time (s)')
    
    # plot first line
    ax1.plot(t, data1, 'red')
    
    # format first axis
    ax1.set_ylabel('exp', color='red')
    ax1.spines['left'].set_color('red')
    ax1.spines['right'].set_visible(False)
    ax1.spines['left'].set_linewidth(3)
    ax1.set_ylim(-30, 70)
    
    # plot second line
    ax2 = ax1.twinx()
    ax2.plot(t, data2, 'blue')
    
    # format second axis
    ax2.set_ylabel('sin', color='blue')
    ax2.yaxis.tick_right()
    ax2.yaxis.set_label_position("right")
    ax2.spines['right'].set_color('blue')
    ax2.spines['left'].set_visible(False)
    ax2.spines['right'].set_linewidth(4)
    ax2.set_ylim(-.6, 1.4)
    
    plt.show()
    

    Example plot