Saltar al contenido
Volver al inicio 100 Días de Código en Python — Día 78: Regresión lineal con seaborn y scikit-learn — ¿más presupuesto, más taquilla?

100 Días de Código en Python — Día 78: Regresión lineal con seaborn y scikit-learn — ¿más presupuesto, más taquilla?

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 78 (nivel avanzado).

Este tutorial adapta pedagógicamente el Día 78. Las secciones 📝 Contenido original del curso provienen de las lecciones 574 a 582; el cuaderno de inicio y el archivo cost_revenue_dirty.csv están en los recursos del curso (Seaborn Linear Regression (start).zip). En varias lecciones parte del código aparece solo en imágenes; aquí se reconstruye y se verifica. Los retos 🟢🟡🟠🔴, la sección qué dice y qué no dice una regresión, el proyecto extra y la etapa 3 del Laboratorio de Datos Abiertos 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, seaborn 0.13.2, Matplotlib 3.11.2 y scikit-learn 1.9.1, y con una copia pública de cost_revenue_dirty.csv (publicada en GitHub por otro estudiante del mismo curso), se ejecutó todo el código. Los resultados coinciden con el curso: 5 391 películas sin faltantes ni duplicados; 512 sin ingresos en Estados Unidos y 357 sin ingresos mundiales; 7 sin estrenar; 37,2 % que no recupera su presupuesto; 153 películas anteriores a 1970; intercepto de −8,65 millones, pendiente de 3,12 y r² de 0,558 para las películas recientes. Hay dos diferencias importantes: la predicción de "unos 600 millones para una película de 350 millones" usa el modelo de las películas antiguas (el de peor ajuste, r² = 0,03), mientras que el modelo de las películas recientes predice unos 1 084 millones; y la afirmación de que "todas las películas del cuartil inferior pierden dinero" no se sostiene: del 25 % con menor presupuesto, pierde dinero el 51,6 %.


Tabla de contenidos

  1. Antes de empezar: la pregunta del día
  2. Explorar y limpiar con bucles anidados
  3. Las películas sin ingresos
  4. Filtrar con varias condiciones: & y query
  5. seaborn: de la dispersión a las burbujas
  6. División entera: de años a décadas
  7. Dibujar una regresión con regplot
  8. Calcular la regresión con scikit-learn
  9. 📖 Qué dice y qué no dice una regresión
  10. 🚀 Proyecto del día: asesoría para un estudio de cine
  11. 🌟 Proyecto extra: ¿el calor vende bebidas en la tienda escolar?
  12. 🏗️ Laboratorio de Datos Abiertos — Etapa 3
  13. 🧩 Comprueba lo aprendido
  14. 🤖 Programar con apoyo de IA
  15. 🛠️ Correcciones técnicas al material original
  16. 📋 Matriz de trazabilidad
  17. 📦 Evidencias de aprendizaje del Día 78

0. Antes de empezar: la pregunta del día

(Fuente: 574-day-78-goals-what-you-will-make-by-the-end-of-the-day.md)

📝 Contenido original del curso

Hoy analizamos datos de presupuestos e ingresos de películas, ideales para probar dos herramientas nuevas: scikit-learn, para calcular una regresión lineal, y seaborn, una biblioteca de visualización construida sobre Matplotlib.

La pregunta: ¿los presupuestos más altos generan más ingresos en taquilla? Es decir, ¿un estudio debería gastar más en una película para ganar más?

Aprenderás a usar seaborn; a calcular e interpretar una regresión lineal con scikit-learn; a dibujar una regresión sobre un gráfico de dispersión; a agregar una tercera dimensión para crear un gráfico de burbujas; y a usar la división entera // para transformar datos.

Preparación: descarga el ZIP, sube el cuaderno a Google Drive, ábrelo con Colab y agrega cost_revenue_dirty.csv.

Prerequisitos: limpieza y conversión de tipos (Días 72 y 76), fechas (Día 73), gráficos de dispersión (Día 74) y la ecuación de la recta y = mx + b de matemáticas. En tu computador: pip install seaborn scikit-learn.


1. Explorar y limpiar con bucles anidados

(Fuente: 575-explore-and-clean-the-data.md)

🎯 Objetivo de aprendizaje

Limpiar varias columnas con el mismo procedimiento usando bucles, en vez de repetir código.

📝 Contenido original del curso

Reto 1: ¿cuántas filas y columnas?, ¿hay NaN?, ¿hay duplicados?, ¿de qué tipo son las columnas? Reto 2: convierte USD_Production_Budget, USD_Worldwide_Gross y USD_Domestic_Gross a número quitando $ y , (domestic = Estados Unidos). Reto 3: convierte Release_Date a fecha.

"Con cualquier conjunto de datos nuevo, primero miro .shape, .head(), .tail(), .info() y .sample()". Hay miles de películas y caracteres no numéricos en las columnas de dinero.

Python
data.isna().values.any()          # False
data.duplicated().values.any()    # False
duplicated_rows = data[data.duplicated()]
len(duplicated_rows)              # 0

Para limpiar, un bucle anidado: una lista de caracteres por quitar y otra de columnas.

Python
chars_to_remove = [',', '$']
columns_to_clean = ['USD_Production_Budget',
                    'USD_Worldwide_Gross',
                    'USD_Domestic_Gross']

for col in columns_to_clean:
    for char in chars_to_remove:
        # Reemplaza cada carácter por una cadena vacía
        data[col] = data[col].astype(str).str.replace(char, "")
    # Convierte la columna a número
    data[col] = pd.to_numeric(data[col])

data.Release_Date = pd.to_datetime(data.Release_Date)

📖 Explicación conceptual (verificada)

Resultado verificado: 5 391 filas y 6 columnas (Rank, Release_Date, Movie_Title y las tres de dinero). Tras la limpieza, las columnas de dinero son int64 y la fecha es datetime64.

¿Por qué un bucle anidado? El bucle exterior recorre columnas y el interior, caracteres: 3 × 2 = 6 reemplazos con 4 líneas. Si mañana aparece un tercer carácter (por ejemplo, un espacio), solo agregas ' ' a la lista. Es el principio DRY (Don't Repeat Yourself).

Otra forma (adaptación): una sola expresión regular que quita todo lo que no sea dígito:

Python
for col in columns_to_clean:
    data[col] = pd.to_numeric(data[col].str.replace(r'[^\d]', '', regex=True))

[^\d] significa "cualquier carácter que no sea un dígito". Es más corta, pero más difícil de leer si no conoces las expresiones regulares: elige la que tu equipo entienda.

💡 Formato de fecha: las fechas del CSV están en formato estadounidense (8/2/1915 = 2 de agosto). pd.to_datetime las interpreta como mes/día por defecto. Si tus datos son colombianos (día/mes), usa dayfirst=True o, mejor, format='%d/%m/%Y'.

⚠️ Errores frecuentes

  • La conversión dentro del bucle interior: si pd.to_numeric va dentro del segundo for, se intenta convertir cuando aún quedan $ → ValueError. Fíjate en la sangría: va en el bucle exterior.
  • Olvidar asignar: data[col].str.replace(...) sin data[col] = no cambia nada.
  • Aplicar .str a una columna ya numérica: si ejecutas la celda dos veces, la segunda vez astype(str) evita el error, pero es mejor ejecutar la limpieza una sola vez desde la carga (Reiniciar y ejecutar todo).

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: convierte el bucle en una función limpiar_columnas(df, columnas, caracteres) y úsala.
  • 🟡 Reto 2: agrega una columna Profit = ingreso mundial − presupuesto. ¿Cuál es la película con mayor ganancia? ¿Y con mayor pérdida?
  • 🟠 Reto 3: agrega una columna ROI (retorno) = ingreso mundial / presupuesto. ¿Qué película tiene el mayor retorno? ¿Por qué el ROI puede engañar con presupuestos muy pequeños?

2. Las películas sin ingresos

(Fuente: 576-investigate-the-films-that-had-zero-revenue.md)

📝 Contenido original del curso

Reto 1: ¿presupuesto promedio?, ¿ingreso mundial promedio?, ¿mínimos?, ¿el 25 % inferior gana o pierde?, ¿máximos?, ¿cuánto ganaron las películas de menor y mayor presupuesto? Casi todo se responde con .describe().

  • La película promedio cuesta unos 31 millones y gana unas 3 veces eso (≈ 89 millones).
  • "Todas las películas del cuartil inferior pierden dinero, porque su costo promedio es de 5 millones y solo ganan 3,8 millones".
  • El mínimo es 0 (películas nunca exhibidas o canceladas). El mayor presupuesto es de 425 millones y el mayor ingreso, 2 783 918 982 dólares.
  • La de menor presupuesto (My Date With Drew) costó 1 100 dólares y ganó 181 041. La de mayor presupuesto es Avatar.

Reto 2: ¿cuántas películas no ganaron nada en Estados Unidos? Reto 3: ¿y en el mundo?

Python
zero_domestic = data[data.USD_Domestic_Gross == 0]
print(f'Number of films that grossed $0 domestically {len(zero_domestic)}')   # 512
zero_domestic.sort_values('USD_Production_Budget', ascending=False)

zero_worldwide = data[data.USD_Worldwide_Gross == 0]
print(f'Number of films that grossed $0 worldwide {len(zero_worldwide)}')     # 357

Las de mayor presupuesto sin ingresos se estrenan después de la recopilación de los datos (1 de mayo de 2018). ¿Por qué 512 contra 357? Porque algunas películas internacionales nunca se exhibieron en Estados Unidos, como Don Gato, el inicio de la pandilla (unos 4,5 millones de ingresos, 80 millones de presupuesto).

📖 Resultados verificados y una corrección importante

Presupuesto promedio 31,1 millones; ingreso mundial promedio 88,9 millones; los valores extremos coinciden con el curso.

🛠️ Nota técnica (verificado): .describe() calcula los cuartiles de cada columna por separado. El 25 % del presupuesto (5 millones) y el 25 % del ingreso (3,87 millones) no corresponden a las mismas películas, así que compararlos no dice si "el cuartil inferior" pierde dinero. Hay que seleccionar las películas y compararlas fila por fila:

Python
q1 = data.USD_Production_Budget.quantile(0.25)
baratas = data[data.USD_Production_Budget <= q1]
(baratas.USD_Production_Budget > baratas.USD_Worldwide_Gross).mean()   # 0.516

Del 25 % de películas con menor presupuesto, pierde dinero el 51,6 %: muchas, pero no todas.

Es un error de razonamiento muy común: una tabla de resúmenes por columna no permite concluir sobre filas individuales.

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: calcula el porcentaje de películas que pierden dinero en cada cuartil de presupuesto (pista: pd.qcut(data.USD_Production_Budget, 4) y groupby).
  • 🟡 Reto 2: ¿qué porcentaje del ingreso mundial viene de Estados Unidos, en promedio? ¿Ha cambiado con los años?

3. Filtrar con varias condiciones: & y query

(Fuente: 577-filter-on-multiple-conditions-international-films.md)

🎯 Objetivo de aprendizaje

Combinar condiciones con &, | y ~, o con .query(), y entender por qué hacen falta los paréntesis.

📝 Contenido original del curso

¿Qué películas ganaron dinero en el mundo pero nada en Estados Unidos? Con .loc[] y el operador & (bitwise and):

Python
international_releases = data.loc[(data.USD_Domestic_Gross == 0) &
                                  (data.USD_Worldwide_Gross != 0)]

pandas está construido sobre NumPy, que usa los operadores bitwise de Python para comparar elemento a elemento. Como estos operadores tienen mayor precedencia que las comparaciones, hay que poner paréntesis.

Reto: lo mismo con .query() (se abre en otra pestaña), usando la palabra and:

Python
international_releases = data.query('USD_Domestic_Gross == 0 and USD_Worldwide_Gross != 0')
print(f'Number of international releases: {len(international_releases)}')

Reto: identifica las películas sin estrenar a la fecha de recopilación (1 de mayo de 2018), cuéntalas y crea data_clean sin ellas. Hay 7:

Python
data_clean = data.drop(future_releases.index)

Reto adicional: ¿qué porcentaje de películas no recupera su presupuesto en taquilla?

Python
money_losing = data_clean.loc[data_clean.USD_Production_Budget > data_clean.USD_Worldwide_Gross]
len(money_losing) / len(data_clean)
# o bien
money_losing = data_clean.query('USD_Production_Budget > USD_Worldwide_Gross')
money_losing.shape[0] / data_clean.shape[0]

¡Un 37,2 %! "¿Quién diría que las finanzas del cine son tan riesgosas?".

📖 Explicación conceptual (verificada)

La línea que el curso muestra solo en imagen es:

Python
scrape_date = pd.Timestamp('2018-5-1')
future_releases = data[data.Release_Date >= scrape_date]
len(future_releases)    # 7: A Wrinkle in Time, Amusement Park, Meg, Aquaman, Singularity...

Resultados verificados: 155 estrenos solo internacionales (con ambas formas); 5 384 películas en data_clean; 37,28 % no recupera su presupuesto.

Operadores de pandas contra palabras de Python:

Significado En [...] y .loc En .query('...') En un if normal
Y & and and
O | or or
No ~ not not

¿Por qué no and dentro de los corchetes? Porque and pregunta "¿toda esta columna es verdadera?", y una columna de miles de valores no es ni verdadera ni falsa. Verificado sin paréntesis:

Texto
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Ese mensaje aparece también si usas and en vez de &: cuando lo veas, revisa operadores y paréntesis.

⚠️ Errores frecuentes

  • Olvidar los paréntesis: data.USD_Domestic_Gross == 0 & data.USD_Worldwide_Gross != 0 → el mensaje de arriba.
  • Nombres de columnas con espacios en query: hay que rodearlos con comillas invertidas: data.query('`Worldwide Gross` > 0').
  • Borrar filas con drop usando posiciones en vez del índice: drop(future_releases.index) funciona porque usa las etiquetas del índice.

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: ¿cuántas películas costaron más de 100 millones y perdieron dinero? Resuélvelo con & y con query.
  • 🟡 Reto 2: usa | para encontrar las películas que costaron menos de 1 millón o ganaron más de 1 000 millones.
  • 🟠 Reto 3: con query puedes usar variables con @: data.query('USD_Production_Budget > @limite'). Crea una función que reciba el límite y devuelva cuántas películas lo superan.

4. seaborn: de la dispersión a las burbujas

(Fuente: 578-seaborn-data-visualisation-bubble-charts.md)

🎯 Objetivo de aprendizaje

Crear gráficos con seaborn, ajustarlos con la capa de Matplotlib y representar tres variables en un gráfico de burbujas.

📝 Contenido original del curso

Seaborn (se abre en otra pestaña) está construido sobre Matplotlib y hace muy cómodas ciertas visualizaciones. Por convención se importa como sns.

Python
import seaborn as sns

sns.scatterplot(data=data_clean,
                x='USD_Production_Budget',
                y='USD_Worldwide_Gross')

Como seaborn usa Matplotlib por debajo, podemos entrar a esa capa en cualquier momento: cambiar el tamaño de la figura o configurar el objeto Axes que devuelve sns.scatterplot():

Python
plt.figure(figsize=(8, 4), dpi=200)

ax = sns.scatterplot(data=data_clean,
                     x='USD_Production_Budget',
                     y='USD_Worldwide_Gross',
                     hue='USD_Worldwide_Gross',   # color
                     size='USD_Worldwide_Gross')  # tamaño del punto

ax.set(ylim=(0, 3000000000),
       xlim=(0, 450000000),
       ylabel='Revenue in $ billions',
       xlabel='Budget in $100 millions')

plt.show()

hue y size crean un gráfico de burbujas: las películas más taquilleras se ven más grandes y oscuras. Para aplicar un estilo a un solo gráfico se usa with (como al abrir archivos):

Python
with sns.axes_style('darkgrid'):
    ax = sns.scatterplot(...)

Otros temas: 'whitegrid', 'dark', 'ticks'.

Reto: un gráfico de burbujas con fecha de estreno en x, presupuesto en y y el ingreso mundial como color y tamaño:

Python
plt.figure(figsize=(8, 4), dpi=200)

with sns.axes_style("darkgrid"):
    ax = sns.scatterplot(data=data_clean,
                         x='Release_Date',
                         y='USD_Production_Budget',
                         hue='USD_Worldwide_Gross',
                         size='USD_Worldwide_Gross')

    ax.set(ylim=(0, 450000000),
           xlim=(data_clean.Release_Date.min(), data_clean.Release_Date.max()),
           xlabel='Year',
           ylabel='Budget in $100 millions')

Análisis: los presupuestos "explotaron" en los últimos 40 años. Hasta los años 70 la industria parecía estar en otra era; los presupuestos crecieron rápido desde los 80 y siguieron creciendo en los 2000. La industria también produce muchísimas más películas: desde el 2000 los puntos se amontonan.

📖 Explicación conceptual (adaptación)

Matplotlib, seaborn y plotly:

Matplotlib seaborn plotly
Nivel Bajo: controlas todo Alto: gráficos estadísticos con una línea Alto: interactivo
Entrada típica Listas o arreglos DataFrame + nombres de columnas DataFrame + nombres de columnas
Punto fuerte Personalización total hue, size, regresiones, distribuciones Interactividad, mapas
Se combinan — Devuelve un Axes de Matplotlib Independiente

Leer el eje: el eje y va hasta 3 000 000 000; Matplotlib muestra 1e9 arriba del eje, que significa "multiplica por mil millones". De ahí la etiqueta "Revenue in $ billions". Una forma más legible es dividir los datos antes de graficar (por ejemplo, en millones).

Cuidado con las burbujas: usar la misma variable en hue y size es redundante (refuerza la idea, pero no agrega información). En el reto, en cambio, el gráfico muestra tres variables distintas: tiempo, presupuesto e ingreso.

⚠️ Errores frecuentes

  • ax.set(...) fuera del bloque with: funciona, pero el estilo solo se aplica a lo creado dentro del bloque.
  • Límites de fecha con números: xlim=(1915, 2018) no funciona si x es una fecha; usa fechas (Release_Date.min()).
  • Demasiados puntos superpuestos: agrega transparencia (alpha=0.3) para ver dónde se amontonan.

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: repite el gráfico de burbujas con los datos en millones y etiquetas en español.
  • 🟡 Reto 2: colorea los puntos según si la película ganó o perdió dinero (crea una columna con np.where).

5. División entera: de años a décadas

(Fuente: 579-floor-division-a-trick-to-convert-years-to-decades.md)

📝 Contenido original del curso

Reto: crea una columna Decade (1992 y 1999 → 1990): 1) crea un DatetimeIndex a partir de Release_Date; 2) toma los años con .year; 3) usa la división entera //; 4) agrega la columna.

Python
dt_index = pd.DatetimeIndex(data_clean.Release_Date)
years = dt_index.year

5.0 / 2     # 2.5
5.0 // 2    # 2.0  (redondea hacia abajo)

decades = years // 10 * 10
data_clean['Decade'] = decades

Reto: crea old_films (antes de 1970, hasta 1969 incluido) y new_films (desde 1970). ¿Cuántas películas antiguas hay? ¿Cuál fue la más cara?

Python
old_films = data_clean[data_clean.Decade <= 1960]
new_films = data_clean[data_clean.Decade > 1960]

El corte es 1960 porque la década de 1960 incluye 1969. Hay solo 153 películas antiguas; la más cara es Cleopatra, con 42 millones de presupuesto.

📖 Explicación conceptual (verificada)

Texto
1994 // 10       = 199
199 * 10         = 1990

La división entera "corta" el último dígito y la multiplicación lo vuelve cero. Resultados verificados: 153 películas antiguas y 5 231 recientes; la década con más películas es la de 2000 (2 181).

Una forma más directa (adaptación): data_clean['Decade'] = data_clean.Release_Date.dt.year // 10 * 10 (verificado: mismo resultado). El accesor .dt evita crear el DatetimeIndex.

⚠️ Errores frecuentes

  • Números negativos: // redondea hacia abajo, no "hacia cero": -7 // 2 es −4, no −3.
  • // con decimales devuelve decimales: 5.0 // 2 es 2.0, no 2.
  • Corte mal puesto: Decade < 1960 excluiría toda la década de los 60.

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: cuenta las películas por década y grafícalas en barras con seaborn (sns.countplot).
  • 🟡 Reto 2: calcula el presupuesto mediano por década. ¿Cuánto creció entre 1970 y 2010?
  • 🟠 Reto 3: usa la misma técnica para agrupar edades en rangos de 5 años (15–19, 20–24…).

6. Dibujar una regresión con regplot

(Fuente: 580-plotting-linear-regressions-with-seaborn.md)

🎯 Objetivo de aprendizaje

Superponer una línea de regresión a un gráfico de dispersión y juzgar visualmente qué tan bien se ajusta.

📝 Contenido original del curso

.regplot() (se abre en otra pestaña) dibuja la dispersión y la recta de regresión con su intervalo de confianza:

Python
plt.figure(figsize=(8, 4), dpi=200)
with sns.axes_style("whitegrid"):
    sns.regplot(data=old_films,
                x='USD_Production_Budget',
                y='USD_Worldwide_Gross',
                scatter_kws={'alpha': 0.4},
                line_kws={'color': 'black'})

Se ve Cleopatra a la derecha, y muchas películas de bajo presupuesto que ganaron mucho más. La relación no es fuerte: muchos puntos están muy lejos de la línea.

Reto: regplot para new_films con fondo 'darkgrid', sin valores negativos en los ejes, etiquetas "Revenue in $ billions" y "Budget in $ millions", puntos azul oscuro (#2f4b7c) y línea naranja (#ff7c43). ¿Se ajusta mejor? ¿Cuánto ganaría una película de 150 millones?

Python
plt.figure(figsize=(8, 4), dpi=200)
with sns.axes_style('darkgrid'):
    ax = sns.regplot(data=new_films,
                     x='USD_Production_Budget',
                     y='USD_Worldwide_Gross',
                     color='#2f4b7c',
                     scatter_kws={'alpha': 0.3},
                     line_kws={'color': '#ff7c43'})

    ax.set(ylim=(0, 3000000000),
           xlim=(0, 450000000),
           ylabel='Revenue in $ billions',
           xlabel='Budget in $100 millions')

El ajuste es mucho mejor: los puntos se alinean con la recta y el intervalo de confianza es más estrecho. Una película de 150 millones ganaría, según la recta, un poco menos de 500 millones. "Podemos estar bastante seguros de que hay una relación entre presupuesto e ingreso". Pero ¿qué parte de la variación explica el presupuesto? ¿Cuánto ingreso extra da cada dólar? Para eso hay que ver los números del modelo.

📖 Explicación conceptual (adaptación)

¿Qué es una regresión lineal? Es la recta que mejor pasa entre los puntos: la que hace más pequeña la suma de los errores al cuadrado (distancias verticales de cada punto a la recta). Se llama "de mínimos cuadrados".

¿Qué es la banda sombreada? El intervalo de confianza del 95 % de la recta: dónde podría estar la recta "verdadera" si repitiéramos el estudio con otras películas parecidas. Es angosta donde hay muchos datos y se ensancha donde hay pocos. No indica dónde caerá una película individual (eso sería mucho más ancho).

Verificado: según el modelo de las películas recientes, una de 150 millones ganaría ≈ 460 millones, coherente con "un poco menos de 500 millones".

Retos adicionales (creados para esta adaptación)

  • 🟢 Reto 1: dibuja el regplot de las películas de la década de 2010 solamente.
  • 🟡 Reto 2: calcula la correlación (.corr()) para películas antiguas y recientes. (Verificado: 0,17 y 0,75.) ¿Cómo se relaciona con lo que ves?

7. Calcular la regresión con scikit-learn

(Fuente: 581-use-scikit-learn-to-run-your-own-regression.md y 582-learning-points-y-summary.md)

🎯 Objetivo de aprendizaje

Ajustar un modelo LinearRegression, interpretar intercepto, pendiente y r², y hacer una predicción.

📝 Contenido original del curso

Usamos una regresión univariada: una sola variable explicativa (el presupuesto), que en aprendizaje automático se llama feature (característica). La recta tiene la forma:

Texto
REVENUE = θ0 + θ1 · BUDGET
  • θ₀ (intercepto): el ingreso si el presupuesto fuera 0.
  • θ₁ (pendiente): cuánto ingreso extra da 1 dólar más de presupuesto.
Python
from sklearn.linear_model import LinearRegression

regression = LinearRegression()

# Variable(s) explicativa(s) o característica(s)
X = pd.DataFrame(new_films, columns=['USD_Production_Budget'])
# Variable de respuesta u objetivo
y = pd.DataFrame(new_films, columns=['USD_Worldwide_Gross'])

regression.fit(X, y)       # encuentra la mejor recta
regression.intercept_      # array([-8650768.00661042])
regression.coef_           # array([[3.12259592]])
regression.score(X, y)     # r cuadrado: ≈ 0.558

LinearRegression "no quiere" series de pandas, por eso se crean DataFrames. intercept_ y coef_ son atributos del objeto; el guion bajo final es parte del nombre.

Interpretación:

  • Intercepto: si el presupuesto es 0, el ingreso estimado es de −8,65 millones. Absurdo: hemos obligado al modelo a ser una recta, y el modelo es más preciso donde hay más datos, no en los extremos.
  • Pendiente: por cada dólar extra de presupuesto, el ingreso aumenta 3,1 dólares. Quizás por eso los presupuestos crecieron tanto en 30 años.
  • r² = 0,558: el modelo explica cerca del 56 % de la variación del ingreso. Para un modelo tan simple, con una sola variable, es bastante bueno.

Reto: la regresión para old_films. La pendiente es 1,6 (positiva), pero el r² es muy bajo: el modelo lineal es inadecuado para las películas antiguas.

Reto: ¿cuánto ingreso estima el modelo para una película de 350 millones? "Unos 600 millones":

Python
22821538 + 1.64771314 * 350000000

budget = 350000000
revenue_estimate = regression.intercept_[0] + regression.coef_[0, 0] * budget
revenue_estimate = round(revenue_estimate, -6)
print(f'The estimated revenue for a $350 film is around ${revenue_estimate:.10}.')

Resumen del día: bucles anidados para limpiar varias columnas; filtros con .loc[] y .query(); gráficos de burbujas con seaborn; estilos de seaborn y ajustes con Matplotlib; división entera; regresiones con seaborn; juzgar un modelo por su ajuste y su r²; regresiones y coeficientes con scikit-learn.

📖 Resultados verificados y una corrección importante

Modelo Intercepto Pendiente r² Predicción para 350 millones
Películas recientes (1970 en adelante) −8,65 millones 3,12 0,558 ≈ 1 084 millones
Películas antiguas (antes de 1970) 22,82 millones 1,65 0,029 ≈ 600 millones

🛠️ Nota técnica (verificado): los números de la predicción del curso (22821538 + 1.64771314 * 350000000) son el intercepto y la pendiente del modelo de las películas antiguas: en el cuaderno, la variable regression se reutilizó para el reto anterior. Ese modelo es el peor de los dos (r² = 0,03) y se construyó con películas de máximo 42 millones: estimar una de 350 millones es una extrapolación enorme. Con el modelo de las películas recientes la predicción es de unos 1 084 millones. Lección: dale a cada modelo su propio nombre (reg_nuevas, reg_antiguas).

🛠️ Nota técnica (verificado): el formato :.10 depende del número. Con 600 000 000 imprime 600000000.0, pero con 1 084 000 000 imprime 1.084e+09 (notación científica). Para dinero, usa un formato explícito: f'{revenue_estimate:,.0f}' → 1,084,000,000. Además, el texto dice "$350 film": falta "million".

Una forma más clara de predecir (adaptación, verificada):

Python
reg_nuevas = LinearRegression()
reg_nuevas.fit(new_films[['USD_Production_Budget']], new_films.USD_Worldwide_Gross)
reg_nuevas.predict(pd.DataFrame({'USD_Production_Budget': [350_000_000]}))
# array([1.0842578e+09])
  • new_films[['USD_Production_Budget']] (doble corchete) ya es un DataFrame: no hace falta pd.DataFrame(..., columns=...).
  • X debe tener 2 dimensiones (filas × características); con una serie, scikit-learn lanza ValueError: Expected a 2-dimensional container but got <class 'pandas.Series'>. En cambio, y sí puede ser una serie.
  • .predict() calcula θ₀ + θ₁·x por ti y funciona igual con 1 o con 13 características (Día 81).

⚠️ Errores frecuentes

Error Qué ocurre Solución
Pasar una serie como X ValueError: Expected a 2-dimensional container Doble corchete: df[['col']]
Reutilizar la misma variable para dos modelos Predices con el modelo equivocado sin notarlo Un nombre por modelo
Interpretar el intercepto literalmente "Una película gratis pierde 8 millones" El intercepto solo tiene sentido si x = 0 es posible y hay datos cerca
Predecir fuera del rango de los datos Resultados absurdos o engañosos Compara con X.min() y X.max()
Olvidar [0] y [0, 0] con y de 2 dimensiones Imprimes arreglos en vez de números Con y como serie, coef_ es 1D: coef_[0]

🔍 Antes de programar

¿Qué es X y qué es y en tu problema? ¿Qué unidades tiene la pendiente (dólares de ingreso por dólar de presupuesto)? ¿En qué rango de x tienes datos? ¿Tiene sentido x = 0?


8. 📖 Qué dice y qué no dice una regresión

(Sección creada para esta adaptación.)

Afirmación ¿La respalda la regresión?
"Las películas con más presupuesto tienden a ganar más" Sí (pendiente positiva, r² = 0,56 en las recientes)
"Si un estudio gasta 1 dólar más, ganará 3,1 dólares más" No necesariamente. Es una asociación. Las películas caras suelen tener actores famosos, secuelas y más publicidad: quizá eso, y no el gasto en sí, explica el ingreso
"Una película de 1 000 millones ganará unos 3 100 millones" No. No hay ninguna película de ese presupuesto: es extrapolación
"El 44 % restante de la variación es azar" No. Son factores que el modelo no incluye (género, estrellas, fecha, calidad…)
"El estudio gana el ingreso mundial" No. El ingreso de taquilla se reparte con los cines y distribuidores, y no incluye publicidad ni otros costos

Tres preguntas antes de creer en un modelo:

  1. ¿Qué tan bien se ajusta? (el r² y el gráfico).
  2. ¿Estoy prediciendo dentro del rango de los datos?
  3. ¿Hay otras variables que podrían explicar la relación?

9. 🚀 Proyecto del día: asesoría para un estudio de cine

(Integra las lecciones 575 a 582.)

Problema

Un estudio de cine colombiano quiere saber si le conviene hacer una película más cara y cuánto podría esperar de taquilla.

Contexto

Los datos son de Hollywood y llegan hasta 2018: el estudio debe saber qué se puede y qué no se puede trasladar a su caso.

Objetivo

Un cuaderno con el análisis del curso y un informe con una respuesta prudente y cuantificada.

Requisitos funcionales

  1. Limpieza con una función reutilizable; fechas convertidas; películas sin estrenar eliminadas.
  2. Estadísticas descriptivas y el porcentaje de películas que pierden dinero (global y por cuartil de presupuesto).
  3. Estrenos solo internacionales con & y con query.
  4. Gráfico de burbujas (tiempo, presupuesto, ingreso) y columna Decade.
  5. regplot para películas antiguas y recientes.
  6. Dos modelos con scikit-learn, con nombres distintos, con intercepto, pendiente y r².
  7. Predicciones para 20, 50 y 150 millones, indicando cuáles están dentro del rango de los datos.

Restricciones técnicas

  • Predicciones con .predict() y cifras formateadas con separador de miles.
  • Una celda de conclusiones que use el vocabulario de la sección 8.

Planificación

  1. Comprender la pregunta. 2. Entradas: el CSV. 3. Procesos: limpiar → filtrar → graficar → modelar → predecir. 4. Salidas: gráficos, tabla de modelos, informe. 5. Una sección por lección. 6. Bocetar los gráficos. 7. Implementar. 8. Probar. 9. Corregir. 10. Redactar.

Construcción paso a paso

  • Versión 1: carga y limpieza con función.
  • Versión 2: exploración y filtros.
  • Versión 3: gráficos de burbujas y décadas.
  • Versión 4: regresiones con seaborn y scikit-learn.
  • Versión final: predicciones e informe.

Pruebas

Comprobación Valor esperado (verificado)
Forma inicial (5 391, 6)
Sin ingresos en EE. UU. / en el mundo 512 / 357
Estrenos solo internacionales 155
Sin estrenar / data_clean 7 / 5 384 filas
Porcentaje que pierde dinero 37,2 %
Películas antiguas / recientes 153 / 5 231
r² recientes / antiguas 0,558 / 0,029

🧪 Prueba y depuración

Python
data['USD_Production_Budget'] = pd.to_numeric(data['USD_Production_Budget'].str.replace(',', ''))
international = data[data.USD_Domestic_Gross == 0 and data.USD_Worldwide_Gross != 0]
data_clean['Decade'] = data_clean.Release_Date // 10 * 10
regression = LinearRegression()
regression.fit(new_films.USD_Production_Budget, new_films.USD_Worldwide_Gross)
print(regression.intercept_ + regression.coef_ * 1_000_000_000)

Encuentra cinco problemas: falta quitar el $ antes de to_numeric (ValueError); and en lugar de & y sin paréntesis (ValueError: The truth value of a Series is ambiguous); // sobre una fecha en vez de sobre el año (.dt.year); X como serie (ValueError: Expected a 2-dimensional container); y la predicción para 1 000 millones es una extrapolación fuera del rango de los datos (máximo: 425 millones).

⭐ Extensión del proyecto

  • Reto 1: ajusta el modelo solo con películas de 2000 en adelante. ¿Cambian la pendiente y el r²?
  • Reto 2: grafica los residuos (ingreso real − predicho) contra el presupuesto. ¿Los errores crecen con el presupuesto? (Lo profundizarás en el Día 81.)
  • Reto 3: ajusta el modelo con el logaritmo del presupuesto y del ingreso (np.log). ¿Mejora el r²? (Pista: excluye los ingresos de 0.)

10. 🌟 Proyecto extra: ¿el calor vende bebidas en la tienda escolar?

(Proyecto creado para esta adaptación; no existe en el curso original.)

Situación

La tienda escolar quiere saber cuántas bebidas frías preparar cada día. El encargado cree que "cuando hace calor se vende más".

Requisitos

  1. Durante al menos 20 días de clase, registren (con autorización de la tienda y del docente) la temperatura máxima del día (de un servicio meteorológico o de un termómetro del colegio) y el número de bebidas frías vendidas. Si no es posible, usen datos abiertos equivalentes (por ejemplo, temperatura del IDEAM y un registro de consumo de otra fuente) y documenten la fuente.
  2. Limpien los datos: días sin clase, datos faltantes, registros mal escritos.
  3. Hagan un gráfico de dispersión con regplot y ajusten un modelo con scikit-learn.
  4. Interpreten la pendiente en palabras ("por cada grado más, se venden unas _ bebidas más").
  5. Predigan las ventas para una temperatura dentro del rango observado y expliquen por qué no conviene predecir para 40 °C.

Decisiones que debes tomar (y justificar)

  • ¿Qué otros factores podrían influir (día de la semana, eventos, precio, exámenes)? ¿Cómo los controlarías?
  • ¿Recomendarías usar el modelo? ¿Con qué r² mínimo te sentirías cómodo?

Casos de prueba

  • X tiene forma (n, 1) y y forma (n,).
  • La predicción para la temperatura promedio es cercana al promedio de ventas (propiedad de la regresión lineal: la recta pasa por el punto de los promedios).

11. 🏗️ Laboratorio de Datos Abiertos — Etapa 3

(Proyecto integrador creado para esta adaptación; etapas anteriores en los Días 76 y 77.)

Etapa 3 (hoy):

  1. Con la pareja de variables más correlacionada que encontraste en la etapa 2, crea un gráfico de burbujas con seaborn que muestre tres variables.
  2. Dibuja la regresión con regplot, con estilo y etiquetas en español.
  3. Ajusta el modelo con scikit-learn; reporta intercepto, pendiente, r² y el rango de x de tus datos.
  4. Escribe una interpretación siguiendo la tabla de la sección 8: qué dice y qué no dice tu modelo.
  5. Si tus datos tienen fechas, crea una columna de "década", "año" o "periodo" con // y compara el modelo entre periodos.

Producto de la etapa: sección 4 de laboratorio.ipynb.


12. 🧩 Comprueba lo aprendido

Conceptuales

  1. ¿Qué significan el intercepto y la pendiente en el modelo de películas?
  2. ¿Qué mide el r²? ¿Qué significa un r² de 0,03?
  3. ¿Por qué hacen falta paréntesis al combinar condiciones con &?
  4. ¿Por qué no se puede concluir con .describe() que "el cuartil inferior pierde dinero"?
  5. ¿Qué es extrapolar y por qué es peligroso?

Análisis de código

Python
data.query('USD_Production_Budget > USD_Worldwide_Gross and Decade >= 2000').shape[0]

¿Qué cuenta esta línea? Escríbela con .loc y &.

Predicción

¿Qué devuelve 1987 // 10 * 10? ¿Y -15 // 10 * 10?

Depuración

"Mi modelo predice que una película de 350 millones gana 600 millones, pero mi compañero obtiene 1 084 millones con el mismo código". ¿Qué pudo pasar?

Modificación

Cambia el gráfico de regplot para mostrar los ejes en millones de dólares.

Creación

Escribe ajustar_y_reportar(df, x, y) que ajuste un modelo, imprima intercepto, pendiente, r² y el rango de x, y devuelva el modelo.


13. 🤖 Programar con apoyo de IA

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 — Verificar una conclusión. Muestra a una IA la tabla de .describe() y pregúntale si el 25 % inferior de las películas pierde dinero. Si dice que sí, pídele que lo compruebe fila por fila y compara con tu cálculo (51,6 %).

Actividad 2 — Causalidad. Pregunta a una IA: "Si la pendiente es 3,1, ¿cuánto ganará un estudio si aumenta su presupuesto en 10 millones?". ¿Distingue asociación de causalidad? ¿Menciona la extrapolación?

Actividad 3 — Documentación. Pide a una IA el código para predecir con LinearRegression. Compara su uso de predict, coef_ e intercept_ con la documentación de scikit-learn (sklearn.linear_model.LinearRegression), en especial la forma que deben tener X y y.


14. 🛠️ Correcciones técnicas al material original

Archivo Afirmación o comportamiento del curso Observación Tratamiento
576 "Todas las películas del cuartil inferior pierden dinero" Los cuartiles de describe() son por columna; del 25 % de menor presupuesto pierde el 51,6 % (verificado) Nota técnica de la sección 2
581 Predicción de ≈ 600 millones para 350 millones Usa el modelo de películas antiguas (r² = 0,03, máximo 42 millones): extrapolación; el modelo reciente da ≈ 1 084 millones (verificado) Nota técnica de la sección 7
581 f'{revenue_estimate:.10}' Con otros valores imprime notación científica (1.084e+09, verificado); falta "million" en el texto Nota técnica de la sección 7
581 "Por cada dólar extra, el ingreso aumenta 3,1 dólares" Es una asociación, no un efecto causal garantizado Sección 8
577 Código de future_releases (solo en imagen) Reconstruido y verificado: 7 películas Sección 3
579 pd.DatetimeIndex(...).year Funciona; alternativa directa: .dt.year (verificado, mismo resultado) Sección 5
580 El reto pide la etiqueta "Budget in $ millions"; la solución usa "$100 millions" Diferencia menor entre enunciado y solución —
577 Enlace a la documentación de Python 3.4 (operator) Versión muy antigua; los operadores siguen igual —
575 a 581 Resultados del curso Coinciden (verificado): 5 391 filas, 512, 357, 155, 7, 37,2 %, 153, −8,65 millones, 3,12, 0,558 —

15. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
Pregunta del día, seaborn, scikit-learn, burbujas, //, cuaderno y CSV 574 Sección 0 Preparar el cuaderno — Sección 9
Exploración, NaN y duplicados, bucle anidado de limpieza, to_datetime 575 Sección 1 Retos 1–3 Función, Profit, ROI Secciones 9 y 11
describe, promedios, cuartil inferior, extremos, películas sin ingresos en EE. UU. y en el mundo 576 Sección 2 Retos 1–3 Pérdidas por cuartil Sección 9
.loc con &, precedencia, query, películas sin estrenar, 37,2 % 577 Sección 3 Retos de filtros, sin estrenar y reto adicional Retos 1–3 de la sección 3 Sección 9
sns.scatterplot, capa de Matplotlib, hue, size, axes_style con with, burbujas en el tiempo 578 Sección 4 Reto de presupuestos en el tiempo Millones; ganó o perdió Secciones 9, 10 y 11
DatetimeIndex, .year, //, Decade, películas antiguas y recientes, Cleopatra 579 Sección 5 Dos retos Retos 1–3 de la sección 5 Secciones 9 y 11
regplot, intervalo de confianza, scatter_kws, line_kws, colores HEX, 150 millones 580 Secciones 6 y 8 Reto de películas recientes Década de 2010; correlación Secciones 9, 10 y 11
Regresión univariada, θ₀ y θ₁, LinearRegression, fit, intercept_, coef_, r², películas antiguas, predicción 581 Secciones 7 y 8 Retos de películas antiguas y predicción .predict(); nombres de modelos Secciones 9, 10 y 11
Resumen del día 582 Sección 7 — — Sección 9
(No existe en el curso original) — Sección 8 — Qué dice y qué no dice una regresión Secciones 9, 10 y 11
(No existe en el curso original) — — — La tienda escolar Sección 10
(No existe en el curso original) — — — Laboratorio de Datos Abiertos, etapa 3 Sección 11

16. 📦 Evidencias de aprendizaje del Día 78

  • El cuaderno del análisis de películas con los dos modelos y la tabla de pruebas superada.
  • El informe para el estudio de cine, con predicciones dentro del rango de los datos.
  • El análisis de la tienda escolar (proyecto extra).
  • La etapa 3 del Laboratorio de Datos Abiertos.
  • Cuaderno de errores: conversión dentro del bucle equivocado, and en vez de &, paréntesis olvidados, conclusiones por columna aplicadas a filas, // sobre fechas, X como serie, un modelo reutilizado con el mismo nombre, extrapolación, formato :.10, confundir asociación con causalidad.