Ejercicio

Utilizando datos de la API de GitHub, producir una figura con tres gráficas: commits por día del proyecto, y commits por día de las tres personas con mayor número de commits

Cargar los datos

Se leen directamente desde un fichero JSON, obtenido a través de la API de GitHub con el programa project_hist.py.


In [1]:
import numpy as np
import json

import dateutil.parser

In [2]:
with open("dat/scipy_scipy.json") as f:
    scipy_datos = json.load(f)

A continuación, vamos a descartar todos los datos que no nos interesan. Vamos a construir un array de tres columnas, con tantas filas como commits haya en los datos:

  1. Fecha del commit (no necesariamente ordenadas)
  2. Nombre del commiter (no necesariamente el autor)
  3. Hash del commit (innecesario, solo para identificar los commits)

Con dateutil.parser.parse obtengo un objeto datetime a partir de una cadena en formato ISO 8601. Con su método date lo convierto a date y por tanto descarto horas, minutos y segundos.


In [3]:
N = len(scipy_datos)

commits_info = np.empty((N, 3), dtype=object)
for i in range(N):
    date_commit = scipy_datos[i]['commit']['committer']['date']
    author_name = scipy_datos[i]['commit']['committer']['name']
    commit_hash = scipy_datos[i]['sha']
    dt = dateutil.parser.parse(date_commit)
    commits_info[i] = [dt.date(), author_name, commit_hash]

Proyecto completo

Ahora me interesa saber el número total de commits por día, olvidándome de los autores. Para ello, lo primero que hago es obtener una lista de fechas sin repetir, utilizando np.unique:


In [4]:
fechas = np.unique(commits_info[:, 0])

Ahora voy a construir un array de dos columnas: la primera tendrá las fechas (serán todas distintas), y la segunda tendrá el número de commits correspondientes a esa fecha.

Para conseguir esto, solo tengo que iterar sobre las fechas y utilizar esta propiedad:


In [5]:
ff = fechas[30]  # Una fecha de ejemplo
print(ff)

# La siguiente línea devuelve una máscara
# (un array de True/False) con True en las posiciones
# correspondientes a las filas donde se cumple la condición
mask = commits_info[:, 0] == ff

# Uso esa máscara para obtener los commits de
# fecha `ff`
print(commits_info[mask])


2010-06-07
[[datetime.date(2010, 6, 7) 'rgommers'
  'e780c4bca8de950a92a72b0d22b55b45001aea87']
 [datetime.date(2010, 6, 7) 'David Cournapeau'
  'c0751d0f7c68ed9bccf27a97a7edf77585429fe3']]

Este es el código:


In [6]:
num_fechas = len(fechas)

# Mi array de dos columnas, con fechas y número
# de commits
commits_por_dia = np.empty((num_fechas, 2), dtype=object)

# Itero en las fechas
for ii in range(num_fechas):
    num_commits = len(commits_info[commits_info[:, 0] == fechas[ii]])
    commits_por_dia[ii] = (fechas[ii], num_commits)

Representamos la gráfica:


In [7]:
import matplotlib.pyplot as plt

fig, axes = plt.subplots(4, 1, sharex=True, sharey=True, figsize=(6, 8))

fig.autofmt_xdate()

In [8]:
axes[0].plot(commits_por_dia[:, 0], commits_por_dia[:, 1])


Out[8]:
[<matplotlib.lines.Line2D at 0x7f1759326a50>]

Gráficas por autor

En primer lugar, debemos seleccionar los 3 commiters con máximo número de commits. La lógica es similar que la que hemos seguido para seleccionar las fechas:


In [9]:
committers = np.unique(commits_info[:, 1])
num_committers = len(committers)

commits_por_autor = np.empty((num_committers, 2), dtype=object)

for jj in range(num_committers):
    num_commits = len(commits_info[commits_info[:, 1] == committers[jj]])
    commits_por_autor[jj] = (committers[jj], num_commits)

Una vez tenemos cada persona con su número de commits, solo hay que ordenar el array y sacar los tres primeros: necesitamos la función np.argsort).


In [10]:
indices = np.argsort(commits_por_autor[:, 1])[::-1]
autores_selecc = commits_por_autor[indices][:3, 0]
autores_selecc


Out[10]:
array(['Pauli Virtanen', 'Ralf Gommers', 'rgommers'], dtype=object)

Una vez tenemos los nombres de los autores, ya podemos averigüar los commits por día de cada uno de ellos:


In [11]:
kk = 1
for autor in autores_selecc:
    commits_autor = commits_info[commits_info[:, 1] == autor]

    commits_por_dia_autor = np.empty((num_fechas, 2), dtype=object)

    # Itero en las fechas
    for ii in range(num_fechas):
        num_commits = len(commits_autor[commits_autor[:, 0] == fechas[ii]])
        commits_por_dia_autor[ii] = (fechas[ii], num_commits)
    
    axes[kk].plot(commits_por_dia_autor[:, 0], commits_por_dia_autor[:, 1])
    
    kk += 1

In [12]:
fig.suptitle("Estadísticas GitHub")
fig.savefig("estadisticas_github.png")

In [13]:
from IPython.display import Image

Image("estadisticas_github.png")


Out[13]:

Y fin :)

Extras:

  • Como estamos tomando solo las fechas donde se producen commits, en el gráfico hay «saltos» (ver por ejemplo mayo de 2013). Una mejor manera de hacer esto sería crear un vector con todos los días, y rellenar con ceros cuando no haya commits.
  • ¿Cómo se podría crear el gráfico de commits por semana?
  • ¿Cómo se podría añadir un texto para indicar el significado de cada gráfico?

In [ ]: