Saltar navegación

Reto 11: Choverá?

Predición de choiva

Duración:
50 min
Agrupamento:
2
Choiva
Copilot. Choiva (CC BY-SA)

Obxectivo: Buscarás, descargarás e prepararás datos meteorolóxicos para adestrar unha rede neuronal con Keras que predirá se choverá ou non en función de variables como temperatura, humidade, vento e presión atmosférica.

A predición de choiva é un reto moi útil e aplicable á vida real. Hai datasets dispoñibles que conteñen datos meteorolóxicos históricos, como temperatura, humidade, presión atmosférica e velocidade do vento, que poden ser usados para prever se vai chover ou non.

Dataset recomendado: Podes atopar datos meteorolóxicos en este repositorio , ideais para practicar e afondar no deep learning.

Pasos do reto:

Buscar e descargar os datos desde a web.

✅ Subilos a Google Colab e cargalos con Pandas.

✅ Depuralos eliminando valores nulos e normalizando as variables.

✅ Deseñar unha rede neuronal con Keras para predicir se choverá ou non.

✅ Visualizar os resultados con gráficos de perda e precisión.

✅ Probar o modelo con datos novos para ver se acerta na predición.

Lembra: A IA está para axudarche, pregúntalle!

Paso 1: Buscar e descargar os datos

Os datos meteorolóxicos están dispoñibles en Kaggle e inclúen rexistros históricos de distintas variables. Propóñoche un en concreto, pero podes adaptalo a calquera!

Temperature (numeric: A temperatura en graos Celsius, variando desde frío extremo a calor extrema.
Humidity (numeric): A porcentaxe de humidade, incluíndo valores por riba de 100% para presentar outliers.
Wind Speed (numeric): A velocidade de vento en quilómetros por hora, cunha gama que inclúe unrealistically valores altos.
Precipitation (%) (numeric): A porcentaxe de precipitación, incluíndo outlier valores.
Cloud Cover (categorical): A descrición de cuberta da nube.
Atmospheric Pressure (numeric): A presión atmosférica en hPa, cubrindo unha gama ancha.
UV Index (numeric): O índice de UV, indicando a forza de ultraviolet radiación.
Season (categorical): A estación durante o cal o dato foi gravado.
Visibility (km) (numeric): A visibilidade en kilómetros, incluíndo moi abaixo ou valores moi altos.
Location (categorical): O tipo de localización onde o dato foi gravado.
Weather Type (categorical): Clasificación, indicando o tipo de tempo.

Paso 2: Cargar os datos en Colab e depuralos

Podes recuperar o programa para depurar os datos que usaches para Scikit-learn ou usar este código:

Para subir a colab o arquivo e visualizar as primeiras filas:

import pandas as pd
from google.colab import files

# Cargar o arquivo CSV en Colab
print("Sube o teu arquivo CSV:")
datos = files.upload()

# Obter o nome do primeiro arquivo subido
nome_arquivo = list(datos.keys())[0]

# Ler o arquivo CSV sen depender dun nome fixo
df = pd.read_csv(nome_arquivo)

# Visualizar as primeiras filas dos datos
print(f"Arquivo cargado correctamente: {nome_arquivo}")
df.head()

Limpeza de datos: Revisar se hai valores nulos. Convertir a variable de choiva (Precipitation (%)) a valores binarios (1 se chove, 0 se non). Normalizar as características (para que todos os valores teñan o mesmo rango).

# Comprobar valores nulos
print(df.isnull().sum())

# Seleccionar características relevantes
X = df[['Temperature', 'Humidity', 'Wind Speed', 'Atmospheric Pressure']]
y = df['Precipitation (%)']

# Normalizar os datos
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X = scaler.fit_transform(X)

# Normalizar tamén y para mellorar estabilidade do modelo
y = scaler.fit_transform(y.values.reshape(-1, 1))

# Visualizar as primeiras filas dos datos
print(f"Arquivo revisado correctamente: {nome_arquivo}")
df.head()

Paso 3: Construír a rede neuronal con Keras

Axustando o modelo para que prediga valores continuos (% de choiva) en vez de unha clasificación binaria (0 ou 1).

Define o modelo con este código:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Input

# Crear o modelo
modelo = Sequential([
Input(shape=(X.shape[1],)), # Cantidade de características como entrada
Dense(32, activation='relu'), # Primeira capa oculta con 32 neuronas
Dense(16, activation='relu'), # Segunda capa oculta con 16 neuronas
Dense(1, activation='linear') # Capa de saída con activación lineal para valores continuos
])

# Compilar o modelo con función de perda axeitada para regresión
modelo.compile(optimizer='adam', loss='mean_squared_error', metrics=['mean_absolute_error'])

# Mostrar resumo do modelo
modelo.summary()

PREGUNTAS:

  1. Investiga que significa cada columna e fila da táboa resultante de modelo.summary()
  2. Por que usamos mean-absolute_error como métrica e non accuracy?

Paso 4: Adestramento do modelo

Pasamos a adestrar o modelo co código:

from sklearn.model_selection import train_test_split

# Dividir en datos de adestramento e validación
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)

# Adestramento
historial = modelo.fit(
X_train, y_train,
epochs=10,
batch_size=32,
validation_data=(X_val, y_val)
)

PREGUNTA: Averigüa que significa X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)

Paso 5: Visualización do adestramento

Agora graficamos a perda e o erro medio na precición(xa non usamos accuraty porque non é clasificación binaria):

import matplotlib.pyplot as plt

plt.figure(figsize=(12, 4))

# Gráfica da perda
plt.subplot(1, 2, 1)
plt.plot(historial.history['loss'], label='Perda no Adestramento')
plt.plot(historial.history['val_loss'], label='Perda na Validación')
plt.xlabel('Época')
plt.ylabel('Perda')
plt.title('Progreso da Perda')
plt.legend()

# Gráfica do erro absoluto medio
plt.subplot(1, 2, 2)
plt.plot(historial.history['mean_absolute_error'], label='Erro Absoluto Medio no Adestramento')
plt.plot(historial.history['val_mean_absolute_error'], label='Erro Absoluto Medio na Validación')
plt.xlabel('Época')
plt.ylabel('Erro Absoluto Medio')
plt.title('Progreso do Erro Absoluto Medio')
plt.legend()

plt.tight_layout()
plt.show()

PREGUNTA:Explica as gráficas obtidas

Paso 6: Predición con datos novos

Agora o modelo pode predicir a probabilidade estimada de choiva en % a partir das condicións meteorolóxicas do día.

Proba con novos datos:

import numpy as np
from sklearn.preprocessing import MinMaxScaler

# Crear o modelo (asegúrate de que xa o tes adestrado e cargado)
# modelo = ... # O modelo xa debe estar definido e adestrado

# Crear o escalador e axustalo cos datos de adestramento
scaler = MinMaxScaler()
X = scaler.fit_transform(df[['Temperature', 'Humidity', 'Wind Speed', 'Atmospheric Pressure']])

# Características dun novo día (modificable)
novo_dia = np.array([[15, 80, 5, 1012]]) # Temperatura, Humidade, Vento, Presión

# Convertir `novo_dia` nun DataFrame para manter os nomes das características
novo_dia_df = pd.DataFrame(novo_dia, columns=['Temperature', 'Humidity', 'Wind Speed', 'Atmospheric Pressure'])

# Aplicar a transformación de escalado correctamente
novo_dia_scaled = scaler.transform(novo_dia_df)

# Predicir a Probabilidade esperada de choiva
prediccion = modelo.predict(novo_dia_scaled)
print(f"Probabilidade estimada de choiva: {prediccion[0][0]*100:.2f} %")

PREGUNTA: Cambia o programa para que:

  • che vaia pedinto Temperatura(ºC), Humidade (%), Velocidade do vento (km/h) e Presión atmosférica (milibares)
  • Realice peticións dinámicas con while:True

Código Python: Programa para depurar datos dun arquivo .csv

# Importar librerías necesarias
import pandas as pd

# Función para analizar, limpar, preprocesar e modificar datos de forma educativa
def analizar_limpar_e_modificar_datos():
    print("Benvido/a! Este programa axudarache a analizar, limpar e preparar os teus datos para proxectos de IA.")
    print("Explicaremos cada paso para que comprendas por que é necesario realizar este proceso.")

    # Paso 1: Subir o arquivo CSV
    from google.colab import files
    print("\nPor favor, sube o arquivo CSV cos datos que desexas analizar e limpar:")
    uploaded = files.upload()
    nome_arquivo = list(uploaded.keys())[0]
    datos = pd.read_csv(nome_arquivo)
    print(f"\nArquivo '{nome_arquivo}' cargado correctamente!")
    print("\nVisualizando as primeiras filas dos datos:")
    print(datos.head())

    # Paso 2: Seleccionar columnas para analizar e limpar
    print("\nColumnas dispoñibles no arquivo:")
    print(list(datos.columns))
    print("\nSelecciona as columnas que queres analizar e limpar. Introduce os nomes separados por comas.")
    columnas_seleccionadas = input("Introduce os nomes das columnas (exemplo: 'Column1,Column2'): ").split(',')

    # Limpar espazos ao redor dos nomes das columnas
    columnas_seleccionadas = [col.strip() for col in columnas_seleccionadas]
    print(f"\nSeleccionaches estas columnas: {columnas_seleccionadas}")

    # Comprobar que as columnas existen
    for columna in columnas_seleccionadas:
        if columna not in datos.columns:
            print(f"\nErro: A columna '{columna}' non existe no arquivo. Revise os nomes.")
            return

    # Paso 3: Cambiar nomes das columnas seleccionadas
    print("\nQueres cambiar o nome dalgunha das columnas seleccionadas?")
    print("1 - Si, quero cambiar os nomes.")
    print("2 - Non, quero manter os nomes actuais.")
    cambiar_nomes = input("Introduce o número correspondente á túa elección: ")

    if cambiar_nomes == "1":
        novos_nomes = {}
        for columna in columnas_seleccionadas:
            novo_nome = input(f"Introduce o novo nome para a columna '{columna}': ")
            novos_nomes[columna] = novo_nome
        datos.rename(columns=novos_nomes, inplace=True)
        columnas_seleccionadas = [novos_nomes[col] for col in columnas_seleccionadas]
        print(f"\nOs nomes das columnas foron actualizados: {novos_nomes}")
    else:
        print("\nManteranse os nomes orixinais das columnas seleccionadas.")

    # Crear un DataFrame para traballar con só as columnas seleccionadas
    datos_seleccionados = datos[columnas_seleccionadas].copy()

    # Paso 4: Analizar e modificar valores en cada columna
    print("\nAgora imos analizar os valores nas columnas seleccionadas.")
    filas_a_eliminar = []

    for columna in columnas_seleccionadas:
        print(f"\nAnalizando a columna: '{columna}'")
        tipos_detectados = datos_seleccionados[columna].apply(type).unique()
        print(f"- Tipos de datos detectados: {[tipo.__name__ for tipo in tipos_detectados]}")

        # Preguntar ao usuario que tipo de dato debería ter a columna
        print("\nQue tipo de dato debería ter esta columna?")
        print("1 - bool (True/False)")
        print("2 - int (Números enteiros)")
        print("3 - float (Números decimais)")
        print("4 - str (Texto)")
        tipo_dato_esperado = input("Introduce o número correspondente ao tipo de dato esperado: ")

        tipo_actual = None
        if pd.api.types.is_bool_dtype(datos_seleccionados[columna]):
            tipo_actual = "1"
        elif pd.api.types.is_integer_dtype(datos_seleccionados[columna]):
            tipo_actual = "2"
        elif pd.api.types.is_float_dtype(datos_seleccionados[columna]):
            tipo_actual = "3"
        elif pd.api.types.is_string_dtype(datos_seleccionados[columna]):
            tipo_actual = "4"

        if tipo_dato_esperado == tipo_actual:
            print(f"- A columna '{columna}' xa está no formato esperado. Non require cambios.")
        else:
            # Convertir a columna ao tipo especificado
            try:
                if tipo_dato_esperado == "1":
                    datos_seleccionados[columna] = datos_seleccionados[columna].astype(bool)
                elif tipo_dato_esperado == "2":
                    datos_seleccionados[columna] = datos_seleccionados[columna].astype(int)
                elif tipo_dato_esperado == "3":
                    datos_seleccionados[columna] = datos_seleccionados[columna].astype(float)
                elif tipo_dato_esperado == "4":
                    datos_seleccionados[columna] = datos_seleccionados[columna].astype(str)
                print(f"- A columna '{columna}' foi convertida ao formato esperado.")
            except ValueError as e:
                print(f"\nErro ao converter os datos: {e}. Revisando os valores...")

        # Preguntar ao usuario se quere cambiar os valores das categorías detectadas
        categorias = datos_seleccionados[columna].unique()
        print(f"- A columna '{columna}' ten {len(categorias)} categorías: {categorias}")

        print("\nQueres cambiar os valores das categorías?")
        print("1 - Si, quero cambiar os nomes/valores das categorías.")
        print("2 - Non, quero manter os valores actuais.")
        cambio_categoria = input("Introduce o número da túa elección: ")

        if cambio_categoria == "1":
            conversion = {}
            for categoria in categorias:
                print(f"Para a categoría '{categoria}', introduce o valor co que a queres substituír:")
                novo_valor = input("Novo valor: ")
                conversion[categoria] = novo_valor
            print(f"Mapa de conversión para '{columna}': {conversion}")
            datos_seleccionados[columna] = datos_seleccionados[columna].map(conversion)

        # Validación de valores inexistentes ou problemáticos
        print("- Comprobando valores inexistentes ou problemáticos na columna...")
        for i, valor in enumerate(datos_seleccionados[columna]):
            if pd.isnull(valor):  # Valor inexistente ou NaN
                print(f"Fila {i+1}: O valor está baleiro ou inexistente.")
                decision = input("Queres eliminar esta fila? (Si/Non): ").strip().lower()
                if decision == "si":
                    filas_a_eliminar.append(i)

    # Eliminar filas erróneas segundo decisións do usuario
    print("\nEliminando filas problemáticas segundo as túas decisións...")
    datos_limpados = datos_seleccionados.drop(filas_a_eliminar, axis=0)
    print(f"Filas eliminadas: {len(filas_a_eliminar)}")
    
    # Paso 5: Gardar o arquivo limpo
    print("\nGardando o arquivo limpo e completamente preprocesado...")
    nome_saida = input("Introduce o nome do arquivo de saída (por exemplo, 'datos_limpados.csv'): ")
    datos_limpados.to_csv(nome_saida, index=False)
    print(f"\nArquivo limpo gardado como '{nome_saida}'. Contén os datos seleccionados e correctamente limpos.")

    # Ofrecer ao usuario descargar o arquivo
    print("\nDescargando o arquivo...")
    files.download(nome_saida)

# Executar o programa educativo
analizar_limpar_e_modificar_datos()    
    

Rúbrica de avaliación

Rúbrica Reto 11: Predición de choiva
Criterio Excelente (9-10) Bo (7-8) Aceptable (5-6) Mellorable (3-4) Moi mellorable (0-2)
Obtención e comprensión dos datos Busca, descarga e revisa correctamente o dataset meteorolóxico. Dataset descargado e revisado pero con pequenas dificultades na análise. Dataset obtido pero con problemas na súa comprensión e exploración. Erro na carga ou interpretación dos datos. Non obtén ou revisa os datos.
Preprocesamento e limpeza do dataset Elimina valores nulos e transforma correctamente as variables. Preprocesamento realizado pero con pequenas dificultades na conversión. Dataset preprocesado pero con erros na xestión das variables. Erro na limpeza ou normalización dos datos. Non realiza preprocesamento dos datos.
Configuración da rede neuronal Define correctamente a arquitectura da rede en Keras. Rede configurada pero con pequenas melloras posibles na estrutura. Arquitectura da rede definida pero con dificultades na optimización. Erro na configuración da rede neuronal. Non define a rede neuronal.
Adestramento e validación do modelo Divide correctamente os datos e adestra a rede con parámetros axeitados. Adestramento realizado pero con melloras posibles nos parámetros. Modelo adestrado pero con problemas ocasionais na validación. Erro na división dos datos ou no proceso de adestramento. Non adestra nin valida o modelo.
Visualización dos resultados Gráfica ben elaborada mostrando a evolución da perda e do erro medio. Visualización ben feita pero con pequenas melloras posibles. Gráfica realizada pero con dificultade na interpretación dos resultados. Erro na xestión ou comprensión das gráficas. Non realiza ningunha visualización dos resultados.
Predición con datos novos Permite realizar predicións dinámicas con entrada de usuario. Predición funcional pero con melloras posibles na interacción. Predición realizada pero con problemas ocasionais na resposta. Erro na predición dos datos. Non implementa funcionalidade de predición.
Explicación das métricas e gráficas Describe correctamente o significado das métricas utilizadas. Explicación das métricas ben feita pero con melloras posibles. Interpretación das métricas con dificultades na súa comprensión. Erro na explicación das métricas. Non explica as métricas utilizadas.
Interacción co usuario Solicita entrada de datos de maneira clara e permite interacción iterativa. Interacción ben feita pero con pequenas melloras posibles. Interacción realizada pero con dificultades na fluidez do proceso. Erro na comunicación dos resultados ao usuario. Non implementa interacción co usuario.
Actitude ante os problemas Busca solucións e optimiza os procesos de predición. Resolve os problemas con esforzo e consulta documentación. Resolve con dificultades pero sen explorar melloras. Resígnase ante os erros sen buscar alternativas. Non intenta resolver os problemas ou abandona o traballo.
Traballo colaborativo Comunicación eficaz e traballo equilibrado en parella. Traballo en equipo con pequenas dificultades. Interacción mínima, traballo case independente. Pouca colaboración, realízase gran parte do traballo de forma individual. Non colabora coa parella nin contribúe ao traballo conxunto.