3

So I am trying to create a Data Science workflow using org-mode source-blocks. Basically, I need to emulate the jupyter notebook functionality where you create a plot and it shows the figure. Now, following this link, you can do this:

#+begin_src python :results file
import matplotlib, numpy
matplotlib.use('Agg')
import matplotlib.pyplot as plt
fig=plt.figure(figsize=(4,2))
x=numpy.linspace(-15,15)
plt.plot(numpy.sin(x)/x)
fig.tight_layout()
plt.savefig('images/python-matplot-fig.png')
return 'images/python-matplot-fig.png' # return filename to org-mode
#+end_src

The problem with this is that, you have to save the figure manually and then you have to return the path to the figure (Because org is wrapping that code in a function).

That is super awkward to me, because I don't want to be saving the figure manually nor have that return statement at the end, because I want to export these org files to my blog using orig-publish, and that return statement is not valid code outside of the org source blocks code context. I then tried to do this:

#+begin_src python  :file example.png :dir images/ :results file
  import numpy as np
  import matplotlib.pyplot as plt
  import seaborn as sns

  sns.set()  # Enable Seaborn theme

  random_points = np.random.randint(0, 10, (100, 2))

  plt.scatter(random_points[:, 0], random_points[:, 1])
  plt.show()
#+end_src

#+RESULTS:
[[file:images/example.png]]

But the problem is that, it does not save any figure at the images/example.png path. What I would want to do would be that org-mode would save that figure on the path I specified in the header, and then allow me to visualize it in the results.

How could I achieve this? Thank you!

8
  • 1
    To display the image, you need to toggle org-display-inline-images. Have you tried :file images/example.png and skipping the :dir tag altogether? Finally, you may mean :results output. This enables 'scripting mode'
    – Metropolis
    May 24, 2020 at 15:08
  • 1
    Yes I know you have to toggle the display images. The problem is that it does not save the image. Its like a blank png. Regarding the :dir tag, I have tried using it on the current directory so that is not the problem, and I also tried switching the :results mode to output. The issue here is, how can I make org save the matplotlib images automatically.
    – user28912
    May 24, 2020 at 15:14
  • 1
    hm, when I execute your code block, the png file is being created in the images directory (relative to the directory where the org file exists). Dumb question, but, have you created the images directory?
    – Metropolis
    May 24, 2020 at 15:17
  • 1
    Yes yes I did. Which block are you running the first or the second? And does that png contain the image or is it just blank?
    – user28912
    May 24, 2020 at 15:19
  • 1
    Oh, ha! Yeah it is empty. I assumed it was the right image because the scatterplot popped up in a python window. That is strange. I've definitely gotten this kinda thing to work with other tools (mermaid).
    – Metropolis
    May 24, 2020 at 15:29

2 Answers 2

6

I'm a little late but I just figured out a pretty neat way to do this using :file and without using :var.

As the other anwser already pointed out plt.show() doesn't produce output.

However you can use :results output file instead of :results file (which is the same as :results value file). This means that the data to create the image is taken from stdout instead of the value returned directly by python. Because of this we can use plt.savefig(sys.stdout.buffer) to output the image:

#+BEGIN_SRC python :results output file :file example.png :output-dir images/
  import sys
  import matplotlib.pyplot as plt
  import numpy as np

  x = np.linspace(0, 2 * np.pi, 100)
  y = np.sin(x)
  plt.plot(x, y)
  plt.savefig(sys.stdout.buffer)
#+END_SRC

#+RESULTS:
[[file:images/example.png]]
1
  • 1
    This is convenient! But it means the session buffer gets spammed with image data. This noticeably slow when working over ssh w/tramp
    – Will
    Sep 9, 2022 at 22:02
4

The basic problem is that plt.show() does not produce anything (other than showing the image as a side-effect and blocking), so there is nothing to save (it actually produces the None object, so that's what you get in the result):

#+begin_src python :results file
  import numpy as np
  import matplotlib.pyplot as plt

  random_points = np.random.randint(0, 10, (100, 2))

  plt.scatter(random_points[:, 0], random_points[:, 1])
  plt.show()
#+end_src

#+RESULTS:
[[file:None]]

The None that you see in the link is the return value of plt.show().

So you have to save the image:

#+begin_src python :results file :var f="images/example.png"
  import numpy as np
  import matplotlib.pyplot as plt

  random_points = np.random.randint(0, 10, (100, 2))

  plt.scatter(random_points[:, 0], random_points[:, 1])
  plt.savefig(f)
#+end_src

#+RESULTS:
[[file:None]]

That still does not work, because savefig() saves the image but still returns None. So we need to change things to return the file name:

#+begin_src python :results file :var f="images/example.png"
  import numpy as np
  import matplotlib.pyplot as plt

  random_points = np.random.randint(0, 10, (100, 2))

  plt.scatter(random_points[:, 0], random_points[:, 1])
  plt.savefig(f)
  return f
#+end_src

#+RESULTS:
[[file:images/example.png]]

Usign a :var f="..." header allows us to define it in one place and use it in the block. But I don't know how to avoid that and use :file/:dir headers instead.

7
  • 1
    This does not seem to be working for me. It neither creates the figure nor outputs the path to the figure in the result block
    – user28912
    May 24, 2020 at 18:04
  • 1
    You are right: I'm getting something wrong. Let me work on it a bit.
    – NickD
    May 24, 2020 at 18:42
  • 1
    This is still not what you want but it may help a bit.
    – NickD
    May 24, 2020 at 19:17
  • 1
    Thanks for the help! Ok I am starting to understand the problem.. So my problem with doing that is, if I publish that code on a blog, the code is a bit weird right? Maybe I will see if I can use other plotting tool that returns an image. I tried running it without the plt.show(), because the line plt.scatter(random_points[:, 0], random_points[:, 1]) actually returns something but I don't think it works as well
    – user28912
    May 24, 2020 at 20:03
  • 1
    "The code is a bit weird ..." - in what way? I don't understand what you are saying here. Also plt.scatter returns a matplotlib.collections.PathCollection object: I have no idea what you can do with that but it's very unlikely that org-babel will be able to make heads or tails of it.
    – NickD
    May 24, 2020 at 21:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.