plotly
En un artículo anterior ya vimos como pasar las gráficas creadas con matplotlib
a plotly
para obtener cierto nivel de interacción con los datos en la web.
Ahora, lo que vamos a ver es cómo crear gráficas directamente con plotly
.
Para ello vamos a utilizar:
pandas 0.14.1
, para trabajar con tablas de datos.plotly 1.2.6
, para crear las gráficas.Importamos los paquetes de la manera habitual en cada caso.
In [1]:
import pandas as pd
import plotly.plotly as py
from plotly.graph_objs import *
En el caso de Plotly hemos importado además unos objetos graph_objs
que nos serán de ayuda a la hora de crear las gráficas.
Trabajar con la API de Plotly en Python se resume en trabajar con listas y diccionarios de Python.
Los graph_objs
de Plotly nos ayudarán a trabajar con estas listas y diccionarios proporcionando ayuda y validando los datos y parámetros introducidos.
Entre los objetos que importamos tenemos los bloques principales de trabajo:
plotly
.
In [2]:
help(Figure)
In [3]:
help(Data)
In [4]:
help(Layout)
Como vemos, Figure
y Layout
son objetos de tipo diccionario y Data
es un objeto de tipo lista.
En el caso de Data
, el orden es importante pues determina el orden de composición de los trazos, empezando por el primero objeto en Data
.
Por ejemplo, representar una línea sobre una barras no produce, normalmente, el mismo resultado que representar unas barras sobre una línea.
En Plotly cada tipo de gráfica tiene su propio objeto (trace graph object) como son Scatter
, Bar
o Histogram
.
In [5]:
help(Scatter)
Hecha la presentación de Plotly pasamos a un ejemplo práctico de uso de la herramienta. Para ello tomaremos datos de estadísticas de tráfico del aeropuerto de Heathrow, el tercer mayor del mundo por tráfico de pasajeros.
Los datos son proporcionados en un fichero Excel que podemos importar con Pandas.
In [6]:
pasajeros = pd.io.excel.read_excel('07-Heathrow_Monthly_Traffic_Statistics_(Jan_2005-Jul_2014).xls',
sheetname=0, # tomamos la primera hoja del archivo
header=2, # la cebecera empieza en la fila 3
index_col=0) # empleamos las fechas como indices
pasajeros.head(5)
Out[6]:
In [7]:
mercancias = pd.io.excel.read_excel('07-Heathrow_Monthly_Traffic_Statistics_(Jan_2005-Jul_2014).xls',
sheetname=5, # tomamos la sexta hoja del archivo
header=2, # la cebecera empieza en la fila 3
index_col=0) # empleamos las fechas como indices
mercancias.head(5)
Out[7]:
Como podemos ver, se trata de una serie temporal.
Y puesto que los datos se proporcionan mes a mes, podríamos deshacernos del día del mes indicandole a Pandas que se trata de un periodo de frecuencia mensual con to_period
. Pero no es necesario, pues como veremos más adelante, Plotly es capaz de intuir que queremos representar los datos mes a mes.
In [8]:
pasajeros.to_period('M').head(2)
Out[8]:
Si ya hemos guardado nuestras credenciales de Plotly en el ordenador, al importar el paquete como
import plotly.plotly as py
ya nos logueamos automáticamente en el servidor sin tener que acceder mediante
py.sign_in('username', 'api_key')
Una figura (Figure
) Plotly se compone de los datos a representar (Data
) y de un formato (Layout
), y estos a su vez no son más que un conjunto de listas y diccionarios que Plotly se encargará de convertir a un formato JSON.
Como hemos mencionado arriba, Plotly proporciona una serie de graph_objs
que nos facilitarán la tarea de componer gráficas interactivas y que veremos a continuación.
Empezamos por el conjunto de datos a representar. En este caso vamos a representar el tráfico mensual de pasajeros en los aeropuertos británicos del grupo Heathrow, participada al 25% por Ferrovial, que incluye los aeropuertos de:
Para representar estos datos nos valdremos de la herramienta Data
que, como hemos visto anteriormente, admite una lista de objetos.
En nuestro caso, líneas.
Para ello nos valdremos de Scatter
(dispersión) al cual pasaremos los siguentes parámetros:
name
, nombre que damos a la línea, en nuestro caso, el nombre del aeropuerto.x
, array con los meses del año que corresponden al index de nuestro DataFrame
.y
, array con el número de pasajeros correspondientes a cada aeropuerto.mode
, cadena de texto que indica el tipo de representación que queremos, ya sea 'lines'
, 'markers'
, 'text'
o una combinación de ellos como podría ser 'lines+markers'
.Puesto que se trata de una lista con cuatro líneas a representar, haremos uso de las list comprehensions de Python.
In [9]:
p_data = Data([Scatter(name=col,
x=pasajeros.index,
y=pasajeros[col],
mode='lines') for col in pasajeros.columns.values[:4]])
Ya con los datos a representar definidos, ahora podemos pasar a retocar el layout de la figura. Para ello vamos a añadir un título a la gráfica y a los ejes x e y. Otra cosa que haremos también es encuadrar la gráfica con
showline=True, mirror='ticks', linewidth=2
y reducir los margenes derecho r
y superior t
para aprovechar mejor el espacio.
In [10]:
p_layout = Layout(title='Tráfico mensual de pasajeros en aeropuertos del grupo Heathrow',
xaxis=XAxis(title='Mes', showgrid=False, showline=True, mirror='ticks', linewidth=2),
yaxis=YAxis(title='Pasajeros', zeroline=False, showline=True, mirror='ticks', linewidth=2),
margin=Margin(r=20, t=80))
In [11]:
p_fig = Figure(data=p_data, layout=p_layout)
p_plot = py.iplot(p_fig, filename='pybonacci/heathrow-pasajeros')
Tanto Figure
como Layout
, XAxis
, YAxis
y Margin
se podrían substituir por la expresión dict()
pues, como ya hemos mencionados, Plotly trabaja con diccionarios y listas de Python. Sin embargo, el utilizar estas herramientas de plotly.graph_objs
nos da acceso a la ayuda, y nos permite validar los parámetros introducidos.
In [12]:
m_data = Data([Scatter(name=col,
x=mercancias.index,
y=mercancias[col],
mode='lines') for col in pasajeros.columns.values[:4]])
In [13]:
m_layout = Layout(title='Tráfico mensual de mercancías en aeropuertos del grupo Heathrow',
xaxis=XAxis(title='Mes', showgrid=False, showline=True, mirror='ticks', linewidth=2),
yaxis=YAxis(title='Mercancías (t)', zeroline=False, showline=True, mirror='ticks', lineheight=2),
margin=Margin(r=20, t=80))
Si hubiesemos hecho lo mismo con dict()
, el error hubiese pasado desapercibido hasta el final.
In [14]:
m_layout = dict(title='Tráfico mensual de mercancías en aeropuertos del grupo Heathrow',
xaxis=dict(title='Mes', showgrid=False, showline=True, mirror='ticks', linewidth=2),
yaxis=dict(title='Mercancías (t)', zeroline=False, showline=True, mirror='ticks', linewidth=2),
margin=dict(r=20, t=80))
In [15]:
m_fig = Figure(data=m_data, layout=m_layout)
m_plot = py.iplot(m_fig, filename='pybonacci/heathrow-mercancias')
Disponemos de una muestra lo suficientemente grande como desaprovechar la oportunidad de extraer alguna conclusión al respecto. Y contamos con la ventaja de poder hacer zoom en los datos, lo cual resulta especialmente útil en le tráfico de mercancías, donde el aeropuerto de Heathrow está varios órdenes de magnitud por encima del resto.
Vemos claramente que en los meses de verano hay un aumento del número de pasajeros en todos los aeropuertos del grupo.
También se aprecia un ligero repunte en el mes de diciembre con motivo, previsiblemente, de las vacaciones de navidad.
Esto lo podemos visualizar de otra manera mediante un Heatmap
del aeropuerto de Heathrow.
Para ello vamos a utilizar el paquete calendar
que nos permitirá crear una lista con los nombres de los meses; y Numpy para crear una lista con los años.
In [16]:
import calendar
import numpy as np
Para representar el Heatmap
necesitaremos agrupar los datos por años o meses, en función del eje y que tomemos.
En este caso hemos decido representar los meses en el eje de ordenadas, por lo tanto agruparemos los datos por meses.
Para ello nos valdremos de una función anónima.
In [17]:
gb = lambda x: x.month
In [18]:
data = Data([Heatmap(x=np.arange(2005,2015),
y=calendar.month_abbr[1:],
z=[grp['Heathrow'] for key, grp in pasajeros.groupby(gb)],
colorscale='Portland')])
En el eje x hemos colocado los años, en el eje y los meses, y la intensidad del color viene determinada por el número de pasajeros.
Con Layout
añadimos el título de la gráfica y los nombres de los ejes, y también podemos especificar el tamaño de la gráfica deshabilitando el autosize
y definiendo nuestro propio ancho y alto.
In [19]:
layout = Layout(title='Tráfico de pasajeros en el aeropuerto de Heathrow',
autosize=False,
width=550,
height=550,
xaxis=XAxis(title='Año', showgrid=False),
yaxis=YAxis(title='Mes', showgrid=False))
Ya podemos publicar nuestra gráfica de la manera habitual.
In [20]:
heatmap_plot = py.iplot(Figure(data=data,layout=layout), filename='pybonacci/heathrow-heatmap')
Si en el transporte de pasajeros hay un patrón claro, el transporte de mercancías por avión no muestra signos de estacionalidad.
Para verlo mejor podríamos volver a emplear un Heatmap
, pero vamos a hacerlo con un diagrama de caja Box
para el aeropuerto de Heathrow.
Aprovecharemos nuevamente la agrupación por meses que hemos empleado para el Heatmap
de pasajeros. Nuevamente hacemos uso de las list comprehensions de Python para crear una lista de bloques, cada uno correspondiente a un mes. Lo mismo podríamos conseguirlo sin crear una lista y sin necesidad de agrupar si en vez de asignar un name
asignamos un único array x
con el valor del mes correspondiente a cada y
. Con boxpoints='all'
lo que hacemos es mostrar los puntos de la muestra al lado de cada bloque.
In [21]:
data = Data([Box(name=calendar.month_abbr[key],
y=grp['Heathrow'].values,
boxpoints='all') for key, grp in mercancias.groupby(gb)])
Añadimos, como es costumbre, un título a la gráfica y aprovechamos para ocultar la leyenda y ajustar los margenes.
In [22]:
layout = Layout(title='Tráfico de mercancías en el aeropuerto de Heathrow (2005-2014)',
showlegend=False,
margin=Margin(r=20, t=90))
In [23]:
box_plot = py.iplot(Figure(data=data, layout=layout), filename='pybonacci/heathrow-box')
Hasta ahora hemos visto por separado los datos de pasajeros y mercancías. Compararemos en una única gráfica los datos del aeropuerto de Glasgow. Para facilitar la visualización y compensar la diferencia de magnitud utilizaremos múltiples ejes y, de paso, emplearemos diferentes representaciones para el tráfico de pasajeros y el de mercancías.
Los pasajeros los representaremos mediante líneas y puntos 'lines+markers'
y le asignamos el segundo eje y 'y2'
, pues vamos a querer que nos lo represente por encima de las barras de tráfico de mercancías.
El orden en Plotly es importante.
Vamos a representar las lineas de color dorado primero como horizontales y luego verticales de un valor a otro con shape='hv'
.
Los puntos serán de un color dorado más claro con el borde también dorado.
In [24]:
pas = Scatter(name='Pasajeros',
x=pasajeros.index,
y=pasajeros['Glasgow'],
yaxis='y2',
mode='lines+markers',
line=Line(shape='hv', color='darkgoldenrod'),
marker=Marker(color='goldenrod',
line=Line(color='darkgoldenrod', width=2)))
Por su parte, el tráfico de mercancías lo representaremos como barras verticales de color gris claro. Por defecto se le asigna el primer eje y.
In [25]:
mer = Bar(name='Mercancías',
x=pasajeros.index,
y=mercancias['Glasgow'],
marker=Marker(color='lightgray'))
Creamos la lista con los elementos a representar.
In [26]:
data = Data([mer, pas])
Por último configuramos el layout añadiendo un título a la gráfica y configurando los ejes.
In [27]:
layout = Layout(title='Tráfico de pasajeros y mercancías en el aeropuerto de Glasgow',
showlegend=False,
xaxis=XAxis(title='Mes'),
yaxis=YAxis(title='Mercancías (t)',
showgrid=False),
yaxis2=YAxis(title='Pasajeros',
showgrid=False,
overlaying='y',
side='right'))
Incluimos también una nota de texto indicando la fuente de los datos.
Plotly nos permite untilizar un subconjunto de etiquetas HTML para dar formato a los textos para por ejemplo incluir nuevas líneas (<br>
) o añadir hipervínculos (<a href='...'></a>
) que podremos utilizar en cualquier texto de la gráfica (títulos y anotaciones).
In [28]:
fuente = Annotation(text="Fuente: <a href=\
'http://www.heathrowairport.com/about-us/investor-centre/results-and-performance/traffic-statistics'\
>LHR Airports Limited</a>",
xref='paper', # empleamos coordenadas 'paper', con ello la nota no se moverá al mover los ejes.
yref='paper',
x=0,
y=-0.15,
showarrow=False)
Actualizamos el diccionario de layout de la gráfica.
In [29]:
layout.update(annotations=Annotations([fuente]))
In [30]:
ma_plot = py.iplot(Figure(data=data, layout=layout), filename='pybonacci/heathrow-multipleaxis')
Aquí sólo hemos mostrado una pequeña parte del potencial de Plotly. Todo aquel que quiere ampliar detalles encontrará mucha más información en el API library de Plotly. También se pueden sacar buenas ideas del graphing news feed viendo lo que publican otros usuarios.
No dudeis en contactar con nosotros o el equipo de Plotly por email o Twitter para cualquier duda o sugerencia.