Este proyecto no nació en un laboratorio, sino de una necesidad real: ayudar a mi tía a encontrar piso en Zaragoza. El mercado inmobiliario es ruidoso; lleno de precios dispares y anuncios confusos que provocan parálisis por análisis.
Para resolverlo, cambié la intuición por el código. Traté su búsqueda como un problema de Ciencia de Datos, creando un algoritmo capaz de filtrar el ruido y responder a lo importante:
-
Precio Justo 🏷️ ➤ Definir el valor real del m² por barrio.
-
Detección de Chollos 💎 ➤ Identificar matemáticamente activos infravalorados.
-
Timing de Mercado 📅 ➤ Validar el momento de compra.
El resultado fue pasar de la duda emocional a la seguridad de una decisión Data-Driven 📊.
| Categoría | Tecnologías Utilizadas |
|---|---|
| Lenguaje | |
| ETL & Parsing | |
| Data Warehouse | |
| Visualization | |
| Version Control |
El desafío principal fue la obtención de datos de fuentes inmobiliarias protegidas por sistemas anti-bot avanzados (Akamai/Datadome), que bloqueaban las peticiones automatizadas vía Selenium o Requests.
- El Reto: Bloqueo de IP (Error 403) ante peticiones secuenciales automatizadas.
- La Solución (Ingeniería Resiliente): Se diseñó un pipeline desacoplado en dos fases:
- Extracción Controlada: Ingesta manual del código fuente renderizado (DOM Snapshot) a través de herramientas de desarrollo (DevTools) para simular tráfico 100% humano y asegurar la integridad de los datos.
- Parsing Automatizado: Desarrollo de un algoritmo en Python con
BeautifulSouppara procesar los archivos HTML locales, estructurando la información no estructurada (divs, spans, clases ofuscadas) en un dataset tabular limpio.
Transformación de datos crudos en métricas de negocio accionables:
- Limpieza: Normalización de cadenas de texto (Barrios compuestos), conversión de tipos de moneda y eliminación de duplicados.
- Outlier Handling: Aplicación de recorte lógico para eliminar valores extremos que sesgaban la media (mansiones o errores de precio).
- Análisis Variables target: Añálisis distribución de valores, comparación mediana - media de las variables Precio y Precio_m2.
- Precio_m2: KPI estándar para comparativa equitativa.
- Es_Chollo (Algoritmo): Detección automática de oportunidades. Se utilizó la Mediana del barrio (más robusta que la media) para marcar inmuebles un 10% por debajo del valor de mercado.
- Tipo_vivienda: Creación de la dimensión
tipo_viviencasegmentando la muestra según el tipo de inmueble.
Modelado de una base de datos relacional robusta (Star Schema) para alimentar el dashboard:
- Esquema: Tabla de hechos (
fact_inmuebles) vinculada a tablas dimensionales (dim_barrios) con relación muchos a uno.
CREATE TABLE inmuebles(
id_inmueble SERIAL PRIMARY KEY,
titulo TEXT,
precio INT,
habitaciones INT,
metros INT,
barrio_oficial VARCHAR(150),
es_outlier BOOLEAN,
precio_m2 DECIMAL(10,2),
tipo_vivienda VARCHAR(50),
infravalorado BOOLEAN,
CONSTRAINT fk_barrio -- para cargar solo pisos que exista el barrio
FOREIGN KEY (barrio_oficial)
REFERENCES dim_barrios(barrio_oficial)
)CREATE TABLE dim_barrios (
barrio_oficial VARCHAR(150) PRIMARY KEY,
distrito VARCHAR(100),
renta_media DECIMAL(10,2),
seguridad_index DECIMAL(4,1),
latitud DECIMAL(10,6),
longitud DECIMAL(10,6)
);- Consultas Avanzadas: Uso de Vistas SQL para pre-procesar cálculos complejos antes de llegar a Power BI, optimizando el rendimiento. A continuación se presentan las 3 vistas clave creadas para la obtención de información clave de negocio:
- Clasificación de barrios dentro de cada distrito según el precio por m2 ordenados de más caro a más barato.
CREATE VIEW vista_ranking_barrios AS
SELECT
d.distrito,
f.barrio_oficial,
COUNT(f.id_inmueble) as oferta_disponible,
ROUND(AVG(f.precio_m2), 0) as precio_m2_medio,
d.renta_media,
d.seguridad_index,
-- Ranking: 1 = El barrio más caro de SU distrito
RANK() OVER(PARTITION BY d.distrito ORDER BY AVG(f.precio_m2) DESC) as ranking_caro_distrito
FROM fact_inmuebles f
JOIN dim_barrios d ON f.barrio_oficial = d.barrio_oficial
GROUP BY d.distrito, f.barrio_oficial, d.renta_media, d.seguridad_index
ORDER BY d.distrito, ranking_caro_distrito;- Comparación del precio medio de inmuebles marcados como 'Infravalorados' frente al precio medio real del barrio al que pertenecen.
CREATE VIEW comparacion_real_chollo as
SELECT
f.barrio_oficial,
-- Precio medio del mercado sin contar infravalorados
ROUND(AVG(CASE WHEN f.infravalorado = FALSE THEN f.precio END), 0) as precio_mercado,
-- Precio medio de los "infravalorados"
ROUND(AVG(CASE WHEN f.infravalorado = TRUE THEN f.precio END), 0) as precio_oportunidad,
-- Margen Potencial (€)
ROUND(
AVG(CASE WHEN f.infravalorado = FALSE THEN f.precio END) -
AVG(CASE WHEN f.infravalorado = TRUE THEN f.precio END)
, 0) as margen_bruto_medio,
-- Número de infravalorados
SUM(CASE WHEN f.infravalorado = TRUE THEN 1 ELSE 0 END) as n_chollos
FROM inmuebles f
GROUP BY f.barrio_oficial
HAVING SUM(CASE WHEN f.infravalorado = TRUE THEN 1 ELSE 0 END) > 0 -- Solo barrios con oportunidades
ORDER BY margen_bruto_medio DESC;- Cálculo de cantidad de sueldo íntegro necesario para que un vecino de un barrio se compre un piso ahí. Además, se añade columna de clasificación entre categorías según la cantidad:
- Gentrificación -> Más de 15 años.
- Oportunidad local -> Menos de 8 años.
- Resto -> Equilibrado.
WITH metricas_barrio AS (
SELECT
f.barrio_oficial,
AVG(f.precio) as precio_medio_zona,
d.renta_media
FROM fact_inmuebles f
JOIN dim_barrios d ON f.barrio_oficial = d.barrio_oficial
GROUP BY f.barrio_oficial, d.renta_media
)
SELECT
barrio_oficial,
ROUND(precio_medio_zona, 0) as precio_medio,
renta_media,
-- Cálculo de años de esfuerzo
ROUND(precio_medio_zona / NULLIF(renta_media, 0), 1) as anos_esfuerzo_fiscal,
CASE
WHEN (precio_medio_zona / renta_media) > 15 THEN 'Gentrificación'
WHEN (precio_medio_zona / renta_media) < 8 THEN 'Oportunidad Local'
ELSE 'Equilibrado'
END as estado_mercado
FROM metricas_barrio
ORDER BY anos_esfuerzo_fiscal DESC;El resultado final es un informe estratégico de 4 páginas diseñado con enfoque UX, que guía al inversor desde la visión macro hasta el detalle del activo.
Objetivo: Radiografía inmediata del estado del mercado.
- Mapa de Calor Geoespacial: Visualización de la densidad de precios sobre el mapa de Zaragoza, permitiendo identificar las "zonas calientes" de un vistazo.
- KPIs Agregados: Tarjetas dinámicas mostrando el volumen de oferta, precio medio del m² y niveles de renta media.
- Ranking de Barrios: Gráfico de barras que clasifica las zonas de mayor a menor exclusividad.
Objetivo: Entender las causas detrás del precio.
- Matriz Seguridad-Precio: Scatter Plot que cruza el coste del suelo con el índice de seguridad. Permite identificar "Joyas Ocultas" (Barrios seguros pero aún baratos).
- Análisis de Correlación: Gráfico de dispersión (Renta vs. Precio) con línea de tendencia para detectar zonas gentrificadas o infravaloradas respecto al poder adquisitivo de sus habitantes.
- Segmentación: Influencia de la cantidad de habitaciones en el precio de mercado.
Objetivo: Herramienta táctica para la toma de decisión final.
- Tabla de "Chollos": Listado de activos filtrado automáticamente por el algoritmo
Infravalorado = TRUE. Incluye formato condicional para resaltar precios atractivos. - Medidor de Esfuerzo (Gauge Chart): KPI visual que calcula los "Años de Renta" necesarios para adquirir la vivienda, con zonas de objetivo (Verde) y riesgo (Rojo) definidas dinámicamente.
- Filtros Avanzados: Panel lateral para segmentar por presupuesto máximo, habitaciones y distrito.
Se implementó una experiencia de usuario profunda mediante Drill-through (Obtención de detalles).
- Funcionamiento: El usuario puede hacer clic en cualquier barrio de los gráficos y luego pulsar el botón de "Ver Detalles de Barrio".
- Resultado: El informe navega a una página oculta de detalle, filtrando automáticamente todos los visuales para mostrar exclusivamente la micro-economía de ese barrio (dispersión de precios específica, stock disponible y métricas locales).
Acceso al informe dinámico en Power BI -> informe
Este desarrollo ha cubierto el ciclo de vida completo del dato (ETL → DWH → BI), destacando por:
✅ Ingeniería Resiliente (Web Scraping) Pivotamos de Selenium a una Ingesta Híbrida (DOM Parsing), logrando sortear sistemas de seguridad avanzados (Datadome) y garantizando la integridad de los datos.
✅ Data Quality & Enrichment Transformamos datos "sucios" en un Dataset Premium al cruzar precios con Renta y Seguridad, permitiendo segmentaciones avanzadas imposibles en portales web tradicionales.
✅ Arquitectura Profesional El uso de PostgreSQL + Star Schema garantiza que el sistema sea escalable y mantenible, soportando futuras cargas de datos masivas.
✅ Impacto Real en Negocio El Dashboard final reduce el "Time-to-Decision" de semanas a minutos. La página "Investment Hunter" ofrece a la inversora una lista de oportunidades matemáticas libres de sesgos emocionales.
José Ignacio Rubio
Data Analyst | Business Intelligence Specialist




