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
Después, eliminamos las filas con valores vacíos o nulos:
df = df.dropna()
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')
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()
Dando como resultado:
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()
Resultado:
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','')
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()
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')
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)
])
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.
Top comments (0)