In [1]:
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import geopandas as gpd
import folium
from folium.plugins import HeatMap
import matplotlib.colors as mcolors
from sklearn.preprocessing import MinMaxScaler
import plotly.express as px
import plotly.graph_objects as go

# Impostazione stile per grafici
plt.style.use('seaborn-v0_8-whitegrid')
sns.set(font_scale=1.2)
sns.set_style("whitegrid")

# Evita warnings di pandas
import warnings
warnings.filterwarnings('ignore')
In [2]:
ilab_path = os.path.join(os.path.expanduser('~'), 'ILAB_DATA')
istat_path = os.path.join(ilab_path, 'ISTAT', 'DATA')
sanita_path = os.path.join(ilab_path, 'SANITA')
work_path = os.path.join(ilab_path, 'PATRIMONIO', 'DATA')
out_path = os.path.join(ilab_path, 'PATRIMONIO', 'OUT')
In [3]:
path_grid = os.path.join(out_path,'grid_08_adv.csv')
path_metadati = os.path.join(out_path,'metadati_dataset_08_cat.csv')

Importiamo i dati¶

In [4]:
grid_data = pd.read_csv(path_grid)
metadati = pd.read_csv(path_metadati)

1. Indicatori Accessibilità Economica¶

Indice di affordability: percentuale del reddito necessaria per pagare l'affitto¶

Formula: ((prezzo medio al mq) * 80mq * 12 mesi) / reddito annuo * 100

In [5]:
grid_data['indice_affordability'] = (
    ((grid_data['OMI||Loc_min'] + grid_data['OMI||Loc_max']) / 2 * 80 * 12) / 
    grid_data['reddito_pro_capite||redd_pc'] * 100
)
# Limita i valori estremi per evitare infiniti o valori troppo grandi
grid_data['indice_affordability'] = grid_data['indice_affordability'].replace([np.inf, -np.inf], np.nan)
grid_data['indice_affordability'] = grid_data['indice_affordability'].clip(upper=1000)

Range degli affitti: rapporto tra massimo e minimo prezzo al mq¶

In [6]:
grid_data['range_affitto'] = (
    grid_data['OMI||Loc_max'] / grid_data['OMI||Loc_min']
)
grid_data['range_affitto'] = grid_data['range_affitto'].replace([np.inf, -np.inf], np.nan)
grid_data['range_affitto'] = grid_data['range_affitto'].clip(upper=10)

Range di accessibilità: ampiezza relativa tra min e max (in percentuale)¶

In [7]:
grid_data['range_accessibilita'] = (
    (grid_data['OMI||Loc_max'] - grid_data['OMI||Loc_min']) /
    ((grid_data['OMI||Loc_max'] + grid_data['OMI||Loc_min']) / 2) * 100
)
grid_data['range_accessibilita'] = grid_data['range_accessibilita'].replace([np.inf, -np.inf], np.nan)
grid_data['range_accessibilita'] = grid_data['range_accessibilita'].clip(upper=200)

2. Indicatori Struttura Abitativa¶

Tasso di occupazione: percentuale di abitazioni principali sul totale¶

In [8]:
grid_data['tasso_occupazione_abitativa'] = (
    grid_data['OMI_vanivuoti||Ab_Princip'] / 
    grid_data['OMI_vanivuoti||n_Abitazio'] * 100
)
grid_data['tasso_occupazione_abitativa'] = grid_data['tasso_occupazione_abitativa'].replace([np.inf, -np.inf], np.nan)
grid_data['tasso_occupazione_abitativa'] = grid_data['tasso_occupazione_abitativa'].clip(upper=100)

Indice di disponibilità: percentuale di immobili disponibili sul totale¶

In [9]:
grid_data['indice_disponibilita_abitativa'] = (
    grid_data['OMI_vanivuoti||Imm_disp'] / 
    grid_data['OMI_vanivuoti||n_Abitazio'] * 100
)
grid_data['indice_disponibilita_abitativa'] = grid_data['indice_disponibilita_abitativa'].replace([np.inf, -np.inf], np.nan)
grid_data['indice_disponibilita_abitativa'] = grid_data['indice_disponibilita_abitativa'].clip(upper=100)

Tasso di affitto: percentuale di immobili locati sul totale¶

In [10]:
grid_data['tasso_affitto'] = (
    grid_data['OMI_vanivuoti||Imm_Loc'] / 
    grid_data['OMI_vanivuoti||n_Abitazio'] * 100
)
grid_data['tasso_affitto'] = grid_data['tasso_affitto'].replace([np.inf, -np.inf], np.nan)
grid_data['tasso_affitto'] = grid_data['tasso_affitto'].clip(upper=100)

Indice usi alternativi: percentuale di immobili con usi diversi da principale o locato¶

In [11]:
grid_data['indice_usi_alternativi'] = (
    (grid_data['OMI_vanivuoti||Uso_grat'] + 
        grid_data['OMI_vanivuoti||Altri_Util'] + 
        grid_data['OMI_vanivuoti||Non_disp']) / 
    grid_data['OMI_vanivuoti||n_Abitazio'] * 100
)
grid_data['indice_usi_alternativi'] = grid_data['indice_usi_alternativi'].replace([np.inf, -np.inf], np.nan)
grid_data['indice_usi_alternativi'] = grid_data['indice_usi_alternativi'].clip(upper=100)

3. Indicatori Impatto Turistico¶

Indice pressione turistica: strutture ricettive per 1000 abitanti¶

In [12]:
grid_data['indice_pressione_turistica'] = (
    (grid_data['ovm_places_strutture ricettive||walking__15__strutture_alberghiere'] + 
        grid_data['ovm_places_strutture ricettive||walking__15__strutture_extra-alberghiere']) / 
    grid_data['ISTAT||POP_TOT__21'] * 1000
).fillna(0)
grid_data['indice_pressione_turistica'] = grid_data['indice_pressione_turistica'].replace([np.inf, -np.inf], np.nan)
grid_data['indice_pressione_turistica'] = grid_data['indice_pressione_turistica'].clip(upper=1000)

Indice non alberghiero: rapporto tra strutture extra-alberghiere e alberghiere¶

In [13]:
grid_data['indice_non_alberghiero'] = (
    grid_data['ovm_places_strutture ricettive||walking__15__strutture_extra-alberghiere'] / 
    (grid_data['ovm_places_strutture ricettive||walking__15__strutture_alberghiere'] + 0.1)
).fillna(0)
grid_data['indice_non_alberghiero'] = grid_data['indice_non_alberghiero'].replace([np.inf, -np.inf], np.nan)
grid_data['indice_non_alberghiero'] = grid_data['indice_non_alberghiero'].clip(upper=10)

Impatto affitti brevi: rapporto strutture extra-alberghiere per 100 abitazioni¶

In [14]:
grid_data['impatto_affitti_brevi'] = (
    grid_data['ovm_places_strutture ricettive||walking__15__strutture_extra-alberghiere'] / 
    (grid_data['OMI_vanivuoti||n_Abitazio'] + 0.01) * 100
).fillna(0)
grid_data['impatto_affitti_brevi'] = grid_data['impatto_affitti_brevi'].replace([np.inf, -np.inf], np.nan)
grid_data['impatto_affitti_brevi'] = grid_data['impatto_affitti_brevi'].clip(upper=1000)

4. Indicatori Demografici¶

Variazione demografica 2001-2021 (in percentuale)¶

In [15]:
grid_data['variazione_demografica_01_21'] = (
    (grid_data['ISTAT||POP_TOT__21'] - grid_data['ISTAT||POP_TOT__01']) / 
    grid_data['ISTAT||POP_TOT__01'] * 100
)
grid_data['variazione_demografica_01_21'] = grid_data['variazione_demografica_01_21'].replace([np.inf, -np.inf], np.nan)
grid_data['variazione_demografica_01_21'] = grid_data['variazione_demografica_01_21'].clip(lower=-100, upper=1000)

Indice di invecchiamento: anziani in rapporto a giovani (x100)¶

In [16]:
grid_data['indice_invecchiamento'] = (
    grid_data['ISTAT||P_65_99__21'] / 
    (grid_data['ISTAT||P_0_14__21'] + 0.1) * 100
)
grid_data['indice_invecchiamento'] = grid_data['indice_invecchiamento'].replace([np.inf, -np.inf], np.nan)
grid_data['indice_invecchiamento'] = grid_data['indice_invecchiamento'].clip(upper=1000)

Indice giovani adulti: percentuale di popolazione 20-39 anni¶

In [17]:
grid_data['indice_giovani_adulti'] = (
    grid_data['ISTAT||P_20_39__21'] / 
    grid_data['ISTAT||POP_TOT__21'] * 100
)
grid_data['indice_giovani_adulti'] = grid_data['indice_giovani_adulti'].replace([np.inf, -np.inf], np.nan)
grid_data['indice_giovani_adulti'] = grid_data['indice_giovani_adulti'].clip(upper=100)

5. Indicatori Edilizia Residenziale Pubblica (ERP)¶

Accessibilità ERP a 30 minuti (per 1000 abitanti) - NO!¶

In [18]:
# grid_data['accessibilita_erp_30min'] = (
#     grid_data['beni_immobili_beni_immobili||walking__30__Edilizia Residenziale Pubblica'] / 
#     grid_data['ISTAT||POP_TOT__21'] * 1000
# ).fillna(0)
# grid_data['accessibilita_erp_30min'] = grid_data['accessibilita_erp_30min'].replace([np.inf, -np.inf], np.nan)
# grid_data['accessibilita_erp_30min'] = grid_data['accessibilita_erp_30min'].clip(upper=100)

Numero ERP (per 1000 abitanti) -- ?¶

In [19]:
grid_data['accessibilita_erp'] = (
    grid_data['beni_immobili_erp||total_erp'] / 
    grid_data['ISTAT||POP_TOT__21'] * 1000
).fillna(0)
grid_data['accessibilita_erp'] = grid_data['accessibilita_erp'].replace([np.inf, -np.inf], np.nan)
grid_data['accessibilita_erp'] = grid_data['accessibilita_erp'].clip(upper=100)
In [20]:
# grid_data['accessibilita_erp_15min'] = (
#     grid_data['beni_immobili_beni_immobili||walking__15__Edilizia Residenziale Pubblica'] / 
#     grid_data['ISTAT||POP_TOT__21'] * 1000
# ).fillna(0)
# grid_data['accessibilita_erp_15min'] = grid_data['accessibilita_erp_15min'].replace([np.inf, -np.inf], np.nan)
# grid_data['accessibilita_erp_15min'] = grid_data['accessibilita_erp_15min'].clip(upper=100)

Accessibilità ERP pedonale (percentuale rispetto accessibilità veicolare)¶

In [21]:
# grid_data['accessibilita_erp_pedonale'] = (
#     grid_data['beni_immobili_beni_immobili||walking__15__Edilizia Residenziale Pubblica'] / 
#     (grid_data['beni_immobili_beni_immobili||traffic__15__Edilizia Residenziale Pubblica'] + 0.1) * 100
# ).fillna(0)
# grid_data['accessibilita_erp_pedonale'] = grid_data['accessibilita_erp_pedonale'].replace([np.inf, -np.inf], np.nan)
# grid_data['accessibilita_erp_pedonale'] = grid_data['accessibilita_erp_pedonale'].clip(upper=100)

6. Indicatori Servizi Urbani¶

Accessibilità servizi sanitari (per 1000 abitanti)¶

In [22]:
grid_data['accessibilita_servizi_sanitari'] = (
    grid_data['ovm_places_sanita||walking__15__total_sanita'] / 
    grid_data['ISTAT||POP_TOT__21'] * 1000
).fillna(0)
grid_data['accessibilita_servizi_sanitari'] = grid_data['accessibilita_servizi_sanitari'].replace([np.inf, -np.inf], np.nan)
grid_data['accessibilita_servizi_sanitari'] = grid_data['accessibilita_servizi_sanitari'].clip(upper=1000)

Accessibilità istruzione (per 100 giovani)¶

In [23]:
grid_data['accessibilita_istruzione'] = (
    grid_data['ovm_places_istruzione||walking__15__total_istruzione'] / 
    (grid_data['ISTAT||P_0_14__21'] + grid_data['ISTAT||P_15_19__21'] + 0.1) * 100
).fillna(0)
grid_data['accessibilita_istruzione'] = grid_data['accessibilita_istruzione'].replace([np.inf, -np.inf], np.nan)
grid_data['accessibilita_istruzione'] = grid_data['accessibilita_istruzione'].clip(upper=1000)

Accessibilità cultura (per 1000 abitanti)¶

In [24]:
grid_data['accessibilita_cultura'] = (
    grid_data['ovm_places_cultura||walking__15__total_cultura'] / 
    grid_data['ISTAT||POP_TOT__21'] * 1000
).fillna(0)
grid_data['accessibilita_cultura'] = grid_data['accessibilita_cultura'].replace([np.inf, -np.inf], np.nan)
grid_data['accessibilita_cultura'] = grid_data['accessibilita_cultura'].clip(upper=1000)

7. Indicatori Integrazione Urbana¶

Categorie di uso del suolo¶

In [25]:
colonne_uso_suolo = [
    'CLC||Agricultural', 'CLC||Artificial, non-agricultural vegetated',
    'CLC||Forest and semi natural', 'CLC||Industrial, commercial and transport',
    'CLC||Mine, dump and construction', 'CLC||Urban', 'CLC||Water bodies'
]

Diversità uso suolo (indice Shannon)¶

Misura il livello di diversità dell'uso del suolo (maggiore è l'indice, maggiore è la diversità)

In [26]:
def shannon_diversity(row):
    values = [row[col] for col in colonne_uso_suolo]
    values = [v for v in values if not np.isnan(v)]
    if not values:
        return np.nan
    total = sum(values)
    if total == 0:
        return 0
    proportions = [v/total for v in values if v > 0]
    return -sum(p * np.log(p) for p in proportions)

grid_data['mix_uso_suolo'] = grid_data.apply(shannon_diversity, axis=1)
grid_data['mix_uso_suolo'] = grid_data['mix_uso_suolo'].clip(upper=10)

Accessibilità multimodale¶

Conta quante modalità di trasporto sono disponibili (0-3)

In [27]:
# grid_data['accessibilita_multimodale'] = (
#     (grid_data['ovm_infr_infrastrutture||walking__15__total_infrastrutture'] > 0).astype(int) +
#     (grid_data['ovm_infr_infrastrutture||traffic__15__total_infrastrutture'] > 0).astype(int) +
#     (grid_data['ovm_places_istruzione_transit||transit__15__istruzione'] > 0).astype(int)
# )

grid_data['accessibilita_multimodale'] = (
    (grid_data['ovm_places_istruzione||walking__15__total_istruzione'] > 0).astype(int) +
    (grid_data['ovm_places_istruzione||traffic__15__total_istruzione'] > 0).astype(int) +
    (grid_data['ovm_places_istruzione_transit||transit__15__istruzione'] > 0).astype(int)
)

8. Indici Compositi¶

Indice vulnerabilità abitativa (maggiore = più vulnerabile)¶

Combina affordability, disponibilità, invecchiamento e edilizia pubblica

In [28]:
indicatori_vulnerabilita = [
    'indice_affordability',          # Più alto = peggiore
    'indice_disponibilita_abitativa', # Più basso = peggiore (invertiamo)
    'indice_invecchiamento',         # Più alto = peggiore
    'accessibilita_erp'        # Più basso = peggiore (invertiamo)
]

# Creiamo versione normalizzata
df_per_scaling = grid_data[indicatori_vulnerabilita].copy()

# Invertiamo indicatori dove valori bassi indicano maggiore vulnerabilità
max_disp = df_per_scaling['indice_disponibilita_abitativa'].max()
if not np.isnan(max_disp) and max_disp > 0:
    df_per_scaling['indice_disponibilita_abitativa'] = max_disp - df_per_scaling['indice_disponibilita_abitativa']

max_erp = df_per_scaling['accessibilita_erp'].max()
if not np.isnan(max_erp) and max_erp > 0:
    df_per_scaling['accessibilita_erp'] = max_erp - df_per_scaling['accessibilita_erp']

# Sostituiamo valori mancanti con media
for col in df_per_scaling.columns:
    mean_val = df_per_scaling[col].mean()
    if not np.isnan(mean_val):
        df_per_scaling[col] = df_per_scaling[col].fillna(mean_val)

# Verifichiamo che non ci siano infiniti o valori troppo grandi
for col in df_per_scaling.columns:
    df_per_scaling[col] = df_per_scaling[col].replace([np.inf, -np.inf], np.nan)
    df_per_scaling[col] = df_per_scaling[col].fillna(df_per_scaling[col].mean())

# Normalizzazione con MinMaxScaler
try:
    scaler = MinMaxScaler()
    scaled_data = scaler.fit_transform(df_per_scaling)
    
    # Calcoliamo media degli indicatori scalati come indice vulnerabilità
    grid_data['indice_vulnerabilita_abitativa'] = np.mean(scaled_data, axis=1)
except ValueError as e:
    print(f"Errore nella normalizzazione per vulnerabilità: {e}")
    # Fallback: utilizziamo un approccio più semplice
    grid_data['indice_vulnerabilita_abitativa'] = np.nan

Indice qualità abitativa (maggiore = migliore qualità)¶

Combina accessibilità ai servizi, mix uso suolo e multimodalità

In [29]:
indicatori_qualita = [
    'accessibilita_servizi_sanitari',  # Più alto = migliore
    'accessibilita_istruzione',        # Più alto = migliore
    'accessibilita_cultura',          # Più alto = migliore
    'mix_uso_suolo',                  # Più alto = migliore
    'accessibilita_multimodale'       # Più alto = migliore
]

df_per_qualita = grid_data[indicatori_qualita].copy()

# Valori mancanti con media
for col in df_per_qualita.columns:
    mean_val = df_per_qualita[col].mean()
    if not np.isnan(mean_val):
        df_per_qualita[col] = df_per_qualita[col].fillna(mean_val)

# Verifichiamo che non ci siano infiniti
for col in df_per_qualita.columns:
    df_per_qualita[col] = df_per_qualita[col].replace([np.inf, -np.inf], np.nan)
    df_per_qualita[col] = df_per_qualita[col].fillna(df_per_qualita[col].mean())

# Normalizzazione per qualità
try:
    scaler_qualita = MinMaxScaler()
    scaled_qualita = scaler_qualita.fit_transform(df_per_qualita)
    
    # Media indicatori scalati come indice qualità
    grid_data['indice_qualita_abitativa'] = np.mean(scaled_qualita, axis=1)
except ValueError as e:
    print(f"Errore nella normalizzazione per qualità: {e}")
    # Fallback
    grid_data['indice_qualita_abitativa'] = np.nan

print("Indicatori calcolati con successo.")
Indicatori calcolati con successo.
In [ ]: