2

I need to use for loops to print multiple pie charts within a function, preferably horizontally. My assumption was, if I use a for loop to print pie charts, all the charts will be produced but the results would be shown vertically. However, only the very last figure is shown.

import matplotlib.pyplot as plt
for i in range(3):
    labels = ['part_1','part_2','part_3']
    pie_portions = [5,6,7]
    plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.title(f'figure_no : {i+1}')

1 Answers1

1

You are encountering the REPL paradigm built into Jupyter notebooks for the last referenced object. The print (and/or display in the case of notebooks) in 'Read-evaluate-print loop'(REPL) by default usually only applies in Jupyter to the last thing in the output. This is related to why if you have a defined variable, you can just invoke it as the last line of the cell and its value will be shown without you needing print(my_variable).

In regards to your assumption. To have them all show vertical, try:

import matplotlib.pyplot as plt
for i in range(3):
    labels = ['part_1','part_2','part_3']
    pie_portions = [5,6,7]
    plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.title(f'figure_no : {i+1}')
    plt.show()

All the code blocks here were developed and will work right in your browser with no installations necessary via mybinder-served sessions launched via here where the environment is determined by this list of pip installed packages. Many of those listed are not used in these particular examples.


Horizontal solution based on ipywidgets HBox

Much of this initial example is based on adapting my answer here, where the OP wanted the plots on separate widget tabs that could then be selected to view in turn.

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import ipywidgets as widgets
from ipywidgets import HBox
out1 = widgets.Output()
out2 = widgets.Output()
out3 = widgets.Output()
out = HBox(children = [out1, out2, out3])

data1 = pd.DataFrame(np.random.normal(size = 50))
data2 = pd.DataFrame(np.random.normal(size = 100))
data3 = pd.DataFrame(np.random.normal(size = 104))

display(out)

with out1:
    fig1, axes1 = plt.subplots()
    data1.hist(ax = axes1)
    plt.title("test 1")
    plt.show(fig1)

with out2:
    fig2, axes2 = plt.subplots()
    data2.hist(ax = axes2)
    plt.title("test 2")
    plt.show(fig2)

with out3:
    fig3, axes3 = plt.subplots()
    data3.hist(ax = axes3)
    plt.title("test 3")
    plt.show(fig3)

That will show three histograms side by side using HBox. It also uses subplots which are used more in the approach below and could do the job without use of widgets. (I wanted to include widgets as an option it shows how the 'tab' display code I already had the framework for can easily be adapted to HBox code, and I can imagine how having options could come in handy depending if you are making dashboards with widgets involved as well.)
However, OP wanted pie plots. This is a solution closer to what I did with the pie plot here; however, I found it to be glitchy and require a workaround:

import matplotlib.pyplot as plt
import ipywidgets as widgets
from ipywidgets import HBox
out1 = widgets.Output()
out2 = widgets.Output()
out3 = widgets.Output()
out4 = widgets.Output()
out = HBox(children = [out1, out2, out3, out4])

display(out)

with out1:
    labels = ['part_1','part_2','part_3']
    pie_portions = [5,6,7]
    my_plot = plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.title('figure_no :1')

with out2:
    labels = ['part_1','part_2','part_3']
    pie_portions = [5,6,7]
    my_plot = plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.show(my_plot)
    plt.title('figure_no :2')

with out3:
    labels = ['part_1','part_2','part_3']
    pie_portions = [5,6,7]
    my_plot3 = plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.show(my_plot3)
    plt.title('figure_no :3')

# Easiest way to get three to display was to put a dummy one that doesn't display because of `plt.close()`,
# based on https://www.adamsmith.haus/python/answers/how-to-hide-a-figure-from-being-shown-in-matplotlib-in-python
# Otherwise, it messes up third one. This way it only messes up the one it doesn't show.
# I'm not quite sure what causes this apparent glitch, but this is a nice workaround for now. 
with out4:
    my_plot4 = plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.show(my_plot4)
    plt.title(' ')
    plt.close()

Not quite sure why that apparent glitch comes up from that code where adding a fourth dummy pie plot allows all three pie plots to at least show. However, that is not ideal and I found if you combine subplots (see below) with the widgets output, then it works to show the three pie plots side by side without needing that workaround. Cleaner version without the workaround by combining subplots with the widgets output:

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import ipywidgets as widgets
from ipywidgets import HBox
out1 = widgets.Output()
out2 = widgets.Output()
out3 = widgets.Output()
out = HBox(children = [out1, out2, out3])

with out1:
    fig1, axes1 = plt.subplots()
    labels = ['part_1','part_2','part_3']
    pie_portions = [5,6,7]
    my_plot = plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.title('figure_no :1')
    plt.show(my_plot)

with out2:
    fig2, axes2 = plt.subplots()
    my_plot2 = plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.title('figure_no :2')
    plt.show(my_plot2)

with out3:
    fig3, axes3 = plt.subplots()
    my_plot3 = plt.pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    plt.title('figure_no :3')
    plt.show(my_plot3)

display(out)

Horizontal solution based on Matplotlib's subplots

The documentation has an example using subplots to display multiple pie plots. This was adapted from that to do three side-by-side:

#Using subplots based on https://matplotlib.org/stable/gallery/pie_and_polar_charts/pie_demo2.html
import matplotlib.pyplot as plt

# Some data
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
fracs = [15, 30, 45, 10]

# Make figure and axes
fig, axs = plt.subplots(1, 3)

# A standard pie plot
axs[0].pie(fracs, labels=labels, autopct='%1.1f%%', shadow=True)

# Shift the second slice using explode
axs[1].pie(fracs, labels=labels, autopct='%.0f%%', shadow=True,
              explode=(0, 0.1, 0, 0))

# Another standard pie plot
axs[2].pie(fracs, labels=labels, autopct='%1.2f%%', shadow=False);

That one generalized to a for loop like yours:

import matplotlib.pyplot as plt
# Make figure and axes
fig, axs = plt.subplots(1, 3)

for i in range(3):
    labels = ['part_1','part_2','part_3']
    pie_portions = [5,6,7]
    axs[i].pie(pie_portions,labels=labels,autopct = '%1.1f%%')
    axs[i].title.set_text(f'figure_no : {i+1}') #title for subplots based on https://stackoverflow.com/a/39133654/8508004

Note that these solutions were done with active 'in memory' forms of the plots/plot objects. You could also save the plots as image files and display the resulting images side-by-side in a notebook cell using HTML combined with <img align ..> tags, based on based on here,here, and here, or HTML combined with tables, based on here. (That approach to displaying images side-by-side in a notebook gets recycled here to automate making Jupyter RISE slideshows from a collection of image files.)

Wayne
  • 6,607
  • 8
  • 36
  • 93