.. _code_cells:

Writing Code and Formatting Output

IPyPublish utilises metadata to mark-up the notebook with information on how output should be represented in the converted notebook, as shown in :numref:fig:mpl1.


In [ ]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
plt.plot(np.sin(np.linspace(0, 6)))
plt.show()

.. seealso::

[The PDF representation of this notebook](_static/code_cells.pdf)

:ref:`metadata_tags`, for a full description and list of ipypublish metadata

.. _jupytext_python:

Converting Notebooks to Pure Python

To write code, we can work in the conventional Jupyter Notebook environment, or we can use jupytext, to convert between a notebook and the pure python percent format

$ jupytext --to py:percent notebook.ipynb
$ jupytext --to notebook notebook.py            # overwrite notebook.ipynb
$ jupytext --to notebook --update notebook.py   # update notebook.ipynb

This will produce a standard python file, with commented notebook level metadata commented at the top (in YAML format), and each cell beginning with #%% (known as the percent format):

The percent format can be utilised in IDEs, such as Spyder, Atom, PyCharm, and VS Code, to run individual cells:

{#fig:vscode_py width=60%}

.. important::

To preserve ipypublish notebook metadata, you must add:
`"jupytext": {"metadata_filter": {"notebook": "ipub"}}` to
your notebooks metadata before conversion.

.. seealso::

:ref:`jupytext_rmarkdown`

[Using YAML metadata blocks in Pandoc](https://pandoc.org/MANUAL.html#extension-yaml_metadata_block).

NB Setup Helper Functions

:py:mod:ipypublish.scripts.nb_setup offers a number of useful functions, to setup common packages (matplotlib, pandas, etc) for outputting content in high quality formats.


In [ ]:
from ipypublish import nb_setup

.. note::

`ipypublish.scripts.ipynb_latex_setup` is deprecated in v0.9

Text Output


In [ ]:
print("""
This is some printed text,
with a nicely formatted output.
""")

Images (with PIL)


In [ ]:
import os
from ipypublish.tests import TEST_PIC_PATH

In [ ]:
nb_setup.images_hconcat([TEST_PIC_PATH, TEST_PIC_PATH],
               width=600, gap=10)

In [ ]:
nb_setup.images_vconcat([TEST_PIC_PATH, TEST_PIC_PATH],
               height=400, gap=10)

In [ ]:
nb_setup.images_gridconcat([[_,_] for _ in [TEST_PIC_PATH, TEST_PIC_PATH]],
               height=300, vgap=10,hgap=20)

Plots (with Matplotlib)

A matplotlib figure (+@fig:example_mpl{}), and its code (+@code:example_mpl{}).


In [ ]:
plt = nb_setup.setup_matplotlib(output=('pdf','svg'))
plt.scatter(np.random.rand(10), np.random.rand(10),
            label='data label')
plt.ylabel(r'a y label with latex $\alpha$')
plt.legend();

.. note::

If outputting the Matplotlib figures in a PDF format.
See [usetex tutorial](https://matplotlib.org/users/usetex.html#usetex-tutorial), 
and [Stackoverflow question](https://stackoverflow.com/questions/38731201/latex-escaping-in-matplotlib).

Tables (with pandas)

A pandas table (+@tbl:example_pd{}), and its code (+@code:example_pd{}).


In [ ]:
pd = nb_setup.setup_pandas(escape_latex=False)
df = pd.DataFrame(np.random.rand(3,4),columns=['a','b','c','d'])
df.a = ['$\delta$','x','y']
df.b = ['l','m','n']
df.set_index(['a','b'])
df.round(3)

.. note::

If using `escape_latex=False`, then PDF conversion will throw an error 
if there are e.g. `_`'s in your column names. You either need to escape
these manually (`\_`) or use `escape_latex=True`. But note that, 
`escape_latex=True` will also escape math (e.g. `$\delta$`) causing it not
to render.

Equations (with ipython or sympy)

An ipython and sympy equation =[@eqn:example_ipy;@eqn:example_sympy].


In [ ]:
from IPython.display import Latex
Latex('$$ a = b+c $$')

In [ ]:
sym = nb_setup.setup_sympy()
f = sym.Function('f')
y = sym.Function('y')
n = sym.symbols(r'\alpha')
f = y(n)-2*y(n-1/sym.pi)-5*y(n-2)
sym.rsolve(f,y(n),[1,4])

IPywidgets

ipywidgets can be added to the notebook, to create interactive elements. These widgets are preserved in sphinx HTML outputs.


In [ ]:
import ipywidgets
slider1 = ipywidgets.FloatSlider()
slider1.description = "Slide Me"
slider1

Multiple views of the same widget can be created:


In [ ]:
slider1

Using jslink, widgets can also be synced, without the need for an active python kernel:


In [ ]:
slider2 = ipywidgets.BoundedFloatText()
link = ipywidgets.jslink(
    (slider1, 'value'),
    (slider2, 'value'))
slider2

For more complex examples see: jupyter.org/widgets

Object Output Formats

The format of the Jupyter Notebook file allows for the storage of a single output in multiple formats. This is taken advantage of by packages such as matplotlib and pandas, etc to store a figure/table in both latex and html formats, which can then be selected by ipypublish based on the document type required.

Sometimes a user may wish to have greater control over the output format and/or which output types are to be stored. It it possible to achieve this via the Jupyter display function. For example, if we wanted to display a pandas.DataFrame table without the index column, such that it can be output to both a pdf and html document:


In [ ]:
from IPython.display import display
df = pd.DataFrame(np.random.random((3, 3)))
latex = df.to_latex(index=False)
html = df.to_html(index=False)
display({'text/latex': latex,
        'text/html': html}, raw=True)

If you wish to create your own object with multiple output formats, you should create a class with multiple _repr_*_() methods:


In [ ]:
class MyObject(object):
    def __init__(self, text):
        self.text = text

    def _repr_latex_(self):
        return "\\textbf{LaTex: " + self.text + "}"

    def _repr_html_(self):
        return "<b>HTML: " + self.text + "</b>"

MyObject('hallo')

.. seealso::

:ref:`nbformat:notebook_file_format`

[IPython Rich Display](http://ipython.readthedocs.io/en/stable/config/integrating.html#rich-display)

.. _multiple_outputs:

Multiple Outputs from a Single Code Cell

Similarly, with the Jupyter display functionality, you can control the output metadata for multiple outputs in a single code cell:


In [ ]:
from IPython.display import display
from IPython.display import display_latex
from IPython.display import display_markdown

x = np.linspace(0, 3.42)

for i in range(1,3):

    display_markdown(
      '### Code Created Heading {0}'.format(i), raw=True)

    fig, ax = plt.subplots()
    ax.plot(x, np.sin(x*i))
    metadata={'ipub': {
      'figure': {
        'caption': 'Code Created Heading {0}'.format(i)}}}
    display(fig, metadata=metadata)
    plt.close()