jupyter
jupyter copied to clipboard
Python code block returning file name for figure file
I've recently switched to emacs-jupyter for handling Python code blocks in org-mode. I want to save a plot to png format, display it as #+RESULTS, as well as export it. However, the block is returning a badly formatted link:
#+BEGIN_SRC jupyter-python :session py :results file link :exports output
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(np.random.randn(10))
ofile = "junk.png"
fig.savefig(ofile)
plt.close()
ofile
#+END_SRC
#+RESULTS:
[[file:: junk.png
]]
Any pointers welcome.
I recommend using the :file /path/to/figure header argument to save figures as png - it's simpler and requires less code. Check out the "Rich kernel output" section of the readme of this repo.
Ie:
#+BEGIN_SRC jupyter-python :session py :file junk.png :exports output
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(np.random.randn(10))
#+END_SRC
#+RESULTS:
: your figure will show up here as the link you specified in the header
I was hoping to be able to define the file path within the source block. For example, one block has pandas read a file from one path to generate a dataframe, produce a plot, and save a png in the same containing folder, with a name consistent with the base name of the input data file. The plot also needs to be closed.
It seems as if the example I showed should work, but something is botched with the generated link to the file. Using the built-in ob-python, with :session py :results file header arguments, it returns the proper link as: [[file:junk.png]] under #+RESULTS.
The :file /path/to/figure feature is super nice, but it isn't always usable in more complicated scenarios. For instance, it doesn't work with the Altair plotting library, which only generates a PNG version of the figure in the process of saving it to disk.
A possible solution, lifted from here, is to set the mimetype of the output to text/org, and generate the appropriate link yourself:
#+begin_src jupyter-python
import altair as alt
import seaborn as sns
from IPython.display import publish_display_data
iris = sns.load_dataset("iris")
chart = alt.Chart(iris).mark_circle().encode(
x="sepal_length",
y="sepal_width",
color="species",
)
fname = "chart.png"
chart.save(fname)
publish_display_data({"text/org": f"[[file:{fname}]]"})
#+end_src
#+RESULTS:
:RESULTS:
[[file:chart.png]]
:END:
See also https://github.com/nnicandro/emacs-jupyter/issues/237.
the Altair plotting library, which only generates a PNG version of the figure in the process of saving it to disk
Oops, just discovered this is not actually true. You can use a PNG renderer to make Altair output an image directly, which then works perfectly fine with :file ...:
#+begin_src jupyter-python :file chart_via_renderer.png
alt.renderers.enable("png")
chart
#+end_src
#+RESULTS:
[[file:chart_via_renderer.png]]
Still, the mimetype workaround still works for more complicated workflows, where you want to generate the filename dynamically in code.