├── 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 | ![](https://raw.githubusercontent.com/MumukiProject/mumuki-guia-funcional-practica-valores-y-funciones/master/images/pino.png) 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 | ![](https://raw.githubusercontent.com/MumukiProject/mumuki-guia-funcional-practica-valores-y-funciones/master/images/pinos.png) 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 | ![Distancia Euclidea](./dist_euclídea.gif) 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 | ![Carrera profesional](./carrera.jpeg) 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 | ![Carrera profesional](./k_means.png) 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 | --------------------------------------------------------------------------------