0
prices = pd.DataFrame(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
                   columns=['a', 'b', 'c'])

I have my prices dataframe, and it currently has 3 columns. But at other times, it could have more or fewer columns. Is there a way to use some sort of twinx() loop to create a line-chart of all the different timeseries with a (potentially) infinite number of y-axes?

I tried the double for loop below but I got typeError'd:bTypeError: 'AxesSubplot' object does not support item assignment

# for i in range(0,len(prices.columns)):
#     for column in list(prices.columns):
#         fig, ax[i] = plt.subplots()
#         ax[i].set_xlabel(prices.index()) 
#         ax[i].set_ylabel(column[i]) 
#         ax[i].plot(prices.Date, prices[column]) 
#         ax[i].tick_params(axis ='y') 
# 
#         ax[i+1] = ax[i].twinx() 
#         ax[i+1].set_ylabel(column[i+1]) 
#         ax[i+1].plot(prices.Date, column[i+1]) 
#         ax[i+1].tick_params(axis ='y') 
# 
#         fig.suptitle('matplotlib.pyplot.twinx() function \ Example\n\n', fontweight ="bold") 
#         plt.show() 
# =============================================================================

I believe I understand why I got the error - the ax object does not allow the assignment of the i variable. I'm hoping there is some ingenious way to accomplish this.

Mr. T
  • 11,960
  • 10
  • 32
  • 54
Tony
  • 221
  • 1
  • 4
  • 11
  • This is a matplotlib problem, not a Python problem, hence my change to your title. The matplotlib documentation provides [examples of parasite axes](https://matplotlib.org/3.1.1/gallery/ticks_and_spines/multiple_yaxis_with_spines.html). I am sure you will be able to adapt this to your problem. – Mr. T Oct 23 '20 at 21:49
  • Thanks Mr. T, I have looked at this over the last month. Would you be able to give me a push in the right direction? – Tony Nov 25 '20 at 16:26
  • friendly and kind bump :) – Tony Dec 01 '20 at 15:17

1 Answers1

2

Turned out, the main problem was that you should not mix pandas plotting function with matplotlib which led to a duplication of the axes. Otherwise, the implementation is rather straight forward adapted from this matplotlib example.

from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
from matplotlib import pyplot as plt
from itertools import cycle
import pandas as pd


#fake data creation with different spread for different axes
#this entire block can be deleted if you import your df
from pandas._testing import rands_array
import numpy as np
fakencol=5
fakenrow=7
np.random.seed(20200916)
df = pd.DataFrame(np.random.randint(1, 10, fakenrow*fakencol).reshape(fakenrow, fakencol), columns=rands_array(2, fakencol))
df = df.multiply(np.power(np.asarray([10]), np.arange(fakencol)))
df.index = pd.date_range("20200916", periods=fakenrow)

#defining a color scheme with unique colors
#if you want to include more than 20 axes, well, what can I say
sc_color = cycle(plt.cm.tab20.colors)

#defining the size of the figure in relation to the number of dataframe columns
#might need adjustment for optimal data presentation
offset = 60
plt.rcParams['figure.figsize'] = 10+df.shape[1], 5

#host figure and first plot
host = host_subplot(111, axes_class=AA.Axes)
h, = host.plot(df.index, df.iloc[:, 0], c=next(sc_color), label=df.columns[0])
host.set_ylabel(df.columns[0])
host.axis["left"].label.set_color(h.get_color())
host.set_xlabel("time")

#plotting the rest of the axes
for i, cols in enumerate(df.columns[1:]):
  
    curr_ax = host.twinx()       

    new_fixed_axis = curr_ax.get_grid_helper().new_fixed_axis
    curr_ax.axis["right"] = new_fixed_axis(loc="right",
                                axes=curr_ax,
                                offset=(offset*i, 0))
    
    curr_p, = curr_ax.plot(df.index, df[cols], c=next(sc_color), label=cols)
    
    curr_ax.axis["right"].label.set_color(curr_p.get_color())
    curr_ax.set_ylabel(cols)
    curr_ax.yaxis.label.set_color(curr_p.get_color())


plt.legend()
plt.tight_layout()
plt.show()

![enter image description here

Coming to think of it - it would probably have been better to distribute the axes equally to the left and the right of the plot. Oh, well.

Mr. T
  • 11,960
  • 10
  • 32
  • 54