From 7d94e901ddee341d8df4fe9c0f79dcde006ca350 Mon Sep 17 00:00:00 2001 From: Jean Pierre Mandujano G Date: Tue, 28 Oct 2025 00:12:56 -0500 Subject: [PATCH] feat: add pandas course --- .../1 Introduction.ipynb | 0 .../2 Lectura de archivos.ipynb | 0 .../3 Escritura de archivos.ipynb | 0 .../4 Manejo de archivos CSV.ipynb | 0 ...5 Introduccion a Pandas y DataFrames.ipynb | 0 .../Challenge/info.csv | 0 .../Challenge/system.txt | 0 .../Clases/1 Lectura de archivos.ipynb | 0 .../Clases/2 Escritura de archivos.ipynb | 0 .../Clases/archivo.txt | 0 .../Clases/lines.txt | 0 .../Clases/notas.txt | 0 .../Clases/output.txt | 0 .../Workspace.ipynb | 0 .../a.py | 0 .../data.csv | 0 .../df_example.csv | 0 .../images/Notas.jpg | Bin .../images/data.jpg | Bin .../images/outputcsv.jpg | Bin .../images/resultadoscsv.jpg | Bin .../notas.txt | 0 .../output.csv | 0 .../reporte.txt | 0 .../resporte.txt | 0 .../1 Introduccion a APIs.ipynb | 0 .../2 Realizacion de solicitudes HTTP.ipynb | 0 ...o de datos JSON en respuestas de API.ipynb | 0 .../4 Practica en consumo de APIs.ipynb | 0 .../comic_python.png | Bin .../images/GoogleMapApi.jpg | Bin .../images/openaiapi.png | Bin .../1 Introduccion a Base de Datos.ipynb | 0 .../2 SQLite en Python.ipynb | 0 .../4 Operaciones SQL basicas SELECT.ipynb | 0 .../5 Operaciones SQL basicas INSERT.ipynb | 0 .../Popular_Spotify_Songs.csv | 0 .../employee.db | Bin .../spotifydb | Bin .../1 Trabajando con datos.ipynb | 0 .../2 Caracterizando conjunto de datos.ipynb | 0 ...estra Tendencia central y Dispersion.ipynb | 0 .../4 Uso de diagramas de caja .ipynb | 0 .../5 Distribuciones.ipynb | 0 ... Algunas distribuciones importantes .ipynb | 0 .../7 Numeros aleatorios.ipynb | 0 .../Images/Media_muestra.jpg | Bin .../Images/densidad.jpg | Bin .../Images/frecuencia.jpg | Bin .../Images/frecuencia_relativa.jpg | Bin .../Images/mad.jpg | Bin .../Images/normal.jpg | Bin .../Images/uniforme.jpg | Bin .../Images/varianza.jpg | Bin .../Images/weibull.jpg | Bin ...troduccion al Algebra Lineal y Numpy.ipynb | 0 .../2 Vectores y Matrices.ipynb | 0 .../3 Modulo linalg de NumPy.ipynb | 0 ...Determinante e Inversa de una matriz.ipynb | 0 .../06_pandas/INDICE_CURSO.md | 252 ++++ .../06_pandas/Modulo_0_Fundamentos_POO.ipynb | 595 +++++++++ .../06_pandas/Modulo_1_Series.ipynb | 99 ++ .../06_pandas/Modulo_2_DataFrame.ipynb | 353 ++++++ .../Modulo_3B_Sintaxis_Indices.ipynb | 1063 +++++++++++++++++ .../06_pandas/Modulo_3_Index.ipynb | 322 +++++ .../06_pandas/Modulo_4_Encadenamiento.ipynb | 39 + .../06_pandas/Modulo_5_Herencia.ipynb | 39 + .../06_pandas/Modulo_6_Metodos_Magicos.ipynb | 39 + python-for-engineering/06_pandas/README.md | 143 +++ python-for-engineering/06_pandas/main.py | 20 + .../06_pandas/modulo_0_fundamentos_poo.py | 208 ++++ .../06_pandas/modulo_1_series.py | 268 +++++ .../06_pandas/modulo_2_dataframe.py | 299 +++++ .../06_pandas/modulo_3_index.py | 307 +++++ .../06_pandas/modulo_3b_sintaxis_indices.py | 607 ++++++++++ .../06_pandas/modulo_4_encadenamiento.py | 351 ++++++ .../06_pandas/modulo_5_herencia.py | 307 +++++ .../06_pandas/modulo_6_metodos_magicos.py | 425 +++++++ .../06_pandas/requirements.txt | 3 + ...3\263n a la regresi\303\263n lineal.ipynb" | 0 .../2 Regresion Minimos Cuadrados.ipynb | 0 .../3 Regresion lineal con SciPy.ipynb | 0 .../Images/curve1.jpg | Bin .../Images/curve2.jpg | Bin .../data.csv | 0 python-for-engineering/topics.txt | 0 86 files changed, 5739 insertions(+) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/1 Introduction.ipynb (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/2 Lectura de archivos.ipynb (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/3 Escritura de archivos.ipynb (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/4 Manejo de archivos CSV.ipynb (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/5 Introduccion a Pandas y DataFrames.ipynb (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Challenge/info.csv (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Challenge/system.txt (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Clases/1 Lectura de archivos.ipynb (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Clases/2 Escritura de archivos.ipynb (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Clases/archivo.txt (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Clases/lines.txt (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Clases/notas.txt (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Clases/output.txt (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/Workspace.ipynb (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/a.py (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/data.csv (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/df_example.csv (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/images/Notas.jpg (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/images/data.jpg (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/images/outputcsv.jpg (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/images/resultadoscsv.jpg (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/notas.txt (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/output.csv (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/reporte.txt (100%) rename python-for-engineering/{1 Manejo de Archivos en Python => 01_file_management}/resporte.txt (100%) rename python-for-engineering/{2 Consumo de APIs en python => 02_api_consumption}/1 Introduccion a APIs.ipynb (100%) rename python-for-engineering/{2 Consumo de APIs en python => 02_api_consumption}/2 Realizacion de solicitudes HTTP.ipynb (100%) rename python-for-engineering/{2 Consumo de APIs en python => 02_api_consumption}/3 Procesamiento de datos JSON en respuestas de API.ipynb (100%) rename python-for-engineering/{2 Consumo de APIs en python => 02_api_consumption}/4 Practica en consumo de APIs.ipynb (100%) rename python-for-engineering/{2 Consumo de APIs en python => 02_api_consumption}/comic_python.png (100%) rename python-for-engineering/{2 Consumo de APIs en python => 02_api_consumption}/images/GoogleMapApi.jpg (100%) rename python-for-engineering/{2 Consumo de APIs en python => 02_api_consumption}/images/openaiapi.png (100%) rename python-for-engineering/{3 Base de datos => 03_databases}/1 Introduccion a Base de Datos.ipynb (100%) rename python-for-engineering/{3 Base de datos => 03_databases}/2 SQLite en Python.ipynb (100%) rename python-for-engineering/{3 Base de datos => 03_databases}/4 Operaciones SQL basicas SELECT.ipynb (100%) rename python-for-engineering/{3 Base de datos => 03_databases}/5 Operaciones SQL basicas INSERT.ipynb (100%) rename python-for-engineering/{3 Base de datos => 03_databases}/Popular_Spotify_Songs.csv (100%) rename python-for-engineering/{3 Base de datos => 03_databases}/employee.db (100%) rename python-for-engineering/{3 Base de datos => 03_databases}/spotifydb (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/1 Trabajando con datos.ipynb (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/2 Caracterizando conjunto de datos.ipynb (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/3 Estadisticas de muestra Tendencia central y Dispersion.ipynb (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/4 Uso de diagramas de caja .ipynb (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/5 Distribuciones.ipynb (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/6 Algunas distribuciones importantes .ipynb (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/7 Numeros aleatorios.ipynb (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/Media_muestra.jpg (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/densidad.jpg (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/frecuencia.jpg (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/frecuencia_relativa.jpg (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/mad.jpg (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/normal.jpg (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/uniforme.jpg (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/varianza.jpg (100%) rename python-for-engineering/{4 Statistics, working with data => 04_statistics}/Images/weibull.jpg (100%) rename python-for-engineering/{5 Fundamentos de Algebra Lineal en Python => 05_linear_algebra}/1 Introduccion al Algebra Lineal y Numpy.ipynb (100%) rename python-for-engineering/{5 Fundamentos de Algebra Lineal en Python => 05_linear_algebra}/2 Vectores y Matrices.ipynb (100%) rename python-for-engineering/{5 Fundamentos de Algebra Lineal en Python => 05_linear_algebra}/3 Modulo linalg de NumPy.ipynb (100%) rename python-for-engineering/{5 Fundamentos de Algebra Lineal en Python => 05_linear_algebra}/4 Determinante e Inversa de una matriz.ipynb (100%) create mode 100644 python-for-engineering/06_pandas/INDICE_CURSO.md create mode 100644 python-for-engineering/06_pandas/Modulo_0_Fundamentos_POO.ipynb create mode 100644 python-for-engineering/06_pandas/Modulo_1_Series.ipynb create mode 100644 python-for-engineering/06_pandas/Modulo_2_DataFrame.ipynb create mode 100644 python-for-engineering/06_pandas/Modulo_3B_Sintaxis_Indices.ipynb create mode 100644 python-for-engineering/06_pandas/Modulo_3_Index.ipynb create mode 100644 python-for-engineering/06_pandas/Modulo_4_Encadenamiento.ipynb create mode 100644 python-for-engineering/06_pandas/Modulo_5_Herencia.ipynb create mode 100644 python-for-engineering/06_pandas/Modulo_6_Metodos_Magicos.ipynb create mode 100644 python-for-engineering/06_pandas/README.md create mode 100644 python-for-engineering/06_pandas/main.py create mode 100644 python-for-engineering/06_pandas/modulo_0_fundamentos_poo.py create mode 100644 python-for-engineering/06_pandas/modulo_1_series.py create mode 100644 python-for-engineering/06_pandas/modulo_2_dataframe.py create mode 100644 python-for-engineering/06_pandas/modulo_3_index.py create mode 100644 python-for-engineering/06_pandas/modulo_3b_sintaxis_indices.py create mode 100644 python-for-engineering/06_pandas/modulo_4_encadenamiento.py create mode 100644 python-for-engineering/06_pandas/modulo_5_herencia.py create mode 100644 python-for-engineering/06_pandas/modulo_6_metodos_magicos.py create mode 100644 python-for-engineering/06_pandas/requirements.txt rename "python-for-engineering/7 Linear Regression/1 Introducci\303\263n a la regresi\303\263n lineal.ipynb" => "python-for-engineering/07_linear_regression/1 Introducci\303\263n a la regresi\303\263n lineal.ipynb" (100%) rename python-for-engineering/{7 Linear Regression => 07_linear_regression}/2 Regresion Minimos Cuadrados.ipynb (100%) rename python-for-engineering/{7 Linear Regression => 07_linear_regression}/3 Regresion lineal con SciPy.ipynb (100%) rename python-for-engineering/{7 Linear Regression => 07_linear_regression}/Images/curve1.jpg (100%) rename python-for-engineering/{7 Linear Regression => 07_linear_regression}/Images/curve2.jpg (100%) rename python-for-engineering/{7 Linear Regression => 07_linear_regression}/data.csv (100%) delete mode 100644 python-for-engineering/topics.txt diff --git a/python-for-engineering/1 Manejo de Archivos en Python/1 Introduction.ipynb b/python-for-engineering/01_file_management/1 Introduction.ipynb similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/1 Introduction.ipynb rename to python-for-engineering/01_file_management/1 Introduction.ipynb diff --git a/python-for-engineering/1 Manejo de Archivos en Python/2 Lectura de archivos.ipynb b/python-for-engineering/01_file_management/2 Lectura de archivos.ipynb similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/2 Lectura de archivos.ipynb rename to python-for-engineering/01_file_management/2 Lectura de archivos.ipynb diff --git a/python-for-engineering/1 Manejo de Archivos en Python/3 Escritura de archivos.ipynb b/python-for-engineering/01_file_management/3 Escritura de archivos.ipynb similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/3 Escritura de archivos.ipynb rename to python-for-engineering/01_file_management/3 Escritura de archivos.ipynb diff --git a/python-for-engineering/1 Manejo de Archivos en Python/4 Manejo de archivos CSV.ipynb b/python-for-engineering/01_file_management/4 Manejo de archivos CSV.ipynb similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/4 Manejo de archivos CSV.ipynb rename to python-for-engineering/01_file_management/4 Manejo de archivos CSV.ipynb diff --git a/python-for-engineering/1 Manejo de Archivos en Python/5 Introduccion a Pandas y DataFrames.ipynb b/python-for-engineering/01_file_management/5 Introduccion a Pandas y DataFrames.ipynb similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/5 Introduccion a Pandas y DataFrames.ipynb rename to python-for-engineering/01_file_management/5 Introduccion a Pandas y DataFrames.ipynb diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Challenge/info.csv b/python-for-engineering/01_file_management/Challenge/info.csv similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Challenge/info.csv rename to python-for-engineering/01_file_management/Challenge/info.csv diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Challenge/system.txt b/python-for-engineering/01_file_management/Challenge/system.txt similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Challenge/system.txt rename to python-for-engineering/01_file_management/Challenge/system.txt diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Clases/1 Lectura de archivos.ipynb b/python-for-engineering/01_file_management/Clases/1 Lectura de archivos.ipynb similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Clases/1 Lectura de archivos.ipynb rename to python-for-engineering/01_file_management/Clases/1 Lectura de archivos.ipynb diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Clases/2 Escritura de archivos.ipynb b/python-for-engineering/01_file_management/Clases/2 Escritura de archivos.ipynb similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Clases/2 Escritura de archivos.ipynb rename to python-for-engineering/01_file_management/Clases/2 Escritura de archivos.ipynb diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Clases/archivo.txt b/python-for-engineering/01_file_management/Clases/archivo.txt similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Clases/archivo.txt rename to python-for-engineering/01_file_management/Clases/archivo.txt diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Clases/lines.txt b/python-for-engineering/01_file_management/Clases/lines.txt similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Clases/lines.txt rename to python-for-engineering/01_file_management/Clases/lines.txt diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Clases/notas.txt b/python-for-engineering/01_file_management/Clases/notas.txt similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Clases/notas.txt rename to python-for-engineering/01_file_management/Clases/notas.txt diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Clases/output.txt b/python-for-engineering/01_file_management/Clases/output.txt similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Clases/output.txt rename to python-for-engineering/01_file_management/Clases/output.txt diff --git a/python-for-engineering/1 Manejo de Archivos en Python/Workspace.ipynb b/python-for-engineering/01_file_management/Workspace.ipynb similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/Workspace.ipynb rename to python-for-engineering/01_file_management/Workspace.ipynb diff --git a/python-for-engineering/1 Manejo de Archivos en Python/a.py b/python-for-engineering/01_file_management/a.py similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/a.py rename to python-for-engineering/01_file_management/a.py diff --git a/python-for-engineering/1 Manejo de Archivos en Python/data.csv b/python-for-engineering/01_file_management/data.csv similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/data.csv rename to python-for-engineering/01_file_management/data.csv diff --git a/python-for-engineering/1 Manejo de Archivos en Python/df_example.csv b/python-for-engineering/01_file_management/df_example.csv similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/df_example.csv rename to python-for-engineering/01_file_management/df_example.csv diff --git a/python-for-engineering/1 Manejo de Archivos en Python/images/Notas.jpg b/python-for-engineering/01_file_management/images/Notas.jpg similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/images/Notas.jpg rename to python-for-engineering/01_file_management/images/Notas.jpg diff --git a/python-for-engineering/1 Manejo de Archivos en Python/images/data.jpg b/python-for-engineering/01_file_management/images/data.jpg similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/images/data.jpg rename to python-for-engineering/01_file_management/images/data.jpg diff --git a/python-for-engineering/1 Manejo de Archivos en Python/images/outputcsv.jpg b/python-for-engineering/01_file_management/images/outputcsv.jpg similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/images/outputcsv.jpg rename to python-for-engineering/01_file_management/images/outputcsv.jpg diff --git a/python-for-engineering/1 Manejo de Archivos en Python/images/resultadoscsv.jpg b/python-for-engineering/01_file_management/images/resultadoscsv.jpg similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/images/resultadoscsv.jpg rename to python-for-engineering/01_file_management/images/resultadoscsv.jpg diff --git a/python-for-engineering/1 Manejo de Archivos en Python/notas.txt b/python-for-engineering/01_file_management/notas.txt similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/notas.txt rename to python-for-engineering/01_file_management/notas.txt diff --git a/python-for-engineering/1 Manejo de Archivos en Python/output.csv b/python-for-engineering/01_file_management/output.csv similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/output.csv rename to python-for-engineering/01_file_management/output.csv diff --git a/python-for-engineering/1 Manejo de Archivos en Python/reporte.txt b/python-for-engineering/01_file_management/reporte.txt similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/reporte.txt rename to python-for-engineering/01_file_management/reporte.txt diff --git a/python-for-engineering/1 Manejo de Archivos en Python/resporte.txt b/python-for-engineering/01_file_management/resporte.txt similarity index 100% rename from python-for-engineering/1 Manejo de Archivos en Python/resporte.txt rename to python-for-engineering/01_file_management/resporte.txt diff --git a/python-for-engineering/2 Consumo de APIs en python/1 Introduccion a APIs.ipynb b/python-for-engineering/02_api_consumption/1 Introduccion a APIs.ipynb similarity index 100% rename from python-for-engineering/2 Consumo de APIs en python/1 Introduccion a APIs.ipynb rename to python-for-engineering/02_api_consumption/1 Introduccion a APIs.ipynb diff --git a/python-for-engineering/2 Consumo de APIs en python/2 Realizacion de solicitudes HTTP.ipynb b/python-for-engineering/02_api_consumption/2 Realizacion de solicitudes HTTP.ipynb similarity index 100% rename from python-for-engineering/2 Consumo de APIs en python/2 Realizacion de solicitudes HTTP.ipynb rename to python-for-engineering/02_api_consumption/2 Realizacion de solicitudes HTTP.ipynb diff --git a/python-for-engineering/2 Consumo de APIs en python/3 Procesamiento de datos JSON en respuestas de API.ipynb b/python-for-engineering/02_api_consumption/3 Procesamiento de datos JSON en respuestas de API.ipynb similarity index 100% rename from python-for-engineering/2 Consumo de APIs en python/3 Procesamiento de datos JSON en respuestas de API.ipynb rename to python-for-engineering/02_api_consumption/3 Procesamiento de datos JSON en respuestas de API.ipynb diff --git a/python-for-engineering/2 Consumo de APIs en python/4 Practica en consumo de APIs.ipynb b/python-for-engineering/02_api_consumption/4 Practica en consumo de APIs.ipynb similarity index 100% rename from python-for-engineering/2 Consumo de APIs en python/4 Practica en consumo de APIs.ipynb rename to python-for-engineering/02_api_consumption/4 Practica en consumo de APIs.ipynb diff --git a/python-for-engineering/2 Consumo de APIs en python/comic_python.png b/python-for-engineering/02_api_consumption/comic_python.png similarity index 100% rename from python-for-engineering/2 Consumo de APIs en python/comic_python.png rename to python-for-engineering/02_api_consumption/comic_python.png diff --git a/python-for-engineering/2 Consumo de APIs en python/images/GoogleMapApi.jpg b/python-for-engineering/02_api_consumption/images/GoogleMapApi.jpg similarity index 100% rename from python-for-engineering/2 Consumo de APIs en python/images/GoogleMapApi.jpg rename to python-for-engineering/02_api_consumption/images/GoogleMapApi.jpg diff --git a/python-for-engineering/2 Consumo de APIs en python/images/openaiapi.png b/python-for-engineering/02_api_consumption/images/openaiapi.png similarity index 100% rename from python-for-engineering/2 Consumo de APIs en python/images/openaiapi.png rename to python-for-engineering/02_api_consumption/images/openaiapi.png diff --git a/python-for-engineering/3 Base de datos/1 Introduccion a Base de Datos.ipynb b/python-for-engineering/03_databases/1 Introduccion a Base de Datos.ipynb similarity index 100% rename from python-for-engineering/3 Base de datos/1 Introduccion a Base de Datos.ipynb rename to python-for-engineering/03_databases/1 Introduccion a Base de Datos.ipynb diff --git a/python-for-engineering/3 Base de datos/2 SQLite en Python.ipynb b/python-for-engineering/03_databases/2 SQLite en Python.ipynb similarity index 100% rename from python-for-engineering/3 Base de datos/2 SQLite en Python.ipynb rename to python-for-engineering/03_databases/2 SQLite en Python.ipynb diff --git a/python-for-engineering/3 Base de datos/4 Operaciones SQL basicas SELECT.ipynb b/python-for-engineering/03_databases/4 Operaciones SQL basicas SELECT.ipynb similarity index 100% rename from python-for-engineering/3 Base de datos/4 Operaciones SQL basicas SELECT.ipynb rename to python-for-engineering/03_databases/4 Operaciones SQL basicas SELECT.ipynb diff --git a/python-for-engineering/3 Base de datos/5 Operaciones SQL basicas INSERT.ipynb b/python-for-engineering/03_databases/5 Operaciones SQL basicas INSERT.ipynb similarity index 100% rename from python-for-engineering/3 Base de datos/5 Operaciones SQL basicas INSERT.ipynb rename to python-for-engineering/03_databases/5 Operaciones SQL basicas INSERT.ipynb diff --git a/python-for-engineering/3 Base de datos/Popular_Spotify_Songs.csv b/python-for-engineering/03_databases/Popular_Spotify_Songs.csv similarity index 100% rename from python-for-engineering/3 Base de datos/Popular_Spotify_Songs.csv rename to python-for-engineering/03_databases/Popular_Spotify_Songs.csv diff --git a/python-for-engineering/3 Base de datos/employee.db b/python-for-engineering/03_databases/employee.db similarity index 100% rename from python-for-engineering/3 Base de datos/employee.db rename to python-for-engineering/03_databases/employee.db diff --git a/python-for-engineering/3 Base de datos/spotifydb b/python-for-engineering/03_databases/spotifydb similarity index 100% rename from python-for-engineering/3 Base de datos/spotifydb rename to python-for-engineering/03_databases/spotifydb diff --git a/python-for-engineering/4 Statistics, working with data/1 Trabajando con datos.ipynb b/python-for-engineering/04_statistics/1 Trabajando con datos.ipynb similarity index 100% rename from python-for-engineering/4 Statistics, working with data/1 Trabajando con datos.ipynb rename to python-for-engineering/04_statistics/1 Trabajando con datos.ipynb diff --git a/python-for-engineering/4 Statistics, working with data/2 Caracterizando conjunto de datos.ipynb b/python-for-engineering/04_statistics/2 Caracterizando conjunto de datos.ipynb similarity index 100% rename from python-for-engineering/4 Statistics, working with data/2 Caracterizando conjunto de datos.ipynb rename to python-for-engineering/04_statistics/2 Caracterizando conjunto de datos.ipynb diff --git a/python-for-engineering/4 Statistics, working with data/3 Estadisticas de muestra Tendencia central y Dispersion.ipynb b/python-for-engineering/04_statistics/3 Estadisticas de muestra Tendencia central y Dispersion.ipynb similarity index 100% rename from python-for-engineering/4 Statistics, working with data/3 Estadisticas de muestra Tendencia central y Dispersion.ipynb rename to python-for-engineering/04_statistics/3 Estadisticas de muestra Tendencia central y Dispersion.ipynb diff --git a/python-for-engineering/4 Statistics, working with data/4 Uso de diagramas de caja .ipynb b/python-for-engineering/04_statistics/4 Uso de diagramas de caja .ipynb similarity index 100% rename from python-for-engineering/4 Statistics, working with data/4 Uso de diagramas de caja .ipynb rename to python-for-engineering/04_statistics/4 Uso de diagramas de caja .ipynb diff --git a/python-for-engineering/4 Statistics, working with data/5 Distribuciones.ipynb b/python-for-engineering/04_statistics/5 Distribuciones.ipynb similarity index 100% rename from python-for-engineering/4 Statistics, working with data/5 Distribuciones.ipynb rename to python-for-engineering/04_statistics/5 Distribuciones.ipynb diff --git a/python-for-engineering/4 Statistics, working with data/6 Algunas distribuciones importantes .ipynb b/python-for-engineering/04_statistics/6 Algunas distribuciones importantes .ipynb similarity index 100% rename from python-for-engineering/4 Statistics, working with data/6 Algunas distribuciones importantes .ipynb rename to python-for-engineering/04_statistics/6 Algunas distribuciones importantes .ipynb diff --git a/python-for-engineering/4 Statistics, working with data/7 Numeros aleatorios.ipynb b/python-for-engineering/04_statistics/7 Numeros aleatorios.ipynb similarity index 100% rename from python-for-engineering/4 Statistics, working with data/7 Numeros aleatorios.ipynb rename to python-for-engineering/04_statistics/7 Numeros aleatorios.ipynb diff --git a/python-for-engineering/4 Statistics, working with data/Images/Media_muestra.jpg b/python-for-engineering/04_statistics/Images/Media_muestra.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/Media_muestra.jpg rename to python-for-engineering/04_statistics/Images/Media_muestra.jpg diff --git a/python-for-engineering/4 Statistics, working with data/Images/densidad.jpg b/python-for-engineering/04_statistics/Images/densidad.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/densidad.jpg rename to python-for-engineering/04_statistics/Images/densidad.jpg diff --git a/python-for-engineering/4 Statistics, working with data/Images/frecuencia.jpg b/python-for-engineering/04_statistics/Images/frecuencia.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/frecuencia.jpg rename to python-for-engineering/04_statistics/Images/frecuencia.jpg diff --git a/python-for-engineering/4 Statistics, working with data/Images/frecuencia_relativa.jpg b/python-for-engineering/04_statistics/Images/frecuencia_relativa.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/frecuencia_relativa.jpg rename to python-for-engineering/04_statistics/Images/frecuencia_relativa.jpg diff --git a/python-for-engineering/4 Statistics, working with data/Images/mad.jpg b/python-for-engineering/04_statistics/Images/mad.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/mad.jpg rename to python-for-engineering/04_statistics/Images/mad.jpg diff --git a/python-for-engineering/4 Statistics, working with data/Images/normal.jpg b/python-for-engineering/04_statistics/Images/normal.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/normal.jpg rename to python-for-engineering/04_statistics/Images/normal.jpg diff --git a/python-for-engineering/4 Statistics, working with data/Images/uniforme.jpg b/python-for-engineering/04_statistics/Images/uniforme.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/uniforme.jpg rename to python-for-engineering/04_statistics/Images/uniforme.jpg diff --git a/python-for-engineering/4 Statistics, working with data/Images/varianza.jpg b/python-for-engineering/04_statistics/Images/varianza.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/varianza.jpg rename to python-for-engineering/04_statistics/Images/varianza.jpg diff --git a/python-for-engineering/4 Statistics, working with data/Images/weibull.jpg b/python-for-engineering/04_statistics/Images/weibull.jpg similarity index 100% rename from python-for-engineering/4 Statistics, working with data/Images/weibull.jpg rename to python-for-engineering/04_statistics/Images/weibull.jpg diff --git a/python-for-engineering/5 Fundamentos de Algebra Lineal en Python/1 Introduccion al Algebra Lineal y Numpy.ipynb b/python-for-engineering/05_linear_algebra/1 Introduccion al Algebra Lineal y Numpy.ipynb similarity index 100% rename from python-for-engineering/5 Fundamentos de Algebra Lineal en Python/1 Introduccion al Algebra Lineal y Numpy.ipynb rename to python-for-engineering/05_linear_algebra/1 Introduccion al Algebra Lineal y Numpy.ipynb diff --git a/python-for-engineering/5 Fundamentos de Algebra Lineal en Python/2 Vectores y Matrices.ipynb b/python-for-engineering/05_linear_algebra/2 Vectores y Matrices.ipynb similarity index 100% rename from python-for-engineering/5 Fundamentos de Algebra Lineal en Python/2 Vectores y Matrices.ipynb rename to python-for-engineering/05_linear_algebra/2 Vectores y Matrices.ipynb diff --git a/python-for-engineering/5 Fundamentos de Algebra Lineal en Python/3 Modulo linalg de NumPy.ipynb b/python-for-engineering/05_linear_algebra/3 Modulo linalg de NumPy.ipynb similarity index 100% rename from python-for-engineering/5 Fundamentos de Algebra Lineal en Python/3 Modulo linalg de NumPy.ipynb rename to python-for-engineering/05_linear_algebra/3 Modulo linalg de NumPy.ipynb diff --git a/python-for-engineering/5 Fundamentos de Algebra Lineal en Python/4 Determinante e Inversa de una matriz.ipynb b/python-for-engineering/05_linear_algebra/4 Determinante e Inversa de una matriz.ipynb similarity index 100% rename from python-for-engineering/5 Fundamentos de Algebra Lineal en Python/4 Determinante e Inversa de una matriz.ipynb rename to python-for-engineering/05_linear_algebra/4 Determinante e Inversa de una matriz.ipynb diff --git a/python-for-engineering/06_pandas/INDICE_CURSO.md b/python-for-engineering/06_pandas/INDICE_CURSO.md new file mode 100644 index 0000000..ef2b93a --- /dev/null +++ b/python-for-engineering/06_pandas/INDICE_CURSO.md @@ -0,0 +1,252 @@ +# Curso: Pandas desde la Perspectiva de POO + +## 📋 Índice General del Curso + +Este curso enseña Pandas desde una perspectiva de Programación Orientada a Objetos, permitiendo entender no solo cómo usar Pandas, sino cómo está construido y diseñado. + +--- + +## 📚 Módulos + +### **Módulo 0: Fundamentos de POO aplicados a Pandas** +**Archivos:** `Modulo_0_Fundamentos_POO.ipynb`, `modulo_0_fundamentos_poo.py` + +**Contenido:** +- Pandas como biblioteca orientada a objetos +- Jerarquía de herencia (NDFrame) +- Clases, instancias, atributos y métodos +- Composición: DataFrame contiene Series +- Herramientas de exploración: `type()`, `isinstance()`, `dir()` + +**Conceptos POO:** +- Clases e instancias +- Atributos y métodos +- Herencia +- Composición +- Encapsulamiento + +--- + +### **Módulo 1: Series - El Objeto Fundamental** +**Archivos:** `Modulo_1_Series.ipynb`, `modulo_1_series.py` + +**Contenido:** +- Anatomía completa de la clase Series +- Atributos: `values`, `index`, `dtype`, `name`, `shape`, `size` +- Constructores: crear Series de múltiples formas +- Propiedades mutables vs inmutables +- Métodos de acceso: `iloc`, `loc`, `at`, `iat` +- Métodos estadísticos y de transformación +- Encapsulamiento: atributos públicos vs privados + +**Conceptos POO:** +- Constructores y sobrecarga +- Propiedades (properties) +- Métodos de instancia +- Inmutabilidad +- Encapsulamiento + +--- + +### **Módulo 2: DataFrame - Colección de Series** +**Archivos:** `Modulo_2_DataFrame.ipynb`, `modulo_2_dataframe.py` + +**Contenido:** +- DataFrame como composición de Series +- Cada columna es un objeto Series +- Constructores: crear DataFrames de múltiples formas +- Atributos: `values`, `index`, `columns`, `dtypes`, `shape` +- Métodos de acceso 2D: `loc`, `iloc`, `at`, `iat` +- Métodos de agregación: `sum()`, `mean()`, etc. +- Relación bidireccional Series ↔ DataFrame + +**Conceptos POO:** +- Composición y agregación +- Relaciones entre objetos +- Interfaces de acceso + +--- + +### **Módulo 3: Index - La Columna Vertebral** +**Archivos:** `Modulo_3_Index.ipynb`, `modulo_3_index.py` + +**Contenido:** +- Index como clase inmutable +- Jerarquía de subclases: RangeIndex, DatetimeIndex, MultiIndex +- Inmutabilidad: no se puede modificar +- Operaciones de conjuntos: union, intersection, difference +- Index en Series y DataFrame +- Métodos útiles: `get_loc()`, `insert()`, `delete()` + +**Conceptos POO:** +- Inmutabilidad +- Herencia y jerarquía de clases +- Polimorfismo (diferentes tipos de Index) +- Optimización (RangeIndex) + +--- + +### **Módulo 3B: Sintaxis de Índices - Guía Práctica** ⭐ NUEVO +**Archivos:** `Modulo_3B_Sintaxis_Indices.ipynb`, `modulo_3b_sintaxis_indices.py` + +**Contenido:** +- Sintaxis completa de acceso con índices en Series +- Acceso bidimensional en DataFrames +- Diferencias entre `[]`, `.loc[]`, `.iloc[]`, `.at[]`, `.iat[]` +- Filtrado booleano con índices +- Modificación de datos usando índices +- MultiIndex en profundidad +- Ejemplos prácticos y comparaciones directas +- Errores comunes y cómo evitarlos + +**Características Especiales:** +- Guía de referencia rápida +- Tabla comparativa de métodos +- Buenas prácticas y consejos pro +- Ejemplos interactivos ejecutables + +**Conceptos Prácticos:** +- Sintaxis de acceso +- Slicing (incluir vs excluir final) +- Operadores booleanos (&, |, ~) +- Optimización de acceso + +--- + +### **Módulo 4: Métodos y Encadenamiento** +**Archivos:** `Modulo_4_Encadenamiento.ipynb`, `modulo_4_encadenamiento.py` + +**Contenido:** +- Patrón Fluent Interface +- Method chaining (encadenamiento de métodos) +- Métodos que retornan copia vs self +- `inplace=True` vs copias +- `copy()` vs referencias +- Métodos especiales: `pipe()`, `assign()`, `query()` +- Pipeline completo de análisis + +**Conceptos POO:** +- Fluent Interface +- Retorno de self +- Gestión de memoria +- Programación funcional + +--- + +### **Módulo 5: Herencia y Polimorfismo en Pandas** +**Archivos:** `Modulo_5_Herencia.ipynb`, `modulo_5_herencia.py` + +**Contenido:** +- Jerarquía: NDFrame → Series/DataFrame +- Métodos compartidos por herencia +- Polimorfismo: mismo método, comportamiento diferente +- Duck typing en Pandas +- IndexOpsMixin y otras clases base +- Crear subclases personalizadas +- Herencia múltiple en Pandas + +**Conceptos POO:** +- Herencia +- Polimorfismo +- Duck typing +- Clases base abstractas +- Herencia múltiple +- Extensión de clases + +--- + +### **Módulo 6: Atributos Especiales y Métodos Mágicos** +**Archivos:** `Modulo_6_Metodos_Magicos.ipynb`, `modulo_6_metodos_magicos.py` + +**Contenido:** +- Métodos mágicos (dunder methods) +- `__getitem__`, `__setitem__`: operador `[]` +- `__len__`: función `len()` +- `__repr__`, `__str__`: representación +- `__iter__`: iteración con `for` +- Operadores aritméticos: `__add__`, `__sub__`, `__mul__`, etc. +- Operadores de comparación: `__eq__`, `__gt__`, `__lt__`, etc. +- `__contains__`: operador `in` + +**Conceptos POO:** +- Métodos especiales +- Sobrecarga de operadores +- Protocolos de Python +- Interfaces implícitas + +--- + +## 🎯 Objetivos de Aprendizaje General + +Al completar este curso, serás capaz de: + +1. **Entender Pandas desde adentro**: No solo usar, sino comprender cómo funciona +2. **Aplicar conceptos de POO**: Usar Pandas como caso de estudio de buen diseño +3. **Leer código fuente**: Capacidad para explorar el código de Pandas +4. **Crear extensiones**: Desarrollar tus propias subclases y extensiones +5. **Pensar como programador**: No solo como analista de datos + +--- + +## 🛠️ Estructura de Archivos + +Cada módulo incluye: +- **Notebook (`.ipynb`)**: Para ejecución interactiva y experimentación +- **Archivo Python (`.py`)**: Funciones organizadas que se ejecutan desde el notebook + +Esta estructura permite: +- Ejecutar todo el módulo con `%run modulo_X.py` +- Usar funciones individuales según necesidad +- Mantener código organizado y reutilizable + +--- + +## 📖 Cómo Usar Este Curso + +### Opción 1: Secuencial +Estudia los módulos en orden (0 → 1 → 2 → 3 → 4 → 5 → 6) + +### Opción 2: Por Tema +Si ya conoces Pandas: +- **POO Básico**: Módulos 0, 1 +- **Estructuras**: Módulos 2, 3 +- **Patrones avanzados**: Módulos 4, 5, 6 + +### Opción 3: Referencia +Usa los archivos `.py` como referencia rápida de conceptos + +--- + +## 🔧 Requisitos + +- Python 3.8+ +- Pandas 2.0+ +- NumPy +- Entorno virtual configurado en `.env/` + +Ver `requirements.txt` para versiones específicas. + +--- + +## 💡 Filosofía del Curso + +Este curso es diferente porque: +- **No es solo tutorial**: Es educación en diseño de software +- **Profundidad sobre amplitud**: Mejor entender bien que saber superficialmente +- **POO práctica**: Conceptos abstractos con ejemplos concretos +- **Pensamiento crítico**: No solo "qué" sino "por qué" y "cómo" + +--- + +## 📈 Próximos Pasos + +Después de completar este curso: +1. Explora el código fuente de Pandas en GitHub +2. Crea tus propias extensiones de Pandas +3. Contribuye a proyectos open source +4. Aplica estos patrones en tu propio código + +--- + +**¡Feliz aprendizaje! 🚀** + diff --git a/python-for-engineering/06_pandas/Modulo_0_Fundamentos_POO.ipynb b/python-for-engineering/06_pandas/Modulo_0_Fundamentos_POO.ipynb new file mode 100644 index 0000000..0834cd2 --- /dev/null +++ b/python-for-engineering/06_pandas/Modulo_0_Fundamentos_POO.ipynb @@ -0,0 +1,595 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Módulo 0: Fundamentos de POO aplicados a Pandas\n", + "\n", + "## 🎯 Objetivos de Aprendizaje\n", + "\n", + "En este módulo aprenderás:\n", + "- Comprender Pandas como una biblioteca orientada a objetos\n", + "- Identificar las clases principales de Pandas\n", + "- Entender la jerarquía de herencia en Pandas\n", + "- Reconocer atributos y métodos en objetos de Pandas\n", + "- Explorar la composición de objetos en Pandas\n", + "\n", + "---\n", + "\n", + "## 📚 Introducción\n", + "\n", + "**Pandas** no es solo una herramienta para análisis de datos; es un excelente ejemplo de diseño de software orientado a objetos. Cada estructura de datos en Pandas (Series, DataFrame, Index) es una **clase** con:\n", + "\n", + "- **Atributos**: propiedades que describen el objeto\n", + "- **Métodos**: funcionalidades que el objeto puede ejecutar\n", + "- **Herencia**: relaciones entre clases\n", + "- **Encapsulamiento**: control de acceso a datos internos\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pandas versión: 2.3.3\n" + ] + } + ], + "source": [ + "# Importar bibliotecas necesarias\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "# Importar funciones auxiliares del módulo\n", + "from modulo_0_fundamentos_poo import (\n", + " explorar_objeto, \n", + " mostrar_atributos_publicos,\n", + " mostrar_metodos_especiales\n", + ")\n", + "\n", + "print(f\"Pandas versión: {pd.__version__}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Todo en Pandas es un Objeto\n", + "\n", + "En Python, **todo es un objeto**. Cuando creamos una Serie o un DataFrame, estamos **instanciando** una clase.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tipo: \n", + "Clase: Series\n", + "Es una instancia de Series: True\n", + "\n", + "Contenido:\n", + "0 10\n", + "1 20\n", + "2 30\n", + "3 40\n", + "4 50\n", + "Name: ventas, dtype: int64\n" + ] + } + ], + "source": [ + "# Crear una Series - esto es INSTANCIAR la clase Series\n", + "mi_serie = pd.Series([10, 20, 30, 40, 50], name='ventas')\n", + "\n", + "# Verificar que es un objeto de la clase Series\n", + "print(f\"Tipo: {type(mi_serie)}\")\n", + "print(f\"Clase: {mi_serie.__class__.__name__}\")\n", + "print(f\"Es una instancia de Series: {isinstance(mi_serie, pd.Series)}\")\n", + "\n", + "print(\"\\nContenido:\")\n", + "print(mi_serie)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Explorando la Jerarquía de Herencia\n", + "\n", + "En POO, las clases pueden **heredar** de otras clases. Pandas usa herencia para compartir funcionalidad entre sus estructuras de datos.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jerarquía de herencia de Series:\n", + "--------------------------------------------------\n", + "0. \n", + "1. \n", + "2. \n", + "3. \n", + "4. \n", + "5. \n", + "6. \n", + "7. \n" + ] + } + ], + "source": [ + "# Explorar la jerarquía de herencia (MRO - Method Resolution Order)\n", + "print(\"Jerarquía de herencia de Series:\")\n", + "print(\"-\" * 50)\n", + "for i, clase in enumerate(pd.Series.__mro__):\n", + " print(f\"{i}. {clase}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 💡 Concepto Clave: NDFrame\n", + "\n", + "**NDFrame** es la clase base de la que heredan tanto `Series` como `DataFrame`. Proporciona métodos compartidos como:\n", + "- `head()`, `tail()`\n", + "- `describe()`\n", + "- `sum()`, `mean()`, `std()`\n", + "- `copy()`\n", + "- `info()`\n", + "\n", + "Esto es un ejemplo de **herencia** y **reutilización de código**.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Series hereda de NDFrame: True\n", + "DataFrame hereda de NDFrame: True\n", + "DataFrame hereda de Series: False\n", + "\n", + "Métodos compartidos (heredados de NDFrame):\n", + "mi_serie.head(): \n", + "mi_df.head(): \n" + ] + } + ], + "source": [ + "# Verificar herencia de NDFrame\n", + "print(f\"Series hereda de NDFrame: {isinstance(mi_serie, pd.core.generic.NDFrame)}\")\n", + "\n", + "# Crear un DataFrame\n", + "mi_df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})\n", + "print(f\"DataFrame hereda de NDFrame: {isinstance(mi_df, pd.core.generic.NDFrame)}\")\n", + "print(f\"DataFrame hereda de Series: {isinstance(mi_df, pd.Series)}\")\n", + "\n", + "# Ambos comparten métodos de NDFrame\n", + "print(\"\\nMétodos compartidos (heredados de NDFrame):\")\n", + "print(\"mi_serie.head():\", type(mi_serie.head()))\n", + "print(\"mi_df.head():\", type(mi_df.head()))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Atributos de Objetos\n", + "\n", + "Los **atributos** son propiedades que describen el estado de un objeto. En Pandas, cada estructura tiene atributos específicos.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Atributos importantes de Series:\n", + "--------------------------------------------------\n", + "values: [10 20 30 40 50]\n", + "index: RangeIndex(start=0, stop=5, step=1)\n", + "dtype: int64\n", + "name: ventas\n", + "shape: (5,)\n", + "size: 5\n", + "ndim: 1\n" + ] + } + ], + "source": [ + "# Atributos de una Series\n", + "print(\"Atributos importantes de Series:\")\n", + "print(\"-\" * 50)\n", + "print(f\"values: {mi_serie.values}\") # Array de valores\n", + "print(f\"index: {mi_serie.index}\") # Índice\n", + "print(f\"dtype: {mi_serie.dtype}\") # Tipo de datos\n", + "print(f\"name: {mi_serie.name}\") # Nombre de la serie\n", + "print(f\"shape: {mi_serie.shape}\") # Forma (dimensiones)\n", + "print(f\"size: {mi_serie.size}\") # Número de elementos\n", + "print(f\"ndim: {mi_serie.ndim}\") # Número de dimensiones\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Atributos importantes de DataFrame:\n", + "--------------------------------------------------\n", + "values:\n", + "[[1 4]\n", + " [2 5]\n", + " [3 6]]\n", + "\n", + "index: RangeIndex(start=0, stop=3, step=1)\n", + "columns: Index(['A', 'B'], dtype='object')\n", + "dtypes:\n", + "A int64\n", + "B int64\n", + "dtype: object\n", + "\n", + "shape: (3, 2)\n", + "size: 6\n", + "ndim: 2\n" + ] + } + ], + "source": [ + "# Atributos de un DataFrame\n", + "print(\"\\nAtributos importantes de DataFrame:\")\n", + "print(\"-\" * 50)\n", + "print(f\"values:\\n{mi_df.values}\") # Array 2D de valores\n", + "print(f\"\\nindex: {mi_df.index}\") # Índice de filas\n", + "print(f\"columns: {mi_df.columns}\") # Nombres de columnas\n", + "print(f\"dtypes:\\n{mi_df.dtypes}\") # Tipos de cada columna\n", + "print(f\"\\nshape: {mi_df.shape}\") # (filas, columnas)\n", + "print(f\"size: {mi_df.size}\") # Total de elementos\n", + "print(f\"ndim: {mi_df.ndim}\") # Número de dimensiones\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Métodos de Objetos\n", + "\n", + "Los **métodos** son funciones que pertenecen a un objeto y pueden realizar operaciones sobre él.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ejemplos de métodos de Series:\n", + "--------------------------------------------------\n", + "Suma: 150\n", + "Media: 30.0\n", + "Máximo: 50\n", + "Mínimo: 10\n", + "\n", + "Multiplicar por 2:\n", + "0 20\n", + "1 40\n", + "2 60\n", + "3 80\n", + "4 100\n", + "Name: ventas, dtype: int64\n", + "\n", + "Descripción estadística:\n", + "count 5.000000\n", + "mean 30.000000\n", + "std 15.811388\n", + "min 10.000000\n", + "25% 20.000000\n", + "50% 30.000000\n", + "75% 40.000000\n", + "max 50.000000\n", + "Name: ventas, dtype: float64\n" + ] + } + ], + "source": [ + "# Métodos de Series\n", + "print(\"Ejemplos de métodos de Series:\")\n", + "print(\"-\" * 50)\n", + "\n", + "# Métodos estadísticos\n", + "print(f\"Suma: {mi_serie.sum()}\")\n", + "print(f\"Media: {mi_serie.mean()}\")\n", + "print(f\"Máximo: {mi_serie.max()}\")\n", + "print(f\"Mínimo: {mi_serie.min()}\")\n", + "\n", + "# Métodos de transformación\n", + "print(f\"\\nMultiplicar por 2:\\n{mi_serie.mul(2)}\")\n", + "\n", + "# Métodos de información\n", + "print(f\"\\nDescripción estadística:\\n{mi_serie.describe()}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 💡 Concepto Clave: Métodos de Instancia\n", + "\n", + "Los métodos que hemos usado (`sum()`, `mean()`, `describe()`) son **métodos de instancia**: operan sobre una instancia específica del objeto.\n", + "\n", + "```python\n", + "mi_serie.sum() # suma los valores de ESTA instancia de Serie\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Composición: DataFrame como colección de Series\n", + "\n", + "Un concepto importante de POO es la **composición**: un objeto que contiene otros objetos.\n", + "\n", + "En Pandas:\n", + "- Un **DataFrame** está compuesto por múltiples **Series**\n", + "- Cada columna de un DataFrame es una Serie independiente\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataFrame completo:\n", + " nombre edad salario\n", + "0 Ana 25 30000\n", + "1 Luis 30 45000\n", + "2 María 28 38000\n", + "3 Carlos 35 52000\n", + "\n", + "Tipo del DataFrame: \n" + ] + } + ], + "source": [ + "# Crear un DataFrame\n", + "df = pd.DataFrame({\n", + " 'nombre': ['Ana', 'Luis', 'María', 'Carlos'],\n", + " 'edad': [25, 30, 28, 35],\n", + " 'salario': [30000, 45000, 38000, 52000]\n", + "})\n", + "\n", + "print(\"DataFrame completo:\")\n", + "print(df)\n", + "print(f\"\\nTipo del DataFrame: {type(df)}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Columna 'edad':\n", + "0 25\n", + "1 30\n", + "2 28\n", + "3 35\n", + "Name: edad, dtype: int64\n", + "\n", + "Tipo de la columna: \n", + "Es una Series: True\n", + "\n", + "Media de edad: 29.5\n", + "Nombre de la serie: edad\n" + ] + } + ], + "source": [ + "# Acceder a una columna - esto retorna una Series\n", + "columna_edad = df['edad']\n", + "\n", + "print(\"Columna 'edad':\")\n", + "print(columna_edad)\n", + "print(f\"\\nTipo de la columna: {type(columna_edad)}\")\n", + "print(f\"Es una Series: {isinstance(columna_edad, pd.Series)}\")\n", + "\n", + "# La columna tiene todos los atributos y métodos de Series\n", + "print(f\"\\nMedia de edad: {columna_edad.mean()}\")\n", + "print(f\"Nombre de la serie: {columna_edad.name}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Iterando por columnas del DataFrame:\n", + "--------------------------------------------------\n", + "nombre:\n", + " Tipo: Series\n", + " dtype: object\n", + " shape: (4,)\n", + "edad:\n", + " Tipo: Series\n", + " dtype: int64\n", + " shape: (4,)\n", + "salario:\n", + " Tipo: Series\n", + " dtype: int64\n", + " shape: (4,)\n" + ] + } + ], + "source": [ + "# Iterar por las columnas (cada una es una Serie)\n", + "print(\"\\nIterando por columnas del DataFrame:\")\n", + "print(\"-\" * 50)\n", + "for nombre_columna in df.columns:\n", + " columna = df[nombre_columna]\n", + " print(f\"{nombre_columna}:\")\n", + " print(f\" Tipo: {type(columna).__name__}\")\n", + " print(f\" dtype: {columna.dtype}\")\n", + " print(f\" shape: {columna.shape}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Explorando con dir() y help()\n", + "\n", + "Python proporciona funciones útiles para explorar objetos:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total de atributos y métodos: 421\n", + "\n", + "Primeros 10: ['T', '_AXIS_LEN', '_AXIS_ORDERS', '_AXIS_TO_AXIS_NUMBER', '_HANDLED_TYPES', '__abs__', '__add__', '__and__', '__annotations__', '__array__']\n", + "\n", + "Métodos públicos: 205\n", + "Algunos ejemplos: ['T', 'abs', 'add', 'add_prefix', 'add_suffix', 'agg', 'aggregate', 'align', 'all', 'any', 'apply', 'argmax', 'argmin', 'argsort', 'array']\n" + ] + } + ], + "source": [ + "# dir() lista todos los atributos y métodos\n", + "atributos = dir(mi_serie)\n", + "print(f\"Total de atributos y métodos: {len(atributos)}\")\n", + "print(f\"\\nPrimeros 10: {atributos[:10]}\")\n", + "\n", + "# Filtrar solo métodos públicos (sin _)\n", + "publicos = [attr for attr in atributos if not attr.startswith('_')]\n", + "print(f\"\\nMétodos públicos: {len(publicos)}\")\n", + "print(f\"Algunos ejemplos: {publicos[:15]}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Firma del método sum():\n", + "(axis: 'Axis | None' = None, skipna: 'bool' = True, numeric_only: 'bool' = False, min_count: 'int' = 0, **kwargs)\n" + ] + } + ], + "source": [ + "# help() muestra la documentación (comentar para no saturar la salida)\n", + "# Descomentar para ver la documentación completa\n", + "# help(mi_serie.sum)\n", + "\n", + "# Alternativa: ver solo la firma del método\n", + "import inspect\n", + "print(\"Firma del método sum():\")\n", + "print(inspect.signature(mi_serie.sum))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 📚 Resumen del Módulo 0\n", + "\n", + "### Conceptos clave aprendidos:\n", + "\n", + "1. **Pandas es POO**: Cada estructura (Series, DataFrame, Index) es una clase\n", + "2. **Herencia**: Series y DataFrame heredan de `NDFrame`\n", + "3. **Atributos**: Propiedades que describen el estado (`values`, `index`, `dtype`, etc.)\n", + "4. **Métodos**: Funciones que operan sobre el objeto (`sum()`, `mean()`, etc.)\n", + "5. **Composición**: DataFrame está compuesto por Series\n", + "6. **Exploración**: Usar `type()`, `isinstance()`, `dir()`, `help()` para entender objetos\n", + "\n", + "### Tabla de correspondencia POO ↔ Pandas:\n", + "\n", + "| Concepto POO | En Pandas |\n", + "|--------------|-----------|\n", + "| Clase | `pd.Series`, `pd.DataFrame`, `pd.Index` |\n", + "| Instancia | `mi_serie = pd.Series([1,2,3])` |\n", + "| Atributo | `mi_serie.values`, `mi_serie.dtype` |\n", + "| Método | `mi_serie.sum()`, `mi_serie.mean()` |\n", + "| Herencia | `Series` y `DataFrame` heredan de `NDFrame` |\n", + "| Composición | `DataFrame` contiene múltiples `Series` |\n", + "\n", + "---\n", + "\n", + "**Próximo módulo**: Profundizaremos en la clase **Series** como objeto fundamental de Pandas.\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/python-for-engineering/06_pandas/Modulo_1_Series.ipynb b/python-for-engineering/06_pandas/Modulo_1_Series.ipynb new file mode 100644 index 0000000..d1007f5 --- /dev/null +++ b/python-for-engineering/06_pandas/Modulo_1_Series.ipynb @@ -0,0 +1,99 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Módulo 1: Series - El Objeto Fundamental\n", + "\n", + "## 🎯 Objetivos de Aprendizaje\n", + "\n", + "En este módulo aprenderás:\n", + "- Anatomía completa de la clase Series\n", + "- Atributos de instancia y de clase\n", + "- Constructores y formas de instanciar Series\n", + "- Propiedades (properties) mutables e inmutables\n", + "- Métodos de acceso a datos\n", + "- Encapsulamiento en Series\n", + "\n", + "---\n", + "\n", + "## 📚 Introducción\n", + "\n", + "**Series** es la estructura de datos fundamental en Pandas. Es un array unidimensional etiquetado capaz de contener cualquier tipo de datos.\n", + "\n", + "Desde la perspectiva de POO:\n", + "- `Series` es una **clase**\n", + "- Cada serie que creamos es una **instancia** de esa clase\n", + "- Tiene **atributos** (datos) y **métodos** (comportamiento)\n", + "- Hereda de `NDFrame` y otras clases base\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'pandas'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Importar bibliotecas y funciones auxiliares\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpandas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpd\u001b[39;00m\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mnumpy\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mnp\u001b[39;00m\n\u001b[32m 4\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mmodulo_1_series\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m AnalizadorSeries\n", + "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'pandas'" + ] + } + ], + "source": [ + "# Importar bibliotecas y funciones auxiliares\n", + "import pandas as pd\n", + "import numpy as np\n", + "from modulo_1_series import AnalizadorSeries\n", + "\n", + "print(f\"Pandas versión: {pd.__version__}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Anatomía Completa de Series\n", + "\n", + "Usa el archivo .py para explorar Series en detalle con todas las funciones auxiliares creadas.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ejecutar el análisis completo desde el archivo .py\n", + "%run modulo_1_series.py\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/python-for-engineering/06_pandas/Modulo_2_DataFrame.ipynb b/python-for-engineering/06_pandas/Modulo_2_DataFrame.ipynb new file mode 100644 index 0000000..32ae84f --- /dev/null +++ b/python-for-engineering/06_pandas/Modulo_2_DataFrame.ipynb @@ -0,0 +1,353 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Módulo 2: DataFrame - Colección de Series\n", + "\n", + "## 🎯 Objetivos de Aprendizaje\n", + "\n", + "- Comprender DataFrame como composición de objetos Series\n", + "- Entender la relación de agregación entre DataFrame y Series \n", + "- Explorar constructores y atributos de DataFrame\n", + "- Dominar métodos de acceso bidimensional (loc, iloc)\n", + "- Aplicar conceptos de POO: composición y agregación\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "MÓDULO 2: DATAFRAME - COLECCIÓN DE SERIES\n", + "======================================================================\n", + "======================================================================\n", + "ANATOMÍA COMPLETA DEL DATAFRAME\n", + "======================================================================\n", + "\n", + "1. IDENTIDAD DEL OBJETO:\n", + " Tipo: \n", + " Clase: DataFrame\n", + " ID en memoria: 4643953296\n", + "\n", + "2. ATRIBUTOS DE DATOS:\n", + " values (array 2D):\n", + "[['Ana' 25 'Madrid']\n", + " ['Luis' 30 'Barcelona']\n", + " ['María' 28 'Valencia']]\n", + "\n", + " index (filas): RangeIndex(start=0, stop=3, step=1)\n", + " columns (columnas): Index(['nombre', 'edad', 'ciudad'], dtype='object')\n", + " dtypes:\n", + "nombre object\n", + "edad int64\n", + "ciudad object\n", + "dtype: object\n", + "\n", + "3. ATRIBUTOS DE FORMA:\n", + " shape: (3, 3)\n", + " size: 9\n", + " ndim: 2\n", + " empty: False\n", + "\n", + "4. COMPOSICIÓN (columnas como Series):\n", + " Número de columnas: 3\n", + " - 'nombre': tipo=Series, dtype=object\n", + " - 'edad': tipo=Series, dtype=int64\n", + " - 'ciudad': tipo=Series, dtype=object\n", + "\n", + "======================================================================\n", + "COMPOSICIÓN: DataFrame contiene Series\n", + "======================================================================\n", + "\n", + "DataFrame completo:\n", + " nombre edad salario ciudad\n", + "0 Ana 25 30000 Madrid\n", + "1 Luis 30 45000 Barcelona\n", + "2 María 28 38000 Valencia\n", + "3 Carlos 35 52000 Sevilla\n", + "\n", + "Tipo: \n", + "\n", + "----------------------------------------------------------------------\n", + "Accediendo a columna 'edad':\n", + "Tipo: \n", + "Es una Series: True\n", + "Contenido:\n", + "0 25\n", + "1 30\n", + "2 28\n", + "3 35\n", + "Name: edad, dtype: int64\n", + "\n", + "Métodos de Series disponibles:\n", + " mean(): 29.5\n", + " max(): 35\n", + " min(): 25\n", + "\n", + "----------------------------------------------------------------------\n", + "Verificando que todas las columnas son Series:\n", + " nombre: True\n", + " edad: True\n", + " salario: True\n", + " ciudad: True\n", + "\n", + "======================================================================\n", + "CONSTRUCTORES: Diferentes formas de crear DataFrame\n", + "======================================================================\n", + "\n", + "1. Desde diccionario de listas:\n", + " A B C\n", + "0 1 4 7\n", + "1 2 5 8\n", + "2 3 6 9\n", + "\n", + "2. Desde lista de diccionarios:\n", + " nombre edad\n", + "0 Ana 25\n", + "1 Luis 30\n", + "2 María 28\n", + "\n", + "3. Desde diccionario de Series:\n", + " ventas costos\n", + "0 100 50\n", + "1 200 80\n", + "2 300 120\n", + "\n", + "4. Desde array de NumPy:\n", + " A B C D\n", + "fila1 2.121568 0.147154 -1.151266 0.167151\n", + "fila2 -0.362657 -0.082068 0.713732 0.375783\n", + "fila3 0.999883 0.803299 0.490857 1.555068\n", + "\n", + "5. Desde otro DataFrame (copia):\n", + " A B C\n", + "0 1 4 7\n", + "1 2 5 8\n", + "2 3 6 9\n", + "¿Es el mismo objeto? False\n", + "¿Tienen mismos valores? True\n", + "\n", + "======================================================================\n", + "ATRIBUTOS PRINCIPALES DE DATAFRAME\n", + "======================================================================\n", + "\n", + "DataFrame:\n", + " producto precio stock\n", + "P1 A 10.50 100\n", + "P2 B 20.00 50\n", + "P3 C 15.75 75\n", + "P4 D 30.25 25\n", + "\n", + "📊 ATRIBUTOS DE DATOS:\n", + "values (array 2D):\n", + "[['A' 10.5 100]\n", + " ['B' 20.0 50]\n", + " ['C' 15.75 75]\n", + " ['D' 30.25 25]]\n", + "values.dtype: object\n", + "\n", + "🏷️ ATRIBUTOS DE ÍNDICES:\n", + "index: Index(['P1', 'P2', 'P3', 'P4'], dtype='object')\n", + "index.name: None\n", + "columns: Index(['producto', 'precio', 'stock'], dtype='object')\n", + "columns.name: None\n", + "axes: [Index(['P1', 'P2', 'P3', 'P4'], dtype='object'), Index(['producto', 'precio', 'stock'], dtype='object')]\n", + "\n", + "📏 ATRIBUTOS DE FORMA:\n", + "shape: (4, 3)\n", + "size: 12\n", + "ndim: 2\n", + "empty: False\n", + "\n", + "🔤 ATRIBUTOS DE TIPOS:\n", + "dtypes:\n", + "producto object\n", + "precio float64\n", + "stock int64\n", + "dtype: object\n", + "\n", + "======================================================================\n", + "MÉTODOS DE ACCESO A DATOS\n", + "======================================================================\n", + "DataFrame original:\n", + " A B C\n", + "fila1 1 5 9\n", + "fila2 2 6 10\n", + "fila3 3 7 11\n", + "fila4 4 8 12\n", + "\n", + "1. ACCESO A COLUMNAS:\n", + "df['A'] (retorna Series):\n", + "fila1 1\n", + "fila2 2\n", + "fila3 3\n", + "fila4 4\n", + "Name: A, dtype: int64\n", + "\n", + "df[['A', 'C']] (retorna DataFrame):\n", + " A C\n", + "fila1 1 9\n", + "fila2 2 10\n", + "fila3 3 11\n", + "fila4 4 12\n", + "\n", + "2. ACCESO CON loc (por etiqueta):\n", + "df.loc['fila1'] (retorna Series):\n", + "A 1\n", + "B 5\n", + "C 9\n", + "Name: fila1, dtype: int64\n", + "\n", + "df.loc['fila1', 'A']: 1\n", + "\n", + "df.loc['fila1':'fila3', 'A':'B']:\n", + " A B\n", + "fila1 1 5\n", + "fila2 2 6\n", + "fila3 3 7\n", + "\n", + "3. ACCESO CON iloc (por posición):\n", + "df.iloc[0] (retorna Series):\n", + "A 1\n", + "B 5\n", + "C 9\n", + "Name: fila1, dtype: int64\n", + "\n", + "df.iloc[0, 0]: 1\n", + "\n", + "df.iloc[0:2, 0:2]:\n", + " A B\n", + "fila1 1 5\n", + "fila2 2 6\n", + "\n", + "4. ACCESO CON at/iat (valores únicos):\n", + "df.at['fila1', 'A']: 1\n", + "df.iat[0, 0]: 1\n", + "\n", + "======================================================================\n", + "MÉTODOS DE AGREGACIÓN\n", + "======================================================================\n", + "DataFrame:\n", + " ventas costos ganancia\n", + "Q1 100 50 50\n", + "Q2 200 80 120\n", + "Q3 150 60 90\n", + "Q4 300 120 180\n", + "\n", + "1. Agregación por COLUMNAS (axis=0, default):\n", + "sum():\n", + "ventas 750\n", + "costos 310\n", + "ganancia 440\n", + "dtype: int64\n", + "\n", + "mean():\n", + "ventas 187.5\n", + "costos 77.5\n", + "ganancia 110.0\n", + "dtype: float64\n", + "\n", + "2. Agregación por FILAS (axis=1):\n", + "sum(axis=1):\n", + "Q1 200\n", + "Q2 400\n", + "Q3 300\n", + "Q4 600\n", + "dtype: int64\n", + "\n", + "mean(axis=1):\n", + "Q1 66.666667\n", + "Q2 133.333333\n", + "Q3 100.000000\n", + "Q4 200.000000\n", + "dtype: float64\n", + "\n", + "3. Describe (resumen estadístico):\n", + " ventas costos ganancia\n", + "count 4.000000 4.000000 4.000000\n", + "mean 187.500000 77.500000 110.000000\n", + "std 85.391256 30.956959 54.772256\n", + "min 100.000000 50.000000 50.000000\n", + "25% 137.500000 57.500000 80.000000\n", + "50% 175.000000 70.000000 105.000000\n", + "75% 225.000000 90.000000 135.000000\n", + "max 300.000000 120.000000 180.000000\n", + "\n", + "======================================================================\n", + "RELACIÓN SERIES ↔ DATAFRAME\n", + "======================================================================\n", + "\n", + "1. DataFrame → Series (acceso a columna):\n", + "DataFrame:\n", + " A B\n", + "0 1 4\n", + "1 2 5\n", + "2 3 6\n", + "\n", + "Columna 'A' (Series):\n", + "0 1\n", + "1 2\n", + "2 3\n", + "Name: A, dtype: int64\n", + "\n", + "2. Series → DataFrame (to_frame()):\n", + "DataFrame desde Series:\n", + " A\n", + "0 1\n", + "1 2\n", + "2 3\n", + "Tipo: \n", + "\n", + "3. Múltiples Series → DataFrame:\n", + " col1 col2 col3\n", + "0 1 4 7\n", + "1 2 5 8\n", + "2 3 6 9\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "# Ejecutar todo el módulo\n", + "%run modulo_2_dataframe.py\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/python-for-engineering/06_pandas/Modulo_3B_Sintaxis_Indices.ipynb b/python-for-engineering/06_pandas/Modulo_3B_Sintaxis_Indices.ipynb new file mode 100644 index 0000000..88a172b --- /dev/null +++ b/python-for-engineering/06_pandas/Modulo_3B_Sintaxis_Indices.ipynb @@ -0,0 +1,1063 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Módulo 3B: Sintaxis de Índices - Guía Práctica Completa\n", + "\n", + "## 🎯 Objetivos de Aprendizaje\n", + "\n", + "- Dominar la sintaxis de acceso con índices en Series\n", + "- Comprender el acceso bidimensional en DataFrames\n", + "- Diferenciar entre `.loc[]`, `.iloc[]`, `.at[]`, `.iat[]`\n", + "- Aplicar filtrado booleano con índices\n", + "- Modificar datos usando índices\n", + "- Trabajar con MultiIndex (índices jerárquicos)\n", + "\n", + "---\n", + "\n", + "## 📚 Introducción\n", + "\n", + "Este módulo es una **guía práctica y completa** sobre cómo usar índices en Pandas. Cubre todos los casos de uso comunes con ejemplos claros y comparaciones directas.\n", + "\n", + "### ¿Por qué este módulo es importante?\n", + "\n", + "Los índices son **la columna vertebral** de Pandas:\n", + "- Permiten acceso rápido y eficiente a datos\n", + "- Alinean automáticamente datos en operaciones\n", + "- Son esenciales para series temporales\n", + "- Facilitan agrupaciones y agregaciones\n", + "\n", + "---\n", + "\n", + "## 💡 Conceptos Clave\n", + "\n", + "**En Series:**\n", + "- Una Serie tiene UN índice (para identificar cada valor)\n", + "\n", + "**En DataFrames:**\n", + "- Un DataFrame tiene DOS índices:\n", + " - `df.index` → índice de FILAS\n", + " - `df.columns` → índice de COLUMNAS (sí, las columnas también son un Index!)\n", + "\n", + "---\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "MÓDULO 3B: SINTAXIS DE ÍNDICES - GUÍA PRÁCTICA\n", + "======================================================================\n", + "======================================================================\n", + "ÍNDICES EN PANDAS: CONCEPTOS FUNDAMENTALES\n", + "======================================================================\n", + "\n", + "Los ÍNDICES son etiquetas que permiten identificar y acceder a datos:\n", + "\n", + "EN SERIES:\n", + "- Una Serie tiene UN índice (para las filas)\n", + "- Por defecto: RangeIndex(0, 1, 2, 3, ...)\n", + "- Puede ser personalizado con cualquier valor\n", + "\n", + "EN DATAFRAMES:\n", + "- Un DataFrame tiene DOS índices:\n", + " 1. Index para FILAS (df.index)\n", + " 2. Index para COLUMNAS (df.columns)\n", + "- Ambos son objetos Index\n", + "\n", + "VENTAJAS DE LOS ÍNDICES:\n", + "✓ Acceso rápido por etiqueta\n", + "✓ Alineación automática en operaciones\n", + "✓ Agrupación y agregación eficiente\n", + "✓ Series temporales con fechas\n", + " \n", + "\n", + "======================================================================\n", + "SINTAXIS BÁSICA: ÍNDICES EN SERIES\n", + "======================================================================\n", + "\n", + "1. ÍNDICE POR DEFECTO (RangeIndex):\n", + "0 10\n", + "1 20\n", + "2 30\n", + "3 40\n", + "4 50\n", + "dtype: int64\n", + "\n", + "Tipo de índice: \n", + "Índice: RangeIndex(start=0, stop=5, step=1)\n", + "\n", + "2. ÍNDICE PERSONALIZADO:\n", + "a 10\n", + "b 20\n", + "c 30\n", + "d 40\n", + "e 50\n", + "dtype: int64\n", + "Índice: ['a', 'b', 'c', 'd', 'e']\n", + "\n", + "3. FORMAS DE ACCEDER A DATOS:\n", + " serie_custom['a'] = 10\n", + " serie_custom.loc['a'] = 10\n", + " serie_custom.iloc[0] = 10\n", + "\n", + "4. SLICING CON ÍNDICES:\n", + " serie_custom['a':'c'] (INCLUYE el final):\n", + "a 10\n", + "b 20\n", + "c 30\n", + "dtype: int64\n", + "\n", + " serie_custom.iloc[0:3] (EXCLUYE el final):\n", + "a 10\n", + "b 20\n", + "c 30\n", + "dtype: int64\n", + "\n", + "======================================================================\n", + "GUÍA COMPLETA: ACCESO A SERIES\n", + "======================================================================\n", + "Serie de ejemplo:\n", + "ene 100\n", + "feb 200\n", + "mar 300\n", + "abr 400\n", + "may 500\n", + "Name: ventas, dtype: int64\n", + "\n", + "----------------------------------------------------------------------\n", + "1. ACCESO DIRECTO CON [] (por etiqueta)\n", + "----------------------------------------------------------------------\n", + "serie['feb'] = 200\n", + "Tipo: \n", + "\n", + "2. ACCESO MÚLTIPLE:\n", + "serie[['ene', 'mar', 'may']]:\n", + "ene 100\n", + "mar 300\n", + "may 500\n", + "Name: ventas, dtype: int64\n", + "Tipo: \n", + "\n", + "3. SLICING CON ETIQUETAS (incluye ambos extremos):\n", + "serie['feb':'abr']:\n", + "feb 200\n", + "mar 300\n", + "abr 400\n", + "Name: ventas, dtype: int64\n", + "\n", + "4. .loc[] - ACCESO POR ETIQUETA (explícito):\n", + "serie.loc['feb'] = 200\n", + "serie.loc['feb':'abr']:\n", + "feb 200\n", + "mar 300\n", + "abr 400\n", + "Name: ventas, dtype: int64\n", + "\n", + "5. .iloc[] - ACCESO POR POSICIÓN (índice numérico):\n", + "serie.iloc[1] = 200\n", + "serie.iloc[1:4]:\n", + "feb 200\n", + "mar 300\n", + "abr 400\n", + "Name: ventas, dtype: int64\n", + "serie.iloc[-1] = 500\n", + "\n", + "6. .at[] / .iat[] - ACCESO RÁPIDO (solo un valor):\n", + "serie.at['feb'] = 200\n", + "serie.iat[1] = 200\n", + "💡 at/iat son ~2x más rápidos para un solo valor\n", + "\n", + "======================================================================\n", + "SINTAXIS BÁSICA: ÍNDICES EN DATAFRAMES\n", + "======================================================================\n", + "\n", + "1. DATAFRAME CON ÍNDICES POR DEFECTO:\n", + " nombre edad ciudad\n", + "0 Ana 25 Madrid\n", + "1 Luis 30 Barcelona\n", + "2 María 28 Valencia\n", + "3 Carlos 35 Sevilla\n", + "\n", + "Índice de filas: RangeIndex(start=0, stop=4, step=1)\n", + "Índice de columnas: ['nombre', 'edad', 'ciudad']\n", + "\n", + "2. DATAFRAME CON ÍNDICE PERSONALIZADO:\n", + " edad ciudad\n", + "Ana 25 Madrid\n", + "Luis 30 Barcelona\n", + "María 28 Valencia\n", + "Carlos 35 Sevilla\n", + "\n", + "Índice de filas: ['Ana', 'Luis', 'María', 'Carlos']\n", + "\n", + "3. ESTABLECER ÍNDICE DESDE COLUMNA:\n", + " edad ciudad\n", + "nombre \n", + "Ana 25 Madrid\n", + "Luis 30 Barcelona\n", + "María 28 Valencia\n", + "Carlos 35 Sevilla\n", + "\n", + "Índice de filas: ['Ana', 'Luis', 'María', 'Carlos']\n", + "\n", + "4. RESETEAR ÍNDICE (volver a RangeIndex):\n", + " nombre edad ciudad\n", + "0 Ana 25 Madrid\n", + "1 Luis 30 Barcelona\n", + "2 María 28 Valencia\n", + "3 Carlos 35 Sevilla\n", + "\n", + "======================================================================\n", + "ACCESO A COLUMNAS EN DATAFRAME\n", + "======================================================================\n", + "DataFrame:\n", + " producto precio stock categoria\n", + "0 A 10.50 100 X\n", + "1 B 20.00 50 Y\n", + "2 C 15.75 75 X\n", + "3 D 30.25 25 Y\n", + "\n", + "1. ACCESO A UNA COLUMNA CON []:\n", + "df['precio'] (retorna Series):\n", + "0 10.50\n", + "1 20.00\n", + "2 15.75\n", + "3 30.25\n", + "Name: precio, dtype: float64\n", + "Tipo: \n", + "\n", + "2. ACCESO COMO ATRIBUTO (si el nombre es válido):\n", + "df.precio:\n", + "0 10.50\n", + "1 20.00\n", + "2 15.75\n", + "3 30.25\n", + "Name: precio, dtype: float64\n", + "⚠️ Solo funciona si el nombre no tiene espacios ni caracteres especiales\n", + "\n", + "3. ACCESO A MÚLTIPLES COLUMNAS (retorna DataFrame):\n", + "df[['producto', 'precio']]:\n", + " producto precio\n", + "0 A 10.50\n", + "1 B 20.00\n", + "2 C 15.75\n", + "3 D 30.25\n", + "Tipo: \n", + "\n", + "4. REORDENAR COLUMNAS:\n", + " producto stock precio categoria\n", + "0 A 100 10.50 X\n", + "1 B 50 20.00 Y\n", + "2 C 75 15.75 X\n", + "3 D 25 30.25 Y\n", + "\n", + "5. SELECCIONAR Y RENOMBRAR COLUMNAS:\n", + " item precio\n", + "0 A 10.50\n", + "1 B 20.00\n", + "2 C 15.75\n", + "3 D 30.25\n", + "\n", + "======================================================================\n", + "ACCESO A FILAS EN DATAFRAME\n", + "======================================================================\n", + "DataFrame:\n", + " nombre edad salario\n", + "E001 Ana 25 30000\n", + "E002 Luis 30 45000\n", + "E003 María 28 38000\n", + "E004 Carlos 35 52000\n", + "\n", + "1. SLICING DE FILAS CON []:\n", + "df[0:2] (primeras 2 filas por posición):\n", + " nombre edad salario\n", + "E001 Ana 25 30000\n", + "E002 Luis 30 45000\n", + "\n", + "df['E001':'E003'] (por etiqueta, INCLUYE el final):\n", + " nombre edad salario\n", + "E001 Ana 25 30000\n", + "E002 Luis 30 45000\n", + "E003 María 28 38000\n", + "\n", + "2. .loc[] - POR ETIQUETA DE FILA:\n", + "df.loc['E002'] (retorna Series):\n", + "nombre Luis\n", + "edad 30\n", + "salario 45000\n", + "Name: E002, dtype: object\n", + "\n", + "df.loc[['E001', 'E003']] (retorna DataFrame):\n", + " nombre edad salario\n", + "E001 Ana 25 30000\n", + "E003 María 28 38000\n", + "\n", + "df.loc['E001':'E003'] (slicing con etiquetas):\n", + " nombre edad salario\n", + "E001 Ana 25 30000\n", + "E002 Luis 30 45000\n", + "E003 María 28 38000\n", + "\n", + "3. .iloc[] - POR POSICIÓN DE FILA:\n", + "df.iloc[1] (segunda fila, retorna Series):\n", + "nombre Luis\n", + "edad 30\n", + "salario 45000\n", + "Name: E002, dtype: object\n", + "\n", + "df.iloc[[0, 2]] (filas 1 y 3, retorna DataFrame):\n", + " nombre edad salario\n", + "E001 Ana 25 30000\n", + "E003 María 28 38000\n", + "\n", + "df.iloc[1:3] (filas 2 y 3, EXCLUYE el final):\n", + " nombre edad salario\n", + "E002 Luis 30 45000\n", + "E003 María 28 38000\n", + "\n", + "======================================================================\n", + "ACCESO BIDIMENSIONAL: FILAS Y COLUMNAS\n", + "======================================================================\n", + "DataFrame:\n", + " A B C D\n", + "fila1 1 5 9 13\n", + "fila2 2 6 10 14\n", + "fila3 3 7 11 15\n", + "fila4 4 8 12 16\n", + "\n", + "1. .loc[FILAS, COLUMNAS] - POR ETIQUETAS:\n", + "\n", + "a) Un valor específico:\n", + "df.loc['fila2', 'B'] = 6\n", + "\n", + "b) Una fila, múltiples columnas:\n", + "df.loc['fila2', ['A', 'C']]:\n", + "A 2\n", + "C 10\n", + "Name: fila2, dtype: int64\n", + "\n", + "c) Múltiples filas, una columna:\n", + "df.loc[['fila1', 'fila3'], 'B']:\n", + "fila1 5\n", + "fila3 7\n", + "Name: B, dtype: int64\n", + "\n", + "d) Múltiples filas y columnas:\n", + "df.loc[['fila1', 'fila3'], ['A', 'C']]:\n", + " A C\n", + "fila1 1 9\n", + "fila3 3 11\n", + "\n", + "e) Slicing de filas y columnas:\n", + "df.loc['fila1':'fila3', 'A':'C']:\n", + " A B C\n", + "fila1 1 5 9\n", + "fila2 2 6 10\n", + "fila3 3 7 11\n", + "\n", + "f) Todas las filas, algunas columnas:\n", + "df.loc[:, ['A', 'C']]:\n", + " A C\n", + "fila1 1 9\n", + "fila2 2 10\n", + "fila3 3 11\n", + "fila4 4 12\n", + "\n", + "2. .iloc[FILAS, COLUMNAS] - POR POSICIONES:\n", + "\n", + "a) Un valor específico:\n", + "df.iloc[1, 1] = 6\n", + "\n", + "b) Una fila, múltiples columnas:\n", + "df.iloc[1, [0, 2]]:\n", + "A 2\n", + "C 10\n", + "Name: fila2, dtype: int64\n", + "\n", + "c) Múltiples filas, una columna:\n", + "df.iloc[[0, 2], 1]:\n", + "fila1 5\n", + "fila3 7\n", + "Name: B, dtype: int64\n", + "\n", + "d) Slicing de filas y columnas:\n", + "df.iloc[0:3, 0:2]:\n", + " A B\n", + "fila1 1 5\n", + "fila2 2 6\n", + "fila3 3 7\n", + "\n", + "e) Todas las filas, algunas columnas:\n", + "df.iloc[:, [0, 2]]:\n", + " A C\n", + "fila1 1 9\n", + "fila2 2 10\n", + "fila3 3 11\n", + "fila4 4 12\n", + "\n", + "======================================================================\n", + "FILTRADO CON ÍNDICES BOOLEANOS\n", + "======================================================================\n", + "DataFrame:\n", + " producto precio stock\n", + "0 A 10 100\n", + "1 B 25 50\n", + "2 C 15 75\n", + "3 D 30 25\n", + "4 E 20 60\n", + "\n", + "1. CREAR MÁSCARA BOOLEANA:\n", + "mascara = df['precio'] > 15:\n", + "0 False\n", + "1 True\n", + "2 False\n", + "3 True\n", + "4 True\n", + "Name: precio, dtype: bool\n", + "\n", + "2. APLICAR MÁSCARA:\n", + "df[mascara]:\n", + " producto precio stock\n", + "1 B 25 50\n", + "3 D 30 25\n", + "4 E 20 60\n", + "\n", + "3. FILTRADO EN UNA LÍNEA:\n", + "df[df['precio'] > 15]:\n", + " producto precio stock\n", + "1 B 25 50\n", + "3 D 30 25\n", + "4 E 20 60\n", + "\n", + "4. MÚLTIPLES CONDICIONES CON & (AND):\n", + "df[(df['precio'] > 15) & (df['stock'] > 50)]:\n", + " producto precio stock\n", + "4 E 20 60\n", + "\n", + "5. MÚLTIPLES CONDICIONES CON | (OR):\n", + "df[(df['precio'] < 15) | (df['stock'] < 50)]:\n", + " producto precio stock\n", + "0 A 10 100\n", + "3 D 30 25\n", + "\n", + "6. NEGACIÓN CON ~:\n", + "df[~(df['precio'] > 15)] (precio NO mayor que 15):\n", + " producto precio stock\n", + "0 A 10 100\n", + "2 C 15 75\n", + "\n", + "7. .isin() - VALORES EN LISTA:\n", + "df[df['producto'].isin(['A', 'C', 'E'])]:\n", + " producto precio stock\n", + "0 A 10 100\n", + "2 C 15 75\n", + "4 E 20 60\n", + "\n", + "======================================================================\n", + "MODIFICACIÓN DE DATOS CON ÍNDICES\n", + "======================================================================\n", + "DataFrame original:\n", + " producto precio stock\n", + "P1 A 10 100\n", + "P2 B 20 50\n", + "P3 C 15 75\n", + "\n", + "1. MODIFICAR UN VALOR ESPECÍFICO:\n", + "df.loc['P2', 'precio'] = 25:\n", + " producto precio stock\n", + "P1 A 10 100\n", + "P2 B 25 50\n", + "P3 C 15 75\n", + "\n", + "2. MODIFICAR UNA COLUMNA COMPLETA:\n", + "df['precio'] = df['precio'] * 1.10:\n", + " producto precio stock\n", + "P1 A 11.0 100\n", + "P2 B 22.0 50\n", + "P3 C 16.5 75\n", + "\n", + "3. MODIFICAR UNA FILA COMPLETA:\n", + "df.loc['P2'] = ['X', 999, 999]:\n", + " producto precio stock\n", + "P1 A 10 100\n", + "P2 X 999 999\n", + "P3 C 15 75\n", + "\n", + "4. MODIFICAR CON CONDICIÓN:\n", + "df.loc[df['stock'] < 60, 'stock'] = 100:\n", + " producto precio stock\n", + "P1 A 10 100\n", + "P2 B 20 100\n", + "P3 C 15 75\n", + "\n", + "5. AGREGAR NUEVA COLUMNA:\n", + "df['total'] = df['precio'] * df['stock']:\n", + " producto precio stock total\n", + "P1 A 10 100 1000\n", + "P2 B 20 50 1000\n", + "P3 C 15 75 1125\n", + "\n", + "6. AGREGAR NUEVA FILA:\n", + "df.loc['P4'] = ['D', 30, 40]:\n", + " producto precio stock\n", + "P1 A 10 100\n", + "P2 B 20 50\n", + "P3 C 15 75\n", + "P4 D 30 40\n", + "\n", + "======================================================================\n", + "MULTIINDEX: ÍNDICES JERÁRQUICOS\n", + "======================================================================\n", + "DataFrame con MultiIndex:\n", + " población area_km2\n", + "país ciudad \n", + "España Madrid 3200000 604\n", + " Barcelona 1600000 101\n", + "Francia París 2200000 105\n", + " Lyon 500000 48\n", + "Italia Roma 2800000 1285\n", + " Milán 1400000 181\n", + "\n", + "1. ACCESO A NIVEL SUPERIOR:\n", + "df.loc['España']:\n", + " población area_km2\n", + "ciudad \n", + "Madrid 3200000 604\n", + "Barcelona 1600000 101\n", + "\n", + "2. ACCESO A NIVEL ESPECÍFICO:\n", + "df.loc[('España', 'Madrid')]:\n", + "población 3200000\n", + "area_km2 604\n", + "Name: (España, Madrid), dtype: int64\n", + "\n", + "3. ACCESO CON SLICE:\n", + "df.loc[('España', slice(None)), :]:\n", + " población area_km2\n", + "país ciudad \n", + "España Madrid 3200000 604\n", + " Barcelona 1600000 101\n", + "\n", + "4. CROSS-SECTION (xs):\n", + "df.xs('España', level='país'):\n", + " población area_km2\n", + "ciudad \n", + "Madrid 3200000 604\n", + "Barcelona 1600000 101\n", + "\n", + "======================================================================\n", + "EJEMPLOS PRÁCTICOS COMPLETOS\n", + "======================================================================\n", + "Datos de ventas:\n", + " fecha producto cantidad precio\n", + "0 2024-01-01 A 10 100\n", + "1 2024-01-02 B 15 150\n", + "2 2024-01-03 A 12 100\n", + "3 2024-01-04 C 8 200\n", + "4 2024-01-05 B 20 150\n", + "5 2024-01-06 A 11 100\n", + "6 2024-01-07 C 9 200\n", + "7 2024-01-08 B 18 150\n", + "8 2024-01-09 A 13 100\n", + "9 2024-01-10 C 7 200\n", + "\n", + "1. VENTAS DE PRODUCTO 'A' CON CANTIDAD > 10:\n", + " fecha producto cantidad precio\n", + "2 2024-01-03 A 12 100\n", + "5 2024-01-06 A 11 100\n", + "8 2024-01-09 A 13 100\n", + "\n", + "2. TOTAL DE VENTAS POR PRODUCTO:\n", + "producto\n", + "A 4600\n", + "B 7950\n", + "C 4800\n", + "Name: total, dtype: int64\n", + "\n", + "3. USAR FECHA COMO ÍNDICE:\n", + " producto cantidad precio total\n", + "fecha \n", + "2024-01-01 A 10 100 1000\n", + "2024-01-02 B 15 150 2250\n", + "2024-01-03 A 12 100 1200\n", + "2024-01-04 C 8 200 1600\n", + "2024-01-05 B 20 150 3000\n", + "\n", + "Acceso por fecha: df_fecha.loc['2024-01-05']:\n", + "producto B\n", + "cantidad 20\n", + "precio 150\n", + "total 3000\n", + "Name: 2024-01-05 00:00:00, dtype: object\n", + "\n", + "4. PRODUCTOS B O C CON CANTIDAD > 10:\n", + " fecha producto cantidad\n", + "1 2024-01-02 B 15\n", + "4 2024-01-05 B 20\n", + "7 2024-01-08 B 18\n", + "\n", + "======================================================================\n", + "RESUMEN: COMPARACIÓN DE MÉTODOS DE ACCESO\n", + "======================================================================\n", + "\n", + "╔════════════╦═══════════════╦════════════════╦════════════════╗\n", + "║ Método ║ Tipo Acceso ║ Retorna ║ Uso Principal ║\n", + "╠════════════╬═══════════════╬════════════════╬════════════════╣\n", + "║ [] ║ Mixto ║ Serie/DF ║ Columnas, slice║\n", + "║ .loc[] ║ Etiqueta ║ Serie/DF/Valor ║ Por nombre ║\n", + "║ .iloc[] ║ Posición ║ Serie/DF/Valor ║ Por número ║\n", + "║ .at[] ║ Etiqueta ║ Valor escalar ║ 1 valor rápido ║\n", + "║ .iat[] ║ Posición ║ Valor escalar ║ 1 valor rápido ║\n", + "╚════════════╩═══════════════╩════════════════╩════════════════╝\n", + "\n", + "REGLAS IMPORTANTES:\n", + "\n", + "1. [] EN SERIES:\n", + " - serie['etiqueta'] → acceso por etiqueta\n", + " - serie[0:3] → slicing por posición (si índice numérico)\n", + "\n", + "2. [] EN DATAFRAMES:\n", + " - df['col'] → acceso a columna (retorna Series)\n", + " - df[['col1', 'col2']] → columnas (retorna DataFrame)\n", + " - df[0:3] → slicing de filas (por posición)\n", + " - df['etiq1':'etiq3'] → slicing de filas (por etiqueta)\n", + "\n", + "3. .loc[] (ETIQUETAS):\n", + " - df.loc['fila'] → una fila\n", + " - df.loc[:, 'col'] → una columna\n", + " - df.loc['fila', 'col'] → un valor\n", + " - df.loc['f1':'f3', 'c1':'c3'] → sub-DataFrame\n", + "\n", + "4. .iloc[] (POSICIONES):\n", + " - df.iloc[0] → primera fila\n", + " - df.iloc[:, 0] → primera columna\n", + " - df.iloc[0, 0] → primer valor\n", + " - df.iloc[0:3, 0:2] → sub-DataFrame\n", + "\n", + "5. DIFERENCIAS CLAVE:\n", + " ✓ loc INCLUYE el final en slicing\n", + " ✓ iloc EXCLUYE el final en slicing\n", + " ✓ at/iat son ~2x más rápidos para un solo valor\n", + " ✓ [] es más corto pero menos explícito\n", + "\n", + "💡 RECOMENDACIÓN: Usa .loc[] y .iloc[] para código claro\n", + " \n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "# Ejecutar el módulo completo\n", + "%run modulo_3b_sintaxis_indices.py\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 📖 Guía Rápida de Referencia\n", + "\n", + "### Métodos de Acceso\n", + "\n", + "| Método | Tipo | Ejemplo | Resultado |\n", + "|--------|------|---------|-----------|\n", + "| `[]` | Mixto | `serie['a']` | Por etiqueta |\n", + "| `.loc[]` | Etiqueta | `df.loc['fila', 'col']` | Por nombre |\n", + "| `.iloc[]` | Posición | `df.iloc[0, 1]` | Por número |\n", + "| `.at[]` | Etiqueta | `df.at['fila', 'col']` | 1 valor rápido |\n", + "| `.iat[]` | Posición | `df.iat[0, 1]` | 1 valor rápido |\n", + "\n", + "### Reglas de Slicing\n", + "\n", + "**Con `.loc[]` (etiquetas):**\n", + "```python\n", + "df.loc['inicio':'fin'] # INCLUYE 'fin'\n", + "```\n", + "\n", + "**Con `.iloc[]` (posiciones):**\n", + "```python\n", + "df.iloc[0:5] # EXCLUYE posición 5\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Ejemplos Interactivos\n", + "\n", + "Ahora puedes experimentar con los ejemplos. Aquí hay algunos casos de uso comunes:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Serie:\n", + "ene 100\n", + "feb 200\n", + "mar 300\n", + "abr 400\n", + "may 500\n", + "Name: ventas, dtype: int64\n", + "\n", + "Acceso directo: serie['mar'] = 300\n", + "Con .loc: serie.loc['mar'] = 300\n", + "Con .iloc: serie.iloc[2] = 300\n", + "\n", + "Slicing con etiquetas: serie['feb':'abr']\n", + "feb 200\n", + "mar 300\n", + "abr 400\n", + "Name: ventas, dtype: int64\n" + ] + } + ], + "source": [ + "# Ejemplo 1: Crear y acceder a una Serie con índice personalizado\n", + "serie = pd.Series(\n", + " [100, 200, 300, 400, 500],\n", + " index=['ene', 'feb', 'mar', 'abr', 'may'],\n", + " name='ventas'\n", + ")\n", + "\n", + "print(\"Serie:\")\n", + "print(serie)\n", + "\n", + "# Diferentes formas de acceso\n", + "print(f\"\\nAcceso directo: serie['mar'] = {serie['mar']}\")\n", + "print(f\"Con .loc: serie.loc['mar'] = {serie.loc['mar']}\")\n", + "print(f\"Con .iloc: serie.iloc[2] = {serie.iloc[2]}\")\n", + "\n", + "# Slicing\n", + "print(f\"\\nSlicing con etiquetas: serie['feb':'abr']\")\n", + "print(serie['feb':'abr'])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataFrame con 'nombre' como índice:\n", + " edad salario\n", + "nombre \n", + "Ana 25 30000\n", + "Luis 30 45000\n", + "María 28 38000\n", + "Carlos 35 52000\n", + "\n", + "Datos de Luis:\n", + "edad 30\n", + "salario 45000\n", + "Name: Luis, dtype: int64\n", + "\n", + "Salario de María: 38000\n" + ] + } + ], + "source": [ + "# Ejemplo 2: DataFrame con índice personalizado\n", + "df = pd.DataFrame({\n", + " 'nombre': ['Ana', 'Luis', 'María', 'Carlos'],\n", + " 'edad': [25, 30, 28, 35],\n", + " 'salario': [30000, 45000, 38000, 52000]\n", + "})\n", + "\n", + "# Establecer una columna como índice\n", + "df_indexed = df.set_index('nombre')\n", + "print(\"DataFrame con 'nombre' como índice:\")\n", + "print(df_indexed)\n", + "\n", + "# Acceder por nombre de persona\n", + "print(f\"\\nDatos de Luis:\")\n", + "print(df_indexed.loc['Luis'])\n", + "\n", + "# Acceder a dato específico\n", + "print(f\"\\nSalario de María: {df_indexed.loc['María', 'salario']}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataFrame:\n", + " A B C\n", + "fila1 1 5 9\n", + "fila2 2 6 10\n", + "fila3 3 7 11\n", + "fila4 4 8 12\n", + "\n", + "Un valor: df.loc['fila2', 'B'] = 6\n", + "\n", + "Una fila completa:\n", + "A 2\n", + "B 6\n", + "C 10\n", + "Name: fila2, dtype: int64\n", + "\n", + "Una columna completa:\n", + "fila1 5\n", + "fila2 6\n", + "fila3 7\n", + "fila4 8\n", + "Name: B, dtype: int64\n", + "\n", + "Sub-DataFrame:\n", + " A C\n", + "fila1 1 9\n", + "fila3 3 11\n" + ] + } + ], + "source": [ + "# Ejemplo 3: Acceso bidimensional con .loc[]\n", + "df = pd.DataFrame({\n", + " 'A': [1, 2, 3, 4],\n", + " 'B': [5, 6, 7, 8],\n", + " 'C': [9, 10, 11, 12]\n", + "}, index=['fila1', 'fila2', 'fila3', 'fila4'])\n", + "\n", + "print(\"DataFrame:\")\n", + "print(df)\n", + "\n", + "# Diferentes tipos de acceso con .loc[]\n", + "print(f\"\\nUn valor: df.loc['fila2', 'B'] = {df.loc['fila2', 'B']}\")\n", + "\n", + "print(f\"\\nUna fila completa:\")\n", + "print(df.loc['fila2'])\n", + "\n", + "print(f\"\\nUna columna completa:\")\n", + "print(df.loc[:, 'B'])\n", + "\n", + "print(f\"\\nSub-DataFrame:\")\n", + "print(df.loc[['fila1', 'fila3'], ['A', 'C']])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ejemplo 4: Filtrado con índices booleanos\n", + "df = pd.DataFrame({\n", + " 'producto': ['A', 'B', 'C', 'D', 'E'],\n", + " 'precio': [10, 25, 15, 30, 20],\n", + " 'stock': [100, 50, 75, 25, 60]\n", + "})\n", + "\n", + "print(\"DataFrame:\")\n", + "print(df)\n", + "\n", + "# Filtrar productos con precio > 15\n", + "print(f\"\\nProductos con precio > 15:\")\n", + "print(df[df['precio'] > 15])\n", + "\n", + "# Filtrar con múltiples condiciones\n", + "print(f\"\\nProductos con precio > 15 Y stock > 50:\")\n", + "print(df[(df['precio'] > 15) & (df['stock'] > 50)])\n", + "\n", + "# Filtrar con .isin()\n", + "print(f\"\\nProductos A, C o E:\")\n", + "print(df[df['producto'].isin(['A', 'C', 'E'])])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ejemplo 5: Modificar datos con índices\n", + "df = pd.DataFrame({\n", + " 'producto': ['A', 'B', 'C'],\n", + " 'precio': [10, 20, 15],\n", + " 'stock': [100, 50, 75]\n", + "}, index=['P1', 'P2', 'P3'])\n", + "\n", + "print(\"DataFrame original:\")\n", + "print(df)\n", + "\n", + "# Modificar un valor específico\n", + "df.loc['P2', 'precio'] = 25\n", + "print(f\"\\nDespués de cambiar precio de P2 a 25:\")\n", + "print(df)\n", + "\n", + "# Modificar con condición\n", + "df.loc[df['stock'] < 60, 'stock'] = 100\n", + "print(f\"\\nDespués de poner stock=100 donde stock < 60:\")\n", + "print(df)\n", + "\n", + "# Agregar nueva columna calculada\n", + "df['total'] = df['precio'] * df['stock']\n", + "print(f\"\\nCon columna 'total' agregada:\")\n", + "print(df)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ejemplo 6: Comparación directa loc vs iloc\n", + "df = pd.DataFrame({\n", + " 'A': [10, 20, 30],\n", + " 'B': [40, 50, 60],\n", + " 'C': [70, 80, 90]\n", + "}, index=['x', 'y', 'z'])\n", + "\n", + "print(\"DataFrame:\")\n", + "print(df)\n", + "\n", + "print(\"\\n=== COMPARACIÓN .loc[] vs .iloc[] ===\")\n", + "\n", + "print(\"\\nCon .loc[] (por ETIQUETA):\")\n", + "print(f\"df.loc['y', 'B'] = {df.loc['y', 'B']}\")\n", + "print(f\"df.loc['x':'y', 'A':'B']:\")\n", + "print(df.loc['x':'y', 'A':'B'])\n", + "print(\" ↑ INCLUYE 'y' y 'B'\")\n", + "\n", + "print(\"\\nCon .iloc[] (por POSICIÓN):\")\n", + "print(f\"df.iloc[1, 1] = {df.iloc[1, 1]}\")\n", + "print(f\"df.iloc[0:2, 0:2]:\")\n", + "print(df.iloc[0:2, 0:2])\n", + "print(\" ↑ EXCLUYE posición 2\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 📚 Resumen y Buenas Prácticas\n", + "\n", + "### ✅ Cuándo Usar Cada Método\n", + "\n", + "**Usa `[]` cuando:**\n", + "- Accedas a columnas: `df['columna']`\n", + "- Hagas slicing simple: `df[0:5]`\n", + "- Filtres con booleanos: `df[df['edad'] > 25]`\n", + "\n", + "**Usa `.loc[]` cuando:**\n", + "- Trabajes con etiquetas personalizadas\n", + "- Necesites acceso bidimensional explícito\n", + "- Quieras código más legible\n", + "\n", + "**Usa `.iloc[]` cuando:**\n", + "- Trabajes con posiciones numéricas\n", + "- Necesites los primeros/últimos N elementos\n", + "- Iteres con índices numéricos\n", + "\n", + "**Usa `.at[]` / `.iat[]` cuando:**\n", + "- Accedas a UN SOLO valor\n", + "- Necesites máximo rendimiento\n", + "- Hagas muchas operaciones de lectura/escritura\n", + "\n", + "### ⚠️ Errores Comunes\n", + "\n", + "1. **Confundir loc con iloc:**\n", + " ```python\n", + " # ❌ Mal\n", + " df.loc[0:5] # Esperas posiciones pero usa etiquetas\n", + " \n", + " # ✅ Bien\n", + " df.iloc[0:5] # Usa iloc para posiciones\n", + " ```\n", + "\n", + "2. **Olvidar que loc incluye el final:**\n", + " ```python\n", + " df.loc['a':'c'] # Incluye 'c'\n", + " df.iloc[0:3] # NO incluye posición 3\n", + " ```\n", + "\n", + "3. **No usar & y | en filtros:**\n", + " ```python\n", + " # ❌ Mal\n", + " df[df['a'] > 5 and df['b'] < 10] # Error!\n", + " \n", + " # ✅ Bien\n", + " df[(df['a'] > 5) & (df['b'] < 10)] # Paréntesis importantes!\n", + " ```\n", + "\n", + "### 💡 Consejos Pro\n", + "\n", + "1. **Usa `.copy()` al modificar subconjuntos:**\n", + " ```python\n", + " subset = df[df['edad'] > 25].copy()\n", + " subset['nueva_col'] = ... # Evita SettingWithCopyWarning\n", + " ```\n", + "\n", + "2. **Encadena operaciones para claridad:**\n", + " ```python\n", + " resultado = (df\n", + " .query('edad > 25')\n", + " .loc[:, ['nombre', 'salario']]\n", + " .sort_values('salario', ascending=False)\n", + " )\n", + " ```\n", + "\n", + "3. **Usa `.isin()` en lugar de múltiples ORs:**\n", + " ```python\n", + " # ✅ Mejor\n", + " df[df['ciudad'].isin(['Madrid', 'Barcelona', 'Valencia'])]\n", + " \n", + " # ❌ Peor\n", + " df[(df['ciudad'] == 'Madrid') | \n", + " (df['ciudad'] == 'Barcelona') | \n", + " (df['ciudad'] == 'Valencia')]\n", + " ```\n", + "\n", + "---\n", + "\n", + "**¡Ahora dominas la sintaxis de índices en Pandas! 🎉**\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/python-for-engineering/06_pandas/Modulo_3_Index.ipynb b/python-for-engineering/06_pandas/Modulo_3_Index.ipynb new file mode 100644 index 0000000..3e210e8 --- /dev/null +++ b/python-for-engineering/06_pandas/Modulo_3_Index.ipynb @@ -0,0 +1,322 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Módulo 3: Index - La Columna Vertebral\n", + "\n", + "## 🎯 Objetivos de Aprendizaje\n", + "\n", + "- Comprender Index como clase inmutable\n", + "- Explorar la jerarquía de subclases de Index\n", + "- Dominar RangeIndex, DatetimeIndex, MultiIndex\n", + "- Entender el papel de Index en Series y DataFrame\n", + "- Aplicar conceptos de POO: inmutabilidad, herencia, polimorfismo\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "MÓDULO 3: INDEX - LA COLUMNA VERTEBRAL\n", + "======================================================================\n", + "======================================================================\n", + "LA CLASE INDEX BASE\n", + "======================================================================\n", + "\n", + "Tipo: \n", + "Clase: Index\n", + "Contenido: Index([1, 2, 3, 4, 5], dtype='int64')\n", + "\n", + "ATRIBUTOS PRINCIPALES:\n", + " values: [1 2 3 4 5]\n", + " dtype: int64\n", + " name: None\n", + " shape: (5,)\n", + " size: 5\n", + " ndim: 1\n", + "\n", + "PROPIEDADES:\n", + " is_unique: True\n", + " is_monotonic_increasing: True\n", + " is_monotonic_decreasing: False\n", + " has_duplicates: False\n", + "\n", + "======================================================================\n", + "INMUTABILIDAD DE INDEX\n", + "======================================================================\n", + "Index original: Index([1, 2, 3, 4, 5], dtype='int64')\n", + "\n", + "💡 Index es INMUTABLE - no se puede modificar directamente\n", + "\n", + "❌ Error al intentar modificar: Index does not support mutable operations\n", + "\n", + "✅ Para 'modificar', crear un nuevo Index:\n", + "Nuevo Index: Index([10, 2, 3, 4, 5], dtype='int64')\n", + "¿Es el mismo objeto? False\n", + "ID original: 4869042128\n", + "ID nuevo: 4869043728\n", + "\n", + "======================================================================\n", + "JERARQUÍA DE CLASES INDEX\n", + "======================================================================\n", + "\n", + "Index base:\n", + " Tipo: Index\n", + " Hereda de Index: True\n", + " Contenido: Index([1, 2, 3], dtype='int64')\n", + " dtype: int64\n", + "\n", + "RangeIndex:\n", + " Tipo: RangeIndex\n", + " Hereda de Index: True\n", + " Contenido: RangeIndex(start=0, stop=10, step=2)\n", + " dtype: int64\n", + "\n", + "Int64Index:\n", + " Tipo: Index\n", + " Hereda de Index: True\n", + " Contenido: Index([1, 2, 3], dtype='int64')\n", + " dtype: int64\n", + "\n", + "Float64Index:\n", + " Tipo: Index\n", + " Hereda de Index: True\n", + " Contenido: Index([1.1, 2.2, 3.3], dtype='float64')\n", + " dtype: float64\n", + "\n", + "DatetimeIndex:\n", + " Tipo: DatetimeIndex\n", + " Hereda de Index: True\n", + " Contenido: DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04',\n", + " '2024-01-05'],\n", + " dtype='datetime64[ns]', freq='D')\n", + " dtype: datetime64[ns]\n", + "\n", + "TimedeltaIndex:\n", + " Tipo: TimedeltaIndex\n", + " Hereda de Index: True\n", + " Contenido: TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq='D')\n", + " dtype: timedelta64[ns]\n", + "\n", + "PeriodIndex:\n", + " Tipo: PeriodIndex\n", + " Hereda de Index: True\n", + " Contenido: PeriodIndex(['2024-01', '2024-02', '2024-03', '2024-04', '2024-05'], dtype='period[M]')\n", + " dtype: period[M]\n", + "\n", + "MultiIndex:\n", + " Tipo: MultiIndex\n", + " Hereda de Index: True\n", + " Contenido: MultiIndex([('a', 1),\n", + " ('a', 2),\n", + " ('b', 1)],\n", + " )\n", + " dtype: object\n", + "\n", + "CategoricalIndex:\n", + " Tipo: CategoricalIndex\n", + " Hereda de Index: True\n", + " Contenido: CategoricalIndex(['a', 'b', 'c', 'a', 'b'], categories=['a', 'b', 'c'], ordered=False, dtype='category')\n", + " dtype: category\n", + "\n", + "======================================================================\n", + "RANGEINDEX: Índice optimizado para memoria\n", + "======================================================================\n", + "RangeIndex(0, 1000000, 1):\n", + " Tipo: \n", + " Primeros 10: RangeIndex(start=0, stop=10, step=1)\n", + " Memoria: 132 bytes\n", + "\n", + "Index normal (0 a 999999):\n", + " Tipo: \n", + " Memoria: 132 bytes\n", + "\n", + "💡 RangeIndex usa ~1x menos memoria\n", + "\n", + "======================================================================\n", + "DATETIMEINDEX: Índice para series temporales\n", + "======================================================================\n", + "Tipo: \n", + "Contenido:\n", + "DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04',\n", + " '2024-01-05', '2024-01-06', '2024-01-07', '2024-01-08',\n", + " '2024-01-09', '2024-01-10'],\n", + " dtype='datetime64[ns]', freq='D')\n", + "\n", + "ATRIBUTOS ESPECIALES:\n", + " year: Index([2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024], dtype='int32')\n", + " month: Index([1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype='int32')\n", + " day: Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype='int32')\n", + " dayofweek: Index([0, 1, 2, 3, 4, 5, 6, 0, 1, 2], dtype='int32')\n", + " day_name(): Index(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',\n", + " 'Sunday', 'Monday', 'Tuesday', 'Wednesday'],\n", + " dtype='object')\n", + "\n", + "MÉTODOS ESPECIALES:\n", + " freq: \n", + " freqstr: D\n", + "\n", + "Series con DatetimeIndex:\n", + "2024-01-01 0.294335\n", + "2024-01-02 -0.503553\n", + "2024-01-03 0.204265\n", + "2024-01-04 -1.041464\n", + "2024-01-05 1.972534\n", + "2024-01-06 -0.908984\n", + "2024-01-07 1.448257\n", + "2024-01-08 0.527202\n", + "2024-01-09 -0.495096\n", + "2024-01-10 1.194515\n", + "Freq: D, Name: valores, dtype: float64\n", + "\n", + "======================================================================\n", + "MULTIINDEX: Índice jerárquico (multinivel)\n", + "======================================================================\n", + "Tipo: \n", + "Niveles: 2\n", + "Nombres: ['país', 'ciudad']\n", + "\n", + "Contenido:\n", + "MultiIndex([( 'España', 'Madrid'),\n", + " ( 'España', 'Barcelona'),\n", + " ('Francia', 'París'),\n", + " ('Francia', 'Lyon'),\n", + " ( 'Italia', 'Roma'),\n", + " ( 'Italia', 'Milán')],\n", + " names=['país', 'ciudad'])\n", + "\n", + "Series con MultiIndex:\n", + "país ciudad \n", + "España Madrid 3200000\n", + " Barcelona 1600000\n", + "Francia París 2200000\n", + " Lyon 500000\n", + "Italia Roma 2800000\n", + " Milán 1400000\n", + "Name: población, dtype: int64\n", + "\n", + "Acceso por nivel:\n", + " población['España']:\n", + "ciudad\n", + "Madrid 3200000\n", + "Barcelona 1600000\n", + "Name: población, dtype: int64\n", + "\n", + "======================================================================\n", + "OPERACIONES CON INDEX\n", + "======================================================================\n", + "Index 1: Index([1, 2, 3, 4, 5], dtype='int64')\n", + "Index 2: Index([4, 5, 6, 7, 8], dtype='int64')\n", + "\n", + "1. UNIÓN (union):\n", + " Index([1, 2, 3, 4, 5, 6, 7, 8], dtype='int64')\n", + "\n", + "2. INTERSECCIÓN (intersection):\n", + " Index([4, 5], dtype='int64')\n", + "\n", + "3. DIFERENCIA (difference):\n", + " Index([1, 2, 3], dtype='int64')\n", + "\n", + "4. DIFERENCIA SIMÉTRICA (symmetric_difference):\n", + " Index([1, 2, 3, 6, 7, 8], dtype='int64')\n", + "\n", + "💡 Estas son operaciones de TEORÍA DE CONJUNTOS\n", + "\n", + "======================================================================\n", + "INDEX EN SERIES Y DATAFRAME\n", + "======================================================================\n", + "\n", + "1. Index en Series:\n", + "Serie:\n", + "a 10\n", + "b 20\n", + "c 30\n", + "d 40\n", + "dtype: int64\n", + "Tipo de índice: \n", + "serie.index: Index(['a', 'b', 'c', 'd'], dtype='object')\n", + "\n", + "2. Index en DataFrame:\n", + "DataFrame:\n", + " col1 col2 col3\n", + "fila1 1 2 3\n", + "fila2 4 5 6\n", + "fila3 7 8 9\n", + "\n", + "Índice de filas: Index(['fila1', 'fila2', 'fila3'], dtype='object')\n", + "Tipo: \n", + "\n", + "Índice de columnas: Index(['col1', 'col2', 'col3'], dtype='object')\n", + "Tipo: \n", + "\n", + "💡 DataFrame tiene DOS Index: uno para filas, otro para columnas\n", + "\n", + "======================================================================\n", + "MÉTODOS ÚTILES DE INDEX\n", + "======================================================================\n", + "Index: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')\n", + "\n", + "1. BÚSQUEDA:\n", + " get_loc('c'): 2\n", + " 'c' in idx: True\n", + "\n", + "2. TRANSFORMACIÓN:\n", + " to_list(): ['a', 'b', 'c', 'd', 'e']\n", + " to_numpy(): ['a' 'b' 'c' 'd' 'e']\n", + "\n", + "3. REINDEXACIÓN:\n", + " insert(2, 'x'): Index(['a', 'b', 'x', 'c', 'd', 'e'], dtype='object')\n", + " delete(2): Index(['a', 'b', 'd', 'e'], dtype='object')\n", + "\n", + "4. RENOMBRAR:\n", + " rename('letras'): Index(['a', 'b', 'c', 'd', 'e'], dtype='object', name='letras')\n", + " name: letras\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "# Ejecutar todo el módulo\n", + "%run modulo_3_index.py\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/python-for-engineering/06_pandas/Modulo_4_Encadenamiento.ipynb b/python-for-engineering/06_pandas/Modulo_4_Encadenamiento.ipynb new file mode 100644 index 0000000..a4b7b7a --- /dev/null +++ b/python-for-engineering/06_pandas/Modulo_4_Encadenamiento.ipynb @@ -0,0 +1,39 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Módulo 4: Métodos y Encadenamiento (Method Chaining)\n", + "\n", + "## 🎯 Objetivos de Aprendizaje\n", + "\n", + "- Comprender el patrón Fluent Interface\n", + "- Dominar el encadenamiento de métodos (method chaining)\n", + "- Entender copy() vs referencias\n", + "- Usar pipe(), assign(), query()\n", + "- Aplicar conceptos de POO: retorno de self, programación fluida\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "# Ejecutar todo el módulo\n", + "%run modulo_4_encadenamiento.py\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/python-for-engineering/06_pandas/Modulo_5_Herencia.ipynb b/python-for-engineering/06_pandas/Modulo_5_Herencia.ipynb new file mode 100644 index 0000000..2baf167 --- /dev/null +++ b/python-for-engineering/06_pandas/Modulo_5_Herencia.ipynb @@ -0,0 +1,39 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Módulo 5: Herencia y Polimorfismo en Pandas\n", + "\n", + "## 🎯 Objetivos de Aprendizaje\n", + "\n", + "- Comprender la jerarquía de clases (NDFrame)\n", + "- Identificar métodos compartidos por herencia\n", + "- Entender polimorfismo en Pandas\n", + "- Aplicar duck typing\n", + "- Crear subclases personalizadas de Series/DataFrame\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "# Ejecutar todo el módulo\n", + "%run modulo_5_herencia.py\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/python-for-engineering/06_pandas/Modulo_6_Metodos_Magicos.ipynb b/python-for-engineering/06_pandas/Modulo_6_Metodos_Magicos.ipynb new file mode 100644 index 0000000..b121baf --- /dev/null +++ b/python-for-engineering/06_pandas/Modulo_6_Metodos_Magicos.ipynb @@ -0,0 +1,39 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Módulo 6: Atributos Especiales y Métodos Mágicos\n", + "\n", + "## 🎯 Objetivos de Aprendizaje\n", + "\n", + "- Comprender los métodos mágicos (dunder methods)\n", + "- Dominar __getitem__, __setitem__, __len__\n", + "- Entender operadores sobrecargados (+, -, *, /, ==, >, <)\n", + "- Explorar __repr__, __str__, __iter__\n", + "- Aplicar conceptos de POO: sobrecarga de operadores\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "# Ejecutar todo el módulo\n", + "%run modulo_6_metodos_magicos.py\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/python-for-engineering/06_pandas/README.md b/python-for-engineering/06_pandas/README.md new file mode 100644 index 0000000..3857199 --- /dev/null +++ b/python-for-engineering/06_pandas/README.md @@ -0,0 +1,143 @@ +# Curso: Pandas desde la Perspectiva de POO + +## 📖 Descripción + +Este es un curso completo de **Pandas con enfoque en Programación Orientada a Objetos**. Aprenderás no solo a usar Pandas, sino a entender cómo está construido y diseñado desde una perspectiva de ingeniería de software. + +## 🎯 Para Quién es Este Curso + +- Desarrolladores que quieren entender Pandas en profundidad +- Programadores que desean aplicar conceptos de POO en análisis de datos +- Ingenieros que buscan aprender diseño de software con casos reales +- Cualquiera que quiera ir más allá de "copiar y pegar código" + +## 📚 Contenido del Curso + +El curso contiene **8 módulos** (0-6 + 3B) que cubren: + +1. **Módulo 0**: Fundamentos de POO aplicados a Pandas +2. **Módulo 1**: Series - El Objeto Fundamental +3. **Módulo 2**: DataFrame - Colección de Series +4. **Módulo 3**: Index - La Columna Vertebral +5. **Módulo 3B**: **Sintaxis de Índices - Guía Práctica** ⭐ **NUEVO** +6. **Módulo 4**: Métodos y Encadenamiento (Method Chaining) +7. **Módulo 5**: Herencia y Polimorfismo en Pandas +8. **Módulo 6**: Atributos Especiales y Métodos Mágicos + +Ver `INDICE_CURSO.md` para detalles completos. + +## 🛠️ Configuración del Entorno + +### 1. Activar el entorno virtual + +```bash +# En macOS/Linux +source .env/bin/activate + +# En Windows +.env\Scripts\activate +``` + +### 2. Instalar las dependencias + +```bash +pip install -r requirements.txt +``` + +### 3. Verificar instalación + +```bash +python main.py +``` + +## 📂 Estructura del Proyecto + +``` +Pandas/ +├── INDICE_CURSO.md # Índice detallado del curso +├── README.md # Este archivo +├── requirements.txt # Dependencias +├── main.py # Script de verificación +│ +├── Modulo_0_Fundamentos_POO.ipynb +├── modulo_0_fundamentos_poo.py +│ +├── Modulo_1_Series.ipynb +├── modulo_1_series.py +│ +├── Modulo_2_DataFrame.ipynb +├── modulo_2_dataframe.py +│ +├── Modulo_3_Index.ipynb +├── modulo_3_index.py +│ +├── Modulo_3B_Sintaxis_Indices.ipynb ⭐ NUEVO +├── modulo_3b_sintaxis_indices.py ⭐ NUEVO +│ +├── Modulo_4_Encadenamiento.ipynb +├── modulo_4_encadenamiento.py +│ +├── Modulo_5_Herencia.ipynb +├── modulo_5_herencia.py +│ +├── Modulo_6_Metodos_Magicos.ipynb +└── modulo_6_metodos_magicos.py +``` + +## 🚀 Cómo Usar + +### Opción 1: Notebooks Interactivos +Abre los archivos `.ipynb` en Jupyter: +```bash +jupyter notebook +``` + +### Opción 2: Scripts Python +Ejecuta directamente los módulos: +```bash +python modulo_0_fundamentos_poo.py +python modulo_1_series.py +# etc... +``` + +### Opción 3: Desde los Notebooks +Dentro de cada notebook, ejecuta: +```python +%run modulo_X.py +``` + +## 💡 Filosofía del Curso + +Este curso es diferente porque: +- ✅ Enseña **por qué** Pandas funciona como funciona +- ✅ Usa Pandas como **caso de estudio de POO** +- ✅ Combina **teoría** con **práctica** +- ✅ Prepara para **leer código fuente** y **crear extensiones** + +## 🎓 Conceptos de POO Cubiertos + +- Clases e instancias +- Atributos y métodos +- Herencia y polimorfismo +- Composición y agregación +- Encapsulamiento +- Inmutabilidad +- Fluent Interface +- Duck typing +- Métodos mágicos (dunder methods) +- Sobrecarga de operadores + +## 📖 Recursos Adicionales + +- [Documentación oficial de Pandas](https://pandas.pydata.org/docs/) +- [Código fuente de Pandas](https://github.com/pandas-dev/pandas) +- Ver `INDICE_CURSO.md` para índice detallado + +## 🤝 Contribuciones + +Este es material educativo. Si encuentras errores o tienes sugerencias, siéntete libre de mejorar el contenido. + +--- + +**¡Disfruta aprendiendo Pandas desde una nueva perspectiva! 🐼✨** + diff --git a/python-for-engineering/06_pandas/main.py b/python-for-engineering/06_pandas/main.py new file mode 100644 index 0000000..3db1bfd --- /dev/null +++ b/python-for-engineering/06_pandas/main.py @@ -0,0 +1,20 @@ +""" +Proyecto de análisis de datos con Pandas +""" +import pandas as pd +from dotenv import load_dotenv +import os + +# Cargar variables de entorno +load_dotenv() + +def main(): + """ + Función principal del proyecto + """ + print("Proyecto Pandas iniciado correctamente") + print(f"Versión de Pandas: {pd.__version__}") + +if __name__ == "__main__": + main() + diff --git a/python-for-engineering/06_pandas/modulo_0_fundamentos_poo.py b/python-for-engineering/06_pandas/modulo_0_fundamentos_poo.py new file mode 100644 index 0000000..49ad2cb --- /dev/null +++ b/python-for-engineering/06_pandas/modulo_0_fundamentos_poo.py @@ -0,0 +1,208 @@ +""" +Módulo 0: Fundamentos de POO aplicados a Pandas +================================================ +Este módulo introduce los conceptos básicos de POO y cómo se aplican en Pandas. +""" + +import pandas as pd +import numpy as np + + +def explorar_objeto(obj, nombre="objeto"): + """ + Explora un objeto mostrando su tipo, clase y algunos métodos. + + Args: + obj: El objeto a explorar + nombre: Nombre descriptivo del objeto + """ + print(f"\n{'='*60}") + print(f"EXPLORANDO: {nombre}") + print(f"{'='*60}") + print(f"Tipo: {type(obj)}") + print(f"Clase: {obj.__class__.__name__}") + print(f"Módulo: {obj.__class__.__module__}") + print(f"\nJerarquía de herencia (MRO):") + for i, clase in enumerate(obj.__class__.__mro__): + print(f" {i}. {clase}") + + +def mostrar_atributos_publicos(obj, limite=10): + """ + Muestra los atributos públicos de un objeto. + + Args: + obj: El objeto a inspeccionar + limite: Número máximo de atributos a mostrar + """ + atributos = [attr for attr in dir(obj) if not attr.startswith('_')] + print(f"\nAtributos públicos (mostrando {min(limite, len(atributos))} de {len(atributos)}):") + for attr in atributos[:limite]: + print(f" - {attr}") + + +def mostrar_metodos_especiales(obj, limite=15): + """ + Muestra los métodos especiales (dunder methods) de un objeto. + + Args: + obj: El objeto a inspeccionar + limite: Número máximo de métodos a mostrar + """ + metodos = [attr for attr in dir(obj) if attr.startswith('__') and attr.endswith('__')] + print(f"\nMétodos especiales (mostrando {min(limite, len(metodos))} de {len(metodos)}):") + for metodo in metodos[:limite]: + print(f" - {metodo}") + + +def comparar_estructuras(): + """ + Compara las estructuras básicas de Pandas desde perspectiva de POO. + """ + # Crear ejemplos + serie = pd.Series([1, 2, 3, 4, 5]) + dataframe = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + index = pd.Index([0, 1, 2]) + + estructuras = [ + ("Series", serie), + ("DataFrame", dataframe), + ("Index", index) + ] + + print("\n" + "="*70) + print("COMPARACIÓN DE ESTRUCTURAS DE PANDAS") + print("="*70) + + for nombre, obj in estructuras: + print(f"\n{nombre}:") + print(f" Tipo: {type(obj).__name__}") + print(f" Padre directo: {type(obj).__bases__}") + print(f" Es NDFrame: {isinstance(obj, pd.core.generic.NDFrame)}") + print(f" Es IndexOpsMixin: {isinstance(obj, pd.core.ops.OpsMixin)}") + + +def demostrar_herencia(): + """ + Demuestra cómo Series y DataFrame heredan de NDFrame. + """ + serie = pd.Series([1, 2, 3]) + df = pd.DataFrame({'A': [1, 2, 3]}) + + print("\n" + "="*70) + print("JERARQUÍA DE HERENCIA EN PANDAS") + print("="*70) + + # Encontrar métodos compartidos + metodos_serie = set(dir(serie)) + metodos_df = set(dir(df)) + compartidos = metodos_serie & metodos_df + + print(f"\nMétodos compartidos entre Series y DataFrame: {len(compartidos)}") + print(f"Métodos solo en Series: {len(metodos_serie - metodos_df)}") + print(f"Métodos solo en DataFrame: {len(metodos_df - metodos_serie)}") + + # Algunos métodos compartidos importantes + print("\nAlgunos métodos compartidos importantes:") + metodos_importantes = ['head', 'tail', 'describe', 'sum', 'mean', 'info', 'copy'] + for metodo in metodos_importantes: + print(f" - {metodo}") + + +def ejemplo_instanciacion(): + """ + Muestra diferentes formas de crear objetos en Pandas. + """ + print("\n" + "="*70) + print("INSTANCIACIÓN DE OBJETOS EN PANDAS") + print("="*70) + + # Series - diferentes constructores + print("\n1. Creando Series de diferentes formas:") + + print("\n a) Desde lista:") + s1 = pd.Series([1, 2, 3, 4, 5]) + print(f" {s1.tolist()}") + + print("\n b) Desde diccionario:") + s2 = pd.Series({'a': 1, 'b': 2, 'c': 3}) + print(f" {s2.to_dict()}") + + print("\n c) Desde escalar:") + s3 = pd.Series(5, index=['a', 'b', 'c']) + print(f" {s3.tolist()}") + + print("\n d) Desde array de NumPy:") + s4 = pd.Series(np.array([1, 2, 3, 4])) + print(f" {s4.tolist()}") + + # DataFrame - diferentes constructores + print("\n2. Creando DataFrame de diferentes formas:") + + print("\n a) Desde diccionario de listas:") + df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + print(f" Shape: {df1.shape}") + + print("\n b) Desde lista de diccionarios:") + df2 = pd.DataFrame([{'A': 1, 'B': 4}, {'A': 2, 'B': 5}]) + print(f" Shape: {df2.shape}") + + print("\n c) Desde array de NumPy:") + df3 = pd.DataFrame(np.random.randn(3, 3), columns=['X', 'Y', 'Z']) + print(f" Shape: {df3.shape}") + + +def ejemplo_composicion(): + """ + Demuestra cómo DataFrame es una composición de Series. + """ + print("\n" + "="*70) + print("COMPOSICIÓN: DataFrame como colección de Series") + print("="*70) + + df = pd.DataFrame({ + 'nombre': ['Ana', 'Luis', 'María'], + 'edad': [25, 30, 28], + 'ciudad': ['Madrid', 'Barcelona', 'Valencia'] + }) + + print("\nDataFrame creado:") + print(df) + + print("\n\nAccediendo a una columna (retorna Series):") + columna_edad = df['edad'] + print(f"Tipo: {type(columna_edad)}") + print(f"Es una Serie: {isinstance(columna_edad, pd.Series)}") + print(f"Contenido: {columna_edad.tolist()}") + + print("\n\nIterando por columnas (cada una es una Series):") + for nombre_col in df.columns: + col = df[nombre_col] + print(f" {nombre_col}: tipo={type(col).__name__}, dtype={col.dtype}") + + +if __name__ == "__main__": + print("="*70) + print("MÓDULO 0: FUNDAMENTOS DE POO APLICADOS A PANDAS") + print("="*70) + + # Crear una serie simple para explorar + mi_serie = pd.Series([10, 20, 30, 40, 50], name='ventas') + + # Explorar la serie + explorar_objeto(mi_serie, "Series de Pandas") + mostrar_atributos_publicos(mi_serie, 15) + mostrar_metodos_especiales(mi_serie, 15) + + # Comparar estructuras + comparar_estructuras() + + # Demostrar herencia + demostrar_herencia() + + # Ejemplos de instanciación + ejemplo_instanciacion() + + # Ejemplo de composición + ejemplo_composicion() + diff --git a/python-for-engineering/06_pandas/modulo_1_series.py b/python-for-engineering/06_pandas/modulo_1_series.py new file mode 100644 index 0000000..ac89570 --- /dev/null +++ b/python-for-engineering/06_pandas/modulo_1_series.py @@ -0,0 +1,268 @@ +""" +Módulo 1: Series - El Objeto Fundamental +========================================= +Exploración profunda de la clase Series desde la perspectiva de POO. +""" + +import pandas as pd +import numpy as np + + +class AnalizadorSeries: + """ + Clase para analizar objetos Series de Pandas. + Demuestra cómo crear clases que interactúan con Series. + """ + + def __init__(self, serie): + """ + Constructor que recibe un objeto Series. + + Args: + serie: Un objeto pd.Series + """ + if not isinstance(serie, pd.Series): + raise TypeError("El argumento debe ser un objeto pd.Series") + self.serie = serie + + def anatomia_completa(self): + """Muestra la anatomía completa de la Serie.""" + print("="*70) + print("ANATOMÍA COMPLETA DE LA SERIES") + print("="*70) + + print("\n1. IDENTIDAD DEL OBJETO:") + print(f" Tipo: {type(self.serie)}") + print(f" ID en memoria: {id(self.serie)}") + print(f" Tamaño en bytes: {self.serie.memory_usage(deep=True)} bytes") + + print("\n2. ATRIBUTOS DE DATOS:") + print(f" values: {self.serie.values}") + print(f" index: {self.serie.index}") + print(f" dtype: {self.serie.dtype}") + print(f" name: {self.serie.name}") + + print("\n3. ATRIBUTOS DE FORMA:") + print(f" shape: {self.serie.shape}") + print(f" size: {self.serie.size}") + print(f" ndim: {self.serie.ndim}") + print(f" empty: {self.serie.empty}") + + print("\n4. ATRIBUTOS DE ÍNDICE:") + print(f" Tipo de índice: {type(self.serie.index)}") + print(f" Nombre del índice: {self.serie.index.name}") + print(f" Es único: {self.serie.index.is_unique}") + print(f" Es monótono: {self.serie.index.is_monotonic_increasing}") + + def mostrar_atributos_de_clase(self): + """Muestra los atributos de clase vs instancia.""" + print("\n" + "="*70) + print("ATRIBUTOS DE CLASE VS INSTANCIA") + print("="*70) + + # Atributos de instancia (propios del objeto) + print("\nATRIBUTOS DE INSTANCIA (propios de este objeto):") + instancia = vars(self.serie) if hasattr(self.serie, '__dict__') else {} + print(f" Cantidad: {len(instancia)}") + + # Atributos de clase (compartidos por todas las Series) + print("\nATRIBUTOS DE CLASE (compartidos por todas las Series):") + print(f" __name__: {pd.Series.__name__}") + print(f" __module__: {pd.Series.__module__}") + + +def demostrar_constructores(): + """ + Demuestra las diferentes formas de construir una Serie. + En POO, estos son diferentes "constructores" o formas de instanciar. + """ + print("\n" + "="*70) + print("CONSTRUCTORES: Diferentes formas de crear Series") + print("="*70) + + ejemplos = [] + + # 1. Desde lista + s1 = pd.Series([1, 2, 3, 4, 5]) + ejemplos.append(("Lista", s1)) + + # 2. Desde diccionario + s2 = pd.Series({'a': 10, 'b': 20, 'c': 30}) + ejemplos.append(("Diccionario", s2)) + + # 3. Desde escalar + s3 = pd.Series(100, index=['x', 'y', 'z']) + ejemplos.append(("Escalar", s3)) + + # 4. Desde array NumPy + s4 = pd.Series(np.array([1.1, 2.2, 3.3])) + ejemplos.append(("NumPy array", s4)) + + # 5. Con índice personalizado + s5 = pd.Series([10, 20, 30], index=['primero', 'segundo', 'tercero'], name='valores') + ejemplos.append(("Con índice y nombre", s5)) + + # 6. Con dtype específico + s6 = pd.Series([1, 2, 3], dtype='float64') + ejemplos.append(("Con dtype específico", s6)) + + for nombre, serie in ejemplos: + print(f"\n{nombre}:") + print(f" dtype: {serie.dtype}") + print(f" index: {serie.index.tolist()}") + print(f" values: {serie.values}") + + +def demostrar_propiedades(): + """ + Demuestra las propiedades (properties) de Series. + Las propiedades son atributos calculados dinámicamente. + """ + print("\n" + "="*70) + print("PROPIEDADES: Atributos calculados dinámicamente") + print("="*70) + + serie = pd.Series([10, 20, 30, 40, 50], name='datos') + + print("\nPropiedades de solo lectura (no se pueden modificar):") + propiedades_readonly = { + 'values': serie.values, + 'size': serie.size, + 'shape': serie.shape, + 'ndim': serie.ndim, + 'empty': serie.empty, + 'hasnans': serie.hasnans, + } + + for nombre, valor in propiedades_readonly.items(): + print(f" {nombre}: {valor}") + + print("\nPropiedades modificables:") + print(f" name (original): {serie.name}") + serie.name = 'datos_modificados' + print(f" name (modificado): {serie.name}") + + print(f"\n index (original): {serie.index.tolist()}") + serie.index = ['a', 'b', 'c', 'd', 'e'] + print(f" index (modificado): {serie.index.tolist()}") + + +def demostrar_metodos_acceso(): + """ + Demuestra los métodos de acceso a datos en Series. + """ + print("\n" + "="*70) + print("MÉTODOS DE ACCESO A DATOS") + print("="*70) + + serie = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e']) + + print("\nSerie original:") + print(serie) + + print("\n1. Acceso por posición (iloc):") + print(f" serie.iloc[0] = {serie.iloc[0]}") + print(f" serie.iloc[1:3] =\n{serie.iloc[1:3]}") + + print("\n2. Acceso por etiqueta (loc):") + print(f" serie.loc['a'] = {serie.loc['a']}") + print(f" serie.loc['b':'d'] =\n{serie.loc['b':'d']}") + + print("\n3. Acceso directo con [] (funciona con ambos):") + print(f" serie['a'] = {serie['a']}") + print(f" serie[0] = {serie[0]}") + + print("\n4. Acceso con at e iat (más rápido para valores únicos):") + print(f" serie.at['a'] = {serie.at['a']}") + print(f" serie.iat[0] = {serie.iat[0]}") + + +def demostrar_metodos_estadisticos(): + """ + Demuestra los métodos estadísticos de Series. + Estos son métodos de instancia que calculan estadísticas. + """ + print("\n" + "="*70) + print("MÉTODOS ESTADÍSTICOS") + print("="*70) + + serie = pd.Series([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) + + print(f"Serie: {serie.tolist()}") + print("\nEstadísticas descriptivas:") + + metodos = { + 'sum': serie.sum, + 'mean': serie.mean, + 'median': serie.median, + 'std': serie.std, + 'var': serie.var, + 'min': serie.min, + 'max': serie.max, + 'count': serie.count, + 'quantile (0.25)': lambda: serie.quantile(0.25), + 'quantile (0.75)': lambda: serie.quantile(0.75), + } + + for nombre, metodo in metodos.items(): + resultado = metodo() + print(f" {nombre}: {resultado}") + + +def demostrar_encapsulamiento(): + """ + Demuestra el encapsulamiento en Series. + Algunos atributos internos no deberían modificarse directamente. + """ + print("\n" + "="*70) + print("ENCAPSULAMIENTO: Atributos públicos vs privados") + print("="*70) + + serie = pd.Series([1, 2, 3, 4, 5]) + + # Atributos públicos (recomendado usar) + print("\nATRIBUTOS PÚBLICOS (usar estos):") + print(f" serie.values: {serie.values}") + print(f" serie.index: {serie.index}") + print(f" serie.dtype: {serie.dtype}") + + # Atributos "privados" (comienzan con _) + print("\nATRIBUTOS 'PRIVADOS' (no usar directamente):") + atributos_privados = [attr for attr in dir(serie) if attr.startswith('_') and not attr.startswith('__')] + print(f" Cantidad: {len(atributos_privados)}") + print(f" Ejemplos: {atributos_privados[:5]}") + + print("\n💡 En POO, los atributos que comienzan con _ son") + print(" 'privados' y no deberían accederse directamente.") + + +if __name__ == "__main__": + print("="*70) + print("MÓDULO 1: SERIES - EL OBJETO FUNDAMENTAL") + print("="*70) + + # Crear una serie para análisis + mi_serie = pd.Series([10, 20, 30, 40, 50], + index=['a', 'b', 'c', 'd', 'e'], + name='ventas') + + # Usar la clase AnalizadorSeries + analizador = AnalizadorSeries(mi_serie) + analizador.anatomia_completa() + analizador.mostrar_atributos_de_clase() + + # Demostrar constructores + demostrar_constructores() + + # Demostrar propiedades + demostrar_propiedades() + + # Demostrar métodos de acceso + demostrar_metodos_acceso() + + # Demostrar métodos estadísticos + demostrar_metodos_estadisticos() + + # Demostrar encapsulamiento + demostrar_encapsulamiento() + diff --git a/python-for-engineering/06_pandas/modulo_2_dataframe.py b/python-for-engineering/06_pandas/modulo_2_dataframe.py new file mode 100644 index 0000000..f3a791c --- /dev/null +++ b/python-for-engineering/06_pandas/modulo_2_dataframe.py @@ -0,0 +1,299 @@ +""" +Módulo 2: DataFrame - Colección de Series +========================================== +Exploración del DataFrame como composición de Series desde perspectiva de POO. +""" + +import pandas as pd +import numpy as np + + +def anatomia_dataframe(df): + """ + Muestra la anatomía completa de un DataFrame. + + Args: + df: Un objeto pd.DataFrame + """ + print("="*70) + print("ANATOMÍA COMPLETA DEL DATAFRAME") + print("="*70) + + print("\n1. IDENTIDAD DEL OBJETO:") + print(f" Tipo: {type(df)}") + print(f" Clase: {df.__class__.__name__}") + print(f" ID en memoria: {id(df)}") + + print("\n2. ATRIBUTOS DE DATOS:") + print(f" values (array 2D):\n{df.values}") + print(f"\n index (filas): {df.index}") + print(f" columns (columnas): {df.columns}") + print(f" dtypes:\n{df.dtypes}") + + print("\n3. ATRIBUTOS DE FORMA:") + print(f" shape: {df.shape}") + print(f" size: {df.size}") + print(f" ndim: {df.ndim}") + print(f" empty: {df.empty}") + + print("\n4. COMPOSICIÓN (columnas como Series):") + print(f" Número de columnas: {len(df.columns)}") + for col in df.columns: + print(f" - '{col}': tipo={type(df[col]).__name__}, dtype={df[col].dtype}") + + +def demostrar_composicion(): + """ + Demuestra cómo DataFrame es una composición de Series. + """ + print("\n" + "="*70) + print("COMPOSICIÓN: DataFrame contiene Series") + print("="*70) + + # Crear DataFrame + df = pd.DataFrame({ + 'nombre': ['Ana', 'Luis', 'María', 'Carlos'], + 'edad': [25, 30, 28, 35], + 'salario': [30000, 45000, 38000, 52000], + 'ciudad': ['Madrid', 'Barcelona', 'Valencia', 'Sevilla'] + }) + + print("\nDataFrame completo:") + print(df) + print(f"\nTipo: {type(df)}") + + # Acceder a una columna (retorna Series) + print("\n" + "-"*70) + print("Accediendo a columna 'edad':") + columna_edad = df['edad'] + print(f"Tipo: {type(columna_edad)}") + print(f"Es una Series: {isinstance(columna_edad, pd.Series)}") + print(f"Contenido:\n{columna_edad}") + + # La columna tiene todos los métodos de Series + print(f"\nMétodos de Series disponibles:") + print(f" mean(): {columna_edad.mean()}") + print(f" max(): {columna_edad.max()}") + print(f" min(): {columna_edad.min()}") + + # Todas las columnas son Series + print("\n" + "-"*70) + print("Verificando que todas las columnas son Series:") + for col_name in df.columns: + col = df[col_name] + print(f" {col_name}: {isinstance(col, pd.Series)}") + + +def demostrar_constructores(): + """ + Demuestra las diferentes formas de crear DataFrames. + """ + print("\n" + "="*70) + print("CONSTRUCTORES: Diferentes formas de crear DataFrame") + print("="*70) + + # 1. Desde diccionario de listas + print("\n1. Desde diccionario de listas:") + df1 = pd.DataFrame({ + 'A': [1, 2, 3], + 'B': [4, 5, 6], + 'C': [7, 8, 9] + }) + print(df1) + + # 2. Desde lista de diccionarios + print("\n2. Desde lista de diccionarios:") + df2 = pd.DataFrame([ + {'nombre': 'Ana', 'edad': 25}, + {'nombre': 'Luis', 'edad': 30}, + {'nombre': 'María', 'edad': 28} + ]) + print(df2) + + # 3. Desde diccionario de Series + print("\n3. Desde diccionario de Series:") + df3 = pd.DataFrame({ + 'ventas': pd.Series([100, 200, 300]), + 'costos': pd.Series([50, 80, 120]) + }) + print(df3) + + # 4. Desde array de NumPy + print("\n4. Desde array de NumPy:") + df4 = pd.DataFrame( + np.random.randn(3, 4), + columns=['A', 'B', 'C', 'D'], + index=['fila1', 'fila2', 'fila3'] + ) + print(df4) + + # 5. Desde otro DataFrame + print("\n5. Desde otro DataFrame (copia):") + df5 = pd.DataFrame(df1) + print(df5) + print(f"¿Es el mismo objeto? {df1 is df5}") + print(f"¿Tienen mismos valores? {df1.equals(df5)}") + + +def demostrar_atributos(): + """ + Demuestra los atributos principales de DataFrame. + """ + print("\n" + "="*70) + print("ATRIBUTOS PRINCIPALES DE DATAFRAME") + print("="*70) + + df = pd.DataFrame({ + 'producto': ['A', 'B', 'C', 'D'], + 'precio': [10.5, 20.0, 15.75, 30.25], + 'stock': [100, 50, 75, 25] + }, index=['P1', 'P2', 'P3', 'P4']) + + print("\nDataFrame:") + print(df) + + print("\n📊 ATRIBUTOS DE DATOS:") + print(f"values (array 2D):\n{df.values}") + print(f"values.dtype: {df.values.dtype}") + + print("\n🏷️ ATRIBUTOS DE ÍNDICES:") + print(f"index: {df.index}") + print(f"index.name: {df.index.name}") + print(f"columns: {df.columns}") + print(f"columns.name: {df.columns.name}") + print(f"axes: {df.axes}") + + print("\n📏 ATRIBUTOS DE FORMA:") + print(f"shape: {df.shape}") + print(f"size: {df.size}") + print(f"ndim: {df.ndim}") + print(f"empty: {df.empty}") + + print("\n🔤 ATRIBUTOS DE TIPOS:") + print(f"dtypes:\n{df.dtypes}") + + +def demostrar_acceso_datos(): + """ + Demuestra los métodos de acceso a datos en DataFrame. + """ + print("\n" + "="*70) + print("MÉTODOS DE ACCESO A DATOS") + print("="*70) + + df = pd.DataFrame({ + 'A': [1, 2, 3, 4], + 'B': [5, 6, 7, 8], + 'C': [9, 10, 11, 12] + }, index=['fila1', 'fila2', 'fila3', 'fila4']) + + print("DataFrame original:") + print(df) + + print("\n1. ACCESO A COLUMNAS:") + print(f"df['A'] (retorna Series):\n{df['A']}") + print(f"\ndf[['A', 'C']] (retorna DataFrame):\n{df[['A', 'C']]}") + + print("\n2. ACCESO CON loc (por etiqueta):") + print(f"df.loc['fila1'] (retorna Series):\n{df.loc['fila1']}") + print(f"\ndf.loc['fila1', 'A']: {df.loc['fila1', 'A']}") + print(f"\ndf.loc['fila1':'fila3', 'A':'B']:\n{df.loc['fila1':'fila3', 'A':'B']}") + + print("\n3. ACCESO CON iloc (por posición):") + print(f"df.iloc[0] (retorna Series):\n{df.iloc[0]}") + print(f"\ndf.iloc[0, 0]: {df.iloc[0, 0]}") + print(f"\ndf.iloc[0:2, 0:2]:\n{df.iloc[0:2, 0:2]}") + + print("\n4. ACCESO CON at/iat (valores únicos):") + print(f"df.at['fila1', 'A']: {df.at['fila1', 'A']}") + print(f"df.iat[0, 0]: {df.iat[0, 0]}") + + +def demostrar_metodos_agregacion(): + """ + Demuestra métodos de agregación en DataFrame. + """ + print("\n" + "="*70) + print("MÉTODOS DE AGREGACIÓN") + print("="*70) + + df = pd.DataFrame({ + 'ventas': [100, 200, 150, 300], + 'costos': [50, 80, 60, 120], + 'ganancia': [50, 120, 90, 180] + }, index=['Q1', 'Q2', 'Q3', 'Q4']) + + print("DataFrame:") + print(df) + + print("\n1. Agregación por COLUMNAS (axis=0, default):") + print(f"sum():\n{df.sum()}") + print(f"\nmean():\n{df.mean()}") + + print("\n2. Agregación por FILAS (axis=1):") + print(f"sum(axis=1):\n{df.sum(axis=1)}") + print(f"\nmean(axis=1):\n{df.mean(axis=1)}") + + print("\n3. Describe (resumen estadístico):") + print(df.describe()) + + +def demostrar_relacion_series_dataframe(): + """ + Demuestra la relación bidireccional entre Series y DataFrame. + """ + print("\n" + "="*70) + print("RELACIÓN SERIES ↔ DATAFRAME") + print("="*70) + + # DataFrame → Series + print("\n1. DataFrame → Series (acceso a columna):") + df = pd.DataFrame({ + 'A': [1, 2, 3], + 'B': [4, 5, 6] + }) + print(f"DataFrame:\n{df}") + serie = df['A'] + print(f"\nColumna 'A' (Series):\n{serie}") + + # Series → DataFrame + print("\n2. Series → DataFrame (to_frame()):") + df_from_series = serie.to_frame() + print(f"DataFrame desde Series:\n{df_from_series}") + print(f"Tipo: {type(df_from_series)}") + + # Múltiples Series → DataFrame + print("\n3. Múltiples Series → DataFrame:") + s1 = pd.Series([1, 2, 3], name='col1') + s2 = pd.Series([4, 5, 6], name='col2') + s3 = pd.Series([7, 8, 9], name='col3') + + df_from_multiple = pd.DataFrame({ + s1.name: s1, + s2.name: s2, + s3.name: s3 + }) + print(df_from_multiple) + + +if __name__ == "__main__": + print("="*70) + print("MÓDULO 2: DATAFRAME - COLECCIÓN DE SERIES") + print("="*70) + + # Crear DataFrame de ejemplo + df_ejemplo = pd.DataFrame({ + 'nombre': ['Ana', 'Luis', 'María'], + 'edad': [25, 30, 28], + 'ciudad': ['Madrid', 'Barcelona', 'Valencia'] + }) + + # Demostrar conceptos + anatomia_dataframe(df_ejemplo) + demostrar_composicion() + demostrar_constructores() + demostrar_atributos() + demostrar_acceso_datos() + demostrar_metodos_agregacion() + demostrar_relacion_series_dataframe() + diff --git a/python-for-engineering/06_pandas/modulo_3_index.py b/python-for-engineering/06_pandas/modulo_3_index.py new file mode 100644 index 0000000..d0085d9 --- /dev/null +++ b/python-for-engineering/06_pandas/modulo_3_index.py @@ -0,0 +1,307 @@ +""" +Módulo 3: Index - La Columna Vertebral +======================================= +Exploración de la clase Index y sus subclases desde perspectiva de POO. +""" + +import pandas as pd +import numpy as np +from datetime import datetime, timedelta + + +def explorar_index_base(): + """ + Explora la clase Index base. + """ + print("="*70) + print("LA CLASE INDEX BASE") + print("="*70) + + # Crear un Index simple + idx = pd.Index([1, 2, 3, 4, 5]) + + print(f"\nTipo: {type(idx)}") + print(f"Clase: {idx.__class__.__name__}") + print(f"Contenido: {idx}") + + print("\nATRIBUTOS PRINCIPALES:") + print(f" values: {idx.values}") + print(f" dtype: {idx.dtype}") + print(f" name: {idx.name}") + print(f" shape: {idx.shape}") + print(f" size: {idx.size}") + print(f" ndim: {idx.ndim}") + + print("\nPROPIEDADES:") + print(f" is_unique: {idx.is_unique}") + print(f" is_monotonic_increasing: {idx.is_monotonic_increasing}") + print(f" is_monotonic_decreasing: {idx.is_monotonic_decreasing}") + print(f" has_duplicates: {idx.has_duplicates}") + + +def demostrar_inmutabilidad(): + """ + Demuestra que Index es inmutable. + """ + print("\n" + "="*70) + print("INMUTABILIDAD DE INDEX") + print("="*70) + + idx = pd.Index([1, 2, 3, 4, 5]) + print(f"Index original: {idx}") + + print("\n💡 Index es INMUTABLE - no se puede modificar directamente") + + # Intentar modificar causará error (comentado) + try: + idx[0] = 10 + except TypeError as e: + print(f"\n❌ Error al intentar modificar: {e}") + + print("\n✅ Para 'modificar', crear un nuevo Index:") + idx_nuevo = pd.Index([10, 2, 3, 4, 5]) + print(f"Nuevo Index: {idx_nuevo}") + print(f"¿Es el mismo objeto? {idx is idx_nuevo}") + print(f"ID original: {id(idx)}") + print(f"ID nuevo: {id(idx_nuevo)}") + + +def jerarquia_de_index(): + """ + Muestra la jerarquía de clases de Index. + """ + print("\n" + "="*70) + print("JERARQUÍA DE CLASES INDEX") + print("="*70) + + tipos_index = [ + ("Index base", pd.Index([1, 2, 3])), + ("RangeIndex", pd.RangeIndex(start=0, stop=10, step=2)), + ("Int64Index", pd.Index([1, 2, 3], dtype='int64')), + ("Float64Index", pd.Index([1.1, 2.2, 3.3])), + ("DatetimeIndex", pd.date_range('2024-01-01', periods=5)), + ("TimedeltaIndex", pd.timedelta_range('1 day', periods=5)), + ("PeriodIndex", pd.period_range('2024-01', periods=5, freq='M')), + ("MultiIndex", pd.MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1)])), + ("CategoricalIndex", pd.CategoricalIndex(['a', 'b', 'c', 'a', 'b'])), + ] + + for nombre, idx in tipos_index: + print(f"\n{nombre}:") + print(f" Tipo: {type(idx).__name__}") + print(f" Hereda de Index: {isinstance(idx, pd.Index)}") + print(f" Contenido: {idx}") + print(f" dtype: {idx.dtype}") + + +def demostrar_range_index(): + """ + Demuestra RangeIndex - optimizado para memoria. + """ + print("\n" + "="*70) + print("RANGEINDEX: Índice optimizado para memoria") + print("="*70) + + # RangeIndex no almacena todos los valores, solo start, stop, step + idx_range = pd.RangeIndex(start=0, stop=1000000, step=1) + idx_normal = pd.Index(range(1000000)) + + print(f"RangeIndex(0, 1000000, 1):") + print(f" Tipo: {type(idx_range)}") + print(f" Primeros 10: {idx_range[:10]}") + print(f" Memoria: {idx_range.memory_usage(deep=True)} bytes") + + print(f"\nIndex normal (0 a 999999):") + print(f" Tipo: {type(idx_normal)}") + print(f" Memoria: {idx_normal.memory_usage(deep=True)} bytes") + + print(f"\n💡 RangeIndex usa ~{idx_normal.memory_usage(deep=True) // idx_range.memory_usage(deep=True)}x menos memoria") + + +def demostrar_datetime_index(): + """ + Demuestra DatetimeIndex para series temporales. + """ + print("\n" + "="*70) + print("DATETIMEINDEX: Índice para series temporales") + print("="*70) + + # Crear DatetimeIndex + fechas = pd.date_range('2024-01-01', periods=10, freq='D') + + print(f"Tipo: {type(fechas)}") + print(f"Contenido:\n{fechas}") + + print("\nATRIBUTOS ESPECIALES:") + print(f" year: {fechas.year}") + print(f" month: {fechas.month}") + print(f" day: {fechas.day}") + print(f" dayofweek: {fechas.dayofweek}") + print(f" day_name(): {fechas.day_name()}") + + print("\nMÉTODOS ESPECIALES:") + print(f" freq: {fechas.freq}") + print(f" freqstr: {fechas.freqstr}") + + # Usar con Series + serie_temporal = pd.Series( + np.random.randn(10), + index=fechas, + name='valores' + ) + print(f"\nSeries con DatetimeIndex:") + print(serie_temporal) + + +def demostrar_multiindex(): + """ + Demuestra MultiIndex (índice jerárquico). + """ + print("\n" + "="*70) + print("MULTIINDEX: Índice jerárquico (multinivel)") + print("="*70) + + # Crear MultiIndex desde tuplas + idx = pd.MultiIndex.from_tuples([ + ('España', 'Madrid'), + ('España', 'Barcelona'), + ('Francia', 'París'), + ('Francia', 'Lyon'), + ('Italia', 'Roma'), + ('Italia', 'Milán') + ], names=['país', 'ciudad']) + + print(f"Tipo: {type(idx)}") + print(f"Niveles: {idx.nlevels}") + print(f"Nombres: {idx.names}") + print(f"\nContenido:\n{idx}") + + # Usar con Series + poblacion = pd.Series( + [3200000, 1600000, 2200000, 500000, 2800000, 1400000], + index=idx, + name='población' + ) + + print(f"\nSeries con MultiIndex:") + print(poblacion) + + print(f"\nAcceso por nivel:") + print(f" población['España']:\n{poblacion['España']}") + + +def demostrar_operaciones_index(): + """ + Demuestra operaciones con Index. + """ + print("\n" + "="*70) + print("OPERACIONES CON INDEX") + print("="*70) + + idx1 = pd.Index([1, 2, 3, 4, 5]) + idx2 = pd.Index([4, 5, 6, 7, 8]) + + print(f"Index 1: {idx1}") + print(f"Index 2: {idx2}") + + print("\n1. UNIÓN (union):") + union = idx1.union(idx2) + print(f" {union}") + + print("\n2. INTERSECCIÓN (intersection):") + interseccion = idx1.intersection(idx2) + print(f" {interseccion}") + + print("\n3. DIFERENCIA (difference):") + diferencia = idx1.difference(idx2) + print(f" {diferencia}") + + print("\n4. DIFERENCIA SIMÉTRICA (symmetric_difference):") + sim_dif = idx1.symmetric_difference(idx2) + print(f" {sim_dif}") + + print("\n💡 Estas son operaciones de TEORÍA DE CONJUNTOS") + + +def demostrar_index_en_series_dataframe(): + """ + Demuestra cómo Index se usa en Series y DataFrame. + """ + print("\n" + "="*70) + print("INDEX EN SERIES Y DATAFRAME") + print("="*70) + + # Index en Series + print("\n1. Index en Series:") + serie = pd.Series( + [10, 20, 30, 40], + index=['a', 'b', 'c', 'd'] + ) + print(f"Serie:\n{serie}") + print(f"Tipo de índice: {type(serie.index)}") + print(f"serie.index: {serie.index}") + + # Index en DataFrame (filas y columnas) + print("\n2. Index en DataFrame:") + df = pd.DataFrame( + [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + index=['fila1', 'fila2', 'fila3'], + columns=['col1', 'col2', 'col3'] + ) + print(f"DataFrame:\n{df}") + print(f"\nÍndice de filas: {df.index}") + print(f"Tipo: {type(df.index)}") + print(f"\nÍndice de columnas: {df.columns}") + print(f"Tipo: {type(df.columns)}") + + print("\n💡 DataFrame tiene DOS Index: uno para filas, otro para columnas") + + +def demostrar_metodos_index(): + """ + Demuestra métodos útiles de Index. + """ + print("\n" + "="*70) + print("MÉTODOS ÚTILES DE INDEX") + print("="*70) + + idx = pd.Index(['a', 'b', 'c', 'd', 'e']) + + print(f"Index: {idx}") + + print("\n1. BÚSQUEDA:") + print(f" get_loc('c'): {idx.get_loc('c')}") + print(f" 'c' in idx: {'c' in idx}") + + print("\n2. TRANSFORMACIÓN:") + print(f" to_list(): {idx.to_list()}") + print(f" to_numpy(): {idx.to_numpy()}") + + print("\n3. REINDEXACIÓN:") + nuevo_idx = idx.insert(2, 'x') + print(f" insert(2, 'x'): {nuevo_idx}") + + eliminado = idx.delete(2) + print(f" delete(2): {eliminado}") + + print("\n4. RENOMBRAR:") + idx_con_nombre = idx.rename('letras') + print(f" rename('letras'): {idx_con_nombre}") + print(f" name: {idx_con_nombre.name}") + + +if __name__ == "__main__": + print("="*70) + print("MÓDULO 3: INDEX - LA COLUMNA VERTEBRAL") + print("="*70) + + explorar_index_base() + demostrar_inmutabilidad() + jerarquia_de_index() + demostrar_range_index() + demostrar_datetime_index() + demostrar_multiindex() + demostrar_operaciones_index() + demostrar_index_en_series_dataframe() + demostrar_metodos_index() + diff --git a/python-for-engineering/06_pandas/modulo_3b_sintaxis_indices.py b/python-for-engineering/06_pandas/modulo_3b_sintaxis_indices.py new file mode 100644 index 0000000..d03d62b --- /dev/null +++ b/python-for-engineering/06_pandas/modulo_3b_sintaxis_indices.py @@ -0,0 +1,607 @@ +""" +Módulo 3B: Sintaxis de Índices - Guía Práctica Completa +======================================================== +Guía detallada sobre cómo usar índices en Series y DataFrames. +""" + +import pandas as pd +import numpy as np + + +def introduccion_indices(): + """ + Introducción a los índices en Pandas. + """ + print("="*70) + print("ÍNDICES EN PANDAS: CONCEPTOS FUNDAMENTALES") + print("="*70) + + print(""" +Los ÍNDICES son etiquetas que permiten identificar y acceder a datos: + +EN SERIES: +- Una Serie tiene UN índice (para las filas) +- Por defecto: RangeIndex(0, 1, 2, 3, ...) +- Puede ser personalizado con cualquier valor + +EN DATAFRAMES: +- Un DataFrame tiene DOS índices: + 1. Index para FILAS (df.index) + 2. Index para COLUMNAS (df.columns) +- Ambos son objetos Index + +VENTAJAS DE LOS ÍNDICES: +✓ Acceso rápido por etiqueta +✓ Alineación automática en operaciones +✓ Agrupación y agregación eficiente +✓ Series temporales con fechas + """) + + +def sintaxis_basica_series(): + """ + Sintaxis básica de índices en Series. + """ + print("\n" + "="*70) + print("SINTAXIS BÁSICA: ÍNDICES EN SERIES") + print("="*70) + + # Serie con índice por defecto + serie_default = pd.Series([10, 20, 30, 40, 50]) + print("\n1. ÍNDICE POR DEFECTO (RangeIndex):") + print(serie_default) + print(f"\nTipo de índice: {type(serie_default.index)}") + print(f"Índice: {serie_default.index}") + + # Serie con índice personalizado + serie_custom = pd.Series( + [10, 20, 30, 40, 50], + index=['a', 'b', 'c', 'd', 'e'] + ) + print("\n2. ÍNDICE PERSONALIZADO:") + print(serie_custom) + print(f"Índice: {serie_custom.index.tolist()}") + + # Acceso por índice + print("\n3. FORMAS DE ACCEDER A DATOS:") + print(f" serie_custom['a'] = {serie_custom['a']}") + print(f" serie_custom.loc['a'] = {serie_custom.loc['a']}") + print(f" serie_custom.iloc[0] = {serie_custom.iloc[0]}") + + # Slicing con índice + print("\n4. SLICING CON ÍNDICES:") + print(f" serie_custom['a':'c'] (INCLUYE el final):") + print(serie_custom['a':'c']) + + print(f"\n serie_custom.iloc[0:3] (EXCLUYE el final):") + print(serie_custom.iloc[0:3]) + + +def acceso_series_detallado(): + """ + Guía detallada de acceso a Series. + """ + print("\n" + "="*70) + print("GUÍA COMPLETA: ACCESO A SERIES") + print("="*70) + + serie = pd.Series( + [100, 200, 300, 400, 500], + index=['ene', 'feb', 'mar', 'abr', 'may'], + name='ventas' + ) + + print("Serie de ejemplo:") + print(serie) + + # 1. Acceso por etiqueta con [] + print("\n" + "-"*70) + print("1. ACCESO DIRECTO CON [] (por etiqueta)") + print("-"*70) + print(f"serie['feb'] = {serie['feb']}") + print(f"Tipo: {type(serie['feb'])}") + + # 2. Acceso múltiple + print("\n2. ACCESO MÚLTIPLE:") + print(f"serie[['ene', 'mar', 'may']]:") + print(serie[['ene', 'mar', 'may']]) + print(f"Tipo: {type(serie[['ene', 'mar', 'may']])}") + + # 3. Slicing con etiquetas + print("\n3. SLICING CON ETIQUETAS (incluye ambos extremos):") + print(f"serie['feb':'abr']:") + print(serie['feb':'abr']) + + # 4. loc - acceso por etiqueta (explícito) + print("\n4. .loc[] - ACCESO POR ETIQUETA (explícito):") + print(f"serie.loc['feb'] = {serie.loc['feb']}") + print(f"serie.loc['feb':'abr']:") + print(serie.loc['feb':'abr']) + + # 5. iloc - acceso por posición + print("\n5. .iloc[] - ACCESO POR POSICIÓN (índice numérico):") + print(f"serie.iloc[1] = {serie.iloc[1]}") + print(f"serie.iloc[1:4]:") + print(serie.iloc[1:4]) + print(f"serie.iloc[-1] = {serie.iloc[-1]}") + + # 6. at/iat - acceso rápido a un solo valor + print("\n6. .at[] / .iat[] - ACCESO RÁPIDO (solo un valor):") + print(f"serie.at['feb'] = {serie.at['feb']}") + print(f"serie.iat[1] = {serie.iat[1]}") + print("💡 at/iat son ~2x más rápidos para un solo valor") + + +def sintaxis_basica_dataframe(): + """ + Sintaxis básica de índices en DataFrames. + """ + print("\n" + "="*70) + print("SINTAXIS BÁSICA: ÍNDICES EN DATAFRAMES") + print("="*70) + + # DataFrame simple + df = pd.DataFrame({ + 'nombre': ['Ana', 'Luis', 'María', 'Carlos'], + 'edad': [25, 30, 28, 35], + 'ciudad': ['Madrid', 'Barcelona', 'Valencia', 'Sevilla'] + }) + + print("\n1. DATAFRAME CON ÍNDICES POR DEFECTO:") + print(df) + print(f"\nÍndice de filas: {df.index}") + print(f"Índice de columnas: {df.columns.tolist()}") + + # DataFrame con índice personalizado + df_custom = pd.DataFrame({ + 'edad': [25, 30, 28, 35], + 'ciudad': ['Madrid', 'Barcelona', 'Valencia', 'Sevilla'] + }, index=['Ana', 'Luis', 'María', 'Carlos']) + + print("\n2. DATAFRAME CON ÍNDICE PERSONALIZADO:") + print(df_custom) + print(f"\nÍndice de filas: {df_custom.index.tolist()}") + + # Establecer índice desde columna + print("\n3. ESTABLECER ÍNDICE DESDE COLUMNA:") + df_indexed = df.set_index('nombre') + print(df_indexed) + print(f"\nÍndice de filas: {df_indexed.index.tolist()}") + + # Resetear índice + print("\n4. RESETEAR ÍNDICE (volver a RangeIndex):") + df_reset = df_indexed.reset_index() + print(df_reset) + + +def acceso_dataframe_columnas(): + """ + Acceso a columnas en DataFrame. + """ + print("\n" + "="*70) + print("ACCESO A COLUMNAS EN DATAFRAME") + print("="*70) + + df = pd.DataFrame({ + 'producto': ['A', 'B', 'C', 'D'], + 'precio': [10.5, 20.0, 15.75, 30.25], + 'stock': [100, 50, 75, 25], + 'categoria': ['X', 'Y', 'X', 'Y'] + }) + + print("DataFrame:") + print(df) + + # 1. Acceso a una columna con [] + print("\n1. ACCESO A UNA COLUMNA CON []:") + print(f"df['precio'] (retorna Series):") + print(df['precio']) + print(f"Tipo: {type(df['precio'])}") + + # 2. Acceso como atributo + print("\n2. ACCESO COMO ATRIBUTO (si el nombre es válido):") + print(f"df.precio:") + print(df.precio) + print("⚠️ Solo funciona si el nombre no tiene espacios ni caracteres especiales") + + # 3. Acceso a múltiples columnas + print("\n3. ACCESO A MÚLTIPLES COLUMNAS (retorna DataFrame):") + print(f"df[['producto', 'precio']]:") + print(df[['producto', 'precio']]) + print(f"Tipo: {type(df[['producto', 'precio']])}") + + # 4. Reordenar columnas + print("\n4. REORDENAR COLUMNAS:") + df_reordenado = df[['producto', 'stock', 'precio', 'categoria']] + print(df_reordenado) + + # 5. Seleccionar y renombrar + print("\n5. SELECCIONAR Y RENOMBRAR COLUMNAS:") + df_rename = df[['producto', 'precio']].rename(columns={'producto': 'item'}) + print(df_rename) + + +def acceso_dataframe_filas(): + """ + Acceso a filas en DataFrame. + """ + print("\n" + "="*70) + print("ACCESO A FILAS EN DATAFRAME") + print("="*70) + + df = pd.DataFrame({ + 'nombre': ['Ana', 'Luis', 'María', 'Carlos'], + 'edad': [25, 30, 28, 35], + 'salario': [30000, 45000, 38000, 52000] + }, index=['E001', 'E002', 'E003', 'E004']) + + print("DataFrame:") + print(df) + + # 1. Slicing de filas con [] + print("\n1. SLICING DE FILAS CON []:") + print(f"df[0:2] (primeras 2 filas por posición):") + print(df[0:2]) + print(f"\ndf['E001':'E003'] (por etiqueta, INCLUYE el final):") + print(df['E001':'E003']) + + # 2. loc - por etiqueta de fila + print("\n2. .loc[] - POR ETIQUETA DE FILA:") + print(f"df.loc['E002'] (retorna Series):") + print(df.loc['E002']) + + print(f"\ndf.loc[['E001', 'E003']] (retorna DataFrame):") + print(df.loc[['E001', 'E003']]) + + print(f"\ndf.loc['E001':'E003'] (slicing con etiquetas):") + print(df.loc['E001':'E003']) + + # 3. iloc - por posición de fila + print("\n3. .iloc[] - POR POSICIÓN DE FILA:") + print(f"df.iloc[1] (segunda fila, retorna Series):") + print(df.iloc[1]) + + print(f"\ndf.iloc[[0, 2]] (filas 1 y 3, retorna DataFrame):") + print(df.iloc[[0, 2]]) + + print(f"\ndf.iloc[1:3] (filas 2 y 3, EXCLUYE el final):") + print(df.iloc[1:3]) + + +def acceso_dataframe_bidimensional(): + """ + Acceso bidimensional en DataFrame (filas y columnas). + """ + print("\n" + "="*70) + print("ACCESO BIDIMENSIONAL: FILAS Y COLUMNAS") + print("="*70) + + df = pd.DataFrame({ + 'A': [1, 2, 3, 4], + 'B': [5, 6, 7, 8], + 'C': [9, 10, 11, 12], + 'D': [13, 14, 15, 16] + }, index=['fila1', 'fila2', 'fila3', 'fila4']) + + print("DataFrame:") + print(df) + + # loc - etiquetas [filas, columnas] + print("\n1. .loc[FILAS, COLUMNAS] - POR ETIQUETAS:") + print("\na) Un valor específico:") + print(f"df.loc['fila2', 'B'] = {df.loc['fila2', 'B']}") + + print("\nb) Una fila, múltiples columnas:") + print(f"df.loc['fila2', ['A', 'C']]:") + print(df.loc['fila2', ['A', 'C']]) + + print("\nc) Múltiples filas, una columna:") + print(f"df.loc[['fila1', 'fila3'], 'B']:") + print(df.loc[['fila1', 'fila3'], 'B']) + + print("\nd) Múltiples filas y columnas:") + print(f"df.loc[['fila1', 'fila3'], ['A', 'C']]:") + print(df.loc[['fila1', 'fila3'], ['A', 'C']]) + + print("\ne) Slicing de filas y columnas:") + print(f"df.loc['fila1':'fila3', 'A':'C']:") + print(df.loc['fila1':'fila3', 'A':'C']) + + print("\nf) Todas las filas, algunas columnas:") + print(f"df.loc[:, ['A', 'C']]:") + print(df.loc[:, ['A', 'C']]) + + # iloc - posiciones [filas, columnas] + print("\n2. .iloc[FILAS, COLUMNAS] - POR POSICIONES:") + print("\na) Un valor específico:") + print(f"df.iloc[1, 1] = {df.iloc[1, 1]}") + + print("\nb) Una fila, múltiples columnas:") + print(f"df.iloc[1, [0, 2]]:") + print(df.iloc[1, [0, 2]]) + + print("\nc) Múltiples filas, una columna:") + print(f"df.iloc[[0, 2], 1]:") + print(df.iloc[[0, 2], 1]) + + print("\nd) Slicing de filas y columnas:") + print(f"df.iloc[0:3, 0:2]:") + print(df.iloc[0:3, 0:2]) + + print("\ne) Todas las filas, algunas columnas:") + print(f"df.iloc[:, [0, 2]]:") + print(df.iloc[:, [0, 2]]) + + +def filtrado_con_indices(): + """ + Filtrado usando índices booleanos. + """ + print("\n" + "="*70) + print("FILTRADO CON ÍNDICES BOOLEANOS") + print("="*70) + + df = pd.DataFrame({ + 'producto': ['A', 'B', 'C', 'D', 'E'], + 'precio': [10, 25, 15, 30, 20], + 'stock': [100, 50, 75, 25, 60] + }) + + print("DataFrame:") + print(df) + + # 1. Crear máscara booleana + print("\n1. CREAR MÁSCARA BOOLEANA:") + mascara = df['precio'] > 15 + print(f"mascara = df['precio'] > 15:") + print(mascara) + + # 2. Aplicar máscara + print("\n2. APLICAR MÁSCARA:") + print(f"df[mascara]:") + print(df[mascara]) + + # 3. Filtrado en una línea + print("\n3. FILTRADO EN UNA LÍNEA:") + print(f"df[df['precio'] > 15]:") + print(df[df['precio'] > 15]) + + # 4. Múltiples condiciones con & (AND) + print("\n4. MÚLTIPLES CONDICIONES CON & (AND):") + print(f"df[(df['precio'] > 15) & (df['stock'] > 50)]:") + print(df[(df['precio'] > 15) & (df['stock'] > 50)]) + + # 5. Múltiples condiciones con | (OR) + print("\n5. MÚLTIPLES CONDICIONES CON | (OR):") + print(f"df[(df['precio'] < 15) | (df['stock'] < 50)]:") + print(df[(df['precio'] < 15) | (df['stock'] < 50)]) + + # 6. Negación con ~ + print("\n6. NEGACIÓN CON ~:") + print(f"df[~(df['precio'] > 15)] (precio NO mayor que 15):") + print(df[~(df['precio'] > 15)]) + + # 7. isin() para valores en lista + print("\n7. .isin() - VALORES EN LISTA:") + print(f"df[df['producto'].isin(['A', 'C', 'E'])]:") + print(df[df['producto'].isin(['A', 'C', 'E'])]) + + +def modificacion_con_indices(): + """ + Modificar datos usando índices. + """ + print("\n" + "="*70) + print("MODIFICACIÓN DE DATOS CON ÍNDICES") + print("="*70) + + df = pd.DataFrame({ + 'producto': ['A', 'B', 'C'], + 'precio': [10, 20, 15], + 'stock': [100, 50, 75] + }, index=['P1', 'P2', 'P3']) + + print("DataFrame original:") + print(df) + + # 1. Modificar un valor + print("\n1. MODIFICAR UN VALOR ESPECÍFICO:") + df_mod = df.copy() + df_mod.loc['P2', 'precio'] = 25 + print(f"df.loc['P2', 'precio'] = 25:") + print(df_mod) + + # 2. Modificar una columna completa + print("\n2. MODIFICAR UNA COLUMNA COMPLETA:") + df_mod = df.copy() + df_mod['precio'] = df_mod['precio'] * 1.10 + print(f"df['precio'] = df['precio'] * 1.10:") + print(df_mod) + + # 3. Modificar una fila completa + print("\n3. MODIFICAR UNA FILA COMPLETA:") + df_mod = df.copy() + df_mod.loc['P2'] = ['X', 999, 999] + print(f"df.loc['P2'] = ['X', 999, 999]:") + print(df_mod) + + # 4. Modificar con condición + print("\n4. MODIFICAR CON CONDICIÓN:") + df_mod = df.copy() + df_mod.loc[df_mod['stock'] < 60, 'stock'] = 100 + print(f"df.loc[df['stock'] < 60, 'stock'] = 100:") + print(df_mod) + + # 5. Agregar nueva columna + print("\n5. AGREGAR NUEVA COLUMNA:") + df_mod = df.copy() + df_mod['total'] = df_mod['precio'] * df_mod['stock'] + print(f"df['total'] = df['precio'] * df['stock']:") + print(df_mod) + + # 6. Agregar nueva fila + print("\n6. AGREGAR NUEVA FILA:") + df_mod = df.copy() + df_mod.loc['P4'] = ['D', 30, 40] + print(f"df.loc['P4'] = ['D', 30, 40]:") + print(df_mod) + + +def indices_multiindex(): + """ + Trabajar con MultiIndex (índices jerárquicos). + """ + print("\n" + "="*70) + print("MULTIINDEX: ÍNDICES JERÁRQUICOS") + print("="*70) + + # Crear MultiIndex + arrays = [ + ['España', 'España', 'Francia', 'Francia', 'Italia', 'Italia'], + ['Madrid', 'Barcelona', 'París', 'Lyon', 'Roma', 'Milán'] + ] + index = pd.MultiIndex.from_arrays(arrays, names=['país', 'ciudad']) + + df = pd.DataFrame({ + 'población': [3200000, 1600000, 2200000, 500000, 2800000, 1400000], + 'area_km2': [604, 101, 105, 48, 1285, 181] + }, index=index) + + print("DataFrame con MultiIndex:") + print(df) + + # 1. Acceso a nivel superior + print("\n1. ACCESO A NIVEL SUPERIOR:") + print(f"df.loc['España']:") + print(df.loc['España']) + + # 2. Acceso a nivel específico + print("\n2. ACCESO A NIVEL ESPECÍFICO:") + print(f"df.loc[('España', 'Madrid')]:") + print(df.loc[('España', 'Madrid')]) + + # 3. Acceso con slice + print("\n3. ACCESO CON SLICE:") + print(f"df.loc[('España', slice(None)), :]:") + print(df.loc[('España', slice(None)), :]) + + # 4. Cross-section (xs) + print("\n4. CROSS-SECTION (xs):") + print(f"df.xs('España', level='país'):") + print(df.xs('España', level='país')) + + +def ejemplos_practicos(): + """ + Ejemplos prácticos combinando diferentes técnicas. + """ + print("\n" + "="*70) + print("EJEMPLOS PRÁCTICOS COMPLETOS") + print("="*70) + + # Datos de ventas + df = pd.DataFrame({ + 'fecha': pd.date_range('2024-01-01', periods=10, freq='D'), + 'producto': ['A', 'B', 'A', 'C', 'B', 'A', 'C', 'B', 'A', 'C'], + 'cantidad': [10, 15, 12, 8, 20, 11, 9, 18, 13, 7], + 'precio': [100, 150, 100, 200, 150, 100, 200, 150, 100, 200] + }) + + print("Datos de ventas:") + print(df) + + # Ejemplo 1: Filtrar y calcular + print("\n1. VENTAS DE PRODUCTO 'A' CON CANTIDAD > 10:") + resultado = df[(df['producto'] == 'A') & (df['cantidad'] > 10)] + print(resultado) + + # Ejemplo 2: Agrupar por producto + print("\n2. TOTAL DE VENTAS POR PRODUCTO:") + df['total'] = df['cantidad'] * df['precio'] + ventas_por_producto = df.groupby('producto')['total'].sum() + print(ventas_por_producto) + + # Ejemplo 3: Usar fecha como índice + print("\n3. USAR FECHA COMO ÍNDICE:") + df_fecha = df.set_index('fecha') + print(df_fecha.head()) + print(f"\nAcceso por fecha: df_fecha.loc['2024-01-05']:") + print(df_fecha.loc['2024-01-05']) + + # Ejemplo 4: Selección compleja + print("\n4. PRODUCTOS B O C CON CANTIDAD > 10:") + resultado = df[df['producto'].isin(['B', 'C']) & (df['cantidad'] > 10)] + print(resultado[['fecha', 'producto', 'cantidad']]) + + +def comparacion_metodos_acceso(): + """ + Tabla comparativa de métodos de acceso. + """ + print("\n" + "="*70) + print("RESUMEN: COMPARACIÓN DE MÉTODOS DE ACCESO") + print("="*70) + + print(""" +╔════════════╦═══════════════╦════════════════╦════════════════╗ +║ Método ║ Tipo Acceso ║ Retorna ║ Uso Principal ║ +╠════════════╬═══════════════╬════════════════╬════════════════╣ +║ [] ║ Mixto ║ Serie/DF ║ Columnas, slice║ +║ .loc[] ║ Etiqueta ║ Serie/DF/Valor ║ Por nombre ║ +║ .iloc[] ║ Posición ║ Serie/DF/Valor ║ Por número ║ +║ .at[] ║ Etiqueta ║ Valor escalar ║ 1 valor rápido ║ +║ .iat[] ║ Posición ║ Valor escalar ║ 1 valor rápido ║ +╚════════════╩═══════════════╩════════════════╩════════════════╝ + +REGLAS IMPORTANTES: + +1. [] EN SERIES: + - serie['etiqueta'] → acceso por etiqueta + - serie[0:3] → slicing por posición (si índice numérico) + +2. [] EN DATAFRAMES: + - df['col'] → acceso a columna (retorna Series) + - df[['col1', 'col2']] → columnas (retorna DataFrame) + - df[0:3] → slicing de filas (por posición) + - df['etiq1':'etiq3'] → slicing de filas (por etiqueta) + +3. .loc[] (ETIQUETAS): + - df.loc['fila'] → una fila + - df.loc[:, 'col'] → una columna + - df.loc['fila', 'col'] → un valor + - df.loc['f1':'f3', 'c1':'c3'] → sub-DataFrame + +4. .iloc[] (POSICIONES): + - df.iloc[0] → primera fila + - df.iloc[:, 0] → primera columna + - df.iloc[0, 0] → primer valor + - df.iloc[0:3, 0:2] → sub-DataFrame + +5. DIFERENCIAS CLAVE: + ✓ loc INCLUYE el final en slicing + ✓ iloc EXCLUYE el final en slicing + ✓ at/iat son ~2x más rápidos para un solo valor + ✓ [] es más corto pero menos explícito + +💡 RECOMENDACIÓN: Usa .loc[] y .iloc[] para código claro + """) + + +if __name__ == "__main__": + print("="*70) + print("MÓDULO 3B: SINTAXIS DE ÍNDICES - GUÍA PRÁCTICA") + print("="*70) + + introduccion_indices() + sintaxis_basica_series() + acceso_series_detallado() + sintaxis_basica_dataframe() + acceso_dataframe_columnas() + acceso_dataframe_filas() + acceso_dataframe_bidimensional() + filtrado_con_indices() + modificacion_con_indices() + indices_multiindex() + ejemplos_practicos() + comparacion_metodos_acceso() + diff --git a/python-for-engineering/06_pandas/modulo_4_encadenamiento.py b/python-for-engineering/06_pandas/modulo_4_encadenamiento.py new file mode 100644 index 0000000..a2020d1 --- /dev/null +++ b/python-for-engineering/06_pandas/modulo_4_encadenamiento.py @@ -0,0 +1,351 @@ +""" +Módulo 4: Métodos y Encadenamiento (Method Chaining) +===================================================== +Exploración del patrón Fluent Interface y method chaining en Pandas. +""" + +import pandas as pd +import numpy as np + + +def demostrar_fluent_interface(): + """ + Demuestra el patrón Fluent Interface en Pandas. + """ + print("="*70) + print("FLUENT INTERFACE: Encadenamiento de métodos") + print("="*70) + + # Crear DataFrame de ejemplo + df = pd.DataFrame({ + 'nombre': ['Ana', 'Luis', 'María', 'Carlos', 'Laura'], + 'edad': [25, 30, 28, 35, 22], + 'salario': [30000, 45000, 38000, 52000, 28000], + 'departamento': ['IT', 'Ventas', 'IT', 'Ventas', 'IT'] + }) + + print("\nDataFrame original:") + print(df) + + # Forma tradicional (sin encadenamiento) + print("\n" + "-"*70) + print("FORMA TRADICIONAL (paso por paso):") + df_filtrado = df[df['salario'] > 30000] + df_ordenado = df_filtrado.sort_values('edad') + df_final = df_ordenado[['nombre', 'edad', 'salario']] + print(df_final) + + # Forma con encadenamiento + print("\n" + "-"*70) + print("FORMA CON ENCADENAMIENTO (method chaining):") + resultado = (df + .query('salario > 30000') + .sort_values('edad') + [['nombre', 'edad', 'salario']] + ) + print(resultado) + + print("\n💡 Ambas formas producen el mismo resultado") + print(f"Son iguales: {df_final.equals(resultado)}") + + +def demostrar_retorno_self(): + """ + Demuestra cómo los métodos retornan self o una copia. + """ + print("\n" + "="*70) + print("MÉTODOS QUE RETORNAN SELF VS COPIA") + print("="*70) + + df = pd.DataFrame({ + 'A': [1, 2, 3, 4, 5], + 'B': [10, 20, 30, 40, 50] + }) + + print("DataFrame original:") + print(df) + print(f"ID: {id(df)}") + + # Métodos que retornan COPIA (nuevo objeto) + print("\n1. Métodos que retornan NUEVA COPIA:") + df_copia = df.sort_values('A') + print(f" sort_values() - ID: {id(df_copia)}") + print(f" ¿Es el mismo objeto? {df is df_copia}") + + df_multiplicado = df * 2 + print(f" operador * - ID: {id(df_multiplicado)}") + print(f" ¿Es el mismo objeto? {df is df_multiplicado}") + + # Métodos con inplace=True (modifican el original) + print("\n2. Métodos con inplace=True (modifican original):") + df_inplace = df.copy() + print(f" DataFrame antes: {df_inplace['A'].tolist()}") + print(f" ID antes: {id(df_inplace)}") + + resultado = df_inplace.sort_values('A', ascending=False, inplace=True) + print(f" sort_values(inplace=True) retorna: {resultado}") + print(f" DataFrame después: {df_inplace['A'].tolist()}") + print(f" ID después: {id(df_inplace)}") + print(f" 💡 Con inplace=True, el método retorna None") + + +def demostrar_copy_vs_referencia(): + """ + Demuestra la diferencia entre copia y referencia. + """ + print("\n" + "="*70) + print("COPY VS REFERENCIA") + print("="*70) + + df_original = pd.DataFrame({ + 'A': [1, 2, 3], + 'B': [4, 5, 6] + }) + + print("DataFrame original:") + print(df_original) + print(f"ID: {id(df_original)}") + + # Referencia (mismo objeto) + print("\n1. REFERENCIA (asignación simple):") + df_ref = df_original + print(f" ID de df_ref: {id(df_ref)}") + print(f" ¿Es el mismo objeto? {df_original is df_ref}") + + df_ref.loc[0, 'A'] = 999 + print(f"\n Modificando df_ref.loc[0, 'A'] = 999") + print(f" df_original también cambió: {df_original.loc[0, 'A']}") + + # Copia (nuevo objeto) + df_original = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + print("\n2. COPIA (método copy()):") + df_copia = df_original.copy() + print(f" ID de df_copia: {id(df_copia)}") + print(f" ¿Es el mismo objeto? {df_original is df_copia}") + + df_copia.loc[0, 'A'] = 999 + print(f"\n Modificando df_copia.loc[0, 'A'] = 999") + print(f" df_original NO cambió: {df_original.loc[0, 'A']}") + print(f" df_copia sí cambió: {df_copia.loc[0, 'A']}") + + print("\n💡 Usar .copy() cuando quieras un objeto independiente") + + +def demostrar_pipe(): + """ + Demuestra el método pipe() para funciones personalizadas. + """ + print("\n" + "="*70) + print("MÉTODO PIPE: Aplicar funciones personalizadas") + print("="*70) + + df = pd.DataFrame({ + 'nombre': ['Ana', 'Luis', 'María'], + 'edad': [25, 30, 28], + 'salario': [30000, 45000, 38000] + }) + + print("DataFrame original:") + print(df) + + # Definir funciones personalizadas + def agregar_columna_senior(df): + """Agrega columna indicando si es senior (>= 30 años).""" + df = df.copy() + df['es_senior'] = df['edad'] >= 30 + return df + + def calcular_salario_mensual(df): + """Calcula salario mensual.""" + df = df.copy() + df['salario_mensual'] = df['salario'] / 12 + return df + + def formatear_nombre(df): + """Convierte nombres a mayúsculas.""" + df = df.copy() + df['nombre'] = df['nombre'].str.upper() + return df + + # Aplicar con pipe (encadenamiento) + print("\nAplicando transformaciones con pipe():") + resultado = (df + .pipe(agregar_columna_senior) + .pipe(calcular_salario_mensual) + .pipe(formatear_nombre) + ) + + print(resultado) + + print("\n💡 pipe() permite encadenar funciones personalizadas") + + +def demostrar_assign(): + """ + Demuestra el método assign() para agregar/modificar columnas. + """ + print("\n" + "="*70) + print("MÉTODO ASSIGN: Agregar columnas en cadena") + print("="*70) + + df = pd.DataFrame({ + 'producto': ['A', 'B', 'C'], + 'precio': [10, 20, 15], + 'cantidad': [5, 3, 8] + }) + + print("DataFrame original:") + print(df) + + # Sin assign (forma tradicional) + print("\nForma tradicional:") + df_trad = df.copy() + df_trad['total'] = df_trad['precio'] * df_trad['cantidad'] + df_trad['con_iva'] = df_trad['total'] * 1.21 + print(df_trad) + + # Con assign (encadenable) + print("\nCon assign (encadenable):") + resultado = (df + .assign(total=lambda x: x['precio'] * x['cantidad']) + .assign(con_iva=lambda x: x['total'] * 1.21) + ) + print(resultado) + + # Assign múltiples columnas a la vez + print("\nAssign múltiples columnas:") + resultado2 = df.assign( + total=lambda x: x['precio'] * x['cantidad'], + con_iva=lambda x: x['precio'] * x['cantidad'] * 1.21, + descuento=lambda x: x['precio'] * 0.1 + ) + print(resultado2) + + +def demostrar_query(): + """ + Demuestra el método query() para filtrado expresivo. + """ + print("\n" + "="*70) + print("MÉTODO QUERY: Filtrado expresivo") + print("="*70) + + df = pd.DataFrame({ + 'nombre': ['Ana', 'Luis', 'María', 'Carlos', 'Laura'], + 'edad': [25, 30, 28, 35, 22], + 'salario': [30000, 45000, 38000, 52000, 28000], + 'departamento': ['IT', 'Ventas', 'IT', 'Ventas', 'IT'] + }) + + print("DataFrame:") + print(df) + + # Forma tradicional + print("\nForma tradicional:") + filtrado = df[(df['edad'] > 25) & (df['salario'] > 30000)] + print(filtrado) + + # Con query + print("\nCon query() (más legible):") + filtrado_query = df.query('edad > 25 and salario > 30000') + print(filtrado_query) + + # Query con variables externas + print("\nQuery con variables externas:") + edad_minima = 28 + salario_minimo = 35000 + filtrado_var = df.query('edad >= @edad_minima and salario >= @salario_minimo') + print(filtrado_var) + + # Query con strings + print("\nQuery con strings:") + filtrado_dept = df.query('departamento == "IT"') + print(filtrado_dept) + + +def ejemplo_completo_encadenamiento(): + """ + Ejemplo completo de encadenamiento de métodos. + """ + print("\n" + "="*70) + print("EJEMPLO COMPLETO: Análisis con encadenamiento") + print("="*70) + + # Datos de ventas + df = pd.DataFrame({ + 'fecha': pd.date_range('2024-01-01', periods=20, freq='D'), + 'producto': ['A', 'B', 'C', 'A', 'B'] * 4, + 'ventas': np.random.randint(100, 1000, 20), + 'costo': np.random.randint(50, 500, 20) + }) + + print("Datos originales:") + print(df.head(10)) + + # Pipeline completo con encadenamiento + print("\nPipeline de análisis:") + resultado = (df + .assign(ganancia=lambda x: x['ventas'] - x['costo']) + .assign(margen=lambda x: (x['ganancia'] / x['ventas'] * 100).round(2)) + .query('ganancia > 0') + .sort_values('margen', ascending=False) + .groupby('producto') + .agg({ + 'ventas': 'sum', + 'ganancia': 'sum', + 'margen': 'mean' + }) + .round(2) + .sort_values('ganancia', ascending=False) + ) + + print(resultado) + + print("\n💡 Todo el análisis en una sola expresión encadenada") + + +def demostrar_ventajas_encadenamiento(): + """ + Resume las ventajas del encadenamiento. + """ + print("\n" + "="*70) + print("VENTAJAS DEL METHOD CHAINING") + print("="*70) + + print(""" +1. LEGIBILIDAD: + - Código más expresivo y fácil de leer + - Flujo de transformaciones claro + +2. MENOS VARIABLES TEMPORALES: + - No necesitas df_temp1, df_temp2, etc. + - Menos uso de memoria + +3. ESTILO FUNCIONAL: + - Cada método retorna un nuevo objeto + - Sin efectos secundarios + +4. FACILITA DEBUGGING: + - Puedes comentar líneas individuales + - Fácil agregar/quitar pasos + +5. COMPOSICIÓN: + - Puedes construir pipelines complejos + - Reutilizable y testeable + """) + + +if __name__ == "__main__": + print("="*70) + print("MÓDULO 4: MÉTODOS Y ENCADENAMIENTO") + print("="*70) + + demostrar_fluent_interface() + demostrar_retorno_self() + demostrar_copy_vs_referencia() + demostrar_pipe() + demostrar_assign() + demostrar_query() + ejemplo_completo_encadenamiento() + demostrar_ventajas_encadenamiento() + diff --git a/python-for-engineering/06_pandas/modulo_5_herencia.py b/python-for-engineering/06_pandas/modulo_5_herencia.py new file mode 100644 index 0000000..1b6efaf --- /dev/null +++ b/python-for-engineering/06_pandas/modulo_5_herencia.py @@ -0,0 +1,307 @@ +""" +Módulo 5: Herencia y Polimorfismo en Pandas +============================================ +Exploración de la jerarquía de clases y polimorfismo en Pandas. +""" + +import pandas as pd +import numpy as np + + +def explorar_jerarquia_ndframe(): + """ + Explora la jerarquía de herencia: NDFrame → Series/DataFrame + """ + print("="*70) + print("JERARQUÍA DE HERENCIA: NDFrame") + print("="*70) + + serie = pd.Series([1, 2, 3]) + df = pd.DataFrame({'A': [1, 2, 3]}) + + print("\n1. SERIES:") + print(f" Tipo: {type(serie)}") + print(f" Hereda de NDFrame: {isinstance(serie, pd.core.generic.NDFrame)}") + print(f"\n Jerarquía (MRO):") + for i, clase in enumerate(serie.__class__.__mro__[:6]): + print(f" {i}. {clase}") + + print("\n2. DATAFRAME:") + print(f" Tipo: {type(df)}") + print(f" Hereda de NDFrame: {isinstance(df, pd.core.generic.NDFrame)}") + print(f"\n Jerarquía (MRO):") + for i, clase in enumerate(df.__class__.__mro__[:6]): + print(f" {i}. {clase}") + + print("\n💡 NDFrame es la clase base compartida") + + +def demostrar_metodos_compartidos(): + """ + Demuestra métodos compartidos entre Series y DataFrame. + """ + print("\n" + "="*70) + print("MÉTODOS COMPARTIDOS (heredados de NDFrame)") + print("="*70) + + serie = pd.Series([10, 20, 30, 40, 50], name='valores') + df = pd.DataFrame({ + 'A': [10, 20, 30, 40, 50], + 'B': [5, 10, 15, 20, 25] + }) + + print("Serie:") + print(serie) + print("\nDataFrame:") + print(df) + + # Métodos compartidos + metodos_comunes = ['head', 'tail', 'describe', 'sum', 'mean', + 'copy', 'info', 'isna', 'dropna', 'fillna'] + + print("\n" + "-"*70) + print("Métodos que ambos comparten:") + for metodo in metodos_comunes: + tiene_serie = hasattr(serie, metodo) + tiene_df = hasattr(df, metodo) + print(f" {metodo:15} - Serie: {tiene_serie}, DataFrame: {tiene_df}") + + # Ejecutar algunos métodos + print("\n" + "-"*70) + print("Ejecutando métodos compartidos:") + print(f"\nserie.head(3):\n{serie.head(3)}") + print(f"\ndf.head(3):\n{df.head(3)}") + + print(f"\nserie.sum(): {serie.sum()}") + print(f"\ndf.sum():\n{df.sum()}") + + +def demostrar_polimorfismo(): + """ + Demuestra polimorfismo: mismo método, comportamiento diferente. + """ + print("\n" + "="*70) + print("POLIMORFISMO: Mismo método, comportamiento diferente") + print("="*70) + + serie = pd.Series([1, 2, 3, 4, 5]) + df = pd.DataFrame({ + 'A': [1, 2, 3, 4, 5], + 'B': [10, 20, 30, 40, 50] + }) + + # El método sum() se comporta diferente + print("\n1. Método sum():") + print(f" serie.sum() (retorna escalar): {serie.sum()}") + print(f" Tipo: {type(serie.sum())}") + + print(f"\n df.sum() (retorna Series):\n{df.sum()}") + print(f" Tipo: {type(df.sum())}") + + # El método describe() se comporta diferente + print("\n2. Método describe():") + print(f" serie.describe() (retorna Series):\n{serie.describe()}") + print(f" Tipo: {type(serie.describe())}") + + print(f"\n df.describe() (retorna DataFrame):\n{df.describe()}") + print(f" Tipo: {type(df.describe())}") + + print("\n💡 Polimorfismo: mismo nombre, implementación diferente") + + +def demostrar_duck_typing(): + """ + Demuestra duck typing en Pandas. + """ + print("\n" + "="*70) + print("DUCK TYPING: 'Si camina como pato y grazna como pato...'") + print("="*70) + + def procesar_datos(datos): + """ + Función que acepta Series o DataFrame. + No verifica el tipo, solo que tenga los métodos necesarios. + """ + print(f"\nProcesando objeto tipo: {type(datos).__name__}") + print(f" Suma: {datos.sum()}") + print(f" Media: {datos.mean()}") + print(f" Primeras 3 filas:\n{datos.head(3)}") + + # Funciona con Series + serie = pd.Series([10, 20, 30, 40, 50]) + procesar_datos(serie) + + # Funciona con DataFrame + df = pd.DataFrame({'A': [10, 20, 30, 40, 50]}) + procesar_datos(df) + + print("\n💡 Duck typing: no importa el tipo, importan los métodos") + + +def explorar_clase_indexopsmixin(): + """ + Explora IndexOpsMixin, otra clase base importante. + """ + print("\n" + "="*70) + print("INDEXOPSMIXIN: Operaciones con índices") + print("="*70) + + serie = pd.Series([1, 2, 3, 4, 5]) + index = pd.Index([10, 20, 30]) + + print(f"Serie hereda de IndexOpsMixin: {isinstance(serie, pd.core.base.IndexOpsMixin)}") + print(f"Index hereda de IndexOpsMixin: {isinstance(index, pd.core.base.IndexOpsMixin)}") + + # Métodos compartidos de IndexOpsMixin + metodos = ['value_counts', 'unique', 'nunique', 'is_unique', 'duplicated'] + + print("\nMétodos compartidos de IndexOpsMixin:") + for metodo in metodos: + tiene_serie = hasattr(serie, metodo) + tiene_index = hasattr(index, metodo) + print(f" {metodo:15} - Series: {tiene_serie}, Index: {tiene_index}") + + +def demostrar_metodos_especificos(): + """ + Demuestra métodos específicos de cada clase. + """ + print("\n" + "="*70) + print("MÉTODOS ESPECÍFICOS DE CADA CLASE") + print("="*70) + + serie = pd.Series([1, 2, 3, 4, 5]) + df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + + # Métodos solo en Series + print("\n1. Métodos SOLO en Series:") + metodos_serie = ['autocorr', 'between', 'item', 'argmax', 'argmin'] + for metodo in metodos_serie: + tiene_serie = hasattr(serie, metodo) + tiene_df = hasattr(df, metodo) + print(f" {metodo:15} - Series: {tiene_serie}, DataFrame: {tiene_df}") + + # Métodos solo en DataFrame + print("\n2. Métodos SOLO en DataFrame:") + metodos_df = ['pivot', 'pivot_table', 'stack', 'unstack', 'melt'] + for metodo in metodos_df: + tiene_serie = hasattr(serie, metodo) + tiene_df = hasattr(df, metodo) + print(f" {metodo:15} - Series: {tiene_serie}, DataFrame: {tiene_df}") + + +def crear_subclase_personalizada(): + """ + Demuestra cómo crear una subclase de Series. + """ + print("\n" + "="*70) + print("CREAR SUBCLASE PERSONALIZADA") + print("="*70) + + class SerieConUnidades(pd.Series): + """ + Subclase de Series que incluye unidades de medida. + """ + + @property + def _constructor(self): + return SerieConUnidades + + def __init__(self, *args, unidad=None, **kwargs): + super().__init__(*args, **kwargs) + self.unidad = unidad + + def __repr__(self): + repr_normal = super().__repr__() + if self.unidad: + return f"{repr_normal}\nUnidad: {self.unidad}" + return repr_normal + + def convertir_a(self, nueva_unidad, factor): + """Convierte a otra unidad.""" + nueva_serie = self * factor + nueva_serie.unidad = nueva_unidad + return nueva_serie + + # Usar la subclase + print("\nCreando SerieConUnidades:") + distancias = SerieConUnidades([100, 200, 300], unidad='metros', name='distancia') + print(distancias) + + print("\nConvertir a kilómetros:") + distancias_km = distancias.convertir_a('kilómetros', 0.001) + print(distancias_km) + + print("\n💡 Podemos extender Pandas con nuestras propias clases") + + +def demostrar_herencia_multiple(): + """ + Explora la herencia múltiple en Pandas. + """ + print("\n" + "="*70) + print("HERENCIA MÚLTIPLE EN PANDAS") + print("="*70) + + serie = pd.Series([1, 2, 3]) + + print("Series hereda de múltiples clases:") + print(f"\nClases base directas de Series:") + for i, clase in enumerate(pd.Series.__bases__): + print(f" {i+1}. {clase}") + + print(f"\nMRO completo (primeras 10 clases):") + for i, clase in enumerate(pd.Series.__mro__[:10]): + print(f" {i+1}. {clase}") + + print("\n💡 Series usa herencia múltiple para combinar funcionalidad") + + +def comparar_estructuras_completo(): + """ + Comparación completa de las estructuras desde POO. + """ + print("\n" + "="*70) + print("COMPARACIÓN COMPLETA: Series vs DataFrame vs Index") + print("="*70) + + estructuras = { + 'Series': pd.Series([1, 2, 3]), + 'DataFrame': pd.DataFrame({'A': [1, 2, 3]}), + 'Index': pd.Index([1, 2, 3]) + } + + clases_base = [ + 'NDFrame', + 'IndexOpsMixin', + 'PandasObject', + 'SelectionMixin' + ] + + print("\nHerencia de clases base:") + print(f"{'Estructura':<12} | {'NDFrame':<10} | {'IndexOpsMixin':<15} | {'PandasObject':<15}") + print("-" * 70) + + for nombre, obj in estructuras.items(): + ndframe = isinstance(obj, pd.core.generic.NDFrame) + indexops = isinstance(obj, pd.core.base.IndexOpsMixin) + pandasobj = isinstance(obj, pd.core.base.PandasObject) + + print(f"{nombre:<12} | {str(ndframe):<10} | {str(indexops):<15} | {str(pandasobj):<15}") + + +if __name__ == "__main__": + print("="*70) + print("MÓDULO 5: HERENCIA Y POLIMORFISMO EN PANDAS") + print("="*70) + + explorar_jerarquia_ndframe() + demostrar_metodos_compartidos() + demostrar_polimorfismo() + demostrar_duck_typing() + explorar_clase_indexopsmixin() + demostrar_metodos_especificos() + crear_subclase_personalizada() + demostrar_herencia_multiple() + comparar_estructuras_completo() + diff --git a/python-for-engineering/06_pandas/modulo_6_metodos_magicos.py b/python-for-engineering/06_pandas/modulo_6_metodos_magicos.py new file mode 100644 index 0000000..689b000 --- /dev/null +++ b/python-for-engineering/06_pandas/modulo_6_metodos_magicos.py @@ -0,0 +1,425 @@ +""" +Módulo 6: Atributos Especiales y Métodos Mágicos +================================================= +Exploración de los métodos especiales (dunder methods) en Pandas. +""" + +import pandas as pd +import numpy as np + + +def introduccion_metodos_magicos(): + """ + Introducción a los métodos mágicos en Python. + """ + print("="*70) + print("MÉTODOS MÁGICOS (DUNDER METHODS)") + print("="*70) + + print(""" +Los métodos mágicos son métodos especiales en Python que: +- Comienzan y terminan con doble guion bajo (__método__) +- Permiten que los objetos se comporten como tipos nativos +- Son invocados automáticamente por Python en ciertas operaciones + +Ejemplos: +- __init__: Constructor +- __repr__: Representación del objeto +- __str__: Conversión a string +- __len__: Longitud (cuando usas len()) +- __getitem__: Acceso con [] (indexación) +- __setitem__: Asignación con [] +- __add__: Operador + (suma) + """) + + +def demostrar_getitem(): + """ + Demuestra __getitem__ (operador []). + """ + print("\n" + "="*70) + print("__getitem__: Operador de indexación []") + print("="*70) + + serie = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e']) + df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + + print("\nSeries:") + print(serie) + + print("\n1. Acceso con []:") + print(f" serie['a'] llama a serie.__getitem__('a'): {serie['a']}") + print(f" Equivalente directo: {serie.__getitem__('a')}") + + print("\n2. Slicing con []:") + print(f" serie['a':'c']:\n{serie['a':'c']}") + + print("\n3. Lista de índices:") + print(f" serie[['a', 'c', 'e']]:\n{serie[['a', 'c', 'e']]}") + + print("\n" + "-"*70) + print("\nDataFrame:") + print(df) + + print("\n1. Acceso a columna:") + print(f" df['A'] llama a df.__getitem__('A'):\n{df['A']}") + + print("\n2. Múltiples columnas:") + print(f" df[['A', 'B']]:\n{df[['A', 'B']]}") + + +def demostrar_setitem(): + """ + Demuestra __setitem__ (asignación con []). + """ + print("\n" + "="*70) + print("__setitem__: Asignación con []") + print("="*70) + + serie = pd.Series([10, 20, 30], index=['a', 'b', 'c']) + print("Serie original:") + print(serie) + + print("\n1. Modificar un valor:") + print(" serie['a'] = 999") + serie['a'] = 999 + print(f" Resultado: {serie['a']}") + + print("\n2. Agregar un nuevo índice:") + print(" serie['d'] = 40") + serie['d'] = 40 + print(f" Serie actualizada:\n{serie}") + + print("\n" + "-"*70) + df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + print("\nDataFrame original:") + print(df) + + print("\n1. Modificar una columna:") + print(" df['A'] = [10, 20, 30]") + df['A'] = [10, 20, 30] + print(f" DataFrame actualizado:\n{df}") + + print("\n2. Agregar una columna nueva:") + print(" df['C'] = [7, 8, 9]") + df['C'] = [7, 8, 9] + print(f" DataFrame actualizado:\n{df}") + + +def demostrar_len(): + """ + Demuestra __len__ (función len()). + """ + print("\n" + "="*70) + print("__len__: Función len()") + print("="*70) + + serie = pd.Series([1, 2, 3, 4, 5]) + df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + + print("Serie:", serie.tolist()) + print(f"len(serie) llama a serie.__len__(): {len(serie)}") + print(f"Equivalente directo: {serie.__len__()}") + + print(f"\nDataFrame:\n{df}") + print(f"len(df) retorna número de FILAS: {len(df)}") + print(f"Equivalente directo: {df.__len__()}") + + print("\n💡 len() en DataFrame retorna el número de filas, no elementos totales") + + +def demostrar_repr_str(): + """ + Demuestra __repr__ y __str__ (representación del objeto). + """ + print("\n" + "="*70) + print("__repr__ y __str__: Representación de objetos") + print("="*70) + + serie = pd.Series([1, 2, 3], name='mi_serie') + + print("\n1. __repr__ (representación técnica):") + print(f" repr(serie):\n{repr(serie)}") + + print("\n2. __str__ (representación legible):") + print(f" str(serie):\n{str(serie)}") + + print("\n3. print() usa __str__:") + print(" print(serie):") + print(serie) + + print("\n💡 En Pandas, __repr__ y __str__ suelen retornar lo mismo") + + +def demostrar_iter(): + """ + Demuestra __iter__ (iteración). + """ + print("\n" + "="*70) + print("__iter__: Iteración con for") + print("="*70) + + serie = pd.Series([10, 20, 30], index=['a', 'b', 'c']) + + print("Serie:") + print(serie) + + print("\n1. Iterar sobre Serie (itera sobre VALORES):") + print(" for valor in serie:") + for valor in serie: + print(f" {valor}") + + print("\n2. Iterar sobre índices:") + print(" for indice in serie.index:") + for indice in serie.index: + print(f" {indice}") + + print("\n3. Iterar sobre items (índice, valor):") + print(" for idx, val in serie.items():") + for idx, val in serie.items(): + print(f" {idx}: {val}") + + print("\n" + "-"*70) + df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + print("\nDataFrame:") + print(df) + + print("\n1. Iterar sobre DataFrame (itera sobre COLUMNAS):") + print(" for columna in df:") + for columna in df: + print(f" {columna}") + + print("\n2. Iterar sobre filas con iterrows():") + print(" for idx, fila in df.iterrows():") + for idx, fila in df.iterrows(): + print(f" Fila {idx}: {fila.tolist()}") + + +def demostrar_operadores_aritmeticos(): + """ + Demuestra operadores aritméticos sobrecargados. + """ + print("\n" + "="*70) + print("OPERADORES ARITMÉTICOS SOBRECARGADOS") + print("="*70) + + serie1 = pd.Series([1, 2, 3]) + serie2 = pd.Series([10, 20, 30]) + + print(f"Serie 1: {serie1.tolist()}") + print(f"Serie 2: {serie2.tolist()}") + + print("\n1. __add__ (operador +):") + print(f" serie1 + serie2 = {(serie1 + serie2).tolist()}") + print(f" Equivalente: serie1.__add__(serie2)") + + print("\n2. __sub__ (operador -):") + print(f" serie1 - serie2 = {(serie1 - serie2).tolist()}") + + print("\n3. __mul__ (operador *):") + print(f" serie1 * serie2 = {(serie1 * serie2).tolist()}") + + print("\n4. __truediv__ (operador /):") + print(f" serie2 / serie1 = {(serie2 / serie1).tolist()}") + + print("\n5. __pow__ (operador **):") + print(f" serie1 ** 2 = {(serie1 ** 2).tolist()}") + + # Con escalares + print("\n" + "-"*70) + print("\nOperaciones con escalares:") + print(f" serie1 * 10 = {(serie1 * 10).tolist()}") + print(f" serie1 + 100 = {(serie1 + 100).tolist()}") + + +def demostrar_operadores_comparacion(): + """ + Demuestra operadores de comparación sobrecargados. + """ + print("\n" + "="*70) + print("OPERADORES DE COMPARACIÓN SOBRECARGADOS") + print("="*70) + + serie = pd.Series([1, 2, 3, 4, 5]) + print(f"Serie: {serie.tolist()}") + + print("\n1. __eq__ (operador ==):") + print(f" serie == 3:\n{serie == 3}") + + print("\n2. __gt__ (operador >):") + print(f" serie > 3:\n{serie > 3}") + + print("\n3. __lt__ (operador <):") + print(f" serie < 3:\n{serie < 3}") + + print("\n4. __ge__ (operador >=):") + print(f" serie >= 3:\n{serie >= 3}") + + print("\n5. __le__ (operador <=):") + print(f" serie <= 3:\n{serie <= 3}") + + print("\n6. __ne__ (operador !=):") + print(f" serie != 3:\n{serie != 3}") + + # Filtrado booleano + print("\n" + "-"*70) + print("\nUso en filtrado booleano:") + filtro = serie > 3 + print(f" filtro = serie > 3: {filtro.tolist()}") + print(f" serie[filtro] = {serie[filtro].tolist()}") + + +def demostrar_contains(): + """ + Demuestra __contains__ (operador in). + """ + print("\n" + "="*70) + print("__contains__: Operador 'in'") + print("="*70) + + serie = pd.Series([10, 20, 30], index=['a', 'b', 'c']) + + print("Serie:") + print(serie) + + print("\n1. Verificar si un índice existe:") + print(f" 'a' in serie: {'a' in serie}") + print(f" 'd' in serie: {'d' in serie}") + + print("\n💡 'in' verifica el ÍNDICE, no los valores") + print(f" 10 in serie.values: {10 in serie.values}") + + print("\n" + "-"*70) + df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) + print("\nDataFrame:") + print(df) + + print("\n1. Verificar si una columna existe:") + print(f" 'A' in df: {'A' in df}") + print(f" 'C' in df: {'C' in df}") + + +def demostrar_call(): + """ + Demuestra métodos que actúan como callable. + """ + print("\n" + "="*70) + print("OBJETOS CALLABLE EN PANDAS") + print("="*70) + + serie = pd.Series([1, 2, 3, 4, 5]) + + print("Serie:") + print(serie) + + print("\n1. Usar apply con función lambda:") + print(" serie.apply(lambda x: x ** 2)") + resultado = serie.apply(lambda x: x ** 2) + print(f" {resultado.tolist()}") + + print("\n2. Usar map con función:") + def clasificar(x): + return 'bajo' if x < 3 else 'alto' + + print(" serie.map(clasificar)") + resultado = serie.map(clasificar) + print(f" {resultado.tolist()}") + + +def explorar_metodos_magicos_disponibles(): + """ + Explora todos los métodos mágicos disponibles en Series. + """ + print("\n" + "="*70) + print("MÉTODOS MÁGICOS DISPONIBLES EN SERIES") + print("="*70) + + serie = pd.Series([1, 2, 3]) + + metodos_magicos = [attr for attr in dir(serie) + if attr.startswith('__') and attr.endswith('__') + and not attr.startswith('___')] + + print(f"\nTotal de métodos mágicos: {len(metodos_magicos)}") + + # Categorizar métodos mágicos + categorias = { + 'Construcción': ['__init__', '__new__'], + 'Representación': ['__repr__', '__str__', '__format__'], + 'Acceso': ['__getitem__', '__setitem__', '__delitem__', '__contains__'], + 'Iteración': ['__iter__', '__next__', '__reversed__'], + 'Longitud': ['__len__', '__length_hint__'], + 'Aritméticos': ['__add__', '__sub__', '__mul__', '__truediv__', '__floordiv__', + '__mod__', '__pow__'], + 'Aritméticos (reverso)': ['__radd__', '__rsub__', '__rmul__', '__rtruediv__'], + 'Aritméticos (in-place)': ['__iadd__', '__isub__', '__imul__', '__itruediv__'], + 'Comparación': ['__eq__', '__ne__', '__lt__', '__le__', '__gt__', '__ge__'], + 'Booleanos': ['__bool__', '__nonzero__'], + 'Otros': ['__hash__', '__sizeof__', '__dir__', '__class__', '__doc__'] + } + + for categoria, metodos in categorias.items(): + disponibles = [m for m in metodos if m in metodos_magicos] + if disponibles: + print(f"\n{categoria}:") + for metodo in disponibles: + print(f" - {metodo}") + + +def ejemplo_practico_completo(): + """ + Ejemplo práctico usando varios métodos mágicos. + """ + print("\n" + "="*70) + print("EJEMPLO PRÁCTICO: Análisis de ventas") + print("="*70) + + ventas = pd.Series([100, 150, 200, 175, 225], + index=['Lun', 'Mar', 'Mie', 'Jue', 'Vie'], + name='ventas_diarias') + + print("Datos de ventas:") + print(ventas) + + # Usar múltiples métodos mágicos + print("\n1. Calcular promedio (usa __iter__, __len__):") + promedio = sum(ventas) / len(ventas) + print(f" Promedio: {promedio}") + + print("\n2. Encontrar días con ventas > 150 (usa __gt__):") + dias_altos = ventas[ventas > 150] + print(f" {dias_altos}") + + print("\n3. Aumentar todas las ventas 10% (usa __mul__):") + ventas_aumentadas = ventas * 1.10 + print(f" {ventas_aumentadas}") + + print("\n4. Verificar si hay datos del sábado (usa __contains__):") + tiene_sabado = 'Sab' in ventas + print(f" ¿Tiene datos del sábado? {tiene_sabado}") + + print("\n5. Acceder a venta específica (usa __getitem__):") + venta_miercoles = ventas['Mie'] + print(f" Venta del miércoles: {venta_miercoles}") + + print("\n💡 Todo esto usa métodos mágicos internamente!") + + +if __name__ == "__main__": + print("="*70) + print("MÓDULO 6: ATRIBUTOS ESPECIALES Y MÉTODOS MÁGICOS") + print("="*70) + + introduccion_metodos_magicos() + demostrar_getitem() + demostrar_setitem() + demostrar_len() + demostrar_repr_str() + demostrar_iter() + demostrar_operadores_aritmeticos() + demostrar_operadores_comparacion() + demostrar_contains() + demostrar_call() + explorar_metodos_magicos_disponibles() + ejemplo_practico_completo() + diff --git a/python-for-engineering/06_pandas/requirements.txt b/python-for-engineering/06_pandas/requirements.txt new file mode 100644 index 0000000..f60fb92 --- /dev/null +++ b/python-for-engineering/06_pandas/requirements.txt @@ -0,0 +1,3 @@ +pandas>=2.0.0 +python-dotenv>=1.0.0 + diff --git "a/python-for-engineering/7 Linear Regression/1 Introducci\303\263n a la regresi\303\263n lineal.ipynb" "b/python-for-engineering/07_linear_regression/1 Introducci\303\263n a la regresi\303\263n lineal.ipynb" similarity index 100% rename from "python-for-engineering/7 Linear Regression/1 Introducci\303\263n a la regresi\303\263n lineal.ipynb" rename to "python-for-engineering/07_linear_regression/1 Introducci\303\263n a la regresi\303\263n lineal.ipynb" diff --git a/python-for-engineering/7 Linear Regression/2 Regresion Minimos Cuadrados.ipynb b/python-for-engineering/07_linear_regression/2 Regresion Minimos Cuadrados.ipynb similarity index 100% rename from python-for-engineering/7 Linear Regression/2 Regresion Minimos Cuadrados.ipynb rename to python-for-engineering/07_linear_regression/2 Regresion Minimos Cuadrados.ipynb diff --git a/python-for-engineering/7 Linear Regression/3 Regresion lineal con SciPy.ipynb b/python-for-engineering/07_linear_regression/3 Regresion lineal con SciPy.ipynb similarity index 100% rename from python-for-engineering/7 Linear Regression/3 Regresion lineal con SciPy.ipynb rename to python-for-engineering/07_linear_regression/3 Regresion lineal con SciPy.ipynb diff --git a/python-for-engineering/7 Linear Regression/Images/curve1.jpg b/python-for-engineering/07_linear_regression/Images/curve1.jpg similarity index 100% rename from python-for-engineering/7 Linear Regression/Images/curve1.jpg rename to python-for-engineering/07_linear_regression/Images/curve1.jpg diff --git a/python-for-engineering/7 Linear Regression/Images/curve2.jpg b/python-for-engineering/07_linear_regression/Images/curve2.jpg similarity index 100% rename from python-for-engineering/7 Linear Regression/Images/curve2.jpg rename to python-for-engineering/07_linear_regression/Images/curve2.jpg diff --git a/python-for-engineering/7 Linear Regression/data.csv b/python-for-engineering/07_linear_regression/data.csv similarity index 100% rename from python-for-engineering/7 Linear Regression/data.csv rename to python-for-engineering/07_linear_regression/data.csv diff --git a/python-for-engineering/topics.txt b/python-for-engineering/topics.txt deleted file mode 100644 index e69de29..0000000