Search code examples
pythonmatplotlibplotaspect-ratio

How to make a squared plot when using 2 different y-axis in matplotlib?


I would like to know how can I make a squared plot using matplotlib when I have 2 y-axis. Here is an example:

    import matplotlib.pyplot as plt
    import seaborn as sns

    gammas = sns.load_dataset("gammas")
    sns.set(context="paper", palette="colorblind", style="ticks")
    fig, ax1 = plot.subplots()
    sns.tsplot(gammas[(gammas["ROI"] == "IPS")].reset_index(), time="timepoint", unit="subject", value="BOLD signal", ci=95, color="#4477AA", legend=False, ax=ax1)
    ax1.set_xlabel("Timepoint")
    ax1.set_ylabel("BOLD signal (1)")
    ax1.spines["top"].set_visible(False)
    ax1.tick_params(top='off')
    ax2 = ax1.twinx()
    ax2.yaxis.tick_right()
    ax2.yaxis.set_label_position("right")
    sns.tsplot(gammas[(gammas["ROI"] == "AG")].reset_index(), time="timepoint", unit="subject", value="BOLD signal", ci=95, color="#CC6677", legend=False, ax=ax2)
    ax2.set_ylabel("BOLD signal (2)")
    ax2.spines["top"].set_visible(False)
    ax2.tick_params(top='off')
    # Set legend #
    ax2.legend([ax1.get_lines()[0], ax2.get_lines()[0]], ["IPS", "AG"], loc='upper left')
    plt.show()

As you can see, the resulting plot is not squared: enter image description here

So far, I have tried the following before the plt.show() command:

  • ax1.set_aspect(1. / ax1.get_data_ratio())
  • ax1.set_aspect(1. / ax1.get_data_ratio()) and ax2.set_aspect(1. / ax2.get_data_ratio())
  • scaling the data values used in ax2 so they adjust in magnitude to the values in ax1
  • fig.set_size_inches(fig.get_size_inches()[0], fig.get_size_inches()[0]) to force the image to be squared, but I have measured the x and y axis with a ruler and their size is different (by a slight difference)

The data I am using has 2 different scales: the 1st y-axis ranges from 0 to 250 while the 2nd one ranges from 0 to 100 (this is why I thought about multiplying all values used in ax2 by a factor of 2.5). I am sure there is something obvious that I am not seeing, so thank you in advance.


Solution

  • It's not entirely clear whether you want your axes to be of equal length, or whether you want the scaling on your axes to be equal.

    To get a square aspect ratio, I created a figure with a square dimension fig = plt.figure(figsize=(5,5)) this is enough to get axes that are the same length.

    enter image description here

    To get the same scaling on all axes, I added the set_scaling() instructions

    enter image description here

    import matplotlib.pyplot as plt
    import seaborn as sns
    
    gammas = sns.load_dataset("gammas")
    sns.set(context="paper", palette="colorblind", style="ticks")
    fig = plt.figure(figsize=(5,5))
    ax1 = fig.add_subplot(111)
    sns.tsplot(gammas[(gammas["ROI"] == "IPS")].reset_index(), time="timepoint", unit="subject", value="BOLD signal", ci=95, color="#4477AA", legend=False, ax=ax1)
    ax1.set_xlabel("Timepoint")
    ax1.set_ylabel("BOLD signal (1)")
    ax1.spines["top"].set_visible(False)
    ax1.tick_params(top='off')
    ax2 = ax1.twinx()
    ax2.yaxis.tick_right()
    ax2.yaxis.set_label_position("right")
    sns.tsplot(gammas[(gammas["ROI"] == "AG")].reset_index(), time="timepoint", unit="subject", value="BOLD signal", ci=95, color="#CC6677", legend=False, ax=ax2)
    ax2.set_ylabel("BOLD signal (2)")
    ax2.spines["top"].set_visible(False)
    ax2.tick_params(top='off')
    # Set legend #
    ax2.legend([ax1.get_lines()[0], ax2.get_lines()[0]], ["IPS", "AG"], loc='upper left')
    # set the aspect ratio so that the scaling is the same on all the axes
    ax1.set_aspect('equal')
    ax2.set_aspect('equal')