Python 52.4%
TeX 47.6%
1#!/usr/bin/env python32# -*- coding: utf-8 -*-3"""Fusionne les features GPT (specs.jsonl) aux données maison (maisons_avec_prix.json)."""4import json, csv56# 1) charger specs GPT7specs = {}8for line in open("data/processed/specs.jsonl", encoding="utf-8"):9 r = json.loads(line)10 if r.get("specs"):11 specs[r["uid"]] = r["specs"]12print(f"Specs GPT chargées : {len(specs)}")1314# 2) charger maisons15data = json.load(open("data/processed/maisons_avec_prix.json", encoding="utf-8"))1617# 3) JSON fusionné : record complet + gptSpecs imbriqué18merged = []19n_match = 020for d in data:21 uid = f"{d.get('source')}:{d.get('listingId')}"22 d2 = dict(d)23 d2["uid"] = uid24 s = specs.get(uid)25 d2["gptSpecs"] = s # None si absent26 if s:27 n_match += 128 merged.append(d2)29json.dump(merged, open("data/processed/maisons_specs.json", "w", encoding="utf-8"),30 ensure_ascii=False)31print(f"JSON fusionné -> maisons_specs.json ({len(merged)} records, "32 f"{n_match} avec specs)")3334# 4) CSV aplati pour analyse35BOOL = ["turnkey","recently_renovated","garage","pool","waterfront",36 "finished_basement","fireplace","central_ac","new_construction",37 "income_property","renovated_kitchen","renovated_bathroom",38 "large_lot","quiet_area"]39FLOOR = ["bois_franc","ceramique","plancher_flottant","tapis","vinyle","beton","autre"]40HEAT = ["electrique","plinthes","thermopompe","bois","gaz","mazout","radiant","geothermie","autre"]4142cols = (["uid","source","listingId","price","currency","city","province",43 "ptype","sqft_raw","lot_raw","beds","baths","yearBuilt","parking",44 "has_gpt","g_condition","g_countertop","g_view"]45 + ["g_"+b for b in BOOL]46 + ["fl_"+f for f in FLOOR]47 + ["heat_"+h for h in HEAT]48 + ["g_highlights"])4950with open("data/processed/maisons_specs.csv", "w", newline="", encoding="utf-8") as fh:51 w = csv.writer(fh)52 w.writerow(cols)53 for d in data:54 uid = f"{d.get('source')}:{d.get('listingId')}"55 p = d.get("property") or {}56 ad = d.get("address") or {}57 s = specs.get(uid)58 row = [uid, d.get("source"), d.get("listingId"), d.get("priceNumber"),59 d.get("currency"), ad.get("city"), ad.get("province"),60 (p.get("buildingType") or p.get("type")),61 p.get("squareFeet"), p.get("lotSize"),62 p.get("bedrooms"), p.get("bathrooms"), p.get("yearBuilt"),63 p.get("parkingSpaces"),64 1 if s else 0,65 (s or {}).get("condition",""),66 (s or {}).get("countertop",""),67 (s or {}).get("view","")]68 row += [int(bool((s or {}).get(b))) if s else "" for b in BOOL]69 fl = set((s or {}).get("flooring") or [])70 row += [int(f in fl) if s else "" for f in FLOOR]71 ht = set((s or {}).get("heating") or [])72 row += [int(h in ht) if s else "" for h in HEAT]73 row += ["; ".join((s or {}).get("highlights") or [])]74 w.writerow(row)75print(f"CSV aplati -> maisons_specs.csv ({len(cols)} colonnes)")7677# 5) petit récap de distribution78import collections79cond = collections.Counter(s.get("condition") for s in specs.values())80print("\nRépartition 'condition' :", dict(cond))81for b in ["turnkey","recently_renovated","central_ac","pool","waterfront",82 "renovated_kitchen","income_property"]:83 n = sum(1 for s in specs.values() if s.get(b))84 print(f" {b:20s}: {n:5d} ({n/len(specs)*100:.1f}%)")85