Contenidos

A veces quieres mostrar el resultado de una celda, pero no mostrar el código. Por ejemplo, con el código de un gráfico o de una tabla.

Hay 2 maneras de cumplir con este objetivo:

  1. Simple: creando función auxiliar y ejecutándola.
  2. No tan simple: usando un poco de magia de jupyter notebook.

Opción Simple:

Podemos definir la función en un archivo, y luego importarla y ejecutarla:

from mi_carpeta import mi_script
mi_script.mi_grafico(order=6)

Si es necesario, es posible mostrar el contenido del script o la función:

!cat mi_carpeta/mi_script.py
# Tomado y adaptado desde:
# https://matplotlib.org/stable/gallery/lines_bars_and_markers/fill.html

import numpy as np
import matplotlib.pyplot as plt

def koch_snowflake(order, scale=10):
    """
    Return two lists x, y of point coordinates of the Koch snowflake.

    Parameters
    ----------
    order : int
        The recursion depth.
    scale : float
        The extent of the snowflake (edge length of the base triangle).
    """
    def _koch_snowflake_complex(order):
        if order == 0:
            # initial triangle
            angles = np.array([0, 120, 240]) + 90
            return scale / np.sqrt(3) * np.exp(np.deg2rad(angles) * 1j)
        else:
            ZR = 0.5 - 0.5j * np.sqrt(3) / 3

            p1 = _koch_snowflake_complex(order - 1)  # start points
            p2 = np.roll(p1, shift=-1)  # end points
            dp = p2 - p1  # connection vectors

            new_points = np.empty(len(p1) * 4, dtype=np.complex128)
            new_points[::4] = p1
            new_points[1::4] = p1 + dp / 3
            new_points[2::4] = p1 + dp * ZR
            new_points[3::4] = p1 + dp / 3 * 2
            return new_points

    points = _koch_snowflake_complex(order)
    x, y = points.real, points.imag
    return x, y

def mi_grafico(order=5, figsize=(12,8)):
    """
    Plots the koch snowflake.
    
    Parameters
    ----------
    order : int
        The recursion depth.
    scale : float
        The extent of the snowflake (edge length of the base triangle).
    """
    x, y = koch_snowflake(order=order)
    plt.figure(figsize=figsize)
    plt.axis('equal')
    plt.fill(x, y)
    plt.show()

Opción 2:

Usando una función específica para ocultar código, creada con una mezcla de html y javascript.

def toggle_cell_code(button_id):
    """
    Adds a button to toggle (show/hide) the code cell but not the output. 
    
    Parameters
    ----------
    button_id : str
        An identifier for cells that will hide/show when button is pressed.
    """
    
    from IPython.display import display_html
    my_html = '''
    <button type="button" id="%s" onclick="code_toggle('%s')">Codigo</button>
    <script>
    function code_toggle(my_id) {
        // get the parent element for the cell code and output
        var p = $("#"+my_id);
        if (p.length==0) return;
        while (!p.hasClass("cell")) {
            p = p.parent();
            if (p.prop("tagName") =="body") return;
        }
    // get the cell code and toggle its value
    var cell_code = p.find(".input");
    cell_code.toggle();
    }
    </script>
    ''' %(button_id, button_id)
    return display_html(my_html, raw=True)

Para usar esta funcionalidad, basta con incluirla en la celda del código (antes o después), para agregar el botón que permitirá ocultar y mostrar el código.

toggle_cell_code("un_string_unico_y_reconocible")

# Código para graficar
import numpy as np
import matplotlib.pyplot as plt

# Fixing random state for reproducibility
np.random.seed(19680801)


N = 50
x = np.random.rand(N)
y = np.random.rand(N)
colors = np.random.rand(N)
area = (30 * np.random.rand(N))**2  # 0 to 15 point radii

plt.scatter(x, y, s=area, c=colors, alpha=0.5)
plt.show()

Así se ve:

¿Cómo funciona?

Analicemos el código en python que ejecuta la función toggle_cell_code:

Primero:

def toggle_cell_code(button_id):

La función toma un único argumento: button_id.

Segundo:

from IPython.display import display_html

Tercero:

my_html = '''
    Mucho código en html acá que analizaremos después...
    ''' %(button_id, button_id)

Este código define la variable my_html, en la cual se inserta 2 veces el valor button_id.

Cuarto:

return display_html(my_html, raw=True)

Finalmente, se regresa como el display del html creado mediante una función de IPython.display.

Analicemos ahora el código html:

Primero:

<button type="button" id="%s" onclick="code_toggle('%s')">Codigo</button>

El código anterior define un botón, con un identificador único (button_id) cuyo valor se define de manera dinámica (en el momento de ejecutar la celda del notebook). Al apretar el botón ejecuta la función code_toggle() con el mismo identificador (button_id) como argumento. Eso permite que la función en javascript sepa cuál es la celda en específico que debe ocultar.

Segundo:

<script>
    function code_toggle(my_id) {
        // get the parent element for the cell code and output
        var p = $("#"+my_id);
        if (p.length==0) return;
        while (!p.hasClass("cell")) {
            p = p.parent();
            if (p.prop("tagName") =="body") return;
        }
    // get the cell code and toggle its value
    var cell_code = p.find(".input");
    cell_code.toggle();
    }
    </script>

Primero que nada, $ (jquery) permite encontrar los elementos en una página web, en este caso, el elemento asociado al identificador entregado (button_id). Luego se itera hasta encontrar el elemento que contiene la celda de código y el output (se identifica porque tiene clase "cell"). Luego se accede a la celda de código (que siempre la clase "input"). Finalmente, toggle muestra/oculta el botón.

Nota:

  • Los identificadores tienen el caracter especial #
  • Las clases tienen el carácter especial .

Todas estas ideas son una compilación y simplificación de códigos de muchas otras personas. Algunos enlaces relevantes son: