In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import geopandas as gpd
import os
from shapely import wkt
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
In [2]:
# Configurazione stile grafici
plt.style.use('seaborn-v0_8-whitegrid')
sns.set_palette('viridis')
In [3]:
# Percorso del file
file_path = os.path.join(os.path.expanduser("~"), "ILAB_DATA/PATRIMONIO/OUT/grid_08_adv.csv")
In [4]:
# Caricamento dati
def load_data(path):
    """Carica il dataset e lo converte in GeoDataFrame"""
    df = pd.read_csv(path)
    print(f"Dataset caricato con {df.shape[0]} righe e {df.shape[1]} colonne")
    
    # Converti in GeoDataFrame se c'è una colonna geometry
    if 'geometry' in df.columns:
        try:
            df['geometry'] = df['geometry'].apply(wkt.loads)
            gdf = gpd.GeoDataFrame(df, geometry='geometry')
            print("Dati convertiti in formato geospaziale")
            return gdf
        except Exception as e:
            print(f"Errore nella conversione in GeoDataFrame: {e}")
            return df
    else:
        print("Colonna 'geometry' non trovata, utilizzo DataFrame standard")
        return df

# Carica i dati
data = load_data(file_path)
Dataset caricato con 2286 righe e 232 colonne
Dati convertiti in formato geospaziale
In [5]:
# Visualizza prime righe e informazioni sul dataframe
data.head()
Out[5]:
geometry h3_08 cell_a_m2 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 ... sanita_ospedale_transit||transit__120__ospedali sanita_ospedale_transit||transit__120__A.O. INTEGRATA CON IL SSN sanita_ospedale_transit||transit__120__A.O. INTEGRATA CON L'UNIVERSITA' sanita_ospedale_transit||transit__120__AZIENDA OSPEDALIERA sanita_ospedale_transit||transit__120__IRCCS FONDAZIONE sanita_ospedale_transit||transit__120__IRCCS PRIVATO sanita_ospedale_transit||transit__120__IRCCS PUBBLICO sanita_ospedale_transit||transit__120__OSPED. A GESTIONE DIRETTA PRESIDIO A.S.L. sanita_ospedale_transit||transit__120__OSPED. CLASSIF. O ASSIMIL. ART 1 L.132/1968 sanita_ospedale_transit||transit__120__POLICLINICO UNIVERSITARIO PRIVATO
0 POLYGON ((778248.961 4640757.537, 778115.135 4... 881e805327fffff 789678.181136 554587.539667 0.0 0.0 0.0 8439.846982 226650.794486 0.000000 ... 21.0 1.0 0.0 2.0 2.0 2.0 1.0 5.0 7.0 1.0
1 POLYGON ((780507.591 4630136.495, 780373.7 462... 881e805ae7fffff 790737.567794 512453.371816 0.0 0.0 0.0 0.000000 278284.195977 0.000000 ... 17.0 1.0 0.0 2.0 2.0 1.0 1.0 4.0 5.0 1.0
2 POLYGON ((779036.316 4626205.889, 778902.376 4... 881e805803fffff 790949.982986 0.000000 0.0 0.0 0.0 0.000000 790949.982986 0.000000 ... 19.0 1.0 0.0 2.0 2.0 2.0 1.0 4.0 6.0 1.0
3 POLYGON ((772630.254 4624140.134, 772496.23 46... 881e80594bfffff 790625.764584 0.000000 0.0 0.0 0.0 0.000000 0.000000 790625.764584 ... 7.0 0.0 0.0 2.0 1.0 0.0 1.0 1.0 1.0 1.0
4 POLYGON ((778925.734 4653302.784, 778792.022 4... 881e80c9dbfffff 788677.527561 788677.527561 0.0 0.0 0.0 0.000000 0.000000 0.000000 ... 17.0 1.0 1.0 2.0 1.0 2.0 1.0 3.0 5.0 1.0

5 rows × 232 columns

In [6]:
# Mostra le colonne disponibili
print("Colonne disponibili nel dataset:")
for col in data.columns:
    print(f"- {col}")
Colonne disponibili nel dataset:
- geometry
- h3_08
- cell_a_m2
- 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
- CLC||LABEL
- CLC||int_a_m2
- CLC||perc
- ISTAT||POP_F__91
- ISTAT||POP_M__91
- ISTAT||POP_TOT__91
- ISTAT||P_0_14__91
- ISTAT||P_15_19__91
- ISTAT||P_20_39__91
- ISTAT||P_40_64__91
- ISTAT||P_65_99__91
- ISTAT||POP_F__01
- ISTAT||POP_M__01
- ISTAT||POP_TOT__01
- ISTAT||P_0_14__01
- ISTAT||P_15_19__01
- ISTAT||P_20_39__01
- ISTAT||P_40_64__01
- ISTAT||P_65_99__01
- ISTAT||POP_F__11
- ISTAT||POP_M__11
- ISTAT||POP_TOT__11
- ISTAT||P_0_14__11
- ISTAT||P_15_19__11
- ISTAT||P_20_39__11
- ISTAT||P_40_64__11
- ISTAT||P_65_99__11
- ISTAT||POP_F__21
- ISTAT||POP_M__21
- ISTAT||POP_TOT__21
- ISTAT||P_0_14__21
- ISTAT||P_15_19__21
- ISTAT||P_20_39__21
- ISTAT||P_40_64__21
- ISTAT||P_65_99__21
- ISTAT||POP_TOT__21__11
- ovm_infr_infrastrutture||traffic__15__total_infrastrutture
- ovm_infr_infrastrutture||traffic__30__total_infrastrutture
- ovm_infr_infrastrutture||walking__15__total_infrastrutture
- ovm_infr_infrastrutture||walking__30__total_infrastrutture
- ovm_infr_infrastrutture||traffic__15__bus
- ovm_infr_infrastrutture||traffic__30__bus
- ovm_infr_infrastrutture||traffic__30__stazione
- ovm_infr_infrastrutture||traffic__15__stazione
- ovm_infr_infrastrutture||walking__15__bus
- ovm_infr_infrastrutture||walking__30__bus
- ovm_infr_infrastrutture||traffic__30__aeroporto
- ovm_infr_infrastrutture||walking__30__stazione
- ovm_infr_infrastrutture||traffic__15__aeroporto
- ovm_infr_infrastrutture||walking__15__stazione
- ovm_infr_infrastrutture||walking__30__aeroporto
- ovm_infr_infrastrutture||walking__15__aeroporto
- ovm_places_cultura||traffic__30__total_cultura
- ovm_places_cultura||traffic__15__total_cultura
- ovm_places_cultura||walking__30__total_cultura
- ovm_places_cultura||walking__15__total_cultura
- ovm_places_cultura||traffic__30__biblioteca
- ovm_places_cultura||traffic__30__cinema
- ovm_places_cultura||traffic__30__museo
- ovm_places_cultura||traffic__30__teatro_sala_concerto
- ovm_places_cultura||traffic__15__cinema
- ovm_places_cultura||traffic__15__museo
- ovm_places_cultura||traffic__15__biblioteca
- ovm_places_cultura||traffic__15__teatro_sala_concerto
- ovm_places_cultura||walking__30__museo
- ovm_places_cultura||walking__15__museo
- ovm_places_cultura||walking__30__cinema
- ovm_places_cultura||walking__30__teatro_sala_concerto
- ovm_places_cultura||traffic__30__evento_culturale
- ovm_places_cultura||walking__15__teatro_sala_concerto
- ovm_places_cultura||walking__30__biblioteca
- ovm_places_cultura||traffic__15__evento_culturale
- ovm_places_cultura||walking__15__biblioteca
- ovm_places_cultura||walking__15__cinema
- ovm_places_cultura||walking__30__evento_culturale
- ovm_places_cultura||walking__15__evento_culturale
- ovm_places_ristorazione||traffic__15__total_ristorazione
- ovm_places_ristorazione||traffic__30__total_ristorazione
- ovm_places_ristorazione||walking__30__total_ristorazione
- ovm_places_ristorazione||walking__15__total_ristorazione
- ovm_places_ristorazione||traffic__15__bar
- ovm_places_ristorazione||traffic__15__ristorante
- ovm_places_ristorazione||traffic__30__altri_servizi_alimentari
- ovm_places_ristorazione||traffic__30__bar
- ovm_places_ristorazione||traffic__30__ristorante
- ovm_places_ristorazione||walking__30__ristorante
- ovm_places_ristorazione||walking__15__ristorante
- ovm_places_ristorazione||traffic__15__altri_servizi_alimentari
- ovm_places_ristorazione||walking__15__bar
- ovm_places_ristorazione||walking__30__bar
- ovm_places_ristorazione||walking__30__altri_servizi_alimentari
- ovm_places_ristorazione||walking__15__altri_servizi_alimentari
- ovm_places_sanita||traffic__30__total_sanita
- ovm_places_sanita||traffic__15__total_sanita
- ovm_places_sanita||walking__15__total_sanita
- ovm_places_sanita||walking__30__total_sanita
- ovm_places_sanita||traffic__30__centri_sanitari
- ovm_places_sanita||traffic__30__cliniche_mediche
- ovm_places_sanita||traffic__30__servizi_diagnostici
- ovm_places_sanita||traffic__30__servizi_sanitari
- ovm_places_sanita||traffic__15__centri_sanitari
- ovm_places_sanita||traffic__15__servizi_sanitari
- ovm_places_sanita||walking__15__centri_sanitari
- ovm_places_sanita||walking__30__centri_sanitari
- ovm_places_sanita||traffic__15__servizi_diagnostici
- ovm_places_sanita||walking__30__servizi_sanitari
- ovm_places_sanita||walking__30__servizi_diagnostici
- ovm_places_sanita||traffic__15__cliniche_mediche
- ovm_places_sanita||walking__15__servizi_sanitari
- ovm_places_sanita||walking__15__servizi_diagnostici
- ovm_places_sanita||walking__30__cliniche_mediche
- ovm_places_sanita||walking__15__cliniche_mediche
- ovm_places_sport||traffic__15__total_sport
- ovm_places_sport||traffic__30__total_sport
- ovm_places_sport||walking__15__total_sport
- ovm_places_sport||walking__30__total_sport
- ovm_places_sport||traffic__15__altre_attivita_sportive
- ovm_places_sport||traffic__30__altre_attivita_sportive
- ovm_places_sport||traffic__30__club_e_squadre_sportive
- ovm_places_sport||traffic__30__fitness_e_allenamento
- ovm_places_sport||traffic__30__formazione_sportiva
- ovm_places_sport||traffic__30__luoghi_sportivi
- ovm_places_sport||traffic__30__sport_acquatici
- ovm_places_sport||traffic__30__sport_con_palla
- ovm_places_sport||traffic__30__sport_da_combattimento
- ovm_places_sport||traffic__15__fitness_e_allenamento
- ovm_places_sport||traffic__15__club_e_squadre_sportive
- ovm_places_sport||traffic__15__sport_acquatici
- ovm_places_sport||traffic__15__sport_con_palla
- ovm_places_sport||traffic__15__sport_da_combattimento
- ovm_places_sport||walking__15__fitness_e_allenamento
- ovm_places_sport||walking__30__fitness_e_allenamento
- ovm_places_sport||traffic__15__formazione_sportiva
- ovm_places_sport||walking__30__club_e_squadre_sportive
- ovm_places_sport||walking__30__sport_con_palla
- ovm_places_sport||walking__30__altre_attivita_sportive
- ovm_places_sport||walking__15__altre_attivita_sportive
- ovm_places_sport||walking__30__formazione_sportiva
- ovm_places_sport||traffic__15__luoghi_sportivi
- ovm_places_sport||walking__15__formazione_sportiva
- ovm_places_sport||walking__30__sport_da_combattimento
- ovm_places_sport||walking__15__club_e_squadre_sportive
- ovm_places_sport||walking__15__sport_acquatici
- ovm_places_sport||walking__15__sport_con_palla
- ovm_places_sport||walking__30__sport_acquatici
- ovm_places_sport||walking__15__sport_da_combattimento
- ovm_places_sport||walking__30__luoghi_sportivi
- ovm_places_sport||walking__15__luoghi_sportivi
- ovm_places_strutture ricettive||traffic__15__total_strutture ricettive
- ovm_places_strutture ricettive||traffic__30__total_strutture ricettive
- ovm_places_strutture ricettive||walking__15__total_strutture ricettive
- ovm_places_strutture ricettive||walking__30__total_strutture ricettive
- ovm_places_strutture ricettive||traffic__15__strutture_alberghiere
- ovm_places_strutture ricettive||traffic__30__strutture_alberghiere
- ovm_places_strutture ricettive||traffic__30__strutture_extra-alberghiere
- ovm_places_strutture ricettive||traffic__15__strutture_extra-alberghiere
- ovm_places_strutture ricettive||walking__15__strutture_alberghiere
- ovm_places_strutture ricettive||walking__30__strutture_alberghiere
- ovm_places_strutture ricettive||walking__30__strutture_extra-alberghiere
- ovm_places_strutture ricettive||walking__15__strutture_extra-alberghiere
- reddito_pro_capite||redd_cell
- reddito_pro_capite||redd_pc
- sanita_ospedale||traffic__30__total_ospedale
- sanita_ospedale||traffic__15__total_ospedale
- sanita_ospedale||walking__30__total_ospedale
- sanita_ospedale||walking__15__total_ospedale
- sanita_ospedale||traffic__30__OSPED. A GESTIONE DIRETTA PRESIDIO A.S.L.
- sanita_ospedale||traffic__15__OSPED. A GESTIONE DIRETTA PRESIDIO A.S.L.
- sanita_ospedale||traffic__30__A.O. INTEGRATA CON L'UNIVERSITA'
- sanita_ospedale||traffic__30__OSPED. CLASSIF. O ASSIMIL. ART 1 L.132/1968
- sanita_ospedale||traffic__30__A.O. INTEGRATA CON IL SSN
- sanita_ospedale||traffic__30__IRCCS FONDAZIONE
- sanita_ospedale||traffic__30__POLICLINICO UNIVERSITARIO PRIVATO
- sanita_ospedale||traffic__15__POLICLINICO UNIVERSITARIO PRIVATO
- sanita_ospedale||traffic__30__AZIENDA OSPEDALIERA
- sanita_ospedale||traffic__30__IRCCS PRIVATO
- sanita_ospedale||traffic__30__IRCCS PUBBLICO
- sanita_ospedale||traffic__15__OSPED. CLASSIF. O ASSIMIL. ART 1 L.132/1968
- sanita_ospedale||traffic__15__IRCCS FONDAZIONE
- sanita_ospedale||traffic__15__A.O. INTEGRATA CON L'UNIVERSITA'
- sanita_ospedale||walking__30__IRCCS FONDAZIONE
- sanita_ospedale||walking__30__OSPED. A GESTIONE DIRETTA PRESIDIO A.S.L.
- sanita_ospedale||walking__15__OSPED. A GESTIONE DIRETTA PRESIDIO A.S.L.
- sanita_ospedale||walking__30__POLICLINICO UNIVERSITARIO PRIVATO
- sanita_ospedale||walking__15__POLICLINICO UNIVERSITARIO PRIVATO
- sanita_ospedale||traffic__15__AZIENDA OSPEDALIERA
- sanita_ospedale||traffic__15__IRCCS PRIVATO
- sanita_ospedale||traffic__15__IRCCS PUBBLICO
- sanita_ospedale||walking__30__AZIENDA OSPEDALIERA
- sanita_ospedale||walking__30__IRCCS PUBBLICO
- sanita_ospedale||walking__15__AZIENDA OSPEDALIERA
- sanita_ospedale||walking__15__IRCCS PUBBLICO
- sanita_ospedale||walking__30__OSPED. CLASSIF. O ASSIMIL. ART 1 L.132/1968
- sanita_ospedale||walking__30__IRCCS PRIVATO
- sanita_ospedale||walking__15__OSPED. CLASSIF. O ASSIMIL. ART 1 L.132/1968
- sanita_ospedale||walking__15__IRCCS FONDAZIONE
- sanita_ospedale||walking__15__IRCCS PRIVATO
- sanita_ospedale||traffic__15__A.O. INTEGRATA CON IL SSN
- sanita_ospedale||walking__30__A.O. INTEGRATA CON IL SSN
- sanita_ospedale||walking__15__A.O. INTEGRATA CON IL SSN
- sanita_ospedale||walking__30__A.O. INTEGRATA CON L'UNIVERSITA'
- sanita_ospedale||walking__15__A.O. INTEGRATA CON L'UNIVERSITA'
- sanita_ospedale_transit||transit__60__ospedali
- sanita_ospedale_transit||transit__60__A.O. INTEGRATA CON IL SSN
- sanita_ospedale_transit||transit__60__A.O. INTEGRATA CON L'UNIVERSITA'
- sanita_ospedale_transit||transit__60__AZIENDA OSPEDALIERA
- sanita_ospedale_transit||transit__60__IRCCS FONDAZIONE
- sanita_ospedale_transit||transit__60__IRCCS PRIVATO
- sanita_ospedale_transit||transit__60__IRCCS PUBBLICO
- sanita_ospedale_transit||transit__60__OSPED. A GESTIONE DIRETTA PRESIDIO A.S.L.
- sanita_ospedale_transit||transit__60__OSPED. CLASSIF. O ASSIMIL. ART 1 L.132/1968
- sanita_ospedale_transit||transit__60__POLICLINICO UNIVERSITARIO PRIVATO
- sanita_ospedale_transit||transit__120__ospedali
- sanita_ospedale_transit||transit__120__A.O. INTEGRATA CON IL SSN
- sanita_ospedale_transit||transit__120__A.O. INTEGRATA CON L'UNIVERSITA'
- sanita_ospedale_transit||transit__120__AZIENDA OSPEDALIERA
- sanita_ospedale_transit||transit__120__IRCCS FONDAZIONE
- sanita_ospedale_transit||transit__120__IRCCS PRIVATO
- sanita_ospedale_transit||transit__120__IRCCS PUBBLICO
- sanita_ospedale_transit||transit__120__OSPED. A GESTIONE DIRETTA PRESIDIO A.S.L.
- sanita_ospedale_transit||transit__120__OSPED. CLASSIF. O ASSIMIL. ART 1 L.132/1968
- sanita_ospedale_transit||transit__120__POLICLINICO UNIVERSITARIO PRIVATO

1. ANALISI EDILIZIA RESIDENZIALE PUBBLICA E SOCIALE¶

In [7]:
def analisi_edilizia_residenziale(df):
    """Analisi del contesto socio-demografico per l'edilizia residenziale"""
    
    # Converti colonne demografiche a numerico
    demo_cols = [col for col in df.columns if 'ISTAT||P_' in col and '__21' in col]
    for col in demo_cols:
        df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
    
    # Calcolo popolazione totale 2021
    df['pop_tot_21'] = sum(df[col] for col in demo_cols)
    
    # Calcolo percentuali fasce d'età
    for col in demo_cols:
        fascia = col.replace('ISTAT||P_', '').replace('__21', '')
        df[f'perc_{fascia}'] = np.where(df['pop_tot_21'] > 0, 
                                       df[col] / df['pop_tot_21'] * 100, 
                                       0)
    
    # Calcolo densità abitativa
    if 'cell_a_m2' in df.columns:
        df['cell_a_m2'] = pd.to_numeric(df['cell_a_m2'], errors='coerce')
        df['densita_abitativa'] = np.where(df['cell_a_m2'] > 0,
                                         df['pop_tot_21'] / (df['cell_a_m2'] / 1_000_000),
                                         0)  # Abitanti per km²
    
    # Calcolo indice di invecchiamento
    if 'ISTAT||P_65_99__21' in df.columns and 'ISTAT||P_0_14__21' in df.columns:
        df['ISTAT||P_65_99__21'] = pd.to_numeric(df['ISTAT||P_65_99__21'], errors='coerce').fillna(0)
        df['ISTAT||P_0_14__21'] = pd.to_numeric(df['ISTAT||P_0_14__21'], errors='coerce').fillna(0)
        df['indice_invecchiamento'] = np.where(df['ISTAT||P_0_14__21'] > 0,
                                             df['ISTAT||P_65_99__21'] / df['ISTAT||P_0_14__21'] * 100,
                                             0)
        df['indice_invecchiamento'] = df['indice_invecchiamento'].clip(0, 500)
    
    # Relazione tra reddito e densità abitativa
    if 'reddito_pro_capite||redd_pc' in df.columns:
        df['reddito_pc'] = pd.to_numeric(df['reddito_pro_capite||redd_pc'], errors='coerce').fillna(0)
    
    return df
In [8]:
# 1. Analisi edilizia residenziale
data_edilizia = analisi_edilizia_residenziale(data)
In [9]:
# Grafico distribuzione densità abitativa
plt.figure(figsize=(10, 6))
sns.histplot(data_edilizia['densita_abitativa'].clip(0, data_edilizia['densita_abitativa'].quantile(0.95)), bins=20)
plt.title('Distribuzione della densità abitativa')
plt.xlabel('Abitanti per km²')
plt.ylabel('Numero di celle')
plt.show()
No description has been provided for this image
In [10]:
# Grafico indice di invecchiamento
plt.figure(figsize=(10, 6))
sns.histplot(data_edilizia['indice_invecchiamento'], bins=20)
plt.title('Distribuzione dell\'indice di invecchiamento')
plt.xlabel('Indice di invecchiamento')
plt.ylabel('Numero di celle')
plt.show()
No description has been provided for this image
In [11]:
# Relazione tra reddito e densità abitativa
if 'reddito_pro_capite||redd_pc' in data_edilizia.columns:    
    plt.figure(figsize=(10, 6))
    sns.scatterplot(x='reddito_pc', y='densita_abitativa', 
                    data=data_edilizia.sample(min(1000, len(data_edilizia))))
    plt.title('Relazione tra reddito e densità abitativa')
    plt.xlabel('Reddito pro capite')
    plt.ylabel('Densità abitativa (ab/km²)')
    plt.show()
No description has been provided for this image
In [12]:
# Cluster socio-demografici per edilizia sociale
features = []
if 'densita_abitativa' in data_edilizia.columns:
    features.append('densita_abitativa')
if 'indice_invecchiamento' in data_edilizia.columns:
    features.append('indice_invecchiamento')
if 'reddito_pc' in data_edilizia.columns:
    features.append('reddito_pc')

# Aggiungi percentuali delle fasce d'età rilevanti
perc_fasce = [col for col in data_edilizia.columns if col.startswith('perc_') and 
                any(fascia in col for fascia in ['0_14', '15_19', '65_99'])]
features.extend(perc_fasce)

# if len(features) > 1:
# Rimuovi righe con valori NaN nelle feature
df_cluster = data_edilizia.dropna(subset=features).copy()

if len(df_cluster) > 10:
    # Standardizzazione
    X = df_cluster[features].values
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
    # K-means clustering
    kmeans = KMeans(n_clusters=5, random_state=42)
    df_cluster['cluster_edilizia'] = kmeans.fit_predict(X_scaled)
    
    # Caratteristiche dei cluster
    cluster_info = df_cluster.groupby('cluster_edilizia')[features].mean()
    print("\nCaratteristiche dei cluster per edilizia sociale:")

cluster_info
Caratteristiche dei cluster per edilizia sociale:
Out[12]:
densita_abitativa indice_invecchiamento reddito_pc perc_0_14 perc_15_19 perc_65_99
cluster_edilizia
0 1225.660847 132.436239 19531.971486 14.869562 5.294206 18.427548
1 0.218013 4.599994 375.425236 0.465423 0.067151 0.857530
2 547.632964 289.425983 9943.074123 9.129260 3.940189 29.307963
3 11082.573911 217.754268 21744.564689 11.870539 4.464646 25.442565
4 333.216567 93.514240 1643.907332 17.447156 7.179406 14.582976
In [13]:
# Visualizzazione dei cluster su mappa
if isinstance(data_edilizia, gpd.GeoDataFrame):
    df_geo = data_edilizia.copy()
    df_geo.loc[df_cluster.index, 'cluster_edilizia'] = df_cluster['cluster_edilizia']
    
    fig, ax = plt.subplots(figsize=(12, 8))
    df_geo.dropna(subset=['cluster_edilizia']).plot(
        column='cluster_edilizia', 
        cmap='Set3', 
        categorical=True,
        legend=True, 
        ax=ax
    )
    plt.title('Cluster socio-demografici per edilizia sociale')
    plt.axis('off')
    plt.show()
No description has been provided for this image

2. ANALISI CARENZA DI AFFITTI E AFFITTI BREVI¶

In [14]:
def analisi_mercato_affitti(df):
    """Analisi del mercato degli affitti e degli affitti brevi"""
    
    # Verifica presenza colonne di reddito
    if 'reddito_pro_capite||redd_pc' in df.columns:
        df['reddito_pc'] = pd.to_numeric(df['reddito_pro_capite||redd_pc'], errors='coerce').fillna(0)
    
    # Indice di urbanizzazione (indicatore indiretto di mercato immobiliare)
    if 'CLC||Urban' in df.columns and 'cell_a_m2' in df.columns:
        df['CLC||Urban'] = pd.to_numeric(df['CLC||Urban'], errors='coerce')
        df['cell_a_m2'] = pd.to_numeric(df['cell_a_m2'], errors='coerce')
        df['indice_urbanizzazione'] = np.where(df['cell_a_m2'] > 0,
                                             df['CLC||Urban'] / df['cell_a_m2'] * 100,
                                             0)
    
    # Indice di pressione abitativa
    # Combina densità abitativa, accessibilità e reddito
    if all(col in df.columns for col in ['densita_abitativa', 'indice_urbanizzazione']):
        # Normalizzazione dei valori
        df['densita_norm'] = (df['densita_abitativa'] - df['densita_abitativa'].min()) / \
                           (df['densita_abitativa'].max() - df['densita_abitativa'].min()) * 100
        
        df['urban_norm'] = (df['indice_urbanizzazione'] - df['indice_urbanizzazione'].min()) / \
                          (df['indice_urbanizzazione'].max() - df['indice_urbanizzazione'].min()) * 100
        
        # Indice di pressione sul mercato degli affitti
        df['indice_pressione_affitti'] = df['densita_norm'] * 0.7 + df['urban_norm'] * 0.3
        
        # Se disponibile, incorpora anche il reddito
        if 'reddito_pc' in df.columns:
            df['reddito_norm'] = (df['reddito_pc'] - df['reddito_pc'].min()) / \
                               (df['reddito_pc'].max() - df['reddito_pc'].min()) * 100
            df['indice_pressione_affitti'] = df['densita_norm'] * 0.5 + \
                                           df['urban_norm'] * 0.2 + \
                                           df['reddito_norm'] * 0.3
        
        # Classificazione aree per pressione sul mercato affitti
        df['classe_pressione_affitti'] = pd.qcut(
            df['indice_pressione_affitti'], 
            q=5, 
            labels=['Molto bassa', 'Bassa', 'Media', 'Alta', 'Molto alta']
        )
    
    return df

Pressione sugli affitti:

  • reddito
  • valori omi locazioni
  • numero di vani vuoti
  • popolazione
In [15]:
# 2. Analisi mercato affitti
data_affitti = analisi_mercato_affitti(data)
In [16]:
# Distribuzione del reddito
if 'reddito_pro_capite||redd_pc' in data_affitti.columns:
    plt.figure(figsize=(10, 6))
    sns.histplot(data_affitti['reddito_pc'].clip(0, data_affitti['reddito_pc'].quantile(0.95)), bins=20)
    plt.title('Distribuzione del reddito pro capite')
    plt.xlabel('Reddito pro capite')
    plt.ylabel('Numero di celle')
    plt.show()
No description has been provided for this image
In [17]:
# Distribuzione dell'indice
plt.figure(figsize=(10, 6))
sns.histplot(data_affitti['indice_pressione_affitti'], bins=20)
plt.title('Distribuzione dell\'indice di pressione sul mercato degli affitti')
plt.xlabel('Indice di pressione')
plt.ylabel('Numero di celle')
plt.show()
No description has been provided for this image
In [18]:
if isinstance(data_affitti, gpd.GeoDataFrame):
    fig, ax = plt.subplots(figsize=(12, 8))
    data_affitti.plot(column='indice_pressione_affitti', cmap='OrRd', legend=True, ax=ax)
    plt.title('Indice di pressione sul mercato degli affitti')
    plt.axis('off')
    plt.show()
No description has been provided for this image
In [19]:
if isinstance(data_affitti, gpd.GeoDataFrame):
    # Mappa delle classi
    fig, ax = plt.subplots(figsize=(12, 8))
    data_affitti.dropna(subset=['classe_pressione_affitti']).plot(
        column='classe_pressione_affitti', 
        cmap='OrRd', 
        categorical=True,
        legend=True, 
        ax=ax
    )
    plt.title('Classi di pressione sul mercato degli affitti')
    plt.axis('off')
    plt.show()
No description has been provided for this image

3. ANALISI PER AGENZIA SOCIALE PER L'ABITARE¶

  • basso reddito

  • alti costi d'affitto

  • affitto medio/reddito medio

  • indice di invecchiamento

  • (acc a piedi alle infr)

  • acc tpl ai servizi (da selezionare: istruzione, sanità, cultura, verde)

In [20]:
def analisi_agenzia_sociale(df):
    """Analisi per supportare un'agenzia sociale per l'abitare"""
    
    # Combinazione di fattori socio-demografici e accessibilità
    features = []
    
    # Controllo disponibilità delle feature
    if 'densita_abitativa' in df.columns:
        features.append('densita_abitativa')
    
    if 'indice_invecchiamento' in df.columns:
        features.append('indice_invecchiamento')
    
    if 'reddito_pc' in df.columns:
        features.append('reddito_pc')
    
    # Accessibilità a infrastrutture
    if 'ovm_infr_infrastrutture||traffic__15__total_infrastrutture' in df.columns:
        df['accessibilita_15min'] = pd.to_numeric(
            df['ovm_infr_infrastrutture||traffic__15__total_infrastrutture'], errors='coerce').fillna(0)
        features.append('accessibilita_15min')
    
    if 'ovm_infr_infrastrutture||traffic__30__total_infrastrutture' in df.columns:
        df['accessibilita_30min'] = pd.to_numeric(
            df['ovm_infr_infrastrutture||traffic__30__total_infrastrutture'], errors='coerce').fillna(0)
        features.append('accessibilita_30min')
    
    if len(features) > 1:
        # Rimuovi righe con valori NaN nelle feature
        df_model = df.dropna(subset=features).copy()
        
        if len(df_model) > 10:
            # Standardizzazione
            scaler = StandardScaler()
            X_scaled = scaler.fit_transform(df_model[features].values)
            
            # Converto in DataFrame con nomi colonne
            X_scaled_df = pd.DataFrame(X_scaled, columns=features, index=df_model.index)
            
            # Costruisci un indice composito per intervento agenzia sociale
            weights = {}
            for feat in features:
                if 'densita' in feat:
                    weights[feat] = 0.2
                elif 'invecchiamento' in feat:
                    weights[feat] = 0.15
                elif 'reddito' in feat:
                    # Inverto il reddito (meno reddito = più bisogno)
                    weights[feat] = -0.3
                elif 'accessibilita' in feat:
                    weights[feat] = 0.1
            
            # Calcola indice composto
            df_model['indice_intervento_sociale'] = sum(X_scaled_df[feat] * weights[feat] for feat in features)
            
            # Normalizza tra 0 e 100
            min_val = df_model['indice_intervento_sociale'].min()
            max_val = df_model['indice_intervento_sociale'].max()
            df_model['indice_intervento_sociale'] = (df_model['indice_intervento_sociale'] - min_val) / (max_val - min_val) * 100
            
            # Classificazione aree
            df_model['classe_intervento'] = pd.qcut(
                df_model['indice_intervento_sociale'], 
                q=5, 
                labels=['Priorità molto bassa', 'Priorità bassa', 'Priorità media', 'Priorità alta', 'Priorità molto alta']
            )

        # Aggiungi il dato al dataframe originale
        df.loc[df_model.index, 'indice_intervento_sociale'] = df_model['indice_intervento_sociale']
    
    return df_model
In [21]:
# 3. Analisi agenzia sociale per l'abitare
data_agenzia = analisi_agenzia_sociale(data)
In [22]:
# Grafico distribuzione
plt.figure(figsize=(10, 6))
sns.histplot(data_agenzia['indice_intervento_sociale'], bins=20)
plt.title('Distribuzione dell\'indice di priorità per intervento sociale')
plt.xlabel('Indice di priorità (0-100)')
plt.ylabel('Numero di celle')
plt.show()
No description has been provided for this image
In [23]:
if isinstance(data, gpd.GeoDataFrame):
    df_geo = data.copy()
    df_geo.loc[data_agenzia.index, 'indice_intervento_sociale'] = data_agenzia['indice_intervento_sociale']
    
    fig, ax = plt.subplots(figsize=(12, 8))
    df_geo.dropna(subset=['indice_intervento_sociale']).plot(
        column='indice_intervento_sociale',
        cmap='RdYlGn_r',
        legend=True,
        ax=ax
    )
    plt.title('Priorità di intervento per agenzia sociale per l\'abitare')
    plt.axis('off')
    plt.show()
No description has been provided for this image
In [24]:
if isinstance(data, gpd.GeoDataFrame):    
    # Aggiungi classe al dataframe originale
    data.loc[data_agenzia.index, 'classe_intervento'] = data_agenzia['classe_intervento']
    
    # Mappa delle classi di priorità
    fig, ax = plt.subplots(figsize=(12, 8))
    data.dropna(subset=['classe_intervento']).plot(
        column='classe_intervento',
        cmap='RdYlGn_r',
        categorical=True,
        legend=True,
        ax=ax
    )
    plt.title('Classi di priorità per intervento dell\'agenzia sociale')
    plt.axis('off')
    plt.show()
No description has been provided for this image

4. ANALISI ENERGIA VERDE E CONSUMO DI SUOLO¶

In [25]:
def analisi_energia_suolo(df):
    """Analisi del potenziale per energia verde e del consumo di suolo"""
    
    # Analisi uso del suolo
    uso_suolo_cols = [col for col in df.columns if 'CLC||' in col]
    
    if uso_suolo_cols:
        print(f"Categorie di uso del suolo trovate: {len(uso_suolo_cols)}")
        for col in uso_suolo_cols:
            print(f"- {col}")
        
        # Calcola percentuali di uso del suolo
        if 'cell_a_m2' in df.columns:
            for col in uso_suolo_cols:
                categoria = col.replace('CLC||', '')
                categoria_norm = categoria.lower().replace(' and ', '_').replace(' ', '_')
                df[col] = pd.to_numeric(df[col], errors='coerce')
                df[f'perc_{categoria_norm}'] = np.where(df['cell_a_m2'] > 0, 
                                                     df[col] / df['cell_a_m2'] * 100,
                                                     0)
        
        # Indice di urbanizzazione
        if 'CLC||Urban' in df.columns:
            df['indice_urbanizzazione'] = np.where(df['cell_a_m2'] > 0,
                                                df['CLC||Urban'] / df['cell_a_m2'] * 100,
                                                0)
            
        # Indice potenziale energia verde
        # Componenti: aree non urbanizzate, foreste, aree agricole
        
        # Componente base: area non urbanizzata
        if 'indice_urbanizzazione' in df.columns:
            df['potenziale_base'] = 100 - df['indice_urbanizzazione']
        else:
            df['potenziale_base'] = 50  # Valore predefinito
        
        # Componente foreste
        if 'CLC||Forest and semi natural' in df.columns:
            df['CLC||Forest and semi natural'] = pd.to_numeric(df['CLC||Forest and semi natural'], errors='coerce')
            df['bonus_foreste'] = np.where(df['cell_a_m2'] > 0,
                                         df['CLC||Forest and semi natural'] / df['cell_a_m2'] * 30,
                                         0)
        else:
            df['bonus_foreste'] = 0
        
        # Componente aree agricole
        if 'CLC||Agricultural' in df.columns:
            df['CLC||Agricultural'] = pd.to_numeric(df['CLC||Agricultural'], errors='coerce')
            df['bonus_agricolo'] = np.where(df['cell_a_m2'] > 0,
                                          df['CLC||Agricultural'] / df['cell_a_m2'] * 15,
                                          0)
        else:
            df['bonus_agricolo'] = 0
        
        # Calcolo indice potenziale verde
        df['indice_potenziale_verde'] = df['potenziale_base'] + df['bonus_foreste'] + df['bonus_agricolo']
        df['indice_potenziale_verde'] = df['indice_potenziale_verde'].clip(0, 100)
        
        # Classificazione aree per potenziale verde
        df['classe_potenziale_verde'] = pd.cut(
            df['indice_potenziale_verde'], 
            bins=[0, 20, 40, 60, 80, 100],
            labels=['Molto basso', 'Basso', 'Medio', 'Alto', 'Molto alto']
        )
    
    return df
In [26]:
# 4. Analisi energia verde e consumo di suolo
data_energia = analisi_energia_suolo(data)
Categorie di uso del suolo trovate: 10
- 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
- CLC||LABEL
- CLC||int_a_m2
- CLC||perc
In [27]:
# Indice di urbanizzazione
if 'CLC||Urban' in data_energia.columns:    
    # Grafico distribuzione urbanizzazione
    plt.figure(figsize=(10, 6))
    sns.histplot(data_energia['indice_urbanizzazione'].clip(0, 100), bins=20)
    plt.title('Distribuzione dell\'indice di urbanizzazione')
    plt.xlabel('Percentuale di area urbanizzata')
    plt.ylabel('Numero di celle')
    plt.show()
No description has been provided for this image
In [28]:
# Grafico distribuzione potenziale verde
plt.figure(figsize=(10, 6))
sns.histplot(data_energia['indice_potenziale_verde'], bins=20)
plt.title('Distribuzione dell\'indice di potenziale per energia verde')
plt.xlabel('Indice di potenziale (0-100)')
plt.ylabel('Numero di celle')
plt.show()
No description has been provided for this image
In [29]:
# Distribuzione delle classi
plt.figure(figsize=(10, 6))
sns.countplot(y='classe_potenziale_verde', data=data_energia)
plt.title('Distribuzione delle classi di potenziale per energia verde')
plt.xlabel('Numero di celle')
plt.ylabel('Classe di potenziale')
plt.show()
No description has been provided for this image
In [30]:
# Visualizzazione su mappa
if isinstance(data_energia, gpd.GeoDataFrame):
    fig, ax = plt.subplots(figsize=(12, 8))
    data_energia.plot(column='indice_potenziale_verde', cmap='YlGn', legend=True, ax=ax)
    plt.title('Indice di potenziale per energia verde')
    plt.axis('off')
    plt.show()
No description has been provided for this image

Sintesi dei risultati¶

In [31]:
# Indici principali
indici_principali = [
    'densita_abitativa', 
    'indice_invecchiamento', 
    'indice_urbanizzazione',
    'indice_pressione_affitti',
    'indice_intervento_sociale',
    'indice_potenziale_verde'
]
In [32]:
# Statistiche descrittive
indici_disponibili = [col for col in indici_principali if col in data.columns]
if indici_disponibili:
    print("\nStatistiche descrittive degli indici principali:")
data[indici_disponibili].describe()
Statistiche descrittive degli indici principali:
Out[32]:
densita_abitativa indice_invecchiamento indice_urbanizzazione indice_pressione_affitti indice_intervento_sociale indice_potenziale_verde
count 2286.000000 2286.000000 2285.000000 2285.000000 2286.000000 2285.000000
mean 1521.539076 123.155967 19.951445 13.025702 35.948870 82.412511
std 3529.968817 107.664148 31.284540 15.048723 11.887125 30.686294
min 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 0.767867 22.222222 0.000000 0.008994 29.743039 75.795526
50% 31.204119 118.417747 0.000000 9.429323 34.547198 100.000000
75% 1112.854476 190.152750 32.879901 19.525916 41.440873 100.000000
max 32200.332674 500.000000 100.000000 80.989596 100.000000 100.000000
In [33]:
# Top 5 aree per priorità di intervento sociale
if 'indice_intervento_sociale' in data.columns:
    print("\nTop 5 aree per priorità di intervento sociale:")
    top_aree = data.sort_values('indice_intervento_sociale', ascending=False).head(5)
    for idx, row in top_aree.iterrows():
        location = f"h3_08: {row['h3_08']}" if 'h3_08' in row else f"Indice {idx}"
        print(f"{location} - Indice: {row['indice_intervento_sociale']:.2f}")

# Top 5 aree per potenziale energia verde
if 'indice_potenziale_verde' in data.columns:
    print("\nTop 5 aree per potenziale energia verde:")
    top_verde = data.sort_values('indice_potenziale_verde', ascending=False).head(5)
    for idx, row in top_verde.iterrows():
        location = f"h3_08: {row['h3_08']}" if 'h3_08' in row else f"Indice {idx}"
        print(f"{location} - Indice: {row['indice_potenziale_verde']:.2f}")

# Top 5 aree critiche per pressione sul mercato degli affitti
if 'indice_pressione_affitti' in data.columns:
    print("\nTop 5 aree critiche per pressione sul mercato degli affitti:")
    top_affitti = data.sort_values('indice_pressione_affitti', ascending=False).head(5)
    for idx, row in top_affitti.iterrows():
        location = f"h3_08: {row['h3_08']}" if 'h3_08' in row else f"Indice {idx}"
        print(f"{location} - Indice: {row['indice_pressione_affitti']:.2f}")
Top 5 aree per priorità di intervento sociale:
h3_08: 881e805097fffff - Indice: 100.00
h3_08: 881e8050d1fffff - Indice: 94.85
h3_08: 881e8051cbfffff - Indice: 94.60
h3_08: 881e805089fffff - Indice: 91.49
h3_08: 881e80572dfffff - Indice: 86.51

Top 5 aree per potenziale energia verde:
h3_08: 881e80cc13fffff - Indice: 100.00
h3_08: 881e80d89dfffff - Indice: 100.00
h3_08: 881e80c943fffff - Indice: 100.00
h3_08: 881e8041e5fffff - Indice: 100.00
h3_08: 881e805309fffff - Indice: 100.00

Top 5 aree critiche per pressione sul mercato degli affitti:
h3_08: 881e805097fffff - Indice: 80.99
h3_08: 881e805089fffff - Indice: 78.99
h3_08: 881e805007fffff - Indice: 73.52
h3_08: 881e8050c7fffff - Indice: 70.73
h3_08: 881e8051cbfffff - Indice: 69.72
In [ ]: