Package: reproducible-figures
Reproducible FiguresA small Python utility to generate easily reproducible figures for scientific papers.
I often find myself generating figures for papers and then later having to go through needlessly tedious processes to regenerate them when I want to make a small change, so I made this package.
By generating figures using this package a folder is created with the figure,
the data used to generate the figure, and the code used to generate the figure.
I recommend combining this with a version control system like git to track changes for your figures.The reproduction code is built automatically by finding all the imports
needed for create_figure and the functions or classes
used in create_figure. This process is not flawless and can
potentially miss some imports if they are not accessible
through automated inspection. Additionally, if the code reads from
external data sources, these may not be available when
reproducing the figure. However, it should work well for most cases!Installationpipinstallreproducible-figuresExample Usagefromreproducible_figuresimportsave_reproducible_figureimportpandasaspdimportmatplotlib.pyplotaspltimportnumpyasnpdefcreate_figure(data):fig,ax=plt.subplots()ax.plot(data['x'],data['y'])returnfigdata=pd.DataFrame({'x':range(10),'y':np.random.normal(size=10)})save_reproducible_figure('test_save_figure',data,create_figure)Note:Thecreate_figurefunction must take a single argument, which is the data used to generate the figure, and should not callplt.show,plt.close, orplt.savefig.
If you want show the figure at the same time, you can pass theshow=Trueargument to thesave_reproducible_figurefunction.save_reproducible_figure('test_save_figure',data,create_figure,show=True)After callingsave_reproducible_figure, the data, figure and code will be saved to the folderfigures/test_save_figure:figures/test_save_figure
├── data.csv
├── test_save_figure.pdf
└── code.pyThedata.csvfile contains the data used to generate the figure, thefigure.pdffile contains the figure itself, and thecode.pyfile contains the code used to generate the figure.In order to reproduce the figure, you can run:pythonfigures/test_save_figure/code.pyIf you want to modify the figure, you can edit thecode.pyfile and run it again.Figure StyleThe function you use to create the figure can apply any stylisation you want to the figure, but the package provides a functionset_plotting_style.
This function checks if LaTeX is available and uses it if it is.
By default, it uses the Times New Roman font for any textmode text, and Computer Modern for any mathmode text.In order to setup LaTeX in Linux, you can run:>apt-getinstalltexlive-binariestexlivetexlive-latex-extratexlive-fonts-recommendeddvipngcm-super-y
>pipinstalllatexPreparing and Using Figures in LaTeX DocumentsTo make sure that the figure looks good in a publication, you can use theset_plotting_stylefunction with an appropriate figure size and font scale for your document.
I have found that a figure size of(10, 6)with a font scale of2.5works well for a single column figure in a LaTeX document with 11pt Times New Roman font. For example:fromreproducible_figuresimportsave_reproducible_figurefromreproducbile_figures.plottingimportset_plotting_styleimportpandasaspdimportmatplotlib.pyplotaspltimportnumpyasnpdefcreate_figure(data):set_plotting_style(font_size=2.5)plt.figure(figsize=(10,6))...# Create figure code herereturnfigsave_reproducible_figure('my_figure',data,create_figure)In order to embed the figure into your document, I recommend:\includegraphics[height=3.5cm]{figures/my_figure.pdf}The following is a screenshot of an example figure created using the above parameters in a LaTeX document:Note that the text in the figure is the same font and a similar size as the text in the document.Advanced UsageMultiple Data SourcesThesave_reproducible_figurefunction can handle multiple data sources as follows:defcreate_figure(data1,data2):...save_reproducible_figure('test_save_figure',(data1,data2),create_figure)Each dataframe will be saved to a separate csv file, and thecreate_figurefunction will be called with the dataframes as arguments.Complex Figure Generation CodeThesave_reproducible_figurefunction should naturally be able to handle most some quite complicated code structure. For example, suppose you have the following figure generating code:importnumpyasnpdefexternal_fn(x):returnnp.sqrt(x)classHelperClass:def__init__(self,a:int):self.a=adefinternal_fn(self,x):returnx*self.adefpreprocess_data(self,data:pd.DataFrame)->pd.DataFrame:"""Preprocess data."""data['y']=self.internal_fn(data['y'])data['x']=external_fn(data['x'])returndatadefcomplex_create_figure(data:pd.DataFrame):fig,ax=plt.subplots()helper_class=HelperClass(a=10)data=helper_class.preprocess_data(data)ax.plot(data['x'],data['y'])returnfigAdditional ImportsIn general, you should not need to manually specify additional imports.
However, in some cases it is unavoidable, so you can pass them to thesave_reproducible_figurefunction:save_reproducible_figure('test_save_figure',data,create_figure,additional_imports=['import networkx as nx'])This will addimport networkx as nxto thecode.pyfile.
See the tests for an example usingnetworkxto generate a figure.Additional FunctionsIn most cases, this should not be needed as the automatic
source builder should find all the functions needed. However,
if there are any issues or you just want to preserve some code
(e.g. code used to generate the data), the functions provided
can be added here to be put into the source file.If you want to use helper functions in yourcode.pyfile, you can pass them to thesave_reproducible_figurefunction. For example:defpreprocess_data(data:pd.DataFrame)->pd.DataFrame:"""Preprocess data."""data['y']=data['y']*100returndatadefcreate_test_figure_with_helper_fns(data:pd.DataFrame):"""Create a figure."""fig,ax=plt.subplots()data=preprocess_data(data)ax.plot(data['x'],data['y'])save_reproducible_figure('test_fig_preprocessor',data,create_test_figure_with_helper_fns,helper_fns=[preprocess_data])Reproducing Figures in VSCodeIf you use VSCode, you can easily reproduce the figure with thePython: Filerun configuration. Select the gear icon in the "Run and Debug" tab to create a new run configuration.
Then with thecode.pyfile as your active tab, selectPython: Fileas the run configuration and run it. Or you can use the keyboard shortcutF5to run the file.