Calidad del Agua del Río Liberia con cálculo de Índice Holandés¶

Integrantes:¶

Johanna Rojas Conejo

Fernanda Salas Jara

Anny Guillén Watson

Descripción del Proyecto y justificación¶

En el presente documento se resume la aplicación de los conocimientos adquiridos en el curso "Python para ciencia de datos" impartido y apoyado con el esfuerzo de diversas instituciones como la Red de Ciencia de Datos para la Conservación de la biodiversidad Mesoamericana (Redbioma), Tecnológico de Costa Rica (TEC), Centro Internacional de Investigaciones para el Desarrollo (IDRC), Consejo Superior Universitario Centroamericano (CSUCA), Comisión para el Desarrollo Científico y Tecnológico de Centroamérica, Panamá y República Dominicana (CTCAP) y el Sistema de Integración Centroamericana (SICA).

De esta forma, el desarrollo del proyecto se enfoca en la demostración de procesamiento de datos para el desarrollo de gráficos y proyecciones en polígonos georreferenciados basándose en cálculo del Índice Holandés del Reglamento N°33903-MINAE-SALUD, mediante los datos recopilados durante 11 años en los muestreos y análisis fisico-químicos de la cuenca del Río Liberia, en el cantón de Liberia, Guanacaste. Esto con el fin de generar un producto que refleje la colaboración desde la ciencia de datos para la facilitación de la conversión de datos a información en el campo de la contaminación hídrica.

Antecedentes¶

Costa Rica presenta retos en materia de conservación de la biodiversidad, especialmente para asegurar la integridad ecológica de los cuerpos de agua, así como, el mantenimiento de los flujos de bienes y servicios ambientales a la población que a raíz de las presiones antrópicas se exacerban, generando consecuencias negativas que afectan particularmente a las poblaciones más vulnerables.

El río Liberia es una subcuenca del río Tempisque que abarca una extensión de 201.91 km2 y es esencial para diversas actividades, incluido el suministro de agua para consumo humano. Desde 2013, el HIDROCEC, un centro de investigación adscrito a la Universidad Nacional de Costa Rica, ha estado realizando un monitoreo exhaustivo de la calidad del agua en la zona media-alta. El monitoreo se conforma de ocho puntos de muestreo estratégicamente ubicados, que abarcan desde las zonas altas en el Parque Nacional Rincón de la Vieja hasta áreas urbanas en la ciudad de Liberia y puntos intermedios en la ruta nacional N°21.

Una forma de solventar las problemáticas ambientales es a partir de un monitoreo continuo del recurso hídrico que permita identificar, prevenir y revertir los procesos de degradación y contaminación, para dar soporte a la implementación y seguimiento de las políticas y programas nacionales. Para evaluar la calidad del agua, se aplican los indicadores establecidos en el Reglamento para la Evaluación y Clasificación de Cuerpos de Agua Superficial N°33903-MINAE-S, particularmente el apartado del Índice Holandés.

En este sentido, la ciencia de datos posibilita el monitoreo oportuno de la calidad del agua al generar información en tiempo real, lo cual resulta crucial para identificar riesgos para la salud y el medio ambiente (Discover Data Science, s.f.). Además, facilita el involucramiento de los ciudadanos a través de hackatones y talleres que proponen herramientas para abordar esta problemática, permitiendo su integración en sistemas de informes locales e internacionales (Warner et al., 2024).

El uso de Python ofrece grandes ventajas a los científicos interesados en aprender a programar para aplicarlo en sus estudios ambientales. Es un lenguaje de programación que se asemeja al lenguaje humano, lo que facilita la comprensión del software y reduce la complejidad de la curva de aprendizaje. Además, cuenta con una amplia gama de funciones, lo que permite utilizar paquetes para Sistemas de Información Geográfica (SIG), análisis matemáticos e inteligencia artificial (Oscar, 2017; Montoya, 2014).

Descripción del problema¶

El índice que se desea aplicar se basa en una clasificación que describe el nivel de contaminación presente en las condiciones y el tiempo en que se tómo una muestra determinada. Requiere conocer la concentración de DBO, N-NH3 y porcentaje de saturación; a partir del rango en el analito se encuentre se le otorgará un número del 1 al 5 a cada uno, la sumatoria de estos 3 valores brindará la clase, interpretación y color representativo según la calidad del agua de la cuenca.

El HIDROCEC desea conocer cómo se ha comportado históricamente la calidad del agua del Río Liberia en cada punto, sin embargo la base que recopila sus datos requiere de mantenimiento en las siguientes áreas:

  • Selección de columnas de los parámetros de interés
  • Eliminación de filas con datos nulos.
  • Homologar valores descriptivos.
  • Añadir columnas para el cálculo del índice.
  • Corrección en la lectura de tipo de datos.

Además se desea que el producto final indique:

  • Comportamiento de cada parámetro, es de especial interés visualizarlo a nivel histórico y por época o estación.
  • Conocer el valor del índice Holandés para cada punto.
  • Georreferenciar los puntos de la cuenca.

Objetivo¶

Desarrollar un proceso automatizado en Python que permita la preparación, estimación y visualización de los datos de variables fisicoquimicas muestreadas en la cuenca Río Liberia, para facilitar la comprensión del comportamiento histórico de la calidad del agua del rio.

Descripción del conjunto de datos a utilizar¶

Se utilizará un archivo en formato .csv llamado "RioLiberia.csv" que contiene, entre otras columnas que no se utilizarán, la siguiente información:

  • Punto: se refiere a los sitios donde se tomaron las muestras, se componen de 8 en total.
  • Fecha: la fecha puntual de la toma de cada muestra.
  • % O: Porcentaje disponible de oxígeno en el agua.
  • N-NH3 (mg/L): Nitrógeno amoniacal en unidades de miligramo por litro.
  • DBO 5 (mg/L): Demanda Bioquímica de Oxígeno en un ensayo durante 5 días en miligramo por litro. *Los últimos 3 parámetros serán utilizados para calcular el Índice Holandés (HIDROCEC, 2024).

Además se utilizará un archivo en formato .csv llamado "test[1]", que describe las coordenadas de cada punto de muestreo (HIDROCEC, 2024).

También se utilizará el archivo "gadm41_CRI_3_shp" que contiene diversos archivos en formato .cpg., .dbf, .prj, .shp, y .shx que contienen información que generará mapas de Costa Rica para ubicar los puntos de muestreo (GADM, s.f.).

Las bibliotecas de Python:

  • Pandas: Herramienta para análisis y manipulación de datos.
  • Numpy: maneja operaciones matemáticas y lógicas en matrices y arreglos.
  • Seaborn: visualización de datos en Python con gráficos estadísticos.
  • Scikit-learn: proporciona herramientas para Machine Learning y análisis de datos en Python.
  • Matplotlib: creación de gráficos y visualizaciones variadas.
  • Ydata-profiling: herramienta de Python que facilita el análisis exploratorio de datos.
  • Geopy: para trabajar con datos Geoespaciales.

ANALISIS DE DATOS DEL RIO LIBERIA¶

1. Instalación e importación de bibliotecas¶

In [ ]:
!pip install numpy
!pip install pandas
!pip install seaborn
!pip install scikit-learn
!pip install matplotlib
!pip install ydata_profiling

import numpy as np
import pandas as pd
import geopandas as gp
import seaborn as sns
from sklearn import datasets
from ydata_profiling import ProfileReport
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
%matplotlib inline


# Importar geopandas y geodatasets
import geopandas as gpd
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (1.25.2)
Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (2.0.3)
Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas) (2023.4)
Requirement already satisfied: tzdata>=2022.1 in /usr/local/lib/python3.10/dist-packages (from pandas) (2024.1)
Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/dist-packages (from pandas) (1.25.2)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)
Requirement already satisfied: seaborn in /usr/local/lib/python3.10/dist-packages (0.13.1)
Requirement already satisfied: numpy!=1.24.0,>=1.20 in /usr/local/lib/python3.10/dist-packages (from seaborn) (1.25.2)
Requirement already satisfied: pandas>=1.2 in /usr/local/lib/python3.10/dist-packages (from seaborn) (2.0.3)
Requirement already satisfied: matplotlib!=3.6.1,>=3.4 in /usr/local/lib/python3.10/dist-packages (from seaborn) (3.7.1)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.2.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (4.52.4)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.4.5)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (24.0)
Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (9.4.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (3.1.2)
Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.2->seaborn) (2023.4)
Requirement already satisfied: tzdata>=2022.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.2->seaborn) (2024.1)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn) (1.16.0)
Requirement already satisfied: scikit-learn in /usr/local/lib/python3.10/dist-packages (1.2.2)
Requirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (1.25.2)
Requirement already satisfied: scipy>=1.3.2 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (1.11.4)
Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (1.4.2)
Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (3.5.0)
Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (3.7.1)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.2.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (4.52.4)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.4.5)
Requirement already satisfied: numpy>=1.20 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.25.2)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (24.0)
Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (9.4.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (3.1.2)
Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (2.8.2)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)
Collecting ydata_profiling
  Downloading ydata_profiling-4.8.3-py2.py3-none-any.whl (359 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 359.5/359.5 kB 2.7 MB/s eta 0:00:00
Requirement already satisfied: scipy<1.14,>=1.4.1 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (1.11.4)
Requirement already satisfied: pandas!=1.4.0,<3,>1.1 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (2.0.3)
Requirement already satisfied: matplotlib<3.9,>=3.2 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (3.7.1)
Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (2.7.2)
Requirement already satisfied: PyYAML<6.1,>=5.0.0 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (6.0.1)
Requirement already satisfied: jinja2<3.2,>=2.11.1 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (3.1.4)
Collecting visions[type_image_path]<0.7.7,>=0.7.5 (from ydata_profiling)
  Downloading visions-0.7.6-py3-none-any.whl (104 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 104.8/104.8 kB 10.6 MB/s eta 0:00:00
Requirement already satisfied: numpy<2,>=1.16.0 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (1.25.2)
Collecting htmlmin==0.1.12 (from ydata_profiling)
  Downloading htmlmin-0.1.12.tar.gz (19 kB)
  Preparing metadata (setup.py) ... done
Collecting phik<0.13,>=0.11.1 (from ydata_profiling)
  Downloading phik-0.12.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (686 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 686.1/686.1 kB 14.2 MB/s eta 0:00:00
Requirement already satisfied: requests<3,>=2.24.0 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (2.31.0)
Requirement already satisfied: tqdm<5,>=4.48.2 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (4.66.4)
Requirement already satisfied: seaborn<0.14,>=0.10.1 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (0.13.1)
Collecting multimethod<2,>=1.4 (from ydata_profiling)
  Downloading multimethod-1.11.2-py3-none-any.whl (10 kB)
Requirement already satisfied: statsmodels<1,>=0.13.2 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (0.14.2)
Collecting typeguard<5,>=3 (from ydata_profiling)
  Downloading typeguard-4.3.0-py3-none-any.whl (35 kB)
Collecting imagehash==4.3.1 (from ydata_profiling)
  Downloading ImageHash-4.3.1-py2.py3-none-any.whl (296 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 296.5/296.5 kB 27.4 MB/s eta 0:00:00
Requirement already satisfied: wordcloud>=1.9.1 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (1.9.3)
Collecting dacite>=1.8 (from ydata_profiling)
  Downloading dacite-1.8.1-py3-none-any.whl (14 kB)
Requirement already satisfied: numba<1,>=0.56.0 in /usr/local/lib/python3.10/dist-packages (from ydata_profiling) (0.58.1)
Requirement already satisfied: PyWavelets in /usr/local/lib/python3.10/dist-packages (from imagehash==4.3.1->ydata_profiling) (1.6.0)
Requirement already satisfied: pillow in /usr/local/lib/python3.10/dist-packages (from imagehash==4.3.1->ydata_profiling) (9.4.0)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2<3.2,>=2.11.1->ydata_profiling) (2.1.5)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata_profiling) (1.2.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata_profiling) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata_profiling) (4.52.4)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata_profiling) (1.4.5)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata_profiling) (24.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata_profiling) (3.1.2)
Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib<3.9,>=3.2->ydata_profiling) (2.8.2)
Requirement already satisfied: llvmlite<0.42,>=0.41.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba<1,>=0.56.0->ydata_profiling) (0.41.1)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas!=1.4.0,<3,>1.1->ydata_profiling) (2023.4)
Requirement already satisfied: tzdata>=2022.1 in /usr/local/lib/python3.10/dist-packages (from pandas!=1.4.0,<3,>1.1->ydata_profiling) (2024.1)
Requirement already satisfied: joblib>=0.14.1 in /usr/local/lib/python3.10/dist-packages (from phik<0.13,>=0.11.1->ydata_profiling) (1.4.2)
Requirement already satisfied: annotated-types>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->ydata_profiling) (0.7.0)
Requirement already satisfied: pydantic-core==2.18.3 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->ydata_profiling) (2.18.3)
Requirement already satisfied: typing-extensions>=4.6.1 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->ydata_profiling) (4.12.0)
Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.24.0->ydata_profiling) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.24.0->ydata_profiling) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.24.0->ydata_profiling) (2.0.7)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests<3,>=2.24.0->ydata_profiling) (2024.2.2)
Requirement already satisfied: patsy>=0.5.6 in /usr/local/lib/python3.10/dist-packages (from statsmodels<1,>=0.13.2->ydata_profiling) (0.5.6)
Requirement already satisfied: attrs>=19.3.0 in /usr/local/lib/python3.10/dist-packages (from visions[type_image_path]<0.7.7,>=0.7.5->ydata_profiling) (23.2.0)
Requirement already satisfied: networkx>=2.4 in /usr/local/lib/python3.10/dist-packages (from visions[type_image_path]<0.7.7,>=0.7.5->ydata_profiling) (3.3)
Requirement already satisfied: six in /usr/local/lib/python3.10/dist-packages (from patsy>=0.5.6->statsmodels<1,>=0.13.2->ydata_profiling) (1.16.0)
Building wheels for collected packages: htmlmin
  Building wheel for htmlmin (setup.py) ... done
  Created wheel for htmlmin: filename=htmlmin-0.1.12-py3-none-any.whl size=27080 sha256=ff36c3ba7f241b7c30f5e6420b7138eabd083ce2eafb45f93ba9bb9823aebd63
  Stored in directory: /root/.cache/pip/wheels/dd/91/29/a79cecb328d01739e64017b6fb9a1ab9d8cb1853098ec5966d
Successfully built htmlmin
Installing collected packages: htmlmin, typeguard, multimethod, dacite, imagehash, visions, phik, ydata_profiling
Successfully installed dacite-1.8.1 htmlmin-0.1.12 imagehash-4.3.1 multimethod-1.11.2 phik-0.12.4 typeguard-4.3.0 visions-0.7.6 ydata_profiling-4.8.3

2. Importar datos de entrada¶

In [ ]:
#Se separa la delimitación o tabulación por ; y en utf-8
archivo = '/content/RiosLiberia.csv'
Rio = pd.read_csv(archivo, sep=';', encoding='utf-8')
display(Rio)
Punto pH CE (µs/cm)\t\t T (C°) OD (mg/L) % O Chlorophyll RFU\t\t Salinidad N-NH3 (mg/L) NO2 (mg/L) ... STD (mg/L) ST (mg/L) Turbidity FNU\t\t Sólidos sedimentables totales (mg/L) Sólidos suspendidos totales (mg/L) Coliformes Totales (NMP/100mL) Coliformes Fecales (NMP/100mL) Escherichia coli (NMP/100mL) Enterobacterias (NMP/100mL) Fecha
0 Pt1 NaN NaN NaN NaN 106.8 NaN NaN 0.082 NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN 1/3/2013
1 Pt2 NaN NaN NaN NaN 92.3 NaN NaN 0.034 NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN 1/3/2013
2 Pt3 NaN NaN NaN NaN 70.5 NaN NaN 0.95 NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN 1/3/2013
3 Pt4 NaN NaN NaN NaN 97.7 NaN NaN 0.78 NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN 1/3/2013
4 Pt5 NaN NaN NaN NaN 67.9 NaN NaN 5.586 NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN 1/3/2013
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
201 Pt4 7.00 170.0 26.94 5.01 85.0 NaN NaN 1.256 1.346 ... NaN NaN NaN NaN NaN 32550 NaN NaN NaN 19/9/2023
202 Pt5 7.30 165.0 27.14 6.52 82.0 NaN NaN 0.146 0.545 ... NaN NaN NaN NaN NaN 5650 NaN NaN NaN 19/9/2023
203 Pt6 7.20 342.0 27.61 4.71 171.0 NaN NaN 6.969 0.379 ... NaN NaN NaN NaN NaN 12100 NaN NaN NaN 19/9/2023
204 Pt7 7.21 358.0 26.55 3.03 179.0 NaN NaN 6.963 0.762 ... NaN NaN NaN NaN NaN 4100 NaN NaN NaN 19/9/2023
205 Puente Real 7.03 125.0 26.92 3.92 63.0 NaN NaN 0.148 ND ... NaN NaN NaN NaN NaN 38900 NaN NaN NaN 19/9/2023

206 rows × 26 columns

3. Analisis exploratorio de los datos¶

In [ ]:
#Informe
Profile = ProfileReport(Rio, title="Rio", explorative=True)

# Mostrar el informe en un notebook
Profile.to_notebook_iframe()
Rio.describe()
Summarize dataset:   0%|          | 0/5 [00:00<?, ?it/s]
Generate report structure:   0%|          | 0/1 [00:00<?, ?it/s]
Render HTML:   0%|          | 0/1 [00:00<?, ?it/s]
Out[ ]:
pH CE (µs/cm)\t\t T (C°) OD (mg/L) % O Chlorophyll RFU\t\t Salinidad DBO 5 (mg/L) DQO (mg/L) fDOM QSU\t\t fDOM RFU\t\t STD (mg/L) ST (mg/L) Turbidity FNU\t\t Sólidos sedimentables totales (mg/L) Sólidos suspendidos totales (mg/L) Enterobacterias (NMP/100mL)
count 128.000000 128.000000 121.000000 128.000000 174.000000 19.000000 28.000000 158.000000 1.000 12.00000 19.000000 68.000000 0.0 26.000000 0.0 7.000000 0.0
mean 7.221484 121.101953 25.399587 6.507109 82.516667 0.655995 0.041486 6.231418 5.601 104.50000 10.101642 58.466912 NaN 26.325662 NaN 9.385714 NaN
std 0.392071 87.456600 2.182249 2.154502 28.838934 1.476543 0.028386 10.519920 NaN 70.11873 13.076871 38.019095 NaN 31.992831 NaN 5.491032 NaN
min 5.430000 24.610000 20.020000 0.460000 6.200000 0.000000 0.010000 0.018000 5.601 4.00000 0.000000 5.000000 NaN 0.000000 NaN 3.400000 NaN
25% 7.037500 67.000000 24.600000 5.372500 67.000000 0.000000 0.020000 1.192500 5.601 69.50000 1.500000 33.407500 NaN 4.000000 NaN 5.525000 NaN
50% 7.245000 85.345000 25.340000 6.540000 84.050000 0.000000 0.040000 3.120000 5.601 93.00000 7.000000 43.160000 NaN 11.841950 NaN 8.150000 NaN
75% 7.420000 157.850000 26.600000 7.482500 95.125000 0.464900 0.040000 5.910000 5.601 119.00000 11.704250 82.865000 NaN 33.611150 NaN 11.925000 NaN
max 8.150000 460.100000 33.800000 16.860000 219.200000 6.000000 0.130000 66.000000 5.601 253.00000 47.385600 168.000000 NaN 104.000000 NaN 19.250000 NaN

4. Limpieza y modificación de datos¶

4.1. Observar los datos de entrada

In [ ]:
#Cantidad de valores únicos en cada columna
Rio.nunique()
Out[ ]:
Punto                                     8
pH                                       74
CE (µs/cm)\t\t                          115
T (C°)                                   77
OD (mg/L)                               107
% O                                     148
Chlorophyll RFU\t\t                      10
Salinidad                                 9
N-NH3 (mg/L)                            103
NO2 (mg/L)                                5
NO3 (mg/L)                               40
P-PO4-3 (mg/L)                           35
DBO 5 (mg/L)                            141
DQO (mg/L)                                1
fDOM QSU\t\t                             12
fDOM RFU\t\t                             13
STD (mg/L)                               59
ST (mg/L)                                 0
Turbidity FNU\t\t                        21
Sólidos sedimentables totales (mg/L)      0
Sólidos suspendidos totales (mg/L)        7
Coliformes Totales (NMP/100mL)           64
Coliformes Fecales (NMP/100mL)           82
Escherichia coli (NMP/100mL)             55
Enterobacterias (NMP/100mL)               0
Fecha                                    27
dtype: int64
In [ ]:
#Cantidad de valores nulos en cada columna
print(Rio.isnull().sum())
Punto                                     0
pH                                       78
CE (µs/cm)\t\t                           78
T (C°)                                   85
OD (mg/L)                                78
% O                                      32
Chlorophyll RFU\t\t                     187
Salinidad                               178
N-NH3 (mg/L)                             51
NO2 (mg/L)                              199
NO3 (mg/L)                              164
P-PO4-3 (mg/L)                          164
DBO 5 (mg/L)                             48
DQO (mg/L)                              205
fDOM QSU\t\t                            194
fDOM RFU\t\t                            187
STD (mg/L)                              138
ST (mg/L)                               206
Turbidity FNU\t\t                       180
Sólidos sedimentables totales (mg/L)    206
Sólidos suspendidos totales (mg/L)      199
Coliformes Totales (NMP/100mL)          131
Coliformes Fecales (NMP/100mL)          111
Escherichia coli (NMP/100mL)            139
Enterobacterias (NMP/100mL)             206
Fecha                                     0
dtype: int64
In [ ]:
#Tipos de datos, cantidad de columnas, datos no nulos
Rio.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 206 entries, 0 to 205
Data columns (total 26 columns):
 #   Column                                Non-Null Count  Dtype  
---  ------                                --------------  -----  
 0   Punto                                 206 non-null    object 
 1   pH                                    128 non-null    float64
 2   CE (µs/cm)		                          128 non-null    float64
 3   T (C°)                                121 non-null    float64
 4   OD (mg/L)                             128 non-null    float64
 5   % O                                   174 non-null    float64
 6   Chlorophyll RFU		                     19 non-null     float64
 7   Salinidad                             28 non-null     float64
 8   N-NH3 (mg/L)                          155 non-null    object 
 9   NO2 (mg/L)                            7 non-null      object 
 10  NO3 (mg/L)                            42 non-null     object 
 11  P-PO4-3 (mg/L)                        42 non-null     object 
 12  DBO 5 (mg/L)                          158 non-null    float64
 13  DQO (mg/L)                            1 non-null      float64
 14  fDOM QSU		                            12 non-null     float64
 15  fDOM RFU		                            19 non-null     float64
 16  STD (mg/L)                            68 non-null     float64
 17  ST (mg/L)                             0 non-null      float64
 18  Turbidity FNU		                       26 non-null     float64
 19  Sólidos sedimentables totales (mg/L)  0 non-null      float64
 20  Sólidos suspendidos totales (mg/L)    7 non-null      float64
 21  Coliformes Totales (NMP/100mL)        75 non-null     object 
 22  Coliformes Fecales (NMP/100mL)        95 non-null     object 
 23  Escherichia coli (NMP/100mL)          67 non-null     object 
 24  Enterobacterias (NMP/100mL)           0 non-null      float64
 25  Fecha                                 206 non-null    object 
dtypes: float64(17), object(9)
memory usage: 42.0+ KB
In [ ]:
# Selección de columnas de interés, que permitan determinar el índice holandes, de acuerdo a reglamento.
Riocolumnas = ['Punto', '% O', 'N-NH3 (mg/L)', 'DBO 5 (mg/L)','Fecha']
Riosred= Rio[Riocolumnas]

Riosred.head()
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha
0 Pt1 106.8 0.082 0.560 1/3/2013
1 Pt2 92.3 0.034 0.930 1/3/2013
2 Pt3 70.5 0.95 1.160 1/3/2013
3 Pt4 97.7 0.78 1.465 1/3/2013
4 Pt5 67.9 5.586 16.107 1/3/2013

4.2. Homologar nombres

In [ ]:
#Homologar nombres

Riosred['Punto'].unique()
Out[ ]:
array(['Pt1', 'Pt2', 'Pt3', 'Pt4', 'Pt5', 'Pt6', 'Pt7', 'Puente Real'],
      dtype=object)
In [ ]:
#Reemplazar nombre Puente Real para homogenizar los nombres de los puntos de muestreo.
new_df = Riosred.copy()
new_df['Punto'].replace({"Puente Real":"Pt8"}, inplace=True)
print(new_df['Punto'].value_counts())
Punto
Pt1    27
Pt2    27
Pt3    27
Pt4    27
Pt5    27
Pt6    27
Pt7    27
Pt8    17
Name: count, dtype: int64

4.3. Eliminar filas nulas

In [ ]:
#Eliminar filas nulas
df_sin_nulos = new_df.dropna(subset=['% O', 'N-NH3 (mg/L)', 'DBO 5 (mg/L)'])
print(df_sin_nulos)

#Comprobando eliminación de nulos
print(df_sin_nulos.isnull().sum())
    Punto    % O N-NH3 (mg/L)  DBO 5 (mg/L)      Fecha
0     Pt1  106.8        0.082         0.560   1/3/2013
1     Pt2   92.3        0.034         0.930   1/3/2013
2     Pt3   70.5         0.95         1.160   1/3/2013
3     Pt4   97.7         0.78         1.465   1/3/2013
4     Pt5   67.9        5.586        16.107   1/3/2013
..    ...    ...          ...           ...        ...
193   Pt4   86.4         0.07         3.400  12/4/2023
194   Pt5   78.9        0.133         3.530  12/4/2023
195   Pt6   72.0        5.698        11.510  12/4/2023
196   Pt7   25.6        0.665         2.910  12/4/2023
197   Pt8   52.9        0.031         3.920  12/4/2023

[148 rows x 5 columns]
Punto           0
% O             0
N-NH3 (mg/L)    0
DBO 5 (mg/L)    0
Fecha           0
dtype: int64

4.4. Cambiar los "No Detectables" (ND) por valor 0

In [ ]:
#Reemplazar valores ND (no detectable) por 0, para limpiar los datos y que se puedan analizar de mejor manera.
SinND = df_sin_nulos.copy()
SinND['N-NH3 (mg/L)'].replace({"ND": 0}, inplace=True)
print(SinND.value_counts())
Punto  % O    N-NH3 (mg/L)  DBO 5 (mg/L)  Fecha     
Pt1    58.4   0.086         0.478         13/6/2013     1
Pt5    125.0  0.26          5.080         29/7/2014     1
Pt6    45.9   0.27          1.200         24/10/2017    1
       47.9   1.34          6.130         8/9/2022      1
       51.8   0.085         11.710        13/6/2013     1
                                                       ..
Pt3    96.4   0.1           0.670         9/9/2020      1
       214.1  0.057         2.550         22/6/2022     1
Pt4    50.3   0.085         3.030         13/6/2013     1
       54.7   1.68          8.900         10/10/2013    1
Pt8    172.4  0.121         2.750         22/6/2022     1
Name: count, Length: 148, dtype: int64

4.5. Corrregir lectura de tipo de datos

In [ ]:
#Corroborar tipo de datos
SinND.dtypes
Out[ ]:
Punto            object
% O             float64
N-NH3 (mg/L)     object
DBO 5 (mg/L)    float64
Fecha            object
dtype: object
In [ ]:
Tipodedato = SinND.copy()
Tipodedato

#Cambiar Tipo de datos: Fecha

Tipodedato['Fecha'] = pd.to_datetime(Tipodedato['Fecha'], format= '%d/%m/%Y')
print("Tipo de dato modificado fecha:", Tipodedato['Fecha'].dtypes)

#Cambiar Tipo de datos: N-NH3 (mg/L)

Tipodedato['N-NH3 (mg/L)'] = Tipodedato['N-NH3 (mg/L)'].astype(float)
print("Tipo de dato modificado N-NH3:", Tipodedato['N-NH3 (mg/L)'].dtypes)
Tipo de dato modificado fecha: datetime64[ns]
Tipo de dato modificado N-NH3: float64

4.6. Agrupar valores por orden de Punto y Hora

In [ ]:
#Agrupar por Valores de Punto y Fecha; y tener un mejor orden y visualización

NewRio = Tipodedato.copy()

NRio = NewRio.groupby(['Punto','Fecha']).apply(lambda x: x.sort_values(by='Punto', ascending =False)).reset_index(drop=True)
print(NRio)
    Punto    % O  N-NH3 (mg/L)  DBO 5 (mg/L)      Fecha
0     Pt1  106.8         0.082         0.560 2013-03-01
1     Pt1   58.4         0.086         0.478 2013-06-13
2     Pt1  120.6         0.162         0.569 2013-10-10
3     Pt1  115.4         0.070         0.503 2013-11-21
4     Pt1  100.8         0.010         1.100 2014-02-04
..    ...    ...           ...           ...        ...
143   Pt8  172.4         0.121         2.750 2022-06-22
144   Pt8   66.5         0.035         4.860 2022-09-08
145   Pt8   63.0         0.011         1.830 2022-11-15
146   Pt8   76.3         0.036         2.250 2023-02-14
147   Pt8   52.9         0.031         3.920 2023-04-12

[148 rows x 5 columns]

4.7. Generar las columnas para el índice Holandés

In [ ]:
#Crear columnas vacías necesarias para determinar el indice holandés
NRio["VAmonio"] = None
NRio["VDBO"] = None
NRio["VOxigeno"] = None
NRio['Sumatoria'] = None
NRio['Clase'] = None
NRio['Codigo de color'] = None
NRio['Interpretacion de Calidad'] = None

NRio.head()
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad
0 Pt1 106.8 0.082 0.560 2013-03-01 None None None None None None None
1 Pt1 58.4 0.086 0.478 2013-06-13 None None None None None None None
2 Pt1 120.6 0.162 0.569 2013-10-10 None None None None None None None
3 Pt1 115.4 0.070 0.503 2013-11-21 None None None None None None None
4 Pt1 100.8 0.010 1.100 2014-02-04 None None None None None None None

5. Generación del Indice Holandés¶

5.1. Introducir valores: VAmonio, VDBO, VOxigeno

In [ ]:
#Insertar datos en VAmonio según el Reglamento
NRio['VAmonio'] = NRio['N-NH3 (mg/L)'].apply(
 lambda x: 1 if x < 0.500 else (
     2 if 0.500 <= x < 1.099 else (
      3 if 1.100 <= x < 2.099 else (
       4 if 2.100 <= x < 5.000 else 5
       )
      )
     )
    )

print("Valores únicos en la columna:", NRio['VAmonio'].unique())
NRio.head(10)
Valores únicos en la columna: [1 2 3 5 4]
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad
0 Pt1 106.8 0.082 0.560 2013-03-01 1 None None None None None None
1 Pt1 58.4 0.086 0.478 2013-06-13 1 None None None None None None
2 Pt1 120.6 0.162 0.569 2013-10-10 1 None None None None None None
3 Pt1 115.4 0.070 0.503 2013-11-21 1 None None None None None None
4 Pt1 100.8 0.010 1.100 2014-02-04 1 None None None None None None
5 Pt1 110.8 0.020 0.018 2014-07-29 1 None None None None None None
6 Pt1 85.2 0.260 2.823 2015-05-28 1 None None None None None None
7 Pt1 101.7 0.340 6.130 2017-04-25 1 None None None None None None
8 Pt1 70.8 0.240 0.830 2017-10-24 1 None None None None None None
9 Pt1 76.0 0.100 0.370 2020-07-30 1 None None None None None None
In [ ]:
#Insertar datos en VDBO según el Reglamento
NRio['VDBO'] = NRio['DBO 5 (mg/L)'].apply(
 lambda x: 1 if x <= 3.099 else (
     2 if 3.100 <= x < 6.099 else (
      3 if 6.100 <= x < 9.099 else (
       4 if 9.100 <= x < 15.000 else 5
       )
      )
     )
    )

print("Valores únicos en la columna:", NRio['VDBO'].unique())
NRio.head(10)
Valores únicos en la columna: [1 3 2 4 5]
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad
0 Pt1 106.8 0.082 0.560 2013-03-01 1 1 None None None None None
1 Pt1 58.4 0.086 0.478 2013-06-13 1 1 None None None None None
2 Pt1 120.6 0.162 0.569 2013-10-10 1 1 None None None None None
3 Pt1 115.4 0.070 0.503 2013-11-21 1 1 None None None None None
4 Pt1 100.8 0.010 1.100 2014-02-04 1 1 None None None None None
5 Pt1 110.8 0.020 0.018 2014-07-29 1 1 None None None None None
6 Pt1 85.2 0.260 2.823 2015-05-28 1 1 None None None None None
7 Pt1 101.7 0.340 6.130 2017-04-25 1 3 None None None None None
8 Pt1 70.8 0.240 0.830 2017-10-24 1 1 None None None None None
9 Pt1 76.0 0.100 0.370 2020-07-30 1 1 None None None None None
In [ ]:
#Insertar datos en VOxígeno según el Reglamento
NRio['VOxigeno'] = NRio['% O'].apply(
 lambda x: 5 if x <= 30.9 else(
        4 if 31.0 <= x < 50.9 else(
         3 if 51.0 <= x < 70.9 else (
          2 if 71.0 <= x < 90.9 else (
            1 if 91.0 <= x < 100.9 else (
                2 if 101.0 <= x < 120.9 else (
                    3 if 121.0 <= x < 130.0 else 5
                      )
                  )
               )
            )
         )
      )
    )

print("Valores únicos en la columna:", NRio['VOxigeno'].unique())
NRio.head(10)
Valores únicos en la columna: [2 3 1 5 4]
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad
0 Pt1 106.8 0.082 0.560 2013-03-01 1 1 2 None None None None
1 Pt1 58.4 0.086 0.478 2013-06-13 1 1 3 None None None None
2 Pt1 120.6 0.162 0.569 2013-10-10 1 1 2 None None None None
3 Pt1 115.4 0.070 0.503 2013-11-21 1 1 2 None None None None
4 Pt1 100.8 0.010 1.100 2014-02-04 1 1 1 None None None None
5 Pt1 110.8 0.020 0.018 2014-07-29 1 1 2 None None None None
6 Pt1 85.2 0.260 2.823 2015-05-28 1 1 2 None None None None
7 Pt1 101.7 0.340 6.130 2017-04-25 1 3 2 None None None None
8 Pt1 70.8 0.240 0.830 2017-10-24 1 1 3 None None None None
9 Pt1 76.0 0.100 0.370 2020-07-30 1 1 2 None None None None

5.2. Introducir valores: Sumatoria de cada valor asignado según el rango

In [ ]:
# Sumar los valores para completar la columna Sumatoria, que permita determinar estimar posteriormente si cumple con el reglamento de calidad de cuerpos de agua superficial.
NRio['Sumatoria'] = NRio['VAmonio'] + NRio['VDBO'] + NRio['VOxigeno']
NRio.head(10)
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad
0 Pt1 106.8 0.082 0.560 2013-03-01 1 1 2 4 None None None
1 Pt1 58.4 0.086 0.478 2013-06-13 1 1 3 5 None None None
2 Pt1 120.6 0.162 0.569 2013-10-10 1 1 2 4 None None None
3 Pt1 115.4 0.070 0.503 2013-11-21 1 1 2 4 None None None
4 Pt1 100.8 0.010 1.100 2014-02-04 1 1 1 3 None None None
5 Pt1 110.8 0.020 0.018 2014-07-29 1 1 2 4 None None None
6 Pt1 85.2 0.260 2.823 2015-05-28 1 1 2 4 None None None
7 Pt1 101.7 0.340 6.130 2017-04-25 1 3 2 6 None None None
8 Pt1 70.8 0.240 0.830 2017-10-24 1 1 3 5 None None None
9 Pt1 76.0 0.100 0.370 2020-07-30 1 1 2 4 None None None

5.3. Introducir valores: Clase

In [ ]:
#Asignar un Valor a la columna Clase, según la columna Sumatoria que se basa en el Reglamento de cuerpos de agua superficial
NRio['Clase'] = NRio['Sumatoria'].apply(
 lambda x: 1 if x <= 3 else(
        2 if 4 <= x <= 6 else(
         3 if 7 <= x <= 9 else (
          4 if 10 <= x <= 12 else 5
          )
        )
      )
     )

print("Valores únicos en la columna:", NRio['Clase'].unique())
NRio.head(20)
Valores únicos en la columna: [2 1 3 5 4]
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad
0 Pt1 106.8 0.082 0.560 2013-03-01 1 1 2 4 2 None None
1 Pt1 58.4 0.086 0.478 2013-06-13 1 1 3 5 2 None None
2 Pt1 120.6 0.162 0.569 2013-10-10 1 1 2 4 2 None None
3 Pt1 115.4 0.070 0.503 2013-11-21 1 1 2 4 2 None None
4 Pt1 100.8 0.010 1.100 2014-02-04 1 1 1 3 1 None None
5 Pt1 110.8 0.020 0.018 2014-07-29 1 1 2 4 2 None None
6 Pt1 85.2 0.260 2.823 2015-05-28 1 1 2 4 2 None None
7 Pt1 101.7 0.340 6.130 2017-04-25 1 3 2 6 2 None None
8 Pt1 70.8 0.240 0.830 2017-10-24 1 1 3 5 2 None None
9 Pt1 76.0 0.100 0.370 2020-07-30 1 1 2 4 2 None None
10 Pt1 103.1 0.100 0.260 2020-09-09 1 1 2 4 2 None None
11 Pt1 96.8 0.000 2.190 2021-02-02 1 1 1 3 1 None None
12 Pt1 103.1 0.100 1.000 2021-06-22 1 1 2 4 2 None None
13 Pt1 98.4 0.040 1.770 2021-10-27 1 1 1 3 1 None None
14 Pt1 100.6 0.040 4.060 2021-12-01 1 2 1 4 2 None None
15 Pt1 105.3 0.010 0.930 2022-03-23 1 1 2 4 2 None None
16 Pt1 219.2 0.019 3.190 2022-06-22 1 2 5 8 3 None None
17 Pt1 86.8 0.000 4.440 2022-09-08 1 2 2 5 2 None None
18 Pt1 68.4 0.000 1.550 2022-11-15 1 1 3 5 2 None None
19 Pt1 89.7 0.003 0.750 2023-02-14 1 1 2 4 2 None None

5.4. Introducir valores: Codigo de color

In [ ]:
#Asignar un Valor a la columna de Codigo de color, según Clase acorde a resultados del Reglamento de calidad de cuerpos de agua superficial.
NRio['Codigo de color'] = NRio['Clase'].apply(
 lambda x: "Azul" if x == 1 else(
        "Verde" if x == 2 else(
         "Amarillo" if x == 3 else (
          "Anaranjado" if x == 4 else "Rojo"
          )
        )
      )
     )

print("Valores únicos en la columna:", NRio['Codigo de color'].unique())
NRio.head(20)
Valores únicos en la columna: ['Verde' 'Azul' 'Amarillo' 'Rojo' 'Anaranjado']
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad
0 Pt1 106.8 0.082 0.560 2013-03-01 1 1 2 4 2 Verde None
1 Pt1 58.4 0.086 0.478 2013-06-13 1 1 3 5 2 Verde None
2 Pt1 120.6 0.162 0.569 2013-10-10 1 1 2 4 2 Verde None
3 Pt1 115.4 0.070 0.503 2013-11-21 1 1 2 4 2 Verde None
4 Pt1 100.8 0.010 1.100 2014-02-04 1 1 1 3 1 Azul None
5 Pt1 110.8 0.020 0.018 2014-07-29 1 1 2 4 2 Verde None
6 Pt1 85.2 0.260 2.823 2015-05-28 1 1 2 4 2 Verde None
7 Pt1 101.7 0.340 6.130 2017-04-25 1 3 2 6 2 Verde None
8 Pt1 70.8 0.240 0.830 2017-10-24 1 1 3 5 2 Verde None
9 Pt1 76.0 0.100 0.370 2020-07-30 1 1 2 4 2 Verde None
10 Pt1 103.1 0.100 0.260 2020-09-09 1 1 2 4 2 Verde None
11 Pt1 96.8 0.000 2.190 2021-02-02 1 1 1 3 1 Azul None
12 Pt1 103.1 0.100 1.000 2021-06-22 1 1 2 4 2 Verde None
13 Pt1 98.4 0.040 1.770 2021-10-27 1 1 1 3 1 Azul None
14 Pt1 100.6 0.040 4.060 2021-12-01 1 2 1 4 2 Verde None
15 Pt1 105.3 0.010 0.930 2022-03-23 1 1 2 4 2 Verde None
16 Pt1 219.2 0.019 3.190 2022-06-22 1 2 5 8 3 Amarillo None
17 Pt1 86.8 0.000 4.440 2022-09-08 1 2 2 5 2 Verde None
18 Pt1 68.4 0.000 1.550 2022-11-15 1 1 3 5 2 Verde None
19 Pt1 89.7 0.003 0.750 2023-02-14 1 1 2 4 2 Verde None

Introducir valores: Interpretación de calidad

In [ ]:
#Asignar un Valor a Interpretacion de Calidad, según Clase acorde al Reglamento
NRio['Interpretacion de Calidad'] = NRio['Clase'].apply(
 lambda x: "Sin Contaminación" if x == 1 else(
        "Contaminación incipiente" if x == 2 else(
         "Contaminación moderada" if x == 3 else (
          "Contaminación severa" if x == 4 else "Contaminación muy severa"
          )
        )
      )
     )

print("Valores únicos en la columna:", NRio['Interpretacion de Calidad'].unique())
NRio.head(20)
Valores únicos en la columna: ['Contaminación incipiente' 'Sin Contaminación' 'Contaminación moderada'
 'Contaminación muy severa' 'Contaminación severa']
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad
0 Pt1 106.8 0.082 0.560 2013-03-01 1 1 2 4 2 Verde Contaminación incipiente
1 Pt1 58.4 0.086 0.478 2013-06-13 1 1 3 5 2 Verde Contaminación incipiente
2 Pt1 120.6 0.162 0.569 2013-10-10 1 1 2 4 2 Verde Contaminación incipiente
3 Pt1 115.4 0.070 0.503 2013-11-21 1 1 2 4 2 Verde Contaminación incipiente
4 Pt1 100.8 0.010 1.100 2014-02-04 1 1 1 3 1 Azul Sin Contaminación
5 Pt1 110.8 0.020 0.018 2014-07-29 1 1 2 4 2 Verde Contaminación incipiente
6 Pt1 85.2 0.260 2.823 2015-05-28 1 1 2 4 2 Verde Contaminación incipiente
7 Pt1 101.7 0.340 6.130 2017-04-25 1 3 2 6 2 Verde Contaminación incipiente
8 Pt1 70.8 0.240 0.830 2017-10-24 1 1 3 5 2 Verde Contaminación incipiente
9 Pt1 76.0 0.100 0.370 2020-07-30 1 1 2 4 2 Verde Contaminación incipiente
10 Pt1 103.1 0.100 0.260 2020-09-09 1 1 2 4 2 Verde Contaminación incipiente
11 Pt1 96.8 0.000 2.190 2021-02-02 1 1 1 3 1 Azul Sin Contaminación
12 Pt1 103.1 0.100 1.000 2021-06-22 1 1 2 4 2 Verde Contaminación incipiente
13 Pt1 98.4 0.040 1.770 2021-10-27 1 1 1 3 1 Azul Sin Contaminación
14 Pt1 100.6 0.040 4.060 2021-12-01 1 2 1 4 2 Verde Contaminación incipiente
15 Pt1 105.3 0.010 0.930 2022-03-23 1 1 2 4 2 Verde Contaminación incipiente
16 Pt1 219.2 0.019 3.190 2022-06-22 1 2 5 8 3 Amarillo Contaminación moderada
17 Pt1 86.8 0.000 4.440 2022-09-08 1 2 2 5 2 Verde Contaminación incipiente
18 Pt1 68.4 0.000 1.550 2022-11-15 1 1 3 5 2 Verde Contaminación incipiente
19 Pt1 89.7 0.003 0.750 2023-02-14 1 1 2 4 2 Verde Contaminación incipiente

6. Analisis de datos¶

6.1. Profile con los nuevos valores

In [ ]:
# Creamos el informe con pandas-profiling
profile = ProfileReport(NRio, title="NRio", explorative=True)

# Mostrar el informe en un notebook
profile.to_notebook_iframe()
Summarize dataset:   0%|          | 0/5 [00:00<?, ?it/s]
Generate report structure:   0%|          | 0/1 [00:00<?, ?it/s]
Render HTML:   0%|          | 0/1 [00:00<?, ?it/s]

7. Gráficos¶

7.1. Distribución y tendencia central de DBO, Nitrógeno Amoniacal y Porcentaje de saturación

In [ ]:
fig, axes = plt.subplots(1, 3, figsize=(21,7))

# Boxplot para Valores de DBO
sns.boxplot(x="Punto", y="DBO 5 (mg/L)", data=NRio, ax=axes[0])
axes[0].set_title('Boxplot del comportamiento de DBO por punto')

#Boxplot para valores de N-NH3
sns.boxplot(x="Punto", y="N-NH3 (mg/L)", data=NRio, ax=axes[1])
axes[1].set_title('Boxplot del comportamiento de N-NH3 por punto')

#Boxplot para valores de Porcentaje de Oxígeno
sns.boxplot(x="Punto", y="% O", data=NRio, ax=axes[2])
axes[2].set_title('Boxplot del comportamiento de Porcentaje de saturación de Oxígeno por punto')

plt.tight_layout() #
plt.show()
No description has been provided for this image

7.2. Creación de histogramas para observar la distribución de todos los parámetros

In [ ]:
# Crear histogramas para todas las columnas numéricas del dataframe NRio
NRio.hist(edgecolor='black', linewidth=0.5, figsize=(12, 8))

plt.show()
No description has been provided for this image

7.3 Diagrama de dispersión para observar la clase que obtuvo cada punto, durante los 11 años de monitoreo.

In [ ]:
# Crear el gráfico
plt.figure(figsize=(10, 6))
sns.scatterplot(x='Fecha', y='Clase', hue='Punto', data=NRio, s=100, palette='deep')

# Personalizar el gráfico
plt.xlabel('Fecha')
plt.ylabel('Clase')
plt.title('Gráfico de Fecha vs Clase')
plt.legend(title='Punto', loc='best')
plt.ylim(0.5,6)

# Mostrar el gráfico
plt.show()
No description has been provided for this image

7.4 Inserción de una nueva columna para determinar Epoca del año

In [ ]:
# Columna Epoca

NRio["Epoca"] = None

#Definir la función para determinar la época
def determinar_epoca(fecha):
    if (fecha.month == 1) or (fecha.month == 2) or (fecha.month == 3) or (fecha.month == 4 and fecha.day <= 15):
        return 'Seca'
    elif (fecha.month == 4 and fecha.day > 15) or (fecha.month == 5 and fecha.day <= 15):
        return 'Seca-Lluviosa'
    elif (fecha.month == 5 and fecha.day > 15) or (fecha.month in [6, 7, 8, 9, 10] and fecha.day <= 31):
        return 'Lluviosa'
    elif (fecha.month == 11) or (fecha.month == 12):
        return 'Lluviosa-Seca'

# Aplicar la función a la columna 'Fecha'
NRio['Epoca'] = NRio['Fecha'].apply(determinar_epoca)

# Mostrar los primeros registros para verificar
print(NRio.head())
  Punto    % O  N-NH3 (mg/L)  DBO 5 (mg/L)      Fecha  VAmonio  VDBO  \
0   Pt1  106.8         0.082         0.560 2013-03-01        1     1   
1   Pt1   58.4         0.086         0.478 2013-06-13        1     1   
2   Pt1  120.6         0.162         0.569 2013-10-10        1     1   
3   Pt1  115.4         0.070         0.503 2013-11-21        1     1   
4   Pt1  100.8         0.010         1.100 2014-02-04        1     1   

   VOxigeno  Sumatoria  Clase Codigo de color Interpretacion de Calidad  \
0         2          4      2           Verde  Contaminación incipiente   
1         3          5      2           Verde  Contaminación incipiente   
2         2          4      2           Verde  Contaminación incipiente   
3         2          4      2           Verde  Contaminación incipiente   
4         1          3      1            Azul         Sin Contaminación   

           Epoca  
0           Seca  
1       Lluviosa  
2       Lluviosa  
3  Lluviosa-Seca  
4           Seca  

7.5. Selección de datos por Punto

In [ ]:
# Seleccionando datos por condicion (Punto=Pt1)
condicion1 = NRio["Punto"] == 'Pt1'
registros_pt1 = NRio[condicion1]
registros_pt1

# Seleccionando datos por condicion (Punto=Pt2)
condicion2 = NRio["Punto"] == 'Pt2'
registros_pt2 = NRio[condicion2]
registros_pt2

# Seleccionando datos por condicion (Punto=Pt3)
condicion3 = NRio["Punto"] == 'Pt3'
registros_pt3 = NRio[condicion3]
registros_pt3

# Seleccionando datos por condicion (Punto=Pt4)
condicion4 = NRio["Punto"] == 'Pt4'
registros_pt4 = NRio[condicion4]
registros_pt4

# Seleccionando datos por condicion (Punto=Pt5)
condicion5 = NRio["Punto"] == 'Pt5'
registros_pt5 = NRio[condicion5]
registros_pt5

# Seleccionando datos por condicion (Punto=Pt6)
condicion6 = NRio["Punto"] == 'Pt6'
registros_pt6 = NRio[condicion6]
registros_pt6

# Seleccionando datos por condicion (Punto=Pt7)
condicion7 = NRio["Punto"] == 'Pt7'
registros_pt7 = NRio[condicion7]
registros_pt7

# Seleccionando datos por condicion (Punto=Pt8)
condicion8 = NRio["Punto"] == 'Pt8'
registros_pt8 = NRio[condicion8]
registros_pt8
Out[ ]:
Punto % O N-NH3 (mg/L) DBO 5 (mg/L) Fecha VAmonio VDBO VOxigeno Sumatoria Clase Codigo de color Interpretacion de Calidad Epoca
136 Pt8 95.8 0.100 1.85 2020-09-09 1 1 1 3 1 Azul Sin Contaminación Lluviosa
137 Pt8 75.5 0.000 3.03 2021-02-02 1 1 2 4 2 Verde Contaminación incipiente Seca
138 Pt8 61.9 0.100 2.00 2021-06-22 1 1 3 5 2 Verde Contaminación incipiente Lluviosa
139 Pt8 24.7 0.050 6.00 2021-08-27 1 2 5 8 3 Amarillo Contaminación moderada Lluviosa
140 Pt8 87.6 0.040 3.61 2021-10-27 1 2 2 5 2 Verde Contaminación incipiente Lluviosa
141 Pt8 89.3 0.040 4.54 2021-12-01 1 2 2 5 2 Verde Contaminación incipiente Lluviosa-Seca
142 Pt8 79.7 0.010 7.22 2022-03-23 1 3 2 6 2 Verde Contaminación incipiente Seca
143 Pt8 172.4 0.121 2.75 2022-06-22 1 1 5 7 3 Amarillo Contaminación moderada Lluviosa
144 Pt8 66.5 0.035 4.86 2022-09-08 1 2 3 6 2 Verde Contaminación incipiente Lluviosa
145 Pt8 63.0 0.011 1.83 2022-11-15 1 1 3 5 2 Verde Contaminación incipiente Lluviosa-Seca
146 Pt8 76.3 0.036 2.25 2023-02-14 1 1 2 4 2 Verde Contaminación incipiente Seca
147 Pt8 52.9 0.031 3.92 2023-04-12 1 2 3 6 2 Verde Contaminación incipiente Seca

7.6 Gráfico de barras de clase por punto, según época del año

In [ ]:
#Grafico de Barras para cada punto

clasifpt1 = registros_pt1.groupby('Epoca')['Clase'].mean()
clasifpt2 = registros_pt2.groupby('Epoca')['Clase'].mean()
clasifpt3 = registros_pt3.groupby('Epoca')['Clase'].mean()
clasifpt4 = registros_pt4.groupby('Epoca')['Clase'].mean()
clasifpt5 = registros_pt5.groupby('Epoca')['Clase'].mean()
clasifpt6 = registros_pt6.groupby('Epoca')['Clase'].mean()
clasifpt7 = registros_pt7.groupby('Epoca')['Clase'].mean()
clasifpt8 = registros_pt8.groupby('Epoca')['Clase'].mean()

# Crear figura y ejes para dos subplots
fig, axes = plt.subplots(2, 4, figsize=(12, 6))

# Gráfico de barras para registros_pt1
clasifpt1.plot(kind='bar', ax=axes[0,0])
axes[0,0].set_title('Clasificación Punto 1,San María')
axes[0,0].set_xlabel("Epoca")
axes[0,0].set_ylabel('Clase')
axes[0,0].set_xticks(range(len(clasifpt1)))
axes[0,0].set_xticklabels(clasifpt1.index, rotation=45)
axes[0,0].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt2
clasifpt2.plot(kind='bar', ax=axes[0,1])
axes[0,1].set_title('Clasificación Punto 2, El Yugo')
axes[0,1].set_xlabel("Epoca")
axes[0,1].set_ylabel('Clase')
axes[0,1].set_xticks(range(len(clasifpt2)))
axes[0,1].set_xticklabels(clasifpt2.index, rotation=45)
axes[0,1].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt3
clasifpt3.plot(kind='bar', ax=axes[0,2])
axes[0,2].set_title('Clasificación Punto 3, Saca de Agua')
axes[0,2].set_xlabel("Epoca")
axes[0,2].set_ylabel('Clase')
axes[0,2].set_xticks(range(len(clasifpt3)))
axes[0,2].set_xticklabels(clasifpt3.index, rotation=45)
axes[0,2].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt4
clasifpt4.plot(kind='bar', ax=axes[0,3])
axes[0,3].set_title('Clasificación Punto 4, Condega')
axes[0,3].set_xlabel("Epoca")
axes[0,3].set_ylabel('Clase')
axes[0,3].set_xticks(range(len(clasifpt4)))
axes[0,3].set_xticklabels(clasifpt4.index, rotation=45)
axes[0,3].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt5
clasifpt5.plot(kind='bar', ax=axes[1,0])
axes[1,0].set_title('Clasificación Punto 5,Capulin')
axes[1,0].set_xlabel("Epoca")
axes[1,0].set_ylabel('Clase')
axes[1,0].set_xticks(range(len(clasifpt5)))
axes[1,0].set_xticklabels(clasifpt5.index, rotation=45)
axes[1,0].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt6
clasifpt6.plot(kind='bar', ax=axes[1,1])
axes[1,1].set_title('Clasificación Punto 6, PTAR')
axes[1,1].set_xlabel("Epoca")
axes[1,1].set_ylabel('Clase')
axes[1,1].set_xticks(range(len(clasifpt6)))
axes[1,1].set_xticklabels(clasifpt6.index, rotation=45)
axes[1,1].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt7
clasifpt7.plot(kind='bar', ax=axes[1,2])
axes[1,2].set_title('Clasificación Punto 7,Aforo')
axes[1,2].set_xlabel("Epoca")
axes[1,2].set_ylabel('Clase')
axes[1,2].set_xticks(range(len(clasifpt7)))
axes[1,2].set_xticklabels(clasifpt7.index, rotation=45)
axes[1,2].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt8
clasifpt8.plot(kind='bar', ax=axes[1,3])
axes[1,3].set_title('Clasificación Punto 8, Puente Real')
axes[1,3].set_xlabel("Epoca")
axes[1,3].set_ylabel('Clase')
axes[1,3].set_xticks(range(len(clasifpt8)))
axes[1,3].set_xticklabels(clasifpt8.index, rotation=45)
axes[1,3].set_ylim(0.5, 6)

# Ajustar el layout para que no haya solapamiento
plt.tight_layout()
plt.show()
No description has been provided for this image

7.7 Grafico de barras de interpretación de calidad por punto

In [ ]:
# Agrupar datos por especie y obtener el promedio de la columna sepal length
clasifpt1 = registros_pt1.groupby('Interpretacion de Calidad')['Clase'].mean()
clasifpt2 = registros_pt2.groupby('Interpretacion de Calidad')['Clase'].mean()
clasifpt3 = registros_pt3.groupby('Interpretacion de Calidad')['Clase'].mean()
clasifpt4 = registros_pt4.groupby('Interpretacion de Calidad')['Clase'].mean()
clasifpt5 = registros_pt5.groupby('Interpretacion de Calidad')['Clase'].mean()
clasifpt6 = registros_pt6.groupby('Interpretacion de Calidad')['Clase'].mean()
clasifpt7 = registros_pt7.groupby('Interpretacion de Calidad')['Clase'].mean()
clasifpt8 = registros_pt8.groupby('Interpretacion de Calidad')['Clase'].mean()

# Crear figura y ejes para dos subplots
fig, axes = plt.subplots(2, 4, figsize=(12, 6))

# Gráfico de barras para registros_pt1
clasifpt1.plot(kind='bar', ax=axes[0,0])
axes[0,0].set_title('Calidad hidrica Punto 1')
axes[0,0].set_xlabel("Interpretacion de Calidad")
axes[0,0].set_ylabel('Clase')
axes[0,0].set_xticks(range(len(clasifpt1)))
axes[0,0].set_xticklabels(clasifpt1.index, rotation=50)
axes[0,0].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt2
clasifpt2.plot(kind='bar', ax=axes[0,1])
axes[0,1].set_title('Calidad hidrica Punto 2')
axes[0,1].set_xlabel("Interpretacion de Calidad")
axes[0,1].set_ylabel('Clase')
axes[0,1].set_xticks(range(len(clasifpt2)))
axes[0,1].set_xticklabels(clasifpt2.index, rotation=50)
axes[0,1].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt3
clasifpt3.plot(kind='bar', ax=axes[0,2])
axes[0,2].set_title('Calidad hidrica Punto 3')
axes[0,2].set_xlabel("Interpretacion de Calidad")
axes[0,2].set_ylabel('Clase')
axes[0,2].set_xticks(range(len(clasifpt3)))
axes[0,2].set_xticklabels(clasifpt3.index, rotation=50)
axes[0,2].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt4
clasifpt4.plot(kind='bar', ax=axes[0,3])
axes[0,3].set_title('Calidad hidrica Punto 4')
axes[0,3].set_xlabel("Interpretacion de Calidad")
axes[0,3].set_ylabel('Clase')
axes[0,3].set_xticks(range(len(clasifpt4)))
axes[0,3].set_xticklabels(clasifpt4.index, rotation=50)
axes[0,3].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt5
clasifpt5.plot(kind='bar', ax=axes[1,0])
axes[1,0].set_title('Calidad hidrica Punto 5')
axes[1,0].set_xlabel("Interpretacion de Calidad")
axes[1,0].set_ylabel('Clase')
axes[1,0].set_xticks(range(len(clasifpt5)))
axes[1,0].set_xticklabels(clasifpt5.index, rotation=50)
axes[1,0].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt6
clasifpt6.plot(kind='bar', ax=axes[1,1])
axes[1,1].set_title('Calidad hidrica Punto 6')
axes[1,1].set_xlabel("Interpretacion de Calidad")
axes[1,1].set_ylabel('Clase')
axes[1,1].set_xticks(range(len(clasifpt6)))
axes[1,1].set_xticklabels(clasifpt6.index, rotation=50)
axes[1,1].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt7
clasifpt7.plot(kind='bar', ax=axes[1,2])
axes[1,2].set_title('Calidad hidrica Punto 7')
axes[1,2].set_xlabel("Interpretacion de Calidad")
axes[1,2].set_ylabel('Clase')
axes[1,2].set_xticks(range(len(clasifpt7)))
axes[1,2].set_xticklabels(clasifpt7.index, rotation=50)
axes[1,2].set_ylim(0.5, 6)

# Gráfico de barras para registros_pt8
clasifpt8.plot(kind='bar', ax=axes[1,3])
axes[1,3].set_title('Calidad hidrica Punto 8')
axes[1,3].set_xlabel("Interpretacion de Calidad")
axes[1,3].set_ylabel('Clase')
axes[1,3].set_xticks(range(len(clasifpt8)))
axes[1,3].set_xticklabels(clasifpt8.index, rotation=50)
axes[1,3].set_ylim(0.5, 6)

# Ajustar el layout para que no haya solapamiento
plt.tight_layout()
plt.show()
No description has been provided for this image

7.8. Gráfico de líneas del comportamiento en el tiempo de la clase de cada punto

In [ ]:
fig, axes = plt.subplots(2, 4, figsize=(21, 14))  # 2 filas, 4 columnas

# Lista de registros y títulos
registros = [registros_pt1, registros_pt2, registros_pt3, registros_pt4, registros_pt5, registros_pt6, registros_pt7, registros_pt8]
titulos = [
    'Gráfico de líneas de Fecha vs. Clase para Punto 1 Santa María',
    'Gráfico de líneas de Fecha vs. Clase para Punto 2 El Yugo',
    'Gráfico de líneas de Fecha vs. Clase para Punto 3 Saca de Agua',
    'Gráfico de líneas de Fecha vs. Clase para Punto 4 Condega',
    'Gráfico de líneas de Fecha vs. Clase para Punto 5 El Capulín',
    'Gráfico de líneas de Fecha vs. Clase para Punto 6 PTAR',
    'Gráfico de líneas de Fecha vs. Clase para Punto 7 Aforo',
    'Gráfico de líneas de Fecha vs. Clase para Punto 8 Puente Real'
]

# Crear cada subplot
for i, ax in enumerate(axes.flatten()):
    ax.plot(registros[i]['Fecha'], registros[i]['Clase'], marker='o', linestyle='-')
    ax.set_title(titulos[i])
    ax.set_xlabel('Fecha')
    ax.set_ylabel('Clase')
    ax.set_ylim(0.5, 5.5)

# Ajustar los espacios entre los subplots
plt.tight_layout()

# Mostrar los gráficos
plt.show()
No description has been provided for this image

8. Georeferenciación¶

8.1 Leer archivo de mapa de Costa Rica

In [ ]:
cr_mapa = gpd.read_file('gadm41_CRI_3.shp')

# Obtener el área, límite y centroide por provincia
cr_mapa["centroid"] = cr_mapa.centroid

cr_mapa
Out[ ]:
geometry centroid
0 POLYGON ((-84.18735 10.05284, -84.18024 10.052... POINT (-84.20609 10.02408)
1 POLYGON ((-84.18024 10.05246, -84.18735 10.052... POINT (-84.17419 10.08557)
2 POLYGON ((-84.20731 10.00834, -84.20686 10.008... POINT (-84.18495 10.02750)
3 POLYGON ((-84.27063 9.97580, -84.27134 9.97544... POINT (-84.30354 9.98748)
4 POLYGON ((-84.21308 9.99937, -84.21285 9.99892... POINT (-84.26058 9.95930)
... ... ...
481 POLYGON ((-83.88311 9.97049, -83.88330 9.97049... POINT (-83.92888 10.03244)
482 POLYGON ((-84.02831 10.06006, -84.02830 10.082... POINT (-83.97975 10.08888)
483 POLYGON ((-84.02446 9.96711, -84.02461 9.96712... POINT (-84.02234 9.97465)
484 POLYGON ((-83.99859 9.96849, -83.99884 9.96852... POINT (-83.99224 9.97255)
485 POLYGON ((-83.90474 9.97574, -83.90556 9.97540... POINT (-83.95146 9.97853)

486 rows × 2 columns

In [ ]:
cr_mapa.plot(figsize = (8,8), edgecolor="w", linewidth=0.3)
Out[ ]:
<Axes: >
No description has been provided for this image

8.2. Obtener un distrito con Geopy

In [ ]:
# !pip install geopy
from geopy.geocoders import Nominatim

# Initialize Nominatim API
geolocator = Nominatim(user_agent="geoPandas")

ubicaciones = {
    "liberia": "10.626442159158902, -85.44803406302779",
}
# Get location with geocode
location = geolocator.geocode(ubicaciones["liberia"])

print("Dirección completa:", location[0])
print("Longitud y Latitud:", location[1])

print("\nNivel administrativo 1:", location[0].split(',')[-5][1:])
Dirección completa: Walmart, Calle 26, El Capulín, Liberia, Cantón Liberia, Provincia Guanacaste, 50101, Costa Rica
Longitud y Latitud: (10.6266216, -85.44820014915676)

Nivel administrativo 1: Liberia

8.3.

In [ ]:
# Import module
from geopy.geocoders import Nominatim

def get_prov(p):
    global geolocator
    location = geolocator.geocode(f"{p.y}, {p.x}")
    return location[0].split(",")[-3][11:]

def get_canton(p):
    global geolocator
    location = geolocator.geocode(f"{p.y}, {p.x}")
    return location[0].split(',')[-4][7:]

def get_distrito(p):
    global geolocator
    location = geolocator.geocode(f"{p.y}, {p.x}")
    return location[0].split(',')[-5][1:]

# Initialize Nominatim API
geolocator = Nominatim(user_agent="geoPandas")

8.4. Buscar centroid por distrito

In [ ]:
cr_mapa["Distrito"] = cr_mapa.centroid.map(lambda p: get_distrito(p))
cr_mapa
WARNING:urllib3.connectionpool:Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='nominatim.openstreetmap.org', port=443): Read timed out. (read timeout=1)")': /search?q=10.398246425960604%2C+-84.38437575861356&format=json&limit=1
WARNING:urllib3.connectionpool:Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='nominatim.openstreetmap.org', port=443): Read timed out. (read timeout=1)")': /search?q=10.564728151068248%2C+-84.63249835859824&format=json&limit=1
WARNING:urllib3.connectionpool:Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='nominatim.openstreetmap.org', port=443): Read timed out. (read timeout=1)")': /search?q=10.326186578000762%2C+-84.41256656330046&format=json&limit=1
WARNING:urllib3.connectionpool:Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='nominatim.openstreetmap.org', port=443): Read timed out. (read timeout=1)")': /search?q=10.566035139229172%2C+-84.76453080745506&format=json&limit=1
WARNING:urllib3.connectionpool:Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='nominatim.openstreetmap.org', port=443): Read timed out. (read timeout=1)")': /search?q=9.965588587303277%2C+-84.47656791978665&format=json&limit=1
WARNING:urllib3.connectionpool:Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='nominatim.openstreetmap.org', port=443): Read timed out. (read timeout=1)")': /search?q=9.92639462174031%2C+-83.99251996588586&format=json&limit=1
WARNING:urllib3.connectionpool:Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='nominatim.openstreetmap.org', port=443): Read timed out. (read timeout=1)")': /search?q=9.935076431571288%2C+-84.10726779453026&format=json&limit=1
Out[ ]:
geometry centroid Distrito
0 POLYGON ((-84.18735 10.05284, -84.18024 10.052... POINT (-84.20609 10.02408) Alajuela
1 POLYGON ((-84.18024 10.05246, -84.18735 10.052... POINT (-84.17419 10.08557) Carrizal
2 POLYGON ((-84.20731 10.00834, -84.20686 10.008... POINT (-84.18495 10.02750) Desamparados
3 POLYGON ((-84.27063 9.97580, -84.27134 9.97544... POINT (-84.30354 9.98748) Garita
4 POLYGON ((-84.21308 9.99937, -84.21285 9.99892... POINT (-84.26058 9.95930) Guácima
... ... ... ...
481 POLYGON ((-83.88311 9.97049, -83.88330 9.97049... POINT (-83.92888 10.03244) Cascajal
482 POLYGON ((-84.02831 10.06006, -84.02830 10.082... POINT (-83.97975 10.08888) Dulce Nombre de Jesús
483 POLYGON ((-84.02446 9.96711, -84.02461 9.96712... POINT (-84.02234 9.97465) Patalillo
484 POLYGON ((-83.99859 9.96849, -83.99884 9.96852... POINT (-83.99224 9.97255) San Isidro
485 POLYGON ((-83.90474 9.97574, -83.90556 9.97540... POINT (-83.95146 9.97853) San Rafael

486 rows × 3 columns

8.5. Visualizar polígono: Liberia

In [ ]:
cr_mapa[cr_mapa.Distrito == 'Liberia'].plot()
Out[ ]:
<Axes: >
No description has been provided for this image

8.6. Puntos geográficos desde el csv

In [ ]:
df = pd.read_csv("test.csv")
In [ ]:
df
Out[ ]:
Punto Estación x y MSNM Características del sitio
0 Pt1 Santa María 10.738333 -85.314167 690 Ubicado aguas abajo del derivador de aguas ubi...
1 Pt2 El Yugo 10.670000 -85.391944 260 Rodeado por cañones muy pronunciados en la mes...
2 Pt3 Saca de agua 10.647500 -85.416111 180 Inmediatamente aguas arriba de la planta potab...
3 Pt4 Condega 10.622500 -85.439167 145 Ubicado en extremo suroeste del centro históri...
4 Pt5 Capulin 10.626667 -85.460000 125 Aguas debajo de la confluencia entre la quebra...
5 Pt6 PTAR 10.626389 -85.460833 124 Aguas debajo de la descarga de la planta munic...
6 Pt7 Aforo 10.613611 -85.485000 113 Ubicado fuera del área urbana y aguas arriba d...
7 Pt8 Puente Real 10.625000 -85.435556 150 Localizado en calle central sobre puente decla...
In [ ]:
my_geometry = gpd.points_from_xy(df.y, df.x) # ejes invertidos por convenciones cartográficas
print(my_geometry)
<GeometryArray>
[<POINT (-85.314 10.738)>,  <POINT (-85.392 10.67)>, <POINT (-85.416 10.648)>,
 <POINT (-85.439 10.622)>,  <POINT (-85.46 10.627)>, <POINT (-85.461 10.626)>,
 <POINT (-85.485 10.614)>, <POINT (-85.436 10.625)>]
Length: 8, dtype: geometry
In [ ]:
obvs_gdf = gpd.GeoDataFrame(df, geometry=my_geometry)
obvs_gdf.plot()
Out[ ]:
<Axes: >
No description has been provided for this image

8.7. Selección de polígono de Liberia

In [ ]:
cr_mapa[(cr_mapa["Distrito"] == 'Mogote') | (cr_mapa["Distrito"] == "Liberia")]

# | =  or
# & = and
Out[ ]:
geometry centroid Distrito
172 POLYGON ((-85.29365 10.77595, -85.29248 10.775... POINT (-85.27194 10.69542) Mogote
194 POLYGON ((-85.32543 10.81208, -85.32526 10.811... POINT (-85.45043 10.58659) Liberia

8.8. Colocar los puntos en el polígono

In [ ]:
# Plot sólo los registros Distrito Liberia
distrito = "Liberia"

# mapa_distrito = cr_mapa[(cr_mapa["Distrito"] == 'Mogote') | (cr_mapa["Distrito"] == "Liberia")]
mapa_distrito = cr_mapa[cr_mapa["Distrito"] == distrito]

lienzo = mapa_distrito.plot(
    # Ploteo del mapa
    figsize = (7,7),
    color="lightgreen",
    edgecolor="black",
    linewidth = 0.6
)

# Asignar título
title = f'Ubicación de los puntos muestreados en el río Liberia {distrito}'
lienzo.set_title(title)

# Labels
lienzo.set_ylabel("Latitud")
lienzo.set_xlabel("Longitud")

# # Ploteo de los puntos del río Liberia
obvs_gdf.plot(
    ax=lienzo,
    column="Estación",
    categorical=True,
    legend=True,
    legend_kwds={"loc": "center right", "bbox_to_anchor": (1.4, .84), },
)
Out[ ]:
<Axes: title={'center': 'Obvservaciones registradas en el distrito de Liberia'}, xlabel='Longitud', ylabel='Latitud'>
No description has been provided for this image

8. Resultados¶

Aplicando los conocimientos adquiridos en el curso, se logró desarrollar una automatización que permite preparar los datos y convertirlos en información para analizar la calidad del agua de la subcuenca Río Liberia.

Según los resultados obtenidos mediante la estimación del Índice Holandés, se observa que, en términos generales, la mayor categoría de contaminación en los puntos de muestreo ubicados en las partes más altas de la cuenca, como Santa María, El Yugo, Saca de Agua y Condega, corresponde a la clase 3, "Contaminación Moderada"; sin embargo la mayoría de los resultados han marcado una "contaminación incipiente". En contraste, los sitios situados en las zonas más pobladas, como Capulín, PTAR y Aforo, han alcanzado el nivel más alto de contaminación, clase 5, "Contaminación Muy Severa".

De igual manera, se observa un aumento considerable de contaminación en los puntos PTAR y Aforo durante la transición de la época seca a la lluviosa. Esto sugiere que, con las primeras lluvias del año y el consiguiente aumento del caudal del río, se produce una mayor carga de materia transportada por las aguas.

9. Conclusiones¶

El buen manejo de una base de datos es crucial para el tratamiento efectivo de la información, ya que, facilita la organización, almacenamiento y acceso a los datos de manera eficiente, permitiendo realizar análisis precisos y tomar decisiones informadas. Además, la automatización en el procesamiento de datos mejora la rapidez y exactitud en la generación de información, lo cual en un contexto de monitoreo de calidad de aguas superficiales es fundamental para detectar y mejorar la respuesta a las alteraciones que puedan presentarse, de manera oportuna.

La automatización de los datos permitió determinar resultados en conjunto en lineas de tiempo en las que se realizaron los monitoreos de las distintas épocas climáticas, esto generó una mayor comprensión de las problemáticas en cuanto a contaminación, para cada uno de los sitios estudiados, logrando determinarse que, según el indice holandés, el rio presenta una contaminación incipiente en los puntos más alejados mientras que en la sección urbana hay presencia de contaminación muy severa.

10. Referencias bibliográficas¶

Discover Data Science. (s.f.). How is data science being used to tackle the global problem of clean water? https://www.discoverdatascience.org/social-good/clean-water/

Dyson, J.R., Gyori, B.M., & Beaman, A.L. (2021). An open-source package for data profiling in Python. Journal of Open Source Software, 6(63), 3306.

GADM. (s.f.) gadm41_CRI_shp [Archivo .zip de geolocalización]. https://gadm.org/download_country.html

GeoPy Community. (2022). GeoPy 2.2.0 documentation. Retrieved from https://geopy.readthedocs.io/en/stable/.

Harris, C.R., Millman, K.J., van der Walt, S.J., Gommers, R., Virtanen, P., Cournapeau, D., ... & de Jong, J. (2020). Array programming with NumPy. Nature, 585(7825), 357-362.

HIDROCEC. (2024). RioLiberia [Archivo de Excel].

HIDROCEC. (2024). Test[1] [Archivo de Excel].

Hunter, J.D. (2007). Matplotlib: A 2D graphics environment. Computing in Science & Engineering, 9(3), 90-95.

Montoya, S. (2014, 28 de mayo). ¿Por qué los especialistas ambientales deberían programar? Gidahatari. Recuperado de https://gidahatari.com/nh-es/diplomado-en-python-para-recursos-hdricos-y-geociencias-asincrnico

Oscar. (2017, 8 de enero). Python en consultoría medioambiental. Alburen. Recuperado de https://www.alburen.com/2017/01/python-en-consultoria-medioambiental/)

Pedregosa, F., Varoquaux, G., Gramfort, A., Michel, V., Thirion, B., Grisel, O., ... & Vanderplas, J. (2011). Scikit-learn: Machine learning in Python. Journal of Machine Learning Research, 12(Oct), 2825-2830.

Reglamento Para la Evaluación y Clasificación de la Calidad de Cuerpos de Agua Superficiales N°33903-MINAE-S (17 de setiembre de 2007). Diario Oficial La Gaceta N. 178. https://www.aya.go.cr/centroDocumetacion/catalogoGeneral/Reglamento%20evaluaci%C3%B3n%20y%20clasificaci%C3%B3n%20de%20calidad%20de%20cuerpos%20de%20agua%20superficiales.pdf

Warner, S., Blanco Ramírez, S., de Vries, S., Marangu, N., Ateba Bessa, H., Toranzo, C., Imaralieva, M., Abrate, T., Kiminta, E., Castro, J., de Souza, M. L., Ghaffar Memon, A., Loiselle, S., & Juanah, M. S. E. (2024). Empowering citizen scientists to improve water quality: From monitoring to action. Frontiers in Water, 6. https://doi.org/10.3389/frwa.2024.1367198

Waskom, M. (2021). seaborn: statistical data visualization. Journal of Open Source Software, 6(60), 3021