El Poder de lo Simple: Regresión Lineal para Predecir Precios de Casas

edgarcajusol

Edgar Cajusol

Posted on November 24, 2024

El Poder de lo Simple: Regresión Lineal para Predecir Precios de Casas

El sector inmobiliario es uno de los más diversos y complejos del mundo. Entender cómo se determinan los precios de las propiedades puede ser un desafío, ya que depende de múltiples factores como el tamaño de la casa, el número de habitaciones, la ubicación, el tamaño del garaje (si tiene uno), entre otros. ¿Pero serán realmente todos estos factores determinantes?

En este artículo, exploraremos un modelo simple pero poderoso, la Regresión Lineal, para ayudarnos no solo a predecir los precios de las casas, sino también a identificar si algunas de las variables mencionadas realmente son importantes o influyentes en el modelo.

A lo largo del artículo, aprenderás:

  • Como preparar los datos inmobiliarios para el análisis.
  • Los fundamentos de la regresión lineal, incluyendo los supuestos que deben cumplirse (normalidad, homocedasticidad, entre otros) para obtener resultados confiables.
  • La implementación práctica del modelo en Python.
  • La interpretación de resultados, las métricas clave y los próximos pasos a considerar.

imagen-inmueble

Para entrenar a nuestro modelo usaremos un dataset obtenido en kaggle, en el cual buscaremos predecir el precio de las casas en Bangladesh.

link del dataset: https://www.kaggle.com/datasets/durjoychandrapaul/house-price-bangladesh

En este dataset podemos encontrar columnas como:

  • Title: El titulo de la oferta de venta de la propiedad.
  • Bedrooms: Representa la cantidad de habitaciones de la propiedad.
  • Bathrooms: Cantidad de baños de la propiedad.
  • Floor_no: El número de piso en el que se encuentra la propiedad.
  • Occupancy_status: Indica si la propiedad está desocupada u ocupada.
  • Floor_area: La superficie total construida de la propiedad en pies cuadrados.
  • City: La ciudad donde se encuentra la propiedad.
  • Price_in_taka: Precio de la propiedad en Taka bangladesí.
  • Location: La ubicación o dirección específica dentro de la ciudad.

1. Importación de las librerías y carga de datos.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.api as sm
Enter fullscreen mode Exit fullscreen mode
df = pd.read_csv('house_price_bd.csv')
df.head(2)
Enter fullscreen mode Exit fullscreen mode
Title Bedrooms Bathrooms Floor_no Occupancy_status Floor_area City Price_in_taka Location
We Are Offering You A Very Spacious 1960 Sq Ft Flat For Sale In Gulshan 1 3.0 4.0 3 vacant 1960.0 dhaka ৳39,000,000 Gulshan 1, Gulshan
Valuable 1705 Square Feet Apartment Is Ready To Sale In Kalabagan 3.0 3.0 1 vacant 1705.0 dhaka ৳16,900,000 Lake Circus Road, Kalabagan

1.2. Limpieza de datos

Que tal si luego observamos el tipo de datos que tenemos para corroborar que todo esté en orden.

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3865 entries, 0 to 3864
Data columns (total 9 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   Title             3865 non-null   object 
 1   Bedrooms          2864 non-null   float64
 2   Bathrooms         2864 non-null   float64
 3   Floor_no          3181 non-null   object 
 4   Occupancy_status  3766 non-null   object 
 5   Floor_area        3766 non-null   float64
 6   City              3865 non-null   object 
 7   Price_in_taka     3865 non-null   object 
 8   Location          3859 non-null   object 
dtypes: float64(3), object(6)
memory usage: 271.9+ KB
Enter fullscreen mode Exit fullscreen mode

Lo que podemos notar:

  • Bedrooms y Bathrooms son tipo float (decimal). Aunque esto no debería afectar el rendimiento del modelo, es incorrecto, ya que no tiene sentido tener valores decimales en estas columnas, como "2.4 baños", ¿verdad? Lo correcto sería convertir ambas columnas a tipo int (entero).

  • Floor_no y Price_in_taka son de tipo object, lo cual es un error. El número de piso debería ser un valor entero, por lo que es necesario convertir esta columna a tipo int. En cuanto a la columna Price_in_taka, se ha considerado un object debido a la presencia de caracteres especiales, como el símbolo "৳". Para corregir esto, debemos eliminar dicho símbolo y luego cambiar el tipo de ambas columnas a su formato adecuado.

Ojo aquí:

df['Bedrooms'] = df['Bedrooms'].astype(int)
df['Bathrooms'] = df['Bathrooms'].astype(int)
df['Floor_no'] = df['Floor_no'].astype(int)

ValueError: invalid literal for int() with base 10: '8th'
Enter fullscreen mode Exit fullscreen mode

Al intentar convertir las columnas Bedrooms, Bathrooms y Floor_no a tipo int, nos encontramos con un error. Este ValueError ocurre porque algunas filas contienen valores no numéricos, como '8th' en lugar de un valor numérico válido. Este error se presenta cuando intentamos convertir valores no numéricos o NaN a tipo entero (int), ya que el tipo int no puede manejar estos valores faltantes o textos. Por otro lado, float sí permite NaN como valor y es por eso que las columnas tenían este tipo de datos inicialmente.

De hecho, encontramos indicadores de valores faltantes antes de llegar a este punto, lo que nos ayudó a identificar el origen del error.

#Limpiar el simbolo en el Precio
df['Price_in_taka'] = df['Price_in_taka'].str.replace(r'','')
df['Price_in_taka'] = df['Price_in_taka'].str.replace(r',','')
df['Price_in_taka'] = df['Price_in_taka'].astype(float)
Enter fullscreen mode Exit fullscreen mode

¿Porque convetí la columna Price_in_taka a float y no int?

Decidí convertir la columna de precio a float en lugar de int por varias razones. Inicialmente, la columna Price_in_taka estaba de tipo object, lo que indicaba que contenía valores no numéricos o con caracteres no válidos, como el símbolo "৳". Aunque el precio podría ser un valor entero, no podemos estar seguros de que todos los valores estén libres de decimales.

Si hubiésemos elegido convertir esta columna a int directamente, podríamos haber perdido la información de decimales en caso de que existieran, lo que podría sesgar nuestros datos. Además, el tipo float permite manejar valores decimales y, al mismo tiempo, preserva la posibilidad de representar números enteros. Así evitamos cualquier posible distorsión en el análisis.

El siguiente paso es opcional, lo que haré será convertir la moneda a dólares estadounidenses para una compresión mejor del columna precio.

#Transformar de Taka Bandaglesí a dolares
df['Price_in_taka'] = df['Price_in_taka']*0.0084
df.rename(columns={'Price_in_taka':'Price_in_dolars'},inplace=True)
Enter fullscreen mode Exit fullscreen mode

Ahora sí, revisemos la cantidad de nulos que sabemos que existen.

df.isna().sum()

Title                  0
Bedrooms            1001
Bathrooms           1001
Floor_no             684
Occupancy_status      99
Floor_area            99
City                   0
Price_in_dolars        0
Location               6
dtype: int64
Enter fullscreen mode Exit fullscreen mode
#Cantidad total de datos
len(df)
3865
Enter fullscreen mode Exit fullscreen mode

La mayor cantidad de filas nulas que tenemos son alrededor de 1001 filas de un total de 3865 filas en el conjunto de datos. El porcentaje que representan se calcula de la siguiente manera:

calculo_porcentaje

Este porcentaje representa aproximadamente el 26% del total de los datos, lo que supera el umbral recomendado de un 5% para la imputación de datos. Con esto en mente, tenemos dos opciones:

  1. Imputar los datos (aunque esto podría incluir valores erróneos).
  2. Eliminar las filas con valores nulos, ya que contamos con una cantidad suficiente de datos.

Riesgos de imputar datos:

Imputar datos puede introducir sesgos si los valores imputados no representan adecuadamente la distribución original, distorsionando las relaciones entre las variables. Además, si los datos faltantes siguen un patrón específico, la imputación podría reforzar ese sesgo y afectar la precisión del modelo. Aunque mantener más datos puede ser útil, la imputación también puede aumentar la varianza del modelo si los valores imputados son muy diferentes de los reales.

#Observando los datos nulos
df[df['Bedrooms'].isna()].head(3)
Enter fullscreen mode Exit fullscreen mode
Title Bedrooms Bathrooms Floor_no Occupancy_status Floor_area City Price_in_taka Location
22 Large 5000 Sq Ft Commercial Space For Sale In Free School Street Road, Kathalbagan NaN NaN 9 vacant 5000.0 dhaka 110000000.0 Free School Street, Kathalbagan
40 A Commercial Building Is Up For Sale Which Is Located In Mirpur Nearby Mirpur Adhunik Hospital NaN NaN 6 vacant 16000.0 dhaka 120000000.0 Section 12, Mirpur
45 2.75 Katha Residential Plot For Sale In Mirpur-11 NaN NaN NaN vacant 1980.0 dhaka 18000000.0 Section 11, Mirpur

Al observar algunos datos nulos, podemos notar que puede haber coincidencias entre las columnas, es decir, que tanto Bedrooms como Bathrooms estén vacíos al mismo tiempo. Esto podría indicar que la información de estas propiedades no fue registrada correctamente, o bien que se decidió no incluirla desde el principio, tal vez porque no era relevante para el análisis inicial. En cualquier caso, es importante identificar patrones en los datos faltantes para tomar decisiones informadas sobre cómo manejarlos.

Tomaré la opción de eliminar los datos nulos, debido a que imputar podría introducir error en mi conjunto de datos.

df_null = df.copy()
df.dropna(subset=['Bedrooms','Bathrooms','Floor_no'],inplace=True)
df.reset_index(drop=True,inplace=True)

print("La longitud del dataframe con datos nulos es: ",len(df_null))
print("La longitud eliminando datos nulos es: ",len(df))
print("Se han eliminado :", len(df_null) - len(df), "filas")

La longitud del dataframe con datos nulos es:  3865
La longitud eliminando datos nulos es:  2832
Se han eliminado : 1033 filas
Enter fullscreen mode Exit fullscreen mode

Despues de eliminar nos hemos quedado con 2832 filas para entrenar a nuestro modelo.

Volvemos a ver los datos nulos.

df.isna().sum()

Title               0
Bedrooms            0
Bathrooms           0
Floor_no            0
Occupancy_status    0
Floor_area          0
City                0
Price_in_dolars     0
Location            1
dtype: int64
Enter fullscreen mode Exit fullscreen mode

Vemos que aún tenemos un dato nulo en la columna Location, pero dado que no utilizaré esta variable en el análisis, no es necesario eliminar la fila.

Para mayor comodidad visual, decidí convertir la columna de precio a miles de unidades dividiéndola entre 1000. Esto facilita la interpretación de los valores y mejora la legibilidad de los datos.

df['Price_in_dolars'] = df['Price_in_dolars']/1000
df.rename(columns={'Price_in_dolars':'Price_$_k'},inplace=True)
Enter fullscreen mode Exit fullscreen mode

No olvidemos convetir el tipo de variable de las columnas Bedrooms, Bathrooms y Floor_no.

Como Floor_no posee caracteres especiales deberíamos eliminarlos primero y extraer solamente el carácter numérico.

df['Floor_no'] = df['Floor_no'].astype(str).str.extract('(\d+)')
df['Floor_no'] = pd.to_numeric(df['Floor_no'], errors='coerce')
Enter fullscreen mode Exit fullscreen mode

La expresión regular (\d+) extrae solo los dígitos (números) de cada valor. Si el valor contiene texto o símbolos no numéricos, solo se extraerán los números. Luego coerce convierte los valores extraídos a tipo numérico , si no logra hacerlo le colocará NaN.

df['Bedrooms'] = df['Bedrooms'].astype(int)
df['Bathrooms'] = df['Bathrooms'].astype(int)
Enter fullscreen mode Exit fullscreen mode

Finalmente convertiremos a tipo int las otras dos columnas y tendríamos todo correctamente hecho.

2. Análisis Exploratorio de Datos (EDA)

En muchos problemas de predicción, un modelo lineal puede ser una herramienta poderosa. El objetivo de este artículo es aplicar la regresión lineal simple para predecir el precio de propiedades basándonos en el área del piso y el número de dormitorios. Utilizaremos un diagrama de dispersión (scatterplot) para visualizar si nuestros datos siguen una tendencia lineal, que es uno de los supuestos fundamentales para aplicar regresión lineal.

2.1. Normalidad

#GRAFICO POR DORMITORIOS
sns.scatterplot(x='Bedrooms',y='Price_$_k',data=df)
plt.title('Dormitorios vs Precios en ($)', fontsize=16)
plt.xlabel('Dormitorios')
plt.ylabel('Precio ($) en miles')
plt.show()
Enter fullscreen mode Exit fullscreen mode

scatter1

En el gráfico anterior, observamos que los datos no presentan un comportamiento lineal evidente entre el número de dormitorios y el precio. Esto sugiere que no se cumple el supuesto de linealidad, uno de los principios clave para la regresión lineal. Este patrón indica que otras variables podrían estar influyendo más significativamente en los precios.

2.2. Homocedasticidad

#Grafico por Área
sns.scatterplot(x='Floor_area',y='Price_$_k',data=df)
plt.title('Area del piso vs Precios en ($) en miles', fontsize=16)
plt.xlabel('Area del piso')
plt.ylabel('Precio ($) en miles')
plt.show()
#Regla de Homecedasticidad violada, conjunto Heterocedastico
Enter fullscreen mode Exit fullscreen mode

scatter2

En el gráfico anterior, notamos que los datos están más agrupados en valores bajos de Área del piso, pero se dispersan considerablemente conforme aumentan los valores del área. Esto indica que la homocedasticidad (varianza constante) no se cumple, y en su lugar, observamos un patrón de heterocedasticidad, lo que significa que la varianza de los precios no es constante a lo largo de los valores del área del piso.

Esto se puede corroborar con análisis estadísticos, como pruebas de linealidad y heterocedasticidad, pero en este artículo nos enfocaremos en un enfoque práctico apoyándonos en gráficos para identificar posibles violaciones a estos supuestos

Entonces ¿Cómo podemos seguir adelante?

Una manera efectiva de abordar estos problemas es transformar las variables mediante su logaritmo. Esta técnica es particularmente útil por las siguientes razones:

  1. Linealización de relaciones no lineales: Muchas relaciones no lineales entre variables pueden convertirse en lineales al aplicar una transformación logarítmica, facilitando el ajuste del modelo.
  2. Reducción de heterocedasticidad: Las transformaciones logarítmicas tienden a estabilizar la varianza, ayudando a evitar problemas de heterocedasticidad.
  3. Escala más manejable: Si los valores tienen rangos amplios, el logaritmo los compacta, haciendo que sean más fáciles de interpretar y modelar.

En los próximos pasos, aplicaremos esta transformación a nuestras variables clave y evaluaremos cómo mejora la calidad de nuestros datos para la regresión lineal.

# Usar variables logaritmicas
df['log_area'] = np.log(df['Floor_area'])
df['log_precio']  = np.log(df['Price_$_k'])

sns.scatterplot(x='log_area',y='log_precio',data=df)
plt.title('Area del piso vs Precios en ($) en miles', fontsize=16)
plt.xlabel('Area del piso')
plt.ylabel('Precio ($) en miles')
plt.show()
Enter fullscreen mode Exit fullscreen mode

linealizado

Excelente! Ahora nuestras variables presentan no solo un comportamiento lineal si no que también hemos eliminado la heterocedasticidad.

Como siguiente paso realizaremos la configuración y aplicación de nuestro modelo de regresión lineal.

3. Aplicar el modelo de regresión

¿Por qué utilizaremos OLS de Statsmodels?

El modelo de Regresión Lineal mediante Ordinary Least Squares (OLS) es uno de los métodos más populares para estimar los coeficientes de un modelo lineal, ya que no solo ajusta el modelo, sino que también proporciona un análisis detallado de los resultados, lo que es muy importante para interpretar y evaluar nuestro modelo.

En este contexto, el uso de OLS no solo nos permitirá ajustar un modelo, sino también profundizar en la comprensión de los factores que afectan los precios de las propiedades.

¿Por qué no usamos solo scikit-learn? Aunque scikit-learn también permite implementar la regresión lineal, su enfoque está más orientado a tareas de predicción que a la interpretación detallada del modelo. Por eso, Statsmodels es mi elección cuando el análisis interpretativo y estadístico es una prioridad.

#Ejecutar el model
y = df['log_precio'] #variable dependiente
x1 = df['log_area'] #variable independiente
x = sm.add_constant(x1) #Añadimos la constante
results = sm.OLS(y,x).fit() #entrenamos al modelo
results.summary() #Observamos el cuadro informativo
Enter fullscreen mode Exit fullscreen mode

Después de ajustar nuestro modelo de regresión lineal, obtuvimos el siguiente cuadro resumen:

ols_results

  1. R-squared (Coeficiente de determinación): El valor de R2 es 0.706 lo que indica que el 70.6% de la variabilidad en los precios de las casas (en escala logarítmica) puede ser explicada por el área del piso (también en escala logarítmica). Es un resultado bueno aunque podría mejorarse al incluir otras variables relevantes.

  2. Coeficiente de la variable independiente (log(Area)): El coeficiente es 1.4417 lo que indica que en promedio, un aumento del 1% en el área del piso está asociado con un incremento del 1.4417% en el precio de la casa. Esta es una relación fuerte y positiva.

  3. Significancia estadística (Valores p): Tanto la constante como
    log(Area) tienen valores p significativamente menores a 0.05, lo que confirma que son estadísticamente significativos en el modelo.

Veamos como se gráfico la línea de regresión en nuestro conjunto de datos.

sns.scatterplot(x=x1,y=y)
yhat = -6.1998 + 1.4417*x1
plt.plot(x1,yhat,c='red',label='regression line')
plt.xlabel('Area del piso')
plt.ylabel('Precio miles ($)')
plt.title('Area del piso vs Precios en ($) en miles', fontsize=16, color='green')
plt.legend()
plt.show()
Enter fullscreen mode Exit fullscreen mode

scatterconreg

Espera! no nos quedaremos allí, ¿Qué ocurriría si añadimos otra variable más? Como por ejemplo: Bedrooms, eso es lo que veremos en el siguiente punto.

4. Regresión Lineal Múltiple

Aquí añadiremos la columnas Bedrooms para ver si nuestro modelo mejora o no, ya estaríamos hablando de un problema de regresión lineal múltiple.

x1 = df[['Bedrooms','log_area']]
y = df['log_precio']
x = sm.add_constant(x1)
results = sm.OLS(y,x).fit()
results.summary()
Enter fullscreen mode Exit fullscreen mode

olsmultiple

Análisis de los resultados del modelo y comparación de cambios:

  1. R-squared Ajustado (Coeficiente de determinación ajustado): En un problema de regresión lineal múltiple es más adecuado utilizar el R2-ajustado en lugar del R2 estándr, ya que este penaliza la inclusión de variables adicionales que no aporten significativamente al modelo.

Al comparar el R2-ajustado de 72.4% frente a 70.6% de la regresión simple (solo con log(Area)), podemos observar que la inclusión de Bedrooms ha mejorado la capacidad del modelo para explicar la variabilidad en los precios.

  1. Significancia estadística (Valores p): El valor p asociado a la nueva variable Bedrooms es menor a 0.05, lo que indica que es estadísticamente significativa para el modelo. Esto confirma que Bedrooms aporta información relevante para explicar la variabilidad en los precios, contribuyendo a una mejor predicción.

  2. Coeficiente de la variable independiente Bedrooms: El coeficiente negativo de Bedrooms indica que, al aumentar en 1 el número de dormitorios, el log(Precio) disminuye en promedio, manteniendo constante el área. Esto puede parecer contraintuitivo, pero podría reflejar que, en propiedades con áreas similares, agregar dormitorios reduce el espacio disponible para otras características de alto valor (como una sala más grande o acabados de lujo).

Este resultado enfatiza que Bedrooms por sí solo no es siempre un predictor lineal positivo de precio, especialmente cuando otras variables como el área ya están incluidas en el modelo.

Conclusión:
La adición de Bedrooms no solo ha mejorado el ajuste del modelo (mayor R2-ajustado), sino que también ha demostrado ser una variable relevante desde el punto de vista estadístico. Esto refuerza la importancia de explorar y seleccionar cuidadosamente las variables predictoras en los modelos de regresión, por último es importante interpretar estos resultados no de forma aislada, sino considerando las interacciones y la posible multicolinealidad entre las variables.

5. Añadir más variables

Para mejorar el modelo de regresión y ver cómo se comporta con la regresión lineal múltiple, añadiremos una nueva variable: Ciudad. Intuitivamente, podemos suponer que la ubicación de la propiedad, representada por la ciudad, puede tener un impacto significativo en su precio. Por lo tanto, vamos a incluirla como una variable adicional en nuestro análisis.

df['City'] = df['City'].map({'dhaka':0,'chattogram':1,'cumilla':2,'narayanganj-city':3,'gazipur':4})
Enter fullscreen mode Exit fullscreen mode

En el código anterior, lo que hice fue mapear la columna "City", que contiene los nombres de las ciudades, asignando a cada una de ellas un valor numérico único. Por ejemplo, Dhaka se convierte en 0, Chattogram en 1, y así sucesivamente. Este mapeo nos permite incluir la variable "Ciudad" como una variable numérica en nuestro modelo de regresión lineal.

Es importante destacar que este es un enfoque sencillo para convertir variables categóricas en numéricas. Sin embargo, si las ciudades tienen más de dos categorías, una alternativa más robusta es usar técnicas como el OneHotEncoder de la librería scikit-learn, que crea una columna binaria por cada categoría, permitiendo modelar de manera más eficiente y evitando problemas de interpretación cuando hay más de dos categorías.

x1 = df[['City','log_area','Bedrooms']]
y = df['log_precio']
x = sm.add_constant(x1)
results = sm.OLS(y,x).fit()
results.summary()
Enter fullscreen mode Exit fullscreen mode

ols_3

¡Muchísimo mejor, ¿Verdad?! Al incluir la variable 'City', logramos mejorar significativamente el rendimiento de nuestro modelo, alcanzando un R² de 78.7%. Esto sugiere que la ciudad en la que se encuentra la propiedad tiene una influencia considerable en su precio. Los p-valores menores a 0.05 refuerzan la idea de que las variables utilizadas son estadísticamente significativas, lo que significa que podemos confiar en los resultados del modelo. Este análisis nos permite hacer predicciones más precisas y establecer una base sólida para futuras investigaciones en el sector inmobiliario.

6. Predecir y evaluar correctamente los resultados.

Para finalizar y dar un análisis más completo, no solo nos quedaremos con el ajuste del modelo, sino que también realizaremos predicciones utilizando nuevos valores y evaluaremos los resultados.

# Ejemplo de nuevos datos
new_data = pd.DataFrame({
    'City': [0, 1],
    'log_area': [7.5, 6.8],
    'Bedrooms': [3, 4]
})

# Agregar la constante para los nuevos datos
new_data_with_const = sm.add_constant(new_data)

# Hacer predicciones
predicted_values = results.predict(new_data_with_const)

# Mostrar las predicciones
print(predicted_values)
Enter fullscreen mode Exit fullscreen mode

Una vez realizadas las predicciones, obtuvimos los precios predichos (en logaritmo de precios) para los dos datos nuevos introducidos, los cuales nunca antes habían sido vistos por el modelo:

0    4.759433
1    3.427864
dtype: float64
Enter fullscreen mode Exit fullscreen mode

Ahora, vamos a evaluar la precisión de nuestro modelo y entender cómo se comportan estas predicciones en comparación con los datos reales.

¿Como evaluamos los errores?

Para evaluar la precisión de nuestras predicciones, utilizaremos el Mean Squared Error (MSE) o Error Cuadrático Medio, que mide el promedio de los cuadrados de las diferencias entre los valores predichos y los valores observados. Este valor nos permite cuantificar qué tan bien está funcionando nuestro modelo. Para calcularlo, utilizaremos la librería de scikit-learn y su módulo mean_squared_error.

from sklearn.metrics import mean_squared_error
y_pred = results.predict(x)

# Calcular MSE
mse = mean_squared_error(y, y_pred)

print(f"El MSE del modelo es: {mse}")
Enter fullscreen mode Exit fullscreen mode

Una vez aplicada la librería obtenemos el siguiente valor de MSE.

El MSE del modelo es: 0.0738931778107037
Enter fullscreen mode Exit fullscreen mode

Es importante tener en cuenta que el MSE está en unidades elevadas al cuadrado, lo que puede hacer que su interpretación sea más difícil. Para facilitar la lectura y comprensión del error, utilizaremos el RMSE (Root Mean Squared Error), que es simplemente la raíz cuadrada del MSE. El RMSE tiene las mismas unidades que la variable de interés, lo que lo hace más fácil de interpretar.

rmse = np.sqrt(mse)
print(f"El RMSE del modelo es: {rmse}")
Enter fullscreen mode Exit fullscreen mode
El RMSE del modelo es: 0.2718329961772553
Enter fullscreen mode Exit fullscreen mode

Para interpretar el RMSE en las unidades originales de los precios (no logarítmicas), es necesario exponenciar el RMSE, ya que el modelo trabaja sobre valores logarítmicos. Esto nos dará una medida en la misma escala de los precios originales.

rmse_original_scale = np.exp(0.2718) - 1
print(f"El RMSE Original es: {rmse_original_scale}")
Enter fullscreen mode Exit fullscreen mode
El RMSE Original es: 0.31232451016083607
Enter fullscreen mode Exit fullscreen mode

El RMSE de 0.3123 miles de dólares muestra que nuestro modelo tiene una capacidad bastante decente para predecir los precios de las propiedades, con un error promedio de alrededor de 312 dólares. Dependiendo del destino de uso del modelo como para predicciones comerciales, este error puede ser aceptable, pero siempre es bueno compararlo con el rango de precios para tener una evaluación más precisa.

7. Conclusiones y pasos finales

En este artículo, hemos utilizado la regresión lineal, específicamente el modelo OLS (Ordinary Least Squares), para predecir los precios de propiedades basado en variables como el área del piso, la cantidad de dormitorios y la ciudad. A través de este análisis, hemos logrado lo siguiente:

  • Validación de supuestos: Aunque la linealidad no fue perfecta y encontramos evidencias de heterocedasticidad en nuestros datos (Omnibus y Jarque-Bera), aplicamos una transformación logarítmica que ayudó a mejorar la linealidad y la varianza constante, acercando el modelo a las condiciones ideales de la regresión lineal.

  • Mejora con variables adicionales: Hemos ido viendo como la incorporación de variables ha ido mejorando al modelo, la variable City, que representa las diferentes ciudades, logramos mejorar significativamente el modelo, alcanzando un R2 ajustado del 78.7%, lo que indica que el modelo puede explicar un alto porcentaje de la variabilidad en los precios, cabe recalcar que esto no siempre es así, por eso es bueno revisar siempre el R2-ajustado y el p-valor de cada variable.

  • Evaluación del rendimiento: Con el cálculo del RMSE (Root Mean Squared Error) en escala original, encontramos que el modelo tiene un error promedio de 312.3 dólares por predicción. Este valor, aunque dependiente del rango de precios, muestra un error razonablemente bajo, lo que sugiere que el modelo es adecuado para las predicciones de precios de propiedades en este conjunto de datos.

En resumen, hemos construido un modelo de regresión lineal efectivo para predecir precios de propiedades, aunque siempre existen áreas para mejorar, como la posible inclusión de variables adicionales o el ajuste más fino del modelo.

Pasos Finales a Seguir

  • Ampliar los datos: Incluir más datos y variables podría mejorar la precisión del modelo.
  • Probar otros modelos: Modelos como Random Forest o XGBoost pueden captar mejor las interacciones y mejorar las predicciones.
  • Evaluar con nuevos datos: Validar el modelo con un conjunto de prueba o usar validación cruzada para asegurar que no haya sobreajuste.
  • Incluir más variables: Añadir factores como el tipo de propiedad o la antigüedad podría mejorar la precisión.
  • Predicción en tiempo real: Si el modelo se va a implementar en producción se debe de considerar actualizarlo periódicamente con nuevos datos, recomiendo ir leyendo sobre MLOps.

¿Quién dijo que las matemáticas no resuelven problemas de la vida real? La regresión lineal lo hace con más estilo que un chef con su receta secreta.

Edgar Cajusol - Data Scientist - Creando impacto un modelo a la vez.
https://www.linkedin.com/in/edgarcajusol/

💖 💪 🙅 🚩
edgarcajusol
Edgar Cajusol

Posted on November 24, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related