0

I have a Jupyter notebook with %matplotlib widget as the first line. The notebook contains several markdown cells providing a header structure and some explaining texts.

Also there I am generating some plots from pandas.DataFrames, which are grouped using dynamically generated sections.

Extracted (not executable in this way), it looks like:

%matplotlib widget

import pandas
from IPython.display import display_markdown

dictionary: dict[str, pandas.DataFrame] = {
    "DataFrame 1": pandas.util.testing.makeDataFrame(),
    "DataFrame 2": pandas.util.testing.makeDataFrame(),
}
group: str
dataframe: pandas.DataFrame
for group, dataframe in dictionary.items():
    display_markdown("## %s" % (group), raw=True)
    dataframe.plot()

However, when running the notebook, it first shows me all the created sub-sections and then, after the last one, all the plots.

How can I bring them in the intended order?

For the case that this is relevant: I am using the Jupyter extension of Visual Studio Code.

Minimal exeutable/ runnable example: https://colab.research.google.com/drive/1iTefKtR93MuzStgpNB3zIxx9S0pAhAO8#scrollTo=yRqBQywrCr7T

Torsten Knodt
  • 477
  • 1
  • 5
  • 20
  • 1
    If you provided a minimal reproducible example, I could provide some more specific guidance. My main idea would be to use `%%capture` on the first line to suppress all output from the main cell and then collect all the items you want as the code is run and then in another cell, use `display(df)` and `display(HTML()` to code control exactly how the output is shown. – Wayne Aug 11 '22 at 18:00
  • @Wayne Done. BTW, I tried this myself based on your hint, but didn't get it done. Seems I misunderstand "%%capture". – Torsten Knodt Aug 11 '22 at 18:12
  • What is 'Done'? You solved your issue? As for what I meant by using `%%capture`. Putting `%%capture` alone as first line of a cell should suppress displaying the output as the rest of that cell runs. You can use what you collected as the cell ran to make a report in the other cell. That's the idea. – Wayne Aug 11 '22 at 18:40
  • @Wayne The example is added and no, I was not able to solve it. I guess I have to play more around with capture. Seems this works for all but plots somehow. – Torsten Knodt Aug 11 '22 at 19:06
  • What you posted is not a minimal reproducible example. It doesn't run in a fresh environment. For example, go [here](https://github.com/fomightez/3Dscatter_plot-binder) and click `launch binder` to bring up a session. Try your code there after running `%pip install ipympl`. – Wayne Aug 11 '22 at 19:36
  • Even if I edit your code to fix making the dictionary, I don't see plots unless I remove `%matplotlib widget`. – Wayne Aug 11 '22 at 19:43

1 Answers1

1

You are seeing the plots last because of the way matplotlib and Jupyter interact. Modern Jupyter puts the plots generated in a cell as a separate entity. To interweave them with markdown produced in the course of looping as the code runs procedurally you can suppress the output using %%capture in that cell, collect the plots, and arrange to show them how you want in another cell using display for both.

Demonstration:
You can the code the follows in sessions launched from here after running %pip install ipympl in a cell first:

Top cell

%%capture
import pandas
from IPython.display import display_markdown

dictionary = {
    "DataFrame 1": pandas.util.testing.makeDataFrame(),
    "DataFrame 2": pandas.util.testing.makeDataFrame(),
}
group: str
dataframe: pandas.DataFrame
title_n_plots =[]
for group, dataframe in dictionary.items():
    #display_markdown("## %s" % (group), raw=True)
    title_n_plots.append([group,dataframe.plot()])

That should display nothing.

Next cell

# Display how they should be associated
for x in title_n_plots:
    display_markdown("## %s" % (x[0]), raw=True)
    display(x[1].figure)

Option(s) for still using a single cell and code more like originally posted by adding text as a plot title instead of separate markdown

Of course, an option using the original code layout along the lines of your posted MRE and not suppressing anything could be achieved by adding real titles in the plots that would have stayed associated with the appropriate plot. Like so:

import pandas
from IPython.display import display_markdown

dictionary = {
    "DataFrame 1": pandas.util.testing.makeDataFrame(),
    "DataFrame 2": pandas.util.testing.makeDataFrame(),
}
group: str
dataframe: pandas.DataFrame
title_n_plots =[]
for group, dataframe in dictionary.items():
    #ax = dataframe.plot(title = r"$\bf{" + group + "}$") 
    ax = dataframe.plot(title = r"$\bf{" + group[:-1] + "\ "+ group[-1:] + "}$") 
    #bold in title based on https://stackoverflow.com/a/44123579/8508004
    #hack to fix space showing up before number in `group` based on https://stackoverflow.com/a/34703257/8508004
    ax.title.set_size(40) # based on https://stackoverflow.com/a/67154403/8508004

Or, if you don't want the title centered, you can make it more like the 'display_markdown' example like so:

import pandas
from IPython.display import display_markdown

dictionary = {
    "DataFrame 1": pandas.util.testing.makeDataFrame(),
    "DataFrame 2": pandas.util.testing.makeDataFrame(),
}
group: str
dataframe: pandas.DataFrame
title_n_plots =[]
for group, dataframe in dictionary.items():
    #ax = dataframe.plot(title = r"$\bf{" + group + "}$") 
    ax = dataframe.plot(title = r"$\bf{" + group[:-1] + "\ "+ group[-1:] + "}$") 
    #bold in title based on https://stackoverflow.com/a/44123579/8508004
    #hack to fix space showing up before number in `group` based on https://stackoverflow.com/a/34703257/8508004
    ax.title.set_size(27) # based on https://stackoverflow.com/a/67154403/8508004
    ax.title.set_horizontalalignment("right") # based on https://stackoverflow.com/a/67154403/8508004 and 
    # https://stackoverflow.com/a/44411195/8508004 and that it shows on left-aligned when "right" supplied & vice versa
Wayne
  • 6,607
  • 8
  • 36
  • 93