<jupyter_start><jupyter_text>Matplotlib: Plotting Subplots in a loop**17th June 2020**[See accompanying blog post](https://engineeringfordatascience.com/posts/matplotlib_subplots/) on Engineering for Data Science<jupyter_code># install requirements if necessary
# ! pip install matplotlib pandas ffn nb_black
%load_ext nb_black<jupyter_output><empty_output><jupyter_text>Problem StatementYou can't directly loop through the subplots if both `nrows` and `ncols` are greater than 1. This is because you are returned a list of lists, rather than a list of subplot objects.<jupyter_code>import matplotlib.pyplot as plt

%matplotlib inline
# create subplots
fig, axs = plt.subplots(nrows=3, ncols=2)
print(axs.shape)
axs<jupyter_output>(3, 2)<jupyter_text>How can you loop through subplots?  Load Example DataIn this notebook we will use daily stock data for 5 stocks collected using a finance library called **[ffn](https://pypi.org/project/ffn/)** and then demonstrate methods for plotting the daily time series of each stock on a different subplot<jupyter_code># library to get stock data
import ffn

# load daily stock prices for selected stocks from ffn
tickers = ["aapl", "msft", "tsla", "nvda", "intc"]
prices = ffn.get(tickers, start="2017-01-01")
# convert data into a 'long' table for this plotting exercise
df = prices.melt(ignore_index=False, var_name="ticker", value_name="closing_price")
df.head()<jupyter_output><empty_output><jupyter_text>Method 1: `axs.ravel()`<jupyter_code># define subplot grid
fig, axs = plt.subplots(nrows=3, ncols=2, figsize=(15, 12))
plt.subplots_adjust(hspace=0.5)
fig.suptitle("Daily closing prices", fontsize=18, y=0.95)

# loop through tickers and axes
for ticker, ax in zip(tickers, axs.ravel()):
    # filter df for ticker and plot on specified axes
    df[df["ticker"] == ticker].plot(ax=ax)

    # chart formatting
    ax.set_title(ticker.upper())
    ax.get_legend().remove()
    ax.set_xlabel("")

plt.show()<jupyter_output><empty_output><jupyter_text>Method 2: `plt.subplot`<jupyter_code>plt.figure(figsize=(15, 12))
plt.subplots_adjust(hspace=0.5)
plt.suptitle("Daily closing prices", fontsize=18, y=0.95)

# loop through the length of tickers and keep track of index
for n, ticker in enumerate(tickers):
    # add a new subplot iteratively
    ax = plt.subplot(3, 2, n + 1)

    # filter df and plot ticker on the new subplot axis
    df[df["ticker"] == ticker].plot(ax=ax)

    # chart formatting
    ax.set_title(ticker.upper())
    ax.get_legend().remove()
    ax.set_xlabel("")<jupyter_output><empty_output><jupyter_text>**Pros of Method 2**- do not get an empty plot at the end if the number of charts is an odd number- simple syntax**Cons of Method 2**- still need to predefine the subplot grid (i.e. number of rows and columns) which means you need to know the length of the tickers in advance  - this normally should not be a problem as you are unlikely to be plotting a generator instead of a list  - you could calculate the subplot grid dynamically  Dynamic Grid SizingThe snippet below can be used to dynamically calculate the number of rows in a grid<jupyter_code># find minimium required rows given we want 2 columns
ncols = 2
nrows = len(tickers) // ncols + (len(tickers) % ncols > 0)
nrows
plt.figure(figsize=(15, 12))
plt.subplots_adjust(hspace=0.2)
plt.suptitle("Daily closing prices", fontsize=18, y=0.95)

# set number of columns (use 3 to demonstrate the change)
ncols = 3
# calculate number of rows
nrows = len(tickers) // ncols + (len(tickers) % ncols > 0)

# loop through the length of tickers and keep track of index
for n, ticker in enumerate(tickers):
    # add a new subplot iteratively using nrows and cols
    ax = plt.subplot(nrows, ncols, n + 1)

    # filter df and plot ticker on the new subplot axis
    df[df["ticker"] == ticker].plot(ax=ax)

    # chart formatting
    ax.set_title(ticker.upper())
    ax.get_legend().remove()
    ax.set_xlabel("")<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup