Archivo de la categoría: Programación

Que roles tiene Scrum?

Ya llevo un par de posts explicando que son las metodologías ágiles y que es scrum en especifico. En esta entrada voy a exponer los distintos roles que tiene un equipo de scrum. Por el bien de la simplicidad Scrum solo considera tres roles.

Product owner (dueño de producto)

El dueño de producto representa a las partes interesadas (stakeholders = los clientes y consumidores principalmente). Su principal tarea es crear una hoja de ruta para que el equipo entregue el mayor valor posible. Es el encargado de diseñar el producto teniendo el cliente en mente. Diseña las historias de usuario (user stories), las pone en la cola de tares y las prioriza dependiendo en la importancia y sus interrelaciones. Este rol no se puede llevar a cabo conjuntamente con el de scrum master ya que el jefe de producto es el encargado de lidiar con los clientes mientas que el scrum master es el encargado de de decidir el camino para la implementación técnica. En resumen, el dueño de producto es el proxy entre los desarrolladores y los clientes, por lo tanto este rol requiere buenas habilidades de comunicación.

Desarrollador

Como ya indica el nombre, el desarrollador es el encargado de desarrollar el producto. Los desarrolladores son los encargados de implementar partes de producto en cada sprint que puedan ser entregadas al cliente. Como subcategorías encontraríamos los desarrolladores (máquinas biológicas que transforman café en lineas de código), los testers, arquitectos de software, analistas, diseñadores, etc.

Scrum master

Es principalmente el encargado de quitar los impedimentos que tengan los miembros de su equipo (los desarrolladores) al implementar el entregable. Es el encargado de facilitar los meetings, evitar distracciones, y asegurarse que se siguen las reglas acordadas para la metodología. También es el encargado de asegurarse de cumplir con las fechas de entrega y ayudar al equipo a mejorar y superarse día a día. Por decirlo de otro modo es el encargado de manejar el backend del equipo, manejarse internamente.

De que hablan los desarolladores cuando mencionan Scrum?

La semana pasada hablamos de metodologías ágiles como concepto general. Esta semana vamos a profundizar con Scrum, una metodología ágil entre muchas. El Scrum hace especial énfasis en el desarrollo de software para equipos (idealmente) de 3-9 desarrolladores (que curiosamente, o no, sigue la regla de las dos pizzas). Los equipos pequeños tienden a obtener una mejor productividad en general. Los meetings son más directos y requieren menos interacciones. Si cada miembro de un equipo tiene que mantenerse en contacto con los demás miembros el número de interacciones entre participantes se incrementa exponencialmente con el número de integrantes del grupo.

Los integrantes del grupo tienen un tiempo definido para finalizar las tareas asignadas. El tiempo asignado depende de cada equipo. Normalmente se sitúan en 15 días, aunque puede llegar hasta los 30. Estos intervalos de tiempo son comúnmente nombrados “sprints”. Pero para seguir la evolución de las tareas y re-planificar si es necesario, Scrum incorpora standup meetings. Estos meetings son reuniones diarias en las que la duración aproximada es de 15 minutos y se hacen de pie. Los standup meetings tienen el nombre en clave de scrum diario (daily scrum).

La idea principal de scrum se basa en la colaboración plena entre miembros del equipo para la construcción del producto, contrario a otras metodologías más tradicionales. Previamente cada individuo hacia su tarea asignada en base a los requerimientos hecho que incrementaba las probabilidades de fracaso estrepitosamente. Cualquier error de planificación o falta de detalle era pagado con creces ya que a menudo el proyecto tenía que ser desarrollado de nuevo y a menudo requería desechar buena parte del progreso. Lo que intenta hacer Scrum es evitar justamente todo esto. Scrum intenta adaptarse a los cambios con facilidad y poder desechar partes sin que el coste total del proyecto se incremente prohibitivamente.

Implícitamente Scrum reconoce que los clientes no saben lo que quieren y que sus necesidades evolucionan continuamente adaptándose a las nuevas tendencias. A menudo los cambios son impredecibles y consecuentemente imposibles de planificar. Con todo este en mente, Scrum crea un marco en el que la adaptabilidad permite responder a los cambios emergentes, nuevas tecnologías y cambios en el mercado.

 

Que son las metodologías de desarrollo ágiles?

Hace ya algunos años que las metodologías ágiles están de moda entre ingenieros, especialmente equipos de programadores. Muchos hablan Scrum, pero hay otras como XP (eXtrem Programming), kanban, V-model, waterfall model, modelo espiral, y un largo etc. Frecuentemente estas metodologías son usadas como onanismo teórico. Recursos humanos frecuentemente recurren a las palabras “trending” para atraer talento. Pero del dicho al hecho hay un trecho.

Los métodos agile de desarrollo de software se adaptan al proyecto. Los requerimientos y soluciones evolucionan gracias al esfuerzo colaborativo entre miembros del equipo interdisciplinar de desarrollo y el cliente. Su objetivo principal es la adaptación y flexibilidad para desarrollar soluciones, entregar productos antes de tiempo, y mejorar continuamente el producto. Para los que queráis leer más sobre los principios de los métodos ágiles lo podéis encontrar un manifesto online.

Ninguna de las metodologías es perfecta. Cada una tiene un foco especifico y un objetivo concreto. Cada uno de los marcos de desarrollo intenta solventar alguno de los problemas que los equipos tienen. Algunos con más éxito que otros, pero al final una parte importante del resultado depende de las personas. Cada empresa y cada equipo tiene su blueprint para enfrentarse a problemas. Por lo que cada equipo tiene que entender la metodología y adaptarla para mejorar el rendimiento y no al revés.

 

 

PyCon: Las tres charlas que me gustaron

El PyCon2018 fue del 9 al 17 de mayo en Cleveland. La conferencia nos dejó una gran cantidad de charlas interesantes. A mi personalmente me gustaron tres. Dos de machine learning y una de optimización de código. Aquí dejo los vídeos para que podáis echarles una ojeada. Además dejo un mini-resumen con cada vídeo.

A practical guide to Singular Value Decomposition in Python

Esta charla es una introducción a los SVD (Singular Value Decomposition). Los SVD descomponen cada punto en vectores y miden las diferencias basándose en el ángulo de separación entre los elementos. Daniel Pyrathon defiende el uso de este algoritmo gracias a la demostrada efectividad de Netflix. Como ejemplo pone a Netflix, dónde la mayoría de vídeos recomendados nos gustan. Nos gustan porque el algoritmo ha sabido encontrar similitudes entre otros usuarios y entre series/películas. La charla es amena y recomendable.

Fighting the Good Fight: Python 3 in your organization

Durante unos 30min muy entretenidos Jason Fried nos explica como entrenó la inteligencia artificial para aprender a jugar al Street fighter. Des del principio el conferenciante engancha y nos explica la pipeline usada dónde el ordenador aprende a “entender” la pantalla (barras de salud, tiempo, y los jugadores) y pasa a optimizar las acciones para maximizar la recompensa. Optimizando para conseguir la mayor recompensa el algoritmo encuentra combinaciones de movimientos para ganar a los sus oponentes.

Performance Python: Seven Strategies for Optimizing Your Numerical Code

Quizás esta sea la charla más técnica de las tres. Jake VanderPlas nos explica como podemos optimizar la ejecución de nuestro código. En la charla expone siete herramientas que los programadores podemos usar para optimizar nuestro código. Des del uso de librerías para manejar vectores de forma eficiente, hasta compiladores que transforman el código de Python a C. Pasando por herramientas para paralelizar el código. Numpy, Cython, Numba, y Dask son algunas de las herramientas mencionadas.

¿Que te parecieron a ti las otras charlas? ¿Tienes alguna que recomiendes?

Introducción a TensorFlow

La web oficial de TensorFlow tiene muy buenos recursos. En esencia lo que hay en este post proviene del “get started” de la web oficial.

En el primer ejemplo importaremos TensorFlow. Crearemos dos constantes y las imprimiremos en pantalla.

[code language=”python”]
import tensorflow as tf
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0) # También tf.float32 de forma implícita
sess = tf.Session()
print(sess.run([node1, node2]))
node3 = tf.add(node1, node2)
print("node3: ", node3) #Esta linea muestra las propiedades del tensor
print("sess.run(node3): ",sess.run(node3)) # Aquí se imprime el resultado[/code]

También podemos aplicar operaciones a los tensors. Las operaciones producen más tensores. En el siguiente ejemplo sumamos dos variables (tensores) y luego las sumamos. Un tensor es capaz de procesar listas también como veremos.

[code language=”python”]
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b
print(sess.run(adder_node, {a: 3, b:4.5})) # 7.5
print(sess.run(adder_node, {a: [1,3], b: [2, 4]})) # Itera sobre la lista: 3 y 7
[/code]

Podemos multiplicar

[code language=”python”]
add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b:4.5}))
[/code]

E incluso podemos crear modelos lineales

[code language=”python”]
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
[/code]

Las constantes son inicializadas con tf.constant, y su valor no puede ser cambiado. Contrariamente las variables son creadas usando tf.Variable y no son inicializadas en el inicio. Para inicializar las variables  tenemos que usar una función especial como en el siguiente ejemplo:

[code language=”python”]
init = tf.global_variables_initializer()
sess.run(init)
[/code]

Lo importante es usar init. Es un “handle” que inicia el grafo, inicializando las variables que hasta entonces habían permanecido sin inicializar.

Ya que la X es la variable podemos evaluar el modelo usando distintos valores simultáneamente de la siguiente manera:

[code language=”python”]
print(sess.run(linear_model, {x:[1,2,3,4]}))
# Output: [ 0, 0.30000001, 0.60000002, 0.90000004]
[/code]

La función de pérdida (loss function) puede medir la distancia entre el modelo actual y los datos proporcionados. Usaremos la función de pérdida estándar para hacer una regresión lineal que sume los cuadrados de las distancias entre el modelo actual y los datos. El modelo crea un vector en el que cada posición corresponde a una distancia de error. Cuando usamos tf.square estamos elevando al cuadrado el error. Luego sumamos los errores para crear un simple escalar que abstrae el error de todos los puntos usando tf.reduce_sum:

[code language=”python”]
y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model – y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
[/code]

El valor producido es:  23.66

Podríamos mejorar el modelo manualmente asignando nuevos valores a W para obtener resultados perfectos de -1 y 1. Una variables es inicializada por el valor proporcionado por tf.Variable pero puede ser cambiada si usamos operaciones como tf.assign. Por ejemplo, W=-1 y b=1 son los parámetros óptimos de nuestro modelo. Podemos cambiar W y b acordemente:

[code language=”python”]
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]})
#resultado: 0
[/code]

Hemos adivinado el valor perfecto para W i b aunque este no es el objetivo de  machine learning. El objetivo es encontrar los parámetros correctos para el modelo automáticamente. Pero esto lo vais a encontrar en la próxima entrega 😉

Bonus: En matemáticas y en física, un tensor es cierta clase de entidad algebraica de varias componentes, que generaliza los conceptos de escalar, vector y matriz de una manera que sea independiente de cualquier sistema de coordenadas elegido. ( wikipedia )

Generar una barra de progreso en ipython notebooks

Manual con código para generar una barra de progreso en nuestro código para saber en que porcentaje de compleción estamos sin llenar el output con números. Muchos de los que usáis jupyter (el nuevo ipython notebooks) podéis imprimir por pantalla la iteración en la que vuestro loop reside. Eso es solo posible para una cantidad pequeña de iteraciones. Cuando llegamos a varios miles se puede generar un output bastante engorroso. Googleando un poco encontré la solución. Este código nos genera una clase que luego podemos llamar para obtener la barra de progreso junto con el porcentaje completado.

[code language=”python”]
import sys, time
try:
    from IPython.core.display import clear_output
    have_ipython = True
except ImportError:
    have_ipython = False

class ProgressBar:
    def __init__(self, iterations):
        self.iterations = iterations
        self.prog_bar = ‘[]’
        self.fill_char = ‘*’
        self.width = 40
        self.__update_amount(int(0))
        if have_ipython:
            self.animate = self.animate_ipython
        else:
            self.animate = self.animate_noipython

    def animate_ipython(self, iter):
        try:
            clear_output()
        except Exception:
            # terminal IPython has no clear_output
            pass
        print ‘\r’, self,
        sys.stdout.flush(),
        self.update_iteration(iter + 1)
        
    def update_iteration(self, elapsed_iter):
        self.__update_amount((elapsed_iter / float(self.iterations)) * 100.0)
        self.prog_bar += ‘  %d of %s complete’ % (elapsed_iter, self.iterations)

    def __update_amount(self, new_amount):
        percent_done = int(round((new_amount / 100.0) * 100.0))
        all_full = int(self.width – 2)
        num_hashes = int(round((percent_done / 100.0) * all_full))
        self.prog_bar = ‘[‘ + self.fill_char * num_hashes + ‘ ‘ * (all_full – num_hashes) + ‘]’
        pct_place = int((len(self.prog_bar) / 2) – len(str(percent_done)))
        pct_string = ‘%d%%’ % percent_done
        self.prog_bar = self.prog_bar[0:pct_place] + \
            (pct_string + self.prog_bar[pct_place + len(pct_string):])

    def __str__(self):
        return str(self.prog_bar)
        

[/code]

Y el siguiente código vemos un ejemplo de como llamaremos la clase para obtener la funcionalidad deseada

[code language=”python”]
c = ProgressBar(1000)
for i in range(1000):
    c.animate_ipython(i)
[/code]

Para los interesados encontré el código aquí.

Usar listas en matplotlib en vez de un punto a la vez

TLTR: Cuando uses matplotlib en python pasa a la función dos listas con todos los puntos en vez de ir uno por uno. Así te ahorras el rendering cada vez que usas la función consiguiendo incrementos de velocidad de varias magnitudes.

Hasta hace poco lo que hacía mayoritariamente cada vez que tenia que hacer una gráfica con matlplotlib era poner un punto cada vez. ERROR!!! Hasta el momento asumía que el tiempo de ejecución del script era debido a los algoritmos que usaba. Pero no, era debido a una  malapraxis muy simple de resolver. Últimamente estoy trabajando con cantidades más grandes de datos y la ejecución de mi código en python me tardaba más de lo que debería hasta que descubrí que hacia un error de novato. Poner punto por punto a una gráfica en vez de pasarle la lista entera era lo que causaba una ejecución tan lenta de mi código. Iba punto por punto porque era como me llegaban los datos y no los tenia que guardar, pero resulta que si los pongo en una lista y luego paso la lista a la librería el resultado es infinitamente más veloz y eficaz. La librería se ahorra renderizar la gráfica cada vez si recibe una lista.

El siguiente código tiene una ejecución de más de 6 segundos y como veis es la forma incorrecta de hacer un scatter plot:

[code language=”python”]
start_time = timeit.default_timer()
for x in range(100):
for y in range(10):
matplotlib.pyplot.scatter(x,y)

matplotlib.pyplot.show()
end_time = timeit.default_timer()
print(end_time – start_time)
[/code]

El siguiente código es la manera rápida de hacer un scatter plot y tarda unos 300ms (si MILI-segundos).

[code language=”python”]
xlist = []
ylist = []

start_time = timeit.default_timer()
for x in range(100):
for y in range(10):
xlist.append(x)
ylist.append(y)

matplotlib.pyplot.scatter(xlist, ylist)
matplotlib.pyplot.show()
end_time = timeit.default_timer()
print(end_time – start_time)
[/code]

He subido un python notebooks (jupyter notebook) a github para que lo ejecutéis vosotros si queréis.

He intentado hacer como Moneyball para predecir el resultado de los partidos de fútbol y he fracasado

Hace ya algún tiempo leí un post interesante sobre como un data scientist usó el concepto presentado en el libro Moneyball para escoger mejor los jugadores del FIFA 2016 cuando jugaba con “Career Mode”. El concepto de Moneyball es básicamente hacer data mining para conseguir el mejor equipo de béisbol con el menor precio. El argumento principal del libro es que al fichar los jugadores de béisbol las personas usan sus propios inclinaciones que normalmente no coinciden con la realidad o que están ancladas al pasado. Como ejemplo en el libro pone que a menudo el resultado de una táctica ofensiva de un jugador se puede medir mejor usando la frecuencia con que el bateador llega a la base en vez de la velocidad con la que batea. La velocidad de bateo ha sido la métrica que se ha usado des del principio pero después de un análisis detallado los analistas descubrieron que otras métricas son más relevantes.

A partir de este concepto creé mi propio experimento. La idea era que normalmente los analistas usan datos reales de los partidos para definir la habilidad del jugador. El problema que tiene esto es que lo que se luce el jugador depende del nivel del contrincante. Por eso mi idea era usar una valoración más o menos neutra y absoluta para predecir la calidad de cada jugador. Estas métricas las usaría para predecir la calidad del once inicial y así poder determinar el resultado final del partido.

Lo primero que hice fue recopilar tanta información como pude de todos los jugadores que pude. Para determinar la calidad del jugador usé la que usan en el videojuego FIFA 2015. El valor de las apuestas que se habían hecho en distintos sitios web de apuestas lo saqué de football-data. La alineación inicial y el resultado final del partido lo saqué de 11v11.

Todo el scrapping que hice puede sonar muy divertido y rápido pero no fue así. Durante el proceso pasé por alto varias cosas importantes. Lo primero es que en la liga española tenemos muchos jugadores españoles y con nombres españoles. El problema de los nombres españoles es que usan caracteres especiales como acentos o la ñ causando problemas con el encoding. El segundo problema es que el scrapping es divertido si la página web es consistente y está bien ordenada. Resultó no ser el caso. Por algún motivo que desconozco algunas de las páginas de la web no tenían un formato consistente con el resto de la web y tuve que programar excepciones. El tercer y último punto es que las webs de dónde extraje la información tenían herramientas para evitar el scrapping. Por suerte no eran muy estrictos con el número de peticiones que un mismo ordenador podía hacer y conseguí toda la información que buscaba en relativamente poco tiempo.

El resultado final fue que no funcionó. Me pareció una idea innovadora pero quizás no muy realista. El fútbol se ha caracterizado por las sorpresas. Usando sólo la información de las apuestas puedo predecir en un 50% cual será el resultado final del partido. Curiosamente con regresión lineal y toda la información de los jugadores se puede predecir también con un 50% quién será el ganador del partido. Si quitamos el empate de la ecuación todos los algoritmos que probé siguen funcionando igual de “bien”. Básicamente podría tirar una moneda en el aire y decir que cara gana el equipo local y cruz el visitante y acertaría el mismo número de veces que todos los algoritmos probados. Una pena que no funcionara pero me alegra haberlo probado.

Que formato es el mejor para guardar números con decimales (float)?

Hace ya algunos días que le estoy dando vueltas al crear un script que me cogiera determinados datos y los fuera guardando en un archivo. Como quiero guardar muchos datos durante un largo periodo de tiempo he pensado que quizás debería empezar por lo básico. Que formato de archivo es mejor para almacenar este tipo de datos.

Para averiguar que formato es el ideal para tal propósito he ideado un script en python que lo que hace es generar cuatro números aleatorios entre -10 y 10 por cada una de las 100.000 hileras disponibles (en total 400.000 valores). Luego, usando el mismo data set, he almacenado esta información usando distintos formatos. Los formatos que he usado son texto plano, CSV, TSV, JSON, SQLite y HDF5. Estos formatos son los que me han parecido adecuados para este tipo de tarea. He dejado fuera XML porque me pareció que tiene gran similitud con JSON y realmente no necesito la jerarquía ni flexibilidad que este formato me ofrece.

Véase que el mismo script indica el tamaño de cada fichero. El tiempo de ejecución es de unos 65 segundos en mi laptop.

El script:

[code language=”python”]

import numpy as np
import os
from os import listdir
from os.path import isfile, join

import json
import csv
import sqlite3
import h5py #pip install h5py

#generated 4 rows with number_of_floats data
def data_generation(number_of_floats):
ret = np.ndarray((number_of_floats, 4))
for i in range(number_of_floats):
ret[i] = np.random.uniform( -10, 10, 4 )
return ret

#standard text saving
def save_text_file(data):
f = open(‘text.txt’, ‘w’)
for el in data:
f.write(str(el).strip(‘[]’))
f.write(‘\n’)
f.close()
return True

#saving with json files
def save_json(data):
f = open(‘json.json’, ‘w’)
j=json.dumps(data.tolist())
json.dump(j, f)
f.close()
return True

#saving numbers on csv
def save_csv(data):
with open(‘csv.csv’, ‘w’) as csvfile:
fieldnames = [‘Col_A’, ‘Col_B’, ‘Col_C’, ‘Col_D’]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
for el in data:
writer.writerow({‘Col_A’: str(el[0]), ‘Col_B’: str(el[1]),’Col_C’: str(el[2]),’Col_D’: str(el[3])})
return True

#Tab Separated Values (TSV)
def save_tsv(data):
with open(‘tsv.tsv’, ‘w’) as tsvfile:
writer = csv.writer(tsvfile, delimiter=’\t’)
for el in data:
writer.writerow(el)
return True

#save data in sqlite
def save_sqlite(data):
conn = sqlite3.connect(‘data.sqlite3’)
cur = conn.cursor()
cur.execute(‘DROP TABLE IF EXISTS Data ‘)
cur.execute(‘CREATE TABLE Data (Col_A REAL, Col_B REAL, Col_C REAL, Col_D REAL)’)
for el in data:
cur.execute(‘INSERT INTO Data VALUES (‘+str(el[0])+’, ‘+str(el[1])+’, ‘+str(el[2])+’, ‘+str(el[3])+’)’)
conn.commit()
conn.close()

#save the data in a hdf file
def save_hdf(data):
h = h5py.File(‘data.hdf5’, ‘w’)
dset = h.create_dataset(‘data’, data=data)

#check the file size
def show_file_size():
mypath = ‘.’
onlyfiles = [ f for f in listdir(mypath) if isfile(join(mypath,f)) ]
for fil in onlyfiles:
statinfo = os.stat(fil)
print ‘name: ‘+str(fil)
print ‘size: ‘+str(statinfo.st_size)
print ‘ ‘
print onlyfiles

##############################
#
# Main
#
##############################

data = data_generation(100000)
save_text_file(data)
save_csv(data)
save_json(data)
save_csv(data)
save_tsv(data)
save_sqlite(data)
save_hdf(data)
show_file_size()
print ‘Done’

[/code]

Finalmente los resultados:

data-format-comparsion-plot

Formato Tamaño (Bytes)
Texto plano 4806060
CSV 5900742
TSV 7901330
JSON 8109034
SQLite 4468736
HDF5 3202144

Como se puede observar HDF5 es el ganador claramente siendo casi un 30% menor en tamaño que Sqlite que está ocupando el segundo lugar. No es de extrañar puesto que HDF es un formato de fichero diseñado especialmente para organizar y almacenar grandes cantidades de datos (ideado para supercomputadores). Para nuestra suerte las librerías tienen licencia BSD permitiendo mejoras y creación aplicaciones por parte de terceros. Lo que no me esperaba es que TSV obtuviera un tamaño similar a JSON y no a CSV. Sinceramente pensaba que CSV y TSV eran básicamente lo mismo.

Bonus: Para los interesados el script está en github.

Forzar GPy para que muestre el gráfico (plot)

Recientemente he estado programando con Gaussian processes framework in python (GPy) y no conseguía mostrar a través de un gráfico los resultados obtenidos. El problema es que al finalizar la ejecución del script la ventana con el gráfico también se cerraba automáticamente. Para solucionar esto se tiene que forzar la librería matplotlib que bloquee la ventana para así poder visualizar los datos. Si ubicamos la siguiente linea después del plot nos congelará la ventana evitando que se cierre al finalizar el la ejecución.

matplotlib.pylab.show(block=True)