├── 8_gestion_entornos
└── README.md
├── .gitignore
├── 7_frontend
├── 1_introduccion_html
│ ├── cv.html
│ └── README.md
├── 3_disposicion
│ ├── cv_ejemplo.pdf
│ ├── README.md
│ └── cv_base.html
├── 2_introduccion_css
│ ├── cv_ejemplo.pdf
│ ├── cv_base.html
│ ├── cv_con_estilo.html
│ └── README.md
└── 4_integracion
│ └── README.md
├── 3_ciencia_de_datos
├── 4_clustering
│ ├── carrera.jpeg
│ ├── k_means.png
│ ├── dist_euclídea.gif
│ ├── iris_data.txt
│ └── Clustering.md
├── 2_limpieza_de_datos
│ └── heat_map_null.jpeg
├── 3_introducción_a_aprendizaje_de_maquina
│ └── Introducción_ML.md
└── 1_introducción_a_pandas
│ └── Introducción_pandas.md
├── 9_testing
├── 1_introduccion
│ ├── descuento.py
│ ├── test_descuento.py
│ └── README.md
└── 2_integracion
│ └── recomendaciones.py
├── Gemfile
├── README.md
├── 404.html
├── _config.yml
├── 4_programacion_con_objetos
├── 1_introduccion
│ └── aves.py
├── 5_integracion
│ └── README.md
├── 6_integracion
│ └── README.md
├── 3_integracion
│ └── README.md
└── 2_delegacion_y_polimorfismo
│ └── README.md
├── 2_introducción_al_análisis_de_datos
├── 3_práctica_tablas_filas_y_columnas
│ └── README.md
├── 4_práctica_listas_y_tablas
│ └── README.md
├── 5_práctica_agregaciones
│ └── README.md
├── 2_filas_y_columnas
│ └── README.md
└── 1_tablas
│ └── README.md
├── 6_requests
└── HTTP_&_Url.md
├── 5_entrada_salida
├── 1_manejo_de_archivos
│ ├── manipulacion_archivos.txt
│ └── Manipulación_de_archivos.md
└── 2_os_rutas
│ └── os_rutas.md
├── index.md
├── 0_presentacion_general
└── 2_entorno_local
│ └── README.md
├── 1_introduccion_a_la_programacion
├── 17_excepciones
│ └── README.md
├── 05_práctica_lógica_booleana
│ └── README.md
├── 09_práctica_variables_y_procedimientos
│ └── README.md
├── 12_práctica_repeticion
│ └── README.md
├── 15_integración
│ ├── referencia_rápida_python
│ │ └── README.md
│ └── referencia_rápida_pandas
│ │ └── README.md
├── 18_integracion
│ └── README.md
├── 03_práctica_funciones_y_parámetros
│ └── README.md
├── 07_práctica_alternativa_condicional
│ └── README.md
└── 16_expresiones_regulares
│ └── README.md
└── Gemfile.lock
/8_gestion_entornos/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | _site
3 | .sass-cache
4 | .jekyll-metadata
5 | .pytest_cache
6 | .vscode
--------------------------------------------------------------------------------
/7_frontend/1_introduccion_html/cv.html:
--------------------------------------------------------------------------------
1 |
Feli Pérez
2 | Docente
3 |
4 | Me gusta programar. Contratame
5 |
--------------------------------------------------------------------------------
/7_frontend/3_disposicion/cv_ejemplo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flbulgarelli/recursos-python/HEAD/7_frontend/3_disposicion/cv_ejemplo.pdf
--------------------------------------------------------------------------------
/3_ciencia_de_datos/4_clustering/carrera.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flbulgarelli/recursos-python/HEAD/3_ciencia_de_datos/4_clustering/carrera.jpeg
--------------------------------------------------------------------------------
/3_ciencia_de_datos/4_clustering/k_means.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flbulgarelli/recursos-python/HEAD/3_ciencia_de_datos/4_clustering/k_means.png
--------------------------------------------------------------------------------
/7_frontend/2_introduccion_css/cv_ejemplo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flbulgarelli/recursos-python/HEAD/7_frontend/2_introduccion_css/cv_ejemplo.pdf
--------------------------------------------------------------------------------
/3_ciencia_de_datos/4_clustering/dist_euclídea.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flbulgarelli/recursos-python/HEAD/3_ciencia_de_datos/4_clustering/dist_euclídea.gif
--------------------------------------------------------------------------------
/3_ciencia_de_datos/2_limpieza_de_datos/heat_map_null.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flbulgarelli/recursos-python/HEAD/3_ciencia_de_datos/2_limpieza_de_datos/heat_map_null.jpeg
--------------------------------------------------------------------------------
/7_frontend/3_disposicion/README.md:
--------------------------------------------------------------------------------
1 | * Repaso tags y el modelo de cajas
2 | * nuevo tag: `main`
3 | * Cuando la semántica de html no alcanza: `divs` y clases
4 | * Disposición: `flex`
5 | * `display: flex`
6 | * `flex-direction`
7 | * `flex-basis`, `flex-grow`
--------------------------------------------------------------------------------
/9_testing/1_introduccion/descuento.py:
--------------------------------------------------------------------------------
1 | def aplicar_descuento_2x1(cantidad, precio_base):
2 | """
3 | Aplica el descuento 2 X 1 a un precio: los pares de productos los cobra a la mitad de precio
4 | """
5 | if cantidad % 2 == 0:
6 | return precio_base * cantidad / 2
7 | else:
8 | return precio_base * (1 + (cantidad - 1) / 2)
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "github-pages", "~> 217", group: :jekyll_plugins
4 |
5 | gem "jekyll-theme-minimal", "~> 0.2"
6 |
7 | install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do
8 | gem "tzinfo", "~> 1.2"
9 | gem "tzinfo-data"
10 | end
11 | gem "wdm", "~> 0.1.0", :install_if => Gem.win_platform?
12 | gem "kramdown-parser-gfm"
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![CC BY-SA 4.0][cc-by-sa-shield]][cc-by-sa]
2 |
3 | # recursos-python
4 |
5 |
6 | This work is licensed under a
7 | [Creative Commons Attribution-ShareAlike 4.0 International License][cc-by-sa].
8 |
9 | [![CC BY-SA 4.0][cc-by-sa-image]][cc-by-sa]
10 |
11 | [cc-by-sa]: http://creativecommons.org/licenses/by-sa/4.0/
12 | [cc-by-sa-image]: https://licensebuttons.net/l/by-sa/4.0/88x31.png
13 | [cc-by-sa-shield]: https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg
14 |
15 |
16 |
--------------------------------------------------------------------------------
/404.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
18 |
19 |
20 |
404
21 |
22 |
Page not found :(
23 |
The requested page could not be found.
24 |
25 |
--------------------------------------------------------------------------------
/9_testing/1_introduccion/test_descuento.py:
--------------------------------------------------------------------------------
1 | from descuento import *
2 |
3 | def test_descuento_cuando_la_cantidad_es_cero():
4 | assert aplicar_descuento_2x1(0, 450) == 0
5 |
6 | def test_el_precio_es_100_cuando_la_cantidad_es_2_y_precio_base_es_100():
7 | assert aplicar_descuento_2x1(2, 100) == 100
8 |
9 | def test_el_precio_es_300_cuando_la_cantidad_es_4_y_precio_base_es_150():
10 | assert aplicar_descuento_2x1(4, 150) == 300
11 |
12 | def test_el_precio_es_200_cuando_la_cantidad_es_1_y_el_precio_base_es_200():
13 | assert aplicar_descuento_2x1(1, 200) == 200
14 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | title: Recursos Python
2 | email: your-email@example.com
3 | description: >
4 | Recursos libres para aprender a programar en español
5 | baseurl: "/recursos-python"
6 | url: "https://flbulgarelli.github.io"
7 | twitter_username: flbulgarelli
8 | github_username: flbulgarelli
9 | markdown: kramdown
10 | theme: jekyll-theme-minimal
11 |
12 | # Exclude from processing.
13 | # The following items will not be processed, by default. Create a custom list
14 | # to override the default setting.
15 | # exclude:
16 | # - Gemfile
17 | # - Gemfile.lock
18 | # - node_modules
19 | # - vendor/bundle/
20 | # - vendor/cache/
21 | # - vendor/gems/
22 | # - vendor/ruby/
23 |
--------------------------------------------------------------------------------
/4_programacion_con_objetos/1_introduccion/aves.py:
--------------------------------------------------------------------------------
1 | class Golondrina:
2 | def __init__(self, energia):
3 | self.energia = energia
4 |
5 | def comer_alpiste(self, gramos):
6 | self.energia = self.energia + 4 * gramos
7 |
8 | def volar_en_circulos(self):
9 | self.volar(0)
10 |
11 | def volar(self, kms):
12 | self.energia -= 10 + kms
13 |
14 | def __repr__(self) -> str:
15 | return f"<🐦 at {hex(id(self))}>"
16 |
17 | class Dragon:
18 | def __init__(self, cantidad_dientes, energia):
19 | self.energia = energia
20 | self.cantidad_dientes = cantidad_dientes
21 |
22 | def escupir_fuego(self):
23 | self.energia -= 2 * self.cantidad_dientes
24 |
25 | def comer_peces(self, unidades):
26 | self.energia += 100 * unidades
27 |
28 | def volar_en_circulos(self):
29 | self.energia -= 1
30 |
31 | def volar(self, kms):
32 | self.energia -= 10 + kms/10
33 |
34 | def __repr__(self) -> str:
35 | return f"<🔥 at {hex(id(self))}>"
36 |
37 | pepita = Golondrina(100)
38 | anastasia = Golondrina(200)
39 | roberta = Dragon(10, 1000)
40 |
--------------------------------------------------------------------------------
/2_introducción_al_análisis_de_datos/3_práctica_tablas_filas_y_columnas/README.md:
--------------------------------------------------------------------------------
1 | # Práctica Operaciones Básicas con Pandas
2 |
3 | ## 1. Buscando bibliotecas
4 |
5 | En esta práctica vamos a trabajar con datos sobre las bibliotecas (las que tienen libros, no código 😛️) de la Ciudad de Buenos Aires. ¿Dónde podremos encontrar información sobre las mismas?
6 |
7 | > Buscá en la página de [data.buenosaires.gob.ar](https://data.buenosaires.gob.ar) un dataset de bibliotecas 📚 de CABA y cargalo en un `DataFrame` usando `pandas`.
8 |
9 | ## 2. Cuento y recuento
10 |
11 | > #️⃣ Averiguá cuantas bibliotecas hay
12 |
13 | ## 3. Los 100 barrios porteños
14 |
15 | > #️⃣ Averiguá en cuántos barrios **diferentes** están estas bibliotecas
16 |
17 | ## 4. ¿Cómo se llaman?
18 |
19 | > 🔡 Averiguá los nombres de las bibliotecas y ordenalos alfabéticamente
20 |
21 | ## 5. ¿Y por el barrio cómo andamos?
22 |
23 | > #️⃣ Averiguá cuántas bibliotecas hay por barrio
24 |
25 | ## 6. Una imágen vale más que mil palabras
26 |
27 | Si bien el listado anterior nos puede arrojar información útil, no es tan cómodo de leer. ¡Sería mejor que fuera un gráfico!
28 |
29 | > 📊 Hacé un gráfico de barras que muestre cuántas bibliotecas hay por barrio. ¿Qué conclusión sacás?
--------------------------------------------------------------------------------
/7_frontend/2_introduccion_css/cv_base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Feli Pérez
4 |
5 |
6 |
7 | Feli Pérez
8 | Docente
9 |
10 |
11 |
12 | Sobre mí
13 | Me gusta programar. Contratame
14 |
15 |
16 |
17 | Educación
18 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
19 |
20 |
21 |
22 | Experiencia
23 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
24 |
25 |
26 |
--------------------------------------------------------------------------------
/3_ciencia_de_datos/3_introducción_a_aprendizaje_de_maquina/Introducción_ML.md:
--------------------------------------------------------------------------------
1 | ### Introducción a la Ciencia de Datos con Python
2 |
3 | Para este recorrido necesitarás las librerías [Sklearn](https://scikit-learn.org/stable/), [Seaborn](https://seaborn.pydata.org/) y [Scipy](https://www.scipy.org/)
4 |
5 |
6 | Podes corroborar si las tienes instaladas corriendo las siguientes líneas en tu intérprete de Python:
7 |
8 | ```python
9 | import pandas as pd
10 | import sklearn
11 | import scipy.stats as ss
12 | ```
13 |
14 | Si correr estas lineas no tira ningún error, etonces están felizmente instaladas las bibliotecas enc uestión. De lo contrario, obtendremos un mensaje de error `ModuleNotFoundError: No module named` al correr las lineas anteriores. En tal caso, podés instalar las bibliotecas desde la consola, con el comando:
15 |
16 | ```bash
17 | pip install pandas
18 | pip install sklearn
19 | pip install scipy
20 | ```
21 |
22 | # Guias de Trabajo
23 | * [1.Machine Learning ¿Con qué se come eso?](#1-Intro)
24 | * [2.Regresión lineal](#1-Intro)
25 | * [3.Anova](#1-Intro)
26 |
27 |
28 | [1.Machine Learning ¿Con qué se come eso?](#1-Intro)
29 | Machine Learning o aprendizaje automático es la identificación de patrones o tendencias que se “esconden” en los datos de forma automática. Esto se realiza mediante el uso de distintos algoritmos y su posterior parametrización para cada problemática concreta.
--------------------------------------------------------------------------------
/4_programacion_con_objetos/5_integracion/README.md:
--------------------------------------------------------------------------------
1 | # MacoWins - Entrega 4
2 |
3 | ¡Se agregan nuevos requerimientos!
4 |
5 | 1. Se agregan tres nuevos criterio de actualización de precios:
6 | 1. 💰 Por precio: queremos poder actualizar a cualquier prenda que tenga un valor menor a una cierta cantidad de dinero
7 | 2. #️⃣ Por stock: queremos poder actualizar a cualquier prenda para la que haya stock
8 | 3. 🙅♀️ Por oposición: queremos poder actualizar a cualquier prenda que NO cumpla alguno de los criterios anteriores. Por ejemplo, nos gustaría poder actualizar las prendas para las que no haya stock o que no tengan un cierto nombre. ¿Podemos resolverlo sin repetir lógica?
9 | 2. 🔍 Queremos poder _listar_ (es decir, obtener una lista con) todos los productos que cumplan cualquiera de los criterios anteriores (por nombre, por categoría, por precio, etc)
10 | 3. 👋 Queremos poder `discontinuar` los productos que correspondan de forma automática **una vez por semana**.
11 | 4. 📈 _Bonus_: queremos **una vez por día** escribir en un archivo `reporte.csv` una línea con la fecha de hoy, la cantidad de ventas del día y el monto total de dichas ventas, separado por comas. Para esto punto, recomendamos leer [una introducción al manejo de archivos](https://github.com/AJVelezRueda/Fundamentos_de_informatica/blob/master/Python_intro/Manipulaci%C3%B3n_de_archivos.md)
12 |
13 | 🧪 Nota: todos los requerimientos del punto 1 y 2 tienen que estar adecuadamente probados con `pytest`.
14 |
--------------------------------------------------------------------------------
/2_introducción_al_análisis_de_datos/4_práctica_listas_y_tablas/README.md:
--------------------------------------------------------------------------------
1 | # Práctica listas y tablas
2 |
3 | ## 1. Registrando todo
4 |
5 | > Escribí una procedimiento `registrar_cursada` que tome como parámetro el nombre de un una materia (por ejemplo _pensamiento computacional_), un día de la semana y un horario, y los almacene en las correspondientes variables globales `materias`, `dias_de_cursada` y `horarios_de_cursada`
6 |
7 |
8 | ## 2. Armando un calendario
9 |
10 | > Reescribí el procedimiento anterior para que almacene los datos de la cursada en una tabla cuyas columnas sean `materias`, `dias_de_cursada` y `horarios_de_cursada`
11 | >
12 | > Para pensar 🤔: ¿Que nombre le darías a esta variable?
13 |
14 |
15 | **Pistas**
16 |
17 | ```python
18 | una_tabla = pd.DataFrame(columns=['materias', 'dias','horario'])
19 | ```
20 |
21 | Nuestro procedimiento debría:
22 |
23 | - paso 1: generar el `una_tabla` con las materias, dias, etc que vienen de parámetro
24 | - paso 2: es _appendear_ al `una_tabla` global
25 |
26 |
27 | ## 3. ¡Otra vez sopa!
28 |
29 | > Escribí una función `materia_que_mas_se_repite` que no tome argumentos
30 | > y retorne la materia de la tabla anterior que más se repite.
31 | >
32 | > Ejemplo:
33 | >
34 | > ```python
35 | > >>> registrar_cursada("Matemática 1", "Lunes", 9)
36 | > >>> registrar_cursada("Historia del arte", "Lunes", 11)
37 | > >>> registrar_cursada("Pensamiento sistémico", "Martes", 10)
38 | > >>> registrar_cursada("Pensamiento sistémico", "Jueves", 11)
39 | > >>> materia_que_mas_se_repite()
40 | > "Pensamiento sistémico"
41 | > ```
42 |
--------------------------------------------------------------------------------
/7_frontend/2_introduccion_css/cv_con_estilo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Feli Pérez
4 |
28 |
29 |
30 |
31 | Feli Pérez
32 | Docente e Ingenierio
33 |
34 |
35 |
36 | Sobre mí
37 | Me gusta programar. Contratame
38 |
39 |
40 |
41 | Educación
42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
43 |
44 |
45 |
46 | Experiencia
47 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
48 |
49 |
50 |
--------------------------------------------------------------------------------
/6_requests/HTTP_&_Url.md:
--------------------------------------------------------------------------------
1 | # *HTTP & REST*
2 |
3 | En este [recorrido](https://github.com/flbulgarelli/recursos-python/blob/master/requests/Intro_HTTP.md) abordaremos los contenidos relativos a HTTP y REST. Para ello vas a necesitar instalarte [requests] (https://pypi.org/project/requests/):
4 |
5 | ```bash
6 | pip install requests
7 | ```
8 |
9 | Primero puedes verificar si está o no instalado escribiendo en la consola de Python:
10 | ```python
11 | import requests
12 | ```
13 |
14 | Una vez que hayas completado el recorrido de HTTP podés continuar con este tutorial 👇
15 |
16 | [Diseño de URLs](#)
17 |
18 | En general un servidor Web provee varios contenidos diferentes, es por ello que existen las URLs. Estas nos permiten identificar y localizar de forma unívoca un recurso, y dentro del servidor, encontraremos en distintas URLs relativas al mismo.
19 |
20 | En general el diseño de estas URLs se hace tal que cada ruta apunta a un recurso bien definido. La semántica de cada ruta está dada en parte por el sentido propio en el contexto de ese dado dominio, pero también por el método HTTP que se utilice.
21 |
22 | Ejemplos:
23 |
24 | - /productos/45: si se usa GET, se devuelve el producto con id 45. Si se usa DELETE, se lo borra.
25 |
26 | - /productos: si se usa GET devuelve todos los productos, si se usa PUT, se los actualiza en lote
27 |
28 | - /productos/45/ventasRecientes: si se usa GET, devuelve todas las ventas recientes del producto con id 45
29 |
30 | >
31 | > 🧗♀️ Desafío I: Estamos construyendo una aplicación Web para un biblioteca, en la cuál podrá:
32 | >- consultar y cargar o borrar información sobre libros
33 | >- consultar y cargar o borrar revistas y audio libros disponibles
34 | > Escribí las posibles rutas indicando sus métodos HTTP correspondientes.
35 | >
36 | > 🧗♀️ Desafío II: Estamos construyendo una aplicación para un e-comerce de venta de productos cosméticos naturales. La aplicación debe permitir:
37 | > - Buscar los productos por número
38 | > - Editar la información de los productos
39 | > - Eliminar la información de los productos
40 | > Escribí las posibles rutas indicando sus métodos HTTP correspondientes.
41 | >
--------------------------------------------------------------------------------
/5_entrada_salida/1_manejo_de_archivos/manipulacion_archivos.txt:
--------------------------------------------------------------------------------
1 | Miradnos.
2 | Somos la luz de nuestra propia sombra,
3 | el reflejo de la carne que nos ha acompañado,
4 | la fuerza que impulsa a las olas más minúsculas.
5 |
6 | Somos el azar de lo oportuno,
7 | la paz que termina con las guerras ajenas,
8 | dos rodillas arañadas que resisten con valentía.
9 |
10 | Miradnos.
11 | Decidimos cambiar la dirección del puño
12 | porque nosotras no nos defendemos:
13 | nosotras luchamos.
14 |
15 | Miradnos.
16 | Somos, también, dolor,
17 | somos miedo,
18 | somos un tropiezo fruto de la zancadilla de otro
19 | que pretende marcar un camino que no existe.
20 | Somos, también, una espalda torcida,
21 | una mirada maltratada, una piel obligada,
22 | pero la misma mano que alzamos
23 | abre todas las puertas,
24 | la misma boca con la que negamos
25 | hace que el mundo avance,
26 | y somos las únicas capaces de enseñar
27 | a un pájaro a volar.
28 |
29 | Miradnos.
30 | Somos música,
31 | inabarcables, invencibles, incontenibles, inhabitables,
32 | luz en un lugar que aún no es capaz de
33 | abarcarnos, vencernos, contenernos, habitarnos,
34 | porque la belleza siempre cegó los ojos
35 | de aquel que no sabía mirar.
36 |
37 | Nuestro animal es una bestia indomable
38 | que dormía tranquila hasta que decidisteis
39 | abrirle los ojos con vuestros palos,
40 | con vuestros insultos, con este desprecio
41 | que, oídnos:
42 | no aceptamos.
43 |
44 | Miradnos.
45 | Porque yo lo he visto en nuestros ojos,
46 | lo he visto cuando nos reconocemos humanas
47 | en esta selva que no siempre nos comprende
48 | pero que hemos conquistado.
49 |
50 | He visto en nosotras
51 | la armonía de la vida y de la muerte,
52 | la quietud del cielo y del suelo,
53 | la unión del comienzo y del fin,
54 | el fuego de la nieve y la madera,
55 | la libertad del sí y el no,
56 | el valor de quien llega y quien se va,
57 | el don de quien puede y lo consigue.
58 |
59 | Miradnos,
60 | y nunca olvidéis que el universo y la luz
61 | salen de nuestras piernas.
62 |
63 | Porque un mundo sin mujeres
64 | no es más que un mundo vacío y a oscuras.
65 | Y nosotras
66 | estamos aquí
67 | para despertaros
68 | y encender la mecha.
69 |
70 | - Elvira Sastre -
--------------------------------------------------------------------------------
/7_frontend/1_introduccion_html/README.md:
--------------------------------------------------------------------------------
1 | # 1. ⚽️ Antes de empezar
2 |
3 |
4 | > 🤔️ Para pensar:
5 | >
6 | > 1. ¿Qué es una red de computadoras? ¿Cómo se ve? ¿Se te viene a la mente algún elemento físico?
7 | > 2. ¿Qué es internet?
8 | > 3. ¿Qué es la Web? ¿Y una página web?
9 | > 4. ¿Qué es un navegador?
10 |
11 | > 💬️ Para discutir: ¿Y cómo estarán hechas las páginas Web? ¿Quién las hace? ¿Para qué?
12 |
13 | # 2. 💀️ El esqueleto de las páginas
14 |
15 | ¡Veamos cómo están hechas las páginas por dentro! Entrá desde tu navegador a las siguientes páginas...
16 |
17 | 1. https://google.com
18 | 1. https://mail.google.com
19 | 1. https://docs.google.com
20 | 1. https://www.argentina.gob.ar/
21 |
22 | ... y para cada una de ellas, hacé click derecho sobre algún de la misma y tocá "Ver código fuente" o "View page source", según el navegador e idioma que tengas. También podés activar este modo desde el teclado usando `CTRL` + `U` si estás en Google Chrome.
23 |
24 | > 🤔️ Para pensar:
25 | >
26 | > 1. ¿Qué ves en común entre todas estas páginas? Mirala con detenimiento para ver si observás _patrones_ en el texto que aparece.
27 | > 1. ¿Cómo lo relacionarías con la idea de _código fuente_?
28 |
29 |
30 | # 3. Alto en el camino
31 |
32 | ¡Momento! Ya Google apareció un montón de veces, pero ¿es todo lo mismo?
33 |
34 |
35 | > 🤔️ Para pensar:
36 | >
37 | > 1. ¿Qué son los buscadores? ¿Para que nos sirven?
38 | > 1. ¿Son los buscadores lo mismo que los navegadores?
39 | > 1. ¿Qué buscadores conocés? ¿Y navegadores?
40 | > 1. ¿Qué son las URLs? ¿Para qué sirven? ¿Qué URLs acabamos de ingresar?
41 |
42 | # 4. El inspector
43 |
44 | # 5. La hora del código
45 |
46 | 1. Abrir Visual Code, que usaremos como editor de código
47 | 1. Crear un archivo `cv.html`
48 | 1. Cargarle nuestro nombre y título. Por ejemplo "Feli Pérez" y "Docente"
49 | 1. Abrirlo con el navegador: presten atención donde lo guardaste.
50 | 1. Ver qué sucede. ¿Vemos el texto? ¿Cómo se ve? ¿Cómo le damos estructura?
51 | 1. Envolvamos nuestro nombre entre `` y `
` y nuestro título entre `` y `
`
52 | 1. Guardá el archivo y recargá la página. ¿Qué sucede?
53 |
54 | > 🏅️ Desafío:
55 | >
56 | > Ahora te toca a vos: agregá un párrafo con una descripción breve. Encerrarlo entre las etiquetas (_tags_) ``
57 |
58 | # 6. Profundizando un poco más
59 |
60 | 🎥️ [¿Qué es internet?](https://www.youtube.com/watch?v=-JVdH8ne-2s)
--------------------------------------------------------------------------------
/7_frontend/3_disposicion/cv_base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Feli Pérez
4 |
28 |
29 |
30 |
31 | Feli Pérez
32 | Docente e Ingenierio
33 |
34 |
35 |
36 |
37 | Resumen
38 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat
39 |
40 |
41 |
42 | Contacto
43 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
44 |
45 |
46 |
47 |
48 | Sobre mí
49 | Me gusta programar. Contratame
50 |
51 |
52 |
53 | Educación
54 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
55 |
56 |
57 |
58 | Experiencia
59 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/4_programacion_con_objetos/6_integracion/README.md:
--------------------------------------------------------------------------------
1 | # MacoWins - Entrega 4B
2 |
3 | ¡MacoWins se sumó a la fiebre por las tarjetas 💳! Desde ahora, cuando se compre un producto, MacoWins ofrecerá 3 medios de pago diferentes:
4 |
5 | 1. Efectivo: al usar este medio, el precio de la venta es el precio de lista de la prenda, es decir, sin cambios.
6 | 2. Tarjeta de crédito: aplica un 10% de recargo al precio de la prenda.
7 | 3. Tarjeta MacoWins: aplica un 10% de descuento sobre los productos de la marca Macowins.
8 |
9 | Además, la tarjeta MacoWins tiene un sistema de puntos y medallas: cada vez que alguien compra con una tarjeta MacoWins, la misma incrementa 1 punto. A medida que se van sumando puntos pasan cosas:
10 |
11 | * 🥉 Cuando **suma** 5 puntos pasa a nivel bronce (que da descuentos del 15%)
12 | * 🥈 Cuando luego suma otros 10 pasa a nivel plata (que da descuentos del 20%)
13 | * 🥇 Cuando luego suma otros 30 puntos pasa a nivel oro (que da 25%, pero hasta 3 mil pesos de descuento máximo).
14 |
15 | 📆 Cada primero de mes todas las tarjetas son bajadas de medalla: si tu tarjeta estaba en oro pasa a plata, si estaba en plata pasa a bronce y si estaba en bronce vuelve a no tener medalla (de 10% de descuento). Aunque aún no se usarán para generar descuentos, **los puntos de la tarjeta deben quedar intactos**. Este es un ejemplo de situación:
16 |
17 | 1. Tu tarjeta está inicialmente en cero y sin medallas
18 | 2. Realizás 5 compras y pasás a bronce. Tenés 5 puntos totales
19 | 3. Realizás 10 compras más y pasas a plata. Tenés 15 puntos totales
20 | 4. Realizás 2 compras más y seguís en plata. Tenés 17 puntos totales
21 | 5. Inicia el nuevo mes: pasás a bronce nuevamente pero tus puntos totales siguen siendo 17.
22 | 6. Realizás 10 comprás mas y volvés a plata. Tenés 27 puntos totales
23 |
24 | Se pide:
25 |
26 | 1. Agregar a los productos la información de si son de la marca MacoWins.
27 | 2. Incorporar los tres medios de pago antes mencionados.
28 | 3. Hacer que las tarjetas ganen puntos, ganen medallas y cambien su cálculo de descuento.
29 | 4. Poder saber con qué medio de pago se realizó una venta.
30 | 5. Hacer que automáticamente cada mes las tarjetas sean bajadas de medalla.
31 |
32 | 🧪 Notas:
33 |
34 | 1. Todos los requerimientos 1, 2, 3 y 4 tienen que estar adecuadamente probados con `pytest`. Todos los mismos deben estar en verde ✅.
35 | 2. Para el punto 3 se requiere integración con [el módulo de `persistencia`](https://gist.github.com/flbulgarelli/3b34f870783cba3d88c996da6acf773c).
36 | 3. Cada uno de los requerimientos debe ser integrado al proyecto usando Git, en múltiples commits con nombres representativos.
37 | 4. El código debe estar separado en múltiples archivos. Sin contar el archivo de pruebas, como mínimo el proyecto debe contar un archivo para el código de las sucursales, otro para los estados de prendas y otro para el código de las tarjetas.
38 |
--------------------------------------------------------------------------------
/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 | # Índice
6 |
7 | ## 1. Antes de empezar
8 |
9 | 1. [Preparación de un entorno local](./0_presentacion_general/2_entorno_local)
10 |
11 | ## 2. Introducción a la programación
12 |
13 | 1. [Qué es la Programación](./1_introduccion_a_la_programacion/00_qué_es_la_programación)
14 | 1. [Hola Python](./1_introduccion_a_la_programacion/01_hola_python)
15 |
16 | ## 3. Primeros programas
17 |
18 | 1. [Funciones y Parámetros](./1_introduccion_a_la_programacion/02_funciones_y_parámetros)
19 | 1. [Práctica Funciones y Parámetros](./1_introduccion_a_la_programacion/03_práctica_funciones_y_parámetros)
20 | 1. [Lógica Booleana](./1_introduccion_a_la_programacion/04_lógica_booleana)
21 | 1. [Práctica Lógica Booleana](./1_introduccion_a_la_programacion/05_práctica_lógica_booleana)
22 | 1. [Alternativa Condicional](./1_introduccion_a_la_programacion/06_alternativa_condicional)
23 | 1. [Práctica Alternativa Condicional](./1_introduccion_a_la_programacion/07_práctica_alternativa_condicional)
24 | 1. [Variables y Procedimientos](./1_introduccion_a_la_programacion/08_variables_y_procedimientos)
25 | 1. [Práctica Variables y Procedimientos](./1_introduccion_a_la_programacion/09_práctica_variables_y_procedimientos)
26 | 1. [Excepciones](./1_introduccion_a_la_programacion/17_excepciones)
27 |
28 | ## 4. Estructuras de datos
29 |
30 | 1. [Listas](./1_introduccion_a_la_programacion/10_listas)
31 | 1. [Secuencias y Repetición](./1_introduccion_a_la_programacion/11_secuencias_y_repetición)
32 | 1. [Práctica Repeticion](./1_introduccion_a_la_programacion/12_práctica_repeticion)
33 | 1. [Diccionarios](./1_introduccion_a_la_programacion/13_diccionarios)
34 | 1. [Recorridos](./1_introduccion_a_la_programacion/14_recorridos)
35 | 1. [Expresiones Regulares](./1_introduccion_a_la_programacion/16_expresiones_regulares)
36 | 1. [Integración](./1_introduccion_a_la_programacion/15_integración)
37 |
38 | ## 5. Introducción al análisis de datos
39 |
40 | 1. [Tablas](./2_introducción_al_análisis_de_datos/1_tablas)
41 | 1. [Filas y columnas](./2_introducción_al_análisis_de_datos/2_filas_y_columnas)
42 | 1. [Práctica tablas, filas y columnas](./2_introducción_al_análisis_de_datos/3_práctica_tablas_filas_y_columnas)
43 | 1. [Práctica listas y tablas](./2_introducción_al_análisis_de_datos/4_práctica_listas_y_tablas)
44 | 1. [Práctica agregaciones](./2_introducción_al_análisis_de_datos/5_práctica_agregaciones)
45 |
46 | ## 5. Programación con objetos
47 |
48 | 1. [Objetos, clases, interfaces, identidad y herencia](./4_programacion_con_objetos/1_introduccion/)
49 | 1. [Delegación y polimorfismo](./4_programacion_con_objetos/2_delegacion_y_polimorfismo/)
50 |
51 |
52 | ## 7. Testing
53 |
54 | 1. [Introducción a `pytest`](./9_testing/1_introduccion/)
55 |
56 | ## 8. Entrada/Salida
57 |
58 | 1. [Rutas](5_entrada_salida/2_os_rutas/os_rutas.md)
59 | 1. [Manejo de archivos](5_entrada_salida/1_manejo_de_archivos/Manipulación_de_archivos.md)
60 |
61 |
--------------------------------------------------------------------------------
/3_ciencia_de_datos/4_clustering/iris_data.txt:
--------------------------------------------------------------------------------
1 | sepal.length sepal.width petal.length petal.width
2 | 5.1 3.5 1.4 0.2
3 | 4.9 3 1.4 0.2
4 | 4.7 3.2 1.3 0.2
5 | 4.6 3.1 1.5 0.2
6 | 5 3.6 1.4 0.2
7 | 5.4 3.9 1.7 0.4
8 | 4.6 3.4 1.4 0.3
9 | 5 3.4 1.5 0.2
10 | 4.4 2.9 1.4 0.2
11 | 4.9 3.1 1.5 0.1
12 | 5.4 3.7 1.5 0.2
13 | 4.8 3.4 1.6 0.2
14 | 4.8 3 1.4 0.1
15 | 4.3 3 1.1 0.1
16 | 5.8 4 1.2 0.2
17 | 5.7 4.4 1.5 0.4
18 | 5.4 3.9 1.3 0.4
19 | 5.1 3.5 1.4 0.3
20 | 5.7 3.8 1.7 0.3
21 | 5.1 3.8 1.5 0.3
22 | 5.4 3.4 1.7 0.2
23 | 5.1 3.7 1.5 0.4
24 | 4.6 3.6 1 0.2
25 | 5.1 3.3 1.7 0.5
26 | 4.8 3.4 1.9 0.2
27 | 5 3 1.6 0.2
28 | 5 3.4 1.6 0.4
29 | 5.2 3.5 1.5 0.2
30 | 5.2 3.4 1.4 0.2
31 | 4.7 3.2 1.6 0.2
32 | 4.8 3.1 1.6 0.2
33 | 5.4 3.4 1.5 0.4
34 | 5.2 4.1 1.5 0.1
35 | 5.5 4.2 1.4 0.2
36 | 4.9 3.1 1.5 0.1
37 | 5 3.2 1.2 0.2
38 | 5.5 3.5 1.3 0.2
39 | 4.9 3.1 1.5 0.1
40 | 4.4 3 1.3 0.2
41 | 5.1 3.4 1.5 0.2
42 | 5 3.5 1.3 0.3
43 | 4.5 2.3 1.3 0.3
44 | 4.4 3.2 1.3 0.2
45 | 5 3.5 1.6 0.6
46 | 5.1 3.8 1.9 0.4
47 | 4.8 3 1.4 0.3
48 | 5.1 3.8 1.6 0.2
49 | 4.6 3.2 1.4 0.2
50 | 5.3 3.7 1.5 0.2
51 | 5 3.3 1.4 0.2
52 | 7 3.2 4.7 1.4
53 | 6.4 3.2 4.5 1.5
54 | 6.9 3.1 4.9 1.5
55 | 5.5 2.3 4 1.3
56 | 6.5 2.8 4.6 1.5
57 | 5.7 2.8 4.5 1.3
58 | 6.3 3.3 4.7 1.6
59 | 4.9 2.4 3.3 1
60 | 6.6 2.9 4.6 1.3
61 | 5.2 2.7 3.9 1.4
62 | 5 2 3.5 1
63 | 5.9 3 4.2 1.5
64 | 6 2.2 4 1
65 | 6.1 2.9 4.7 1.4
66 | 5.6 2.9 3.6 1.3
67 | 6.7 3.1 4.4 1.4
68 | 5.6 3 4.5 1.5
69 | 5.8 2.7 4.1 1
70 | 6.2 2.2 4.5 1.5
71 | 5.6 2.5 3.9 1.1
72 | 5.9 3.2 4.8 1.8
73 | 6.1 2.8 4 1.3
74 | 6.3 2.5 4.9 1.5
75 | 6.1 2.8 4.7 1.2
76 | 6.4 2.9 4.3 1.3
77 | 6.6 3 4.4 1.4
78 | 6.8 2.8 4.8 1.4
79 | 6.7 3 5 1.7
80 | 6 2.9 4.5 1.5
81 | 5.7 2.6 3.5 1
82 | 5.5 2.4 3.8 1.1
83 | 5.5 2.4 3.7 1
84 | 5.8 2.7 3.9 1.2
85 | 6 2.7 5.1 1.6
86 | 5.4 3 4.5 1.5
87 | 6 3.4 4.5 1.6
88 | 6.7 3.1 4.7 1.5
89 | 6.3 2.3 4.4 1.3
90 | 5.6 3 4.1 1.3
91 | 5.5 2.5 4 1.3
92 | 5.5 2.6 4.4 1.2
93 | 6.1 3 4.6 1.4
94 | 5.8 2.6 4 1.2
95 | 5 2.3 3.3 1
96 | 5.6 2.7 4.2 1.3
97 | 5.7 3 4.2 1.2
98 | 5.7 2.9 4.2 1.3
99 | 6.2 2.9 4.3 1.3
100 | 5.1 2.5 3 1.1
101 | 5.7 2.8 4.1 1.3
102 | 6.3 3.3 6 2.5
103 | 5.8 2.7 5.1 1.9
104 | 7.1 3 5.9 2.1
105 | 6.3 2.9 5.6 1.8
106 | 6.5 3 5.8 2.2
107 | 7.6 3 6.6 2.1
108 | 4.9 2.5 4.5 1.7
109 | 7.3 2.9 6.3 1.8
110 | 6.7 2.5 5.8 1.8
111 | 7.2 3.6 6.1 2.5
112 | 6.5 3.2 5.1 2
113 | 6.4 2.7 5.3 1.9
114 | 6.8 3 5.5 2.1
115 | 5.7 2.5 5 2
116 | 5.8 2.8 5.1 2.4
117 | 6.4 3.2 5.3 2.3
118 | 6.5 3 5.5 1.8
119 | 7.7 3.8 6.7 2.2
120 | 7.7 2.6 6.9 2.3
121 | 6 2.2 5 1.5
122 | 6.9 3.2 5.7 2.3
123 | 5.6 2.8 4.9 2
124 | 7.7 2.8 6.7 2
125 | 6.3 2.7 4.9 1.8
126 | 6.7 3.3 5.7 2.1
127 | 7.2 3.2 6 1.8
128 | 6.2 2.8 4.8 1.8
129 | 6.1 3 4.9 1.8
130 | 6.4 2.8 5.6 2.1
131 | 7.2 3 5.8 1.6
132 | 7.4 2.8 6.1 1.9
133 | 7.9 3.8 6.4 2
134 | 6.4 2.8 5.6 2.2
135 | 6.3 2.8 5.1 1.5
136 | 6.1 2.6 5.6 1.4
137 | 7.7 3 6.1 2.3
138 | 6.3 3.4 5.6 2.4
139 | 6.4 3.1 5.5 1.8
140 | 6 3 4.8 1.8
141 | 6.9 3.1 5.4 2.1
142 | 6.7 3.1 5.6 2.4
143 | 6.9 3.1 5.1 2.3
144 | 5.8 2.7 5.1 1.9
145 | 6.8 3.2 5.9 2.3
146 | 6.7 3.3 5.7 2.5
147 | 6.7 3 5.2 2.3
148 | 6.3 2.5 5 1.9
149 | 6.5 3 5.2 2
150 | 6.2 3.4 5.4 2.3
151 | 5.9 3 5.1 1.8
152 |
--------------------------------------------------------------------------------
/2_introducción_al_análisis_de_datos/5_práctica_agregaciones/README.md:
--------------------------------------------------------------------------------
1 | # Práctica Agregaciones
2 |
3 | Cuando tenemos un conjunto de datos (es decir, de varios elementos, como por ejemplo...
4 |
5 | * Los meses del año representados como strings (ejemplo `['Enero', 'Febrero', 'Marzo', ...]`)
6 | * Las notas de un parcial para un curso representadas como una lista de números (ejemplo `[10, 4, 2, 9, 8, 8]`)
7 | * La lista de los animales de una reserva natural como `["loro", "caraya", "ñandún"]`, `[15, 5, 9]` (esto último lo llamamos identificador único, o también ID)
8 | * Las bicicletarías de CABA como una `DataFrame` con nombre, direccion, email, si tiene servicio mecánico, etc.
9 |
10 | ... es común que necesitemos calcular un único resultado (que puede ser o no del mismo tipo de datos que los elementos del conjunto) que de alguna forma nos describa a esos datos. Por ejemplo:
11 |
12 | * máximos y mínimos (`max`, `min`)
13 | * cantidad de elementos (`len`)
14 | * cantidad de elementos únicos (`nunique`)
15 | * sumatoria (`sum` )
16 | * promedio o media (`mean`)
17 | * moda (`mode`)
18 | * mediana (`median`)
19 |
20 | A este tipo de cálculos, que usualmente podemos resolver recorriendo cada elemento del conjunto mediante un `for`, los denominamos _agregaciones_.
21 |
22 | ¡Practiquémoslas!
23 |
24 | ## 1. `promedio`
25 |
26 | > Definí la función `promedio` que calcule el promedio de una lista (no vacía) de números.
27 |
28 | ## 2. `maximo` y `minimo`
29 |
30 | > Definí las funciones `maximo` y `mínimo` que obtengan el valor más grande y más chico de una lista (no vacía) de números, respectivamente.
31 |
32 | ## 3. `mediana`
33 |
34 | Definí la función `mediana`, que devuelva la mediana de una lista (no vacía) de números. Ejemplo:
35 |
36 | ```python
37 | >>> mediana([1, 2, 3, 4, 18, 20])
38 | 3
39 | >>> mediana([48, 18, 2, 4])
40 | 4
41 | ```
42 |
43 | ## 4. `moda`
44 |
45 | Definí la función `moda`, que devuelva el elemento que más se repite en una lista (no vacía) de números. Si ninguno se repite, puede devolver un elemento cualquiera. Ejemplo:
46 |
47 | ```python
48 | >>> moda([1, 2, 3, 4, 3, 20])
49 | 3
50 | >>> moda([48, 2, 18, 2, 4])
51 | 2
52 | >>> moda([48, 2, 18, 20, 4])
53 | 4 # cualquier resultado sería correcto
54 | ```
55 |
56 | ### Ayuda
57 |
58 | 1. la mediana se obtiene obteniendo el elemento del medio tras ordenar la lista de menor a mayor.
59 | 2. las siguientes funciones pueden ser útiles: `int`, `rount`, `sorted`
60 |
61 | ## 5. `cuantil`
62 |
63 | > Definí la función `cuantil`, que tome una lista y un número entre 0 y 1 y retorne el valor del cuantil correspondiente:
64 |
65 | ```python
66 | >>> cuantil([1, 2, 3, 4, 18, 20], 0.5)
67 | 3
68 | >>> cuantil([48, 18, 2, 4], 0.25)
69 | 2
70 | >>> cuantil([48, 18, 2, 4], 0.75)
71 | 18
72 | ```
73 |
74 | ## 6. Brecha ecológica
75 |
76 | Vamos a definir como la _brecha ecológica_ de una ciudad a la diferencia entre la cantidad de árboles que hay entre el barrio que más tiene y el barrio que menos tiene.
77 |
78 | > Partiendo del [lote de datos de arbolado público lineal de CABA](http://cdn.buenosaires.gob.ar/datosabiertos/datasets/arbolado-publico-lineal/arbolado-publico-lineal.csv), definí una función `brecha_ecologica`, que resuelva este cálculo.
79 |
--------------------------------------------------------------------------------
/0_presentacion_general/2_entorno_local/README.md:
--------------------------------------------------------------------------------
1 | > Nota: esta guía está orientada a una instalación local en Linux.
2 |
3 | # Entorno local
4 |
5 | Para utilizar Python localmente (es decir, en tu computadora en lugar de en una plataforma online como [Replit](https://replit.com/) o [Mumuki](https://mumuki.io)) vamos a tener que instalar algunos programas y aprender a usar terminales. ¡Acompañános!
6 |
7 | ## 1. Terminales
8 |
9 | Abrí una terminal. Notarás que aparece algo similar a lo siguiente:
10 |
11 | ```shell
12 | mi_nombre@mi_computadora:~$
13 | ```
14 |
15 | Esto lo que está indicando es que iniciaste sesión en la computadora `mi_computadora` con un usuario llamado `mi_nombre`. Además, el signo `$` (también llamado prompt) indica que la terminal está lista para aceptar comandos. Por último, el símbolo `~` indica que estás en el directorio principal de `mi_nombre`, también denominado _home_.
16 |
17 | ¿Y qué comandos podés ejecutar? Estos son algunos de los (tantísimos) disponibles:
18 |
19 | * `cd`: cambia de directorio
20 | * `ls`: muestra los contenidos del directorio
21 | * `pwd`: muestra el directorio actual
22 |
23 | ## 2. Instalación de Python
24 |
25 | La forma más sencilla para instalar Python en Ubuntu (20.04 o superior) es con el siguiente comando:
26 |
27 | ```bash
28 | $ sudo apt install python3 python-is-python3 python3-pip
29 | ```
30 |
31 | ## 3. Instalación de Visual Code
32 |
33 | Visual Code es uno de los editores de código más comunes y flexibles (en 2022). Para instalarlo en Ubuntu ejecutaremos lo siguiente:
34 |
35 | ```bash
36 | $ sudo snap install code
37 | ```
38 |
39 | O, si este comando genera una advertencia, podremos hacer lo siguiente:
40 |
41 | ```bash
42 | $ sudo snap install code --classic
43 | ```
44 |
45 | ## 4. Probando todo
46 |
47 | Para editar un archivo, podés abrir Visual Code desde el menú de aplicaciones, o ejecutando en una terminal el comando `code`. Ejemplo:
48 |
49 | ```bash
50 | $ code programa.py
51 | ```
52 |
53 | Luego, para ejecutar los contenidos del archivo, podés hacer:
54 |
55 | ```bash
56 | $ python
57 | # en modo interactivo
58 | $ python -i
59 | ```
60 |
61 | ## 5. Proyectos
62 |
63 | Cuando empezamos a tener muchos archivos de código, conviene hacer orden 🧹. Por eso vamos a organizar nuestros `.py` dentro de un directorio que representará un _proyecto_, es decir, un conjunto de materiales que están relacionados de alguna forma.
64 |
65 | ¿Y cómo creamos directorios la terminal de Linux? ¡Con el comando `mkdir`!
66 |
67 | ```bash
68 | # crea un directorio (inicialmente) vacío
69 | $ mkdir mi_proyecto
70 | ```
71 |
72 | ¿Y si nos arrepentimos? Podemos borrar el directorio con `rmdir`:
73 |
74 | ```bash
75 | $ rmdir mi_proyecto
76 | ```
77 |
78 | ¡Pero cuidado! Esto funcionará sólo si el directorio está vacío (y esto es bueno, porque si nos equivocamos podríamos estar borrando de más 😅).
79 |
80 | Finalmente, si queremos _abrir_ un proyecto y editar sus archivos, podremos navegar movernos hacia el directorio correspondiente y luego abrir VisualCode allí:
81 |
82 | ```bash
83 | $ mv mi_proyecto
84 | # el . significa "el directorio donde estoy actualmente"
85 | $ code .
86 | ```
87 |
88 | ## 6. De acá para allá
89 |
90 | Otros comandos que serán útiles para la gestión de archivos son los siguientes:
91 |
92 | * `cat`: muestra los contenidos de un archivo (entre otras cosas)
93 | * `cp`: copia un archivo
94 | * `mv`: mueve un archivo
95 | * `touch`: crea un archivo vacío
96 | * `sed`: realiza ediciones (sencillas) sobre un archivo
97 | * `echo`: imprime un mensaje por consola
98 |
--------------------------------------------------------------------------------
/7_frontend/4_integracion/README.md:
--------------------------------------------------------------------------------
1 | # MacoWins - Entrega 5
2 |
3 | 🌐 ¡Llegó la hora de permitir que todas las personas ingresen al sitio de MacoWins! Por ello vamos a incluir Flask a nuestro proyecto y convertir nuestro código en una aplicación web.
4 |
5 | Esta aplicación debe contar con las siguientes pantallas:
6 |
7 | 1. 👕 Listado de productos: desde esta página se deben poder ver todos los productos, con su nombre, **primera** categoría y precio. Los productos que no tienen stock deben tener un estilo diferente, fácilmente reconocible. Desde el listado de productos se debe poner navegar a la pantalla de detalle del producto (punto 2).
8 |
9 | 2. 👕 Detalle de producto: debe contener la misma información que la que se encuentra en el listado de productos (nombre, precio, y cantidad en stock), pero además se deben ver **todas** sus categorías y código de producto. También debe ser posible ver en qué sucursal se encuentra y desde allí navegar a la página de detalle de sucursal (punto 3).
10 |
11 | 3. 🏬 Detalle de sucursal: debe mostrar la cantidad de productos en stock, su nombre y dirección (sólo para las físicas). Estos últimos dos datos deben ser incorporados al modelo de objetos. Además, las sucursales que son virtuales deben tener un estilo diferente, fácilmente identificable. En todos los casos, también se deben mostrar los tres productos más vendidos, mostrando por cada uno de ellos los mismos datos que el listado del punto 1, y permitiendo navegar hacia el detalle del producto (punto 2)
12 |
13 | 4. 👕 Buscador de productos: la home de MacoWins. Desde aquí debe ser posible ingresar un nombre de prenda, una categoría y/o un precio máximo y al presionar enviar obtener todos los productos que encajen con ese criterio. Opcionalmente, debe ser posible navegar hacia el listado completo de productos (punto 1). Cuando se muestra el resultado de la búsqueda, se debe ver claramente cuál fue la búsqueda realizada y permitir realizar una nueva.
14 |
15 | 5. 🛒 Listado de ventas: esta página estará destinada sólamente al equipo que trabaja de MacoWins. Desde allí se deberán poder ver todas las ventas realizadas, de cualquier producto. Se debe poder ver el precio de venta, nombre del producto vendido, fecha y cantidad. Además, debe ser posible ver la sucursal donde se realizó la venta. Por defecto, debe mostrar todas las ventas de MacoWins, debe contar con un filtro que permita mostrar ventas sólo entre dos fechas.
16 |
17 | 6. 🏬 Listado de sucursales: desde esta página se debe poder ver todas las sucursales. Aquellas que son virtuales deben ser claramente distinguibles con un estilo diferente. Se debe mostrar el nombre de la sucursal (ver punto 3) y si es virtual o física, usando un estilo diferente, fácilmente diferenciable. La página debe tener un filtro que permita mostrar todas las sucursales, sólo las físicas o sólo las virtuales.
18 |
19 | Consideraciones:
20 |
21 | 1. Todas las pantallas deben presentar la misma estructura: pie de página con información del sitio, cabecera con nombre del sitio y enlaces de navegación, cuerpo con los contenidos de la página. Además, debe utilizar la misma tipografía y colores.
22 | 2. La entrega debe ser completamente funcional y debe permitir navegar entre las pantallas.
23 | 3. La aplicación debe ser integrada con [el módulo de `persistencia`](https://gist.github.com/flbulgarelli/3b34f870783cba3d88c996da6acf773c), si no lo fue hecho aún.
24 | 4. Cada uno de los requerimientos debe ser integrado al proyecto usando Git, en múltiples commits con nombres representativos.
25 | 5. Antes de la entrega, los equipos deben entregar:
26 | - los bosquejos (a mano alzada o usando una herramienta online como [moqups.com](https://moqups.com/) de cómo se verán las pantallas que implementarán.
27 |
28 | - el detalle de las rutas que utilizarán para su aplicación, con los verbos HTTP asociados a dichas rutas
--------------------------------------------------------------------------------
/2_introducción_al_análisis_de_datos/2_filas_y_columnas/README.md:
--------------------------------------------------------------------------------
1 | # Operaciones básicas
2 |
3 | Ya sabemos qué es un lote de datos, un `DataFrame` y cómo podemos usar `pandas` para cargarlo con datos. ¡Descubramos entonces qué podemos hacer con los datos a partir de ahora!
4 |
5 | ¡Seguínos!
6 |
7 | ## 1. De flor en flor
8 |
9 | Carguemos un luego lote de datos
10 |
11 | ```python
12 | >>> florerias = pd.read_csv("https://cdn.buenosaires.gob.ar/datosabiertos/datasets/ministerio-de-espacio-publico-e-higiene-urbana/puestos-de-flores/puestos-flores.csv")
13 | >>> florerias
14 | ```
15 |
16 | ## 2. Accediendo a las columnas
17 |
18 | ```python
19 | # para obtener los valores de una columna,
20 | # la sintaxis es tabla[nombre_de_columna]
21 | >>> florerias["TITULAR"]
22 | 0 PEVES SALLY STEPHANIE
23 | 1 HEREDIA LUIS HECTOR
24 | 2 AYBAR MARIA SOLEDAD
25 | 3 REBASA OTINIANO SANTOS CLARA
26 | 4 SALAZAR ESCOBAR ROSENDA
27 | ...
28 | 95 DE NICOLO CONO WALTER DARIO
29 | 96 SAGARDIA DESIDERIO
30 | 97 CONDOLUCI MARIA TERESA
31 | 98 MC INTYRE YOLANDA ELIZABETH
32 | 99 CORDERO EZEQUIEL MAXIMILIANO
33 | Name: TITULAR, Length: 100, dtype: object
34 | ```
35 |
36 | > ¡Ahora te toca a vos! Obtené las calles de las florerías
37 |
38 | ### Solución posible
39 |
40 |
41 | 👀 Ver
42 |
43 | ```python
44 | >>> florerias["Calle"]
45 | 0 FLORIDA
46 | 1 LACROZE, FEDERICO AV.
47 | 2 CABILDO AV.
48 | 3 FLORIDA
49 | 4 MARTIN GARCIA AV.
50 | ...
51 | 95 EMILIO MITRE
52 | 96 AV. CORRIENTES
53 | 97 AV. GUZMAN PUESTO 7
54 | 98 JURAMENTO
55 | 99 C. PELLEGRINI
56 | Name: Calle, Length: 100, dtype: object
57 | ```
58 |
59 |
60 |
61 | ## 3. La revancha de los últimos
62 |
63 | De la misma forma que podemos obtener las primeras y últimas filas de una tabla, también podemos obtener los primeros valores de una columna...
64 |
65 | ```python
66 | >>> florerias["TITULAR"].head(5)
67 | 0 PEVES SALLY STEPHANIE
68 | 1 HEREDIA LUIS HECTOR
69 | 2 AYBAR MARIA SOLEDAD
70 | 3 REBASA OTINIANO SANTOS CLARA
71 | 4 SALAZAR ESCOBAR ROSENDA
72 | Name: TITULAR, dtype: object
73 | ```
74 |
75 | ... como los últimos:
76 |
77 | ```python
78 | >>> florerias["TITULAR"].tail(5)
79 | 95 DE NICOLO CONO WALTER DARIO
80 | 96 SAGARDIA DESIDERIO
81 | 97 CONDOLUCI MARIA TERESA
82 | 98 MC INTYRE YOLANDA ELIZABETH
83 | 99 CORDERO EZEQUIEL MAXIMILIANO
84 | Name: TITULAR, dtype: object
85 | ```
86 |
87 | Como vemos, esta funciones infijas toman un `Series` y un número, y devuelven otro `Series` con los valores seleccionados.
88 |
89 | ## 4. Series vs DataFrames
90 |
91 | ```python
92 | # a lo que llamos tabla, pandas lo llama DataFrame
93 | # a lo que llamamos columna, pandas lo llama Series
94 | ```
95 | Hay funciones y procedimientos que funcionan tanto con los DataFrames como con los Series.
96 |
97 | Pero, al ser distintas sus estructuras, ya que el primero es una tabla con muchas columnas, mientras que el Series es básicamente una única columna, los parámetros variarán.
98 |
99 |
100 | ## 5. No tan único
101 |
102 | ```python
103 | florerias["Calle"].unique()
104 | ```
105 |
106 | ```python
107 | >>> len(florerias["Calle"].unique())
108 | 67
109 | ```
110 |
111 | Como esta operación es tan común, contamos con un atajo: la función infija `nunique`:
112 |
113 | ```python
114 | >>> florerias["Calle"].nunique()
115 | 67
116 | ```
117 |
118 | ## 6. Cuántos cuentan
119 |
120 | ```python
121 | florerias["Calle"].value_counts()
122 | ```
123 |
124 | ## 7. También vale ordenar
125 |
126 | Vuelta sobre el `sort_values`
127 |
128 | ## 8. Índices
129 |
130 | No, ¡no es el índice de la lección!
131 |
132 | ```python
133 | # otro uso de corchetes:
134 | # la función iloc se usa de la siguiente forma:
135 | # tabla.iloc[indice]
136 | # con iloc podemos obtener una fila en base a su número (índice) de fila
137 | # OJO: el número o indice de fila no necesariamente coincide con la posición de la fila en la tabla
138 | florerias.iloc[4]
139 | ```
140 |
--------------------------------------------------------------------------------
/9_testing/2_integracion/recomendaciones.py:
--------------------------------------------------------------------------------
1 | # Acá tenés que importar el código que hayas desarrollado
2 | # Suponiendo que el mismo se encuentra en un archivo main.py, "hermanado" (en el mismo directorio que)
3 | # to test_main.py, deberías hacer el import así:
4 | from main import *
5 |
6 | # A partir de acá, podés escribir tus tests.
7 | # Por ejemplo, podemos probar a función hay_stock
8 | # Si bien no es el primer punto del enunciado, nos va a permitir
9 | # probar en el camino a los dos puntos anteriores: registrar_producto y recargar_stock
10 | # con lo que estamos probando también los otros puntos. Aunque es la idea, no siempre es posible
11 | # probar todo "unitariamente", es decir, de forma totalmente aislada del resto del código
12 |
13 | # Un escenario posible sería probarlo cuando aún no agregaste nada al listado de productos
14 | def test_hay_stock_producto_inexistente():
15 | # recordá que los tests deberían ser
16 | # repetibles e independientes del orden, así que nos tenemos que asegurar de que partamos
17 | # de un estado conocido.
18 | # Yo me hice un procedimiento auxiliar en mi archivo main que reinicia la tienda
19 | # (es decir, (re)inicializa los productos y ventas con una lista vacía)
20 | reiniciar_tienda()
21 |
22 | assert not hay_stock(100)
23 |
24 | # otro escenario posible sería probar buscar un producto (ejemplo: producto con código 100)
25 | # cuando ese producto está registrado, pero no hay stock
26 | def test_hay_stock_producto_existente_sin_stock():
27 | # de nuevo, tenemos que asegurarnos de partir de un estado conocido (la tienda vacía)...
28 | reiniciar_tienda()
29 |
30 | # ...a la que le agregamos una remera para probar
31 | registrar_producto({
32 | "codigo": 100,
33 | "nombre": "remera talle m",
34 | "categoria": "remera",
35 | "precio": 4500
36 | })
37 |
38 | # y probamos efectivamente lo que deseamos
39 | assert not hay_stock(100)
40 |
41 |
42 | # y ahora otro escenario más (y el más "obvio", probablemente): probamos
43 | # que cuando registremos un producto y le carguemos stock, haya stock:
44 | # (De paso, nos viene bien para recordar la idea de precondición y postcondición que vimos hace dos clases)
45 | def test_hay_stock_producto_existente_con_stock():
46 | # nuestro estado inicial... (precondición)
47 | reiniciar_tienda()
48 | registrar_producto({
49 | "codigo": 100,
50 | "nombre": "remera talle m",
51 | "categoria": "remera",
52 | "precio": 4500
53 | })
54 |
55 | # lo que vamos a hacer... (operación)
56 | recargar_stock(100, 10)
57 |
58 | # lo que debería pasar (postcondición)
59 | assert hay_stock(100)
60 |
61 | # ¡Pero hay mas casos para probar! Por ejemplo, ¿qué pasaría si registramos dos productos,
62 | # le agregamos stock al segundo pero preguntamos por el stock del primero?
63 |
64 | # Y así también podemos seguir probando con los demás puntos. Por ejemplo, podemos probar más en detalle
65 | # recargar_stock: ¿qué debería pasar, por ejemplo, si intentamos cargar stock a un producto inexistente?
66 | # ¿o si registramos dos veces un mismo producto? Son todas cosas que podríamos probar acá.
67 |
68 | # Obviamente no todos los tests utilizarán hay_stock: acá apareció siempre porque es lo que estabamos probando,
69 | # pero para probar `calcular_precio_final_producto` probablemente todo lo referido al stock sea irrelevante.
70 | # Ejemplo:
71 |
72 | def test_calcular_precio_final_con_iva():
73 | assert calcular_precio_final_producto("...completar...") == "...completar..."
74 |
75 | # Quizás en otros tests más conveniente será incluso ver si se agregó
76 | # un elemento a la lista de productos, o a la lista de ventas, o si fue modificado algún elemento
77 | # Por ejemplo, podrías hacer pruebas del estilo siguiente:
78 |
79 | def test_al_recargar_el_stock_el_stock_aumenta():
80 | # precondición
81 | reiniciar_tienda()
82 | registrar_producto({
83 | "codigo": 150,
84 | "nombre": "remera talle s",
85 | "categoria": "remera",
86 | "precio": 4100
87 | })
88 |
89 | # operación
90 | recargar_stock(150, 10)
91 |
92 | # postcondición
93 | assert productos[0]["stock"] == 10
94 |
95 |
96 | # Y también podrías hace otro test que verifique qué pasaría si hacer dos recargas consecutivas.
97 | # ¿Se incrementará realmente como corresponde?
98 |
99 | # Otro test similar a este, pero para realizar_compra, podría ser justamente que si realizas_compra
100 | # el stock disminuya
101 |
102 | # Y en la misma línea, que si realizás compra, se cree una nueva venta (que nuevamente podrías resolverlo haciendo un
103 | # assert que pregunte si esa venta se agregó, o si su tamaño cambió, o si la última venta es la que estás esperando)
104 |
105 | # ¡Y así! Recordá maximizar la cobertura: idealmente todos los puntos deberán estar probados, con cierto detalle.
106 | # ¡Éxitos!
--------------------------------------------------------------------------------
/4_programacion_con_objetos/3_integracion/README.md:
--------------------------------------------------------------------------------
1 | # MacoWins - Entrega 3
2 |
3 | ¡MacoWins sigue creciendo! Tenemos nuevos requerimientos y todo parece indicar que seguiremos desarrollando su sistema por varias iteraciones más.
4 |
5 | Por eso, antes de empezar:
6 |
7 | 1. 🧪 Asegurate de que tu código y sus pruebas automatizadas con pytest estén en un mismo directorio y que las pruebas se puedan ejecutar correctamente y sin errores.
8 | 2. Creá un repositorio en Github y subí tu código al control de versiones. El mismo debe ser público.
9 |
10 |
11 | ## Requerimientos
12 |
13 | Y ahora sí, los requerimientos:
14 |
15 | 1. ✨ Modelar los _estados_ de prenda, que son modificadores del precio. Hay tres estados diferentes:
16 | 1. `Nueva`: no se modifica el precio de la prenda.
17 | 2. `Promoción`: se le resta un valor fijo, propio de cada promoción.
18 | 3. `Liquidación`: la prenda queda al 50% de su valor original.
19 | 2. 🏗 Modificar el código de las entregas pasadas para que ahora las prendas puedan estar en los estados descriptos arriba. En particular:
20 | 1. Inicialmente una prenda debe ser nueva
21 | 2. En cualquier momento debe ser posible poner a la prenda en liquidación o promoción, o incluso volverla a marcar como nueva.
22 | 3. El precio debe siempre reflejar el estado actual de la prenda. Si el estado cambia, el precio de la misma debe verse afectado instantáneamente y tener impacto en todos los puntos en donde se use: `realizar_compra`, `calcular_precio_final_producto`, etc.
23 | 4. Responder: ¿puede la programación orientada a objetos ayudarnos en este punto? ¿Por qué?
24 | 3. 🆕 A partir de ahora, las prendas pueden tener múltiples categorías (pero siempre al menos una). Realizar los agregados y modificaciones necesarias para que:
25 | 1. Las prendas puedan respondernos si son de una categoría dada, teniendo en cuenta todas las que tiene y aplicando las mismas reglas de búsqueda inexacta explicada en la primera entrega.
26 | 2. Podamos agregarle en cualquier momento nuevas categorías a una prenda existente.
27 | 3. `actualizar_precios_por_categoria` siga funcionando adecuadamente.
28 | 4. 🛍 Dado el éxito de nuestro sistema, múltiples sucursales de MacoWins solicitaron poder gestionar sus propios productos y stock. Modificar el código existente para que sea posible crear múltiples sucursales, y cada una pueda resolver los problemas originalmente planteados. Ejemplo:
29 |
30 | ```python
31 | sucursal_cabildo.registrar_producto(remera_talle_m)
32 | sucursal_cabildo.recargar_stock(100, 10)
33 | sucursal_cabildo.hay_stock(100) # True
34 |
35 | sucursal_avellaneda.hay_stock(100) # False
36 | ```
37 |
38 | 5. 🤔 Luego de realizar el cambio anterior, responder: ¿qué objeto debería tener ahora la responsabilidad de resolver el problema de calcular el _precio final_ de un producto?
39 | 6. 🌐 A la franquicia MacoWins se desea incorporar también una sucursal virtual, que es muy similar a las sucursales de siempre, pero tienen formas diferentes de calcular los _gastos del día_:
40 | 1. Las sucursales comunes gastan diariamente un valor fijo, que cada una conoce.
41 | 2. La sucursal virtual se comporta de igual forma forma, pero si supera las 100 ventas diarias, su gasto pasa a computarse como `cantidad de ventas del día × gasto variable`. Dicho gasto variable también es configurable.
42 | 7. 🤑 Se desea poder saber la ganancia diaria de una sucursal, es decir, la diferencia entre las ventas del día y su gasto del día. Este comportamiento debe funcionar para los dos tipos de sucursales.
43 | 8. 📛 Además de poder actualizar precios según (alguna de las) categorías del producto, se desea contar con una funcionalidad similar, pero que aplique los cambios a los productos cuyo nombre coincide con una [expresión regular](https://flbulgarelli.github.io/recursos-python/1_introduccion_a_la_programacion/16_expresiones_regulares/).
44 | 1. Incorporar los métodos y/o clases necesarios para soportar este requerimiento.
45 | 2. Responder: ¿puede este requerimiento ser resuelto sin repetir lógica y posibilitando agregar más formas de búsqueda en el futuro? ¿Cómo?
46 | 9. 💡 Una vez resueltos los puntos anteriores, responder: ¿hay otros cambios que podríamos realizar a nuestra solución original desde los puntos de vista del encapsulamiento y la delegación?
47 |
48 | Al terminar, asegurate de que todo el código esté adecuadamente subido a Github y realizá un tag.
49 |
50 | Tené en cuenta que:
51 |
52 | 1. Las nuevas funcionalidades deben ser realizadas bajo el paradigma de objetos.
53 | 2. No es necesario convertir todo el código existente al paradigma de objetos, aunque probablemente te convenga hacerlo al menos de forma parcial, para hacer más fácil el desarrollo de los nuevos requerimientos de esta entrega. Este cambio del diseño de la solución para favorecer la _extensibilidad_, pero preservando su _funcionalidad_ es lo que se conoce como _refactor_.
54 | 3. Todos los tests existentes tienen que seguir andando, aunque puede que sea necesario modificarlos ligeramente para adaptarse a los _refactors_
55 | 4. Para probar las nuevas funcionalidades será necesario incorporar nuevas pruebas automatizadas con `pytest`.
56 |
--------------------------------------------------------------------------------
/5_entrada_salida/2_os_rutas/os_rutas.md:
--------------------------------------------------------------------------------
1 | > Basado en el material de [Fundamentos de Informática](https://github.com/AJVelezRueda/Fundamentos_de_informatica)
2 |
3 | [Rutas, absolutas y relativas](#os)
4 |
5 | Como ya hemos visto, para acceder a los archivos necesitamos saber cuál es la ruta (o path) dónde se encuentran. En este punto surge el inconveniente de los distintos sistemas operativos, ya que hay una diferencia en cómo se arman estas rutas. Para solucionar esto se puede utilizar las bibliotecas **os** y **pathlib** las cuales ya vienen incorporadas en python (built-in modules), de manera de poder armar las rutas necesarias y poder usarlas independientemente del sistema operativo. Veamos como funcionan.
6 |
7 | Supongamos que un grupo de alumnos trabajan con los sitemas operativos macOS y Windows y uno de ellos tiene un archivo en la carpeta _/home/usuario/Documentos/Fundamentos/Práctica4/archivo.txt_, a la hora de acceder al archivo podemos enfocarlo de manera de acceder al archivo con la ruta absoluta (completa) o la ruta relativa. Con la primera usamos la ruta desde la carpeta base o raíz hasta el archivo en cuestión, y con la segunda tenemos en cuenta dónde estamos parados y aclaramos la ruta desde allí. En este caso la ruta absoluta es la que está mostrada más arriba y si, por ejemplo, estamos en la carpeta _/home/usuario_ la ruta relativa que deberíamos usar es: _./Documentos/Fundamentos/Práctica4/archivo.txt_ (recuerden que si usamos un punto, _._, estamos diciendo "a partir de esta carpeta"). Ahora bien ¿cómo armamos estas rutas para que se puedan usar en cualquier sistema operativo? (para esto consideramos que todos los alumnos lo tienen guardado en la misma carpeta, es decir, dentro de la carpeta Documentos hay una carpeta Fundamentos, etc., sin importar el sistema operativo). Por un lado, si vamos a usar la ruta absoluta, hay que saber cómo se escribe en ese sistema la carpeta del usuario (home), por lo que vamos a usar la biblioteca **pathlib** de la siguiente manera:
8 |
9 | ```python
10 | >>> from pathlib import Path
11 | >>> home = str(Path.home())
12 | ```
13 |
14 | Con esto obtenemos cual es la ruta del home para cualquier sistema. Luego hay que completar la ruta con las carpetas que ya conocemos, por lo que para esto vamos a usar **os.path.join()**, el cual genera la ruta con la manera de escritura del sistema operativo, por ejemplo:
15 |
16 | ```python
17 | >>> ruta_relativa = os.path.join("Documentos", "Fundamentos", "Práctica4", "archivo.txt")
18 | >>> print(ruta_relativa)
19 | # Para macOS y Linux
20 | 'Documentos/Fundamentos/Práctica4/archivo.txt'
21 | # Para Windows
22 | 'Documentos\\Fundamentos\\Práctica4\\archivo.txt'
23 | >>> ruta_absoluta = os.path.join(home, "Documentos", "Fundamentos", "Práctica4", "archivo.txt")
24 | # Para macOS y Linux
25 | '/home/usuario/Documentos/Fundamentos/Práctica4/archivo.txt'
26 | # Para Windows
27 | 'C:\\Users\\Usuario\\Documentos\\Fundamentos\\Práctica4\\archivo.txt'
28 | ```
29 |
30 | Las dos barras invertidas en las rutas de Windows se dan porque una barra invertida en general es una secuencia de escape (como **"\n"**) pero aquí es parte de la ruta, por lo que se le agrega otra barra invertida para que python lo entienda (la otra manera es usar el metacaracter **r** antes de un string de manera de escribir solo una barra: ```r"C:\ruta\al\archivo"```).
31 | Como verán esto no genera carpetas o archivos en la computadora, por lo que puede llegar a pasar que la ruta que hayamos armado no exista (o la hayamos escrito mal), en tales casos lo mejor es verificar si la ruta existe:
32 |
33 | ```python
34 | >>> ruta_relativa = os.path.join("Documentos", "Fundamentos", "Práctica4", "archivo.txt")
35 | >>> os.path.exists(ruta_relativa) #Cuyo resultado es un booleano
36 | ```
37 |
38 | Por cuestiones de comodidad muchas veces es recomendable trabajar con rutas relativas ya que de esta manera solo son movemos dentro de las carpetas correspondientes y no hay peligro de poder acceder a otra carpeta y modificar cosas que no deberíamos, siendo, tal vez, el mayor "inconveniente" saber en qué carpeta estamos parados, para saber para dónde hay que moverse.
39 |
40 | De esta manera, realizando estos pasos, el mismo código va a funcionar independientemente del sistema operativo dónde se ejecute.
41 |
42 | [os.listdir y glob](#glob)
43 |
44 | Algo que resulta muy útil es poder acceder a los archivos que hayan en una determinada carpeta sin conocer sus nombres en particular, o acceder a un grupo de estos archivos que tengan algo en común (que todos tengan la misma extensión por ejemplo), más aún si son archivos que el programa genera en su ejecución, de manera de que a priori no los tenemos. Para esto podemos usar dos herramientas, el método ```listdir``` de la biblioteca ```os``` y el método ```glob``` de la biblioteca ```glob```. Con el primero obtenemos una lista de todos los archivos que se encuentran en una carpeta, mientras que con el segundo, además de esto, tenemos la posibilidad de listar archivos específicos. Es decir:
45 |
46 | ```python
47 | >>> import os
48 | >>> import glob
49 | >>> os.listdir()
50 | ['Ej1.py', 'Ej3.py', 'archivo2.txt', 'Ej2.py', 'Ej4.py', 'documento.txt', 'Ej5.py'...]
51 | >>> glob.glob("*")
52 | ['Ej1.py', 'Ej3.py', 'archivo2.txt', 'Ej2.py', 'Ej4.py', 'documento.txt', 'Ej5.py'...]
53 | >>> glob.glob("*.py")
54 | ['Ej1.py', 'Ej3.py', 'Ej2.py', 'Ej4.py', 'Ej5.py'...]
55 | ```
56 |
57 | Como ven, podemos obtener una lista, la cual podríamos recorrer tanto para todos los archivos de una carpeta como para los archivos específicos.
58 |
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/17_excepciones/README.md:
--------------------------------------------------------------------------------
1 | # *Manejo de Excepciones con Python*
2 |
3 | # Guias de Trabajo
4 | * [1. La excepción hace a la regla](#1-intro)
5 | * [2. Errores de Sintaxis](#2-sintax_error)
6 | * [3. Excepciones](#3-exceptions)
7 | * [4. Con intentar no se pierde nada](#4-try)
8 | * [5. Excepciones personalizadas](#5-raise)
9 |
10 |
11 | [1. La excepción hace a la regla](#1-intro)
12 | Si estuviste haciendo bien las cosas hasta aquí, te habrás equivocado ¡y mucho! En todo proceso de aprendizaje los ‘errores’ tienen un rol muy importante: nos plantean nuevos interrogantes, nos acercan a nuevas hipótesis y sobre todo nos dan oportunidades para seguir aprendiendo.
13 | En la programación los ‘errores’ también importan, ¡y mucho! Son una suerte de comunicación con la máquina, que nos advierte cuando no funciona algo de lo que intentamos hacer.
14 | Existen (al menos) dos tipos diferentes de errores en Python: errores de sintaxis y excepciones. Con cada tipo de error la máquina nos indica qué es lo que puede estar fallando de nuestro código.
15 | No tengas miedo de equivocarte, ¡no se rompe nada! Y como diría la señorita Ricitos del Autobús Mágico: _a cometer errores, tomar oportunidades y rockear con Python, que donde termina la carretera comienza la aventura!_
16 |
17 | [2. Errores de Sintaxis](#2-sintax_error)
18 | Cuando existen errores en el código se detiene la ejecución del programa que hemos creado. Uno de los tipos de errores mas frecuente al iniciarnos en la programación con Python son los errores de sintaxis, o también conocidos como errores de interpretación. ¿A ver si te suena?
19 |
20 | ```Python
21 | >>> print(Hola mundo')
22 | File "", line 1
23 | print(Hola mundo')
24 | ^
25 | SyntaxError: invalid syntax
26 | ```
27 |
28 | Si observamos el error este tiene en su mensaje información que nos permitirá encontar el origen del problema.
29 |
30 | >
31 | > 🧗♀️Desafio I: Descargá y ejecutá el [`script1_manejo_errores.py`](https://github.com/AJVelezRueda/UCEMA_Fundamentos_de_informatica/blob/master/Python_intro/script1_manejo_errores.py)
32 | >
33 | > Para pensar 🤔: ¿Qué tipo de error se obtiene al ejecutar el programa? ¿En dónde se encuentra el error? ¿Cómo te das cuenta?
34 | >
35 |
36 | Como has visto el intérprete de Python imprime la línea responsable del error y muestra una flecha el lugar donde se detectó el error. El error ha sido detectado en el elemento que precede a la flecha. En nuestro ejemplo, el error fué detectado al ejecutar la función print(), ya que faltan las comillas que abre el string.
37 |
38 |
39 | [3. Excepciones](#3-exceptions)
40 |
41 | Sin embargo, aún cuando nuestro código sea sintácticamente correcto, puede generar errores de ejecuciones. Los errores detectados durante la ejecución se llaman excepciones. Existen distintos tipos de excepciones y generalmente el tipo de excepción se imprime como parte del mensaje, al surgir la excepción:
42 |
43 | ```Python
44 | >>> 3 / 0
45 | Traceback (most recent call last):
46 | File "", line 1, in
47 | ZeroDivisionError: division by zero
48 |
49 | ```
50 |
51 | ```Python
52 | >>> print(divisor)
53 | Traceback (most recent call last):
54 | File "", line 1, in
55 | NameError: name 'divisor' is not defined
56 | ```
57 | >
58 | > Para pensar 🤔: ¿Qué nos dice el mensaje de excepción? ¿Qué es la excepción de nombre?
59 | >
60 |
61 | ```Python
62 | >>> 0 + "2"
63 | Traceback (most recent call last):
64 | File "", line 1, in
65 | TypeError: unsupported operand type(s) for +: 'int' and 'str'
66 | ```
67 |
68 | >
69 | > Para pensar 🤔: ¿Qué nos dice el último mensaje de excepción? ¿Qué es la excepción de tipo?
70 | >
71 |
72 |
73 | [4. Con intentar no se pierde nada](#4-try)
74 |
75 | La pregunta que se estarán haciendo es ¿Como podemos preveer estas excepciones para que no se interrumpa la ejecución de nuestro programa? Bueno, para el manejo de excepciones Python nos provee palabras reservadas, que nos permiten manejar las excepciones que puedan surgir y tomar acciones que evitan la interrupción del programa o permitan especificar información adicional antes de interrumpir el programa.
76 | Una de las palabras reservadas es try, esta nos permite "encapsular" un bloque de código para interceptar e identificar excepciones. Si se produce un error dentro de la declaración try-except, se omite una excepción y se ejecuta el bloque de código que maneja la excepción.
77 |
78 | ```Python
79 | try:
80 | # aquí ponemos el código que puede lanzar excepciones
81 | except:
82 | # entrará aquí en caso que se haya producido una excepción
83 | ```
84 |
85 | >
86 | >Para pensar 🤔: ¿Qué tipo de excepción se maneja en el código anterior?
87 | >
88 |
89 | Vamos a trabajar con un ejemplo:
90 |
91 | >
92 | > 🧗♀️Desafio II: Creá una función denominada _mitades_ que tenga como argumento un número e imprima el resultado de la división de ese número por 2
93 | >
94 | >Para pensar 🤔: ¿Qué crees que ocurrirá cuando ingresas un 9 como parámetro? ¿Y con un 0?
95 | >
96 | > 🧗♀️Desafio III: ¿Cómo mejorarías tu función para que sea capaz de manejar el error de la división por cero? Reescribí la función incorporando una declaración try-except
97 | >
98 |
99 |
100 | [5. Excepciones personalizadas](#5-raise)
101 |
102 | En algunos casos, puede ser necesario crear excepciones personalizadas o forzar que ocurra una excepción específica dado un contexto. La sentencia _raise_, se puede indicar el tipo de excepción que deseamos lanzar y el mensaje de que queremos brindarle al usuario:
103 |
104 |
105 | ```Python
106 | def check_int_type():
107 | if type(x) != int:
108 | raise TypeError("Only integers are allowed")
109 | ```
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/05_práctica_lógica_booleana/README.md:
--------------------------------------------------------------------------------
1 | > Basado en https://github.com/MumukiProject/mumuki-guia-javascript-ejercitacion-condicionales
2 |
3 | # Práctica lógica booleana
4 |
5 | ## 1. `puede_ver_pelicula(edad, tiene_autorizacion)`
6 |
7 | Crear una función `puede_ver_pelicula` que tome como argumentos un número `edad` y un booleano `tiene_autorizacion`, y
8 | devuelva si la persona está habilitada para ver la película. Sólo puede ver la película si: tiene 15 años o más, o tiene autorización de sus padres.
9 |
10 | ```python
11 | puede_ver_pelicula(12, False) # False
12 | puede_ver_pelicula(12, True) # True
13 | puede_ver_pelicula(16, False) # True
14 | puede_ver_pelicula(18, True) # True
15 | ```
16 |
17 | ## 2. `esta_en_rango(valor, minimo, maximo)`
18 |
19 | Crear una función `esta_en_rango` que tome como argumentos tres números, un `valor`, un número `minimo` y un número `maximo`, y devuelva si el `valor` se encuentra entre los números `minimo` y `maximo` (si el `valor` es igual a uno de los extremos se considera que está dentro del rango)
20 |
21 | ```python
22 | esta_en_rango(3, 1, 10) # True
23 | esta_en_rango(1, 1, 10) # True
24 | esta_en_rango(10, 1, 10) # True
25 | esta_en_rango(12, 1, 10) # False
26 | esta_en_rango(-3, 1, 10) # False
27 | ```
28 |
29 | ## 3. `puede_avanzar(colo_semaforo)`
30 |
31 | Crear una función `puede_avanzar` que tome como argumento un string con el color del semáforo y devuelva si puede avanzar.
32 |
33 | ```python
34 | puede_avanzar('verde') # True
35 | puede_avanzar('amarillo') # False
36 | puede_avanzar('rojo') # False
37 | ```
38 |
39 | ## 4. `es_vocal(letra)`
40 |
41 | Crear una función `es_vocal` que tome como argumento un string `letra` y devuelva si `letra` es una vocal.
42 |
43 | ```python
44 | es_vocal('a') # True
45 | es_vocal('n') # False
46 | ```
47 |
48 | ## 5. `es_consonante(letra)`
49 |
50 | Crear una función `es_consonante` que tome como argumento un string `letra` y devuelva si es una consonante
51 |
52 | ```python
53 | es_consonante('a') # False
54 | es_consonante('n') # True
55 | ```
56 |
57 | ## 6. `es_hora_valida(hora)`
58 |
59 | > Nota: este ejercicio es difícil porque requiere usar la función `str.split`, que la veremos más adelante...
60 | >
61 | > ```python
62 | >>> hora, minuto = str.split("12:30", ":")
63 | >>> hora
64 | > '12'
65 | >>> minuto
66 | >'30'
67 | >```
68 | >
69 | > ... y también la función `int`, que toma un string y lo conveirte a número, si es posible:
70 | >
71 | >```python
72 | >>> int("23")
73 | > 23
74 | >>> int("hola")
75 | > Traceback (most recent call last):
76 | > File "", line 1, in
77 | > ValueError: invalid literal for int() with base 10: 'hola'
78 | >```
79 | >
80 |
81 |
82 | Crear una función `es_hora_valida` que tome como argumento un string `hora` con el formato `HH:mm` y determine si es una hora válida del día o no
83 |
84 | ```python
85 | es_hora_valida('12:23') # True
86 | es_hora_valida('12:65') # False
87 | es_hora_valida('28:05') # False
88 | es_hora_valida('00:00') # True
89 | ```
90 |
91 | ## 7. `puede_renovar_carnet(paso_test, tiene_multas_impagas, pago_impuestos)`
92 |
93 | Crear una función `puede_renovar_carnet` que tome como argumentos tres booleanos, `paso_test`, `tiene_multas_impagas` y `pago_impuestos`,
94 | y devuelva si una persona está habilitada para renovar su carnet de conducir. Una persona puede renovar su carnet si pasó los tests, no tiene multas impagas y pagó todos los impuestos
95 |
96 | ```python
97 | puede_renovar_carnet(True, True, True) # False
98 | puede_renovar_carnet(True, True, False) # False
99 | puede_renovar_carnet(True, False, True) # True
100 | puede_renovar_carnet(True, False, False) # False
101 | puede_renovar_carnet(False, True, True) # False
102 | puede_renovar_carnet(False, True, False) # False
103 | puede_renovar_carnet(False, False, True) # False
104 | puede_renovar_carnet(False, False, False) # False
105 | ```
106 |
107 | ## 8. `puede_graduarse(asistencia, materias_aprobadas, tesisAprobada)`
108 |
109 | Crear una función `puede_graduarse` que tome como argumentos dos números `asistencia` y `materias_aprobadas` y
110 | un booleano `tesisAprobada`, y devuelva si una persona puede gruadarse. Una persona puede graduarse si tiene un 75%
111 | de asistencia o más, 50 materias aprobadas o más y la tesis aprobada.
112 |
113 | ```python
114 | puede_graduarse(80, 50, True) # True
115 | puede_graduarse(80, 50, False) # False
116 | puede_graduarse(80, 45, True) # False
117 | puede_graduarse(80, 45, False) # False
118 | puede_graduarse(65, 50, True) # False
119 | puede_graduarse(33, 55, False) # False
120 | puede_graduarse(42, 45, True) # False
121 | puede_graduarse(28, 45, False) # False
122 | ```
123 |
124 |
125 | ## 9. `comienza_con_a`
126 |
127 | Definí una función `comienza_con_a` que, al aplicarla con un string, me diga si el mismo comienza con la letra 'a', sin importar si la palabra está escrita en minusculas o mayúsculas. Por ejemplo:
128 |
129 | ```python
130 | >> comienza_con_a("aguja")
131 | True
132 |
133 | >> comienza_con_a("AGUA")
134 | True
135 |
136 | >> comienza_con_a("bote")
137 | False
138 | ```
139 |
140 | ## 10. `es_multiplo_de_3`
141 |
142 | Definí la función `es_multiplo_de_3` que dice si un número se puede dividir por 3. Por ejemplo:
143 |
144 | ```python
145 | >> es_multiplo_de_3(9)
146 | True
147 | >> es_multiplo_de_3(4)
148 | False
149 | ```
150 |
151 | Consejo: no resuelvas la función directamente. En su lugar dividí en subtareas y creá y usá una función `es_multiplo_de` 😎.
152 |
153 | ## 11. `es_bisiesto`
154 |
155 | Definí la función `es_bisiesto` que indica si un año tiene 366 días.
156 |
157 | ```python
158 | >> es_bisiesto(2000)
159 | True
160 | ```
161 |
162 | Tené en cuenta que un año es bisiesto si:
163 |
164 | * es múltiplo de 400, o bien
165 | * es múltiplo de 4 pero no de 100
166 |
167 | Ah: fijate si alguna de las funciones que definiste en los puntos anteriores te puede ser útil 😇.
168 |
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/09_práctica_variables_y_procedimientos/README.md:
--------------------------------------------------------------------------------
1 | # Práctica variables y procedimientos
2 |
3 | ## 1. Alcancía
4 |
5 | Vamos a ahorrar para un viaje post COVID 😷. Tenemos una alcancía comunitaria (nuestro _pozo_) y para decidir si podemos viajar, debemos corroborar que el pozo tenga plata.
6 |
7 | > Definí una función `pozo_vacio`, que nos indique si el `pozo` está en 0. Ejemplo:
8 | >
9 | > ```python
10 | > >>> pozo = 0
11 | > >>> pozo_vacio()
12 | > True
13 | > >>> pozo = 100
14 | > >>> pozo_vacio()
15 | > False
16 | > ```
17 |
18 |
19 | ## 2. ¿Ya llegamos al Chaltén?
20 |
21 | Sabemos que para llegar al Chaltén necesitamos por persona 3000 pesos
22 |
23 | > Hacé una función `cuanta_gente_viaja_al_chalten` que retorne, según el monto del `pozo`, la cantidad de personas que pueden viajar. ¡Tené en cuenta que media persona no puede viajar! 😛
24 | >
25 | > Ejemplo:
26 | >
27 | > ```python
28 | > >>> pozo = 3000
29 | > >>> cuanta_gente_viaja_al_chalten()
30 | > 1
31 | > >>> pozo = 1500
32 | > >>> cuanta_gente_viaja_al_chalten()
33 | > 0
34 | > >>> pozo = 6000
35 | > >>> cuanta_gente_viaja_al_chalten()
36 | > 2
37 | > >>> pozo = 6500
38 | > >>> cuanta_gente_viaja_al_chalten()
39 | > 2
40 | > ```
41 |
42 |
43 | ## 3. Destino alternativo
44 |
45 | ¡A quién no le gusta tener opciones! Sabemos cuánto nos sale por persona el viaje al Chaltén y nos pasaron en la agencia de viaje los valores por persona a Tilcara ($3500 por persona) y Mendoza ($2500 por persona).
46 |
47 | > Definí una función `hasta_donde_llegamos` que según la cantidad de personas que van a viajar, nos devuelva un string con el nombre de la ciudad a la que podemos llegar. Y si no nos alcanza, que nos recomiende seguir ahorrando:
48 | >
49 | > ```python
50 | > >>> hasta_donde_llegamos(2)
51 | > 'Tilcara'
52 | > >>> hasta_donde_llegamos(5)
53 | > 'Seguí ahorrando'
54 | > ```
55 |
56 | ## 4. Hora de aportar
57 |
58 | Pero para todo esto tenga sentido, hay que poner plata 🤑
59 |
60 | > Definí un procedimiento llamada `aportar_al_pozo`, que tome como parámetro un aporte (monto de plata) y actualice el monto del `pozo`:
61 | >
62 | > ```python
63 | > >>> pozo = 500
64 | > >>> aportar_al_pozo(1000)
65 | > >>> pozo
66 | > 1500
67 | > ```
68 |
69 | ## 5. Me quiero bajar
70 |
71 | ¿Pero qué pasa si alguien se quiere bajar? La agencia nos devuelve solo 500, sin importar el monto inicial (asumimos que las personas deben aportar inicialmente más de 500)
72 |
73 | > Definí el procedimiento `darse_de_baja`, que descuenta del pozo 500
74 |
75 | ## 6. Tiempo de descuento
76 |
77 | Por una nueva reglamentación, todos pozos de dinero que tengan más de $15000, deberán tributar un impuesto (llamado _I.V.G.: Impuesto a las Variables Globales_) del 1% si el pozo. Por la misma reglamentación, el valor máximo del impuesto será de $500.
78 |
79 | > Definí:
80 | >
81 | > * una **función** `calcular_monto_ivg`, que indique el valor del impuesto I.V.G. que el pozo debe pagar;
82 | > * un **procedimiento** `aplicar_ivg`, que descuente del `pozo` el valor del impuesto que corresponda.
83 | >
84 | > Ejemplos:
85 | >
86 | > ```python
87 | > >>> pozo = 1000
88 | > >>> calcular_monto_ivg()
89 | > 0 # porque para tributar el monto debe ser de al menos $15000
90 | > >>> pozo = 16000
91 | > >>> calcular_monto_ivg()
92 | > 160 # porque es un pozo de más de $15000, y debe tributar el 1%
93 | > >>> pozo = 125000
94 | > >>> calcular_monto_ivg()
95 | > 500 # porque el valor máximo del impuesto es 500 (el 1% de 125000 hubiera sido $1250)
96 | > >>> aplicar_ivg()
97 | > >>> monto
98 | > 124500 # porque le restó los $500 del impuesto
99 | > ```
100 |
101 | ## 7. La tercera es la vencida
102 |
103 | ¡Otra nueva reglamentación! Después de algunas quejas contra el I.V.G. 😡, se determinó que ningún pozo deberá pagar el impuesto más de tres veces. En otras palabras, al aplicar el impuesto, sólo deberemos descontar del pozo su monto si se aplicó hasta 3 veces. Ejemplo:
104 |
105 | ```python
106 | >>> monto = 100000
107 | >>> aplicar_ivg() # primera aplicación
108 | >>> monto
109 | 99500
110 | >>> aplicar_ivg() # segunda aplicación
111 | >>> monto
112 | 99000
113 | >>> aplicar_ivg() # tercera aplicación
114 | >>> monto
115 | 98500
116 | >>> aplicar_ivg() # cuarta aplicación
117 | >>> aplicar_ivg() # quinta aplicación
118 | >>> aplicar_ivg() # etc
119 | >>> aplicar_ivg() # etc
120 | >>> monto
121 | 98500 # a partir de la cuarta aplicación ya no se descuenta más del pozo
122 | ```
123 |
124 | > Modificá el procedimiento `aplicar_ivg` para que refleje estos cambios de reglamentación.
125 | >
126 | > 💡 Sugerencia: para poder hacer estos cambios en la aplicación del impuesto I.V.G., quizás te convenga agregar nuevas variables globales (_qué ironía 😜_).
127 |
128 | ## 8. Que no se acabe el vuelo
129 |
130 | Parece que la recaudación no anduvo tan bien y la gente se quiere retirar del pozo 😥️. Así que definimos una nueva regla: si no llegamos a un objetivo mínimo de $1000, el pozo vuelve a cero (porque se devolverá la plata a sus participantes 💸️)
131 |
132 | > Definí un procedimiento `volver_a_empezar`, tal que si tenemos menos de $1000 en el `pozo`, lo vuelva a cero.
133 |
134 | ## 9. El delegado/a
135 |
136 | En la clase están votando al delegado que representará al curso. Pero como esta es una clase de pensamiento computacional, vamos a crear un procedimiento `declarar_delegado` que asigne en la variable global `delegado` el nombre de la persona que tenga más votos:
137 |
138 | ```python
139 | >>> declarar_delegado("Perla", 5, "Enzo", 2)
140 | >>> delegado
141 | Perla 2022
142 | ```
143 |
144 | > Definí el procedimiento `declarar_delegado`
145 |
146 | ## 10. Registro histórico
147 |
148 | En la comisión E están creando el registro histórico de delegados/as del curso. Para ellos quieren ahora un procedimiento que les permita registrar los/las delegados/as del curso en cada año en la variable global delegados_por_anio
149 |
150 | ```python
151 | >>> registrar_delegado_del_anio("Sol", 2021)
152 | >>> registrar_delegado_del_anio("Perla", 2022)
153 | >>> delegados_por_anio
154 | Sol 2021, Perla 2022
155 | ```
156 |
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/12_práctica_repeticion/README.md:
--------------------------------------------------------------------------------
1 | > Basado en https://github.com/MumukiProject/mumuki-guia-javascript-ejercitacion-bucles
2 |
3 | # Práctica Repetición
4 |
5 | ## 1. `obtener_indice(valor, lista)`
6 |
7 | Definí una función `obtener_indice` que tome como argumento un valor cualquiera `valor` y un lista cualquiera `lista` y devuelva el índice del _primer ítem_ con dicho valor en la lista, o -1 si no hay ninguno.
8 |
9 | ```python
10 | obtener_indice(12, [5, 7, 12, 34, 54, 2, 12])
11 | 2
12 | obtener_indice(83, [5, 7, 12, 34, 54, 2, 12])
13 | -1
14 | ```
15 |
16 | ## 2. `repetir(valor, cantidad)`
17 |
18 | Definí una función `repetir` que tome como argumento un valor `valor` y un número entero `cantidad`, y devuelva una lista con `valor` repetido `cantidad` de veces.
19 |
20 | ```python
21 | repetir('lovelace', 3)
22 | ['lovelace', 'lovelace', 'lovelace']
23 | repetir('a', 5)
24 | ['a', 'a', 'a', 'a', 'a']
25 | repetir('python', 0)
26 | []
27 | ```
28 |
29 | ## 3. `sumar_impares_hasta(numero)`
30 |
31 | Definí una función `sumar_impares_hasta` que tome como argumento un número `numero` y que devuelva la suma de todos los impares empezando desde 0 hasta dicho `numero` inclusive.
32 |
33 | ```python
34 | sumar_impares_hasta(5)
35 | 9 #(1 + 3 + 5 = 9)
36 | sumar_impares_hasta(13)
37 | 49
38 | sumar_impares_hasta(47)
39 | 576
40 | ```
41 |
42 | ## 4. `cuenta_regresiva(numero_inicial)`
43 |
44 | Definí una función `cuenta_regresiva` que tome como argumento un número entero `numero_inicial` y que devuelva un lista con cuyo primer ítem sea `numero_inicial` y los demás ítems sean números enteros sucesivos descendientes, hasta llegar a 0.
45 |
46 | ```python
47 | cuenta_regresiva(3)
48 | [3, 2, 1, 0]
49 | cuenta_regresiva(5)
50 | [5, 4, 3, 2, 1, 0]
51 | ```
52 |
53 | ## 4. `invertir(lista)`
54 |
55 | Definí una función `invertir` que tome como argumento un lista `lista` y que devuelva un lista con los mismos valores pero en orden invertido.
56 |
57 | ```python
58 | >>> invertir([1, 2, 3])
59 | [3, 2, 1]
60 | >>> invertir([5, 7, 99, 34, 54, 2, 12])
61 | [12, 2, 54, 34, 99, 7, 5]
62 | ```
63 |
64 | ## 4. `remover_duplicados(lista)`
65 |
66 | Definí una función `remover_duplicados` que tome como argumento un lista `lista` y que devuelva un lista con los mismos valores de `lista` pero sin valores duplicados.
67 |
68 | ```python
69 | >>> remover_duplicados([1, 1, 1])
70 | [1]
71 | >>> remover_duplicados([1, 1, 2, 2, 3, 3])
72 | [1, 2 ,3]
73 | >>> remover_duplicados([5, 23, 8, 5, 5, 44, 23])
74 | [5, 23 ,8, 44]
75 | ```
76 |
77 | ## 5. `repetir_letras(palabra, cantidad)`
78 |
79 | Definí una función `repetir_letras` que tome como argumento un string `palabra` y un número entero `cantidad`, y devuelva una string donde cada letra de `palabra` esté repetida `cantidad` de veces.
80 |
81 | ```python
82 | >>> repetir_letras('hola', 2)
83 | 'hhoollaa'
84 | >>> repetir_letras('ada', 3)
85 | 'aaadddaaa'
86 | >>> repetir_letras('ah!', 5)
87 | 'aaaaahhhhh!!!!!'
88 | >>> repetir_letras('basta', 1)
89 | 'basta'
90 | ```
91 |
92 | ## 6. `capitalizar_palabras(string)`
93 |
94 | Definí una función `capitalizar_palabras` tome como argumento un string `string` y devuelva un string donde cada palabra está capitalizada (con la primera letra máyuscula). Dejar las demás letras como están.
95 |
96 | ```python
97 | >>> capitalizar_palabras('Esto es un título')
98 | 'Esto Es Un Título'
99 | >>> capitalizar_palabras('había una vez')
100 | 'Había Una Vez'
101 | >>> capitalizar_palabras('OMG')
102 | 'OMG'
103 | ```
104 |
105 | ## 7. `sumar_seccion(lista, comienzo, cantidad)`
106 |
107 | Definí una función `sumar_seccion` que tome como argumento un lista de números enteros `lista`, un número entero `comienzo` y un número entero `cantidad`, y que devuelva la suma de `cantidad` de números de `lista` empezando a contar desde el ítem con índice `comienzo`.
108 |
109 | ```python
110 | >>> sumar_seccion([2, 2, 4, 3, 10, 20, 5], 0, 3)
111 | 8 #(2 + 2 + 4 = 8)
112 | >>> sumar_seccion([2, 2, 4, 3, 10, 20, 5], 2, 4)
113 | 37 #(4 + 3 + 10 + 20 = 37)
114 | >>> sumar_seccion([2, 2, 4, 3, 10, 20, 5], 4, 1)
115 | 10
116 | ```
117 |
118 | ## 8. `es_subconjunto(subconjunto, conjunto)`
119 |
120 | Definí una función `es_subconjunto` que tome como argumento dos listas, `subconjunto` y `conjunto`, y devuelva `True` si `subconjunto` es realmente subconjunto de `conjunto`, es decir, si todos los valores de `subconjunto` están en `conjunto`.
121 |
122 | ```python
123 | >>> es_subconjunto([1], [1, 2, 3])
124 | True
125 | >>> es_subconjunto([1, 2, 3], [1, 2, 3, 4, 5])
126 | True
127 | >>> es_subconjunto([27, 49, 54], [54, 27, 8, 27, 49])
128 | True
129 | >>> es_subconjunto([1, 2, 3], [1, 2])
130 | False
131 | >>> es_subconjunto([1], [2, 3, 4])
132 | False
133 | ```
134 |
135 | ## 9. `tiene_bloque(lista)`
136 |
137 | Definí una función `tiene_bloque` que tome como argumento un lista `lista` y devuelva `True` si dicho `lista` tiene un bloque de 3 o más ítems consecutivos idénticos, o `False` si no tiene.
138 |
139 | ```python
140 | >>> tiene_bloque([1, 2, 3])
141 | False
142 | >>> tiene_bloque([1, 1, 1, 2, 3])
143 | True
144 | >>> tiene_bloque([1, 2, 3, 3, 3])
145 | True
146 | >>> tiene_bloque([1, 2, 3, 3, 3, 8])
147 | True
148 | >>> tiene_bloque([1, 2, 2, 3, 3, 4])
149 | False
150 | ```
151 |
152 | ## 10. `es_palindromo(palabra)`
153 |
154 | Definí una función `es_palindromo` que tenga como parámetro un string `palabra` y devuelva `True` si dicha palabra es palíndroma, es decir, si puede leerse de igual manera de izquierda a derecha que de derecha a izquierda, o `False` sino.
155 |
156 | ```python
157 | >>> es_palindromo('kayak')
158 | True
159 | >>> es_palindromo('reconocer')
160 | True
161 | >>> es_palindromo('mama')
162 | False
163 | >>> es_palindromo('python')
164 | False
165 | ```
166 | ## 11. `agregar_al_principio(lista, elemento)`
167 |
168 | > Definí un procedimiento `agregar_al_principio` que agregue un elemento al principio de una lista (y no al final, como lo haría `push`)
169 |
170 | ```python
171 | >>> unos_numeros = [3, 5, 9]
172 | >>> agregar_al_principio(unos_numeros, 1)
173 | >>> unos_numeros
174 | [1, 3, 5, 9]
175 | >>> unos_string = ["mundo", "!"]
176 | >>> agregar_al_principio(unos_strings, "hola")
177 | >>> unos_strings
178 | ["hola", "mundo", "!"]
179 | ```
180 |
--------------------------------------------------------------------------------
/7_frontend/2_introduccion_css/README.md:
--------------------------------------------------------------------------------
1 | # Introducción a CSS
2 |
3 | ## Repaso
4 |
5 | Repasemos lo visto:
6 |
7 | * 🥅️ Red de computadoras: ¿viste que un hilo por sí solo no sirve para mucho, pero si unís muchos formando una red podés sostener papas, evitar que una pelota salga volando de la cancha o pescar decenas de peces? ¿O que si muchas personas se conectan a través de una red social pueden lograr cosas que quizás ninguna hubiera podido hacer sola? Bueno, las redes de computadoras son parecidas: cuando unís muchas computadoras (a través de cables, ondas de WiFi, 3G o incluso satélites), estas van a poder "hablar" entre sí y compartir información. De esa forma, todas juntas, pueden hacer mucho más que una sola. ¡Y vos también!
8 |
9 | * 🌎️Internet: es la "red de redes", ese conjunto de computadoras globales que nos permiten acceder a un montón de información y servicios públicos de estados y empresas a lo largo de tooooodo el mundo. Con una conexión a internet podés consultar la información que está en Wikipedia, enviar mensajes a través de whatsapp, y usar las aplicaciones de Google Drive, Instagram o TicToc.
10 |
11 | * 🕸️La Web: es una de las "cosas" (o, más técnicamente, uno de los servicios) más obvias que podés encontrar en Internet. Se trata de un montón de información organizada en páginas (sí, las famosas páginas Web) que podés encontrar y navegar usando un navegador.
12 |
13 | * 🚢️ Navegadores: Son aplicaciones, como Firefox, Chrome o Edge, que nos permiten navegar la Web y también cuentan con algunas herramientas para ayudarnos a crear nuestras propias páginas. Ojo, no confundirlos con los _buscadores_, que son páginas concretas, como google.com o bing.com, que sirven para buscar otras páginas en toda la Web.
14 |
15 | * 🤔️ Google: sí, todo el tiempo escuchamos de este gigante de la tecnología. Pero aunque es una empresa enorme y omnipresente, para nada es la única ni es todopoderosa. Y aunque muchas veces usamos sus productos y servicios, ¡no todos son lo mismo ni son la única opción! Como vimos recién: Chrome es un navegador, pero google.com es un buscador. Y Google Drive son un conjunto de aplicaciones online para crear documentos.
16 |
17 | * 🎉️HTML: es el lenguaje con el que construimos el esqueleto de nuestras páginas Web y con el que empezaremos hacer las nuestras. En HTML todo es una etiqueta (tag), que sirve para encerrar a nuestro texto y darle significado. Vimos 3 tags (¡de entre muchísimos!):
18 |
19 | * 🗃️ Los archivos HTML deben tener extensión (es decir, terminar en) .html. Los podemos editar con Visual Code y abrirlos con cualquier navegador.
20 |
21 |
22 | ## Dando formato
23 |
24 | 1. Terminar de armar el HTML. Comentar los tags semánticos:
25 | 1. `html`
26 | 2. `head`, (donde pondremos `title`) `body`
27 | 3. `header`, `section`
28 | 2. Cargar el css en un tag `style`. Hay mejores formas de hacerlo, pero las veremos luego porque esta es la más sencilla y rápida.
29 | 3. Arrancar por un "truco" que nos va a servir muchísimo: tomar "el control" de los tamaños:
30 |
31 | ```css
32 | * {
33 | margin: 0;
34 | padding: 0;
35 | }
36 | ```
37 |
38 | 4. Vamos darle _estilo_ (es decir, formato) a nuestra cabezera. Dado que esta parte contendrá nuestro título y actividad, tiene que resaltar, así que empezaremos por darle color de fondo (`background-color`) y color de fuente (`color`):
39 |
40 | ```css
41 | header {
42 | background-color: #164e98;
43 | color: white;
44 | }
45 | ```
46 |
47 | 5. ¿Qué otras cosas pueden ayudarnos a resaltar algo? ¡El espacio! Sí, por más antiintuitivo que parezca, si querés que algo resalte, ponele mucho espacio a su alrededor, y para eso vamos a usar `padding`, que nos agrega espacio **dentro** de _la caja_ de un componente html:
48 |
49 | ```css
50 | header {
51 | background-color: #164e98;
52 | color: white;
53 | padding: 70px 0 45px 0;
54 | }
55 | ```
56 |
57 |
58 | el padding funciona como las agujas del reloj: primero te dice el espacio que va arriba, luego el de la derecha, luego el de abajo y luego el de la izquierda. Otro elemento que nos va a servir para dar espacio es el `margin` que indica cuánto hay espacio **fuera** de la caja:
59 |
60 | ```css
61 | h1 {
62 | margin: 0 0 5px;
63 | }
64 | ```
65 |
66 | 6. El centrado es otra propiedad que nos ayuda a concentrar la atención, y la podemos lograr con `text-align`.
67 |
68 | ```css
69 | header {
70 | background-color: #164e98;
71 | color: white;
72 | padding: 70px 0 45px 0;
73 | text-align: center;
74 | }
75 | ```
76 |
77 | 7. Ya empieza a tomar otra forma, ¿no? ¡Pero aún podemos hacerlo mejor! Podemos aumentar los tamaños de la fuente (con `font-size`) y hacerla mayúsculas (con `text-transform`)
78 |
79 | ```css
80 | header {
81 | background-color: #164e98;
82 | color: white;
83 | padding: 70px 0 45px 0;
84 | text-align: center;
85 | text-transform: uppercase;
86 | font-size: 20px;
87 | }
88 | ```
89 |
90 | Alternativa: cambiar el texto al h1 y h2 individualmente. (40px y 30px). ¿Qué cambia?
91 |
92 | 8. Hmm, se nos fue un poco la mano, porque ahora todo quedó muy grande. Y otra técnica para que algo resalto es hacer que haya algo que resalte menos cerca. Al fin y al cabo lo primero que queremos que se vea es nuestro nombre, ¿no? Saquémosle entonces _grosor_ a la fuente del h2:
93 |
94 | ```css
95 | h2 {
96 | font-weight: lighter;
97 | }
98 | ```
99 |
100 | 9. Y para que ocupe más espacio (¿te acordás que el espacio es importante?)
101 |
102 | ```css
103 | header {
104 | background-color: #164e98;
105 | color: white;
106 | padding: 70px 0 45px 0;
107 | text-align: center;
108 | text-transform: uppercase;
109 | font-size: 20px;
110 | letter-spacing: 5px;
111 | }
112 | ```
113 |
114 | 10. Y para cerrar, usemos `sans-serif` como `font-family`.
115 |
116 | ## Desafío
117 |
118 | ¡Ahora te toca a vos! Usando las herramientas que vimos, completá tu CV y dale formato. Podés usar todos los elementos HTML y estilos CSS que vimos hasta ahora, pero quizás quieras explorar un poco más sobre las siguiente etiquetas:
119 |
120 | 1. `ul`, `ol`, `li`
121 | 3. `hr`
122 | 2. `img`
123 |
124 | Para que te sirva de inspiración, te dejamos un `cv_ejemplo.pdf` provisto por canva.com, pero sentite libre de cambiar su estilo tanto como quieras 😁️.
125 |
126 | ¡El próximo encuentro aprenderemos a ponerlo en línea!
127 |
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/15_integración/referencia_rápida_python/README.md:
--------------------------------------------------------------------------------
1 | > Basado en https://github.com/MumukiProject/mumuki-apendice-imperativa-javascript
2 |
3 |
18 |
19 | Referencia rápida del lenguaje Python
20 |
21 | El lenguaje Python es utilizado ampliamente para construir software en todo el mundo, siendo una herramienta muy frecuente para el análisis de datos y la construcción de aplicaciones.
22 |
23 | Declaración de Funciones
24 |
25 | Las funciones en Python se declaran mediante la _palabra clave_ `def` y:
26 |
27 | * los parámetros se declaran entre paréntesis (`(` y `)`), separados por comas (`,`) ;
28 | * la primera línea (llamada cabecera) se separa del resto (cuerpo) usando un `:`;
29 | * el cuerpo se declara aplicando un nivel de tabulación;
30 | * el cuerpo de la función debe tener al menos un retorno, que se expresa mediante `return`.
31 |
32 | Ejemplo:
33 |
34 | ```python
35 | def nombre_de_la_funcion(parametro1, parametro2, parametro3):
36 | return ...
37 | ```
38 |
39 | Por otro lado, las mismas pueden ser invocadas escribiendo su nombre y, entre paréntesis y separados por comas, pasando sus argumentos:
40 |
41 | ```python
42 | nombre_de_la_funcion(argumento1, argumento2, argumento3)
43 | ```
44 |
45 | Esta forma de invocación se llama _posicional_ porque el orden de los argumentos se corresponde directamente con el orden de los parámetros. Alternativamente, se pueden pasar los argumentos de forma _nombrada_ (o _etiquetada_), en la que la correspondencia se especifica explícitamente...
46 |
47 | ```python
48 | nombre_de_la_funcion(parametro1 = argumento1, parametro2 = argumento2, parametro3 = argumento3)
49 | ```
50 |
51 | ... y ya no es necesario respetar el orden de los parámetros:
52 |
53 | ```python
54 | nombre_de_la_funcion(parametro2 = argumento2, parametro3 = argumento3, parametro1 = argumento1)
55 | ```
56 |
57 | Declaración de Procedimientos
58 |
59 | Los procedimientos se declaran de forma similar a las funciones, pero sin colocar un `return`. Además, el cuerpo del procedimiento debe tener algún tipo de _efecto_ (es decir, modificar algún elemento del programa), como por ejemplo:
60 |
61 | * modificar una variable global;
62 | * modificar una estructura de datos tal listas, diccionarios, etc.
63 |
64 | Ejemplo:
65 |
66 | ```python
67 | def nombre_del_procedimiento(parametro1, parametro2, parametro3):
68 | # acá hay que producir algún efecto
69 | ```
70 |
71 | Por otro lado, los procedimientos se invocan de la misma forma que las funciones, pero al no tener un `return`, no devolverán nada (o lo que es lo mismo, devuelve `None`, que significa _nada_ en inglés).
72 |
73 |
74 | Operadores matemáticos
75 |
76 | ```python
77 | 4 + 5
78 | 10 - 5
79 | 8 * 9
80 | 10 / 5
81 | ```
82 |
83 | Operadores lógicos
84 |
85 | ```python
86 | true or false
87 | true and false
88 | not false
89 | ```
90 |
91 | Comparaciones
92 |
93 | ```python
94 | # para cualquier tipo de dato
95 | "hola" == "hola"
96 | "hola" != "chau"
97 |
98 | # para números
99 | 4 >= 5
100 | 4 > 5
101 | 4 <= 5
102 | 4 < 5
103 | ```
104 |
105 | Alternativa Condicional
106 |
107 | Los `if`s en Python llevan su condición delante de un `:` y su cuerpo tabulado:
108 |
109 | ```python
110 | if hay_personas_en_espera():
111 | llamar_siguiente_persona()
112 | ```
113 |
114 | Además, los `if`s pueden opcionalmente tener un `else`:
115 |
116 | ```python
117 | if hay_personas_en_espera():
118 | llamar_siguiente_persona()
119 | else:
120 | esperar_siguiente_persona()
121 | ```
122 |
123 | Por último, podemos combinar varios `if`s para tomar decisiones ante múltiples condiciones usando `elif`:
124 |
125 | ```python
126 | if hay_personas_en_espera():
127 | llamar_siguiente_persona()
128 | elif el_puesto_debe_seguir_abierto():
129 | esperar_siguiente_persona()
130 | else:
131 | cerrar_puesto()
132 | ```
133 |
134 | Variables
135 |
136 | Las variables nos permiten _recordar_ valores y les daremos un valor inicial usando `=`:
137 |
138 | ```python
139 | pesos_en_mi_billetera = 100
140 | dias_que_faltan_para_el_verano = 10
141 | ```
142 |
143 | La mismas se actualizan de la misma forma, también mediante `=`:
144 |
145 | ```python
146 | pesos_en_mi_billetera = 65
147 | dias_que_faltan_para_el_verano = 7
148 | ```
149 |
150 | En ocasiones las asignaremos usando el valor anterior:
151 |
152 | ```python
153 | pesos_en_mi_billetera = pesos_en_mi_billetera * 2
154 | dias_que_faltan_para_el_verano = dias_que_faltan_para_el_verano - 1
155 | ```
156 |
157 | La asignación anterior se puede compactar combinando el signo `=` y la operación:
158 |
159 | ```python
160 | pesos_en_mi_billetera *= 2
161 | dias_que_faltan_para_el_verano -= 1
162 | ```
163 |
164 | Repetición indexada
165 |
166 | Las listas pueden ser _recorridas_, visitando y haciendo algo con cada uno de sus elementos. Para ello contamos con la estructura de control `for..in`, que lleva su generador delante de dos puntos (`:`) y su cuerpo tabulado:
167 |
168 | ```python
169 | patrimonios_de_la_humanidad = [
170 | {"declarado": 1979, "nombre": "Parque nacional Tikal", "pais": "Guatemala"},
171 | {"declarado": 1983, "nombre": "Santuario histórico de Machu Picchu", "pais": "Perú"},
172 | {"declarado": 1986, "nombre": "Parque nacional do Iguaçu", "pais": "Brasil"},
173 | {"declarado": 1995, "nombre": "Parque nacional de Rapa Nui", "pais": "Chile"},
174 | {"declarado": 2003, "nombre": "Quebrada de Humahuaca", "pais": "Argentina"}
175 | ]
176 |
177 | cantidad_patrimonios_declarados_en_este_siglo = 0
178 | for patrimonio in patrimonios_de_la_humanidad:
179 | if patrimonio["declarado"] >= 2000:
180 | cantidad_patrimonios_declarados_en_este_siglo += 1
181 | ```
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/18_integracion/README.md:
--------------------------------------------------------------------------------
1 | # MacoWins
2 |
3 | ## Introducción
4 |
5 | MacoWins es una reconocida cadena de ropa formal, con tiendas en muchas ciudades de Argentina. Recientemente, le han pedido a 2Diseños, nuestra consultora de software, que desarrolle un nuevo sistema para la gestión de sus ventas y stock.
6 |
7 | ## Modelo
8 |
9 | MacoWins guarda la información de sus productos en una lista con la siguiente forma:
10 |
11 | ```python
12 | productos = [
13 | {
14 | "codigo": 100,
15 | "nombre": "remera talle m",
16 | "categoria": "remera",
17 | "precio": 4500,
18 | "stock": 35
19 | }
20 | ]
21 | ```
22 |
23 | Además, guarda la información de cada venta así:
24 |
25 | ```python
26 | ventas = [
27 | {
28 | "codigo_producto": 100,
29 | "cantidad": 3,
30 | "fecha": "2021-09-20",
31 | "precio": 4500
32 | }
33 | ]
34 | ```
35 |
36 | ## Requerimientos
37 |
38 | Sabiendo esto, necesitamos desarrollar y probar las siguientes funciones y procedimientos:
39 |
40 | 1. `registrar_producto`: recibe un diccionario con `codigo`, `nombre`, `categoria`, `precio` y agrega un producto nuevo a la lista de productos. El `stock` del producto agregado debe estar inicialmente en cero.
41 | 1. `recargar_stock`: toma un código de producto y una cantidad de unidad de stock a agregar, e incrementa el stock correspondiente a ese producto. Si el código de producto indicado no existe, debe lanzar una excepción.
42 | 1. `hay_stock`: recibe un código de producto y dice si hay stock (es decir, si el `stock` correspondiente es mayor a cero). Si el código indicado no existe en la lista de productos, debe devolver `False`.
43 | 1. `calcular_precio_final`: toma un producto (un diccionario) y un booleano `es_extranjero` y calcula su valor final, según [la siguiente regla](https://www.argentina.gob.ar/reintegrar-impuestos-turistas-extranjeros):
44 | a. si quien calcula el precio es extranjero y el valor es mayor de $70, es el mismo valor sin cambios.
45 | b. en caso contrario, es el valor original más un 21%
46 | 1. `contar_categorias`: retorna la cantidad de categorías únicas
47 | 1. `realizar_compra`: recibe un código de producto y una cantidad de items a comprar. En base a ello, decrementa el `stock` del producto correspondiente y crea una nueva venta con la información correspondiente. Si no hay suficiente stock, lanzar una excepción.
48 | 1. `discontinuar_productos`: elimina los `productos` sin `stock`.
49 | 1. `valor_ventas_del_dia`: retorna el valor total de las ventas del día de hoy.
50 | 1. `ventas_del_anio`: retorna un listado con todas las ventas para el año actual.
51 | 1. `productos_mas_vendidos`: toma una cantidad `n` de productos y retorna los nombres de los `n` productos más vendidos
52 | 1. `actualizar_precios_por_categoria`: toma una categoría y un porcentaje, y actualiza según ese porcentaje el precio de todos los productos que tengan esa categoría. La búsqueda de categoría en este procedimiento no debe ser exacta: por ejemplo tanto si se pasa como argumento `"REMERA"`, `" REMERA"` o `"Remera"`, deben actualizarse los productos de la categoría `"remera"`.
53 |
54 | ## Ejemplos
55 |
56 | A continuación, dejamos algunos ejemplos de uso. No son todos y deberemos hacer pruebas más exahustivas y de los requerimientos que falten.
57 |
58 |
59 | ```python
60 | # productos de ejemplo
61 | >>> remera_talle_m = {
62 | "codigo": 100,
63 | "nombre": "remera talle m",
64 | "categoria": "remera",
65 | "precio": 4500
66 | }
67 | >>> pulserita = {
68 | "codigo": 1098,
69 | "nombre": "pulserita de tela verde",
70 | "categoria": "accesorios",
71 | "precio": 50
72 | }
73 | >>> remera_talle_s = {
74 | "codigo": 99,
75 | "nombre": "remera talle s",
76 | "categoria": "remera",
77 | "precio": 4500
78 | }
79 | >>> productos
80 | [] # inicialmente no hay productos cargados
81 | >>> registrar_producto(remera_talle_m)
82 | >>> productos # ahora hay un producto
83 | [
84 | {
85 | "codigo": 100,
86 | "nombre": "remera talle m",
87 | "categoria": "remera",
88 | "precio": 4500,
89 | "stock": 0,
90 | }
91 | ]
92 | >>> recargar_stock(45, 6)
93 | Traceback (most recent call last):
94 | File (...)
95 | ValueError: producto desconocido # ups, no existía ese producto
96 | >>> recargar_stock(100, 15) # incrementamos una vez veces
97 | >>> recargar_stock(100, 2) # incrementamos otra vez más
98 | >>> productos
99 | [
100 | {
101 | "codigo": 100,
102 | "nombre": "remera talle m",
103 | "categoria": "remera",
104 | "precio": 4500,
105 | "stock": 17
106 | }
107 | ]
108 | >>> hay_stock(100)
109 | True # porque el producto 100 tiene 17 unidades en stock
110 | >>> hay_stock(12)
111 | False # porque ni siquiera existe
112 | >>> calcular_precio_final_producto(remera_talle_m, False)
113 | 5445.0
114 | >>> calcular_precio_final_producto(remera_talle_m, True)
115 | 4500
116 | >>> contar_categorias()
117 | 1
118 | >>> registrar_producto(remera_talle_s)
119 | >>> contar_categorias()
120 | 1
121 | >>> registrar_producto(pulserita)
122 | >>> contar_categorias()
123 | 2 # recién ahora hay dos categorias: las remeras y los accesorios
124 | ```
125 |
126 | ## Ayudas
127 |
128 | ### Parámetros opcionales
129 |
130 | Cuando declarás una función, podés indicar un valor por defecto para su último o últimos parámetros. Por ejemplo:
131 |
132 | ```python
133 | def numero_magico(duplicar=False):
134 | if duplicar:
135 | return 84
136 | else:
137 | return 42
138 | ```
139 |
140 | De esa forma, podés invocar tu función como siempre...
141 |
142 | ```python
143 | >>> numero_magico(False)
144 | 42
145 | >>> numero_magico(True)
146 | 84
147 | ```
148 |
149 | Pero también podes hacerlo sin especificar el argumento o explicitando su nombre:
150 |
151 | ```python
152 | >>> numero_magico()
153 | 42
154 | >>> numero_magico(duplicar=True)
155 | 84
156 | ```
157 |
158 | Esto no permite hacer cosas nuevas, pero sí hacer a tu código más simple y claro de usar.
159 |
160 | ### Día de hoy
161 |
162 | Para obtener el día de hoy en formato "2022-09-01" (es decir, _año-mes-dia_) podés hacer lo siguiente:
163 |
164 | ```python
165 | >>> from datetime import date
166 | >>> date.strftime(date.today(), "%Y-%m-%d")
167 | '2022-09-21'
168 | ```
169 |
170 | ### Lanzar excepciones
171 |
172 | Lanzar excepciones nos permite generar errores de forma _controlada_, es decir, sabiendo claramente dónde los estamos produciendo y por qué, e indicando un mensaje claro. Esto se hace mediante el comando `raise`:
173 |
174 | ```python
175 | raise ValueError("producto desconocido")
176 | ```
177 |
178 | Cuando se lanza una excepción, el programa, función o procedimiento aborta su ejecución y no se siguen procesando las instrucciones posteriores.
179 |
180 | Si estás dentro de una función, te va a convenir escribirlo de la siguiente forma:
181 |
182 | ```python
183 | def function_misteriosa():
184 | if condicion_de_error_misteriosa():
185 | raise ValueError("mensaje descriptivo")
186 |
187 | # resto del código acá
188 | ```
189 |
190 | ## Modalidad de entrega
191 |
192 | Entregar como un archivo `.py`, el cual debe tener todas las funciones, procedimientos y datos de ejemplo necesarios para probar el código. Para transportar el código de una máquina a otra, recomendamos alguna de las siguientes opciones:
193 |
194 | * Usar un pendrive
195 | * Enviarlo por mail
196 | * Guardarlo en un archivo en Google Drive
197 | * Guardarlo en un gist en https://gist.github.com/. Para ello es necesario hacer antes una cuenta.
198 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | activesupport (6.0.4.1)
5 | concurrent-ruby (~> 1.0, >= 1.0.2)
6 | i18n (>= 0.7, < 2)
7 | minitest (~> 5.1)
8 | tzinfo (~> 1.1)
9 | zeitwerk (~> 2.2, >= 2.2.2)
10 | addressable (2.8.0)
11 | public_suffix (>= 2.0.2, < 5.0)
12 | coffee-script (2.4.1)
13 | coffee-script-source
14 | execjs
15 | coffee-script-source (1.11.1)
16 | colorator (1.1.0)
17 | commonmarker (0.17.13)
18 | ruby-enum (~> 0.5)
19 | concurrent-ruby (1.1.9)
20 | dnsruby (1.61.7)
21 | simpleidn (~> 0.1)
22 | em-websocket (0.5.2)
23 | eventmachine (>= 0.12.9)
24 | http_parser.rb (~> 0.6.0)
25 | ethon (0.14.0)
26 | ffi (>= 1.15.0)
27 | eventmachine (1.2.7)
28 | execjs (2.8.1)
29 | faraday (1.7.0)
30 | faraday-em_http (~> 1.0)
31 | faraday-em_synchrony (~> 1.0)
32 | faraday-excon (~> 1.1)
33 | faraday-httpclient (~> 1.0.1)
34 | faraday-net_http (~> 1.0)
35 | faraday-net_http_persistent (~> 1.1)
36 | faraday-patron (~> 1.0)
37 | faraday-rack (~> 1.0)
38 | multipart-post (>= 1.2, < 3)
39 | ruby2_keywords (>= 0.0.4)
40 | faraday-em_http (1.0.0)
41 | faraday-em_synchrony (1.0.0)
42 | faraday-excon (1.1.0)
43 | faraday-httpclient (1.0.1)
44 | faraday-net_http (1.0.1)
45 | faraday-net_http_persistent (1.2.0)
46 | faraday-patron (1.0.0)
47 | faraday-rack (1.0.0)
48 | ffi (1.15.3)
49 | forwardable-extended (2.6.0)
50 | gemoji (3.0.1)
51 | github-pages (217)
52 | github-pages-health-check (= 1.17.2)
53 | jekyll (= 3.9.0)
54 | jekyll-avatar (= 0.7.0)
55 | jekyll-coffeescript (= 1.1.1)
56 | jekyll-commonmark-ghpages (= 0.1.6)
57 | jekyll-default-layout (= 0.1.4)
58 | jekyll-feed (= 0.15.1)
59 | jekyll-gist (= 1.5.0)
60 | jekyll-github-metadata (= 2.13.0)
61 | jekyll-mentions (= 1.6.0)
62 | jekyll-optional-front-matter (= 0.3.2)
63 | jekyll-paginate (= 1.1.0)
64 | jekyll-readme-index (= 0.3.0)
65 | jekyll-redirect-from (= 0.16.0)
66 | jekyll-relative-links (= 0.6.1)
67 | jekyll-remote-theme (= 0.4.3)
68 | jekyll-sass-converter (= 1.5.2)
69 | jekyll-seo-tag (= 2.7.1)
70 | jekyll-sitemap (= 1.4.0)
71 | jekyll-titles-from-headings (= 0.5.3)
72 | jemoji (= 0.12.0)
73 | kramdown (= 2.3.1)
74 | kramdown-parser-gfm (= 1.1.0)
75 | liquid (= 4.0.3)
76 | mercenary (~> 0.3)
77 | minima (= 2.5.1)
78 | nokogiri (>= 1.10.4, < 2.0)
79 | rouge (= 3.26.0)
80 | terminal-table (~> 1.4)
81 | github-pages-health-check (1.17.2)
82 | addressable (~> 2.3)
83 | dnsruby (~> 1.60)
84 | octokit (~> 4.0)
85 | public_suffix (>= 2.0.2, < 5.0)
86 | typhoeus (~> 1.3)
87 | html-pipeline (2.14.0)
88 | activesupport (>= 2)
89 | nokogiri (>= 1.4)
90 | http_parser.rb (0.6.0)
91 | i18n (0.9.5)
92 | concurrent-ruby (~> 1.0)
93 | jekyll (3.9.0)
94 | addressable (~> 2.4)
95 | colorator (~> 1.0)
96 | em-websocket (~> 0.5)
97 | i18n (~> 0.7)
98 | jekyll-sass-converter (~> 1.0)
99 | jekyll-watch (~> 2.0)
100 | kramdown (>= 1.17, < 3)
101 | liquid (~> 4.0)
102 | mercenary (~> 0.3.3)
103 | pathutil (~> 0.9)
104 | rouge (>= 1.7, < 4)
105 | safe_yaml (~> 1.0)
106 | jekyll-avatar (0.7.0)
107 | jekyll (>= 3.0, < 5.0)
108 | jekyll-coffeescript (1.1.1)
109 | coffee-script (~> 2.2)
110 | coffee-script-source (~> 1.11.1)
111 | jekyll-commonmark (1.3.1)
112 | commonmarker (~> 0.14)
113 | jekyll (>= 3.7, < 5.0)
114 | jekyll-commonmark-ghpages (0.1.6)
115 | commonmarker (~> 0.17.6)
116 | jekyll-commonmark (~> 1.2)
117 | rouge (>= 2.0, < 4.0)
118 | jekyll-default-layout (0.1.4)
119 | jekyll (~> 3.0)
120 | jekyll-feed (0.15.1)
121 | jekyll (>= 3.7, < 5.0)
122 | jekyll-gist (1.5.0)
123 | octokit (~> 4.2)
124 | jekyll-github-metadata (2.13.0)
125 | jekyll (>= 3.4, < 5.0)
126 | octokit (~> 4.0, != 4.4.0)
127 | jekyll-mentions (1.6.0)
128 | html-pipeline (~> 2.3)
129 | jekyll (>= 3.7, < 5.0)
130 | jekyll-optional-front-matter (0.3.2)
131 | jekyll (>= 3.0, < 5.0)
132 | jekyll-paginate (1.1.0)
133 | jekyll-readme-index (0.3.0)
134 | jekyll (>= 3.0, < 5.0)
135 | jekyll-redirect-from (0.16.0)
136 | jekyll (>= 3.3, < 5.0)
137 | jekyll-relative-links (0.6.1)
138 | jekyll (>= 3.3, < 5.0)
139 | jekyll-remote-theme (0.4.3)
140 | addressable (~> 2.0)
141 | jekyll (>= 3.5, < 5.0)
142 | jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
143 | rubyzip (>= 1.3.0, < 3.0)
144 | jekyll-sass-converter (1.5.2)
145 | sass (~> 3.4)
146 | jekyll-seo-tag (2.7.1)
147 | jekyll (>= 3.8, < 5.0)
148 | jekyll-sitemap (1.4.0)
149 | jekyll (>= 3.7, < 5.0)
150 | jekyll-theme-minimal (0.2.0)
151 | jekyll (> 3.5, < 5.0)
152 | jekyll-seo-tag (~> 2.0)
153 | jekyll-titles-from-headings (0.5.3)
154 | jekyll (>= 3.3, < 5.0)
155 | jekyll-watch (2.2.1)
156 | listen (~> 3.0)
157 | jemoji (0.12.0)
158 | gemoji (~> 3.0)
159 | html-pipeline (~> 2.2)
160 | jekyll (>= 3.0, < 5.0)
161 | kramdown (2.3.1)
162 | rexml
163 | kramdown-parser-gfm (1.1.0)
164 | kramdown (~> 2.0)
165 | liquid (4.0.3)
166 | listen (3.7.0)
167 | rb-fsevent (~> 0.10, >= 0.10.3)
168 | rb-inotify (~> 0.9, >= 0.9.10)
169 | mercenary (0.3.6)
170 | mini_portile2 (2.6.1)
171 | minima (2.5.1)
172 | jekyll (>= 3.5, < 5.0)
173 | jekyll-feed (~> 0.9)
174 | jekyll-seo-tag (~> 2.1)
175 | minitest (5.14.4)
176 | multipart-post (2.1.1)
177 | nokogiri (1.12.3)
178 | mini_portile2 (~> 2.6.1)
179 | racc (~> 1.4)
180 | octokit (4.21.0)
181 | faraday (>= 0.9)
182 | sawyer (~> 0.8.0, >= 0.5.3)
183 | pathutil (0.16.2)
184 | forwardable-extended (~> 2.6)
185 | public_suffix (4.0.6)
186 | racc (1.5.2)
187 | rb-fsevent (0.11.0)
188 | rb-inotify (0.10.1)
189 | ffi (~> 1.0)
190 | rexml (3.2.5)
191 | rouge (3.26.0)
192 | ruby-enum (0.9.0)
193 | i18n
194 | ruby2_keywords (0.0.5)
195 | rubyzip (2.3.2)
196 | safe_yaml (1.0.5)
197 | sass (3.7.4)
198 | sass-listen (~> 4.0.0)
199 | sass-listen (4.0.0)
200 | rb-fsevent (~> 0.9, >= 0.9.4)
201 | rb-inotify (~> 0.9, >= 0.9.7)
202 | sawyer (0.8.2)
203 | addressable (>= 2.3.5)
204 | faraday (> 0.8, < 2.0)
205 | simpleidn (0.2.1)
206 | unf (~> 0.1.4)
207 | terminal-table (1.8.0)
208 | unicode-display_width (~> 1.1, >= 1.1.1)
209 | thread_safe (0.3.6)
210 | typhoeus (1.4.0)
211 | ethon (>= 0.9.0)
212 | tzinfo (1.2.9)
213 | thread_safe (~> 0.1)
214 | tzinfo-data (1.2021.1)
215 | tzinfo (>= 1.0.0)
216 | unf (0.1.4)
217 | unf_ext
218 | unf_ext (0.0.7.7)
219 | unicode-display_width (1.7.0)
220 | wdm (0.1.1)
221 | zeitwerk (2.4.2)
222 |
223 | PLATFORMS
224 | ruby
225 |
226 | DEPENDENCIES
227 | github-pages (~> 217)
228 | jekyll-theme-minimal (~> 0.2)
229 | kramdown-parser-gfm
230 | minima (~> 2.0)
231 | tzinfo (~> 1.2)
232 | tzinfo-data
233 | wdm (~> 0.1.0)
234 |
235 | BUNDLED WITH
236 | 2.1.4
237 |
--------------------------------------------------------------------------------
/4_programacion_con_objetos/2_delegacion_y_polimorfismo/README.md:
--------------------------------------------------------------------------------
1 | Delegación y Polimorfismo
2 | =========================
3 |
4 |
5 | ## Sueldo de Pepe
6 |
7 |
8 | > Ejercicio basado en [El sueldo de Pepe](https://docs.google.com/document/d/1DQNuJwO3m6o_0-31tld94eJKJSQQ2TsjqBBY_rOVho4/edit)
9 |
10 | Desarrollar los objetos necesarios para calcular el sueldo de Pepe. El sueldo de pepe se calcula así:
11 |
12 | ```
13 | sueldo = sueldo base + bono x presentismo + bono x resultados.
14 | ```
15 |
16 | El sueldo base es el de la categoría, hay dos categorías:
17 | * gerentes que ganan $1000 de sueldo base,
18 | * cadetes que ganan $1500.
19 |
20 | Hay dos bonos por presentismo:
21 | * Es $100 si la persona a quien se aplica no faltó nunca, $50 si faltó un día, $0 en cualquier otro caso;
22 | * En el otro, nada.
23 |
24 | Hay tres posibilidades para el bono por resultados:
25 | * Un porcentaje sobre el sueldo base
26 | * Un valor fijo
27 | * O nada
28 |
29 | Probar cambiándole a pepe la categoría, la cantidad de días que falta y haciendo que cumpla sus objetivos. Probar también cambiar sus bonos por presentismo y por resultados, o con nosotros empelados con diferente categoría y bonos. En cada caso preguntarle su sueldo.
30 |
31 |
32 | ```python
33 | >>>
34 | >>> dani.faltar()
35 | >>> dani.faltas
36 | 1
37 | >>> dani.sueldo_total()
38 | 1050
39 | >>> dani.faltar()
40 | >>> dani.faltar()
41 | >>> dani.faltar()
42 | >>> dani.sueldo_total()
43 | 1000
44 | >>>
45 | >>> umi.sueldo_total()
46 | 1500
47 | >>> umi.cumplir_objetivos()
48 | >>> umi.sueldo_total()
49 | 1725.0
50 | >>> umi.bono_resultados = BonoFijoPorResultados(80)
51 | >>> umi.sueldo_total()
52 | 1580
53 | >>>
54 | ```
55 |
56 |
57 | ## Trenes y depósitos
58 |
59 |
60 | Una administradora ferroviaria necesita una aplicación que le ayude a manejar las formaciones que tiene disponibles en distintos depósitos.
61 | Una formación es lo que habitualmente llamamos “un tren”, tiene una o varias locomotoras, y uno o varios vagones. Hay vagones de pasajeros y vagones de carga.
62 | En cada depósito hay: formaciones ya armadas, y locomotoras sueltas que pueden ser agregadas a una formación.
63 |
64 | De cada vagón de pasajeros se conoce el largo en metros, y el ancho útil también en metros. La cantidad de pasajeros que puede transportar un vagón de pasajeros es:
65 | Si el ancho útil es de hasta 2.5 metros: metros de largo * 8.
66 | Si el ancho útil es de más de 2.5 metros: metros de largo * 10.
67 | P.ej., si tenemos dos vagones de pasajeros, los dos de 10 metros de largo, uno de 2 metros de ancho útil, y otro de 3 metros de ancho útil, entonces el primero puede llevar 80 pasajeros, y el segundo puede llevar 100.
68 | Un vagón de pasajeros no puede llevar carga.
69 |
70 | De cada vagón de carga se conoce la carga máxima que puede llevar, en kilos. Un vagón de carga no puede llevar ningún pasajero.
71 | No hay vagones mixtos.
72 |
73 | El peso máximo de un vagón, medido en kilos, se calcula así:
74 | Para un vagón de pasajeros: cantidad de pasajeros que puede llevar * 80.
75 | Para un vagón de carga: la carga máxima que puede llevar + 160 (en cada vagón de carga van dos guardas).
76 |
77 | De cada locomotora se sabe: su peso, el peso máximo que puede arrastrar, y su velocidad máxima. P.ej. puedo tener una locomotora que pesa 1000 kg, puede arrastrar hasta 12000 kg, y su velocidad máxima es de 80 km/h. Obviamente se tiene que arrastrar a ella misma, entonces no le puedo cargar 12000 kg de vagones, solamente 11000; diremos que este es su “arrastre útil”.
78 |
79 | Modelar la situación descripta de acuerdo al paradigma de objetos, escribiendo el código en lenguaje Wollok, de manera de poder saber:
80 | El total de pasajeros que puede transportar una formación.
81 | Cuántos vagones livianos tiene una formación; un vagón es liviano si su peso máximo es menor a 2500 kg.
82 | La velocidad máxima de una formación, que es el mínimo entre las velocidades máximas de las locomotoras.
83 | Si una formación es eficiente; es eficiente si cada una de sus locomotoras arrastra, al menos, 5 veces su peso (el de la locomotora misma).
84 | Si una formación puede moverse. Una formación puede moverse si el arrastre útil total de las locomotoras es mayor o igual al peso máximo total de los vagones.
85 | Cuántos kilos de empuje le faltan a una formación para poder moverse, que es: 0 si ya se puede mover, y (peso máximo total de los vagones – arrastre útil total de las locomotoras) en caso contrario.
86 | Dado un depósito, el conjunto formado por el vagón más pesado de cada formación; se espera un conjunto de vagones.
87 | Si un depósito necesita un conductor experimentado.
88 | Un depósito necesita un conductor experimentado si alguna de sus formaciones es compleja. Una formación es compleja si: tiene más de 20 unidades (sumando locomotoras y vagones), o el peso total (sumando locomotoras y vagones) es de más de 10000 kg.
89 | Agregar, dentro de un depósito, una locomotora a una formación determinada, de forma tal que la formación pueda moverse.
90 | Si la formación ya puede moverse, entonces no se hace nada.
91 | Si no, se le agrega una locomotora suelta del depósito cuyo arrastre útil sea mayor o igual a los kilos de empuje que le faltan a la formación. Si no hay ninguna locomotora suelta que cumpla esta condición, no se hace nada.
92 |
93 | O sea: indicar qué clases se necesitan, qué variables de instancia se necesitan en cada clase, qué mensajes van a entender las instancias de cada clase, y escribir los métodos correspondientes.
94 | Para cada punto, indicar a qué objeto se le pide lo que se indica, con qué mensaje, qué parámetros, y qué devuelve.
95 | Para el punto 8, indicar en qué otros objetos delega el responsable de hacer lo que se pide, y qué delega (indicar lo que se delega en castellano). Si hay una cadena de delegaciones (al objeto 1 le piden algo, entonces delega algo en el objeto 2, y el objeto 2 para hacer lo que le pidió el 1 tiene que delegar otra cosa en otro objeto 3) indicarla.
96 |
97 | Ejercicio 2 – Mascota Virtual
98 | Modelar una mascota virtual, onda Tamagotchi, incluyendo los mensajes correspondientes a las acciones de comer y jugar, y la pregunta acerca de si puede jugar o no.
99 |
100 | También hay que poder conocer el nivel de contenta de una mascota, que es un número entero mayor o igual que 0, donde a mayor nivel, más contenta está la mascota.
101 |
102 | Una mascota puede estar aburrida, hambrienta o contenta; y su comportamiento depende de en qué estado esté. Veamos:
103 |
104 | Cuando una mascota come, pasa lo siguiente:
105 | Si está hambrienta, se pone contenta.
106 | Si está contenta, su nivel se incrementa en una unidad.
107 | Si está aburrida, y hace más de 80 minutos que está aburrida, entonces se pone contenta.
108 | Si está aburrida desde hace 80 minutos o menos, entonces no le pasa nada, no cambia nada.
109 |
110 | Cuando una mascota juega, pasa lo siguiente:
111 | Si está contenta, su nivel se incrementa en dos unidades.
112 | Si está aburrida, se pone contenta.
113 | No produce ningún efecto jugar con la mascota si está hambrienta.
114 |
115 | Una mascota puede jugar si está contenta o aburrida, si está hambrienta no.
116 |
117 | NO SE PUEDE CONSULTAR DE NINGUNA MANERA EL ESTADO ACTUAL DE LA MASCOTA.
118 | Esto quiere decir que está prohibido hacer comparaciones del tipo estado == “contento” o cualquiera similar utilizando mensajes especiales.
119 |
120 | Responder las siguientes preguntas:
121 | Indique en palabras los pasos necesarios para incorporar un nuevo estado “Triste” en la mascota, de manera que quede listo para funcionar.
122 | Indique cuál sería la prueba en un test similar para darles de comer a todas las mascotas que están dentro de una colección “mascotas”.
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/03_práctica_funciones_y_parámetros/README.md:
--------------------------------------------------------------------------------
1 | > Basado en https://github.com/MumukiProject/mumuki-guia-javascript-ejercitacion-funciones
2 |
3 | # Práctica Funciones
4 |
5 | ## 1. `sumar(a, b)`
6 |
7 | Definí una función `sumar` que tome dos números y devuelva la suma de ellos
8 |
9 | ```python
10 | >>> sumar(2, 3)
11 | 5
12 | >>> sumar(1.2, 3.4)
13 | 4.6
14 | >>> sumar(3, -5)
15 | -2
16 | ```
17 |
18 | ## 2. `restar(a, b)`
19 |
20 | Definí una función `restar` que tome dos números y devuelva la resta de ellos
21 |
22 | ```python
23 | >>> restar(3, 2)
24 | 1
25 | >>> restar(10, 5.5)
26 | 4.5
27 | >>> restar(3, 5)
28 | -2
29 | ```
30 |
31 | ## 3. `multiplicar(a, b)`
32 |
33 | Definí una función `multiplicar` que tome dos números y devuelva la multiplicación de ellos
34 |
35 | ```python
36 | >>> multiplicar(2, 3)
37 | 6
38 | >>> multiplicar(4, 0.5)
39 | 2
40 | ```
41 |
42 | ## 4. `dividir(a, b)`
43 |
44 | Definí una función `dividir` que tome dos números y devuelva la suma de ellos
45 |
46 | ```python
47 | >>> dividir(12, 3)
48 | 4
49 | >>> dividir(8, 4)
50 | 2
51 | >>> sumar(30, 6)
52 | 5
53 | ```
54 |
55 | ## 5. `es_par(numero)`
56 |
57 | Definí una función `es_par` que tome como argumento un número y si dicho números es par
58 |
59 | **TIP**: un número es par si divido por 2 el resto (o módulo) de esa operación es 0
60 |
61 | ```python
62 | >>> es_par(2)
63 | True
64 | >>> es_par(3)
65 | False
66 | ```
67 |
68 | ## 6. `es_impar(numero)`
69 |
70 | Definí una función `es_impar` que tome como argumento un número y si dicho números es impar
71 |
72 | **TIP**: un número es impar si divido por 2 el resto (o módulo) de esa operación no es 0
73 |
74 | ```python
75 | >>> es_impar(2)
76 | False
77 | >>> es_impar(3)
78 | True
79 | ```
80 |
81 | ## 7. `calcular_area_triangulo(base, altura)`
82 |
83 | Definí una función `calcular_area_triangulo` que tome la base y la altura de un triángulo y devuelva el área del mismo
84 |
85 | ```python
86 | >>> calcular_area_triangulo(3, 4)
87 | 6
88 | >>> calcular_area_triangulo(5, 6)
89 | 40
90 | ```
91 |
92 | ## 8. `nombre_completo(nombre, apellido)`
93 |
94 | Definí una función `nombre_completo` que tome como argumento un nombre y un apellido y devuelva un string con la unión de ambos valores
95 |
96 | ```python
97 | >>> nombre_completo("Ada", "Lovelace")
98 | "Ada Lovelace"
99 | ```
100 |
101 | ## 9. `saludar(nombre)`
102 |
103 | Definí una función `saludar` que tome un nombre y devuelva un saludo que lo incluya.
104 |
105 | ```python
106 | >>> saludar("Ada")
107 | "Hola Ada, un gusto conocerte"
108 | ```
109 |
110 | ## 10. `saludar_completo(nombre, apellido)`
111 |
112 | Usando las funciones anteriores (`nombre_completo` y `saludar`), definí una función `saludar_completo` que tome un nombre y un apellido y devuelva un saludo usando el nombre completo de la persona.
113 |
114 | ```python
115 | >>> saludar_completo("Ada", "Lovelace")
116 | Hola Ada Lovelace, un gusto conocerte
117 | ```
118 |
119 |
120 | ## 11. obtener_datos_ciudad(nombre, poblacion, pais)
121 |
122 | Definí una función `obtener_datos_ciudad` que tome un string `nombre`, un número `poblacion` y un string `pais` y devuelva string con el siguiente formato: `La ciudad de NOMBRE tiene una población de POBLACION habitantes y está ubicada en PAIS`
123 |
124 | ```python
125 | >>> obtener_datos_ciudad("Santa Fe", 545606, "Argentina")
126 | "La ciudad de Santa Fe tiene una población de 545606 habitantes y está ubicada en Argentina"
127 | ```
128 |
129 |
130 | ## 21. `convertir_horas_en_segundos(horas)`
131 |
132 | Definí una función `convertir_horas_en_segundos` que tome como argumento un número de horas y devuelva la conversión a segundos de dicha cantidad de horas
133 |
134 | ```python
135 | >>> convertir_horas_en_segundos(1)
136 | 3600
137 | >>> convertir_horas_en_segundos(3)
138 | 10800
139 | >>> convertir_horas_en_segundos(4.5)
140 | 16200
141 | ```
142 |
143 | ## 13. `calcular_perimetro_rectangulo(ancho, alto)`
144 |
145 | Definí una función `calcular_perimetro_rectangulo` que tome el ancho y el alto de un rectángulo y devuelva su perímetro
146 |
147 | ```python
148 | >>> calcular_perimetro_rectangulo(3.2, 5)
149 | 16.4
150 | >>> calcular_perimetro_rectangulo(10, 20)
151 | 60
152 | ```
153 |
154 | ## 14. `calcular_porcentaje(numero, porcentaje)`
155 |
156 | Definí una función `calcular_porcentaje` que tome un número y un porcentaje y devuelva el valor del porcentaje correspondiente al número
157 |
158 | ```python
159 | >>> calcular_porcentaje(100, 15)
160 | 15
161 | >>> calcular_porcentaje(10, 50)
162 | 5
163 | >>> calcular_porcentaje(200, 10)
164 | 20
165 | ```
166 |
167 | ## 15. `sumar_porcentaje(numero, porcentaje)`
168 |
169 | Definí una función `sumar_porcentaje` que tome un número y un porcentaje y devuelva la suma de dicho número con la de su porcentaje. Usá la función `calcular_porcentaje` para obtener el porcentaje a sumar
170 |
171 | ```python
172 | >>> sumar_porcentaje(100, 15)
173 | 115
174 | >>> sumar_porcentaje(10, 50)
175 | 15
176 | >>> sumar_porcentaje(200, 10)
177 | 220
178 | ```
179 |
180 | ## 16. `restar_porcentaje(numero, porcentaje)`
181 |
182 | Definí una función `restar_porcentaje` que tome un número y un porcentaje y devuelva la resta de dicho número con la de su porcentaje. Usá la función `calcular_porcentaje` para obtener el porcentaje a restar
183 |
184 | ```python
185 | >>> restar_porcentaje(100, 15)
186 | 85
187 | >>> restar_porcentaje(10, 40)
188 | 6
189 | >>> restar_porcentaje(200, 10)
190 | 180
191 | ```
192 |
193 | ## 17. `calcular_fps(fps, minutos)`
194 |
195 | FPS son _cuadros por segundo_ (_frames per second_). Creá una una función `calcular_fps` que tome una cantidad de cuadros por segundo y una cantidad de minutos, y devuelva cuántos cuadros hubo en esa cantidad de minutos
196 |
197 | ```python
198 | >>> calcular_fps(1, 1)
199 | 60
200 | >>> calcular_fps(10, 2)
201 | 1200
202 | >>> calcular_fps(2, 3)
203 | 360
204 | ```
205 |
206 | ## 18. `obtener_rivales(a, b)`
207 |
208 | Definí una función `obtener_rivales` que tome dos strings y devuelva un string con el formato `uno vs. otro`
209 |
210 | ```python
211 | >>> obtener_rivales("python", "Python")
212 | "python vs. Python"
213 | >>> obtener_rivales("Coca", "Pepsi")
214 | "Coca vs. Pepsi"
215 | >>> obtener_rivales("Perros", "Gatos")
216 | "Perros vs. Gatos"
217 | ```
218 |
219 | ## 19. `generar_email(usuario, dominio)`
220 |
221 | Definí una función `generar_email` que tome dos string `usuario` y `dominio` y el un string email con el formato `usuario@dominio.com`
222 |
223 | ```python
224 | >>> generar_email("adalovelace", "gmail")
225 | "adalovelace@gmail.com"
226 | ```
227 |
228 |
229 | ## 20. `hace_calor(temperatura)`
230 |
231 | Definí una función `hace_calor` que tome como argumento un número `temperatura` y si hace calor (22 grados o más)
232 |
233 | ```python
234 | >>> hace_calor(12)
235 | False
236 | >>> hace_calor(22)
237 | True
238 | >>> hace_calor(32)
239 | True
240 | ```
241 |
242 | ## 21. `hace_frio(temperatura)`
243 |
244 | Definí una función `hace_frio` que tome como argumento un número `temperatura` y si hace frio (12 grados o menos)
245 |
246 | ```python
247 | >>> hace_frio(12)
248 | True
249 | >>> hace_frio(22)
250 | False
251 | >>> hace_frio(3)
252 | True
253 | >>> hace_frio(-2)
254 | True
255 | ```
256 |
257 | ## 22. `calcular_puntaje(facil, normal, dificil)`
258 |
259 | Definí una función `calcular_puntaje` que calcule el puntaje de un examen que consiste en ejercicios de distinto nivel. Debe tomar como argumento tres que consisten en la cantidad de ejercicios resueltos en cada nivel y devolver un número con el puntaje correspondiente.
260 |
261 | El puntaje se calcula de la siguiente forma:
262 |
263 | ```
264 | facil: 3 puntos
265 | normal: 5 puntos
266 | dificil: 10 puntos
267 | ```
268 |
269 | ```python
270 | >>> calcular_puntaje(3, 0, 0)
271 | 9
272 | >>> calcular_puntaje(0, 2, 1)
273 | 20
274 | >>> calcular_puntaje(5, 1, 2)
275 | 40
276 | ```
277 |
278 | ## 23. `acepta_deposito(monto)`
279 |
280 | Definí una función `acepta_deposito` que tome como argumento un número `monto` y si el `monto` es divisible por 10
281 |
282 | ```python
283 | >>> acepta_deposito(440)
284 | True
285 | >>> acepta_deposito(123)
286 | False
287 | >>> acepta_deposito(500.50)
288 | False
289 | >>> acepta_deposito(1000)
290 | True
291 | ```
292 |
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/15_integración/referencia_rápida_pandas/README.md:
--------------------------------------------------------------------------------
1 | Guía Rápida Pandas
2 | ==================
3 |
4 | # Lectura
5 |
6 | ## `read_csv`
7 |
8 | Nos permite leer un archivo [CSV](https://es.wikipedia.org/wiki/Valores_separados_por_comas) a partir de una URL (es decir, de la dirección donde se encuentra el archivo). Ejemplo:
9 |
10 | ```python
11 | >>> pd.read_csv("http://cdn.buenosaires.gob.ar/datosabiertos/datasets/arbolado-publico-lineal/arbolado-publico-lineal.csv")
12 | long | lat | id_arbol | altura_tot | diametro | inclinacio | nombre_com
13 | 0 | -58.389059 | -34.620026 | 2430 | 7 | 20 | 17 | Fresno americano
14 | 1 | -58.389211 | -34.620034 | 2431 | 8 | 33 | 16 | Fresno americano
15 | 2 | -58.389269 | -34.620037 | 2432 | 2 | 3 | 0 | Ligustro
16 | 3 | -58.389525 | -34.620052 | 2433 | 9 | 17 | 0 | Arce negundo
17 | 4 | -58.389608 | -34.620057 | 2434 | 6 | 13 | 14 | Fresno americano
18 | ... | ... | ... | ... | .. | .. | ... | ...
19 | 372694 | -58.493294 | -34.623746 | 938000239 | 10 | 52 | 5 | Paraíso
20 | 372695 | -58.493241 | -34.623788 | 938000240 | 11 | 60 | 10 | Paraíso
21 | 372696 | -58.493176 | -34.623840 | 938000241 | 10 | 56 | 20 | Paraíso
22 | 372697 | -58.493116 | -34.623887 | 938000242 | 10 | 58 | 0 | Paraíso
23 | 372698 | -58.493086 | -34.624042 | 938000243 | 3 | 5 | 0 | Crespón (Àrbol de Júpiter)
24 | ```
25 |
26 | Típicamente nos convendrá guardar este resultado en una variable para usarlo más tarde:
27 |
28 | ```python
29 | >>> arbolado = pd.read_csv("http://cdn.buenosaires.gob.ar/datosabiertos/datasets/arbolado-publico-lineal/arbolado-publico-lineal.csv")
30 | ```
31 |
32 | # Consulta
33 |
34 | ## `iloc`
35 |
36 | Nos permite obtener una fila segun el índice. Ejemplo:
37 |
38 | ```python
39 | # nos devuelve la fila cuyo índice es 15
40 | arbolado.iloc[15]
41 | ```
42 | :warning: obtener una fila por índice `n` no es necesariamente lo mismo que obtener la fila en la `n`-ésima posición. Ver manejo de índices.
43 |
44 | ## `head`
45 |
46 | Nos permite obtener un nuevo `DataFrame` con únicamente las primeras `n` filas. Por ejmplo:
47 |
48 | ```python
49 | # nos da los primeros 10 elementos
50 | arbolado.head(10)
51 | ```
52 |
53 | :warning: Head no ordena al `DataFrame`, solo nos dan los primeros o últimos
54 | en el orden en que estén, por tanto nos será útil ordenar previamente según algún criterio. Por ejemplo:
55 |
56 | ```python
57 | arbolado.sort_values('diametro').tail(2)
58 | ```
59 |
60 | ## `tail`
61 |
62 | Nos permite obtener un nuevo `DataFrame` con únicamente las últimas `n` filas. Por ejmplo:
63 |
64 | ```python
65 | # nos da los últimos 10 elementos
66 | arbolado.tail(10)
67 | ```
68 |
69 | :warning: Tal como sucede con `head`, `tail` no ordena de ninguna forma al `DataFrame`.
70 |
71 | # Acceso por columna
72 |
73 | ## `.`
74 | ## `[]`
75 |
76 | # Recuento
77 |
78 | ## `len`
79 |
80 | La función estándar `len`, de la misma forma que nos sirve para contar los elementos de una lista o los caracteres de una palabra, nos permite contar la cantidad de filas de un DataFrame. Ejemplo:
81 |
82 | ```python
83 | >>> len(arbolado)
84 | 372699 # arbolado tiene 372699 filas
85 | ```
86 |
87 | ## `nunique`
88 |
89 | Retorna la cantidad de valores únicos, excluyendo a los valores `NaN`:
90 |
91 | ```python
92 | >>> arbolado.nombre_com.nunique()
93 | 305
94 | ```
95 |
96 | Si la columna no contiene valores nulos, es lo mismo que hacer:
97 |
98 | ```python
99 | >>> len(pd.unique(arbolado.nombre_com))
100 | 306
101 | ```
102 |
103 | # Valores únicos
104 |
105 | ## `unique`
106 |
107 | Nos permite obtener todos los valores de una columna, sin repetidos. Ejemplo:
108 |
109 | ```python
110 | # retorna los nombres comunes únicos de los árboles
111 | >>> pd.unique(arbolado.nombre_com)
112 | [
113 | 'Fresno americano', 'Ligustro', 'Arce negundo',
114 | 'Ciruelo de jardín', 'Ficus', 'Tilo', 'Acacia blanca',
115 | 'Pata de vaca (Pezuña de vaca)', 'Mandarino',
116 | 'Crespón (Àrbol de Júpiter)', 'Ceibo', 'No Determinable',
117 | 'Liquidambar', 'Jacarandá', ... etc ...
118 | ]
119 | ```
120 |
121 |
122 | # Caracterización
123 |
124 | ## `describe`
125 |
126 | Nos sirve como un atajao para para obtener varias medidas estadísticas que nos sirven para caracterizar un dataset:
127 |
128 | * Cantidad de elementos
129 | * Media
130 | * Desviación estándar
131 | * Valores máximos y mínimos
132 | * Cuantiles 25% (Q1), 50% (Q2, que se corresponde con la Mediana) y 75% (Q3)
133 |
134 | Ejemplo:
135 |
136 | ```python
137 | >>> arbolado.describe()
138 | altura_tot | diametro | inclinacio
139 | count | 372699.0 | 372699.0 | 372699.0
140 | mean | 8.473044 | 31.941234 | 3.069783
141 | std | 4.576818 | 20.207216 | 6.029910
142 | min | 0.0 | 0.0 | 0.0
143 | 25% | 5.0 | 17.0 | 0.0
144 | 50% | 8.0 | 28.0 | 0.0
145 | 75% | 11.0 | 43.0 | 5.0
146 | max | 60.0 | 426.0 | 60.0
147 | ```
148 |
149 | # Agregaciones
150 |
151 | ## `mean`
152 |
153 | Nos devuelve el valor promedio de una columna.
154 |
155 | ## `median`
156 |
157 | Nos devuelve la mediana de una columna (o lo que es lo mismo, el cuantil 50%, también llamado Q2).
158 |
159 | ## `quantile`
160 | ## `max` y `min`
161 |
162 | Nos devuelven, respectivamente el valor más grande y más pequeño de la columna. Ejemplo:
163 |
164 | ```python
165 | # altura máxima
166 | arbolado.altura_tot.max()
167 |
168 | # diametro mínimo
169 | arbolado.diametro.min
170 | ```
171 |
172 | ## `idxmax` y `idxmin`
173 |
174 | Nos permiten obtener el índice de la primera fila que hace máxima o mínima a la columna dada, respectivamente. Ejemplo:
175 |
176 | ```python
177 | # el valor del índice de la fila con el máximo diámetro
178 | arbolado.diametro.idxmax()
179 | ```
180 |
181 | Como consecuencia de su definición, si obtenemos el valor de la columna para el índice obtenido, éste va a ser el máximo:
182 |
183 | ```python
184 | # la siguiente expresión siempre va a ser verdadera:
185 | arbolado.diametro.max() == arbolado.diametro.iloc[arbolado.diametro.idxmax()]
186 | ```
187 |
188 | # Ordenamiento
189 |
190 | ## `sort_values`
191 |
192 | # Transformaciones sobre strings
193 |
194 | ## `+`, `-`
195 | ## `str`
196 |
197 | ```python
198 | # así se pueden buscar valores que contengan un string
199 | >>> arbolado[arbolado.calle.str.contains("Santa")]
200 |
201 | # ojo que si usamos filtros del estilo str con una columna numérica se va a romper
202 | # esto por ejemplo no anda:
203 | >>> arbolado_floresta[arbolado_floresta.altura_tot.str.contains('3')]
204 | AttributeError: Can only use .str accessor with string values! # ¡ups!
205 | ```
206 |
207 | ## `dt`
208 |
209 | # Creación y actualización de columnas
210 |
211 | # Filtrado
212 |
213 | ## `&`, `|` y `~`
214 |
215 | ```python
216 | # los arboles que están en flores o versalles
217 | arbolado[(arbolado.barrio == 'FLORES') | (arbolado.barrio == 'VERSALLES')]
218 |
219 | # los árboles que no están NI en constitución NI en belgrano
220 | arbolado[(arbolado.barrio != 'CONSTITUCION') & (arbolado.barrio != 'BELGRANO')]
221 | ```
222 |
223 |
224 | ## `isin`
225 |
226 | Nos permite saber si los valores de una columna están entre (son algunos de) los dados. Nos retorna una columna booleana, útil para realizar filtrados.
227 |
228 | ```python
229 | arbolado[arbolado.barrio.isin(['CONSTITUCION', 'FLORES', 'VERSALLES'])]
230 | ```
231 |
232 | ## `isna`, `notna`
233 |
234 |
235 | Nos permite saber si los valores de una columna son nulos o no nulos, respectivamente. Nos retorna una columna booleana, útil para realizar filtrados.
236 |
237 |
238 | ```python
239 | arbolado[arbolado.barrio.notna()]
240 | ```
241 |
242 |
243 | # Agrupamiento
244 |
245 | ## `groupby`
246 |
247 | ```
248 | bibliotecas.groupby("barrio").count()
249 | bibliotecas.groupby("barrio").biblioteca.count()
250 | bibliotecas.groupby(bibliotecas.barrio).biblioteca.count()
251 | bibliotecas.groupby(bibliotecas.barrio, as_index = False).biblioteca.count()
252 | bibliotecas.groupby(bibliotecas.barrio, as_index = False).biblioteca.filter()
253 | bibliotecas.groupby(bibliotecas.barrio, as_index = False).biblioteca.agg()
254 | ```
255 |
256 | ## `value_counts`
257 |
258 | ```python
259 | >>> pd.value_counts(arbolado.nombre_com)
260 | Fresno americano 141825
261 | Plátano 34786
262 | Paraíso 24558
263 | Ficus 24076
264 | Tilo 17477
265 | ...
266 | Mataojos 1
267 | Nolina 1
268 | Boj cepillo 1
269 | Taxodium 1
270 | Ciprés funerario 1
271 | ```
272 |
273 | # Combinación
274 |
275 | ## `merge`
276 | ## `concat`
277 |
278 | # Gráficos
279 |
280 | ## `plot.bar`
281 |
282 | ```
283 | cantidad_de_bibliotecas_por_barrio.plot.bar(figsize=(20, 10))
284 | ```
285 |
286 | ## `plot.pie`
287 | ## `plot.hist`
288 | ## `plot.line`
289 | ## `plot.scatter`
290 | ## `boxplot`
--------------------------------------------------------------------------------
/5_entrada_salida/1_manejo_de_archivos/Manipulación_de_archivos.md:
--------------------------------------------------------------------------------
1 | > Basado en el material de [Fundamentos de Informática](https://github.com/AJVelezRueda/Fundamentos_de_informatica)
2 |
3 | # *Lectura & Escritura de archivos con Python*
4 |
5 | # Guias de Trabajo
6 | * [1. Archivos](#1-archs)
7 | * [2. Apertura de archivos](#2-open)
8 | * [3. Cierre de archivos](#3-cierre)
9 | * [4. Rutas absolutas y relativas](#4-paths)
10 | * [5. Automatización en la construcción de rutas](#5-os)
11 | * [6. Lectura y escritura de archivos](#6-read)
12 |
13 |
14 | [1. Archivos](#1-archs)
15 |
16 |
17 | Python tiene la capacidad de acceder y realizar operaciones de lectura/escritura sobre los documentos localizados en un sistema de archivos
18 |
19 | Los sistemas _UNIX_, _MacOS_ X y _GNU/Linux_ se basan en la premisa de que todo es un archivo o un directorio, por lo que es común acceder a flujos de datos del dispositivos y/o procesos como si se accediera a un archivo binario. En los sistemas operativos de Windows sin embargo esta premisa no se cumple.
20 |
21 | Para Python existen dos tipos de archivos: de texto o binarios. Estos se manipulan de modos distintos. Los **archivos de texto** están formados por una secuencia de líneas, donde cada línea incluye una secuencia de carácteres. Típicamente, cada línea finaliza con el carácter especial de _fin de linea_ (EOL). Si bien existen distintos tipos de carácteres de fin de línea, el más común es ([\n](https://github.com/AJVelezRueda/UCEMA_Fundamentos_de_informatica/blob/master/Python_intro/Expresiones_regulares.md)).
22 |
23 | Un **archivo binario** es cualquier tipo de archivo que no es un archivo de texto. Estos solo pueden ser interpretados o leídos por aplicaciones.
24 |
25 |
26 | [2. Apertura de archivos](#2-open)
27 |
28 | Para abrir un archivo de texto, ya sea para usarlo o escribir en el, podemos usar la función nativa de Python `open()`:
29 |
30 | ```python
31 | open(path_al_archivo, modo)
32 | ```
33 |
34 | Donde:
35 |
36 | - "path_al_archivo" es un objeto de tipo str que indica la ruta en la que se encuentra el archivo.
37 |
38 | - "modo" es un objeto de tipo str que indica la forma en la que Python accederá al archivo en cuestión.
39 |
40 | Podés encontrar en la siguiente tabla algunos de los modos de lectura más frecuentes y sus difrerencias:
41 |
42 | | Modo de apertura| Significado |
43 | |------------- |---------- |
44 | | r | abre un archivo solo para lectura|
45 | | r+ | abre un archivo para lectura y escritura|
46 | | a | Abre un archivo para agregar información. Si el archivo no existe, crea un nuevo archivo para escritura|
47 | | w | Abre un archivo solo para escritura. Sobreescribe el archivo si este ya existe. Si el archivo no existe, crea un nuevo archivo para escritura|
48 |
49 |
50 | Ahora que sabemos cómo abrir un archivo, el paso siguiente es aprender a cerrarlos.
51 |
52 | [3. Cierre de archivos](#3-cierre)
53 |
54 | ⚠️ ¡Es muy importante cerrar los archivos una vez abiertos! ¡Como los signos de admiración!
55 |
56 | 🤔 Pero ¿Por qué? Bueno, aquí van algunas razones:
57 |
58 |
59 | - Dejar los archivos abiertos, pone al archivo/script en manos de los recolectores de basura. Aunque el archivo en teoría se cerrará automáticamente, puede que no sea posible.
60 |
61 | - De no cerrar los archivos, se puede ralentizar la máquina. Con demasiadas cosas abiertas, pse utiliza más RAM, lo que afectará el rendimiento de la máquina y del programa que estemos creando.
62 |
63 | - Muchos cambios en los archivos en Python no entran en vigencia hasta que se cierra el archivo, por lo que si su secuencia de comandos edita, deja abierto y lee un archivo, no se verán las ediciones.
64 |
65 |
66 | Ahora bien, ¿cómo podemos entonces cerrar un archivo luego de abrirlo? Existe un método `close()`:
67 |
68 | ```python
69 | archivo = open(path_al_archivo, modo)
70 | archivo.close()
71 | ```
72 |
73 | Sin embargo, existe otra forma de apertura de archivos que nos ahorra este paso y siempre nos asegura el cierre de adecuado:
74 |
75 |
76 | ```python
77 | with open(path_al_archivo, modo) as miarch:
78 | #Aquí van las líneas de procesamiento del archivo
79 | ```
80 | Este modo de apertura nos asegura el cierre del archivo al salir del bloque `with`, aún cuando aparezcan errores. Es por eso que esta es la forma más recomendada para la apertura de archivos.
81 |
82 |
83 | * [4. Rutas absolutas y relativas](#4-paths)
84 |
85 | En todos los sistemas operativos modernos la estructura de archivos es jerárquica y depende de los directorios. Semejante a una estructua arbórea en la que existe un nodo (un directorio o carpeta), que contiene los restantes directorios o archivos.
86 |
87 | El path o ruta a un archivo, será entonces, el recorrido de directorios o carpetas que debemos recorrer para llegar a nuestro archivo. Esta se escribe separando los nombres de los respectivos directorios separados por `/`. Esto es lo que se conoce como la ruta absoluta al archivo:
88 |
89 |
90 | ```bash
91 | windows "C:\home\Facultad\Fundamentos\Manipulación_de_archivos.md"
92 | Linux "/home/Facultad/Fundamentos/Manipulación_de_archivos.md"
93 | ```
94 |
95 | Las rutas tambien pueden ser escritas de un modo más compacto o acortado. Se suelen escribir de forma relativa a un determinado directorio. Veamos un ejemplo:
96 |
97 |
98 | ```
99 | /
100 | └── home/ ← carpeta de referencia
101 | │
102 | ├── Facultad/ ← Directorio de trabajo
103 | | └── Estadística
104 | │ └── Fundamentos/
105 | │ └── Manipulación_de_archivos.md
106 | └── Fotos ← Otro directorio
107 | ```
108 |
109 | Imaginemos que esta es la estructura de archivos de nuestra computadora, donde existen dos carpetas en el home: _Fotos_ y _Facultad_. Dentro de la carpeta _Facultad_, podemos encontrar a su vez dos directorios: _Fundamentos_ y _Estadistica_. Nuestra carpeta de trabajo actual es la de Fundamentos, donde tenemos nuestro archivo de interes _Manipulación_de_archivos.md_.
110 |
111 | Desde el directorio _Facultad_ podemos escribir la ruta relativa a nuestro archivo del siguiente modo:
112 |
113 | ```bash
114 | ./Fundamentos/Manipulación_de_archivos.md
115 | /Fundamentos/Manipulación_de_archivos.md
116 | ```
117 | Ahora si quisieramos acceder a las _Fotos_, podemos hacer:
118 |
119 | ```bash
120 | cd ../Fotos
121 | ```
122 |
123 | Como seguramente pudiste deducir un punto (.) se utiliza para referenciar al "directorio actual" y los dos puntos seguidos (..) se utilizan para subir en la jerarquía
124 |
125 |
126 | >
127 | > 🧗♀️ Desafío I: Creá un archivo de prueba (`bio.txt`) en la carpeta destinada a los prácticos de la materia.
128 | >
129 |
130 | [5. Automatización en la construcción de rutas](#5-os)
131 |
132 | Cada programa que se ejecuta en su computadora tiene un directorio de trabajo actual, o `cwd`. Se asume que cualquier nombre de archivo o ruta que no comience con la carpeta raíz se encuentra en el directorio de trabajo actual. Se puede obtener el directorio de trabajo actual como un _string_, utilizando la biblioteca _**`os`**_.
133 |
134 |
135 | Esta biblioteca del sistema operativo de Python proporciona funciones para interactuar con el sistema operativo. Esta incluye métodos que como ```os.getcwd()``` o ```os.chdir()```, que nos permitirá conocer el directorio de trabajo o cambiar de directorio de forma automática:
136 | ```Python
137 | >>> import os
138 | >>> os.getcwd()
139 | '/home/Ana'
140 | >>> os.chdir('/home/Ana/Documents')
141 | >>> os.getcwd()
142 | '/home/Ana/Documents'
143 | ```
144 | Aquí, el directorio de trabajo actual es '/home/Ana' y cambiamos al directorio '/home/Ana/Documents'. Python mostrará un [error](https://github.com/AJVelezRueda/UCEMA_Fundamentos_de_informatica/blob/master/Python_intro/Manejo_excepciones.md) si intenta cambiar a un directorio que no existe.
145 |
146 | >
147 | > 🧗♀️ Desafío II: Investigá sobre los métodos ```os.mkdir()``` y ```os.listdir()```
148 | >
149 |
150 |
151 | [6. Lectura y escritura de archivos](#6-read)
152 |
153 | Ya a esta altura se estarán preguntando qué tipo de procesamientos o manipulaciones podemos hacer de un archivo. Bueno, la respuesta más obvia dado el título de esta sección es "leerlos y escribir" ¡Si, exactamente eso es lo que aprenderemos ahora mismo!
154 |
155 | La escritura de los archivos en Python se hace de forma sencilla con el método `write()`, que toma como parámetro un string con el contenido que desamos almacenar en el archivo:
156 |
157 |
158 | ```python
159 | with open(path_al_archivo, modo) as miarch:
160 | miarch.write("Este es el contenido del archivo")
161 | ```
162 | >
163 | > 🧗♀️ Desafío III: Abrí el archivo `bio.txt` y escribí una mini biografía de presentación.
164 | > Para pensar 🤔:¿Cómo darías formato al texto que estas creando?
165 | >
166 |
167 | Hemos visto que resulta relativamente sencillo escribir archivos con Python, sin embargo, la lectura de los archivos puede realizarse de múltiples formas. Como vimos anteriormente, los *archivos de texto* están formados por una secuencia de lineas, sepradas por un caracter especial de fin de línea. Por ello resulta lógico pensar que existan más de una manera de leer un archivo:
168 |
169 | - Línea a linea
170 | - Archivo completo
171 |
172 | Para que quede más claro veamos un ejemplo concreto. Ejucutá las siguientes líneas (recordá adaptar el path a tu archivo de prueba).
173 |
174 | ```python
175 | bio = open("bio.txt", "r")
176 | bio.read()
177 | ```
178 |
179 | Ahora probemos las siguientes líneas de código:
180 |
181 | ```python
182 | bio = open("bio.txt", "r")
183 | bio.readlines()
184 | ```
185 |
186 | >
187 | > Para pensar 🤔:¿Qué diferencias notás? ¿Para qué sirve cada método? ¿Que tipo de dato obtenemos en cada caso? Usá la función type() para explorarlo:
188 | >
189 |
190 |
191 | Ayuda
192 | type(bio.readlines())
193 |
194 |
195 |
196 | En resumen, podemos utilizar los siguientes modos de lectura de archivos:
197 |
198 | * `.read()` Lee del archivo según el número de bytes de tamaño. Si no se pasa ningún, entonces lee todo el archivo.
199 |
200 | * `.readline()` Lee como máximo el número de caracteres de tamaño de la línea. Esto continúa hasta el final de la línea y luego regresa.
201 |
202 | * `.readlines()` Esto lee las líneas restantes del objeto de archivo y las devuelve como una lista.
203 |
204 | >
205 | > 🧗♀️Desafio IV: Descargá el archivo [`manipulacion_archivos.txt`](https://github.com/AJVelezRueda/UCEMA_Fundamentos_de_informatica/blob/master/Python_intro/manipulacion_archivos.txt) y creá un programa que lea su contenido y lo imprima en pantalla el resultado de la búsqueda de la expresión `-(.*)-`
206 | >
207 | >Para pensar 🤔: ¿Qué significa dicha expresión regular? Imprimí todo el contenido del archivo y descubrí qué hace este personaje incógnito
208 | >
209 | >Para pensar 🤔: ¿Creés que habrá una forma más práctica de leer archivos estructurados o tabulados?
210 | >
211 | > ¡Te dejo con la intriga hasta nuestro capítulo de análisis de datos! Por ahora, hasta aquí llegamos 👋
212 | >
213 |
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/07_práctica_alternativa_condicional/README.md:
--------------------------------------------------------------------------------
1 | > Basado en http://github.com/mumukiproject/mumuki-guia-funcional-practica-valores-y-funciones-pdep-utn
2 | > y en https://github.com/MumukiProject/mumuki-guia-javascript-ejercitacion-condicionales
3 |
4 |
5 | ## 1. `inversa`
6 |
7 | Definí una función `inversa`, que al aplicarla con un número cualquiera me devuelve el resultado de dividir a 1 por ese número.
8 |
9 | ```python
10 | >> inversa(4)
11 | 0.25
12 |
13 | >> inversa(0.5)
14 | 2.0
15 | ```
16 |
17 | ⚠️ ¡Cuidado! Recordá que no se puede dividir por 0, así que la inversa de 0 no se puede calcular. En este ejercicio vamos a tomar la decisión (no muy correcta matemáticamente 😛) de devolver `0` en ese caso.
18 |
19 | ```python
20 | >> inversa(0)
21 | 0
22 | ```
23 |
24 | ## 2. `par_o_impar(numero)`
25 |
26 | Crear una función `par_o_impar` que acepte como argumento un `numero` y devuelva el string `par` si el `numero` es par, o el string `impar` si el `numero` es impar
27 |
28 | ```python
29 | par_o_impar(3) # 'impar'
30 | par_o_impar(10) # 'par'
31 | ```
32 |
33 | ## 3. `positivo_o_negativo(numero)`
34 |
35 | Crear una función `positivo_o_negativo` que acepte como argumento un `numero` y devuelva el string `positivo` si el `numero` es positivo, o el string `negativo` si el `numero` es negativo
36 |
37 | ```python
38 | positivo_o_negativo(3) # 'positivo'
39 | positivo_o_negativo(-5) # 'negativo'
40 | ```
41 |
42 | > 🤔 Para pensar: ¿ves algo parecido entre esta función y la anterior? ¿Se te ocurre alguna forma de extraer a una nueva función las partes comunes de `positivo_o_negativo` y `par_o_impar` y luego modificarlas para no repetir código? ¡Intentalo!
43 |
44 | ## 4. `avanzar_semaforo(color_actual)`
45 |
46 | Crear una función `avanzar_semaforo` que acepte como argumento un string `color_actual` y devuelva un string con el
47 | siguiente color del semáforo, siguiendo el orden: verde -> amarillo -> rojo -> verde
48 |
49 | ```python
50 | avanzar_semaforo('verde') # 'amarillo'
51 | avanzar_semaforo('amarillo') # 'rojo'
52 | avanzar_semaforo('rojo') # 'verde'
53 | ```
54 |
55 | ## 5. `obtener_dias_mes(mes)`
56 |
57 | Crear una función `obtener_dias_mes` que tome como argumento un string `mes` y devuelva un número dependiendo de la
58 | cantidad de días que tenga ese mes
59 |
60 | ```python
61 | obtener_dias_mes("diciembre") # 31
62 | obtener_dias_mes("febrero") # 29
63 | ```
64 |
65 | ## 6. `obtener_generacion(anio_nacimiento)`
66 |
67 | Crear una función `obtener_generacion` que tome como argumento un número `anio_nacimiento` y devuelva un string con la generación a la que pertenece, siguientdo estas reglas:
68 |
69 | | Generación | Años de nacimiento |
70 | | --- | --- |
71 | | Baby boomer | 1949-1968 |
72 | | Generación X | 1969-1980 |
73 | | Millennials | 1981-1993 |
74 | | Generación Z | 1994-2010 |
75 |
76 | ## 7. `obtener_sensacion(temperatura)`
77 |
78 | Crear una función `obtener_sensacion` que tome como argumento un número `temperatura` y devuelva un string dependiendo de la `temperatura`,
79 | con las siguientes reglas:
80 |
81 | | Temperatura | Mensaje |
82 | | --- | --- |
83 | | Menor a 0° | ¡Está helando!
84 | | Mayor o igual a 0° y menor a 15° | ¡Hace frío!
85 | | Mayor o igual a 15° y menor a 25° | Está lindo
86 | | Mayor o igual a entre 25° y menor a 30° | Hace calor
87 | | Mayor o igual de 30° | ¡Hace mucho calor!
88 |
89 | ```python
90 | obtener_sensacion(33) # "¡Hace mucho calor!"
91 | ```
92 |
93 | ## 8. `obtener_nota(puntaje)`
94 |
95 | Crear una función `obtener_nota` que tome como argumento un número `puntaje` y devuelva un string dependiendo del `puntaje`
96 | redondeado, con las siguientes reglas:
97 |
98 | | Puntaje | Nota |
99 | | --- | --- |
100 | | Menor a 6 | Desaprobado
101 | | Mayor o igual a 6 y menor a 7 | Regular
102 | | Mayor o igual a 7 y menor a 8 | Bueno
103 | | Mayor o igual a entre 8 y menor a 10 | Muy bueno
104 | | 10 | Excelente
105 | | Menor a 0 o mayor a 10 | Puntaje inválido
106 |
107 | ```python
108 | obtener_nota(7) # "Bueno"
109 | obtener_nota(9.6) # "Excelente"
110 | obtener_nota(12) # "Puntaje inválido"
111 | ```
112 |
113 | ## 9. `jugar_piedra_papel_tijera(a, b)`
114 |
115 | Crear una función `jugar_piedra_papel_tijera` que tome como argumentos dos strings que representen una jugada (`piedra`, `papel`, `tijera`) y
116 | dependiendo el devuelva un string con un mensaje avisando qué jugada ganó (o si hubo empate)
117 |
118 | ```python
119 | jugar_piedra_papel_tijera('tijera', 'piedra') # ¡Ganó piedra!
120 | jugar_piedra_papel_tijera('piedra', 'tijera') # ¡Ganó piedra!
121 | jugar_piedra_papel_tijera('papel', 'piedra') # ¡Ganó papel!
122 | jugar_piedra_papel_tijera('piedra', 'papel') # ¡Ganó papel!
123 | jugar_piedra_papel_tijera('papel', 'tijera') # ¡Ganó tijera!
124 | jugar_piedra_papel_tijera('tijera', 'papel') # ¡Ganó tijera!
125 | jugar_piedra_papel_tijera('piedra', 'piedra') # ¡Empate!
126 | jugar_piedra_papel_tijera('papel', 'papel') # ¡Empate!
127 | jugar_piedra_papel_tijera('tijera', 'tijera') # ¡Empate!
128 | ```
129 |
130 |
131 | ## 10. Celsius a Farenheit
132 |
133 | La temperatura se mide en _grados_, pero en algunos países se usan grados _"diferentes"_.
134 |
135 | En Argentina, Uruguay y Chile se usan _grados Celsius_ (abreviado _°C_), pero en en Estados Unidos se utilizan _grados Farenheit_ (_°F_). Por ejemplo 10°C son 50°F.
136 |
137 | ¿Y cómo hacemos para convertir grados Celsius en grados Farenheit? ¡Usando la siguiente fórmula!
138 |
139 | ```
140 | GradosFarenheit = GradosCelsius × 1.8 + 32
141 | ```
142 |
143 | > Sabiendo ésto, definí la función `celsius_a_farenheit` que, a partir de una cantidad de grados en escala Celsius, devuelve el equivalente en escala Fahrenheit.
144 | >
145 | > ```python
146 | > >> celsius_a_farenheit(10)
147 | > 50 # porque 10°C son 50°F
148 | > >> celsius_a_farenheit(0)
149 | > 32 # porque 0°C son 32°F
150 | > ```
151 |
152 | ## 11. Farenheit a Celsius
153 |
154 | Ahora hagamos el proceso inverso: aprendamos a convertir una temperatura en grados Farenheit a grados Celsius. La fórmula es la siguiente:
155 |
156 | ```
157 | GradosCelsius = (GradosFarenheit - 32) / 1.8
158 | ```
159 |
160 | > Definí la función `farenheit_a_celsius` que, a partir de una cantidad de grados en escala Fahrenheit, devuelve el equivalente en escala Celsius.
161 | >
162 | > ```python
163 | > >> farenheit_a_celsius(32)
164 | > 0 #- porque 32°F son 0°C
165 | > >> farenheit_a_celsius(50)
166 | > 10 #- porque 50°F son 10°C
167 | > ```
168 |
169 |
170 | ## 12. Hace frío internacional
171 |
172 | ¡Se vino el frío! ❄️ Y necesitamos programar las siguientes funciones:
173 |
174 | * `hace_frio_celsius` que nos diga si hace menos de 8 grados Celsius
175 | * `hace_frio_farenheit` que también nos diga si hace frío, pero que tome una temperatura expresada en grados Farenheit.
176 |
177 | Ejemplo:
178 |
179 | ```python
180 | >> hace_frio_celsius(10)
181 | False # porque son más de 8°C
182 | >> hace_frio_celsius(0)
183 | True # porque son menos de 8°C
184 | >> hace_frio_farenheit(50) # recordá que 50°F son 10°C
185 | False
186 | >> hace_frio_farenheit(32) # recordá que 32°F son 0°C
187 | True
188 | ```
189 |
190 | > Definí las funciones `hace_frio_celsius` y `hace_frio_farenheit`, que nos digan si una temperatura (en Celsius y Farenheit, respectivamente) es fría.
191 | >
192 | > Como desafío adicional, definí `hace_frio_farenheit` reutilizando `hace_frio_celsius` y las funciones anteriores que necesites
193 |
194 | ## 13. Dispersión
195 |
196 | Trabajamos con tres enteros que representan el nivel de un río en tres días consecutivos 📆. Por ejemplo: medimos los días 1, 2 y 3, y las mediciones son: 22 cm, 283 cm, y 294 cm.
197 |
198 | Usando estas mediciones nos gustaría saber tres cosas:
199 |
200 | * `maximo_entre_tres`: toma tres mediciones y nos da la más alta;
201 | * `minimo_entre_tres`: toma tres mediciones y nos da la mas baja;
202 | * `dispersion`: toma los tres mediciones y devuelve la diferencia entre la más alta y la más baja. Ejemplo:
203 |
204 | ```python
205 | >> maximo_entre_tres(22, 283, 294)
206 | 294
207 | >> minimo_entre_tres(22, 283, 294)
208 | 22
209 | >> dispersion(22, 283, 294)
210 | 272 # Porque 294 menos 22 es 272.
211 | ```
212 |
213 | > ¡Desarrollá estas tres funciones! Y no repitas código: reutilizá `maximo_entre_tres` y `minimo_entre_tres` en la definición de `dispersion` 🕶️
214 |
215 |
216 | ## 14. Pasan los días
217 |
218 | Siguiendo con el problema anterior, ahora que contamos con la función `dispersion`, necesitamos definir las siguientes funciones, que reciben los valores de los tres días, y nos responden si son días parejos, locos o normales:
219 |
220 | * `dias_parejos`: son días parejos si la dispersión es chica (menos de 30 cm)
221 | * `dias_locos`: son días locos si la dispersión es grande (más de un metro)
222 | * `dias_normales`, son días normales si no son ni parejos ni locos.
223 |
224 | Ejemplo:
225 |
226 | ```python
227 | >> dias_parejos(110, 98, 100)
228 | True
229 | ```
230 |
231 | ```python
232 | >> dias_normales(1, 200, 500)
233 | False
234 | ```
235 |
236 | > Definí `dias_normales`, `dias_parejos` y `dias_locos`. Asumí que `dispersion` ya está definida.
237 |
238 |
239 | ## 15. Pinos
240 |
241 | En una plantación de pinos, de cada árbol se conoce la altura expresada en **metros**. El peso de un pino se puede calcular a partir de la altura así:
242 |
243 | * 3 kg por cada centímetro hasta 3 metros,
244 | * 2 kg por cada centímetro arriba de los 3 metros.
245 |
246 |
247 | 
248 |
249 |
250 | Por ejemplo:
251 |
252 | * 2 metros pesan 600 kg, porque 200 * 3 = 600
253 | * 5 metros pesan 1300 kg, porque los primeros 3 metros pesan 900 kg y los siguientes 2 pesan los 400 restantes.
254 |
255 | 
256 |
257 |
258 |
259 | Los pinos se usan para llevarlos a una fábrica de muebles, a la que le sirven árboles de entre 400 y 1000 kilos, un pino fuera de este rango no le sirve a la fábrica.
260 |
261 | > * Definí la función `peso_pino`, recibe la altura de un pino en metros y devuelve su peso.
262 | > * Definí la función `es_peso_util`, recibe un peso en kg y responde si un pino de ese peso le sirve a la fábrica
263 | > * Definí la función `sirve_pino`, recibe la altura de un pino y responde si un pino de ese peso le sirve a la fábrica.
264 | >
265 |
266 | ## 16. ¡Puntos para setenta!
267 |
268 |
269 | En el conocido juego de [la escoba de 15](https://es.wikipedia.org/wiki/Escoba_del_15) tenemos que una forma de ganar puntos es mediante el criterio de "setenta" en el cual una carta tiene un valor especifico dependiendo su número:
270 |
271 | * El as (1) tienen 5.5 puntos
272 | * Las figuras (10, 11, 12) tienen 0.5 puntos
273 | * Todas las demás cartas tienen tantos puntos como su número.
274 |
275 | Escribí y explicitá el tipo de `punto_para_setenta` la cual recibe el número de la carta y debe devolver la cantidad de puntos según el criterio anterior.
276 |
277 | Asumí que el parametro que recibe la función ya es un número de una carta válida.
278 |
--------------------------------------------------------------------------------
/9_testing/1_introduccion/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Introducción al _testing_
3 |
4 | ¡Hola! Hasta ahora venimos programando sin parar, y en más de una ocasión nuestro código no ha funcionado como lo deseábamos. Quizás teníamos mal escrito el nombre de una función o una variable y se producía un `NameError`. O quizás, cometíamos un error de sintaxis y nos topábamos con un `SyntaxError`. O peor aún, teníamos un _bug_ (🐛 bicho, en inglés): nuestro código no lanzaba ningún error, pero sin embargo no hacía lo que debía: una cuenta era incorrecta, faltaba o sobraba un elemento en una lista, y así.
5 |
6 | Es más: posiblemente _rara vez_ nuestros programas hayan hecho lo que deben en el primer intento y hayamos tenido que escribirlo y reescribirlo varias veces hasta lograr el resultado deseado 🔁. En definitiva, _errar es humano_ y debemos preparanos para ello. ¡Por eso mismo siempre debemos probar nuestro código! Por ejemplo, si dentro de un archivo `descuento.py` tenemos una función como la siguiente...
7 |
8 | ```python
9 | def aplicar_descuento_2x1(cantidad, precio_base):
10 | """
11 | Aplica el descuento 2 X 1 a un precio: los pares de productos los cobra a la mitad de precio
12 | """
13 | if cantidad % 2 == 0:
14 | return precio_base * cantidad / 2
15 | else:
16 | return precio_base * (cantidad - 1) / 2
17 | ```
18 |
19 | ...¿cómo la probaríamos? La forma más sencilla es cargarla en nuestro intérprete con `python -i descuento.py` y luego _someterla_ a diferentes pruebas:
20 |
21 | * Si la cantidad es cero, el precio debería ser cero, sin importar la cantidad
22 | * Si la cantidad es 2 y el precio base es $100, el precio final debería ser $100
23 | * Si la cantidad es 4 y el precio base es $150, el precio final debería ser $300
24 | * Si la cantidad es 1 y el precio base es $200, el precio final debería ser $200 (no hubo descuento)
25 | * etc...
26 |
27 | Esto lo podríamos _traducir_ así:
28 |
29 |
30 | ```python
31 | >>> aplicar_descuento_2x1(0, 450)
32 | 0.0
33 | >>> aplicar_descuento_2x1(2, 100)
34 | 100
35 | >>> aplicar_descuento_2x1(4, 150)
36 | 300
37 | >>> aplicar_descuento_2x1(1, 200)
38 | 0.0 # ¡ups!
39 | ```
40 |
41 | En otras palabras, algunas pruebas arrojaron resultados correctos y otras, no:
42 |
43 | * ✔️ Si la cantidad es cero, el precio debería ser cero, sin importar la cantidad
44 | * ✔️ Si la cantidad es 2 y el precio base es $100, el precio final debería ser $100
45 | * ✔️ Si la cantidad es 4 y el precio base es $150, el precio final debería ser $300
46 | * ❌ Si la cantidad es 1 y el precio base es $200, el precio final debería ser $200 (no hubo descuento)
47 |
48 | ¿Qué hacemos ahora que encontramos un _bug_? Deberíamos volver a nuestro código, revisarlo y descubrir el error. En nuestro caso, era la falta de un `+ 1`:
49 |
50 | ```python
51 | def aplicar_descuento_2x1(cantidad, precio_base):
52 | """
53 | Aplica el descuento 2 X 1 a un precio: los pares de productos los cobra a la mitad de precio
54 | """
55 | if cantidad % 2 == 0:
56 | return precio_base * cantidad / 2
57 | else:
58 | return precio_base * (1 + (cantidad - 1) / 2)
59 | ```
60 |
61 | ¿Pero qué nos garantiza que esta vez no hayamos cometido ningún error? ¡Absolutamente nada! Es decir, deberíamos volver a probar el caso que falló anteriormente, para asegurarnos de que hayamos corregido el problema:
62 |
63 | ```python
64 | >>> aplicar_descuento_2x1(1, 200)
65 | 200.0 # ¡bien! ¡pasamos la prueba!
66 | ```
67 |
68 | ¡Pero eso no es suficiente! Perfectamente podríamos haber _roto_ accidentalmente los casos que antes sí funcionaban (lo que se conoce como errores de _regresión_):
69 |
70 | ```python
71 | >>> aplicar_descuento_2x1(0, 450)
72 | 0.0
73 | >>> aplicar_descuento_2x1(2, 100)
74 | 100
75 | >>> aplicar_descuento_2x1(4, 150)
76 | 300
77 | ```
78 |
79 | Recién ahora podemos decir que hemos probado todo con resultados satisfactorios 😫:
80 |
81 | * ✔️ Si la cantidad es cero, el precio debería ser cero, sin importar la cantidad
82 | * ✔️ Si la cantidad es 2 y el precio base es $100, el precio final debería ser $100
83 | * ✔️ Si la cantidad es 4 y el precio base es $150, el precio final debería ser $300
84 | * ✔️ Si la cantidad es 1 y el precio base es $200, el precio final debería ser $200 (no hubo descuento)
85 |
86 | Como vemos, todo esto es tedioso y propenso a error. ¿Y qué hacemos cuando algo es así? ¡Programamos!
87 |
88 | ## Pruebas unitarias automatizadas
89 |
90 | Justamente porque probar es necesario, pero al mismo tiempo hacerlo correctamente y luego de cada cambio es muy molesto y aburrido, es que existen las pruebas unitarias automatizadas: se trata de _programar_ nuestras pruebas, usando una herramienta especializada.
91 |
92 | En Python usaremos [`pytest`](https://docs.pytest.org/en/7.1.x/), la cual podemos instalar de la siguiente forma:
93 |
94 | ```bash
95 | $ pip install pytest
96 | ```
97 |
98 | Luego de ésto, podremos escribir, en el mismo directorio que nuestro archivo principal, otro llamado `test_descuento.py`, en que el que escribiremos nuestras _pruebas unitarias automatizadas_, es decir:
99 |
100 | * **pruebas** sobre nuestro programa;
101 | * que evalúan detalladamente pequeñas cada una de sus **unidades** (partes), como por ejemplo cada función o cada procedimiento;
102 | * y que se pueden ejecutar de forma **automática**, tantas veces como se desee.
103 |
104 | Para ello primero debemos _importar_ nuestro código, es decir, leerlo de su archivo y traerlo a aquel que contiene las pruebas...
105 |
106 | ```python
107 | from descuento import *
108 | ```
109 |
110 | ... y luego, escribiremos cada una de nuestras pruebas unitarias automatizadas, o pruebas unitarias, o simplemente, _tests_:
111 |
112 | ```python
113 | def test_el_precio_es_cero_cuando_cuando_cantidad_es_cero():
114 | assert aplicar_descuento_2x1(0, 450) == 0
115 | ```
116 |
117 | Como vemos, un test consiste simplemente en un procedimiento, con ciertas características:
118 |
119 | 1. como todo procedimiento, no debe devolver nada;
120 | 2. su nombre debe empezar con `test`;
121 | 3. debe probar un escenario de nuestro interés, y realizar validaciones con el comando `assert`;
122 | 4. y debe tener un nombre que exprese qué es lo que se está probando.
123 |
124 | En nuestro ejemplo:
125 |
126 | 1. el test anterior no devuelve nada;
127 | 2. su nombre empieza con `test`;
128 | 3. existe para probar el primer escenario que discutimos previamente: _si la cantidad es cero, el precio debería ser cero_.
129 | 4. y finalmente verifica mediante el comando `assert` que efectivamente la función retorne el valor esperado (`0`): si la condición booleana es falsa, el test fallará, pero si es verdadera, el test será exitoso.
130 |
131 | ¿Y cómo hacemos para ejecutar nuestras pruebas? ¡Usando el comando `pytest`!
132 |
133 | ```bash
134 | $ pytest
135 | ============================= test session starts =============================
136 | platform linux -- Python 3.8.10, pytest-7.1.3, pluggy-1.0.0
137 | collected 1 item
138 |
139 | test_descuento.py . [100%]
140 |
141 | ============================== 1 passed in 0.01s ==============================
142 | ```
143 |
144 | ¡Está vivo! Esto nos indica que el test se ejecutó correctamente 🎊.
145 |
146 | Si ahora quisieramos escribir otro test más, para la segunda situación (_Si la cantidad es 2 y el precio base es $100, el precio final debería ser $100_), deberíamos escribirlo así:
147 |
148 | ```python
149 | def test_el_precio_es_100_cuando_cantidad_es_2_y_precio_es_100():
150 | assert aplicar_descuento_2x1(2, 100) == 100
151 | ```
152 |
153 | Y nuevamente podemos ejecutar todos nuestros tests:
154 |
155 | ```bash
156 | $ pytest
157 | ============================= test session starts =============================
158 | platform linux -- Python 3.8.10, pytest-7.1.3, pluggy-1.0.0
159 | collected 2 items
160 |
161 | test_descuento.py .. [100%]
162 |
163 | ============================== 2 passed in 0.01s ==============================
164 | ```
165 |
166 | ¿Y que pasaría si probáramos el caso _Si la cantidad es 1 y el precio base es $200, el precio final debería ser $200_...
167 |
168 | ```python
169 | def test_el_precio_es_200_cuando_la_cantidad_es_1_y_el_precio_base_es_200():
170 | assert aplicar_descuento_2x1(1, 200) == 200
171 | ```
172 |
173 | ... y lo probáramos con la primera versión de nuestro código?
174 |
175 |
176 | ```
177 | $ pytest
178 | ============================= test session starts =============================
179 | platform linux -- Python 3.8.10, pytest-7.1.3, pluggy-1.0.0
180 | collected 3 items
181 |
182 | test_descuento.py ..F [100%]
183 |
184 | ================================== FAILURES ===================================
185 | ____ test_el_precio_es_200_cuando_la_cantidad_es_1_y_el_precio_base_es_200 ____
186 |
187 | def test_el_precio_es_200_cuando_la_cantidad_es_1_y_el_precio_base_es_200():
188 | > assert aplicar_descuento_2x1(1, 200) == 200
189 | E assert 0.0 == 200
190 | E + where 0.0 = aplicar_descuento_2x1(1, 200)
191 |
192 | test_descuento.py:10: AssertionError
193 | =========================== short test summary info ===========================
194 | FAILED test_descuento.py::test_el_precio_es_200_cuando_la_cantidad_es_1_y_el_precio_base_es_200
195 | ========================= 1 failed, 2 passed in 0.06s =========================
196 | ```
197 |
198 | 💣 ¡El test estalla! Pero lo bueno es que nos lo indica con precisión: nos dice cuántos tests fallaron y cuántos no, qué errores hubo y dónde. Con todo esto ahora corregir nuestro código (recordar que faltaba el `+ 1`) y volver a correr el test es sencillo:
199 |
200 | ```
201 | $ pytest
202 | ============================ test session starts =============================
203 | platform linux -- Python 3.8.10, pytest-7.1.3, pluggy-1.0.0
204 | collected 3 items
205 |
206 | test_descuento.py ... [100%]
207 |
208 | ============================= 3 passed in 0.01s ==============================
209 | ```
210 |
211 | ## Conclusiones
212 |
213 | A modo de cierre, podemos sacar algunas conclusiones:
214 |
215 | 1. 🐞 Cometer errores es inevitable; más que tratar a toda costa que nuestro código sea "perfecto", preferiremos que nuestro código esté probado.
216 | 2. ↩️ Cuando hacemos un cambio al código, aún cuando sea para realizar una corrección, es necesario volver a probarlo todo nuevamente para evitar errores de regresión.
217 | 3. 🧑🔬 Las pruebas manuales son muy importantes, pero son tediosas de hacer una y otra vez.
218 | 4. 🤖 Las pruebas unitarias automatizadas requieren poner un poco más de esfuerzo, pero podemos reutilizarlas y ejecutarlas indefinidamente.
219 | 5. 🔨 `pytest` es una herramienta que permite escribir pruebas unitarias automatizadas en Python.
220 | 6. 🧪 Los tests en `pytest` se escriben como procedimientos cuyo nombre empieza con `test` y tienen un `assert`.
221 |
--------------------------------------------------------------------------------
/2_introducción_al_análisis_de_datos/1_tablas/README.md:
--------------------------------------------------------------------------------
1 | # Introducción a pandas
2 |
3 | _¡Qué queremos? ¡Trabajar con datos! ¿Y dónde los vamos a buscar? Eh... en un... ¿conjunto de datos?🤔_
4 |
5 | Cuando queramos analizar datos, con mucha frecuencia los encontraremos en archivos llamados (no muy originalmente 😛) _lotes de datos_ (_datasets_, en inglés). ¿Pero cómo se ven estos lotes? ¿Dónde los podemos encontrar publicados? ¿Cómo los podemos procesar?
6 |
7 | ¡Muchas preguntas! En esta lección responderemos alguans de ellas. Y en el camino, conoceremos a `pandas` 🐼, una biblioteca que poco tiene con los simpáticos osos oriundos de las regiones montañosas de China 🇨🇳, pero que nos ayudará enormemente a trabajar con estos lotes de datos.
8 |
9 | ¡Empecemos!
10 |
11 | ## 1. Abierto al público
12 |
13 | Primero lo primero: entendamos dónde encontrar nuestros tan deseados lotes de datos. O mejor dicho, entendamos que _no siempre_ podremos encontrarlos 😓. Esto se debe a las organizaciones que los recopilan (estados, empresas, ONGs, etc) a veces los _publican_ y otras veces, no.
14 |
15 | Por eso es que tenemos dos grandes familias de lotes de datos:
16 |
17 | * 🔓 Los datos abiertos: pueden ser consultados por cualquier persona y usados para casi cualquier fin.
18 | * 🔒 Los datos cerrados: sólo los pueden consultar personas que pertenezcan a las organizaciones que los producen, o personas externas pero con fuertes limitaciones en su acceso y uso (por ejemplo, a través de un pago)
19 |
20 | > 🏅 Desafío: las siguientes son páginas de estados que ofrecen datos abiertos. Exploralas e identificá qué tipo de información publican y **en qué formatos**.
21 | >
22 | > * 🇦🇷 [www.datos.gob.ar](https://www.datos.gob.ar/)
23 | > * 🇺🇾 [www.gub.uy/datos-abiertos](https://www.gub.uy/datos-abiertos)
24 | > * 🇲🇽 [datos.gob.mx](https://datos.gob.mx/)
25 | > * 🇨🇱 [datos.gob.cl](https://datos.gob.cl/)
26 |
27 | ## 2. Manteniendo las formas
28 |
29 | Si exploraste las páginas anteriores, quizás hayas notado que casi todos los lotes de datos están publicados en forma de tablas. Y dentro de tooodos los formatos que existen, uno de los más frecuentes (y fáciles de usuar) se llama CSV.
30 |
31 | Momento, ¿cesequé? ¡Ce-ese-vé! Se trata de un acrónimo en inglés de _Comma Separated Value_, es decir, un archivo de texto con valores separados por comas. Por ejemplo así se ve un archivo `datos_ejemplo.csv`:
32 |
33 | ```csv
34 | Feli,Perez,24
35 | Dani,Lopez,32
36 | Juani,Vazquez,19
37 | ```
38 |
39 | > Para pensar 🤔: ¿qué está intentando representar este CSV de ejemplo? ¿Qué información contiene cada renglón?
40 |
41 | ## 3. Sin título
42 |
43 | El archivo CSV que vimos antes contenía información de personas, o al menos eso parece. Quizás se traten de sus nombres, apellidos y sus edades, o quizás sean estudiantes de una carrera y nos esté informando la cantidad de materias aprobadas.
44 |
45 | Para evitar este tipo de ambigüedad, normalmente los archivos CSV contendrán en su primer renglón los títulos de cada columna. Por ejemplo así podría verse ahora nuestro `datos_ejemplo.csv`:
46 |
47 | ```csv
48 | nombre,apellido,edad
49 | Feli,Perez,24
50 | Dani,Lopez,32
51 | Juani,Vazquez,19
52 | ```
53 |
54 | > Para pensar 🤔: Y si ahora quisieras agregar la información de cuál es su gusto de helado favorito, ¿cómo harías?
55 |
56 | ## 4. Comerciando código
57 |
58 | Para poder trabajar con estas tablas en formato `csv` vamos a necesitar nuevas funciones, procedimientos, tipos de datos y otras yerbas 🧉, especialmente diseñadas para este fin. Y como no queremos gastar tiempo en escribirlas a mano, vamos a recurrir a una _biblioteca_: código que otras personas ya programaron y empaquetaron para que podamos incluirlo y usarlo en nuestros programas. Hay bibliotecas para miles de tareas diferentes: procesar imágenes, hacer cuentas sofisticadas, producir música, analizar textos, y mucho más 🤯.
59 |
60 | En particular, la biblioteca que nos va a interesar ahora se llama `pandas`, una herramienta poderosa para el lenguaje Python, que posibilita manipular datos de un lote de forma programática. En otras palabaras, va a permitirnos hacer las mismas operaciones que haríamos en una hoja de cálculo, pero utilizando código.
61 |
62 | ¡Carguemos a nuestra biblioteca de ositos 🐼!
63 |
64 | ```python
65 | import pandas as pd
66 | ```
67 |
68 | Con esta sentencia estamos cargando (o _importando_) en nuestro programa, y dejándola lista para ser utilizada, bajo el nombre `pd`. Podríamos leerlo como _"computadora: andá a buscar la biblioteca `pandas` y nombrala como `pd`"_.
69 |
70 | 📝 Nota: El nombre `pd` es totalmente arbitrario y podríamos haberle dado cualquier otro. Pero casi todo el mundo que la usa (sobre todo en internet) en suele abreviar _pandas_ de esa forma 🤷.
71 |
72 | ## 5. A cargar, a cargar, cada tabla en su lugar
73 |
74 | Ahora que importamos la biblioteca `pandas`, el siguiente paso es conseguir un lote de datos, como por ejemplo, el listado de bicicleterías 🚴 que hay que CABA, cuya dirección es [ésta](https://cdn.buenosaires.gob.ar/datosabiertos/datasets/transporte/bicicleterias/bicicleterias-de-la-ciudad.csv)
75 |
76 | Un vez que hayamos encontrado la dirección (y copiado en nuestro portapeles 📋, para no tener que escribir la dirección a mano), podremos finalmente cargarla en un `DataFrame` llamado `bicicleterias`,
77 | utilizando la función `pd.read_csv`:
78 |
79 | ```python
80 | bicicleterias = pd.read_csv("https://cdn.buenosaires.gob.ar/datosabiertos/datasets/transporte/bicicleterias/bicicleterias-de-la-ciudad.csv")
81 | bicicleterias
82 | ```
83 |
84 | > Probá éste código y observá qué sucede
85 |
86 | ### Solución posible
87 |
88 |
89 | 👀 Ver
90 |
91 | Deberías ver una tabla con los datos de las bicicleterías:
92 |
93 | ```python
94 | >>> bicicleterias
95 | 0 POINT (-58.4256879365174 -34.5923050897605) 1 ... Palermo Comuna 14
96 | 1 POINT (-58.4259820523858 -34.581377735346) 2 ... Palermo Comuna 14
97 | 2 POINT (-58.4153429603451 -34.6533516212895) 3 ... Nueva Pompeya Comuna 4
98 | 3 POINT (-58.4432441275002 -34.5943508729756) 4 ... Villa Crespo Comuna 15
99 | 4 POINT (-58.3773343753292 -34.6083635891506) 5 ... Monserrat Comuna 1
100 | .. ... ... ... ... ...
101 | 107 POINT (-58.4514064416743 -34.6064832870543) 119 ... Caballito Comuna 6
102 | 108 POINT (-58.41682788474 -34.595271412766) 120 ... Palermo Comuna 14
103 | 109 POINT (-58.4352606531347 -34.5676102200762) 121 ... Palermo Comuna 14
104 | 110 POINT (-58.4540104298256 -34.6132076385661) 122 ... Caballito Comuna 6
105 | 111 POINT (-58.4221769107392 -34.5994544804325) 123 ... Almagro Comuna 5
106 |
107 | [112 rows x 15 columns]
108 | ```
109 |
110 |
111 |
112 | ### Para pensar
113 |
114 | ¡Está vivo! ⚡ ¡Apareció una tabla! Pero, momento... ¿un `DataFrame`? ¿Qué es eso? ¡Resolvamos el misterio! 🦇🎃
115 |
116 |
117 | ## 6. ¡Hay tabla!
118 |
119 | Hasta ahora veníamos trabajando con valores como números (por ejemplo, el `1`, el `42` y el `30410`), cadenas de caracteres (`"estro es un string"`) o booleanos (`True` o `False`)...
120 |
121 | ```python
122 | # type es una función que nos permite saber el tipo de dato de un valor
123 | >>> type(1)
124 |
125 | >>> type("hola mundo")
126 |
127 | >>> type(True)
128 |
129 | ```
130 | ... pero eso es muy limitado. Por eso vamos a usar ahora un nuevo tipo de dato llamado `DataFrame`, que es el tipo de la variable `bicicleterias`:
131 |
132 | ```python
133 | >>> type(bicicleterias)
134 |
135 | ```
136 |
137 | Los `DataFrame` representan justamente tablas, compuestas por columnas y filas.
138 |
139 | ## 7. Hablando de cantidades
140 |
141 | ¿Y cómo hacemos para saber cuántas filas y columnas tiene un `DataFrame`? Con nuestra vieja y querida función `len`:
142 |
143 | ```python
144 | # cantidad de filas
145 | >>> len(bicicleterias)
146 | 112
147 | # cantidad de columnas
148 | >>> len(bicicleterias.columns)
149 | 15
150 | ```
151 |
152 | 💡 Como vemos, si consultamos simplemente la longitud de la tabla, esta se corresponde con la cantidad de filas, lo cual tiene bastante sentido. Como veremos a partir de ahora muchas de las operaciones justamente tendrán que ver con la cantidad o posición de filas.
153 |
154 | ## 8. De la cabeza a los pies
155 |
156 | Como si de un gusanito 🪱 se tratara, las tablas tienen _cabeza_ y _cola_: la cabeza refiere a las primeras `n` filas, mientras que la cola a las últimas `n`. Para obtener la cabeza podemos usar la función infija (más comunmente llamada _método_) `head`:
157 |
158 | ```python
159 | >>> bicicleterias.head(5)
160 | 0 POINT (-58.4256879365174 -34.5923050897605) 1 Bicicletas Araoz ... ARAOZ 1458 Palermo Comuna 14
161 | 1 POINT (-58.4259820523858 -34.581377735346) 2 Roda2oro ... FRAY JUSTO SANTAMARIA DE ORO 2305 Palermo Comuna 14
162 | 2 POINT (-58.4153429603451 -34.6533516212895) 3 Walter ... LYNCH 3914 Nueva Pompeya Comuna 4
163 | 3 POINT (-58.4432441275002 -34.5943508729756) 4 Bici Shop ... VILLARROEL 1093 Villa Crespo Comuna 15
164 | 4 POINT (-58.3773343753292 -34.6083635891506) 5 Bicicleterias El colo ... RIVADAVIA AV. 770 Monserrat Comuna 1
165 |
166 | [5 rows x 15 columns]
167 | ```
168 |
169 | ⚠️ Es importante notar que `head` no orden la tabla bajo ningún criterio, sino que sólo nos da las primeras filas, según la posición en la que originalmente estuvieran.
170 |
171 | De forma análoga podemos obtener la cola:
172 |
173 | ```python
174 | >>> bicicleterias.tail(3)
175 | WKT id nombre ... direccion_ barrio comuna
176 | 109 POINT (-58.4352606531347 -34.5676102200762) 121 Bicis ... NEWBERY, JORGE 1741 Palermo Comuna 14
177 | 110 POINT (-58.4540104298256 -34.6132076385661) 122 Bicicletería Bike Doctor ... FRAGATA PRES. SARMIENTO 966 Caballito Comuna 6
178 | 111 POINT (-58.4221769107392 -34.5994544804325) 123 Rodados La Esquina Express ... ACUÑA DE FIGUEROA, FRANCISCO 1000 Almagro Comuna 5
179 |
180 | [3 rows x 15 columns]
181 | ```
182 |
183 | ## 9. Ordená, ¡es una orden!
184 |
185 | ```python
186 | # sort_values también es una función infija
187 | # tabla.sort_values(nombre_columna)
188 | bicicleterias.sort_values("comuna")
189 | ```
190 |
191 | ## 10. Del derecho y del revés
192 |
193 | ```python
194 | bicicleterias.sort_values("comuna", ascending=False)
195 | ```
196 |
197 | ## 11. Haciendo valer el orden
198 |
199 | > Desafío: obtené las últimas 3 bicileterías, según su nombre por orden alfabético.
200 |
201 | ## 12. Un gráfico vale más que mil filas
202 |
203 | Podemos hacer un gráfico de barras con el resultado de un `value_counts` usando `plot.bar` de la siguiente manera:
204 |
205 | ```python
206 | bicileterias.plot.bar(figsize=(tamanio_x_en_pulgadas, tamanio_y_en_pulgadas))
207 | ```
208 |
209 | ## 13. Combinando todo
210 |
211 | > Desafío: ¿cómo podríamos hacer para obtener todas las filas desde la décima y hasta la veinteva? ¡Escribilo!
212 |
213 | ### Solución posible
214 |
215 |
216 | 👀 Ver
217 |
218 | ```python
219 | bicicleterias.head(20).tail(10)
220 | ```
221 |
222 |
223 |
224 | ## 14. Matrices recargadas
225 |
226 | Y ahora combinemos todo... ¡de nuevo!
227 |
228 | > Definí una función `entre_limites`, que generalice lo que hicimos en el ejercicio anterior: debe tomar una tabla y dos números, y devolvernos un `DataFrame` que contenga las filas entre los índices dados.
229 |
--------------------------------------------------------------------------------
/3_ciencia_de_datos/1_introducción_a_pandas/Introducción_pandas.md:
--------------------------------------------------------------------------------
1 | > Este material retoma material de [Bioinfo UNQ](https://github.com/AJVelezRueda/Bioinfo_UNQ/tree/master/Trabajos_Practicos/Estadistica_con_pandas)
2 |
3 | ### Introducción a la Ciencia de Datos con Python
4 |
5 | Para este recorrido necesitarás las librerías [Pandas](https://pandas.pydata.org/), [Seaborn](https://seaborn.pydata.org/) y [Scipy](https://www.scipy.org/)
6 |
7 |
8 | Podes corroborar si las tienes instaladas corriendo las siguientes líneas en tu intérprete de Python:
9 |
10 | ```python
11 | import pandas as pd
12 | import seaborn as sns
13 | import scipy.stats as ss
14 | ```
15 |
16 | Si correr estas lineas no tira ningún error, etonces están felizmente instaladas las bibliotecas enc uestión. De lo contrario, obtendremos un mensaje de error `ModuleNotFoundError: No module named` al correr las lineas anteriores. En tal caso, podés instalar las bibliotecas desde la consola, con el comando:
17 |
18 | ```bash
19 | pip install pandas
20 | pip install seaborn
21 | pip install scipy
22 | ```
23 |
24 | En este recorrido trabajaremos sobre los datos abiertos del sobre el personal del [Ministerio de Ciencia y Tecnología](https://datasets.datos.mincyt.gob.ar/dataset/personal-de-ciencia-y-tecnologia/archivo/11dca5bb-9a5f-4da5-b040-28957126be18) del Gobierno Argentino.
25 |
26 | Si bien no es estrictamente necesario saber a fondo la sintaxis de Python para comenzar a utilizar Pandas, te recomendamos fuertemente realizar el [recorrido introductorio de Python](https://github.com/AJVelezRueda/UCEMA_Fundamentos_de_informatica/blob/master/Python_intro/intro_python_tutorial.md), que te brindará los conocimientos básicos de programación en general y de Python particular que te permitiran abordar este contenido sin problemas.
27 |
28 | # Guias de Trabajo
29 | * [1. Un osito cariñosito](#1-pandas)
30 | * [2. Trabajando con DataFrames](#2-dfs)
31 | * [3. Métodos de los DataFrames](#3-metodos)
32 | * [4. Tratamiento de Datos con Python](#4-datos)
33 |
34 | [1. Un osito cariñosito](#1-pandas)
35 |
36 | En este recorrido vamos a adentrarnos en el mundo de los datos, y para ello utilizaremos Pandas, una biblioteca de Python que nos permite trabajar con archivos de formato definido: CSV, un excel, etc. Además, Pandas proporciona estructuras de datos rápidas, flexibles y expresivas diseñadas para que trabajar con datos "relacionales" o "etiquetados" sea fácil e intuitivo. En criollo: Pandas es como en excel, pero super duper!
37 |
38 | > Para pensar 🤔: Si hasta aquí no te has preguntado qué es una bliblioteca, ¡es momento de hacerse esa pregunta! ¿Para qué creés que nos puede resultar útil esta biblioteca? ¿Cuál es la ventaja de usar Pandas? ¿Por qué no solo usar Python `"de a pie"`?
39 | >
40 |
41 | Pandas soporta múltipes tipos de datos:
42 |
43 | - Datos tabulares con columnas de tipo heterogéneo, como en una tabla SQL o en una hoja de cálculo de Excel
44 | - Datos ordenados y desordenados (no necesariamente frecuencia fija).
45 | - Datos matriciales arbitrarios (homogéneamente tipados o heterogéneos) con etiquetas de fila y columna
46 | - Cualquier otra forma de conjuntos de datos observacionales/estadísticos.
47 |
48 | Los datos en realidad no necesitan ser etiquetados para ser colocados en una estructura de datos de pandas. Estas estructuras se construyen a partir de arrays(listas), pero agregando nuevas funcionalidades. Pandas maneja dos estructuras de datos: Series y DataFrames.
49 |
50 | **Series (1-dimensional)**
51 | Las series pueden contener cualquier tipo de datos (enteros, cadenas, números de punto flotante, etc.). Y se pueden crear del siguiente modo:
52 |
53 | ```python
54 | import pandas as pd
55 | una_serie = pd.Series(['Peru', 'Argentina', 'Bolivia', 'Uruguay', 'Brasil', 'Chile'], dtype='string')
56 |
57 | print(una_serie)
58 | ```
59 |
60 | **DataFrames (2-dimensional)**
61 |
62 | Un DataFrame es una estructura tabular bidimensional de datos tabulares, potencialmente heterogéneos, con ejes etiquetados (filas y columnas). Las operaciones aritméticas se alinean en las etiquetas de fila y columna. Se puede considerar como un contenedor similar a un dict para objetos Serie. Podemos crear un DataFrame del sigueinte modo:
63 |
64 | ```python
65 | paises_latam = pd.DataFrame(data ={"Pais": ['Peru', 'Argentina', 'Bolivia', 'Uruguay', 'Brasil', 'Chile'], "Lengua oficial primaria": ['Español', 'Español', 'Español', 'Español', 'Portugues', 'Español']}, index = [1,2,3,4,5,6])
66 |
67 | print(paises_latam)
68 | ```
69 |
70 | Por lo tanto, la serie es la estructura de datos para una sola columna de un DataFrame, no solo conceptualmente, sino literalmente, es decir, los datos en un DataFrame se almacenan realmente en la memoria como una colección de Series.
71 |
72 | También, se puede crear un DataFrame a partir de un diccionario, en este caso las claves se corresponderán con los nombres de las columnas y los valores con los datos de las filas para cada columna:
73 |
74 | ```python
75 | #será DataFrame(data=diccionario, index=filas, columns=columnas, dtype=tipos)
76 | datos = {"Pais": ['Peru', 'Argentina', 'Bolivia', 'Uruguay', 'Brasil', 'Chile'], "Idioma oficial": ['Español', 'Español', 'Español', 'Español', 'Portugues', 'Español']}
77 | paises_latam = pd.DataFrame(datos)
78 |
79 | print(paises_latam)
80 | ```
81 | 🛑 Alerta: los valores asociados a las claves del diccionario deben ser listas del mismo tamaño
82 |
83 | `df` es el nombre génerico para designar DataFrame y es el nombre que utilizaremos de ahora en más para mayor simplicidad.
84 |
85 |
86 | Otra forma muy usual de generar DataFrames es mediante la lectura de **archivos estructurados**. Existen muchas formas de cargar/leer información desde archivos la información desde archivos pero en general la diferencia radica principalmente en los parámetros por defecto que toman para definir las columnas. Por ejemplo:
87 |
88 | - El caracter de separación de columnas por defecto del método `read_cvs` es una coma (',')
89 | - El caracter de separación de columnas por defecto del método `read_fwf` es una tab ('\t').
90 |
91 | ```python
92 | import pandas as pd
93 | df = pd.read_csv(path_al_erchivo)
94 | ```
95 |
96 | > 🧗♀️ Desafío I: Estos métodos aceptan otros parámetros que merecen la pena ser explorados. Averiguá para qué sirven los parámetro sep, index_col, nrows y header
97 |
98 | > 🧗♀️ Desafío II: Descargá a tu computadora la [tabla](https://datasets.datos.mincyt.gob.ar/dataset/personal-de-ciencia-y-tecnologia/archivo/11dca5bb-9a5f-4da5-b040-28957126be18) de personas que conforman el Ministerio de Ciencia y Tecnología de Argentina, en formato csv.
99 | >
100 | > Cargá (lee) la tabla a un DataFrame de Pandas de nombre `personas` ¿Qué forma te lectura de archivos usarías? ¿Qué separación entre columnas posee el archivo? ¿Cómo te diste cuenta? 🤔
101 | >
102 |
103 | Ya tenemos nuestra tabla cargada, podeés hacer una previsualización de los datos haciendo:
104 |
105 | ```python
106 | personas.head()
107 | ```
108 | > Para pensar 🤔: ¿Cuántas filas se imprimen al hacer head? ¿Qué sucede si hacemos `personas.head(10)`? ¡Probalo!
109 |
110 | [2. Trabajando con DataFrames](#2-dfs)
111 |
112 | Ahora que aprendiste a cargar datos en una `"tabla"` de Pandas, podés averiguar la información general de tu tabla haciendo:
113 |
114 | ```python
115 | personas.info()
116 | ```
117 |
118 | Si bien esta información nos ayuda a saber los nombres de las columnas de nuestra tabla, o el tipo de datos que contiene cada una de ellas, quizás una descripción más informativa podría ser:
119 |
120 | ```python
121 | personas.describe()
122 | ```
123 | > Para pensar 🤔: ¿Qué tipo de información nos brinda el método describe? ¿Tienen sentido estos cálculos para todas las columnas?
124 | >
125 |
126 | Podemos acceder a los datos de cada columna haciendo df['nombre de la columna'], en nuestro caso por ejemplo:
127 |
128 | ``` python
129 | personas[' persona_id']
130 | ```
131 |
132 | > Para pensar 🤔: ¿Podés imprimir la columna de los `max_dedicacion_horaria_docente_id` de nuestra tabla? ¿Cómo calcularías el promedio de esta columna?
133 |
134 | Quizás nos resulte útil acceder, no a todos los datos de una columna, sino a un dato de una celda en particular. Para ello podemos utilizar _iloc_:
135 |
136 | ```python
137 | df.loc[fila, columna]
138 | ```
139 | esto devuelve el elemento que se encuentra en la fila con nombre fila y la columna de con nombre columna del DataFrame df. Probá el siguiente código:
140 |
141 | ```python
142 | personas.loc[2, 'persona_id']
143 | ```
144 |
145 | > Para pensar 🤔: ¿Qué resultado obtuviste? ¿Por qué? ¿Cómo obtendrías la edad de esa persona?
146 |
147 | Podemos acceder los datos de una columna del DataFrame como una lista mediante el método _tolist()_:
148 |
149 | ``` python
150 | df['columna'].tolist()
151 | ```
152 |
153 | > 🧗♀️ Desafío IV: Extrae la columna `seniority_level` y contá cuántas personas tenían expertice nivel B, C y D
154 |
155 | Seguramente tu resulación del _Desafío IV_ implicó hacer un _bucle for_ y un if, lo cual parece a priori un tanto engorroso. Para evitarnos tantas lineas de código, podemos hacer uso de los métodos _groupby()_ y _count()_, que nos permiten contar sobre una columna la frecuencia de un dato/evento en particular. Ejecutá las siguientes lineas a ver qué pasa:
156 |
157 | ```python
158 | personas["seniority_level"].count()
159 |
160 | personas.groupby("seniority_level").count()
161 |
162 | personas.groupby("seniority_level")[["persona_id"]].count()
163 | ```
164 |
165 | > 🧗♀️ Desafío V: ¿Qué resultados obtuviste en cada caso? Explicá qué hace cada linea de código
166 |
167 | Podemos operar con las columnas con los mismo operadores relacionales y matemáticos que ya hemos visto:
168 |
169 | ``` python
170 | personas['edad'] * 2
171 | personas['edad'] + 2
172 | personas['edad'] > 2
173 | ```
174 | > Para pensar 🤔: ¿Qué resultado nos daría en cada caso?
175 |
176 | Pero los operadores tambien nos sirven también para filtrar nuestro DataFrame:
177 |
178 | ``` python
179 | personas[personas['edad'] < 35 ]
180 | ```
181 |
182 | > 🧗♀️ Desafío V: Contá cuántas personas de 30 años ingresaron al ministerio en 2011 ¿Cuántas formas de hacer este cálculo se te ocurren?
183 |
184 | Ahora vamos a ver cómo podemos incorporar más información a nuestro DataFrame. En la página del ministerio podés encontrar las tablas que pueblas la tabla general...veamos por ejemplo la tabla de [categoría de conicet](https://datasets.datos.mincyt.gob.ar/dataset/personal-de-ciencia-y-tecnologia/archivo/c72c9f88-d9ef-4349-bb20-5c9a1aca5d67)
185 |
186 | > 🧗♀️ Desafío VI: Descargala en formato csv y cargala en un nuevo DataFrame de nombre `categorias`
187 | > 🧗♀️ Desafío VII: Identificá si existen columnas en común con el DataFrame grande
188 |
189 | Supongamos que ahora queremos poder realizar análisis de nuestros datos filtrando por categoria de conicet, en este caso podemos combinar las dos tablas, de modo de saber qué valos de `categoria_conicet_id` se corresponde con cada categoria de conicet. Probemos haciendo:
190 |
191 | ``` python
192 | personas_cat = pd.merge(personas, categorias, on='categoria_conicet_id')
193 | ```
194 |
195 | > Para pensar 🤔: ¿Qué datos tiene df3? ¿Qué hace el método merge?
196 |
197 | Probemos ahora el método _concat()_:
198 | ``` python
199 | personas_cat = pd.conact([personas, categorias])
200 | ```
201 | > Para pensar 🤔: ¿Qué datos tiene df3? ¿Qué hace el método _concat()_ y qué diferencia tiene con hacer _merge()_?
202 |
203 | [3. Métodos de los DataFrames](#3-metodos)
204 |
205 | Veamos un resumen de los métodos que podés encontrar en Pandas para trabajar con DataFrames:
206 |
207 | | Lectura/carga de datos | Limpieza de los datos | Estdistica de los datos |
208 | |------------- |---------- |--- |
209 | | pd.read_csv() | pd.head() | pd.describe() |
210 | | pd.read_table() | pd.fillna() |df.sample()|
211 | | pd.read_excel() | pd.dropna() | pd.mean() |
212 | | pd.read_sql() | pd.sort_values() | pd.median() |
213 | | pd.read_json() | pd.groupby() | pd.std() |
214 | | pd.to_csv() |pd.apply() | pd.min() |
215 | | pd.DataFrame() | pd.append() | pd.max() |
216 | | pd.concat() | pd.rename() | pd.count() |
217 | | pd.Series() | pd.set_index() | pd.corr() |
218 | | pd.DataFrame.from_dict() | pd.tail() | pd.hist() |
219 |
220 |
221 | >
222 | > 🧗♀️ Desafío III: averiguá para qué sirve cada uno de los métodos y qué parámetros podés pasarseles. ¡Esta información nos será útil para más adelante!
223 | >
224 |
225 | Ahora que conocemos algunas de los métodos que nos permiten trabajar con DataFrames, veamos como cómo [trabajar los datos](https://github.com/flbulgarelli/recursos-python/blob/master/2_Ciencia_de_datos_pandas/Analisis_de_datos_con_pandas.md) 🤓
--------------------------------------------------------------------------------
/1_introduccion_a_la_programacion/16_expresiones_regulares/README.md:
--------------------------------------------------------------------------------
1 | # *Python avanzado*
2 | En este recorrido aprenderemos los conceptos básicos de expresiones regulares en [Python](https://www.python.org.ar/). Para ello vas a necesitar instalarte el módulo [RE](https://pypi.org/project/regex/).
3 |
4 |
5 | # Guias de Trabajo
6 | * [1. Lo esencial es invisible a los ojos](#1-Escape-characters)
7 | * [2. ¿Qué son las expresiones regulares?](#2-ER)
8 | * [3. Metacaracteres](#3-Metacaracteres)
9 | * [4. Expresiones regulares en Python ](#4-RE)
10 | * [5. Coincidencias o Matches](#5-matches)
11 | * [6. Reemplazos o sustituciones masivas](#6-sub)
12 |
13 | [1. Lo esencial es invisible a los ojos](#1-Escape-characters)
14 |
15 | Cuando trabajamos con archivos de texto, suele pasar desapercibida la presencia de caracteres que dan formato legible al texto y que no se representan por así decirlo "graficamente explicitos". Un ejemplo de ello es el espacio entre las palabras que tipeamos para constuir una oración. Este tipo de caracteres, comunmente conocidos como caracteres especiales, se encuentran respresentados por _secuencias de escape_.
16 |
17 | Las _secuencias de escape_ son una combinación de caracteres que tiene un significado distinto de los caracteres literales contenidos en ella y se utilizan para definir ciertos caracteres especiales dentro de cadenas de texto, tipicamente aquellos que dan formato al mismo. Y aún cuando son un conjunto de caracteres, una secuencia de escape se considerada un carácter único.
18 |
19 | Estas combinaciones constan tipicamente de una barra invertida (`\`) seguida de una letra o de una combinación de dígitos, que tendrá un significado distinto según cuales sean. Por ejemplo, para representar un carácter de _salto de línea_ se utiliza la secuencia de espace `\n`.
20 |
21 | Vamos a ver algunas de las secuencias de escape más frecuentes:
22 |
23 | | Secuencia de escape| representación |
24 | |------------- |---------- |
25 | | \n | salto de línea |
26 | | \t | Tab o cambio de columna |
27 | | \s | espacio |
28 | | \' | Comillas simples |
29 | | \" | Comillas dobles |
30 |
31 |
32 |
33 | [2. ¿Qué son las expresiones regulares?](#2-ER)
34 |
35 | Las expresiones regulares son cadenas de caracteres basadas en reglas sintácticas, que permiten describir secuencias de caracteres. Es decir es un criterio para buscar, capturar o reemplazar texto utilizando patrones. Estas son una herramienta poderosa a la hora de hacer búsquedas sofisticadas en Strings de forma simple.
36 |
37 |
38 | Las expresiones regulares se pueden concatenar para formar nuevas expresiones regulares; si, por ejemplo, A y B son expresiones regulares, AB también es una expresión regular.
39 |
40 |
41 | > Para pensar 🤔: ¿Qué usos crees que podemos darle a las expresiones regulares? Proponé una aplicación concreta para tu carrera/disciplina.
42 | >
43 |
44 | [3. Metacaracteres](#3-Metacaracteres)
45 |
46 | Los `metacaracteres` son caracteres especiales que, dependiendo del contexto, tienen un significado especial para las expresiones regulares.
47 |
48 | Existen lo que se conoce como `metacaracteres delimitadores`, que nos permitirán delimitar dónde queremos buscar los patrones de búsqueda. Entre ellos tenemos:
49 |
50 |
51 | | Metacaracter| Significado |
52 | |------------- |---------- |
53 | | ^ | Inicio de línea |
54 | | $ | Fin de linea |
55 | | \A | Inicio de texto |
56 | | \Z | Fin de texto |
57 | | . | Coincide con cualquier caracter en una línea dada |
58 |
59 | > Para pensar 🤔: Dado el siguiente texto:
60 | ```python
61 | texto = 'Esta es la linea uno\npalabra en la linea dos\n'
62 | ```
63 | >¿Cómo crees que darán las siguientes búsquedas?
64 | >
65 | >expresion regular a: `'^palabra'`
66 | >
67 | >expresion regular b: `'\Apalabra'`
68 | >
69 | >expresion regular c: `'palabra$'`
70 | >
71 | >expresion regular d: `'palabra\Z'`
72 | >
73 |
74 | Ya vimos que en programación suele ser útil repetir la ejecución de porciones de código. Las expresiones regulares nos permiten no solo delimitar la porción de texto donde deseamos buscar, sino que también permite repitir cierta cantidad de veces una busqueda dada. Para ello se utilizan los `metacaracteres cuantificadores`:
75 |
76 |
77 | | Metacaracter| Significado |
78 | |------------- |---------- |
79 | | * | Cero o más: todas las ocurrencias de un dado substring |
80 | | + | Una o más ocurrencias del patrón|
81 | |? | Cero o una|
82 | |{n} | Exactamente n veces|
83 | |{n,m} | Por lo menos n pero no más de m veces.|
84 |
85 |
86 | >
87 | > Para pensar 🤔: ¿Qué significará la expresión regular `"X(.*)Y"`? Ennumera algunas de las posibles strings que cumplen con dicha condición.
88 | >
89 | >
90 | > 🧗♀️ Desafío I: ¿Construí la expresión regular que obtenga al menos 4 dígitos?
91 | >
92 | > 🧗♀️ Desafío II: ¿Construí la expresión regular que obtenga al entre 3 y 6 letras minúsculas?
93 | >
94 | > 🧗♀️ Desafío III: ¿Construí la expresión regular que obtenga todas las apariciones del patrón `ab` en un string?
95 | >
96 |
97 |
98 | Respuestas
99 |
100 | ```bash
101 | Desafio I: \d{4,}
102 |
103 | Desafio II: [a-z]{3,6}
104 |
105 | Desafio III: ab*
106 |
107 | ```
108 |
109 |
110 | > Para pensar 🤔: ¿Existe una única respuesta para los ejercicios? ¿Qué otras alternativas se te ocurren?
111 |
112 | Los dígitos entre llaves de la forma {n,m}, especifican el mínimo número de ocurrencias en n y el máximo en m.
113 |
114 | Existen tambien metacaracteres predefinidos, que nos facilitan el uso de las expresiones regulares:
115 |
116 | | Metacaracter| Significado |
117 | |------------- |---------- |
118 | | \w | Caracter alfanumércio|
119 | | \W | Caracter NO alfanumércio|
120 | | \d | Caracter numércio|
121 | | \D | Caracter NO numércio|
122 | | \s | Un espacio, de cualquier tipo (\t\n\r\f)|
123 | | \S | Cualquier caracter que no sea un espacio|
124 |
125 |
126 | Como ya hemos visto, estos metacaracteres puden combinarse para lograr expresiones regulares complejas.
127 |
128 | >
129 | > 🧗♀️Desafio IV: ¿Qué expresión regular usarías para extraer el número de estudiantes que hay en una clase según el siguiente texto:
130 | >
131 | ```python
132 | texto = 'En la clase de Introducción a la programación hay 30 estudiantes'
133 | ```
134 | >
135 |
136 |
137 | Respuestas
138 |
139 | ```bash
140 | Desafío IV: /d+
141 | ```
142 |
143 |
144 | **Rangos**
145 |
146 | Un rango es una clase de caracteres abreviada que se crea escribiendo el primer caracter del rango, un guión y el último caracter del rango. Sirve para listar un conjunto de caracteres de interés, de este modo se encontrará uno cualquiera de los caracteres de la lista. Por ejemplo:
147 |
148 | - El rango [a-d] equivale al [abcd]
149 | - El rango [1-10] equivale al substring [12345678910]
150 | - El rango [Dd] equivale a buscar una D mayúscula y una d minúscula
151 |
152 | Así como podemos listar los caracteres posibles en cierta posición de la cadena, también podemos listar caracteres que no deben aparecer utilizando el `^`. Así, por ejemplo rango [^a-d] coincide con cualquier caracter que no sea `abcd`.
153 |
154 | [4. Expresiones regulares en Python ](#4-RE)
155 |
156 |
157 | Para trabajar con expresiones regulares en Python, es necesaria la librería [RE](https://docs.python.org/3/library/re.html), que puede ser instalada usando el instalador de Python (PIP):
158 |
159 | ```bash
160 | pip install re
161 | ```
162 | De todos modos, antes de instalar una librería siempre es importante comprobar si esta está o no instalada. Para ello, desde una terminal de Python debemos escribir:
163 |
164 | ```python
165 | import re
166 | ```
167 | Si la librería está instalada no nos aparecerá ningún error.
168 |
169 |
170 | [5. Coincidencias o Matches](#5-matches)
171 |
172 |
173 | Comenzaremos por aprender sobre las expresiones regulares más simples posibles. Dado que las expresiones regulares se utilizan para operar en strings, vamos a empezas con la tarea más común: los caracteres coincidentes.
174 |
175 | Si un String se corresponde con el criterio que define una expresión regular, se dice que el String hace match con la expresión, y equivalentemente, se dice que la expresión acepta al String.
176 |
177 | Podemos encontrar patrones en un texto con el función _search_:
178 |
179 | ```python
180 | >>> import re
181 | >>> texto = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Amet et amet."
182 | >>> patron = "amet"
183 | >>> re.search(patron, texto)
184 | ```
185 |
186 | > Para pensar 🤔: ¿Qué resultado obtenemos al ejecutar en la última linea?
187 | >
188 | > 🧗♀️Desafio V: imprimí el fragmento del texto entre la posición 22 y 26 ¿Qué resultado obtenés? ¿Qué quiere decir el mensaje _span_?
189 | >
190 | Ahora veamos qué hace _match()_:
191 |
192 | ```python
193 | >>> import re
194 | >>> texto = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Amet et amet."
195 | >>> patron = "amet"
196 | >>> re.match(patron, texto)
197 | ```
198 |
199 | >
200 | >Para pensar 🤔: ¿Qué resultado obtenemos con _search()_?¿Qué diferencias observan con _match()_?
201 | >
202 |
203 |
204 |
205 | Comentarios
206 |
207 | El función **match()** de re busca el patrón y devuelve la primera aparición y solo al principio de la cadena. Si se encuentra una coincidencia en la primera línea, devuelve el objeto de coincidencia. Pero, si se encuentra una coincidencia en alguna otra línea, devulve un valor nulo.
208 |
209 |
210 | Vamos a ejecutar la última linea con una modificación:
211 |
212 | ```python
213 | >>> import re
214 | >>> texto = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Amet et amet."
215 | >>> patron = "amet"
216 | >>> re.search(patron, texto).group()
217 | ```
218 |
219 | >
220 | >Para pensar 🤔: ¿Qué resultado obtenemos? ¿Para qué sirve la función group()?
221 | >
222 | >
223 |
224 |
225 | Utilicemos ahora otro método que nos permita obtener todas las ocurrencias del substring "amet"
226 |
227 | ```python
228 | >>> re.findall(patron, texto)
229 | ```
230 |
231 | >
232 | >Para pensar 🤔: ¿Qué resultado obtenemos?
233 | >
234 | > 🧗♀️Desafio VI: Expresá el patron de búsqueda utilizando lo visto anteriormente sobre metacaracteres y rangos.
235 | >
236 |
237 | Como vimos hasta acá el método ```group()``` sirve para mostrar el resultado de una búsqueda, pero veamos:
238 |
239 | ```python
240 | >>> import re
241 | >>> texto = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Amet et amet."
242 | >>> patron = "amet"
243 | >>> re.search(patron, texto).group()
244 | 'amet'
245 | >>> re.search(patron, texto).group(0)
246 | 'amet'
247 | ```
248 |
249 | El método ```group()``` (o ```group(0)```) nos devuelve la coincidencia. Sin embargo si lo que se quiere no es encontrar un patrón en particular, sino obtener lo que está dentro de cierto patrón (por ejemplo lo que hay entre ciertas palabras) hay que modificar el patrón.
250 | Vamos a ver el siguiente ejemplo:
251 |
252 | ```Python
253 | >>> import re
254 | >>> texto = "Lorem ipsum dolor sit amet, consectetur ipsum elit. Amet sit amet."
255 | >>> patron = "ipsum(.*)sit"
256 | >>> re.search(patron, texto).group()
257 | 'ipsum dolor sit amet, consectetur ipsum elit. Amet sit'
258 | >>> re.search(patron, texto).group(0)
259 | 'ipsum dolor sit amet, consectetur ipsum elit. Amet sit'
260 | >>> re.search(patron, texto).group(1)
261 | ' dolor sit amet, consectetur ipsum elit. Amet '
262 | ```
263 |
264 | Acá se utilizaron algunos metacaracteres, como lo son el punto (**.**) para indicar que puede ser cualquier carácter, y el asterísco (__*__) para indicar que puede haber 0 o más de estos caracteres. De esta manera obtenemos como resultado lo que se encuentre entre las palabras "ipsum" y "sit", sin embargo observen dos cosas. Primero, el string que nos devuelve tiene dentro un substring que debería haber sido encontrado en la búsqueda: "ipsum dolor sit", pero que sin embargo no aparece. Segundo, nuevamente al hacer ```group()``` o ```group(0)``` obtenemos la coincidencia, pero si nos queremos quedar con el substring que está contenido entre estas palabras debemos utilizar ```group(1)```.
265 | Ahora bien, como vimos, usar el patrón de búsqueda de esta manera prioriza los matches externos, es decir, busca el primer delimitador ("ipsum" en nuestro caso) y luego abarca todo hasta la última aparición del segundo delimitador ("sit"). Existe una forma de priorizar los matches internos y es con el metacarácter ```?```:
266 |
267 | ```Python
268 | >>> import re
269 | >>> texto = "Lorem ipsum dolor sit amet, consectetur ipsum elit. Amet sit amet."
270 | >>> patron = "ipsum(.*?)sit"
271 | >>> re.search(patron, texto).group()
272 | 'ipsum dolor sit'
273 | >>> re.search(patron, texto).group(0)
274 | 'ipsum dolor sit'
275 | >>> re.search(patron, texto).group(1)
276 | ' dolor '
277 | ```
278 |
279 | Cuando se quieren obtener todas las apariciones del patrón se utiliza el método ```findall``` el cual actúa para cada coincidencia como si devolviera el ```group(1)```, es decir devuelve en una lista la parte que se encuentra dentro de los delimitadores.
280 |
281 | ```Python
282 | >>> import re
283 | >>> texto = "Lorem ipsum dolor sit amet, consectetur ipsum elit. Amet sit amet."
284 | >>> patron = "ipsum(.*?)sit"
285 | >>> re.findall(patron, texto)
286 | [' dolor ', ' elit. Amet ']
287 | ```
288 |
289 |
290 | [6. Reemplazos o sustituciones masivas](#6-sub)
291 |
292 | Ejecutemos ahora la siguiente línea:
293 |
294 |
295 | ```python
296 | >>> re.sub(patron, "###", texto)
297 | ```
298 |
299 | >
300 | >Para pensar 🤔: ¿Qué resultado obtenemos? ¿Para qué sirve la función _sub_?
301 | >
302 |
303 | La función _sub_ permite reemplazar todos las ocurrencias del patrón por otro patrón en un String.
304 |
--------------------------------------------------------------------------------
/3_ciencia_de_datos/4_clustering/Clustering.md:
--------------------------------------------------------------------------------
1 | > Este material se basa en [Clustering con Python by Joaquín Amat Rodrigo](https://www.cienciadedatos.net/documentos/py20-clustering-con-python.html)
2 |
3 | # Pasos previos
4 | Para este recorrido necesitarás las librerías [Pandas](https://pandas.pydata.org/), [Seaborn](https://seaborn.pydata.org/) y [Scikitlearn](https://scikit-learn.org/stable/index.html)
5 |
6 | Podes corroborar si las tienes instaladas corriendo las siguientes líneas en tu intérprete de Python:
7 |
8 | ```python
9 | import pandas as pd
10 | import seaborn as sns
11 | import sklearn
12 | ```
13 |
14 | Si correr estas lineas no tira ningún error, etonces están felizmente instaladas las bibliotecas enc uestión. De lo contrario, obtendremos un mensaje de error `ModuleNotFoundError: No module named` al correr las lineas anteriores. En tal caso, podés instalar las bibliotecas desde la consola, con el comando:
15 |
16 | ```bash
17 | pip install pandas
18 | pip install seaborn
19 | pip install sklearn
20 | ```
21 |
22 |
23 | # Guias de Trabajo
24 | * [1. Clustering ¿Qué es?](#1-Intro)
25 | * [2. Un ojo en el Iris](#1-Iris)
26 | * [3. Calculo de distancias](#3-distancia)
27 | * [4. Normalizado y escalado de los datos](#4-escalado)
28 | * [5. K-means](#5-kmeans)
29 | * [6. Evaluación del resultado obtenido](#6-inercia)
30 |
31 | [1. Clustering ¿Qué es?](#1-Intro)
32 |
33 |
34 | Hemos estado trabajando hasta aquí en la carga y limpieza da datos con Pandas. Es momento de comenzar a trabajar con los datos, analizarlos y poder encontrar patrones que nos permitan derivar información. El aprendizaje automático consiste en identificar de patrones o tendencias que de los datos de forma automática.
35 |
36 | > Para pensar 🤔: ¿Qué utilidad le encontrás al aprendizaje automatizado? ¿Qué aplicaciones se te ocurren o conoces?
37 |
38 |
39 |
40 | Recurso audiovisuales complementarios
41 |
42 | [Inteligencia Artificial: ¿Amiga o Enemiga? | Diego Fernández Slezak](https://www.youtube.com/watch?v=znq3ql6wqnE)
43 |
44 | [¿De qué es capaz la inteligencia artificial? | DW Documental](https://www.youtube.com/watch?v=34Kz-PP_X7c&t=146s)
45 |
46 |
47 | En este recorrido nos centraremos particularmente en métodos que nos permitan describir la estructura u organización de nuestros datos. Los métodos de clustering(agrupamientos) nos permiten buscar patrones "ocultos" en los datos sin la necesidad de contar con la información sobre la verdadera clasificación (etiquetas) de los datos. Son métodos exploratorios que agrupan los datos según similitud para simplificar su análisis.
48 |
49 | > Para pensar 🤔: ¿Qué tipo de simplificaciones permite el agrupamiento de datos?
50 |
51 | Existen una amplia cantidad de técnicas que nos permiten encontrar patrones en los datos y agruparlas de algún modo, pero en todos los casos estos agrupamientos se establecen de forma que, las observaciones que están dentro de un mismo grupo, son similares entre ellas y distintas a las de otros grupos. Todos los métodos de clustering requieren de la definición y cuantificación de la similitud entre las observaciones. Es por ello que resulta necesario revisar el concepto de distancia, ya que es lo que se usa como medida de similitud o diferencia entre grupos.
52 |
53 |
54 |
55 | [2. Un ojo en el Iris](#2-Iris)
56 |
57 |
58 | En este recorrido utilizaremos el [set de datos](https://github.com/flbulgarelli/recursos-python/blob/master/2_Ciencia_de_datos_pandas/iris_data.txt) [Iris](https://en.wikipedia.org/wiki/Iris_flower_data_set) que consiste en un conjunto de datos u observaciones realizadas por el biólogo Ronald Fisher, sobre las característica de distintas especies de plantas plantas. ¿Será posible clasificar las plantas utilizando alguno de estas observaciones que hizo Fisher?
59 |
60 | Vamos a explorar los datos:
61 |
62 | ```python
63 | import pandas as pd
64 |
65 | iris = pd.read_csv(datapath, sep = '\t')
66 | ```
67 |
68 |
69 | > 🧗♀️ Desafío I: Averiguá qué variables (columnas) tiene la tabla e inspecioná el DataFrame
70 |
71 | Para tener una idea más intuitiva del "comportamiento" de los datos podemos graficar la distribución de frecuencias de las distintas variables que nos permitirá, por ejemplo, saber si las observaciones son únicas o se repiten. Para ello utilizaremos la biblioteca [Seaborn](https://seaborn.pydata.org/):
72 |
73 | ```python
74 | import seaborn as sns
75 |
76 | g = sns.histplot(data = iris, x = "sepal.length", binwidth=0.25, kde = True)
77 |
78 | ```
79 |
80 |
81 | > Para pensar 🤔: ¿Qué información obtenes del gráfico?
82 | >
83 | > 🧗♀️ Desafío II: Grafica la distribución de frecuencias de la variable "petal.length" ¿Qué información obtenes del gráfico? ¿Qué diferencias notás respecto del observado para la variable sepal.length?
84 | >
85 | > 🧗♀️ Desafío III: Grafica la distribución de frecuencias del resto de las variables.
86 | >
87 | > Para pensar 🤔: ¿Qué información pudiste obtener de observar las distribuciones de las distintas variables? ¿Cuántos tipos de plantas crees que existen?
88 |
89 | Ahora que pudimos observar como se comportan las variables, nos puede ser de gran utilidad estudiar las asociaciones entre las mismas (correlación). De este modo sabremos si el comportamiento (crecimiento o disminución) de una variable, se debe o está influenciada por otra. Con los pairplots de seabron, podemos entonces estudiar si existen correlaciones entre las variables:
90 |
91 |
92 | ```python
93 | import seaborn as sns
94 |
95 | g = sns.pairplot(iris)
96 |
97 | ```
98 | > 🧗♀️ Desafío IV: ¿Existe alguna correlación entre algunas de las variables? ¿Cómo te diste cuenta?
99 |
100 |
101 |
102 | [3. Calculo de distancias](#3-distancia)
103 |
104 | Hemos observado las distribuciones de nuestros datos y la manera en que se correlacionan las variables, y de este modo comenzar a intuir posibles agrupamientos de los datos. Es decir, pudimos observar mediante gráficos exploratorios que algunos registros muestran una mayor similitud entre si.
105 |
106 | Justamente, los métodos de clustering permiten la identificación automática de grupos en los que se pueden agrupar las observaciones de un conjunto de datos. Esto se hace de forma tal que las observaciones o registros asignados a un mismo grupo, muestren una mayor similitud entre sí que con los miembros de otros grupos. Pero, ¿Cómo medimos similitud entre miembros de un grupo dado? 🤔
107 |
108 |
109 | Hagamos un prueba para intentar dar respuesta a esta pregunta. Tomemos, por ejemplo, un conjunto de emojis 🙀😻🥰😱😾🙊🙈😠
110 | > Para pensar 🤔: ¿Cómo los agruparías/clasificarías?¿Se te ocurre más de una forma de clasificarlos?¿Qué criterios usaste para cada caso?
111 |
112 | Como habrás observado, la clasificación de los datos es subjetiva, en tanto depende del modo en que decidimos medir la similitud entre las observaciones. Y tal como hemos visto, existen múltiples formas de calcular lasimilitud entre los datos.
113 |
114 | Una forma de obtener la similitud es asumir que los datos son puntos en el espacio, por lo que si se define la distancia ente los puntos y se mide la separación entre dos registros, podrá obtenerse la similitud entre estos.
115 |
116 | Una de las formas más básicas para calcular la **distancia** entre dos puntos es la **Euclídea**, basada en el famoso [Teorema de Pitágoras](https://es.wikipedia.org/wiki/Teorema_de_Pit%C3%A1goras).. ¡Si, era importante Pitágoras!
117 |
118 | 
119 |
120 |
121 | ¿Pero no todas las definiciones de distancia son aplicables a todos los tipos de datos no? ¡Claro que no! ¿Como por ejemplo...? 🤔
122 |
123 |
124 | > Para pensar 🤔: ¿Qué otras formas de caluclar la distancia se te ocurren?
125 | >
126 | > 🧗♀️ Desafío V: Buscá otras formas de calcular la distancia entre las observaciones ¿Qué ventajas o desventajas encontras en cada forma de calcular las distancias?
127 |
128 | [4. Normalizado y escalado de los datos](#4-escalado)
129 |
130 | Ya hemos identificado las problemáticas a la hora de clasificar los datos, pero para que las comparaciones que hagamos sean completamente válidas, resulta de suma importancia hacer un tratamiento extra de los datos.
131 |
132 | Uno de los tratamientos necesarios es el escalado de los datos. Este procedimiento nos permite asegurarnos de que aún cuando algunas variables toman valores más grandes no se usarán como predictor principal a la hora de clasificar.
133 |
134 |
135 | Veamos un ejemplo gráfico de esta problemática que estamos describiendo:
136 |
137 | 
138 |
139 |
140 | Imaginemos que tenemos que analizar la trayectoria profesional de dos personas, para hacer una selección laboral. A priori, sería lógico pensar en basar esta selección en el curriculum de dichas personas. Sin embargo, resulta evidente que el curriculum no nos da un panaroma completo de las habilidades de una persona. Por ejemplo, no nos permite conocer su capacidad de trabajo en equipo o sus habilidades para realizar más de una tarea a la vez, etc. ¿Qué peso le estamos dando entonces a estas otras características? ¿Estamos subvalorando o sobrevalorando las hablidades de las personas? ¿Que pasa con las personas no tienen las mismas posibilidades para completar su curriculum? ¿Las hace menos capaces para el trabajo?
141 |
142 | Es por ello que resulta necesario escalar los datos. La escala es importante para poder especificar que una modificación en una cantidad no es igual a otra modificación en otra. En pocas palabras, escalar los datos le da a todas las características la misma importancia para que ninguna esté dominada por otra.
143 |
144 | Ademas, resulta necesario antes de comenzar a clasificar nuestros datos es la normalización. Esta implica transformar o convertir el conjunto de datos en una distribución normal, de forma que todos datos tenga una varianza del mismo orden. De este modo, cada dato nos dará una idea de a cuántos desvíos de la media está ese punto.
145 |
146 | Estas operaciones pueden hacerse muy fácilmente con la clase `StandardScaler`, del módulo `scikitlearn`:
147 |
148 |
149 | ```python
150 | from sklearn.preprocessing import StandardScaler
151 | scaler = StandardScaler()
152 | iris_escaleado = scaler.fit_transform(iris)
153 | ```
154 |
155 | [5. K-means](#5-kmeans)
156 |
157 | Ahora que hemos normalizado y escalado nuestros datos podemos finalmente utilizar un método para agrupar nuestros datos. Vamos a utilizar el método K-means(MacQueen, 1967) que agrupa las observaciones en los mejores K grupos distintos, es decirlos k clusters con la menor varianza interna (intra-cluster variation) posible. Es decir que se reparten las observaciones en K clusters de forma que la suma de las varianzas internas de todos ellos sea lo menor posible.
158 |
159 | Podríamos decir que este método repite/itera una serie de pasos hasta que encuentra los mejores k grupos:
160 |
161 | 
162 |
163 | 1) Especifica el número K de clusters que se quieren crear
164 |
165 | 2) Selecciona de forma aleatoria k observaciones del set de datos como centroides iniciales, esto es los datos a los cuáles se calcula la distancia para delimitar el grupo de menor varianza interna
166 |
167 | 3) Calcula las distancia de todos los datos al centroide, para definir a cuál se encuentra más próximo
168 |
169 | 4) Para cada uno de los K clusters recalcular su centroide, la posición del centroide se actualiza tomando como nuevo centroide la posición del promedio de las observaciones pertenecientes a dicho grupo
170 |
171 | 5) Repete los pasos 3 y 4 hasta que los centroides no se mueven, o se mueven por debajo de una distancia umbral en cada paso, o se alcancen el número de iteraciones definidas de antemano.
172 |
173 | Apliquemos ahora este método a nuestros datos:
174 |
175 |
176 | ```python
177 | k = 3 #definimos la cantidad de clusters
178 | kmeans = KMeans(n_clusters = k, init="random", n_init=10, max_iter=300, random_state=123457) #tomamos los centroides de forma aleatoria y definimos un máximo de 300 iteraciones
179 | kmeans.fit(iris_escaleado) #aplicamos el método a nuestros datos
180 | ```
181 |
182 | La asignación de cada punto a un cluster se obtiene el atributo `labels_` del objeto `clusters`, esta propiedad me dice que etiqueta le puso a cada uno de mis datos.
183 |
184 |
185 | ```python
186 | print(kmeans.labels_)
187 |
188 | ```
189 |
190 | Los centroides pueden ser obtenidos con `cluster_centers_`:
191 |
192 | ```python
193 | print(kmeans.cluster_centers_ )
194 |
195 | ```
196 |
197 | Para entender mejor los resultados obtenidos grafiquemos la distribución de puntos, pintando cada punto según el color correspondiente al etiquetado:
198 |
199 |
200 | ```python
201 | colores = ["red", "green", "blue"]
202 | g = sns.scatterplot(x = iris_escaleado[:,2], y = iris_escaleado[:, 3], hue = kmeans.labels_, palette = colores, alpha = 0.5)
203 | g = sns.scatterplot(x = kmeans.cluster_centers_[:,2], y = kmeans.cluster_centers_[:,3], zorder = 10, palette = colores, hue = [0, 1, 2], legend = False, marker=6, s=200)
204 | ```
205 |
206 | > Para pensar 🤔: ¿Es bueno o malo este resultado? ¿Cómo podríamos evaluar el resultado?
207 | >
208 |
209 |
210 | [6. Evaluación del resultado obtenido](#6-inercia)
211 |
212 | Una forma de evaluar cuan bien funcionó nuestro agrupamiento podría ser calcular cuan compactos son los grupos obtenidos. Entendiendo que funcionó mejor si todos los elementos del grupo están lo más cerca posible de su centro. Podemos, entonces, sumar las distancias de cada punto a su respectivo centro y usar eso como medida. A este valor se lo denomina inercia y puede obtenerse haciendo:
213 |
214 |
215 | ```python
216 | print(kmeans.inertia_ )
217 |
218 | ```
219 |
220 |
221 | >
222 | > 🧗♀️ Desafío VI: Calculá la inercia para distintos valores de k y almacenalos en un DataFrame
223 | >
224 | > 🧗♀️ Desafío VII: Realizá un gráfico de inercia vs k, usando el método pointplot de seaborn
225 | >
226 |
227 |
228 |
229 | Pista
230 | sns.pointplot(data = df, x = "K", y = "SSE")
231 |
232 |
233 |
--------------------------------------------------------------------------------