Hackeando el Periodismo con el notebook de IPython

Damián Avila

@damian_avila

Github

Email: info@oquanta.info

http://damianavila.github.io/hackperiodismo

1. "Workflow" en periodismo de datos.

¿Qué hace un periodista de datos?

1- Observa y elige un hecho concreto de la realidad.

2- Obtiene datos a partir del hecho elegido.

3- Procesa los datos obtenidos

4- Visualiza los datos procesados.

5 - Genera un historia a partir de los datos procesados:

  • Propone un modelo para explicar los datos procesados.
  • Genera predicciones a partir del modelo propuesto.

6- Comunica la historia:

  • datos
  • modelo
  • predicciones
  • interpretración

¿Qué elementos contiene una historia?

Una historia contiene:

  • Texto
  • Ecuaciones
  • Imágenes y video
  • Figuras u otras visualizationes

2. El notebook the IPython.

www.ipython.org

¿Qué es el notebook the IPython?

Una herramienta para contar historias.

Técnicamente es una documento basado de JSON:

In [1]:
from IPython.nbformat import current
with open('Hackeando el periodismo.ipynb') as f:
    nb = current.read(f,'json')
In [2]:
nb.worksheets[0].cells[10:20]
Out[2]:
[{u'cell_type': u'heading',
  u'level': 2,
  u'metadata': {u'internals': {u'slide_type': u'subslide'},
   u'slideshow': {u'slide_type': u'slide'}},
  u'source': u'2. El notebook the IPython. '},
 {u'cell_type': u'markdown',
  u'metadata': {u'internals': {}, u'slideshow': {u'slide_type': u'-'}},
  u'source': u'<img src="/files/figs/logo.png" />'},
 {u'cell_type': u'markdown',
  u'metadata': {u'internals': {u'slide_helper': u'subslide_end'},
   u'slide_helper': u'subslide_end',
   u'slideshow': {u'slide_type': u'-'}},
  u'source': u'## www.ipython.org'},
 {u'cell_type': u'heading',
  u'level': 3,
  u'metadata': {u'internals': {u'slide_type': u'subslide'},
   u'slideshow': {u'slide_type': u'subslide'}},
  u'source': u'\xbfQu\xe9 es el  notebook the IPython?'},
 {u'cell_type': u'markdown',
  u'metadata': {u'internals': {u'slide_helper': u'subslide_end'},
   u'slide_helper': u'subslide_end',
   u'slideshow': {u'slide_type': u'-'}},
  u'source': u'Una herramienta para contar **historias**.'},
 {u'cell_type': u'markdown',
  u'metadata': {u'internals': {u'slide_type': u'subslide'},
   u'slideshow': {u'slide_type': u'subslide'}},
  u'source': u'T\xe9cnicamente es una documento basado de JSON:'},
 {u'cell_type': u'code',
  u'collapsed': False,
  u'input': u"from IPython.nbformat import current\nwith open('Hackeando el periodismo.ipynb') as f:\n    nb = current.read(f,'json')",
  u'language': u'python',
  u'metadata': {u'internals': {}, u'slideshow': {u'slide_type': u'-'}},
  u'outputs': [],
  u'prompt_number': 121},
 {u'cell_type': u'code',
  u'collapsed': False,
  u'input': u'nb.worksheets[0].cells[10:20]',
  u'language': u'python',
  u'metadata': {u'internals': {u'slide_helper': u'subslide_end'},
   u'slide_helper': u'subslide_end',
   u'slideshow': {u'slide_type': u'-'}},
  u'outputs': [{u'metadata': {},
    u'output_type': u'pyout',
    u'prompt_number': 122,
    u'text': u'[{u\'cell_type\': u\'heading\',\n  u\'level\': 2,\n  u\'metadata\': {u\'internals\': {u\'slide_type\': u\'subslide\'},\n   u\'slideshow\': {u\'slide_type\': u\'slide\'}},\n  u\'source\': u\'2. El notebook the IPython. \'},\n {u\'cell_type\': u\'markdown\',\n  u\'metadata\': {u\'internals\': {}, u\'slideshow\': {u\'slide_type\': u\'-\'}},\n  u\'source\': u\'<img src="/files/figs/logo.png" />\'},\n {u\'cell_type\': u\'markdown\',\n  u\'metadata\': {u\'internals\': {u\'slide_helper\': u\'subslide_end\'},\n   u\'slide_helper\': u\'subslide_end\',\n   u\'slideshow\': {u\'slide_type\': u\'-\'}},\n  u\'source\': u\'## www.ipython.org\'},\n {u\'cell_type\': u\'heading\',\n  u\'level\': 3,\n  u\'metadata\': {u\'internals\': {u\'slide_type\': u\'subslide\'},\n   u\'slideshow\': {u\'slide_type\': u\'subslide\'}},\n  u\'source\': u\'\\xbfQu\\xe9 es el  notebook the IPython?\'},\n {u\'cell_type\': u\'markdown\',\n  u\'metadata\': {u\'internals\': {u\'slide_helper\': u\'subslide_end\'},\n   u\'slide_helper\': u\'subslide_end\',\n   u\'slideshow\': {u\'slide_type\': u\'-\'}},\n  u\'source\': u\'Una herramienta para contar **historias**.\'},\n {u\'cell_type\': u\'markdown\',\n  u\'metadata\': {u\'internals\': {u\'slide_type\': u\'subslide\'},\n   u\'slideshow\': {u\'slide_type\': u\'subslide\'}},\n  u\'source\': u\'T\\xe9cnicamente es una documento basado de JSON:\'},\n {u\'cell_type\': u\'code\',\n  u\'collapsed\': False,\n  u\'input\': u"from IPython.nbformat import current\\nwith open(\'Hackeando el periodismo.ipynb\') as f:\\n    nb = current.read(f,\'json\')",\n  u\'language\': u\'python\',\n  u\'metadata\': {u\'internals\': {}, u\'slideshow\': {u\'slide_type\': u\'-\'}},\n  u\'outputs\': []},\n {u\'cell_type\': u\'code\',\n  u\'collapsed\': False,\n  u\'input\': u\'nb.worksheets[0].cells[10:20]\',\n  u\'language\': u\'python\',\n  u\'metadata\': {u\'internals\': {u\'slide_helper\': u\'subslide_end\'},\n   u\'slide_helper\': u\'subslide_end\',\n   u\'slideshow\': {u\'slide_type\': u\'-\'}},\n  u\'outputs\': []},\n {u\'cell_type\': u\'markdown\',\n  u\'metadata\': {u\'internals\': {u\'slide_helper\': u\'subslide_end\',\n    u\'slide_type\': u\'subslide\'},\n   u\'slide_helper\': u\'subslide_end\',\n   u\'slideshow\': {u\'slide_type\': u\'subslide\'}},\n  u\'source\': u\'* Genera archivos con la extensi\\xf3n `.ipynb` que se guardan en el directorio local.\\n* En el notebook podemos almacenar:\\n        * C\\xf3digo\\n        * Texto (Markdown)\\n        * Equaciones (LaTeX)\\n        * Im\\xe1genes\\n        * Links a videos\\n        * HTML\\n* Puede ser controlado por versiones.\\n* Puede verse sin tener IPython instalado usando NBViewer: http://nbviewer.ipython.org/.\\n* Puede ser exportado a otros formatos: Slides, HTML, Markdown, reStructured Text, LaTeX y PDF.\'},\n {u\'cell_type\': u\'heading\',\n  u\'level\': 3,\n  u\'metadata\': {u\'internals\': {u\'slide_type\': u\'subslide\'},\n   u\'slideshow\': {u\'slide_type\': u\'subslide\'}},\n  u\'source\': u\'\\xbfCu\\xe1les son sus caracter\\xedsticas principales?\'}]'}],
  u'prompt_number': 122},
 {u'cell_type': u'markdown',
  u'metadata': {u'internals': {u'slide_helper': u'subslide_end',
    u'slide_type': u'subslide'},
   u'slide_helper': u'subslide_end',
   u'slideshow': {u'slide_type': u'subslide'}},
  u'source': u'* Genera archivos con la extensi\xf3n `.ipynb` que se guardan en el directorio local.\n* En el notebook podemos almacenar:\n        * C\xf3digo\n        * Texto (Markdown)\n        * Equaciones (LaTeX)\n        * Im\xe1genes\n        * Links a videos\n        * HTML\n* Puede ser controlado por versiones.\n* Puede verse sin tener IPython instalado usando NBViewer: http://nbviewer.ipython.org/.\n* Puede ser exportado a otros formatos: Slides, HTML, Markdown, reStructured Text, LaTeX y PDF.'},
 {u'cell_type': u'heading',
  u'level': 3,
  u'metadata': {u'internals': {u'slide_type': u'subslide'},
   u'slideshow': {u'slide_type': u'subslide'}},
  u'source': u'\xbfCu\xe1les son sus caracter\xedsticas principales?'}]
  • Genera archivos con la extensión .ipynb que se guardan en el directorio local.
  • En el notebook podemos almacenar: * Código * Texto (Markdown) * Equaciones (LaTeX) * Imágenes * Links a videos * HTML
  • Puede ser controlado por versiones.
  • Puede verse sin tener IPython instalado usando NBViewer: http://nbviewer.ipython.org/.
  • Puede ser exportado a otros formatos: Slides, HTML, Markdown, reStructured Text, LaTeX y PDF.

¿Cuáles son sus características principales?

  • Interactivo
  • Exploratorio
  • Colaborativo
  • Abierto
  • Reproducible

2. Instalando el notebook de IPython.

¿Qué necesito para hacerlo andar?

  • Tornado
  • ZeroMQ/PyZMQ
  • Jinja2
  • Chrome, Firefox, Safari (WebSockets, Flexible Box Model)
  • Matplotlib para hacer figuras
  • En linux, mac pip install ipython[notebook] o desde el source.
  • Instalación fácil (incluyendo instaladores para win): Canopy, Anaconda, Python XY, WinPython, Pyzo
  • Online: Wakari

Interfase de usuario.

  • Dashboard
  • Área del notebook y celdas
  • Menu
  • Barra de herramientas

Tipo de celdas

  • Code
  • Markdown
  • Raw text
  • Heading

"Shortcuts" del teclado

  • Shift-Enter para ejecutar una celda
  • Ctrl-Enter para ejecutar una celda y quedarse en la misma
  • Todas las otras "shortcuts" tienen la forma: Ctrl-m ?
  • Si querés ver las "shortcuts" disponibles: Ctrl-m h

3. Usando el notebook de IPython.

Trabajar con datos, códido y correrlo...

Por ejemplo, puedo generar figuras programáticamente utilizando matplotlib:

In [3]:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
/home/damian/.virtualenvs/blog/local/lib/python2.7/site-packages/pkg_resources.py:1054: UserWarning: /home/damian/.python-eggs is writable by group/others and vulnerable to attack when used with get_resource_filename. Consider a more secure location (set with .set_extraction_path or the PYTHON_EGG_CACHE environment variable).
  warnings.warn(msg, UserWarning)

In [4]:
x = np.linspace(0, 3*np.pi, 500)
y = np.sin(x**2)

fig = plt.figure()

axes = fig.add_axes([0.1, 0.1, 0.8, 0.8])

axes.plot(x, y, 'r')

axes.set_xlabel('x')
axes.set_ylabel('sin x^2')
axes.set_title('Una figura simple');

Puedo probar "pedacitos" de código (por ejemplo de tutoriales):

In [5]:
>>> the_world_is_flat = 1
>>> if the_world_is_flat:
...     print "Be careful not to fall off!"
Be careful not to fall off!

Puedo ver los errores o tracebacks de una manera simple y visualmente informativa:

In [6]:
%run non_existent_file
ERROR: File `u'non_existent_file.py'` not found.

In [7]:
x = 1
y = 4
z = y/(1-x)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-7-dc39888fd1d2> in <module>()
      1 x = 1
      2 y = 4
----> 3 z = y/(1-x)

ZeroDivisionError: integer division or modulo by zero

Otras ventajas a la hora de trabajar con código:

* Completado por Tab
* Ayuda integrada
* Resaltado de sintaxis
* "Atajos" interactivos (aliases, magics)
In []:
np.
In []:
%magic
In []:
%lsmagic

A través de las cell magics (%%) el notebook soporta correr código en otros lenguajes:

  • R
  • Octave
  • Cython
  • Bash
  • Perl
  • Ruby
  • etc.
In [8]:
%%bash
echo "hola gente"
hola gente

Documentar el flujo de trabajo.

El texto reendea vía Markdown, por ejemplo puedo tener italica, enfásis o linkear a google.

Puedo hacer listas:

  1. primero
  2. segundo
  3. tercero
  • auto
  • biblicleta
  • moto

Puedo embeber código para demostración, por ejemplo, para Python:

def f(x):
    """a docstring"""
    return x**2

o para otros lenguajes:

if (i=0; i<n; i++) {
  printf("hello %d\n", i);
  x += 4;
}

Gracias a MathJax, se pueden incluir expresiones matemáticas inline: \(e^{i\pi} + 1 = 0\) o en bloque:

\[e^x=\sum_{i=0}^\infty \frac{1}{i!}x^i\]

Si el navegador puede mostrarlo...

  • Imágenes:
In [9]:
from IPython.display import Image
Image(filename='figs/logo.png')
Out[9]:
In [10]:
from IPython.display import SVG
SVG(filename='figs/python-logo.svg')
Out[10]:
image/svg+xml
  • Video:

Puedo cargar y mostrar videos de Youtube, Vimeo, etc...

In [11]:
from IPython.display import YouTubeVideo
YouTubeVideo('MIAKOMzRl1I')
Out[11]:
  • Sitios web:
In [12]:
from IPython.display import HTML
HTML('<iframe src="http://www.damian.oquanta.info" width=800 height=600></iframe>')
Out[12]:

Cargar código remoto

In [13]:
%load http://matplotlib.sourceforge.net/mpl_examples/api/collections_demo.py
In [14]:
#!/usr/bin/env python
'''Demonstration of LineCollection, PolyCollection, and
RegularPolyCollection with autoscaling.

For the first two subplots, we will use spirals.  Their
size will be set in plot units, not data units.  Their positions
will be set in data units by using the "offsets" and "transOffset"
kwargs of the LineCollection and PolyCollection.

The third subplot will make regular polygons, with the same
type of scaling and positioning as in the first two.

The last subplot illustrates the use of "offsets=(xo,yo)",
that is, a single tuple instead of a list of tuples, to generate
successively offset curves, with the offset given in data
units.  This behavior is available only for the LineCollection.

'''

import matplotlib.pyplot as plt
from matplotlib import collections, transforms
from matplotlib.colors import colorConverter
import numpy as np

nverts = 50
npts = 100

# Make some spirals
r = np.array(range(nverts))
theta = np.array(range(nverts)) * (2*np.pi)/(nverts-1)
xx = r * np.sin(theta)
yy = r * np.cos(theta)
spiral = list(zip(xx,yy))

# Make some offsets
rs = np.random.RandomState([12345678])
xo = rs.randn(npts)
yo = rs.randn(npts)
xyo = list(zip(xo, yo))

# Make a list of colors cycling through the rgbcmyk series.
colors = [colorConverter.to_rgba(c) for c in ('r','g','b','c','y','m','k')]

fig, axes = plt.subplots(2,2)
((ax1, ax2), (ax3, ax4)) = axes # unpack the axes


col = collections.LineCollection([spiral], offsets=xyo,
                                transOffset=ax1.transData)
trans = fig.dpi_scale_trans + transforms.Affine2D().scale(1.0/72.0)
col.set_transform(trans)  # the points to pixels transform
    # Note: the first argument to the collection initializer
    # must be a list of sequences of x,y tuples; we have only
    # one sequence, but we still have to put it in a list.
ax1.add_collection(col, autolim=True)
    # autolim=True enables autoscaling.  For collections with
    # offsets like this, it is neither efficient nor accurate,
    # but it is good enough to generate a plot that you can use
    # as a starting point.  If you know beforehand the range of
    # x and y that you want to show, it is better to set them
    # explicitly, leave out the autolim kwarg (or set it to False),
    # and omit the 'ax1.autoscale_view()' call below.

# Make a transform for the line segments such that their size is
# given in points:
col.set_color(colors)

ax1.autoscale_view()  # See comment above, after ax1.add_collection.
ax1.set_title('LineCollection using offsets')


# The same data as above, but fill the curves.
col = collections.PolyCollection([spiral], offsets=xyo,
                                transOffset=ax2.transData)
trans = transforms.Affine2D().scale(fig.dpi/72.0)
col.set_transform(trans)  # the points to pixels transform
ax2.add_collection(col, autolim=True)
col.set_color(colors)


ax2.autoscale_view()
ax2.set_title('PolyCollection using offsets')

# 7-sided regular polygons

col = collections.RegularPolyCollection(7,
                                        sizes = np.fabs(xx)*10.0, offsets=xyo,
                                        transOffset=ax3.transData)
trans = transforms.Affine2D().scale(fig.dpi/72.0)
col.set_transform(trans)  # the points to pixels transform
ax3.add_collection(col, autolim=True)
col.set_color(colors)
ax3.autoscale_view()
ax3.set_title('RegularPolyCollection using offsets')


# Simulate a series of ocean current profiles, successively
# offset by 0.1 m/s so that they form what is sometimes called
# a "waterfall" plot or a "stagger" plot.

nverts = 60
ncurves = 20
offs = (0.1, 0.0)

yy = np.linspace(0, 2*np.pi, nverts)
ym = np.amax(yy)
xx = (0.2 + (ym-yy)/ym)**2 * np.cos(yy-0.4) * 0.5
segs = []
for i in range(ncurves):
    xxx = xx + 0.02*rs.randn(nverts)
    curve = list(zip(xxx, yy*100))
    segs.append(curve)

col = collections.LineCollection(segs, offsets=offs)
ax4.add_collection(col, autolim=True)
col.set_color(colors)
ax4.autoscale_view()
ax4.set_title('Successive data offsets')
ax4.set_xlabel('Zonal velocity component (m/s)')
ax4.set_ylabel('Depth (m)')
# Reverse the y-axis so depth increases downward
ax4.set_ylim(ax4.get_ylim()[::-1])


plt.show()

Reendeo de LaTeX:

In [15]:
from IPython.display import Latex
Latex(r"""\begin{eqnarray}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, 
\frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\
\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, 
\frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} & = 0 
\end{eqnarray}""")
Out[15]:
\begin{eqnarray} \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ \nabla \cdot \vec{\mathbf{B}} & = 0 \end{eqnarray}

Pandas

In [16]:
import pandas
pandas.set_option('display.notebook_repr_html', True)
In [17]:
%%writefile data.csv
Date,Open,High,Low,Close,Volume,Adj Close
2012-06-01,569.16,590.00,548.50,584.00,14077000,581.50
2012-05-01,584.90,596.76,522.18,577.73,18827900,575.26
2012-04-02,601.83,644.00,555.00,583.98,28759100,581.48
2012-03-01,548.17,621.45,516.22,599.55,26486000,596.99
2012-02-01,458.41,547.61,453.98,542.44,22001000,540.12
2012-01-03,409.40,458.24,409.00,456.48,12949100,454.53
Overwriting data.csv

In [18]:
df = pandas.read_csv('data.csv')
df
Out[18]:
Date Open High Low Close Volume Adj Close
0 2012-06-01 569.16 590.00 548.50 584.00 14077000 581.50
1 2012-05-01 584.90 596.76 522.18 577.73 18827900 575.26
2 2012-04-02 601.83 644.00 555.00 583.98 28759100 581.48
3 2012-03-01 548.17 621.45 516.22 599.55 26486000 596.99
4 2012-02-01 458.41 547.61 453.98 542.44 22001000 540.12
5 2012-01-03 409.40 458.24 409.00 456.48 12949100 454.53

4. Discusión.

El notebook de IPython provee toda la infraestructura para:

  1. Obtener datos.
  2. Procesar los datos obtenidos.
  3. Visualizar los datos procesados
  4. Elaborar una historia que se sustenta en los datos analizados.
  5. Comunicar dicha historia (junto con los datos y las visualizaciones obtenidas).

En resumen:

  • El notebook the IPython nos permite contar toda la historia.
  • Genera un documento sumamente complejo, pero a la vez super informativo.
  • Genera un documento que ya no es estático, sino dinámico y que puede ser reproducido por colegas.
  • Genera un documento que es posible formatear (a través de js y css) de acuerdo a las especificaciones requeridas.
  • El notebook de IPython es un historia ejecutable, pero tambien un libro, un sitio interactivo, una presentación...

NOTA: Pasear por la galería de notebook de IPython disponible en: https://github.com/ipython/ipython/wiki/A-gallery-of-interesting-IPython-Notebooks

5. El fututo del notebook the IPython

Para conocer en forma detallada lo lineamientos a futuro:

https://github.com/ipython/ipython/wiki/Roadmap:-IPython

  • Soporte para multiusuarios
  • Soporte para múltiples directorios
  • Widgets interactivos (js)
  • Exportación mejorada de los notebook a otros formatos
  • Modo presentación

GRACIAS!!

Damián Avila

@damian_avila

Github

Email: info@oquanta.info