DEV Community

Cover image for Predicción de Precios de Portátiles con ML
Joel Farell Cabrera
Joel Farell Cabrera

Posted on

Predicción de Precios de Portátiles con ML

En mi post anterior, creé un script para generar un CSV con datos de portátiles, haciendo web scraping en PCComponentes.

Esta idea surgió al intentar crear un modelo de Machine Learning que, según los componentes que le proporciones, prediga el precio del dispositivo. Sin embargo, al investigar encontré un DataFrame público que podría servir para entrenar el modelo, pero tenía un problema: los precios databan de 2015, lo que lo hacía poco útil.

Por esta razón, decidí construir un DataFrame directamente desde la página web de PCComponentes, lo que me permitiría contar con datos actualizados y confiables. Además, este proceso podría automatizarse en el futuro (al menos hasta que PCComponentes cambie la estructura de su web).

¡Entremos en materia!


Tratamiento de datos del DataFrame

Antes de entrenar el modelo, es necesario organizar y limpiar los datos para facilitar su lectura y procesamiento. Para esto, usaremos las bibliotecas Numpy, Pandas y Matplotlib, ampliamente utilizadas en análisis y tratamiento de datos.

Lo primero es importar estas bibliotecas y abrir el CSV generado:

import pandas as pd  
import numpy as np  
import matplotlib.pyplot as plt  

Enter fullscreen mode Exit fullscreen mode

Después, eliminamos las filas con valores vacíos o nulos:

df = df.dropna()  
Enter fullscreen mode Exit fullscreen mode

Análisis y filtrado de datos

Comencemos por analizar los diferentes tipos de CPU disponibles. Para visualizarlos, usaremos la biblioteca Seaborn:

import seaborn as sns  
sns.countplot(data=df, x='CPU')
Enter fullscreen mode Exit fullscreen mode

Image

Image description

Aquí observamos que hay 207 tipos de CPU distintos. Entrenar un modelo con todos estos valores podría ser problemático, ya que muchos datos serían irrelevantes y generarían ruido que afectaría el rendimiento.

En lugar de eliminar la columna completa, filtraremos los valores más relevantes:

def cpu_type_define(text):
    text = text.split(' ')
    if text[0] == 'intel':
        if 'i' in text[-1]:
            if text[-1].split('-')[0] == 'i3':
                return 'low gamma intel processor'

            return text[0]+' '+text[1]+' '+text[-1].split('-')[0] 

        return 'low gamma intel processor'
    elif text[0] == 'amd':
        if text[1] == 'ryzen':
            if text[2] == '3':
                return 'low gamma amd processor'

            return text[0]+' '+text[1]+' '+text[2]

        return 'low gamma amd processor'
    elif 'm' in text[0]:
        return 'Mac Processor'
    else:
        return 'Other Processor'

data['Cpu'] = data['Cpu'].apply(cpu_type_define)
sns.histplot(data=data,x='Cpu')
data['Cpu'].value_counts()
Enter fullscreen mode Exit fullscreen mode

Dando como resultado:

Image description


Filtrado de GPU

Realizamos un proceso similar con las tarjetas gráficas (GPU), reduciendo la cantidad de categorías para evitar ruido en los datos:

def gpu_type_define(text):    

    if 'rtx' in text:

        num = int(''.join([char for char in text if char.isdigit()]))

        if num == 4080 or num == 4090 or num == 3080:
            return 'Nvidia High gamma'
        elif num == 4070 or num == 3070 or num == 4060 or num == 2080:
            return 'Nivida medium gamma'
        elif num == 3050 or num == 3060 or num == 4050 or num == 2070:
            return 'Nvidia low gamma'
        else:
            return 'Other nvidia grafic card'

    elif 'radeon' in text:

        if 'rx' in text:
            return 'Amd High gamma'
        else:
            return 'Amd low Gamma'

    elif 'gpu' in text:
        return 'Apple integrated graphics'

    return text



data['Gpu'] = data['Gpu'].apply(gpu_type_define)
sns.histplot(data=data,x='Gpu')
data['Gpu'].value_counts()  
Enter fullscreen mode Exit fullscreen mode

Resultado:

Image description


Tratamiento de almacenamiento y RAM

Para simplificar los datos de almacenamiento, combinamos el espacio total de todos los discos duros en un único valor:

def fitler_ssd(text):
    two_discs = text.split('+')


    if len(two_discs) == 2:
        return int(''.join([char for char in two_discs[0] if char.isdigit()])) + int(''.join([char for char in two_discs[1] if char.isdigit()]))        
    else:
        return int(''.join([char for char in text if char.isdigit()]))

data['SSD'] = data['SSD'].str.replace('tb','000')
data['SSD'] = data['SSD'].str.replace('gb','')
data['SSD'] = data['SSD'].str.replace('emmc','')
data['SSD'] = data['SSD'].str.replace('ssd','')
Enter fullscreen mode Exit fullscreen mode

Finalmente, filtramos los valores de RAM para mantener únicamente números:


def filter_ram(text):
    try:
        return int(text)
    except:
        return None

data['Ram'] = data['Ram'].str.replace('gb','')
data['Inches'] = data['Inches'].astype('float')
data['Ram'] = data['Ram'].apply(filter_ram)
data.info()

Enter fullscreen mode Exit fullscreen mode

Codificación de datos no numéricos

Antes de entrenar el modelo, es necesario transformar las columnas no numéricas en datos que el algoritmo pueda interpretar. Para esto, usamos el ColumnTransformer y OneHotEncoder de la biblioteca sklearn:

from sklearn.compose import ColumnTransformer  
from sklearn.preprocessing import OneHotEncoder  

step1 = ColumnTransformer(transformers=[  
    ('col_tnf', OneHotEncoder(), [0, 2, 4, 5])  
], remainder='passthrough')  
Enter fullscreen mode Exit fullscreen mode

Entrenamiento del modelo

Probé varios algoritmos de Machine Learning para determinar cuál era más eficiente según el coeficiente de determinación (R2 Score). Aquí están los resultados:

Modelo R2 Score
Logistic Regression -4086280.26
Random Forest 0.8025
ExtraTreeRegressor 0.7531
GradientBoostingRegressor 0.8025
XGBRegressor 0.7556

Los mejores resultados fueron obtenidos con Random Forest y GradientBoostingRegressor, ambos con un R2 cercano a 1.

Para mejorar aún más, combiné estos algoritmos usando un Voting Regressor, logrando un R2 Score de 0.8085:

from sklearn.ensemble import VotingRegressor  

voting_model = VotingRegressor([  
    ('rf', random_forest_model),  
    ('gbr', gradient_boosting_model)  
])  
Enter fullscreen mode Exit fullscreen mode

Conclusión

El modelo entrenado con el Voting Regressor fue el más eficiente. Ahora está listo para integrarlo en una aplicación web, lo que explicaré en detalle en el próximo post.

Link al proyecto

Top comments (0)