diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..8d9690e --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "windows-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "C:/Strawberry/c/bin/gcc.exe", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "windows-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b7b6e2e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": true, + "cwd": "c:/Users/Usuario PC/Desktop/Tareas", + "program": "c:/Users/Usuario PC/Desktop/Tareas/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bb879da --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,59 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "C:/Program Files/Microsoft Visual Studio/VR_NR/Community/VC/Auxiliary/Build/vcvarsall.bat", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..7fb9284 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: g++.exe compilar archivo activo", + "command": "C:\\Strawberry\\c\\bin\\g++.exe", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}\\${fileBasenameNoExtension}.exe" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Tarea generada por el depurador." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/Algoritmos_de_Ordenamiento/Burbuja.cpp b/Algoritmos_de_Ordenamiento/Burbuja.cpp new file mode 100644 index 0000000..9131d27 --- /dev/null +++ b/Algoritmos_de_Ordenamiento/Burbuja.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +void bubbleSort(std::vector &lista, int n) +{ + for (int i = 0; i <= n - 2; ++i) + { + bool intercambiado = false; + + for (int j = 0; j <= n - 2 - i; ++j) + { + if (lista[j] > lista[j + 1]) + { + std::swap(lista[j], lista[j + 1]); + intercambiado = true; + } + } + + if (!intercambiado) + { + break; + } + } +} \ No newline at end of file diff --git a/Algoritmos_de_Ordenamiento/Burbuja.exe b/Algoritmos_de_Ordenamiento/Burbuja.exe new file mode 100644 index 0000000..e165ebe Binary files /dev/null and b/Algoritmos_de_Ordenamiento/Burbuja.exe differ diff --git a/Algoritmos_de_Ordenamiento/mergesort.cpp b/Algoritmos_de_Ordenamiento/mergesort.cpp new file mode 100644 index 0000000..b4ab4ea --- /dev/null +++ b/Algoritmos_de_Ordenamiento/mergesort.cpp @@ -0,0 +1,84 @@ +#include +#include + +using namespace std; + +void mezclar(vector &lista, int inicio, int medio, int fin) +{ + int n1 = medio - inicio + 1; + int n2 = fin - medio; + + vector izq(n1), der(n2); + + for (int i = 0; i < n1; i++) + izq[i] = lista[inicio + i]; + for (int j = 0; j < n2; j++) + der[j] = lista[medio + 1 + j]; + + int i = 0, j = 0, k = inicio; + + while (i < n1 && j < n2) + { + if (izq[i] <= der[j]) + { + lista[k] = izq[i]; + i++; + } + else + { + lista[k] = der[j]; + j++; + } + k++; + } + + while (i < n1) + { + lista[k] = izq[i]; + i++; + k++; + } + while (j < n2) + { + lista[k] = der[j]; + j++; + k++; + } +} + +void mergeSort(vector &lista, int inicio, int fin) +{ + if (inicio >= fin) + return; + + int medio = inicio + (fin - inicio) / 2; // (Paso 5) + + mergeSort(lista, inicio, medio); + mergeSort(lista, medio + 1, fin); + + mezclar(lista, inicio, medio, fin); +} + +int main() +{ + int n, temp; + vector datos; + + while (cin >> temp) + { + datos.push_back(temp); + } + + if (!datos.empty()) + { + mergeSort(datos, 0, datos.size() - 1); + + for (size_t i = 0; i < datos.size(); i++) + { + cout << datos[i] << (i == datos.size() - 1 ? "" : " "); + } + cout << endl; + } + + return 0; +} \ No newline at end of file diff --git a/Algoritmos_de_Ordenamiento/mergesort.exe b/Algoritmos_de_Ordenamiento/mergesort.exe new file mode 100644 index 0000000..a9777c5 Binary files /dev/null and b/Algoritmos_de_Ordenamiento/mergesort.exe differ diff --git a/Algoritmos_de_Ordenamiento/seleccion.cpp b/Algoritmos_de_Ordenamiento/seleccion.cpp new file mode 100644 index 0000000..c64e087 --- /dev/null +++ b/Algoritmos_de_Ordenamiento/seleccion.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +using namespace std; + +void selectionSort(vector &lista, int n) +{ + for (int i = 0; i <= n - 2; ++i) + { + int min_idx = i; + + for (int j = i + 1; j <= n - 1; ++j) + { + if (lista[j] < lista[min_idx]) + { + min_idx = j; + } + } + + if (min_idx != i) + { + swap(lista[i], lista[min_idx]); + } + } +} + +int main() +{ + int n; + if (!(cin >> n)) + return 0; + + vector lista(n); + for (int i = 0; i < n; ++i) + { + cin >> lista[i]; + } + + selectionSort(lista, n); + + for (int i = 0; i < n; ++i) + { + cout << lista[i] << (i == n - 1 ? "" : " "); + } + cout << endl; + + return 0; +} \ No newline at end of file diff --git a/Algoritmos_de_Ordenamiento/seleccion.exe b/Algoritmos_de_Ordenamiento/seleccion.exe new file mode 100644 index 0000000..8814925 Binary files /dev/null and b/Algoritmos_de_Ordenamiento/seleccion.exe differ diff --git a/Examen_Parcial_2_La_venganza/examen_parcial2_estudiante.ipynb b/Examen_Parcial_2_La_venganza/examen_parcial2_estudiante.ipynb new file mode 100644 index 0000000..978608e --- /dev/null +++ b/Examen_Parcial_2_La_venganza/examen_parcial2_estudiante.ipynb @@ -0,0 +1,797 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "title", + "metadata": {}, + "source": [ + "# Examen Parcial 2 — Programación 1 (F12)\n", + "## Tormentas Geomagnéticas con la API de NASA / DONKI\n", + "\n", + "**Nombre:** Alejandro Sebastian Puac Shar \n", + "**Carnet:** 202604047 \n", + "**Fecha:** 21/04/2026 \n", + "**Punteo total:** 100 puntos\n", + "\n", + "---\n", + "\n", + "| Ejercicio | Tema | Puntos |\n", + "|---|---|:---:|\n", + "| 1a | Construir la URL y realizar la solicitud HTTP | 10 |\n", + "| 1b | Explorar la lista de tormentas con un ciclo `for` | 10 |\n", + "| 2 | Calcular el Kp máximo e identificar la tormenta más intensa | 20 |\n", + "| 3 | Función `clasificar_kp()` con condicionales | 25 |\n", + "| 4 | Función `analizar_tormenta()` que retorna un diccionario | 35 |\n", + "| | **Total** | **100** |\n", + "\n", + "---\n", + "\n", + "> **Instrucciones generales**\n", + "> - Completa cada ejercicio en la celda indicada.\n", + "> - Ejecuta las celdas **en orden** de arriba hacia abajo.\n", + "> - Puedes consultar tus apuntes y el notebook de clase `XML_JSON_APIs.ipynb`.\n", + "> - Entrega el notebook con **todas las celdas ejecutadas** (con output visible)." + ] + }, + { + "cell_type": "markdown", + "id": "background", + "metadata": {}, + "source": [ + "---\n", + "## Contexto: ¿Qué es una tormenta geomagnética?\n", + "\n", + "El Sol emite constantemente partículas cargadas (viento solar). Cuando ocurre una erupción solar intensa, una ola de partículas choca con el campo magnético de la Tierra y provoca una **tormenta geomagnética**.\n", + "\n", + "Estos eventos pueden:\n", + "- Generar auroras boreales visibles en latitudes bajas\n", + "- Interferir con satélites GPS y comunicaciones de radio\n", + "- En casos extremos, dañar redes eléctricas (como el apagón de Quebec en 1989)\n", + "\n", + "### Índice Kp — escala de intensidad\n", + "\n", + "El **índice Kp** (0–9) mide la perturbación del campo magnético terrestre:\n", + "\n", + "| Kp | Categoría | Descripción |\n", + "|---|---|---|\n", + "| 0 – 3 | Quieto | Sin tormenta |\n", + "| 4 | Activo | Perturbación menor |\n", + "| 5 | **G1** — Menor | Auroras en latitudes altas |\n", + "| 6 | **G2** — Moderada | Auroras hasta latitud 55° |\n", + "| 7 | **G3** — Fuerte | Auroras hasta latitud 50° |\n", + "| 8 | **G4** — Severa | Problemas en redes eléctricas |\n", + "| 9 | **G5** — Extrema | Apagones posibles, auroras tropicales |\n", + "\n", + "La tormenta de mayo 2024 alcanzó **G5** — la más intensa en 20 años." + ] + }, + { + "cell_type": "markdown", + "id": "api-docs", + "metadata": {}, + "source": [ + "---\n", + "## El endpoint: DONKI / GST\n", + "\n", + "**DONKI** = Space Weather Database Of Notifications, Knowledge, Information \n", + "**GST** = Geomagnetic Storm\n", + "\n", + "```\n", + "GET https://api.nasa.gov/DONKI/GST\n", + "```\n", + "\n", + "| Parámetro | Tipo | Default | Descripción |\n", + "|---|---|---|---|\n", + "| `startDate` | YYYY-MM-DD | 30 días atrás | Inicio del rango de fechas |\n", + "| `endDate` | YYYY-MM-DD | hoy | Fin del rango de fechas |\n", + "| `api_key` | string | DEMO_KEY | Tu API key de api.nasa.gov |\n", + "\n", + "**Ejemplo de URL:**\n", + "```\n", + "https://api.nasa.gov/DONKI/GST?startDate=2024-05-01&endDate=2024-05-31&api_key=DEMO_KEY\n", + "```\n", + "\n", + "### Estructura de la respuesta\n", + "\n", + "La API devuelve una **lista** de eventos. Cada evento es un diccionario con esta estructura:\n", + "\n", + "```json\n", + "{\n", + " \"gstID\": \"2024-05-10T17:00:00-GST-001\",\n", + " \"startTime\": \"2024-05-10T17:00Z\",\n", + " \"allKpIndex\": [\n", + " { \"observedTime\": \"2024-05-10T21:00Z\", \"kpIndex\": 8.67, \"source\": \"NOAA\" },\n", + " { \"observedTime\": \"2024-05-11T00:00Z\", \"kpIndex\": 9.0, \"source\": \"NOAA\" }\n", + " ],\n", + " \"linkedEvents\": [\n", + " { \"activityID\": \"2024-05-08T21:08:00-CME-001\" }\n", + " ],\n", + " \"link\": \"https://webtools.ccmc.gsfc.nasa.gov/DONKI/view/GST/...\"\n", + "}\n", + "```\n", + "\n", + "**Campos importantes:**\n", + "- `gstID` — identificador único del evento\n", + "- `startTime` — cuándo comenzó la tormenta (formato ISO 8601 UTC)\n", + "- `allKpIndex` — lista de mediciones Kp durante la tormenta\n", + "- `linkedEvents` — eventos espaciales relacionados (erupciones, CME) — puede ser `None`" + ] + }, + { + "cell_type": "markdown", + "id": "setup-title", + "metadata": {}, + "source": [ + "---\n", + "## Paso 1 — Configuración de la API Key\n", + "\n", + "### Obtener tu API key (gratis)\n", + "\n", + "1. Ve a **https://api.nasa.gov**\n", + "2. Completa el formulario con tu nombre y correo electrónico\n", + "3. Haz clic en **\"Signup\"**\n", + "4. Revisa tu correo — recibirás la key en minutos\n", + "5. La key tiene este formato: `TU_API_KEY_AQUI_XXXXXXXXXXXXXXXXXXXXXXXX`\n", + "\n", + "Con tu propia key puedes hacer **1,000 solicitudes por hora** (vs 30 con DEMO_KEY).\n", + "\n", + "---\n", + "\n", + "### Configurar la variable de entorno `NASA_API_KEY`\n", + "\n", + "Una variable de entorno guarda tu key **fuera del código**, para que no quede expuesta \n", + "en el archivo del notebook ni en el historial de git.\n", + "\n", + "#### Linux / macOS — temporal (solo la sesión actual)\n", + "```bash\n", + "export NASA_API_KEY=\"tu_key_aqui\"\n", + "jupyter notebook\n", + "```\n", + "\n", + "#### Linux / macOS — permanente\n", + "Agrega esta línea al final de `~/.bashrc` (bash) o `~/.zshrc` (zsh):\n", + "```bash\n", + "echo 'export NASA_API_KEY=\"tu_key_aqui\"' >> ~/.bashrc\n", + "source ~/.bashrc # aplicar sin reiniciar\n", + "```\n", + "\n", + "#### Windows — PowerShell (temporal)\n", + "```powershell\n", + "$env:NASA_API_KEY = \"tu_key_aqui\"\n", + "jupyter notebook\n", + "```\n", + "\n", + "#### Windows — permanente (Panel de control)\n", + "1. Busca **\"Variables de entorno\"** en el menú Inicio\n", + "2. Clic en **\"Editar las variables de entorno del sistema\"**\n", + "3. Botón **\"Variables de entorno...\"**\n", + "4. En \"Variables de usuario\" → **Nueva**\n", + "5. Nombre: `NASA_API_KEY` | Valor: tu key\n", + "6. Aceptar y **reiniciar** Jupyter\n", + "\n", + "#### Verificar que está configurada\n", + "```bash\n", + "# Linux/macOS\n", + "echo $NASA_API_KEY\n", + "\n", + "# Windows PowerShell\n", + "echo $env:NASA_API_KEY\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "setup-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ API key cargada: nOVI************************************\n", + "\n", + "Librerías importadas correctamente ✓\n" + ] + } + ], + "source": [ + "import os\n", + "import requests\n", + "import json\n", + "import time\n", + "\n", + "# Cargar la API key desde la variable de entorno.\n", + "# Si no está configurada, usa DEMO_KEY como respaldo (30 req/hora).\n", + "API_KEY = os.getenv(\"NASA_API_KEY\", \"\").strip() or \"DEMO_KEY\"\n", + "\n", + "if API_KEY == \"DEMO_KEY\":\n", + " print(\"⚠ Usando DEMO_KEY — límite: 30 solicitudes/hora\")\n", + " print(\" Configura NASA_API_KEY para mayor límite (ver instrucciones arriba).\")\n", + "else:\n", + " print(f\"✓ API key cargada: {API_KEY[:4]}{'*' * (len(API_KEY) - 4)}\")\n", + "\n", + "print(\"\\nLibrerías importadas correctamente ✓\")" + ] + }, + { + "cell_type": "markdown", + "id": "ident-md", + "metadata": {}, + "source": [ + "---\n", + "## Identificacion del estudiante\n", + "\n", + "Completa tu nombre y carnet en la siguiente celda y **ejecutala antes de comenzar**. \n", + "Estos datos quedan registrados en el output del notebook junto con la posicion de la ISS \n", + "en el momento exacto en que realizas el examen." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ident-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Datos completados correctamente.\n", + "Estudiante : Alejandro Puac\n", + "Carnet : 202604047\n" + ] + } + ], + "source": [ + "# Completa tus datos\n", + "NOMBRE = \"Alejandro Puac\" # TU NOMBRE AQUI\n", + "CARNET = \"202604047\" # TU CARNET AQUI\n", + "\n", + "if NOMBRE == \"Alejandro Puac\" and CARNET == \"202604047\":\n", + " print(\"Datos completados correctamente.\")\n", + "else:\n", + " raise ValueError(\"Completa tu nombre y carnet antes de continuar.\")\n", + "\n", + "print(f\"Estudiante : {NOMBRE}\")\n", + "print(f\"Carnet : {CARNET}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ej1-md", + "metadata": {}, + "source": [ + "---\n", + "## Ejercicio 1 — Obtener y explorar los datos (20 pts)\n", + "\n", + "### Parte a) — Solicitud al endpoint (10 pts)\n", + "\n", + "Se te da la URL base del endpoint. Construye la URL completa usando un f-string e incluye los parámetros `startDate`, `endDate` y `api_key`. Luego realiza la solicitud y verifica que fue exitosa.\n", + "\n", + "---\n", + "\n", + "### ¿Por qué verificar el código de estado antes de leer el JSON?\n", + "\n", + "Cuando hacemos una solicitud HTTP el servidor siempre responde con un **código de estado numérico** que indica si todo salió bien o hubo un problema.\n", + "\n", + "El código **200** significa *\"OK — la solicitud fue exitosa y el cuerpo de la respuesta contiene los datos pedidos\"*.\n", + "\n", + "Si el servidor devuelve un código diferente (por ejemplo **503 - Service Unavailable** o **429 - Too Many Requests**), el cuerpo de la respuesta **no contiene JSON válido** — puede estar vacío o contener un mensaje de error en texto plano. Si intentamos llamar `.json()` directamente sin verificar, el programa lanzará un `JSONDecodeError`.\n", + "\n", + "Por eso siempre debemos verificar que `respuesta.status_code == 200` (o equivalentemente `respuesta.ok`) **antes** de intentar parsear la respuesta.\n", + "\n", + "> **Escribe en la celda de código siguiente: ¿qué imprimirías tú para que el usuario sepa qué salió mal cuando el código no es 200?**" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ej1-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Solicitud exitosa ✓ — 5 tormentas recibidas\n" + ] + } + ], + "source": [ + "URL_BASE = \"https://api.nasa.gov/DONKI/GST\"\n", + "\n", + "# Agrega también los parámetros startDate y endDate:\n", + "startDate = \"2024-05-01\"\n", + "endDate = \"2024-05-31\"\n", + "# Hint: los parámetros van separados por \"&\" → ?param1=val1¶m2=val2\n", + "\n", + "url = f\"{URL_BASE}?startDate={startDate}&endDate={endDate}&api_key={API_KEY}\"\n", + "\n", + "# Completa la llamada — usa timeout=15\n", + "respuesta = requests.get(url, timeout=15)\n", + "\n", + "# Verifica que la respuesta fue exitosa antes de parsear el JSON\n", + "# Hint: usa respuesta.ok o compara respuesta.status_code con 200\n", + "# Hint: si hay error, imprime el código y el cuerpo — respuesta.text tiene el mensaje\n", + "if respuesta.status_code != 200:\n", + " print(f\"No se ha ejecutado correctamente el codigo, Codigo de Error: {respuesta.status_code}\\n{respuesta.text}\") # TU CÓDIGO AQUÍ — mensaje informativo\n", + " raise SystemExit(\"No se pudo obtener la respuesta. Intenta de nuevo.\")\n", + "\n", + "tormentas = respuesta.json()\n", + "print(f\"Solicitud exitosa ✓ — {len(tormentas)} tormentas recibidas\")" + ] + }, + { + "cell_type": "markdown", + "id": "ej1-url-warning", + "metadata": {}, + "source": [ + "> ⚠️ **Cuidado con exponer tu API key** \n", + "> Puedes descomentar el `print` de la celda siguiente para verificar que la URL se formó correctamente, pero **vuelve a comentarlo antes de guardar y entregar el notebook**. Si lo dejas activo, tu API key quedará visible en el output del archivo." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ej1-url-check", + "metadata": {}, + "outputs": [], + "source": [ + "# Descomenta la siguiente línea para verificar que la URL se formó correctamente.\n", + "# ⚠ Vuelve a comentarla antes de entregar — el output guarda tu API key en el archivo.\n", + "\n", + "# print(\"URL:\", url)" + ] + }, + { + "cell_type": "markdown", + "id": "ej1b-md", + "metadata": {}, + "source": [ + "### Parte b) — Explorar la lista de tormentas (10 pts)\n", + "\n", + "Usando un ciclo `for`, imprime cuántas tormentas hubo en el período y el `gstID` y `startTime` de cada una." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ej1b-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total de tormentas:5\n", + "gstID2024-05-02T15:00:00-GST-001 **Tiempo de Inicio: 2024-05-02T15:00Z\n", + "gstID2024-05-10T15:00:00-GST-001 **Tiempo de Inicio: 2024-05-10T15:00Z\n", + "gstID2024-05-12T21:00:00-GST-001 **Tiempo de Inicio: 2024-05-12T21:00Z\n", + "gstID2024-05-16T06:00:00-GST-001 **Tiempo de Inicio: 2024-05-16T06:00Z\n", + "gstID2024-05-17T18:00:00-GST-001 **Tiempo de Inicio: 2024-05-17T18:00Z\n" + ] + } + ], + "source": [ + "# Hint: len(tormentas) da el total de eventos\n", + "# Hint: cada elemento de tormentas es un dict — accede con tormenta[\"gstID\"], etc.\n", + "print(f\"Total de tormentas:{len(tormentas)}\")\n", + "for tormenta in tormentas: \n", + " print(f\"gstID{tormenta[\"gstID\"]} **Tiempo de Inicio: {tormenta[\"startTime\"]}\") \n" + ] + }, + { + "cell_type": "markdown", + "id": "ej2-md", + "metadata": {}, + "source": [ + "---\n", + "## Ejercicio 2 — Kp máximo por tormenta (20 pts)\n", + "\n", + "Cada tormenta contiene una lista `allKpIndex` con múltiples mediciones a lo largo del tiempo.\n", + "\n", + "**a) (10 pts)** Para cada tormenta, encuentra el **valor máximo de Kp** usando un ciclo `for` sobre `allKpIndex`. Imprime el `startTime` y el Kp máximo de cada tormenta.\n", + "\n", + "**b) (10 pts)** Encuentra e imprime la tormenta **más intensa** del período (la que tuvo el mayor Kp máximo entre todas las tormentas)." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "ej2-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 2024-05-02T15:00Z Kp máx = 6.67\n", + " 2024-05-10T15:00Z Kp máx = 9.00\n", + " 2024-05-12T21:00Z Kp máx = 6.33\n", + " 2024-05-16T06:00Z Kp máx = 6.00\n", + " 2024-05-17T18:00Z Kp máx = 6.00\n", + "La tormenta más intensa es: 2024-05-10T15:00:00-GST-001\n", + " 9.0\n" + ] + } + ], + "source": [ + "# ── Parte a) ──────────────────────────────────────────────────────────────────\n", + "# Hint: allKpIndex es una lista de dicts, cada uno con la clave \"kpIndex\"\n", + "# Hint: para encontrar el máximo de una lista puedes usar la función max()\n", + "# o un ciclo que compare con una variable auxiliar (kp_max = 0)\n", + "\n", + "for tormenta in tormentas:\n", + " kp_max = 0\n", + " for tormentita in tormenta[\"allKpIndex\"]:\n", + " if tormentita[\"kpIndex\"]> kp_max:\n", + " kp_max = tormentita[\"kpIndex\"]\n", + " print(f\" {tormenta['startTime']} Kp máx = {kp_max:.2f}\")\n", + "\n", + "# ── Parte b) ──────────────────────────────────────────────────────────────────\n", + "# Hint: necesitas dos variables auxiliares:\n", + "tormenta_mas_intensa = None\n", + "kp_global_max = 0\n", + "# Recorre todas las tormentas y actualiza estas variables cuando encuentres un Kp mayor\n", + "\n", + "for tormenta in tormentas:\n", + " for tormentita in tormenta[\"allKpIndex\"]:\n", + " if tormentita[\"kpIndex\"]> kp_global_max:\n", + " kp_global_max = tormentita[\"kpIndex\"] \n", + " tormenta_mas_intensa = tormenta[\"gstID\"]\n", + "print(f\"La tormenta más intensa es: {tormenta_mas_intensa}\\n {kp_global_max}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "ej3-md", + "metadata": {}, + "source": [ + "---\n", + "## Ejercicio 3 — Clasificar la severidad con condicionales (25 pts)\n", + "\n", + "**a) (15 pts)** Escribe una función `clasificar_kp(kp)` que reciba un valor de Kp (float) y retorne una cadena con la categoría según la tabla del contexto:\n", + "- Kp < 4 → `\"Quieto\"`\n", + "- Kp == 4 → `\"Activo\"`\n", + "- Kp == 5 → `\"G1 - Menor\"`\n", + "- Kp == 6 → `\"G2 - Moderada\"`\n", + "- Kp == 7 → `\"G3 - Fuerte\"`\n", + "- Kp == 8 → `\"G4 - Severa\"`\n", + "- Kp >= 9 → `\"G5 - Extrema\"`\n", + "\n", + "> **Nota:** el índice Kp puede ser decimal (ej. 8.67). Usa `int(kp)` para redondear hacia abajo y comparar.\n", + "\n", + "**b) (10 pts)** Usa tu función para imprimir una tabla con cada tormenta, su Kp máximo y su categoría." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "ej3-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " kp=2.00 → Quieto\n", + " kp=4.00 → Activo\n", + " kp=5.33 → G1 - Menor\n", + " kp=6.67 → G2 - Moderada\n", + " kp=7.00 → G3 - Fuerte\n", + " kp=8.67 → G4 - Severa\n", + " kp=9.00 → G5 - Extrema\n", + "\n", + "Fecha inicio Kp máx Categoría\n", + "--------------------------------------------------\n", + "2024-05-02T15:00Z 6.67 G2 - Moderada\n", + "2024-05-10T15:00Z 9.0 G5 - Extrema\n", + "2024-05-12T21:00Z 6.33 G2 - Moderada\n", + "2024-05-16T06:00Z 6.0 G2 - Moderada\n", + "2024-05-17T18:00Z 6.0 G2 - Moderada\n" + ] + } + ], + "source": [ + "# ── Parte a) ──────────────────────────────────────────────────────────────────\n", + "# Hint: usa if / elif / else\n", + "# Hint: int(8.67) == 8 — esto te permite usar comparaciones exactas con enteros\n", + "\n", + "def clasificar_kp(kp):\n", + " \"\"\"Clasifica la intensidad de una tormenta según el índice Kp.\"\"\"\n", + " kp = int(kp)\n", + " if kp < 4: \n", + " return \"Quieto\" \n", + " elif kp == 4:\n", + " return \"Activo\"\n", + " elif kp == 5: \n", + " return \"G1 - Menor\"\n", + " elif kp == 6: \n", + " return \"G2 - Moderada\"\n", + " elif kp == 7:\n", + " return \"G3 - Fuerte\"\n", + " elif kp == 8:\n", + " return \"G4 - Severa\"\n", + " else:\n", + " return \"G5 - Extrema\"\n", + " pass\n", + "\n", + "\n", + "# Prueba tu función con estos valores — verifica que los resultados son correctos:\n", + "for kp_prueba in [2.0, 4.0, 5.33, 6.67, 7.0, 8.67, 9.0]:\n", + " print(f\" kp={kp_prueba:.2f} → {clasificar_kp(kp_prueba)}\")\n", + "\n", + "# ── Parte b) ──────────────────────────────────────────────────────────────────\n", + "print(f\"\\n{'Fecha inicio':<22} {'Kp máx':>7} {'Categoría'}\")\n", + "print(\"-\" * 50)\n", + "\n", + "for tormenta in tormentas:\n", + " kp_max = 0\n", + " for tormentita in tormenta[\"allKpIndex\"]:\n", + " if tormentita[\"kpIndex\"]> kp_max:\n", + " kp_max = tormentita[\"kpIndex\"]\n", + " print(f\"{tormenta[\"startTime\"]} {kp_max:10} {clasificar_kp(kp_max)}\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "ej4-md", + "metadata": {}, + "source": [ + "---\n", + "## Ejercicio 4 — Función de análisis completa (35 pts)\n", + "\n", + "Escribe una función `analizar_tormenta(tormenta)` que reciba **un evento** de la lista `tormentas` y retorne un **diccionario** con las siguientes claves:\n", + "\n", + "| Clave | Valor esperado |\n", + "|---|---|\n", + "| `\"id\"` | el `gstID` del evento |\n", + "| `\"inicio\"` | el `startTime` |\n", + "| `\"kp_max\"` | el valor Kp más alto entre todas las mediciones |\n", + "| `\"kp_min\"` | el valor Kp más bajo entre todas las mediciones |\n", + "| `\"kp_promedio\"` | promedio de todos los valores Kp (redondeado a 2 decimales) |\n", + "| `\"num_mediciones\"` | cuántas mediciones hay en `allKpIndex` |\n", + "| `\"categoria\"` | resultado de llamar `clasificar_kp(kp_max)` |\n", + "| `\"eventos_vinculados\"` | número de eventos en `linkedEvents` (0 si es `None`) |\n", + "\n", + "Luego aplícala a **todas las tormentas** con un ciclo `for` e imprime un resumen de cada una." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "ej4-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ID | Fecha de Inicio | Kp Máximo | Kp Minimo | Kp Promedio | Mediciones totales | Categoria | Eventos Vinculados\n", + "2024-05-02T15:00:00-GST-001) | 2024-05-02T15:00Z | 6.67 | 6.67 | 6.67 | 2 | G2 - Moderada | 2\n", + "2024-05-10T15:00:00-GST-001) | 2024-05-10T15:00Z | 9.0 | 6.67 | 8.13 | 13 | G5 - Extrema | 6\n", + "2024-05-12T21:00:00-GST-001) | 2024-05-12T21:00Z | 6.33 | 5.67 | 6.0 | 3 | G2 - Moderada | 2\n", + "2024-05-16T06:00:00-GST-001) | 2024-05-16T06:00Z | 6.0 | 6.0 | 6.0 | 1 | G2 - Moderada | 1\n", + "2024-05-17T18:00:00-GST-001) | 2024-05-17T18:00Z | 6.0 | 6.0 | 6.0 | 1 | G2 - Moderada | 2\n" + ] + } + ], + "source": [ + "def analizar_tormenta(tormenta):\n", + " \"\"\"\n", + " Analiza un evento de tormenta geomagnética.\n", + " \n", + " Parámetro:\n", + " tormenta (dict): un elemento de la lista retornada por el endpoint GST.\n", + " \n", + " Retorna:\n", + " dict con las claves: id, inicio, kp_max, kp_min, kp_promedio,\n", + " num_mediciones, categoria, eventos_vinculados.\n", + " \"\"\"\n", + " # Hint: extrae la lista de mediciones primero\n", + " mediciones = tormenta[\"allKpIndex\"]\n", + " kp_max = 0\n", + " for tormentita in mediciones:\n", + " if tormentita[\"kpIndex\"] > kp_max:\n", + " kp_max = tormentita[\"kpIndex\"]\n", + " kp_min = 10\n", + " for tormentita in mediciones:\n", + " if tormentita[\"kpIndex\"] < kp_min:\n", + " kp_min = tormentita[\"kpIndex\"]\n", + " suma = 0\n", + " for tormentita in mediciones:\n", + " suma = tormentita[\"kpIndex\"] + suma\n", + "\n", + " kp_promedio = round(suma / len(mediciones), 2)\n", + " if tormentita[\"kpIndex\"] > kp_max:\n", + " kp_max = tormentita[\"kpIndex\"]\n", + " \n", + " # Hint: para kp_promedio, suma todos los kpIndex y divide entre len(mediciones)\n", + " # usa round(..., 2) para redondear a 2 decimales\n", + " \n", + " # Hint: linkedEvents puede ser None — usa (tormenta[\"linkedEvents\"] or [])\n", + " # para tratar None como lista vacía y poder llamar len() sin error\n", + " \n", + " # TU CÓDIGO AQUÍ\n", + " resultado = {\n", + " \"id\": tormenta[\"gstID\"],\n", + " \"inicio\": tormenta[\"startTime\"],\n", + " \"kp_max\": kp_max,\n", + " \"kp_min\": kp_min,\n", + " \"kp_promedio\": kp_promedio,\n", + " \"num_mediciones\": len(mediciones),\n", + " \"categoria\": clasificar_kp(kp_max),\n", + " \"eventos_vinculados\": len(tormenta[\"linkedEvents\"] or []),\n", + " }\n", + " return resultado\n", + "\n", + "\n", + "\n", + "# Aplicar a todas las tormentas e imprimir resumen\n", + "print(f\"{'ID':<28} | {'Fecha de Inicio':<19}| {'Kp Máximo':>1} | Kp Minimo | Kp Promedio | Mediciones totales | {'Categoria':<15} | Eventos Vinculados\")\n", + "for tormenta in tormentas:\n", + " r = analizar_tormenta(tormenta)\n", + " # Hint: accede a cada clave del dict retornado con r[\"clave\"]\n", + " print(f\"{r['id']}) | {r['inicio']} | {r['kp_max']:<8} | {r['kp_min']:<8} | {r['kp_promedio']:<9} | {r['num_mediciones']:20} | {r['categoria']:<15} | {r['eventos_vinculados']:18}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "conclusion-md", + "metadata": {}, + "source": [ + "---\n", + "## Pregunta final — Conclusiones (incluida en el punteo del Ejercicio 4)\n", + "\n", + "Basandote en los datos que obtuviste a lo largo del examen, escribe en la celda de abajo \n", + "una conclusion en tus propias palabras. Puedes responder estas preguntas como guia:\n", + "\n", + "- ¿Cual fue la tormenta mas intensa del periodo analizado y que tan severa fue segun la escala Kp?\n", + "- ¿Que patrones observas en los datos? (frecuencia, intensidad, eventos vinculados)\n", + "- ¿Que impacto podria haber tenido una tormenta de esa magnitud en la vida cotidiana?" + ] + }, + { + "cell_type": "markdown", + "id": "conclusion-answer", + "metadata": {}, + "source": [ + "**Conclusiones:**\n", + "\n", + "\"1. La tormenta más extrema a sido, esta, 2024-05-10T15:00Z escala, 9.0 de tipo G5 - Extrema\"\n", + "\n", + "\"2. Normalmente se mantienen en una escala de G2 entre 6 y 6.5 - Moderada\"\n", + "\n", + "\"3. La última fue en los 1800's, esta afecto a los sistemas de comunicacion de aquel entonces, dañando el primitivo tendido electrico de la epoca y causando incendios sobre estos mismos, en la epoca moderna, seria un problema, puesto a que dependemos demasiado de la electricidad, las pérdidas serian escandalosas a nivel global, en diferentes ámbitos.\"" + ] + }, + { + "cell_type": "markdown", + "id": "sello-md", + "metadata": {}, + "source": [ + "---\n", + "## Sello de entrega\n", + "\n", + "Ejecuta la siguiente celda **como ultimo paso**, justo antes de guardar y subir el notebook.\n", + "\n", + "Realiza una consulta a la API de la ISS para registrar tu posicion geografica aproximada \n", + "al momento de entregar. Dado que la ISS se desplaza ~30 km cada 4 segundos, dos estudiantes \n", + "que entreguen en momentos distintos obtendran coordenadas completamente diferentes.\n", + "\n", + "> Este sello es **unico e irrepetible**: queda vinculado a tu carnet y al instante \n", + "> exacto en que ejecutaste la celda." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "sello-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Consultando posicion de la ISS...\n", + "\n", + "=======================================================\n", + " SELLO DE ENTREGA -- PARCIAL 2\n", + "=======================================================\n", + " Estudiante : Alejandro Puac\n", + " Carnet : 202604047\n", + " Fecha/Hora : 2026-04-22 04:45:30 UTC\n", + " ISS Latitud : -4.5175 deg\n", + " ISS Longitud : -68.0475 deg\n", + " ISS Hora UTC : 2026-04-22 04:45:31 UTC\n", + "=======================================================\n" + ] + } + ], + "source": [ + "from datetime import datetime, timezone\n", + "\n", + "# Obtener la posicion actual de la ISS\n", + "print(\"Consultando posicion de la ISS...\")\n", + "try:\n", + " r_iss = requests.get(\"http://api.open-notify.org/iss-now.json\", timeout=8)\n", + " r_iss.raise_for_status()\n", + " iss_data = r_iss.json()\n", + " iss_lat = float(iss_data[\"iss_position\"][\"latitude\"])\n", + " iss_lon = float(iss_data[\"iss_position\"][\"longitude\"])\n", + " iss_ts = iss_data[\"timestamp\"]\n", + " iss_hora = datetime.fromtimestamp(iss_ts, tz=timezone.utc).strftime(\"%Y-%m-%d %H:%M:%S UTC\")\n", + " iss_ok = True\n", + "except Exception as e:\n", + " iss_lat, iss_lon, iss_hora = 0.0, 0.0, \"no disponible\"\n", + " iss_ok = False\n", + " print(f\" Advertencia: no se pudo obtener la posicion ISS ({e})\")\n", + "\n", + "# Timestamp local del momento de entrega\n", + "ts_entrega = datetime.now(tz=timezone.utc).strftime(\"%Y-%m-%d %H:%M:%S UTC\")\n", + "\n", + "# Imprimir el sello\n", + "print()\n", + "print(\"=\" * 55)\n", + "print(\" SELLO DE ENTREGA -- PARCIAL 2\")\n", + "print(\"=\" * 55)\n", + "print(f\" Estudiante : {NOMBRE}\")\n", + "print(f\" Carnet : {CARNET}\")\n", + "print(f\" Fecha/Hora : {ts_entrega}\")\n", + "print(f\" ISS Latitud : {iss_lat:+.4f} deg\")\n", + "print(f\" ISS Longitud : {iss_lon:+.4f} deg\")\n", + "print(f\" ISS Hora UTC : {iss_hora}\")\n", + "print(\"=\" * 55)\n", + "\n", + "if not iss_ok:\n", + " print(\" Advertencia: sello generado sin datos de ISS (sin conexion).\")\n", + " print(\" Guarda el notebook de todas formas.\")" + ] + }, + { + "cell_type": "markdown", + "id": "cierre", + "metadata": {}, + "source": [ + "---\n", + "## Entrega\n", + "\n", + "1. Ejecuta la celda **Sello de entrega** (arriba)\n", + "2. Guarda el notebook: **Archivo → Guardar** (o `Ctrl+S`)\n", + "3. Verifica que **todas las celdas tienen output** (ejecuta: **Kernel → Restart & Run All**)\n", + "4. Sube el archivo a Github y copia el link en el campo de entrega en la U Virtual" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e9a231d-3229-4072-92d4-bac281a69979", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Examen_Parcial_Alejandro_Puac.ipynb b/Examen_Parcial_Alejandro_Puac.ipynb new file mode 100644 index 0000000..978608e --- /dev/null +++ b/Examen_Parcial_Alejandro_Puac.ipynb @@ -0,0 +1,797 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "title", + "metadata": {}, + "source": [ + "# Examen Parcial 2 — Programación 1 (F12)\n", + "## Tormentas Geomagnéticas con la API de NASA / DONKI\n", + "\n", + "**Nombre:** Alejandro Sebastian Puac Shar \n", + "**Carnet:** 202604047 \n", + "**Fecha:** 21/04/2026 \n", + "**Punteo total:** 100 puntos\n", + "\n", + "---\n", + "\n", + "| Ejercicio | Tema | Puntos |\n", + "|---|---|:---:|\n", + "| 1a | Construir la URL y realizar la solicitud HTTP | 10 |\n", + "| 1b | Explorar la lista de tormentas con un ciclo `for` | 10 |\n", + "| 2 | Calcular el Kp máximo e identificar la tormenta más intensa | 20 |\n", + "| 3 | Función `clasificar_kp()` con condicionales | 25 |\n", + "| 4 | Función `analizar_tormenta()` que retorna un diccionario | 35 |\n", + "| | **Total** | **100** |\n", + "\n", + "---\n", + "\n", + "> **Instrucciones generales**\n", + "> - Completa cada ejercicio en la celda indicada.\n", + "> - Ejecuta las celdas **en orden** de arriba hacia abajo.\n", + "> - Puedes consultar tus apuntes y el notebook de clase `XML_JSON_APIs.ipynb`.\n", + "> - Entrega el notebook con **todas las celdas ejecutadas** (con output visible)." + ] + }, + { + "cell_type": "markdown", + "id": "background", + "metadata": {}, + "source": [ + "---\n", + "## Contexto: ¿Qué es una tormenta geomagnética?\n", + "\n", + "El Sol emite constantemente partículas cargadas (viento solar). Cuando ocurre una erupción solar intensa, una ola de partículas choca con el campo magnético de la Tierra y provoca una **tormenta geomagnética**.\n", + "\n", + "Estos eventos pueden:\n", + "- Generar auroras boreales visibles en latitudes bajas\n", + "- Interferir con satélites GPS y comunicaciones de radio\n", + "- En casos extremos, dañar redes eléctricas (como el apagón de Quebec en 1989)\n", + "\n", + "### Índice Kp — escala de intensidad\n", + "\n", + "El **índice Kp** (0–9) mide la perturbación del campo magnético terrestre:\n", + "\n", + "| Kp | Categoría | Descripción |\n", + "|---|---|---|\n", + "| 0 – 3 | Quieto | Sin tormenta |\n", + "| 4 | Activo | Perturbación menor |\n", + "| 5 | **G1** — Menor | Auroras en latitudes altas |\n", + "| 6 | **G2** — Moderada | Auroras hasta latitud 55° |\n", + "| 7 | **G3** — Fuerte | Auroras hasta latitud 50° |\n", + "| 8 | **G4** — Severa | Problemas en redes eléctricas |\n", + "| 9 | **G5** — Extrema | Apagones posibles, auroras tropicales |\n", + "\n", + "La tormenta de mayo 2024 alcanzó **G5** — la más intensa en 20 años." + ] + }, + { + "cell_type": "markdown", + "id": "api-docs", + "metadata": {}, + "source": [ + "---\n", + "## El endpoint: DONKI / GST\n", + "\n", + "**DONKI** = Space Weather Database Of Notifications, Knowledge, Information \n", + "**GST** = Geomagnetic Storm\n", + "\n", + "```\n", + "GET https://api.nasa.gov/DONKI/GST\n", + "```\n", + "\n", + "| Parámetro | Tipo | Default | Descripción |\n", + "|---|---|---|---|\n", + "| `startDate` | YYYY-MM-DD | 30 días atrás | Inicio del rango de fechas |\n", + "| `endDate` | YYYY-MM-DD | hoy | Fin del rango de fechas |\n", + "| `api_key` | string | DEMO_KEY | Tu API key de api.nasa.gov |\n", + "\n", + "**Ejemplo de URL:**\n", + "```\n", + "https://api.nasa.gov/DONKI/GST?startDate=2024-05-01&endDate=2024-05-31&api_key=DEMO_KEY\n", + "```\n", + "\n", + "### Estructura de la respuesta\n", + "\n", + "La API devuelve una **lista** de eventos. Cada evento es un diccionario con esta estructura:\n", + "\n", + "```json\n", + "{\n", + " \"gstID\": \"2024-05-10T17:00:00-GST-001\",\n", + " \"startTime\": \"2024-05-10T17:00Z\",\n", + " \"allKpIndex\": [\n", + " { \"observedTime\": \"2024-05-10T21:00Z\", \"kpIndex\": 8.67, \"source\": \"NOAA\" },\n", + " { \"observedTime\": \"2024-05-11T00:00Z\", \"kpIndex\": 9.0, \"source\": \"NOAA\" }\n", + " ],\n", + " \"linkedEvents\": [\n", + " { \"activityID\": \"2024-05-08T21:08:00-CME-001\" }\n", + " ],\n", + " \"link\": \"https://webtools.ccmc.gsfc.nasa.gov/DONKI/view/GST/...\"\n", + "}\n", + "```\n", + "\n", + "**Campos importantes:**\n", + "- `gstID` — identificador único del evento\n", + "- `startTime` — cuándo comenzó la tormenta (formato ISO 8601 UTC)\n", + "- `allKpIndex` — lista de mediciones Kp durante la tormenta\n", + "- `linkedEvents` — eventos espaciales relacionados (erupciones, CME) — puede ser `None`" + ] + }, + { + "cell_type": "markdown", + "id": "setup-title", + "metadata": {}, + "source": [ + "---\n", + "## Paso 1 — Configuración de la API Key\n", + "\n", + "### Obtener tu API key (gratis)\n", + "\n", + "1. Ve a **https://api.nasa.gov**\n", + "2. Completa el formulario con tu nombre y correo electrónico\n", + "3. Haz clic en **\"Signup\"**\n", + "4. Revisa tu correo — recibirás la key en minutos\n", + "5. La key tiene este formato: `TU_API_KEY_AQUI_XXXXXXXXXXXXXXXXXXXXXXXX`\n", + "\n", + "Con tu propia key puedes hacer **1,000 solicitudes por hora** (vs 30 con DEMO_KEY).\n", + "\n", + "---\n", + "\n", + "### Configurar la variable de entorno `NASA_API_KEY`\n", + "\n", + "Una variable de entorno guarda tu key **fuera del código**, para que no quede expuesta \n", + "en el archivo del notebook ni en el historial de git.\n", + "\n", + "#### Linux / macOS — temporal (solo la sesión actual)\n", + "```bash\n", + "export NASA_API_KEY=\"tu_key_aqui\"\n", + "jupyter notebook\n", + "```\n", + "\n", + "#### Linux / macOS — permanente\n", + "Agrega esta línea al final de `~/.bashrc` (bash) o `~/.zshrc` (zsh):\n", + "```bash\n", + "echo 'export NASA_API_KEY=\"tu_key_aqui\"' >> ~/.bashrc\n", + "source ~/.bashrc # aplicar sin reiniciar\n", + "```\n", + "\n", + "#### Windows — PowerShell (temporal)\n", + "```powershell\n", + "$env:NASA_API_KEY = \"tu_key_aqui\"\n", + "jupyter notebook\n", + "```\n", + "\n", + "#### Windows — permanente (Panel de control)\n", + "1. Busca **\"Variables de entorno\"** en el menú Inicio\n", + "2. Clic en **\"Editar las variables de entorno del sistema\"**\n", + "3. Botón **\"Variables de entorno...\"**\n", + "4. En \"Variables de usuario\" → **Nueva**\n", + "5. Nombre: `NASA_API_KEY` | Valor: tu key\n", + "6. Aceptar y **reiniciar** Jupyter\n", + "\n", + "#### Verificar que está configurada\n", + "```bash\n", + "# Linux/macOS\n", + "echo $NASA_API_KEY\n", + "\n", + "# Windows PowerShell\n", + "echo $env:NASA_API_KEY\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "setup-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✓ API key cargada: nOVI************************************\n", + "\n", + "Librerías importadas correctamente ✓\n" + ] + } + ], + "source": [ + "import os\n", + "import requests\n", + "import json\n", + "import time\n", + "\n", + "# Cargar la API key desde la variable de entorno.\n", + "# Si no está configurada, usa DEMO_KEY como respaldo (30 req/hora).\n", + "API_KEY = os.getenv(\"NASA_API_KEY\", \"\").strip() or \"DEMO_KEY\"\n", + "\n", + "if API_KEY == \"DEMO_KEY\":\n", + " print(\"⚠ Usando DEMO_KEY — límite: 30 solicitudes/hora\")\n", + " print(\" Configura NASA_API_KEY para mayor límite (ver instrucciones arriba).\")\n", + "else:\n", + " print(f\"✓ API key cargada: {API_KEY[:4]}{'*' * (len(API_KEY) - 4)}\")\n", + "\n", + "print(\"\\nLibrerías importadas correctamente ✓\")" + ] + }, + { + "cell_type": "markdown", + "id": "ident-md", + "metadata": {}, + "source": [ + "---\n", + "## Identificacion del estudiante\n", + "\n", + "Completa tu nombre y carnet en la siguiente celda y **ejecutala antes de comenzar**. \n", + "Estos datos quedan registrados en el output del notebook junto con la posicion de la ISS \n", + "en el momento exacto en que realizas el examen." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ident-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Datos completados correctamente.\n", + "Estudiante : Alejandro Puac\n", + "Carnet : 202604047\n" + ] + } + ], + "source": [ + "# Completa tus datos\n", + "NOMBRE = \"Alejandro Puac\" # TU NOMBRE AQUI\n", + "CARNET = \"202604047\" # TU CARNET AQUI\n", + "\n", + "if NOMBRE == \"Alejandro Puac\" and CARNET == \"202604047\":\n", + " print(\"Datos completados correctamente.\")\n", + "else:\n", + " raise ValueError(\"Completa tu nombre y carnet antes de continuar.\")\n", + "\n", + "print(f\"Estudiante : {NOMBRE}\")\n", + "print(f\"Carnet : {CARNET}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ej1-md", + "metadata": {}, + "source": [ + "---\n", + "## Ejercicio 1 — Obtener y explorar los datos (20 pts)\n", + "\n", + "### Parte a) — Solicitud al endpoint (10 pts)\n", + "\n", + "Se te da la URL base del endpoint. Construye la URL completa usando un f-string e incluye los parámetros `startDate`, `endDate` y `api_key`. Luego realiza la solicitud y verifica que fue exitosa.\n", + "\n", + "---\n", + "\n", + "### ¿Por qué verificar el código de estado antes de leer el JSON?\n", + "\n", + "Cuando hacemos una solicitud HTTP el servidor siempre responde con un **código de estado numérico** que indica si todo salió bien o hubo un problema.\n", + "\n", + "El código **200** significa *\"OK — la solicitud fue exitosa y el cuerpo de la respuesta contiene los datos pedidos\"*.\n", + "\n", + "Si el servidor devuelve un código diferente (por ejemplo **503 - Service Unavailable** o **429 - Too Many Requests**), el cuerpo de la respuesta **no contiene JSON válido** — puede estar vacío o contener un mensaje de error en texto plano. Si intentamos llamar `.json()` directamente sin verificar, el programa lanzará un `JSONDecodeError`.\n", + "\n", + "Por eso siempre debemos verificar que `respuesta.status_code == 200` (o equivalentemente `respuesta.ok`) **antes** de intentar parsear la respuesta.\n", + "\n", + "> **Escribe en la celda de código siguiente: ¿qué imprimirías tú para que el usuario sepa qué salió mal cuando el código no es 200?**" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ej1-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Solicitud exitosa ✓ — 5 tormentas recibidas\n" + ] + } + ], + "source": [ + "URL_BASE = \"https://api.nasa.gov/DONKI/GST\"\n", + "\n", + "# Agrega también los parámetros startDate y endDate:\n", + "startDate = \"2024-05-01\"\n", + "endDate = \"2024-05-31\"\n", + "# Hint: los parámetros van separados por \"&\" → ?param1=val1¶m2=val2\n", + "\n", + "url = f\"{URL_BASE}?startDate={startDate}&endDate={endDate}&api_key={API_KEY}\"\n", + "\n", + "# Completa la llamada — usa timeout=15\n", + "respuesta = requests.get(url, timeout=15)\n", + "\n", + "# Verifica que la respuesta fue exitosa antes de parsear el JSON\n", + "# Hint: usa respuesta.ok o compara respuesta.status_code con 200\n", + "# Hint: si hay error, imprime el código y el cuerpo — respuesta.text tiene el mensaje\n", + "if respuesta.status_code != 200:\n", + " print(f\"No se ha ejecutado correctamente el codigo, Codigo de Error: {respuesta.status_code}\\n{respuesta.text}\") # TU CÓDIGO AQUÍ — mensaje informativo\n", + " raise SystemExit(\"No se pudo obtener la respuesta. Intenta de nuevo.\")\n", + "\n", + "tormentas = respuesta.json()\n", + "print(f\"Solicitud exitosa ✓ — {len(tormentas)} tormentas recibidas\")" + ] + }, + { + "cell_type": "markdown", + "id": "ej1-url-warning", + "metadata": {}, + "source": [ + "> ⚠️ **Cuidado con exponer tu API key** \n", + "> Puedes descomentar el `print` de la celda siguiente para verificar que la URL se formó correctamente, pero **vuelve a comentarlo antes de guardar y entregar el notebook**. Si lo dejas activo, tu API key quedará visible en el output del archivo." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ej1-url-check", + "metadata": {}, + "outputs": [], + "source": [ + "# Descomenta la siguiente línea para verificar que la URL se formó correctamente.\n", + "# ⚠ Vuelve a comentarla antes de entregar — el output guarda tu API key en el archivo.\n", + "\n", + "# print(\"URL:\", url)" + ] + }, + { + "cell_type": "markdown", + "id": "ej1b-md", + "metadata": {}, + "source": [ + "### Parte b) — Explorar la lista de tormentas (10 pts)\n", + "\n", + "Usando un ciclo `for`, imprime cuántas tormentas hubo en el período y el `gstID` y `startTime` de cada una." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ej1b-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total de tormentas:5\n", + "gstID2024-05-02T15:00:00-GST-001 **Tiempo de Inicio: 2024-05-02T15:00Z\n", + "gstID2024-05-10T15:00:00-GST-001 **Tiempo de Inicio: 2024-05-10T15:00Z\n", + "gstID2024-05-12T21:00:00-GST-001 **Tiempo de Inicio: 2024-05-12T21:00Z\n", + "gstID2024-05-16T06:00:00-GST-001 **Tiempo de Inicio: 2024-05-16T06:00Z\n", + "gstID2024-05-17T18:00:00-GST-001 **Tiempo de Inicio: 2024-05-17T18:00Z\n" + ] + } + ], + "source": [ + "# Hint: len(tormentas) da el total de eventos\n", + "# Hint: cada elemento de tormentas es un dict — accede con tormenta[\"gstID\"], etc.\n", + "print(f\"Total de tormentas:{len(tormentas)}\")\n", + "for tormenta in tormentas: \n", + " print(f\"gstID{tormenta[\"gstID\"]} **Tiempo de Inicio: {tormenta[\"startTime\"]}\") \n" + ] + }, + { + "cell_type": "markdown", + "id": "ej2-md", + "metadata": {}, + "source": [ + "---\n", + "## Ejercicio 2 — Kp máximo por tormenta (20 pts)\n", + "\n", + "Cada tormenta contiene una lista `allKpIndex` con múltiples mediciones a lo largo del tiempo.\n", + "\n", + "**a) (10 pts)** Para cada tormenta, encuentra el **valor máximo de Kp** usando un ciclo `for` sobre `allKpIndex`. Imprime el `startTime` y el Kp máximo de cada tormenta.\n", + "\n", + "**b) (10 pts)** Encuentra e imprime la tormenta **más intensa** del período (la que tuvo el mayor Kp máximo entre todas las tormentas)." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "ej2-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 2024-05-02T15:00Z Kp máx = 6.67\n", + " 2024-05-10T15:00Z Kp máx = 9.00\n", + " 2024-05-12T21:00Z Kp máx = 6.33\n", + " 2024-05-16T06:00Z Kp máx = 6.00\n", + " 2024-05-17T18:00Z Kp máx = 6.00\n", + "La tormenta más intensa es: 2024-05-10T15:00:00-GST-001\n", + " 9.0\n" + ] + } + ], + "source": [ + "# ── Parte a) ──────────────────────────────────────────────────────────────────\n", + "# Hint: allKpIndex es una lista de dicts, cada uno con la clave \"kpIndex\"\n", + "# Hint: para encontrar el máximo de una lista puedes usar la función max()\n", + "# o un ciclo que compare con una variable auxiliar (kp_max = 0)\n", + "\n", + "for tormenta in tormentas:\n", + " kp_max = 0\n", + " for tormentita in tormenta[\"allKpIndex\"]:\n", + " if tormentita[\"kpIndex\"]> kp_max:\n", + " kp_max = tormentita[\"kpIndex\"]\n", + " print(f\" {tormenta['startTime']} Kp máx = {kp_max:.2f}\")\n", + "\n", + "# ── Parte b) ──────────────────────────────────────────────────────────────────\n", + "# Hint: necesitas dos variables auxiliares:\n", + "tormenta_mas_intensa = None\n", + "kp_global_max = 0\n", + "# Recorre todas las tormentas y actualiza estas variables cuando encuentres un Kp mayor\n", + "\n", + "for tormenta in tormentas:\n", + " for tormentita in tormenta[\"allKpIndex\"]:\n", + " if tormentita[\"kpIndex\"]> kp_global_max:\n", + " kp_global_max = tormentita[\"kpIndex\"] \n", + " tormenta_mas_intensa = tormenta[\"gstID\"]\n", + "print(f\"La tormenta más intensa es: {tormenta_mas_intensa}\\n {kp_global_max}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "ej3-md", + "metadata": {}, + "source": [ + "---\n", + "## Ejercicio 3 — Clasificar la severidad con condicionales (25 pts)\n", + "\n", + "**a) (15 pts)** Escribe una función `clasificar_kp(kp)` que reciba un valor de Kp (float) y retorne una cadena con la categoría según la tabla del contexto:\n", + "- Kp < 4 → `\"Quieto\"`\n", + "- Kp == 4 → `\"Activo\"`\n", + "- Kp == 5 → `\"G1 - Menor\"`\n", + "- Kp == 6 → `\"G2 - Moderada\"`\n", + "- Kp == 7 → `\"G3 - Fuerte\"`\n", + "- Kp == 8 → `\"G4 - Severa\"`\n", + "- Kp >= 9 → `\"G5 - Extrema\"`\n", + "\n", + "> **Nota:** el índice Kp puede ser decimal (ej. 8.67). Usa `int(kp)` para redondear hacia abajo y comparar.\n", + "\n", + "**b) (10 pts)** Usa tu función para imprimir una tabla con cada tormenta, su Kp máximo y su categoría." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "ej3-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " kp=2.00 → Quieto\n", + " kp=4.00 → Activo\n", + " kp=5.33 → G1 - Menor\n", + " kp=6.67 → G2 - Moderada\n", + " kp=7.00 → G3 - Fuerte\n", + " kp=8.67 → G4 - Severa\n", + " kp=9.00 → G5 - Extrema\n", + "\n", + "Fecha inicio Kp máx Categoría\n", + "--------------------------------------------------\n", + "2024-05-02T15:00Z 6.67 G2 - Moderada\n", + "2024-05-10T15:00Z 9.0 G5 - Extrema\n", + "2024-05-12T21:00Z 6.33 G2 - Moderada\n", + "2024-05-16T06:00Z 6.0 G2 - Moderada\n", + "2024-05-17T18:00Z 6.0 G2 - Moderada\n" + ] + } + ], + "source": [ + "# ── Parte a) ──────────────────────────────────────────────────────────────────\n", + "# Hint: usa if / elif / else\n", + "# Hint: int(8.67) == 8 — esto te permite usar comparaciones exactas con enteros\n", + "\n", + "def clasificar_kp(kp):\n", + " \"\"\"Clasifica la intensidad de una tormenta según el índice Kp.\"\"\"\n", + " kp = int(kp)\n", + " if kp < 4: \n", + " return \"Quieto\" \n", + " elif kp == 4:\n", + " return \"Activo\"\n", + " elif kp == 5: \n", + " return \"G1 - Menor\"\n", + " elif kp == 6: \n", + " return \"G2 - Moderada\"\n", + " elif kp == 7:\n", + " return \"G3 - Fuerte\"\n", + " elif kp == 8:\n", + " return \"G4 - Severa\"\n", + " else:\n", + " return \"G5 - Extrema\"\n", + " pass\n", + "\n", + "\n", + "# Prueba tu función con estos valores — verifica que los resultados son correctos:\n", + "for kp_prueba in [2.0, 4.0, 5.33, 6.67, 7.0, 8.67, 9.0]:\n", + " print(f\" kp={kp_prueba:.2f} → {clasificar_kp(kp_prueba)}\")\n", + "\n", + "# ── Parte b) ──────────────────────────────────────────────────────────────────\n", + "print(f\"\\n{'Fecha inicio':<22} {'Kp máx':>7} {'Categoría'}\")\n", + "print(\"-\" * 50)\n", + "\n", + "for tormenta in tormentas:\n", + " kp_max = 0\n", + " for tormentita in tormenta[\"allKpIndex\"]:\n", + " if tormentita[\"kpIndex\"]> kp_max:\n", + " kp_max = tormentita[\"kpIndex\"]\n", + " print(f\"{tormenta[\"startTime\"]} {kp_max:10} {clasificar_kp(kp_max)}\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "ej4-md", + "metadata": {}, + "source": [ + "---\n", + "## Ejercicio 4 — Función de análisis completa (35 pts)\n", + "\n", + "Escribe una función `analizar_tormenta(tormenta)` que reciba **un evento** de la lista `tormentas` y retorne un **diccionario** con las siguientes claves:\n", + "\n", + "| Clave | Valor esperado |\n", + "|---|---|\n", + "| `\"id\"` | el `gstID` del evento |\n", + "| `\"inicio\"` | el `startTime` |\n", + "| `\"kp_max\"` | el valor Kp más alto entre todas las mediciones |\n", + "| `\"kp_min\"` | el valor Kp más bajo entre todas las mediciones |\n", + "| `\"kp_promedio\"` | promedio de todos los valores Kp (redondeado a 2 decimales) |\n", + "| `\"num_mediciones\"` | cuántas mediciones hay en `allKpIndex` |\n", + "| `\"categoria\"` | resultado de llamar `clasificar_kp(kp_max)` |\n", + "| `\"eventos_vinculados\"` | número de eventos en `linkedEvents` (0 si es `None`) |\n", + "\n", + "Luego aplícala a **todas las tormentas** con un ciclo `for` e imprime un resumen de cada una." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "ej4-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ID | Fecha de Inicio | Kp Máximo | Kp Minimo | Kp Promedio | Mediciones totales | Categoria | Eventos Vinculados\n", + "2024-05-02T15:00:00-GST-001) | 2024-05-02T15:00Z | 6.67 | 6.67 | 6.67 | 2 | G2 - Moderada | 2\n", + "2024-05-10T15:00:00-GST-001) | 2024-05-10T15:00Z | 9.0 | 6.67 | 8.13 | 13 | G5 - Extrema | 6\n", + "2024-05-12T21:00:00-GST-001) | 2024-05-12T21:00Z | 6.33 | 5.67 | 6.0 | 3 | G2 - Moderada | 2\n", + "2024-05-16T06:00:00-GST-001) | 2024-05-16T06:00Z | 6.0 | 6.0 | 6.0 | 1 | G2 - Moderada | 1\n", + "2024-05-17T18:00:00-GST-001) | 2024-05-17T18:00Z | 6.0 | 6.0 | 6.0 | 1 | G2 - Moderada | 2\n" + ] + } + ], + "source": [ + "def analizar_tormenta(tormenta):\n", + " \"\"\"\n", + " Analiza un evento de tormenta geomagnética.\n", + " \n", + " Parámetro:\n", + " tormenta (dict): un elemento de la lista retornada por el endpoint GST.\n", + " \n", + " Retorna:\n", + " dict con las claves: id, inicio, kp_max, kp_min, kp_promedio,\n", + " num_mediciones, categoria, eventos_vinculados.\n", + " \"\"\"\n", + " # Hint: extrae la lista de mediciones primero\n", + " mediciones = tormenta[\"allKpIndex\"]\n", + " kp_max = 0\n", + " for tormentita in mediciones:\n", + " if tormentita[\"kpIndex\"] > kp_max:\n", + " kp_max = tormentita[\"kpIndex\"]\n", + " kp_min = 10\n", + " for tormentita in mediciones:\n", + " if tormentita[\"kpIndex\"] < kp_min:\n", + " kp_min = tormentita[\"kpIndex\"]\n", + " suma = 0\n", + " for tormentita in mediciones:\n", + " suma = tormentita[\"kpIndex\"] + suma\n", + "\n", + " kp_promedio = round(suma / len(mediciones), 2)\n", + " if tormentita[\"kpIndex\"] > kp_max:\n", + " kp_max = tormentita[\"kpIndex\"]\n", + " \n", + " # Hint: para kp_promedio, suma todos los kpIndex y divide entre len(mediciones)\n", + " # usa round(..., 2) para redondear a 2 decimales\n", + " \n", + " # Hint: linkedEvents puede ser None — usa (tormenta[\"linkedEvents\"] or [])\n", + " # para tratar None como lista vacía y poder llamar len() sin error\n", + " \n", + " # TU CÓDIGO AQUÍ\n", + " resultado = {\n", + " \"id\": tormenta[\"gstID\"],\n", + " \"inicio\": tormenta[\"startTime\"],\n", + " \"kp_max\": kp_max,\n", + " \"kp_min\": kp_min,\n", + " \"kp_promedio\": kp_promedio,\n", + " \"num_mediciones\": len(mediciones),\n", + " \"categoria\": clasificar_kp(kp_max),\n", + " \"eventos_vinculados\": len(tormenta[\"linkedEvents\"] or []),\n", + " }\n", + " return resultado\n", + "\n", + "\n", + "\n", + "# Aplicar a todas las tormentas e imprimir resumen\n", + "print(f\"{'ID':<28} | {'Fecha de Inicio':<19}| {'Kp Máximo':>1} | Kp Minimo | Kp Promedio | Mediciones totales | {'Categoria':<15} | Eventos Vinculados\")\n", + "for tormenta in tormentas:\n", + " r = analizar_tormenta(tormenta)\n", + " # Hint: accede a cada clave del dict retornado con r[\"clave\"]\n", + " print(f\"{r['id']}) | {r['inicio']} | {r['kp_max']:<8} | {r['kp_min']:<8} | {r['kp_promedio']:<9} | {r['num_mediciones']:20} | {r['categoria']:<15} | {r['eventos_vinculados']:18}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "conclusion-md", + "metadata": {}, + "source": [ + "---\n", + "## Pregunta final — Conclusiones (incluida en el punteo del Ejercicio 4)\n", + "\n", + "Basandote en los datos que obtuviste a lo largo del examen, escribe en la celda de abajo \n", + "una conclusion en tus propias palabras. Puedes responder estas preguntas como guia:\n", + "\n", + "- ¿Cual fue la tormenta mas intensa del periodo analizado y que tan severa fue segun la escala Kp?\n", + "- ¿Que patrones observas en los datos? (frecuencia, intensidad, eventos vinculados)\n", + "- ¿Que impacto podria haber tenido una tormenta de esa magnitud en la vida cotidiana?" + ] + }, + { + "cell_type": "markdown", + "id": "conclusion-answer", + "metadata": {}, + "source": [ + "**Conclusiones:**\n", + "\n", + "\"1. La tormenta más extrema a sido, esta, 2024-05-10T15:00Z escala, 9.0 de tipo G5 - Extrema\"\n", + "\n", + "\"2. Normalmente se mantienen en una escala de G2 entre 6 y 6.5 - Moderada\"\n", + "\n", + "\"3. La última fue en los 1800's, esta afecto a los sistemas de comunicacion de aquel entonces, dañando el primitivo tendido electrico de la epoca y causando incendios sobre estos mismos, en la epoca moderna, seria un problema, puesto a que dependemos demasiado de la electricidad, las pérdidas serian escandalosas a nivel global, en diferentes ámbitos.\"" + ] + }, + { + "cell_type": "markdown", + "id": "sello-md", + "metadata": {}, + "source": [ + "---\n", + "## Sello de entrega\n", + "\n", + "Ejecuta la siguiente celda **como ultimo paso**, justo antes de guardar y subir el notebook.\n", + "\n", + "Realiza una consulta a la API de la ISS para registrar tu posicion geografica aproximada \n", + "al momento de entregar. Dado que la ISS se desplaza ~30 km cada 4 segundos, dos estudiantes \n", + "que entreguen en momentos distintos obtendran coordenadas completamente diferentes.\n", + "\n", + "> Este sello es **unico e irrepetible**: queda vinculado a tu carnet y al instante \n", + "> exacto en que ejecutaste la celda." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "sello-code", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Consultando posicion de la ISS...\n", + "\n", + "=======================================================\n", + " SELLO DE ENTREGA -- PARCIAL 2\n", + "=======================================================\n", + " Estudiante : Alejandro Puac\n", + " Carnet : 202604047\n", + " Fecha/Hora : 2026-04-22 04:45:30 UTC\n", + " ISS Latitud : -4.5175 deg\n", + " ISS Longitud : -68.0475 deg\n", + " ISS Hora UTC : 2026-04-22 04:45:31 UTC\n", + "=======================================================\n" + ] + } + ], + "source": [ + "from datetime import datetime, timezone\n", + "\n", + "# Obtener la posicion actual de la ISS\n", + "print(\"Consultando posicion de la ISS...\")\n", + "try:\n", + " r_iss = requests.get(\"http://api.open-notify.org/iss-now.json\", timeout=8)\n", + " r_iss.raise_for_status()\n", + " iss_data = r_iss.json()\n", + " iss_lat = float(iss_data[\"iss_position\"][\"latitude\"])\n", + " iss_lon = float(iss_data[\"iss_position\"][\"longitude\"])\n", + " iss_ts = iss_data[\"timestamp\"]\n", + " iss_hora = datetime.fromtimestamp(iss_ts, tz=timezone.utc).strftime(\"%Y-%m-%d %H:%M:%S UTC\")\n", + " iss_ok = True\n", + "except Exception as e:\n", + " iss_lat, iss_lon, iss_hora = 0.0, 0.0, \"no disponible\"\n", + " iss_ok = False\n", + " print(f\" Advertencia: no se pudo obtener la posicion ISS ({e})\")\n", + "\n", + "# Timestamp local del momento de entrega\n", + "ts_entrega = datetime.now(tz=timezone.utc).strftime(\"%Y-%m-%d %H:%M:%S UTC\")\n", + "\n", + "# Imprimir el sello\n", + "print()\n", + "print(\"=\" * 55)\n", + "print(\" SELLO DE ENTREGA -- PARCIAL 2\")\n", + "print(\"=\" * 55)\n", + "print(f\" Estudiante : {NOMBRE}\")\n", + "print(f\" Carnet : {CARNET}\")\n", + "print(f\" Fecha/Hora : {ts_entrega}\")\n", + "print(f\" ISS Latitud : {iss_lat:+.4f} deg\")\n", + "print(f\" ISS Longitud : {iss_lon:+.4f} deg\")\n", + "print(f\" ISS Hora UTC : {iss_hora}\")\n", + "print(\"=\" * 55)\n", + "\n", + "if not iss_ok:\n", + " print(\" Advertencia: sello generado sin datos de ISS (sin conexion).\")\n", + " print(\" Guarda el notebook de todas formas.\")" + ] + }, + { + "cell_type": "markdown", + "id": "cierre", + "metadata": {}, + "source": [ + "---\n", + "## Entrega\n", + "\n", + "1. Ejecuta la celda **Sello de entrega** (arriba)\n", + "2. Guarda el notebook: **Archivo → Guardar** (o `Ctrl+S`)\n", + "3. Verifica que **todas las celdas tienen output** (ejecuta: **Kernel → Restart & Run All**)\n", + "4. Sube el archivo a Github y copia el link en el campo de entrega en la U Virtual" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e9a231d-3229-4072-92d4-bac281a69979", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Tareas_C++/busqueda_dos_en_dos.cpp b/Tareas_C++/busqueda_dos_en_dos.cpp new file mode 100644 index 0000000..5b3bc6a --- /dev/null +++ b/Tareas_C++/busqueda_dos_en_dos.cpp @@ -0,0 +1,54 @@ +// busqueda dos en dos +#include +#include + +using namespace std; + +int busqueda_dos_en_dos(const vector &lista, int n, int objetivo) +{ + int i = 0; + while (i < n && lista[i] < objetivo) + { + i = i + 2; // Se avanza de posiciones + } + + i = i - 1; // Se retrocede de posiciones + + int inicio = i; + if (inicio < 0) + inicio = 0; + + int fin = i + 1; + if (fin >= n) + fin = n - 1; + + for (int j = inicio; j <= fin; j++) + { + if (lista[j] == objetivo) + { + return j; + } + } + + return -1; // No se encuentra chamo +} + +int main() +{ + vector lista = {5, 12, 18, 25, 30, 45}; + int objetivo = 7; // Aquí se ubica el caso a probar, aqui es donde cambio manualmente. + int n = lista.size(); + + int resultado = busqueda_dos_en_dos(lista, n, objetivo); + + if (resultado != -1) + { + cout << "Elemento encontrado en el indice: " << resultado << endl; + } + else + { + cout << "Elemento no encontrado." << endl; + } + + return 0; +} \ No newline at end of file diff --git a/Tareas_C++/busqueda_dos_en_dos.exe b/Tareas_C++/busqueda_dos_en_dos.exe new file mode 100644 index 0000000..e6c3a5f Binary files /dev/null and b/Tareas_C++/busqueda_dos_en_dos.exe differ diff --git a/Tareas_C++/contar_vocales.cpp b/Tareas_C++/contar_vocales.cpp new file mode 100644 index 0000000..4cc4610 --- /dev/null +++ b/Tareas_C++/contar_vocales.cpp @@ -0,0 +1,29 @@ +#include +#include + +using namespace std; + +int main() +{ + string texto; + int contador = 0; + + cout << "Escribe una frase: "; + getline(cin, texto); // Lee la línea completa + + for (char c : texto) + { + // Convertimos a minúscula temporalmente para simplificar la comparación + char minuscula = tolower(c); + + if (minuscula == 'a' || minuscula == 'e' || minuscula == 'i' || + minuscula == 'o' || minuscula == 'u') + { + contador++; + } + } + + cout << "Total de vocales: " << contador << endl; + + return 0; +} \ No newline at end of file diff --git a/Tareas_C++/contar_vocales.exe b/Tareas_C++/contar_vocales.exe new file mode 100644 index 0000000..468fc20 Binary files /dev/null and b/Tareas_C++/contar_vocales.exe differ diff --git a/Tareas_C++/numero_primo.cpp b/Tareas_C++/numero_primo.cpp new file mode 100644 index 0000000..eca5843 --- /dev/null +++ b/Tareas_C++/numero_primo.cpp @@ -0,0 +1,34 @@ +#include +#include + +using namespace std; + +int main() +{ + int n; + bool es_primo = true; + + cout << "Ingresa un número entero (n > 2): "; + cin >> n; + + // Un número es primo si solo es divisible por 1 y por sí mismo + for (int i = 2; i <= sqrt(n); i++) + { + if (n % i == 0) + { + es_primo = false; + break; // Si se encuentra un divisor, dejamos de buscar + } + } + + if (es_primo) + { + cout << n << " es primo." << endl; + } + else + { + cout << n << " es no primo." << endl; + } + + return 0; +} \ No newline at end of file diff --git a/Tareas_C++/numero_primo.exe b/Tareas_C++/numero_primo.exe new file mode 100644 index 0000000..3eb83f4 Binary files /dev/null and b/Tareas_C++/numero_primo.exe differ diff --git a/Tareas_C++/suma_digitos.cpp b/Tareas_C++/suma_digitos.cpp new file mode 100644 index 0000000..6062a50 --- /dev/null +++ b/Tareas_C++/suma_digitos.cpp @@ -0,0 +1,25 @@ +#include +using namespace std; + +int main() +{ + int numero, suma = 0, residuo; + + cout << "Ingresa un entero positivo: "; + cin >> numero; + + // Aquí se asegura de que sea positivo + if (numero < 0) + numero = -numero; + + while (numero > 0) + { + residuo = numero % 10; // Obtiene el último dígito + suma = suma + residuo; // Lo acumula en la suma + numero = numero / 10; // Elimina el último dígito del número + } + + cout << "La suma de los digitos es: " << suma << endl; + + return 0; +} \ No newline at end of file diff --git a/Tareas_C++/suma_digitos.exe b/Tareas_C++/suma_digitos.exe new file mode 100644 index 0000000..7ea2931 Binary files /dev/null and b/Tareas_C++/suma_digitos.exe differ diff --git a/autograder/tareas/Tareas_C++/busqueda_dos_en_dos.cpp b/autograder/tareas/Tareas_C++/busqueda_dos_en_dos.cpp new file mode 100644 index 0000000..5b3bc6a --- /dev/null +++ b/autograder/tareas/Tareas_C++/busqueda_dos_en_dos.cpp @@ -0,0 +1,54 @@ +// busqueda dos en dos +#include +#include + +using namespace std; + +int busqueda_dos_en_dos(const vector &lista, int n, int objetivo) +{ + int i = 0; + while (i < n && lista[i] < objetivo) + { + i = i + 2; // Se avanza de posiciones + } + + i = i - 1; // Se retrocede de posiciones + + int inicio = i; + if (inicio < 0) + inicio = 0; + + int fin = i + 1; + if (fin >= n) + fin = n - 1; + + for (int j = inicio; j <= fin; j++) + { + if (lista[j] == objetivo) + { + return j; + } + } + + return -1; // No se encuentra chamo +} + +int main() +{ + vector lista = {5, 12, 18, 25, 30, 45}; + int objetivo = 7; // Aquí se ubica el caso a probar, aqui es donde cambio manualmente. + int n = lista.size(); + + int resultado = busqueda_dos_en_dos(lista, n, objetivo); + + if (resultado != -1) + { + cout << "Elemento encontrado en el indice: " << resultado << endl; + } + else + { + cout << "Elemento no encontrado." << endl; + } + + return 0; +} \ No newline at end of file diff --git a/autograder/tareas/Tareas_C++/busqueda_dos_en_dos.exe b/autograder/tareas/Tareas_C++/busqueda_dos_en_dos.exe new file mode 100644 index 0000000..e6c3a5f Binary files /dev/null and b/autograder/tareas/Tareas_C++/busqueda_dos_en_dos.exe differ diff --git a/autograder/tareas/Tareas_C++/contar_vocales.cpp b/autograder/tareas/Tareas_C++/contar_vocales.cpp new file mode 100644 index 0000000..4cc4610 --- /dev/null +++ b/autograder/tareas/Tareas_C++/contar_vocales.cpp @@ -0,0 +1,29 @@ +#include +#include + +using namespace std; + +int main() +{ + string texto; + int contador = 0; + + cout << "Escribe una frase: "; + getline(cin, texto); // Lee la línea completa + + for (char c : texto) + { + // Convertimos a minúscula temporalmente para simplificar la comparación + char minuscula = tolower(c); + + if (minuscula == 'a' || minuscula == 'e' || minuscula == 'i' || + minuscula == 'o' || minuscula == 'u') + { + contador++; + } + } + + cout << "Total de vocales: " << contador << endl; + + return 0; +} \ No newline at end of file diff --git a/autograder/tareas/Tareas_C++/contar_vocales.exe b/autograder/tareas/Tareas_C++/contar_vocales.exe new file mode 100644 index 0000000..468fc20 Binary files /dev/null and b/autograder/tareas/Tareas_C++/contar_vocales.exe differ diff --git a/autograder/tareas/Tareas_C++/numero_primo.cpp b/autograder/tareas/Tareas_C++/numero_primo.cpp new file mode 100644 index 0000000..eca5843 --- /dev/null +++ b/autograder/tareas/Tareas_C++/numero_primo.cpp @@ -0,0 +1,34 @@ +#include +#include + +using namespace std; + +int main() +{ + int n; + bool es_primo = true; + + cout << "Ingresa un número entero (n > 2): "; + cin >> n; + + // Un número es primo si solo es divisible por 1 y por sí mismo + for (int i = 2; i <= sqrt(n); i++) + { + if (n % i == 0) + { + es_primo = false; + break; // Si se encuentra un divisor, dejamos de buscar + } + } + + if (es_primo) + { + cout << n << " es primo." << endl; + } + else + { + cout << n << " es no primo." << endl; + } + + return 0; +} \ No newline at end of file diff --git a/autograder/tareas/Tareas_C++/numero_primo.exe b/autograder/tareas/Tareas_C++/numero_primo.exe new file mode 100644 index 0000000..3eb83f4 Binary files /dev/null and b/autograder/tareas/Tareas_C++/numero_primo.exe differ diff --git a/autograder/tareas/Tareas_C++/suma_digitos.cpp b/autograder/tareas/Tareas_C++/suma_digitos.cpp new file mode 100644 index 0000000..6062a50 --- /dev/null +++ b/autograder/tareas/Tareas_C++/suma_digitos.cpp @@ -0,0 +1,25 @@ +#include +using namespace std; + +int main() +{ + int numero, suma = 0, residuo; + + cout << "Ingresa un entero positivo: "; + cin >> numero; + + // Aquí se asegura de que sea positivo + if (numero < 0) + numero = -numero; + + while (numero > 0) + { + residuo = numero % 10; // Obtiene el último dígito + suma = suma + residuo; // Lo acumula en la suma + numero = numero / 10; // Elimina el último dígito del número + } + + cout << "La suma de los digitos es: " << suma << endl; + + return 0; +} \ No newline at end of file diff --git a/autograder/tareas/Tareas_C++/suma_digitos.exe b/autograder/tareas/Tareas_C++/suma_digitos.exe new file mode 100644 index 0000000..7ea2931 Binary files /dev/null and b/autograder/tareas/Tareas_C++/suma_digitos.exe differ diff --git a/autograder/tareas/busqueda_lineal/solucion.exe b/autograder/tareas/busqueda_lineal/solucion.exe new file mode 100644 index 0000000..b068d1c Binary files /dev/null and b/autograder/tareas/busqueda_lineal/solucion.exe differ diff --git a/autograder/tareas/contar_hasta_10/solucion.exe b/autograder/tareas/contar_hasta_10/solucion.exe new file mode 100644 index 0000000..f180398 Binary files /dev/null and b/autograder/tareas/contar_hasta_10/solucion.exe differ diff --git a/ejemplos/python/HojaTrabajo_IntroduccionPython.ipynb b/ejemplos/python/HojaTrabajo_IntroduccionPython.ipynb index b3aab21..21b7b3f 100644 --- a/ejemplos/python/HojaTrabajo_IntroduccionPython.ipynb +++ b/ejemplos/python/HojaTrabajo_IntroduccionPython.ipynb @@ -6,9 +6,9 @@ "source": [ "# Hoja de Trabajo — Introducción a Python\n", "\n", - "**Nombre:** ___________________________ \n", - "**Carnet:** ___________________________ \n", - "**Fecha:** ___________________________\n", + "**Nombre:** Alejandro Sebastián Puac Shar\n", + "**Carnet:** 202604047 \n", + "**Fecha:** 13/04/2026\n", "\n", "---\n", "\n", @@ -31,29 +31,49 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nombre: Alejandro Puac, Carnet: 202604047\n" + ] + } + ], "source": [ "# Ejercicio 1a\n", "# Imprime tu nombre y carnet en una sola línea usando print() con dos argumentos.\n", "# Ejemplo de salida esperada: Nombre: Ana Carnet: 202300001\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "print(\"Nombre: Alejandro Puac, Carnet: 202604047\")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hola desde Python\n" + ] + } + ], "source": [ "# Ejercicio 1b\n", "# Usa end=\"\" para imprimir las palabras \"Hola\", \"desde\" y \"Python\"\n", "# en una sola línea, cada una en su propia llamada a print().\n", "# Salida esperada: Hola desde Python\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "print(\"Hola\", end=\" \")\n", + "print(\"desde\", end=\" \")\n", + "print(\"Python\")" ] }, { @@ -69,9 +89,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nombre: Alejandro Puac (Tipo: str)\n", + "Edad: 20 (Tipo: int)\n", + "Promedio: 85.5 (Tipo: float)\n", + "Activo: True (Tipo: bool)\n" + ] + } + ], "source": [ "# Ejercicio 2a\n", "# Crea variables con los siguientes datos sobre ti:\n", @@ -81,21 +112,39 @@ "# - activo (bool) True si eres estudiante activo\n", "# Luego imprime cada variable junto con su tipo usando type().__name__\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "nombre = \"Alejandro Puac\"\n", + "edad = 20\n", + "promedio = 85.5\n", + "activo = True\n", + "print(f\"Nombre: {nombre} (Tipo: {type(nombre).__name__})\")\n", + "print(f\"Edad: {edad} (Tipo: {type(edad).__name__})\")\n", + "print(f\"Promedio: {promedio} (Tipo: {type(promedio).__name__})\")\n", + "print(f\"Activo: {activo} (Tipo: {type(activo).__name__})\") " ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=10, y=20, z=30\n" + ] + } + ], "source": [ "# Ejercicio 2b\n", "# Usa asignación múltiple para crear tres variables x, y, z\n", "# con los valores 10, 20 y 30 en una sola línea.\n", "# Luego imprime: \"x=10, y=20, z=30\" usando un f-string.\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "x, y, z = 10, 20, 30\n", + "print(f\"x={x}, y={y}, z={z}\")" ] }, { @@ -111,9 +160,19 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "División entera: 3\n", + "Residuo: 2\n", + "Potencia (solo primeros dígitos): 34048\n" + ] + } + ], "source": [ "# Ejercicio 3a\n", "# Dados a=23 y b=7, calcula e imprime:\n", @@ -123,14 +182,29 @@ "\n", "a, b = 23, 7\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "div_entera = a // b\n", + "residuo = a % b\n", + "potencia = a ** b\n", + "print(f\"División entera: {div_entera}\")\n", + "print(f\"Residuo: {residuo}\")\n", + "print(f\"Potencia (solo primeros dígitos): {str(potencia)[:5]}\")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Raíz cuadrada de 144: 12.0\n", + "Techo de 7.3: 8\n" + ] + } + ], "source": [ "# Ejercicio 3b\n", "# Usa el módulo math para calcular:\n", @@ -141,7 +215,13 @@ "\n", "import math\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "raiz_cuadrada = math.sqrt(144)\n", + "techo = math.ceil(7.3)\n", + "piso = math.floor(7.9)\n", + "pi_redondeado = round(math.pi, 4)\n", + "print(f\"Raíz cuadrada de 144: {raiz_cuadrada}\")\n", + "print(f\"Techo de 7.3: {techo}\")" ] }, { @@ -157,9 +237,19 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 83, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sin espacios: 'fisica y matematica'\n", + "En mayúsculas: 'FISICA Y MATEMATICA'\n", + "Reemplazando 'Y' por '&': 'FISICA & MATEMATICA'\n" + ] + } + ], "source": [ "# Ejercicio 4a\n", "# Dada la cadena: \" fisica y matematica \"\n", @@ -171,14 +261,30 @@ "\n", "cadena = \" fisica y matematica \"\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "cadena_sin_espacios = cadena.strip()\n", + "print(f\"Sin espacios: '{cadena_sin_espacios}'\")\n", + "cadena_mayusculas = cadena_sin_espacios.upper()\n", + "print(f\"En mayúsculas: '{cadena_mayusculas}'\")\n", + "cadena_reemplazada = cadena_mayusculas.replace(\"Y\", \"&\")\n", + "print(f\"Reemplazando 'Y' por '&': '{cadena_reemplazada}'\")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lista de colores: ['rojo', 'verde', 'azul', 'amarillo']\n", + "Colores unidos: rojo | verde | azul | amarillo\n", + "¿'verde' está en la lista? True\n" + ] + } + ], "source": [ "# Ejercicio 4b\n", "# Dada la cadena: \"rojo-verde-azul-amarillo\"\n", @@ -188,7 +294,13 @@ "\n", "colores_str = \"rojo-verde-azul-amarillo\"\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "colores_lista = colores_str.split(\"-\")\n", + "print(f\"Lista de colores: {colores_lista}\")\n", + "colores_unidos = \" | \".join(colores_lista)\n", + "print(f\"Colores unidos: {colores_unidos}\")\n", + "verde_en_lista = \"verde\" in colores_lista\n", + "print(f\"¿'verde' está en la lista? {verde_en_lista}\")" ] }, { @@ -204,9 +316,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 85, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lista final de materias: ['Cálculo', 'Física', 'Química', 'Programación', 'Estadística']\n", + "Longitud de la lista: 5\n" + ] + } + ], "source": [ "# Ejercicio 5a\n", "# Crea una lista con las materias: \"Cálculo\", \"Física\", \"Programación\", \"Álgebra\"\n", @@ -216,14 +337,31 @@ "# 3. Elimina \"Álgebra\" de la lista\n", "# 4. Imprime la lista final y su longitud\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "materias = [\"Cálculo\", \"Física\", \"Programación\", \"Álgebra\"]\n", + "materias.append(\"Estadística\")\n", + "materias.insert(2, \"Química\")\n", + "materias.remove(\"Álgebra\")\n", + "print(f\"Lista final de materias: {materias}\")\n", + "print(f\"Longitud de la lista: {len(materias)}\")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nota mínima: 61\n", + "Nota máxima: 95\n", + "Suma de notas: 636\n", + "Promedio de notas: 79.50\n" + ] + } + ], "source": [ "# Ejercicio 5b\n", "# Dada la lista de notas, imprime: mínimo, máximo, suma y promedio.\n", @@ -231,7 +369,15 @@ "\n", "notas = [78, 92, 65, 88, 74, 95, 61, 83]\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "min_nota = min(notas)\n", + "max_nota = max(notas)\n", + "suma_notas = sum(notas)\n", + "promedio_notas = suma_notas / len(notas)\n", + "print(f\"Nota mínima: {min_nota}\")\n", + "print(f\"Nota máxima: {max_nota}\")\n", + "print(f\"Suma de notas: {suma_notas}\")\n", + "print(f\"Promedio de notas: {promedio_notas:.2f}\")" ] }, { @@ -248,9 +394,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Créditos del curso: 4\n", + "Salón: Sin asignar\n" + ] + } + ], "source": [ "# Ejercicio 6a\n", "# Crea un diccionario que represente un curso con las claves:\n", @@ -260,21 +415,45 @@ "# 2. Imprime el valor de \"creditos\" usando .get()\n", "# 3. Intenta obtener la clave \"salon\" con valor por defecto \"Sin asignar\"\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "curso = {\n", + " \"nombre\": \"Introducción a la Programación\",\n", + " \"codigo\": \"CS101\",\n", + " \"creditos\": 4,\n", + " \"activo\": True\n", + "}\n", + "curso[\"estudiantes\"] = 35\n", + "print(f\"Créditos del curso: {curso.get('creditos')}\")\n", + "salon = curso.get(\"salon\", \"Sin asignar\")\n", + "print(f\"Salón: {salon}\")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nombre : Introducción a la Programación\n", + "codigo : CS101\n", + "creditos : 4\n", + "activo : True\n", + "estudiantes : 35\n" + ] + } + ], "source": [ "# Ejercicio 6b\n", "# Itera sobre el diccionario del ejercicio anterior e imprime\n", "# cada clave y su valor en el formato: clave : valor\n", "# (usa f-string con alineación, ej: f\"{clave:12} : {valor}\")\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "for clave, valor in curso.items():\n", + " print(f\"{clave:12} : {valor}\")" ] }, { @@ -290,9 +469,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bajo peso\n", + "Normal\n", + "Sobrepeso\n", + "Obesidad\n" + ] + } + ], "source": [ "# Ejercicio 7a\n", "# Escribe una función llamada clasificar_imc(imc) que reciba el\n", @@ -304,21 +494,49 @@ "#\n", "# Pruébala con los valores: 16.0, 22.5, 27.3, 31.8\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "def clasificar_imc(imc):\n", + " if imc < 18.5:\n", + " return \"Bajo peso\"\n", + " elif 18.5 <= imc <= 24.9:\n", + " return \"Normal\"\n", + " elif 25 <= imc <= 29.9:\n", + " return \"Sobrepeso\"\n", + " else:\n", + " return \"Obesidad\"\n", + "# Pruebas\n", + "print(clasificar_imc(16.0)) # Bajo peso\n", + "print(clasificar_imc(22.5)) # Normal\n", + "print(clasificar_imc(27.3)) # Sobrepeso\n", + "print(clasificar_imc(31.8)) # Obesidad" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4 es par\n", + "7 es impar\n", + "0 es par\n" + ] + } + ], "source": [ "# Ejercicio 7b\n", "# Usa el operador ternario para determinar si un número es par o impar.\n", "# Pruébalo con los números 4, 7 y 0.\n", "# Salida esperada: 4 es par / 7 es impar / 0 es par\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "numeros = [4, 7, 0]\n", + "for num in numeros:\n", + " resultado = \"par\" if num % 2 == 0 else \"impar\"\n", + " print(f\"{num} es {resultado}\")" ] }, { @@ -337,9 +555,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1] Mercurio\n", + "[2] Venus\n", + "[3] Tierra\n", + "[4] Marte\n", + "[5] Júpiter\n", + "[6] Saturno\n", + "[7] Urano\n", + "[8] Neptuno\n" + ] + } + ], "source": [ "# Ejercicio 8a\n", "# Usa un for con enumerate() para imprimir la siguiente lista de planetas\n", @@ -351,35 +584,72 @@ "\n", "planetas = [\"Mercurio\", \"Venus\", \"Tierra\", \"Marte\", \"Júpiter\", \"Saturno\", \"Urano\", \"Neptuno\"]\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "for i, planeta in enumerate(planetas, start=1):\n", + " print(f\"[{i}] {planeta}\")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 x 1 = 7\n", + "7 x 2 = 14\n", + "7 x 3 = 21\n", + "7 x 4 = 28\n", + "7 x 5 = 35\n", + "7 x 6 = 42\n", + "7 x 7 = 49\n", + "7 x 8 = 56\n", + "7 x 9 = 63\n", + "7 x 10 = 70\n" + ] + } + ], "source": [ "# Ejercicio 8b\n", "# Usa un ciclo while para imprimir la tabla de multiplicar del 7\n", "# (del 7x1 hasta el 7x10).\n", "# Formato: 7 x 1 = 7\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "n = 1\n", + "while n <= 10:\n", + " resultado = 7 * n\n", + " print(f\"7 x {n} = {resultado}\")\n", + " n += 1" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cubos del 1 al 8: [1, 8, 27, 64, 125, 216, 343, 512]\n", + "Números del 1 al 30 divisibles por 3 pero no por 9: [3, 6, 12, 15, 21, 24, 30]\n" + ] + } + ], "source": [ "# Ejercicio 8c — List comprehension\n", "# En una sola línea, crea las siguientes listas y luego imprímelas:\n", "# 1. Los cubos de los números del 1 al 8 (x³)\n", "# 2. Los números del 1 al 30 que sean divisibles por 3 pero NO por 9\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "cubos = [x**3 for x in range(1, 9)]\n", + "divisibles = [x for x in range(1, 31) if x % 3 == 0 and x % 9 != 0]\n", + "print(f\"Cubos del 1 al 8: {cubos}\")\n", + "print(f\"Números del 1 al 30 divisibles por 3 pero no por 9: {divisibles}\")\n" ] }, { @@ -399,9 +669,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 94, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100°C a Fahrenheit: 212.00°F\n", + "0°C a Fahrenheit: 32.00°F\n", + "32°F a Celsius: 0.00°C\n", + "212°F a Celsius: 100.00°C\n" + ] + } + ], "source": [ "# Ejercicio 9a\n", "# Escribe una función convertir_temperatura(grados, escala=\"C\") que convierta:\n", @@ -410,14 +691,36 @@ "#\n", "# Pruébala con: 100°C, 0°C, 32°F y 212°F\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "def convertir_temperatura(grados, escala=\"C\"):\n", + " if escala == \"C\":\n", + " return grados * 9/5 + 32\n", + " elif escala == \"F\":\n", + " return (grados - 32) * 5/9\n", + " else:\n", + " raise ValueError(\"Escala no válida. Use 'C' o 'F'.\")\n", + "# Pruebas\n", + "print(f\"100°C a Fahrenheit: {convertir_temperatura(100, 'C'):.2f}°F\")\n", + "print(f\"0°C a Fahrenheit: {convertir_temperatura(0, 'C'):.2f}°F\")\n", + "print(f\"32°F a Celsius: {convertir_temperatura(32, 'F'):.2f}°C\")\n", + "print(f\"212°F a Celsius: {convertir_temperatura(212, 'F'):.2f}°C\")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 95, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mínimo: 7\n", + "Máximo: 89\n", + "Promedio: 38.00\n" + ] + } + ], "source": [ "# Ejercicio 9b\n", "# Escribe una función resumen_lista(lista) que retorne\n", @@ -425,14 +728,42 @@ "# Úsala con: [12, 45, 7, 89, 34, 56, 23]\n", "# Imprime los tres resultados usando desempaquetado.\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "def resumen_lista(lista):\n", + " minimo = min(lista)\n", + " maximo = max(lista)\n", + " promedio = sum(lista) / len(lista)\n", + " return minimo, maximo, promedio\n", + "# Prueba\n", + "numeros = [12, 45, 7, 89, 34, 56, 23]\n", + "min_num, max_num, prom_num = resumen_lista(numeros)\n", + "print(f\"Mínimo: {min_num}\")\n", + "print(f\"Máximo: {max_num}\")\n", + "print(f\"Promedio: {prom_num:.2f}\")" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 96, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fibonacci(0) = 0\n", + "fibonacci(1) = 1\n", + "fibonacci(2) = 1\n", + "fibonacci(3) = 2\n", + "fibonacci(4) = 3\n", + "fibonacci(5) = 5\n", + "fibonacci(6) = 8\n", + "fibonacci(7) = 13\n", + "fibonacci(8) = 21\n", + "fibonacci(9) = 34\n" + ] + } + ], "source": [ "# Ejercicio 9c — Recursión\n", "# Escribe una función recursiva fibonacci(n) que retorne\n", @@ -442,7 +773,17 @@ "#\n", "# Imprime los primeros 10 números: fibonacci(0) ... fibonacci(9)\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "def fibonacci(n):\n", + " if n == 0:\n", + " return 0\n", + " elif n == 1:\n", + " return 1\n", + " else:\n", + " return fibonacci(n-1) + fibonacci(n-2)\n", + "# Imprime los primeros 10 números de Fibonacci\n", + "for i in range(10):\n", + " print(f\"fibonacci({i}) = {fibonacci(i)}\")" ] }, { @@ -465,9 +806,26 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 97, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[intento con: 42]\n", + "42\n", + "Error: No se pudo convertir '3.14' a entero.\n", + "[intento con: 3.14]\n", + "None\n", + "Error: No se pudo convertir 'hola' a entero.\n", + "[intento con: hola]\n", + "None\n", + "[intento con: 100]\n", + "100\n" + ] + } + ], "source": [ "# Ejercicio 10a\n", "# Escribe una función convertir_entero(valor) que intente convertir\n", @@ -478,14 +836,40 @@ "#\n", "# Pruébala con: \"42\", \"3.14\", \"hola\", 100\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "def convertir_entero(valor):\n", + " try:\n", + " resultado = int(valor)\n", + " return resultado\n", + " except ValueError:\n", + " print(f\"Error: No se pudo convertir '{valor}' a entero.\")\n", + " return None\n", + " finally:\n", + " print(f\"[intento con: {valor}]\")\n", + "# Pruebas\n", + "print(convertir_entero(\"42\")) # 42\n", + "print(convertir_entero(\"3.14\")) # Error + None\n", + "print(convertir_entero(\"hola\")) # Error + None\n", + "print(convertir_entero(100)) # 100" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 98, + "id": "bccc2732", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "25 es una edad válida.\n", + "Error con edad '-5': La edad debe estar entre 0 y 150.\n", + "Error con edad '200': La edad debe estar entre 0 y 150.\n", + "Error con edad 'veinte': La edad debe ser un entero.\n" + ] + } + ], "source": [ "# Ejercicio 10b\n", "# Escribe una función validar_edad(edad) que:\n", @@ -495,7 +879,21 @@ "#\n", "# Pruébala dentro de un try/except con: 25, -5, 200, \"veinte\"\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "def validar_edad(edad):\n", + " if not isinstance(edad, int):\n", + " raise TypeError(\"La edad debe ser un entero.\")\n", + " if edad < 0 or edad > 150:\n", + " raise ValueError(\"La edad debe estar entre 0 y 150.\")\n", + " return True\n", + "# Pruebas\n", + "edades = [25, -5, 200, \"veinte\"]\n", + "for e in edades:\n", + " try:\n", + " if validar_edad(e):\n", + " print(f\"{e} es una edad válida.\")\n", + " except (TypeError, ValueError) as error:\n", + " print(f\"Error con edad '{e}': {error}\")\n" ] }, { @@ -510,9 +908,27 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 99, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nombre Promedio Estado\n", + "----------------------------------------\n", + "Ana López 86.25 Aprobado\n", + "Carlos Ruiz 53.75 Reprobado\n", + "María Paz 73.25 Aprobado\n", + "Luis Gómez 96.25 Aprobado\n", + "Sara Díaz 47.50 Reprobado\n", + "Pedro Alva N/A Reprobado\n", + "----------------------------------------\n", + "Total Aprobados: 3\n", + "Total Reprobados: 3\n" + ] + } + ], "source": [ "# Ejercicio integrador\n", "#\n", @@ -537,7 +953,32 @@ " {\"nombre\": \"Pedro Alva\", \"notas\": []},\n", "]\n", "\n", - "# TU CÓDIGO AQUÍ\n" + "# TU CÓDIGO AQUÍ\n", + "def analizar_curso(estudiantes):\n", + " aprobados = 0\n", + " reprobados = 0\n", + " print(f\"{'Nombre':20} {'Promedio':10} {'Estado'}\")\n", + " print(\"-\" * 40)\n", + " for estudiante in estudiantes:\n", + " nombre = estudiante[\"nombre\"]\n", + " notas = estudiante[\"notas\"]\n", + " if notas:\n", + " promedio = sum(notas) / len(notas)\n", + " estado = \"Aprobado\" if promedio >= 61 else \"Reprobado\"\n", + " promedio_str = f\"{promedio:.2f}\"\n", + " else:\n", + " promedio_str = \"N/A\"\n", + " estado = \"Reprobado\"\n", + " print(f\"{nombre:20} {promedio_str:10} {estado}\")\n", + " if estado == \"Aprobado\":\n", + " aprobados += 1\n", + " else:\n", + " reprobados += 1\n", + " print(\"-\" * 40)\n", + " print(f\"Total Aprobados: {aprobados}\")\n", + " print(f\"Total Reprobados: {reprobados}\")\n", + "\n", + "analizar_curso(curso) " ] } ], @@ -548,8 +989,16 @@ "name": "python3" }, "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", "name": "python", - "version": "3.11.0" + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } }, "nbformat": 4,