100 Días de Código en Python — Día 75: Google Trends y series de tiempo — remuestreo y gráficos que cuentan historias
Nivel: Grado décimo
Área: Tecnología e Informática
Modalidad: Tutorial práctico
Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 75 (nivel avanzado).
27 min de lectura
19 secciones
Este tutorial adapta pedagógicamente el Día 75. Las secciones 📝 Contenido original del curso provienen de las lecciones 550 a 558; el cuaderno de inicio y los 5 CSV están en los recursos del curso. En algunas lecciones parte del código aparece solo en imágenes; aquí se reconstruye y se verifica. Los retos 🟢🟡🟠🔴, las secciones sobre correlación y causalidad y gráficos honestos, el proyecto extra y el proyecto integrador del bloque de datos (Días 72 a 75) son creaciones de esta adaptación. Las correcciones al material original aparecen como 🛠️ Nota técnica.
Nota sobre las verificaciones: el 27 de septiembre de 2026, con Python 3.14, pandas 3.0.6, Matplotlib 3.11.2 y una copia pública de los 5 CSV del curso (publicada en GitHub por otro estudiante del mismo curso), se ejecutó todo el código. Las dimensiones y valores coinciden con el curso (124, 181, 2204 y 73 filas; búsquedas de Tesla entre 2 y 31), con tres diferencias importantes: resample('M') ya no funciona en pandas 3 (ValueError: 'M' is no longer supported for offsets. Please use 'ME' instead.); los "2 valores faltantes" del precio de Bitcoin son una sola fila (4 de agosto de 2020) con dos celdas vacías; y el pico anual de búsquedas de "unemployment benefits" cae en enero en 12 de los 16 años completos, no en diciembre.
Google Trends da acceso a la popularidad de los términos de búsqueda de Google. Investigaremos:
cómo se relaciona el volumen de búsqueda de "Bitcoin" con su precio;
cómo se relaciona el de una acción muy comentada, Tesla, con el precio de esa acción;
cómo varían las búsquedas de "Unemployment Benefits" (subsidio de desempleo) con la tasa de desempleo real de Estados Unidos.
Aprenderás a hacer comparables series de tiempo remuestreándolas a la misma periodicidad (de diario a mensual); a afinar el estilo de los gráficos con límites, etiquetas, estilos de línea, marcadores, colores y resolución; a usar cuadrículas para ver la estacionalidad; a encontrar y ubicar valores NaN; y a usar localizadores para el eje del tiempo. También repasarás lo de los tres días anteriores.
Preparación: descarga el ZIP, sube el cuaderno a Google Drive y agrega los 5 CSV.
Prerequisitos: exploración y limpieza (Día 72), fechas, pivote y media móvil (Día 73), dos ejes y agg (Día 74).
El cuaderno ya importa las bibliotecas y crea cuatro DataFrames. Reto: para df_tesla, df_unemployment y los dos de Bitcoin: ¿forma?, ¿filas y columnas?, ¿nombres de columnas?, ¿el mayor número en la columna de búsqueda? (prueba .describe()), ¿periodicidad (diaria, semanal, mensual)?
Solución:
Tesla: 124 filas y 3 columnas (mes, popularidad de búsqueda y precio de cierre de la acción). La búsqueda va de 2 a 31.
Desempleo: 181 filas y 3 columnas, mensual desde 2004; el máximo de búsqueda es 100.
Bitcoin: un CSV con el precio de cierre diario y el volumen (2204 filas) y otro con la búsqueda mensual.
.describe() es "una de mis funciones favoritas": da estadísticas descriptivas de inmediato.
¿Qué significan los números de búsqueda? Google no publica el volumen real. Según su definición: "los números representan el interés de búsqueda relativo al punto más alto del gráfico para la región y el periodo. 100 es la máxima popularidad; 50, la mitad; 0, que no hubo datos suficientes". Cada dato se divide por el total de búsquedas de esa región y periodo y luego se escala para que el máximo sea 100. No dice cuántas búsquedas hubo, pero es una buena aproximación.
Parámetros usados para generar los datos:
"Tesla", mundial, búsqueda web.
"Bitcoin", mundial, búsqueda de noticias.
"Unemployment Benefits", Estados Unidos, búsqueda web.
La consecuencia más importante de la escala 0–100: un 31 para Tesla y un 31 para Bitcoin no son comparables: cada serie está escalada respecto a su propio máximo. En este archivo Tesla nunca llega a 100 (su máximo es 31), lo que indica que el 100 de esa consulta quedó fuera de las filas que tenemos (por ejemplo, en un periodo o un término distinto); los datos no permiten saber cuál. Y si descargas los datos otro día, todos los valores pueden cambiar.
Reto: ¿hay valores faltantes en los 4 DataFrames? Si sí, ¿cuántos y en qué fila? Elimina esas filas.
Solución:
Python
123
print(f'Missing values for Tesla?: {df_tesla.isna().values.any()}')print(f'Missing values for U/E?: {df_unemployment.isna().values.any()}')print(f'Missing values for BTC Search?: {df_btc_search.isna().values.any()}')
Tres no tienen faltantes; el del precio de Bitcoin sí: .sum() cuenta los True y da 2. Para ubicarlos, un subconjunto con .isna(); para quitarlos, .dropna() (con inplace=True o asignando).
Reto: convertir a fechas las columnas MONTH y DATE de los 4 DataFrames y comprobarlo:
df_btc_price.isna().values.sum()# 2
df_btc_price[df_btc_price.CLOSE.isna()]# DATE CLOSE VOLUME# 2148 2020-08-04 NaN NaN
🛠️ Nota técnica (verificado): hay 2 celdas vacías, pero en una sola fila: el 4 de agosto de 2020 no tiene ni precio ni volumen. isna().values.sum() cuenta celdas; para contar filas con algún faltante: df_btc_price.isna().any(axis=1).sum() (da 1). Después de dropna() quedan 2203 filas.
El precio de Bitcoin es diario y su búsqueda es mensual. Para compararlos, .resample() cambia la frecuencia: se indica la columna de fechas y la regla ('M' mensual; otras: 'Y' anual, 'T' minuto). Luego se decide cómo resumir: queremos el último precio del mes (el cierre de mes):
Python
12
df_btc_monthly = df_btc_price.resample('M', on='DATE').last()# el promedio del mes sería: df_btc_price.resample('M', on='DATE').mean()
Resultado: 73 filas, igual que los datos de búsqueda.
📖 Explicación conceptual (verificada con pandas 3.0.6)#
🛠️ Nota técnica (verificado): en pandas 3, resample('M', ...)falla:
Texto
1
ValueError: Invalid frequency: M. ... 'M' is no longer supported for offsets. Please use 'ME' instead.
Los alias cambiaron para ser más claros: 'ME' = fin de mes (month end), 'MS' = inicio de mes, 'YE' = fin de año (antes 'Y'), 'min' = minuto (antes 'T'), 'h' = hora. La línea correcta es:
Resultados verificados: 73 filas; el índice son fechas de fin de mes (2014-09-30, 2014-10-31…), mientras que df_btc_search.MONTH usa el primer día (2014-09-01…). Son los mismos 73 meses (comprobado con to_period('M')), así que se pueden graficar juntos.
last() o mean(): en el último mes, el precio de cierre fue 10 912,5 y el promedio del mes 10 655,7. Ninguno es "el correcto": depende de la pregunta. El precio de cierre es el que ve un inversionista a fin de mes; el promedio representa mejor todo el mes y es menos sensible a un día raro.
Reto 2: colores: un azul con nombre ('skyblue') para la búsqueda y un rojo en hexadecimal ('#E6232E') para el precio, en las líneas y en las etiquetas de los ejes.
Reto 3: tamaño 14 × 8; fuentes de 14 en las etiquetas y el eje x; texto del eje x rotado 45°; título "Tesla Web Search vs Price"; líneas más gruesas; más resolución (DPI); límites en los ejes (set_xlim, set_ylim); y plt.show() para mostrar el gráfico de forma explícita (importante fuera de los cuadernos).
Los límites pueden ser fijos (0 a 600 dólares) o calculados con .min() y .max(). Si aparece una advertencia de Matplotlib sobre el convertidor de fechas, no es un error: se resuelve registrando los convertidores de pandas, como indica la advertencia.
plt contra ax: con un solo eje, plt.plot, plt.ylabel… funcionan sobre "el eje actual". Con dos ejes, es más claro trabajar con los objetosax1 y ax2 (ax1.set_ylabel, ax1.set_xlim), porque así sabes exactamente a cuál afecta cada instrucción. Fíjate en que el nombre cambia: plt.ylabel → ax.set_ylabel.
💡 Resolución:dpi=120 con figsize=(14, 8) produce una imagen de 1680 × 960 píxeles. Para un informe impreso, guarda con plt.savefig('tesla.png', dpi=200, bbox_inches='tight').
🛠️ Nota técnica: en los datos (verificado), el precio máximo de Tesla es 498,32 dólares y el pico de búsqueda (31) es de septiembre de 2020. Los precios de acciones históricos suelen venir ajustados por las divisiones de acciones (splits): si comparas con el precio actual de Tesla en Internet, las cifras no coincidirán, porque la acción se dividió otra vez después de 2020.
Las marcas del eje del tiempo suelen quedar mal. Los localizadores de matplotlib.dates ayudan:
Python
123456789
import matplotlib.dates as mdates
years = mdates.YearLocator()# una marca principal por año
months = mdates.MonthLocator()# una marca secundaria por mes
years_fmt = mdates.DateFormatter('%Y')# mostrar solo el año
ax1.xaxis.set_major_locator(years)
ax1.xaxis.set_major_formatter(years_fmt)
ax1.xaxis.set_minor_locator(months)
Con las marcas se puede fechar el pico de interés de marzo de 2016, cuando se presentó el Tesla Model 3. Y los picos más recientes no coinciden con un auto nuevo, sino con el precio disparado de la acción.
El truco: las fechas de la serie mensual están en el índice: df_btc_monthly.index. Los movimientos locos de precio de comienzos de 2018 coincidieron con búsquedas récord ("todo el mundo hablaba de Bitcoin, y lo compraba"). En el primer trimestre de 2019 hubo otro gran salto de precio, pero con mucho menos aumento de búsquedas ("quizá porque ya todo el mundo sabía qué era Bitcoin").
El máximo mensual del precio de cierre es de diciembre de 2017 (≈ 14 156 dólares) y la búsqueda máxima (100) también es de diciembre de 2017: por eso el límite superior de 15 000 del curso es adecuado.
df_btc_monthly.index y df_btc_search.BTC_NEWS_SEARCH se grafican juntos porque tienen el mismo número de filas en el mismo orden (73 meses). Si una serie tuviera un mes de más, las líneas quedarían corridas sin ningún error. Más seguro: usar para cada línea sus propias fechas (df_btc_search.MONTH).
Estilo de línea
Código
Continua
'-'
Discontinua (rayas)
'--'
Raya y punto
'-.'
Punteada
':'
🛠️ Nota técnica: el resumen del curso llama "punteada" (dotted) a '-.'; en realidad es raya y punto. La línea punteada es ':'.
💡 Precaución sobre Bitcoin: este análisis describe cómo se movieron las búsquedas y el precio en el pasado; no sirve para predecir precios ni para decidir inversiones (sección 9).
09
7. Desempleo: cuadrículas, estacionalidad y media móvil#
Reto: búsquedas de "unemployment benefits" contra la tasa oficial de desempleo (de la FRED, la base de datos de la Reserva Federal de San Luis): título, etiqueta "FRED U/E Rate", límites, y una cuadrícula gris de líneas discontinuas. ¿Hay estacionalidad en las búsquedas?
Python
1
ax1.grid(color='grey', linestyle='--')
Las líneas verticales de la cuadrícula coinciden con los picos de búsqueda: "muchos picos son a fin de año, en diciembre", lo que muestra estacionalidad en el mercado laboral. La crisis financiera de 2007/2008 disparó el desempleo, que tardó unos 10 años (2007–2017) en volver al nivel anterior. Curiosamente, el gran pico de búsquedas de finales de 2013 no vino con un aumento del desempleo: "algo más estaba pasando".
Reto: la media móvil de 3 o 6 meses de las búsquedas. ¿Qué línea se mueve primero?
Las búsquedas suben antes que la tasa oficial y bajan antes: funcionan como un indicador adelantado, mientras que la tasa de desempleo es un indicador rezagado.
La búsqueda máxima (100) es de febrero de 2009, con desempleo de 8,3 %; el desempleo máximo del periodo 2004–2019 es 10,0 %, en octubre de 2009: las búsquedas tocaron techo meses antes que el desempleo, como dice el curso.
La estacionalidad: en los 16 años completos, el mes con más búsquedas fue enero en 12 años, diciembre en 2, y febrero y julio en 1 cada uno.
🛠️ Nota técnica (verificado): la estacionalidad existe, pero el pico es mayoritariamente enero, no diciembre. Las líneas verticales de la cuadrícula están en el 1 de enero de cada año (el localizador de años), y los picos caen justo ahí. Una explicación plausible: muchos empleos temporales de fin de año terminan en diciembre y la gente busca el subsidio en enero. Lección: comprueba con los datos lo que el gráfico parece decir (Reto 1).
🟢 Reto 1: calcula el promedio de búsquedas por mes del año (df.groupby(df.MONTH.dt.month).UE_BENEFITS_WEB_SEARCH.mean()) y grafícalo en barras. ¿Qué mes gana?
🟡 Reto 2: mide el "adelanto": desplaza la serie de búsquedas con .shift(n) y calcula la correlación con el desempleo (.corr()) para n de 0 a 12 meses. ¿Con qué desplazamiento la correlación es mayor?
🟠 Reto 3: investiga qué pasó a finales de 2013 en Estados Unidos con los subsidios de desempleo (pista: busca "emergency unemployment compensation 2013") y escribe una anotación en el gráfico con ax.annotate.
"No es bonito": el desempleo en Estados Unidos se disparó a niveles sin precedentes durante la pandemia de COVID, eclipsando los de la crisis financiera. "Esperemos que la recuperación sea más rápida esta vez".
Resumen del día:.describe(); .resample(); localizadores de matplotlib.dates; contar NaN con .isna().values.sum(); dpi; estilos '--' y '-.'; marcadores ('o', '^'); límites, etiquetas, grosor y colores (con nombre y hexadecimales); .grid() para la estacionalidad.
El archivo con 2020 tiene 200 filas (hasta agosto de 2020); el desempleo máximo es 14,7 % en abril de 2020, frente al 10,0 % de 2009.
Por qué importa: los datos nuevos no solo agregan puntos, pueden cambiar la escala y la historia. En el gráfico de 2004–2019, la crisis de 2008 parece enorme; con 2020, se ve pequeña. Además, como Google Trends escala a 100 el máximo del periodo, el pico de búsquedas de 2020 reescala todos los valores anteriores: los "100" de 2009 dejan de serlo. Compara las dos versiones del CSV (Reto 3 del proyecto).
Encuentra cinco problemas: 'M' falla en pandas 3 ('ME'); ax1.ylabel no existe (es ax1.set_ylabel); se grafican 2203 fechas diarias contra 73 precios mensuales (ValueError por tamaños distintos); linestyle='dotted' funciona, pero marker='circle' no es válido (es 'o'); y el gráfico no tiene título ni límites.
¿Las búsquedas de los colombianos reflejan su calendario? Por ejemplo, "ICFES" o "Saber 11" antes de la prueba, "uniformes" antes del regreso a clases, "lluvias" en temporada invernal.
En trends.google.com, elige dos términos relacionados con tu vida escolar o tu región, con Colombia como región y un periodo de al menos 5 años. Descarga el CSV (botón de descarga del gráfico).
Límpialo: las primeras filas del CSV de Google Trends suelen ser encabezados descriptivos (skiprows), y los valores "<1" deben convertirse.
Remuestrea si hace falta y grafica con localizadores de fecha, cuadrícula y una anotación en cada pico.
Calcula el promedio por mes del año y responde: ¿en qué mes se busca más cada término? ¿Coincide con lo que esperabas?
Compara con un dato oficial si existe (por ejemplo, las fechas de la prueba Saber 11 publicadas por el ICFES).
Tras la limpieza, la columna de búsqueda es numérica (dtype entero o decimal) y sin NaN inesperados.
El gráfico tiene título, ejes, fuente de los datos y fecha de descarga.
14
12. 🏆 Proyecto integrador del bloque de datos (Días 72 a 75)#
(Proyecto creado para esta adaptación. Se construyó por etapas desde el Día 72. El curso continúa con más análisis de datos hasta el Día 81; este integrador reúne lo aprendido en los Días 72 a 75.)
En el colegio y en el municipio se toman decisiones (dónde poner una ciclovía, cuándo programar las jornadas de salud, cómo reducir la deserción) muchas veces sin mirar los datos, aunque existen datos abiertos públicos que podrían ayudar.
Colombia publica miles de conjuntos de datos abiertos en datos.gov.co. Con pandas y Matplotlib, un grupo de estudiantes puede convertirlos en información útil… si trabaja con rigor: limpiar bien, graficar con honestidad y no confundir correlación con causalidad.
¿Qué puede aprender nuestra comunidad educativa a partir de los datos abiertos sobre un tema que nos afecta, y cómo comunicarlo de forma clara y honesta?
Construir el Observatorio de Datos del Colegio: un cuaderno de análisis, bien documentado, que responda tres preguntas sobre un tema de la comunidad con datos abiertos, y un informe de una página para la comunidad.
Exploración y limpieza (Día 72): función diagnostico(df); faltantes y datos basura tratados y documentados.
Series de tiempo (Días 73 y 75): fechas convertidas; remuestreo a una frecuencia común; media móvil donde ayude; gráfico de líneas con localizadores de fecha.
Agregación y combinación (Día 74): al menos dos conjuntos de datos unidos con merge; resúmenes con groupby y agregación con nombre.
Visualización (Días 73 a 75): mínimo cuatro gráficos de al menos tres tipos distintos (líneas, barras, dispersión; dos ejes solo si se justifica), todos con título, ejes, unidades, fuente y leyenda cuando corresponda.
Estacionalidad o tendencia (Día 75): identificar y comprobar con números un patrón en el tiempo.
Conclusiones para cada pregunta, separando lo que los datos muestran de lo que no pueden mostrar.
No funcionales
Reproducibilidad: el cuaderno funciona con Reiniciar y ejecutar todo; los datos están en una carpeta data/ con un FUENTES.md (URL, fecha de descarga, licencia).
Compatibilidad: código que funcione con pandas 3 ('ME', numeric_only=True, .loc en lugar de posiciones).
Accesibilidad: colores con contraste suficiente (Día 65), sin depender solo del color, fuentes legibles.
Ética: ningún dato que permita identificar a personas; datos del colegio solo agregados y con autorización.
Escribe una función grafico_dos_ejes(fechas, serie1, serie2, titulo, etiqueta1, etiqueta2) que dibuje el gráfico con dos ejes, colores, localizadores y cuadrícula, y úsala para los tres casos del día.
Ciclo de trabajo: formula el problema → inténtalo tú → consulta a la IA → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.
Actividad 1 — Alias de frecuencia. Pide a una IA el código para pasar datos diarios a mensuales con pandas. Si usa 'M', ejecútalo en pandas 3 y consulta la tabla de alias de la guía de usuario de pandas (Time series / date functionality, sección de offset aliases).
Actividad 2 — ¿Causalidad? Muestra a una IA el gráfico de búsquedas de Bitcoin contra el precio y pregúntale si las búsquedas "hacen subir" el precio. ¿Distingue correlación de causalidad? Compara con la sección 9.
Actividad 3 — Verificar una afirmación del curso. Pregunta a una IA en qué mes hay más búsquedas de "unemployment benefits" en Estados Unidos. Comprueba con los datos (Reto 1 de la sección 7) y anota si la IA, el curso y los datos coinciden.
17
15. 🛠️ Correcciones técnicas al material original#
Archivo
Afirmación o comportamiento del curso
Observación
Tratamiento
552
resample('M', on='DATE'); alias 'Y' y 'T'
En pandas 3 falla (ValueError, verificado); los alias actuales son 'ME', 'YE' y 'min'
Nota técnica de la sección 3
552
"Hay 2 valores faltantes"
Son 2 celdas en una fila (2020-08-04) (verificado)
Nota técnica de la sección 2
556
"Muchos picos son a fin de año, en diciembre"
En 12 de 16 años el pico es enero (verificado)
Nota técnica de la sección 7
558
'-.' como línea "punteada"
Es raya y punto; la punteada es ':'
Nota técnica de la sección 6
555
Graficar df_btc_monthly.index con df_btc_search.BTC_NEWS_SEARCH
Funciona porque coinciden los 73 meses (verificado); es frágil si cambian los datos
Sección 6
550
"Telsa"
Errata: Tesla
—
553, 555
Límites fijos (600 y 15 000)
Adecuados para estos datos (máximos verificados: 498,32 y ≈14 156)
Secciones 4 y 6
556, 557
Búsquedas como indicador adelantado
Coherente con los datos (pico de búsqueda feb. 2009, desempleo oct. 2009); se agrega la distinción entre correlación y causalidad
Google Trends, Bitcoin, Tesla, desempleo, qué se aprende, cuaderno y 5 CSV
550
Sección 0
Preparar el cuaderno
—
Sección 10
Reto de exploración, formas, máximos y mínimos, describe(), periodicidad, significado de 0–100, parámetros de búsqueda
551
Sección 1
Reto de exploración
Escala relativa
Secciones 10 y 11
Faltantes con isna().values.any() y .sum(), ubicarlos, dropna(), to_datetime en los 4 DataFrames, resample('M') con last() y mean(), 73 filas
552
Secciones 2 y 3
Retos de limpieza, fechas y remuestreo
'ME'; retos 1–3 de la sección 3
Secciones 10, 11 y 12
Tesla con dos ejes, colores con nombre y hexadecimales, tamaño, fuentes, rotación, título, grosor, DPI, límites, plt.show(), advertencia del convertidor
553
Sección 4
Retos de estilo
plt contra ax, savefig
Secciones 10 y 12
matplotlib.dates, YearLocator, MonthLocator, DateFormatter, Model 3 en 2016
554
Sección 5
Localizadores
Códigos de formato
Secciones 10, 11 y 12
Bitcoin: título, límites, linestyle='--', marker='o', índice de la serie mensual, 2018 y 2019
555
Sección 6
Reto de Bitcoin
Tabla de estilos; fechas propias
Sección 10
Desempleo, FRED, cuadrícula, estacionalidad, crisis de 2008, pico de 2013, media móvil, indicador adelantado
El cuaderno de las tres historias con Google Trends y sus 4 gráficos en PNG.
La comprobación numérica de la estacionalidad del desempleo (Reto 1 de la sección 7) y del "adelanto" (Reto 2).
El análisis de búsquedas en Colombia (proyecto extra).
El Observatorio de Datos del Colegio con todos sus entregables.
Cuaderno de errores:resample('M') en pandas 3, contar celdas en vez de filas con NaN, graficar series de distinto tamaño, ax.ylabel en lugar de ax.set_ylabel, marker='circle', series corridas por el orden, comparar valores de Google Trends de descargas distintas, confundir correlación con causalidad.