Taller de Python - Estadística en Física Experimental - 1er día

Esta presentación/notebook está disponible:
Repositorio Github FIFA BsAs (para descargarlo, usen el botón raw o hagan un fork del repositorio)
Página web de talleres FIFA BsAs

Programar ¿con qué se come?

Programar es dar una lista de tareas concretas a la computadora para que haga. Esencialmente, una computadora sabe:

  • Leer datos
  • Escribir datos
  • Transformar datos

Y nada más que esto,. Así, la computadora pasa a ser suna gran gran calculadora que permite hacer cualquier tipo de cuenta de las que necesitemos dentro de la Física (y de la vida también) mientras sepamos cómo decirle a la máquina qué cómputos hacer.

Pero, ¿qué es Python?

Python es un lenguaje para hablarle a la computadora, que se denominan lenguajes de programación. Este lenguaje, que puede ser escrito y entendido por la computadora debe ser transformado a un lenguaje que entieda la computadora (o un intermediario, que se denomina maquina virtual) así se hacen las transformaciones. Todo este modelo de programación lo podemos ver esquematizado en la figura siguiente

Historia

Python nació en 1991, cuando su creador Guido Van Rossum lo hizo público en su versión 0.9. El lenguaje siempre buscó ser fácil de aprender y poder hacer tareas de todo tipo. Es fácil de aprender por su sintaxis, el tipado dinámico (que vamos a ver de que se trata) y además la gran cantidad de librerías/módulos para todo.

Herramientas para el taller

Para trabajar vamos a usar algún editor de texto (recomendamos Visual Studio Code, que viene con Anaconda), una terminal, o directamente el editor Spyder (que pueden buscarlo en las aplicaciones de la computadora si instalaron Anaconda o si lo instalaron en la PC del aula). También, si quieren podemos trabajar en un Jupyter Notebook, que permite hacer archivos como este (y hacer informes con código intercalado)

Esto es a gusto del consumidor, sabemos usar todas esas herramientas. Cada una tiene sus ventajas y desventajas:

  • Escribir y ejecutar en consola no necesita instalar nada más que Python. Aprender a usar la consola da muchos beneficios de productividad
  • El editor o entorno de desarrollo al tener más funcionalidad es más pesado, y probablemente sea más caro (Pycharm, que es el entorno de desarrollo más completo de Python sale alrededor de 200 dolares... auch)
  • Jupyter notebook es un entorno muy interactivo, pero puede traer problemas en la lógica de ejecución. Hay que tener cuidado

Para instalar Python, conviene descargarse Anaconda. Este proyecto corresponde a una distribución de Python, que al tener una interfaz grafica amigable y manejador de paquetes llamado conda te permite instalar todas las librerías científicas de una. En Linux y macOS instalar Python sin Anaconda es más fácil, en Windows diría que es una necesidad sin meterse en asuntos oscuros de compilación (y además que el soporte en Windows de las librerías no es tan amplio).

Existe un proyecto llamado pyenv que en Linux y macOS permite instalar cualquier versión de Python. Si lo quieren tener (aunque para empezar Anaconda es mejor) pregunte que lo configuramos rápidamente.

Datos, memoria y otras yerbas

Para hacer cuentas, primero necesitamos el medio para guardar o almacenar los datos. El sector este se denomina memoria. Nuestros datos se guardan en espacios de memoria, y esos espacios tienen un nombre, un rótulo con el cual los podremos llamar y pedirle a la computadora que los utilice para operar con ellos, los modifique, etc.

Como esos espacios son capaces de variar al avanzar los datos llegamos a llamarlos variables, y el proceso de llenar la variable con un valor se denomina asignación, que en Python se corresponde con el "=".

Hasta ahora sólo tenemos en la cabeza valores numéricos para nuestras variables, considerando la analogía de la super-calculadora. Pero esto no es así, y es más las variables en Python contienen la información adicional del tipo de dato. Este tipo de dato determina las operaciones posibles con la variable (además del tamaño en memoria, pero esto ya era esperable del mismo valor de la variable).

Veamos un par de ejemplos


In [1]:
x = 5
y = 'Hola mundo!'
z = [1,2,3]

Aquí hemos guardado en un espacio de memoria llamado por nosotros "x" la información de un valor de tipo entero, 5, en otro espacio de memoria, que nosotros llamamos "y" guardamos el texto "Hola mundo!". En Python, las comillas indican que lo que encerramos con ellas es un texto. x no es un texto, así que Python lo tratará como variable para manipular. "z" es el nombre del espacio de memoria donde se almacena una lista con 3 elementos enteros.

Podemos hacer cosas con esta información. Python es un lenguaje interpretado (a diferencia de otros como Java o C++), eso significa que ni bien nosotros le pedimos algo a Python, éste lo ejecuta. Así es que podremos pedirle por ejemplo que imprima en pantalla el contenido en y, el tipo de valor que es x (entero) entre otras cosas.


In [2]:
print(y)
print(type(x))
print(type(y), type(z), len(z))


Hola mundo!
<class 'int'>
<class 'str'> <class 'list'> 3

Vamos a utilizar mucho la función type() para entender con qué tipo de variables estamos trabajando. type() es una función predeterminada por Python, y lo que hace es pedir como argumento (lo que va entre los paréntesis) una variable y devuelve inmediatamente el tipo de variable que es.

Ejercicio 1

En el siguiente bloque cree las variables "dato1" y "dato2" y guarde en ellas los textos "estoy programando" y "que emocion!". Con la función type() averigue qué tipo de datos se almacena en esas variables.


In [3]:
# Realice el ejercicio 1

Para las variables integers(enteros) y floats (flotantes) podemos hacer las operaciones matemáticas usuales y esperables. Veamos un poco las compatibilidades entre estos tipos de variables.


In [4]:
a = 5
b = 7
c = 5.0
d = 7.0
print(a+b, b+c, a*d, a/b, a/d, c**2)


12 12.0 35.0 0.7142857142857143 0.7142857142857143 25.0

Ejercicio 2

Calcule el resultado de $$ \frac{(2+7.9)^2}{4^{7.4-3.14*9.81}-1} $$ y guárdelo en una variable


In [5]:
# Realice el ejercicio 2. El resultado esperado es -98.01

Listas, tuplas y diccionarios

Las listas son cadenas de datos de cualquier tipo, unidos por estar en una misma variable, con posiciones dentro de esa lista, con las cuales nosotros podemos llamarlas. En Python, las listas se enumeran desde el 0 en adelante.

Estas listas también tienen algunas operaciones que le son válidas.

Distintas son las tuplas. Las listas son editables (en jerga, mutables), pero las tuplas no (inmutables). Esto es importante cuando, a lo largo del desarrollo de un código donde necesitamos que ciertas cosas no cambien, no editemos por error valores fundamentales de nuestro problema a resolver.


In [6]:
lista1 = [1, 2, 'saraza']
print(lista1, type(lista1))
print(lista1[1], type(lista1[1]))
print(lista1[2], type(lista1[2]))
print(lista1[-1])


[1, 2, 'saraza'] <class 'list'>
2 <class 'int'>
saraza <class 'str'>
saraza

In [7]:
lista2 = [2,3,4]
lista3 = [5,6,7]
#print(lista2+lista3)
print(lista2[2]+lista3[0])


9

In [8]:
tupla1 = (1,2,3)
lista4 = [1,2,3]
lista4[2] = 0
print(lista4)
#tupla1[0] = 0
print(tupla1)


[1, 2, 0]
(1, 2, 3)

Hay formas muy cómodas de hacer listas. Presentamos una que utilizaremos mucho, que es usando la función range. Esta devuelve como una receta de como hacer los numeros; por lo tanto tenemos que decirle al generador que cree la lista, por medio de otra herramienta incorporada de Python, list


In [9]:
listilla = list(range(10))
print(listilla, type(listilla))


[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <class 'list'>

Cómo en general no se hace seguido esto, no existe una forma "rápida" o "más elegante" de hacerlo.

Ejercicio 3

  1. Haga una lista con los resultados de los últimos dos ejercicios y que la imprima en pantalla
  • Sobreescriba en la misma variable la misma lista pero con sus elementos permutados e imprima nuevamente la lista

Ejemplo de lo que debería mostrarse en pantalla

['estoy programando', 'que emocion!', -98.01]

['estoy programando', -98.01, 'que emocion!']


In [10]:
# Realice el ejercicio 3

Ejercicio 4

  1. Haga una lista con la función range de 15 elementos y sume los elementos 5, 10 y 12
  • Con la misma lista, haga el producto de los primeros 4 elementos de esa lista

  • Con la misma lista, reste el último valor con el primero


In [11]:
# Realice el ejercicio 4

Ahora, el titulo hablaba de diccionarios... pero no son los que usamos para buscar el significado de las palabras. ¡Aunque pueden ser parecidos o funcionar igual!.

Un diccionario es un relación entre una variable llamada llave y otra variable llamado valor. Relación en el sentido de función que veíamos en el secundario, pero usualmente de forma discreta.

La magia es que sabiendo la llave, o key, ya tienes el valor, o value, por lo que podés usarlo como una lista pero sin usar indices si no cosas como cadenas. Las keys son únicas, y si quiero crear un diccionario con las mismas keys se van a pisar y queda la última aparición

Veamos un ejemplo


In [12]:
d = {"hola": 1, "mundo": 2, 0: "numero", (0, 1): ["tupla", 0, 1]}  # Las llaves pueden ser casi cualquier cosa (lista no)

print(d, type(d))

print(d["hola"])
print(d[0])
print(d[(0, 1)])

# Podés setear una llave (o key) vieja
d[0] = 10

# O podes agregar una nueva. El orden de las llaves no es algo en qué confiar necesariamente, para eso está OrderedDict
d[42] = "La respuesta"

# Cambiamos el diccionario, así que aparecen nuevas keys y cambios de values
print(d)

# Keys repetidas terminan siendo sobreescritas
rep_d = {0: 1, 0: 2}
print(rep_d)

# Otra cosas menor, un diccionario vacío es
empt_d = {}
print(empt_d)


{'hola': 1, 'mundo': 2, 0: 'numero', (0, 1): ['tupla', 0, 1]} <class 'dict'>
1
numero
['tupla', 0, 1]
{'hola': 1, 'mundo': 2, 0: 10, (0, 1): ['tupla', 0, 1], 42: 'La respuesta'}
{0: 2}
{}

Es particularmente mágico el diccionario y lo podes usar para muchisimas cosas (y además Python lo usa para casi todo internamente, así que está muy bueno saber usarlos!).

El largo de un diccionario es la cantidad de keys que tiene, por ejemplo


In [13]:
new_d = {0: '0', '0': 0}

print(len(new_d))

# Diccionario vacío
print(len({}))


2
0

Ejercicio 5

Haga un diccionario con tal que con el siguiente código

print(tu_dict[1] + tu_dict["FIFA"] + tu_dict[(3,4)])

Imprima "Programador, hola mundo!". Puede tener todas las entradas que quieras, no hay limite de la creatividad acá


In [14]:
# Realice el ejercicio 5

# Descomente esta línea y a trabajar
# print(tu_dict[1] + tu_dict["FIFA"] + tu_dict[(3,4)])

Booleans

Este tipo de variable tiene sólo dos valores posibles: 1 y 0, o True y False. Las utilizaremos escencialmente para que Python reconozca relaciones entre números.


In [15]:
print(5 > 4)
print(4 > 5)
print(4 == 5) #La igualdad matemática se escribe con doble ==
print(4 != 5) #La desigualdad matemática se escribe con !=
print(type(4 > 5))


True
False
False
True
<class 'bool'>

También podemos comparar listas, donde todas las entradas deberíán ser iguales


In [16]:
print([1, 2, 3] == [1, 2, 3])
print([1, 2, 3] == [1, 3, 2])


True
False

Lo mismo para tuplas (y aplica para diccionarios)


In [17]:
print((0, 1) == (0, 1))
print((1, 3) == (0, 3))


True
False

Con la función id() podemos ver si dos variables apuntan a la misma dirección de memoria, es decir podemos ver si dos variables tienen exactamente el mismo valor (aunque sea filosófico, en Python la diferencia es importante)


In [18]:
a = 5
b = a
print(id(a) == id(b))

a = 12  # Reutilizamos la variable, con un nuevo valor
b = 12 
print(id(a) == id(b))  # Python cachea números de 16bits

a = 66000
b = 66000
print(id(a) == id(b)) 

# No cachea listas, ni strings
a = [1, 2, 3]
b = [1, 2, 3]
print(id(a) == id(b))

a = "Python es lo más"
b = "Python es lo más"
print(id(a) == id(b))


True
True
False
False
False

Las listas, tuplas y diccionarios también pueden devolver booleanos cuando se le pregunta si tiene o no algún elemento. Los diccionarios trabajaran sobre las llaves y las listas/tuplas sobre sus indices/valores


In [19]:
nueva_l = [0, 42, 3]
nueva_t = (2.3, 4.2); 
nuevo_d = {"0": -4, (0, 1): "tupla"}

# La frase es 
# >>> x in collection
# donde collection es una tupla, lista o diccionario. Parece inglés escrito no?
print(42 in nueva_l)
print(3 in nueva_t)
print((0,1) in nuevo_d)


True
False
True

Ejercicio 6

Averigue el resultado de 4!=5==1. ¿Dónde pondría paréntesis para que el resultado fuera distinto?


In [20]:
# Realice el ejercicio 5

Control de flujo: condicionales e iteraciones (if y for para los amigos)

Si en el fondo un programa es una serie de algoritmos que la computadora debe seguir, un conocimiento fundamental para programar es saber cómo pedirle a una computadora que haga operaciones si se cumple una condición y que haga otras si no se cumple. Nos va a permitir hacer programas mucho más complejos. Veamos entonces como aplicar un if.


In [21]:
parametro = 5
if parametro > 0: # un if inaugura un nuevo bloque indentado
    print('Tu parametro es {} y es mayor a cero'.format(parametro))
    print('Gracias')
else:            # el else inaugura otro bloque indentado
    print('Tu parametro es {} y es menor o igual a cero'.format(parametro))
    print('Gracias')
print('Vuelva pronto')
print(' ')


Tu parametro es 5 y es mayor a cero
Gracias
Vuelva pronto
 

In [22]:
parametro = -5
if parametro > 0: # un if inaugura un nuevo bloque indentado
    print('Tu parametro es {} y es mayor a cero'.format(parametro))
    print('Gracias')
else:            # el else inaugura otro bloque indentado
    print('Tu parametro es {} y es menor o igual a cero'.format(parametro))
    print('Gracias')
print('Vuelva pronto')
print(' ')


Tu parametro es -5 y es menor o igual a cero
Gracias
Vuelva pronto
 

Ejercicio 7

Haga un programa con un if que imprima la suma de dos números si un tercero es positivo, y que imprima la resta si el tercero es negativo.


In [23]:
# Realice el ejercicio 7

Para que Python repita una misma acción n cantidad de veces, utilizaremos la estructura for. En cada paso, nosotros podemos aprovechar el "número de iteración" como una variable. Eso nos servirá en la mayoría de los casos.


In [24]:
nueva_lista = ['nada',1,2,'tres', 'cuatro', 7-2, 2*3, 7/1, 2**3, 3**2]
for i in range(10):       # i es una variable que inventamos en el for, y que tomará los valores de la 
    print(nueva_lista[i])   #lista que se genere con range(10)


nada
1
2
tres
cuatro
5
6
7.0
8
9

Ejercicio 8

  1. Haga otra lista con 16 elementos, y haga un programa que con un for imprima solo los primeros 7
  • Modifique el for anterior y haga que imprima solo los elementos pares de su lista

In [25]:
# Realice el ejercicio 8

La estructura while es poco recomendada en Python pero es importante saber que existe: consiste en repetir un paso mientras se cumpla una condición. Es como un for mezclado con un if.


In [26]:
i = 1
while i < 10: # tener cuidado con los while que se cumplen siempre. Eso daría lugar a los loops infinitos.
    i = i+1
    print(i)


2
3
4
5
6
7
8
9
10

Ejercicio 9

  1. Calcule el factorial de N, siendo N la única variable que recibe la función (Se puede pensar usando for o usando while).
  • Calcule la sumatoria de los elementos de una lista.

In [27]:
# Realice el ejercicio 8

Funciones

Pero si queremos definir nuestra propia manera de calcular algo, o si queremos agrupar una serie de órdenes bajo un mismo nombre, podemos definirnos nuestras propias funciones, pidiendo la cantidad de argumentos que querramos.

Vamos a usar las funciones lambda (también llamadas anonimas) más que nada para funciones matemáticas, aunque también tenga otros usos. Definamos el polinomio $f(x) = x^2 - 5x + 6$ que tiene como raíces $x = 3$ y $x = 2$.


In [28]:
f = lambda x: x**2 - 5*x + 6
print(f(3), f(2), f(0))


0 0 6

Las funciones lambda son necesariamente funciones de una sola linea y también tienen que retornar nada; por eso son candidatas para expresiones matemáticas simples.

Las otras funciones, las más generales, se las llama funciones def, y tienen la siguiente forma.


In [29]:
def promedio(a,b,c):
    N = a + b + c # Es importante que toda la función tenga su contenido indentado
    N = N/3.0
    return N
mipromedio = promedio(5,5,7) # Aquí rompimos la indentación
print(mipromedio)


5.666666666666667

Algo muy interesante y curioso, es que podemos hacer lo siguiente con las funciones


In [30]:
def otra_funcion(a, b):
    return a + b * 2

# Es un valor!
otra_f = otra_funcion
print(otra_f)
print(type(otra_f))

print(otra_f(2, 3))


<function otra_funcion at 0x7f23f85f42f0>
<class 'function'>
8

Las funciones pueden ser variables y esto abre la puerta a muchas cosas. Si tienen curiosidad, pregunten que está re bueno esto!

Ejercicio 10

Hacer una función que calcule el promedio de $n$ elementos dados en una lista.

Sugerencia: utilizar las funciones len() y sum() como auxiliares.


In [31]:
# Realice el ejercicio 9

Ejercicio 11

Usando lo que ya sabemos de funciones matemáticas y las bifurcaciones que puede generar un if, hacer una función que reciba los coeficientes $a, b, c$ de la parábola $f(x) = ax^2 + bx + c$ y calcule las raíces si son reales (es decir, usando el discriminante $\Delta = b^2 - 4ac$ como criterio), y sino que imprima en pantalla una advertencia de que el cálculo no se puede hacer en $\mathbb{R}$.


In [32]:
# Realice el ejercicio 10

Bonus track 1

Modificar la función anterior para que calcule las raíces de todos modos, aunque sean complejas. Python permite usar números complejos escritos de la forma 1 + 4j. Investiguen un poco


In [33]:
# Bonus track 1

Ejercicio 12

Repitan el ejercicio 8, es decir

  1. Hacer una función que calcule el factorial de N, siendo N la única variable que recibe la función (Se puede pensar usando for o usando while).
  • Hacer una función que calcule la sumatoria de los elementos de una lista.

¿Se les ocurre otra forma de hacer el factorial? Piensen la definición matemática y escribanla en Python, y prueben calcular el factorial de 100 con esta definición nueva


In [34]:
# Realice el ejercicio 12

Paquetes y módulos

Pero las operaciones básicas de suma, resta, multiplicación y división son todo lo que un lenguaje como Python puede hacer "nativamente". Una potencia o un seno es álgebra no lineal, y para hacerlo, habría que inventarse un algoritmo (una serie de pasos) para calcular por ejemplo sen($\pi$). Pero alguien ya lo hizo, ya lo pensó, ya lo escribió en lenguaje Python y ahora todos podemos usar ese algoritmo sin pensar en él. Solamente hay que decirle a nuestro intérprete de Python dónde está guardado ese algoritmo. Esta posibilidad de usar algoritmos de otros es fundamental en la programación, porque es lo que permite que nuestro problema se limite solamente a entender cómo llamar a estos algoritmos ya pensados y no tener que pensarlos cada vez.

Vamos entonces a llamar a un paquete (como se le llama en Python) llamada math que nos va a extender nuestras posibilididades matemáticas.


In [35]:
import math # Llamamos a una biblioteca

r1 = math.pow(2,4)
r2 = math.cos(math.pi)
r3 = math.log(100,10)
r4 = math.log(math.e)

print(r1, r2, r3, r4)


16.0 -1.0 2.0 1.0

Para entender cómo funcionan estas funciones, es importante recurrir a su documentation. La de esta biblioteca en particular se encuentra en

https://docs.python.org/2/library/math.html

Ejercicio 13

Use Python como calculadora y halle los resultados de

  1. $\log(\cos(2\pi))$
  • $\text{atanh}(2^{\cos(e)} -1) $
  • $\sqrt{x^2+2x+1}$ con $x = 125$

In [36]:
# Realice el ejercicio 13

Crear bibliotecas

Bueno, ahora que sabemos como usar bibliotecas, nos queda saber cómo podemos crearlas. Pero para saber eso, tenemos que saber que es un módulo en Python y cómo se relaciona con un paquete.

Se le llama módulo a los archivos de Python, archivos con la extensión *.py, como por ejemplo taller_python.py (como tal vez algunos hicieron ya). En este archivo se agregan funciones, variables, etc, que pueden ser llamadas desde otro módulo con el nombre sin la extensión, es decir


In [37]:
import taller_python  # Vean el repositorio!

Python para buscar estos módulos revisa si el módulo importado (con el comando import) está presente en la misma carpeta del que importa y luego en una serie de lugares estándares de Python (que se pueden alterar y revisar usando sys.path, importando el paquete sys). Si lo encuentra lo importa y podés usar las funciones, y si no puede salta una excepción


In [38]:
print(taller_python.func(5, 6))

# Veamos la documentación
help(taller_python.func)


25
Help on function func in module taller_python:

func(x, y)
    Documentación de la función. Hace x * y - 5

Traten de importar la función __func_oculta. Se puede, pero es un hack de Python y la idea es que no sepa de ella. Es una forma de ocultar y encapsular código, que es uno de los principios de la programación orientada a objetos.

Finalmente, un paquete como math es un conjunto de módulos ordenados en una carpeta con el nombre math, con un archivo especial __init__.py, que hace que la carpeta se comporte como un módulo. Python importa lo que vea en el archivo __init__.py y permite además importar los módulos dentro (o submodulos), si no tienen guiones bajos antes.

Usualmente no es recomendable trabajar en el __init__.py, salvo que se tenga una razón muy necesaria (o simplemente vagancia)

Ejercicio 14

Creen una libraría llamada mi_taller_python y agregen dos funciones, una que devuelva el resultado de $\sqrt{x^2+2x+1}$ para cualquier x y otra que resuelva el resultado de $(x^2+2x+1)^{y}$, para cualquier x e y. Hagan todas las funciones ocultas que requieran (aunque recomendamos siempre minimzarlas)


In [39]:
# Realice el ejercicio 14

Bonus track 2

Ahora que nos animamos a buscar nuevas bibliotecas y definir funciones, buscar la función newton() de la biblioteca scipy.optimize para hallar $x$ tal que se cumpla la siguiente ecuación no lineal $$\frac{1}{x} = ln(x)$$


In [40]:
#Acá va el bonus track 2, para ya saborear la próxima clase

Con esto terminamos la primera sesión del taller! Para la próxima vamos a aprender a manejar muchos datos al mismo tiempo, graficarlos y crear datos estadísticos, usando un par de librerías especificas del set científico de Python (numpy, scipy y matplotlib).

En nuestro repositorio en Github (https://github.com/fifabsas/talleresfifabsas) está colgado este material así como el de la próxima clase. Además tiene ejemplos (hasta de automatización de instrumental) y otras instancias de talleres que hemos dado a traves del tiempo.

Recursos

Para seguir profundizando con la programación en Python, ofrecemos distintos recursos

Un tutorial: http://www.learnpython.org/

How to think like a computer scientist (aprendizaje interactivo): http://interactivepython.org/runestone/static/thinkcspy/index.html

Otro tutorial, en inglés, pero muy completo: http://learnpythonthehardway.org/book

Coursera, que nunca está de más: https://www.coursera.org/learn/interactive-python-1

Otro más: https://es.coursera.org/learn/python

Y por fuera del taller, seguimos en contacto. Tenemos un grupo de Facebook donde pueden hacerse consultas y otros chicos que fueron al taller antes o aprendieron por sus medios podrán responderles. El grupo es https://www.facebook.com/groups/303815376436624/?fref=ts

Agradecimientos

Todo esto es posible gracias al aporte de mucha gente.

  • A los docentes de la materia, por darnos el espacio para ayudar y que se lleve a cabo este taller.
  • Gente muy copada del DF como Hernán Grecco, Guillermo Frank y Agustín Corbat por hacer aportes a estos talleres de diferentes maneras, desde poner su apellido para que nos presten un labo hasta venir como invitado a un taller.
  • El Departamento de Computación que cuatrimestre a cuatrimestre nos presta los labos desinteresadamente.
  • Pibes de la FIFA que prestan su tiempo a organizar el material y llevan a cabo el taller.
  • Todos los que se acercan y piden que estos talleres se sigan dando y nos siguen llenando los Labos. Sí ¡Gracias a todos ustedes!