Home > Enterprise >  Map individual lines across two y axes in Matplotlib
Map individual lines across two y axes in Matplotlib

Time:03-14

I am trying to show cause and effect by displaying lines that are connected by two separate y axes.


eps_surprise = [56.15, 80.41, 218.48, 5.67, 2.99, 5.67]
stock_movement = [-5.72, 14.52, 18.78, -6.77, 6.03, -6.77]

fig, ax = plt.subplots()

y = list(zip(eps_surprise,stock_movement))
for i in y:
    ax.plot((0,1),i)
plt.show()

enter image description here

As you can see in the above chart, I want to include a second y axis that would have a range limited to the min and max of stock_movement. However, my attempts in doing so do not result in the desired chart, as what is plotted is then based on the second axis alone, and not the first.

import matplotlib.pyplot as plt

eps_surprise = [56.15, 80.41, 218.48, 5.67, 2.99, 5.67]
stock_movement = [-5.72, 14.52, 18.78, -6.77, 6.03, -6.77]

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

ax1.set_ylim(min(eps_surprise),max(eps_surprise))
ax2.set_ylim(min(stock_movement),max(stock_movement))

y = list(zip(eps_surprise,stock_movement))
for i in y:
    plt.plot((0,1),i)
plt.show()

enter image description here

The desired output would look something like as follows: enter image description here

CodePudding user response:

Not sure if this is what you are asking exactly, but given your question it's the best I can picture.

import matplotlib.pyplot as plt

eps_surprise = [56.15, 80.41, 218.48, 5.67, 2.99, 5.67]
stock_movement = [-5.72, 14.52, 18.78, -6.77, 6.03, -6.77]

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

ax1.set_ylim(min(eps_surprise),max(eps_surprise))
ax2.set_ylim(min(stock_movement),max(stock_movement))

# plot eps surprise and stock movement on the two separate axes
l1 = ax1.plot(eps_surprise, 'k--', label = 'eps surprise')
l2 = ax2.plot(stock_movement, 'r', label = 'stock movement')

# create legend for both axes
lns = [l1[0], l2[0]]
labs = [l.get_label() for l in lns]
ax2.legend(lns, labs)

ax1.set_ylabel('eps surprise')
ax2.set_ylabel('stock movement')

plot

Honestly, though, if I were trying to show a relationship between two variables, I'd be more tempted to make a scatter plot or a regression.

from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
regressor.fit([[i,] for i in eps_surprise],
              [[i,] for i in stock_movement])
x = [[min(eps_surprise)], [max(eps_surprise)]]

plt.plot(eps_surprise, stock_movement, '.')
plt.plot(x, regressor.predict(x))
plt.grid()
plt.xlabel('eps surprise')
plt.ylabel('stock movement')

other plot

UPDATE

Based on the update in your question of how you want your original figure to look, here's how I would do it. The second set of axes are purely for labelling and instead I scaled the stock movement values to fit on your ax1 scale.

import matplotlib.pyplot as plt

def map_to_eps(stock_movement, eps_surprise):
    ybounds = [min(stock_movement), max(stock_movement)]
    dy = ybounds[1] - ybounds[0]
    xbounds = [min(eps_surprise), max(eps_surprise)]
    dx = xbounds[1] - xbounds[0]
    return([(y - ybounds[0]) / dy * dx   xbounds[0] for y in stock_movement], dy, dx)

eps_surprise = [56.15, 80.41, 218.48, 5.67, 2.99, 5.67]
stock_movement = [-5.72, 14.52, 18.78, -6.77, 6.03, -6.77]
(stock_eps, dstock, deps) = map_to_eps(stock_movement, eps_surprise)

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

ax1.set_ylim(min(eps_surprise) - 0.05 * deps,max(eps_surprise)   0.05 * deps)
ax2.set_ylim(min(stock_movement) - 0.05 * dstock ,max(stock_movement)   0.05 * dstock)

y = list(zip(eps_surprise,stock_eps))
for i in y:
    ax1.plot((0,1),i)
plt.xticks([])
ax1.set_ylabel('eps surprise')
ax2.set_ylabel('stock movement')

edited plot

  • Related