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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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()
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 [ ]: