Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

two (or more) graphs in one plot with different x-axis AND y-axis scales in python

Tags:

I want 3 graphs on one axes object, for example:

#example x- and y-data
x_values1=[1,2,3,4,5]
y_values1=[1,2,3,4,5]

x_values2=[-1000,-800,-600,-400,-200]
y_values2=[10,20,39,40,50]

x_values3=[150,200,250,300,350]
y_values3=[10,20,30,40,50]


#make axes
fig=plt.figure()
ax=fig.add_subplot(111)

now I want to add all three data sets to ax. But they shouldn't share any x- or y-axis (since then because of the diffenrent scales one would be way smaller thant the other. I need something like ax.twinx(), ax.twiny(), but both the x- and y-axis need to be independent.

I want to do this, because I want to put the two attached plots (and a third one, that is similar to the second one) in one plot ("put them on top of each other"). Plot1 Plot2

I then would put the x/y-labels (and/or ticks, limits) of the second plot on the right/top and the x/y-limits of another plot in the bottom/left. I dont need x/y-labels of the 3. plot.

How do I do this?

like image 684
Anton Rodenhauser Avatar asked Mar 11 '17 10:03

Anton Rodenhauser


2 Answers

The idea would be to create three subplots at the same position. In order to make sure, they will be recognized as different plots, their properties need to differ - and the easiest way to achieve this is simply to provide a different label, ax=fig.add_subplot(111, label="1").

The rest is simply adjusting all the axes parameters, such that the resulting plot looks appealing. It's a little bit of work to set all the parameters, but the following should do what you need.

enter image description here

import matplotlib.pyplot as plt

x_values1=[1,2,3,4,5]
y_values1=[1,2,2,4,1]

x_values2=[-1000,-800,-600,-400,-200]
y_values2=[10,20,39,40,50]

x_values3=[150,200,250,300,350]
y_values3=[10,20,30,40,50]


fig=plt.figure()
ax=fig.add_subplot(111, label="1")
ax2=fig.add_subplot(111, label="2", frame_on=False)
ax3=fig.add_subplot(111, label="3", frame_on=False)

ax.plot(x_values1, y_values1, color="C0")
ax.set_xlabel("x label 1", color="C0")
ax.set_ylabel("y label 1", color="C0")
ax.tick_params(axis='x', colors="C0")
ax.tick_params(axis='y', colors="C0")

ax2.scatter(x_values2, y_values2, color="C1")
ax2.xaxis.tick_top()
ax2.yaxis.tick_right()
ax2.set_xlabel('x label 2', color="C1") 
ax2.set_ylabel('y label 2', color="C1")       
ax2.xaxis.set_label_position('top') 
ax2.yaxis.set_label_position('right') 
ax2.tick_params(axis='x', colors="C1")
ax2.tick_params(axis='y', colors="C1")

ax3.plot(x_values3, y_values3, color="C3")
ax3.set_xticks([])
ax3.set_yticks([])

plt.show()
like image 110
ImportanceOfBeingErnest Avatar answered Oct 11 '22 03:10

ImportanceOfBeingErnest


You could also standardize the data so it shares the same limits and then plot the limits of the desired second scale "manually". This function standardizes the data to the limits of the first set of points:

def standardize(data):
    for a in range(2):
        span = max(data[0][a]) - min(data[0][a])
        min_ = min(data[0][a])
        for idx in range(len(data)):
            standardize = (max(data[idx][a]) - min(data[idx][a]))/span
            data[idx][a] = [i/standardize + min_ - min([i/standardize 
                            for i in data[idx][a]]) for i in data[idx][a]]
    return data

Then, plotting the data is easy:

import matplotlib.pyplot as plt
data = [[[1,2,3,4,5],[1,2,2,4,1]], [[-1000,-800,-600,-400,-200], [10,20,39,40,50]], [[150,200,250,300,350], [10,20,30,40,50]]]
limits = [(min(data[1][a]), max(data[1][a])) for a in range(2)]

norm_data = standardize(data)

fig, ax = plt.subplots()

for x, y in norm_data:
    ax.plot(x, y)

ax2, ax3 = ax.twinx(), ax.twiny()
ax2.set_ylim(limits[1])
ax3.set_xlim(limits[0])

plt.show()

Since all data points have the limits of the first set of points, we can just plot them on the same axis. Then, using the limits of the desired second x and y axis we can set the limits for these two.

Rsulting plot

like image 41
Tristan Avatar answered Oct 11 '22 04:10

Tristan