Importazione delle librerie e caricamento dei dati¶
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
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from shapely import wkt
from matplotlib.colors import LinearSegmentedColormap
from jenkspy import jenks_breaks
# Configurazione stile grafici
plt.style.use('seaborn-v0_8-whitegrid')
sns.set_palette('viridis')
In [2]:
ilab_path = os.path.join(os.path.expanduser('~'),'ILAB_DATA')
work_path = os.path.join(ilab_path,'PATRIMONIO','DATA')
out_path = os.path.join(ilab_path,'PATRIMONIO','OUT')
In [3]:
resolution = 8
In [4]:
file_path = os.path.join(out_path, f'grid_{resolution:02}_adv.csv')
# Caricamento dei dati
df = pd.read_csv(file_path)
print(f"Dataset caricato con {df.shape[0]} righe e {df.shape[1]} colonne")
# Mostro le prime righe
df.head()
Dataset caricato con 2286 righe e 336 colonne
Out[4]:
| 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__ospedale | 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.9610496003 4640757.536709883,... | 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.5911791052 4630136.4950892795... | 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.3155420923 4626205.889196136,... | 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.2540061506 4624140.133895666,... | 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.7340115361 4653302.784000916,... | 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 × 336 columns
Esplorazione dei dati relativi agli affitti e Airbnb¶
In [5]:
# Verifica delle colonne relative agli affitti e Airbnb
affitto_cols = ['OMI||Loc_min', 'OMI||Loc_max']
airbnb_cols = ['airbnb_airbnb||total_airbnb', 'airbnb_airbnb||price_avg_airbnb', 'airbnb_airbnb||beds_sum_airbnb']
# Converto le colonne in numerico
for col in affitto_cols + airbnb_cols:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
# Statistiche descrittive
print("Statistiche descrittive delle colonne relative agli affitti:")
df[affitto_cols].describe()
print("\nStatistiche descrittive delle colonne relative ad Airbnb:")
df[airbnb_cols].describe()
# Calcolo del valore medio dell'affitto OMI
if 'OMI||Loc_min' in df.columns and 'OMI||Loc_max' in df.columns:
df['OMI_affitto_medio'] = (df['OMI||Loc_min'] + df['OMI||Loc_max']) / 2
print("\nStatistiche del valore medio di affitto OMI:")
print(df['OMI_affitto_medio'].describe())
# Visualizzazione della distribuzione dei valori di affitto
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
sns.histplot(df['OMI_affitto_medio'].dropna(), bins=30)
plt.title('Distribuzione del valore medio di affitto OMI')
plt.xlabel('€/m²')
plt.subplot(1, 2, 2)
sns.histplot(df['airbnb_airbnb||price_avg_airbnb'].dropna(), bins=30)
plt.title('Distribuzione del prezzo medio Airbnb')
plt.xlabel('€')
plt.tight_layout()
plt.show()
# Relazione tra numero di Airbnb e valo
Statistiche descrittive delle colonne relative agli affitti: Statistiche descrittive delle colonne relative ad Airbnb: Statistiche del valore medio di affitto OMI: count 1662.000000 mean 8.821946 std 3.210303 min 0.000000 25% 7.900000 50% 8.767853 75% 10.652208 max 21.441433 Name: OMI_affitto_medio, dtype: float64
Preparazione dei dati demografici ed economici¶
In [6]:
# Converto le colonne demografiche
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 dell'area in km²
df['area_km2'] = pd.to_numeric(df['cell_a_m2'], errors='coerce') / 1000000
# Calcolo densità abitativa (persone per km²)
df['densita_abitativa'] = np.where(df['area_km2'] > 0,
df['pop_tot_21'] / df['area_km2'],
0)
# Preparazione dati sul 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)
Calcola l'indice di urbanizzazione
In [7]:
# Uso del suolo
if 'CLC||Urban' in df.columns:
df['CLC||Urban'] = pd.to_numeric(df['CLC||Urban'], errors='coerce')
df['indice_urbanizzazione'] = np.where(df['cell_a_m2'] > 0,
df['CLC||Urban'] / df['cell_a_m2'] * 100,
0)
Calcola indice di invecchiamento
In [8]:
# 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)
Creazione dell'Indice di Pressione del Mercato Degli Affitti (IPMA)¶
In [9]:
# Calcolo densità Airbnb per km²
df['densita_airbnb'] = np.where(df['area_km2'] > 0,
df['airbnb_airbnb||total_airbnb'] / df['area_km2'],
0)
# Calcolo rapporto letti Airbnb / popolazione
df['rapporto_letti_airbnb_popolazione'] = np.where(df['pop_tot_21'] > 0,
df['airbnb_airbnb||beds_sum_airbnb'] / df['pop_tot_21'] * 100,
0)
# Normalizzo le variabili per l'IPMA
columns_ipma = ['OMI_affitto_medio', 'densita_abitativa', 'indice_urbanizzazione',
'densita_airbnb', 'rapporto_letti_airbnb_popolazione','reddito_pc']
# Rimuovo valori NaN per la normalizzazione
df_ipma = df[columns_ipma].copy()
df_ipma = df_ipma.fillna(df_ipma.mean())
# Standardizzazione
scaler_ipma = StandardScaler()
df_ipma_scaled = pd.DataFrame(
scaler_ipma.fit_transform(df_ipma),
columns=columns_ipma,
index=df_ipma.index
)
Calcola IPMA (Indice di Pressione del Mercato degli Affitti)¶
Pesi:
- valore medio affitto 30%
- densità airbnb 25%
- rapporto letti airbnb/popolazione 15%
- densità abitativa 15%
- urbanizzazione 10%
- reddito 5%
In [10]:
df_ipma_scaled['IPMA'] = (
df_ipma_scaled['OMI_affitto_medio'] * 0.30 +
df_ipma_scaled['densita_airbnb'] * 0.25 +
df_ipma_scaled['rapporto_letti_airbnb_popolazione'] * 0.15 +
df_ipma_scaled['densita_abitativa'] * 0.15 +
df_ipma_scaled['indice_urbanizzazione'] * 0.10 +
df_ipma_scaled['reddito_pc'] * 0.05
)
# Trasferiamo l'IPMA al dataframe originale e convertiamo a scala 0-100
df['IPMA'] = ((df_ipma_scaled['IPMA'] - df_ipma_scaled['IPMA'].min()) /
(df_ipma_scaled['IPMA'].max() - df_ipma_scaled['IPMA'].min()) * 100)
# Classificazione in categorie di pressione
df['classe_IPMA'] = pd.qcut(
df['IPMA'].rank(method='first'),
q=5,
labels=['Molto bassa', 'Bassa', 'Media', 'Alta', 'Molto alta'],
)
# Visualizzazione della distribuzione dell'IPMA
plt.figure(figsize=(10, 6))
sns.histplot(df['IPMA'].dropna(), bins=30)
plt.title('Distribuzione dell\'Indice di Pressione del Mercato degli Affitti (IPMA)')
plt.xlabel('IPMA (0-100)')
plt.ylabel('Numero di celle')
plt.grid(True)
plt.show()
# Visualizzazione delle classi IPMA
plt.figure(figsize=(10, 6))
sns.countplot(y='classe_IPMA', data=df, order=['Molto alta', 'Alta', 'Media', 'Bassa', 'Molto bassa'])
plt.title('Distribuzione delle classi dell\'Indice di Pressione del Mercato degli Affitti')
plt.xlabel('Numero di celle')
plt.ylabel('Classe IPMA')
plt.show()
Creazione dell'Indice di Scarsità di Offerta in Affitto (ISOA)¶
Questo indice misura il divario tra la domanda potenziale di abitazioni in affitto e l'offerta disponibile
Rapporto tra prezzi Airbnb e prezzi affitto tradizionale¶
Un rapporto alto indica maggiore convenienza per i proprietari a destinare gli immobili ad Airbnb
I prezzi vengono normalizzati per rendere confrontabili i valori
Si assume che un appartamento medio sia di 80m²
In [11]:
mq_medio = 80
df['prezzo_mensile_affitto'] = df['OMI_affitto_medio'] * mq_medio
df['rapporto_prezzi_airbnb_affitto'] = np.where(df['prezzo_mensile_affitto'] > 0,
df['airbnb_airbnb||price_avg_airbnb'] * 20 / df['prezzo_mensile_affitto'],
0) # Assumo 20 giorni di occupazione media mensile per Airbnb
Indice di potenziale domanda di affitto¶
Basato su densità abitativa e componenti demografiche che tendono ad affittare
In [12]:
if 'ISTAT||P_20_39__21' in df.columns:
df['percentuale_giovani_adulti'] = np.where(df['pop_tot_21'] > 0,
df['ISTAT||P_20_39__21'] / df['pop_tot_21'] * 100,
0)
else:
df['percentuale_giovani_adulti'] = 0
# Normalizzo le variabili per l'ISOA
columns_isoa = ['rapporto_prezzi_airbnb_affitto', 'densita_airbnb',
'percentuale_giovani_adulti', 'densita_abitativa','reddito_pc']
# Rimuovo valori NaN per la normalizzazione
df_isoa = df[columns_isoa].copy()
df_isoa = df_isoa.fillna(df_isoa.mean())
# Standardizzazione
scaler_isoa = StandardScaler()
df_isoa_scaled = pd.DataFrame(
scaler_isoa.fit_transform(df_isoa),
columns=columns_isoa,
index=df_isoa.index
)
# Inverto il reddito normalizzato (reddito più basso = maggiore domanda di affitto)
if 'reddito_pc' in df_isoa_scaled.columns:
df_isoa_scaled['reddito_pc_inv'] = -df_isoa_scaled['reddito_pc']
else:
df_isoa_scaled['reddito_pc_inv'] = 0
Calcolo ISOA (Indice di Scarsità di Offerta in Affitto)¶
Pesi:
- rapporto prezzi 35%
- densità airbnb 25%
- percentuale giovani adulti 20%
- densità abitativa 15%
- reddito inverso 5%
In [13]:
df_isoa_scaled['ISOA'] = (
df_isoa_scaled['rapporto_prezzi_airbnb_affitto'] * 0.35 +
df_isoa_scaled['densita_airbnb'] * 0.25 +
df_isoa_scaled['percentuale_giovani_adulti'] * 0.20 +
df_isoa_scaled['densita_abitativa'] * 0.15 +
df_isoa_scaled['reddito_pc_inv'] * 0.05
)
# Trasferiamo l'ISOA al dataframe originale e convertiamo a scala 0-100
df['ISOA'] = ((df_isoa_scaled['ISOA'] - df_isoa_scaled['ISOA'].min()) /
(df_isoa_scaled['ISOA'].max() - df_isoa_scaled['ISOA'].min()) * 100)
# Classificazione in categorie di scarsità
df['classe_ISOA'] = pd.qcut(
df['ISOA'],
q=5,
labels=['Molto bassa', 'Bassa', 'Media', 'Alta', 'Molto alta']
)
# Visualizzazione della distribuzione dell'ISOA
plt.figure(figsize=(10, 6))
sns.histplot(df['ISOA'].dropna(), bins=30)
plt.title('Distribuzione dell\'Indice di Scarsità di Offerta in Affitto (ISOA)')
plt.xlabel('ISOA (0-100)')
plt.ylabel('Numero di celle')
plt.grid(True)
plt.show()
# Visualizzazione delle classi ISOA
plt.figure(figsize=(10, 6))
sns.countplot(y='classe_ISOA', data=df, order=['Molto alta', 'Alta', 'Media', 'Bassa', 'Molto bassa'])
plt.title('Distribuzione delle classi dell\'Indice di Scarsità di Offerta in Affitto')
plt.xlabel('Numero di celle')
plt.ylabel('Classe ISOA')
plt.show()
Creazione dell'Indice di Priorità per Politiche Locative (IPPL)¶
Combina IPMA e ISOA per identificare le aree dove è più urgente intervenire con politiche mirate al mercato degli affitti
In [14]:
# Calcolo IPPL
df['IPPL'] = df['IPMA'] * 0.5 + df['ISOA'] * 0.5
# Classificazione in categorie di priorità
df['classe_IPPL'] = pd.qcut(
df['IPPL'],
q=5,
labels=['Molto bassa', 'Bassa', 'Media', 'Alta', 'Molto alta']
)
# Visualizzazione della distribuzione dell'IPPL
plt.figure(figsize=(10, 6))
sns.histplot(df['IPPL'].dropna(), bins=30)
plt.title('Distribuzione dell\'Indice di Priorità per Politiche Locative (IPPL)')
plt.xlabel('IPPL (0-100)')
plt.ylabel('Numero di celle')
plt.grid(True)
plt.show()
# Visualizzazione delle classi IPPL
plt.figure(figsize=(10, 6))
sns.countplot(y='classe_IPPL', data=df, order=['Molto alta', 'Alta', 'Media', 'Bassa', 'Molto bassa'])
plt.title('Distribuzione delle classi dell\'Indice di Priorità per Politiche Locative')
plt.xlabel('Numero di celle')
plt.ylabel('Classe IPPL')
plt.show()
# Scatter plot tra IPMA e ISOA
plt.figure(figsize=(12, 8))
scatter = plt.scatter(df['IPMA'],
df['ISOA'],
c=df['IPPL'],
cmap='viridis',
alpha=0.6)
plt.colorbar(scatter, label='Indice di Priorità per Politiche Locative')
plt.title('Relazione tra Pressione del Mercato (IPMA) e Scarsità di Offerta (ISOA)')
plt.xlabel('Indice di Pressione del Mercato degli Affitti (IPMA)')
plt.ylabel('Indice di Scarsità di Offerta in Affitto (ISOA)')
plt.grid(True)
plt.show()
Analisi dell'impatto di Airbnb sul mercato degli affitti¶
In [15]:
#Calcolo del rapporto tra letti Airbnb e popolazione
plt.figure(figsize=(10, 6))
df_temp = df['rapporto_letti_airbnb_popolazione'].dropna()
if len(df_temp) > 0:
sns.histplot(df_temp.clip(0, df_temp.quantile(0.95)), bins=30)
plt.title('Distribuzione del rapporto letti Airbnb / popolazione (%)')
plt.xlabel('Letti Airbnb per 100 abitanti')
plt.ylabel('Numero di celle')
plt.grid(True)
plt.show()
else:
print("Dati insufficienti per visualizzare la distribuzione del rapporto letti Airbnb / popolazione")
# Relazione tra la presenza di Airbnb e il prezzo degli affitti
plt.figure(figsize=(12, 8))
# Creiamo un dataframe temporaneo senza valori NaN per entrambe le colonne
df_valid = df[['densita_airbnb', 'OMI_affitto_medio']].dropna()
if len(df_valid) > 0:
plt.scatter(df_valid['densita_airbnb'], df_valid['OMI_affitto_medio'], alpha=0.5)
plt.title('Relazione tra densità di Airbnb e prezzo medio degli affitti')
plt.xlabel('Densità Airbnb (unità/km²)')
plt.ylabel('Prezzo medio affitto (€/m²)')
plt.grid(True)
# Linea di tendenza con lo stesso dataset pulito
if len(df_valid) > 1: # Serve almeno 2 punti per la regressione
z = np.polyfit(df_valid['densita_airbnb'], df_valid['OMI_affitto_medio'], 1)
p = np.poly1d(z)
plt.plot(df_valid['densita_airbnb'], p(df_valid['densita_airbnb']), "r--")
plt.show()
else:
print("Dati insufficienti per visualizzare la relazione tra densità Airbnb e prezzo degli affitti")
# Confronto rapporto prezzi Airbnb vs affitto tradizionale
plt.figure(figsize=(10, 6))
df_ratio = df['rapporto_prezzi_airbnb_affitto'].dropna()
if len(df_ratio) > 0:
sns.histplot(df_ratio.clip(0, df_ratio.quantile(0.95)), bins=30)
plt.title('Distribuzione del rapporto tra prezzo Airbnb e affitto tradizionale')
plt.xlabel('Rapporto prezzi (Airbnb/Affitto)')
plt.ylabel('Numero di celle')
plt.axvline(x=1, color='r', linestyle='--', label='Parità')
plt.legend()
plt.grid(True)
plt.show()
else:
print("Dati insufficienti per visualizzare la distribuzione del rapporto prezzi Airbnb/Affitto")
# Identificazione delle zone con alta concentrazione di Airbnb
df_airbnb = df['densita_airbnb'].dropna()
if len(df_airbnb) > 0:
soglia_airbnb = df_airbnb.quantile(0.9)
zone_alta_airbnb = df[df['densita_airbnb'] > soglia_airbnb]
print(f"Numero di zone con alta concentrazione di Airbnb: {len(zone_alta_airbnb)}")
print("Caratteristiche medie di queste zone:")
cols_to_show = ['OMI_affitto_medio', 'densita_abitativa', 'rapporto_prezzi_airbnb_affitto', 'IPMA', 'ISOA', 'IPPL']
cols_available = [col for col in cols_to_show if col in zone_alta_airbnb.columns]
if len(cols_available) > 0:
print(zone_alta_airbnb[cols_available].mean().dropna())
else:
print("Nessuna colonna disponibile per mostrare le caratteristiche")
else:
print("Dati insufficienti sulla densità di Airbnb")
Numero di zone con alta concentrazione di Airbnb: 61 Caratteristiche medie di queste zone: OMI_affitto_medio 14.535405 densita_abitativa 12783.821150 rapporto_prezzi_airbnb_affitto 2.198999 IPMA 40.034740 ISOA 36.178967 IPPL 38.106854 dtype: float64
Identificazione e caratterizzazione delle aree critiche¶
In [16]:
# Identificazione e caratterizzazione delle aree critiche per la carenza di offerta in affitto
# Definizione delle aree critiche (priorità alta o molto alta)
aree_critiche = df[df['classe_IPPL'].isin(['Alta', 'Molto alta'])]
# Calcola il numero di aree critiche
n_aree_critiche = len(aree_critiche)
perc_aree_critiche = (n_aree_critiche / len(df)) * 100
print(f"Numero di aree critiche: {n_aree_critiche} ({perc_aree_critiche:.1f}% del totale)")
# Caratteristiche delle aree critiche
print("\nCaratteristiche medie delle aree critiche:")
cols_stat = ['IPMA', 'ISOA', 'IPPL', 'OMI_affitto_medio', 'densita_airbnb',
'rapporto_prezzi_airbnb_affitto', 'densita_abitativa']
print(aree_critiche[cols_stat].mean())
# Confronto tra aree critiche e non critiche
aree_non_critiche = df[~df['classe_IPPL'].isin(['Alta', 'Molto alta'])]
print("\nConfronto tra aree critiche e non critiche:")
print("Media aree critiche:")
print(aree_critiche[cols_stat].mean())
print("\nMedia aree non critiche:")
print(aree_non_critiche[cols_stat].mean())
print("\nDifferenza percentuale:")
diff_perc = (aree_critiche[cols_stat].mean() / aree_non_critiche[cols_stat].mean() - 1) * 100
print(diff_perc)
# Distribuzione delle aree critiche per classe di IPMA e ISOA
cross_tab = pd.crosstab(aree_critiche['classe_IPMA'],
aree_critiche['classe_ISOA'],
margins=True, margins_name='Totale')
print("\nDistribuzione delle aree critiche per classe di IPMA e ISOA:")
print(cross_tab)
# Visualizzazione heatmap della distribuzione
plt.figure(figsize=(12, 8))
cross_tab_no_margins = pd.crosstab(aree_critiche['classe_IPMA'], aree_critiche['classe_ISOA'])
sns.heatmap(cross_tab_no_margins, annot=True, cmap='YlOrRd', fmt='d')
plt.title('Distribuzione delle aree critiche per classe di IPMA e ISOA')
plt.xlabel('Classe di Scarsità di Offerta in Affitto')
plt.ylabel('Classe di Pressione del Mercato degli Affitti')
plt.show()
# Top 10 aree più critiche
top_aree_critiche = df.nlargest(10, 'IPPL')
print("\nTop 10 aree più critiche:")
print(top_aree_critiche[cols_stat])
Numero di aree critiche: 914 (40.0% del totale) Caratteristiche medie delle aree critiche: IPMA 17.503276 ISOA 16.853250 IPPL 17.178263 OMI_affitto_medio 10.527847 densita_airbnb 52.984225 rapporto_prezzi_airbnb_affitto 2.505792 densita_abitativa 3720.496848 dtype: float64 Confronto tra aree critiche e non critiche: Media aree critiche: IPMA 17.503276 ISOA 16.853250 IPPL 17.178263 OMI_affitto_medio 10.527847 densita_airbnb 52.984225 rapporto_prezzi_airbnb_affitto 2.505792 densita_abitativa 3720.496848 dtype: float64 Media aree non critiche: IPMA 10.038951 ISOA 7.072707 IPPL 8.555829 OMI_affitto_medio 6.757625 densita_airbnb 1.954872 rapporto_prezzi_airbnb_affitto 0.083541 densita_abitativa 56.635722 dtype: float64 Differenza percentuale: IPMA 74.353638 ISOA 138.285696 IPPL 100.778472 OMI_affitto_medio 55.792108 densita_airbnb 2610.368235 rapporto_prezzi_airbnb_affitto 2899.467564 densita_abitativa 6469.170034 dtype: float64 Distribuzione delle aree critiche per classe di IPMA e ISOA: classe_ISOA Bassa Media Alta Molto alta Totale classe_IPMA Molto bassa 0 0 6 47 53 Bassa 0 0 8 11 19 Media 0 0 33 32 65 Alta 15 94 130 82 321 Molto alta 20 53 101 282 456 Totale 35 147 278 454 914
Top 10 aree più critiche:
IPMA ISOA IPPL OMI_affitto_medio densita_airbnb \
484 100.000000 100.000000 100.000000 21.276402 1961.868092
1250 78.240302 74.959985 76.600143 19.767532 1402.248814
1863 75.361376 77.766835 76.564106 16.066073 1338.179273
2158 76.105189 73.409181 74.757185 19.568461 1370.470867
626 64.978262 71.127637 68.052949 13.452708 1133.707440
835 64.800829 55.427788 60.114309 21.441433 940.440538
197 10.938667 95.364614 53.151640 9.099424 1.266599
288 49.646374 54.559689 52.103032 12.471064 874.427536
1092 52.600230 43.566124 48.083177 18.672764 694.810692
303 49.804144 42.828427 46.316286 17.207830 620.121828
rapporto_prezzi_airbnb_affitto densita_abitativa
484 2.190172 11613.709701
1250 2.142775 9161.730637
1863 2.447840 16466.742698
2158 2.064815 8772.828538
626 2.410806 17485.864178
835 2.648504 6471.036150
197 24.726841 66.523563
288 2.464867 6896.549167
1092 2.621355 3893.475471
303 2.077478 10190.095413
Sintesi dei risultati per la discussione sul tema "RENTAL HOUSING SHORTAGE"¶
In [17]:
# Sintesi dei risultati per la discussione sul tema "RENTAL HOUSING SHORTAGE"
print("SINTESI DEI RISULTATI PER LA DISCUSSIONE SUL RENTAL HOUSING SHORTAGE")
print("=" * 80)
# 1. Indicatori compositi creati
print("\n1. INDICATORI COMPOSITI CREATI:")
print(" - IPMA: Indice di Pressione del Mercato degli Affitti")
print(" Misura la pressione sul mercato considerando valori OMI, densità Airbnb,")
print(" densità abitativa, urbanizzazione e reddito.")
print("\n - ISOA: Indice di Scarsità di Offerta in Affitto")
print(" Misura la scarsità dell'offerta in affitto rispetto alla domanda potenziale,")
print(" considerando la competizione con Airbnb e la componente demografica.")
print("\n - IPPL: Indice di Priorità per Politiche Locative")
print(" Combina IPMA e ISOA per identificare le aree dove è prioritario intervenire.")
# 2. Impatto di Airbnb
avg_ratio = df['rapporto_prezzi_airbnb_affitto'].mean()
print(f"\n2. IMPATTO DI AIRBNB SUL MERCATO DEGLI AFFITTI:")
print(f" - Rapporto medio tra prezzi Airbnb e affitto tradizionale: {avg_ratio:.2f}")
if avg_ratio > 1:
print(f" Airbnb è mediamente {avg_ratio:.1f} volte più redditizio dell'affitto tradizionale")
else:
print(f" L'affitto tradizionale è mediamente più redditizio di Airbnb")
print(f" - Correlazione tra densità Airbnb e prezzo medio degli affitti: {df['densita_airbnb'].corr(df['OMI_affitto_medio']):.2f}")
print(f" - Nelle zone con alta densità di Airbnb, il prezzo medio degli affitti è")
print(f" {zone_alta_airbnb['OMI_affitto_medio'].mean() / df['OMI_affitto_medio'].mean():.1f} volte più alto della media")
# 3. Aree critiche
print(f"\n3. AREE CRITICHE PER LA CARENZA DI OFFERTA IN AFFITTO:")
print(f" - Identificate {n_aree_critiche} aree critiche ({perc_aree_critiche:.1f}% del totale)")
print(f" - In queste aree, il prezzo medio degli affitti è {aree_critiche['OMI_affitto_medio'].mean():.2f} €/m²,")
print(f" {diff_perc['OMI_affitto_medio']:.1f}% più alto rispetto alle aree non critiche")
print(f" - La densità di Airbnb è {diff_perc['densita_airbnb']:.1f}% più alta nelle aree critiche")
# 4. Raccomandazioni
print("\n4. RACCOMANDAZIONI BASATE SUI DATI:")
print(" - Regolamentare l'offerta di Airbnb, particolarmente nelle aree con alta pressione del mercato")
print(" - Sviluppare politiche di incentivo all'affitto di lungo periodo nelle aree con scarsità di offerta")
print(" - Incrementare l'offerta di affitti accessibili nelle aree con alta priorità (IPPL)")
print(" - Implementare politiche differenziate in base alle caratteristiche locali del mercato")
print(" - Monitorare l'evoluzione degli indicatori nel tempo per valutare l'efficacia delle politiche")
print("\n" + "=" * 80)
SINTESI DEI RISULTATI PER LA DISCUSSIONE SUL RENTAL HOUSING SHORTAGE
================================================================================
1. INDICATORI COMPOSITI CREATI:
- IPMA: Indice di Pressione del Mercato degli Affitti
Misura la pressione sul mercato considerando valori OMI, densità Airbnb,
densità abitativa, urbanizzazione e reddito.
- ISOA: Indice di Scarsità di Offerta in Affitto
Misura la scarsità dell'offerta in affitto rispetto alla domanda potenziale,
considerando la competizione con Airbnb e la componente demografica.
- IPPL: Indice di Priorità per Politiche Locative
Combina IPMA e ISOA per identificare le aree dove è prioritario intervenire.
2. IMPATTO DI AIRBNB SUL MERCATO DEGLI AFFITTI:
- Rapporto medio tra prezzi Airbnb e affitto tradizionale: 1.12
Airbnb è mediamente 1.1 volte più redditizio dell'affitto tradizionale
- Correlazione tra densità Airbnb e prezzo medio degli affitti: 0.52
- Nelle zone con alta densità di Airbnb, il prezzo medio degli affitti è
1.6 volte più alto della media
3. AREE CRITICHE PER LA CARENZA DI OFFERTA IN AFFITTO:
- Identificate 914 aree critiche (40.0% del totale)
- In queste aree, il prezzo medio degli affitti è 10.53 €/m²,
55.8% più alto rispetto alle aree non critiche
- La densità di Airbnb è 2610.4% più alta nelle aree critiche
4. RACCOMANDAZIONI BASATE SUI DATI:
- Regolamentare l'offerta di Airbnb, particolarmente nelle aree con alta pressione del mercato
- Sviluppare politiche di incentivo all'affitto di lungo periodo nelle aree con scarsità di offerta
- Incrementare l'offerta di affitti accessibili nelle aree con alta priorità (IPPL)
- Implementare politiche differenziate in base alle caratteristiche locali del mercato
- Monitorare l'evoluzione degli indicatori nel tempo per valutare l'efficacia delle politiche
================================================================================
In [18]:
# Visualizzazione della distribuzione geografica degli affitti e presenza Airbnb
# Converti la colonna geometry in oggetti geometrici se necessario
if 'geometry' in df.columns:
try:
df['geometry'] = df['geometry'].apply(lambda x: wkt.loads(x) if isinstance(x, str) else x)
gdf = gpd.GeoDataFrame(df, geometry='geometry')
print("Dati convertiti in formato geospaziale")
except Exception as e:
print(f"Errore nella conversione in GeoDataFrame: {e}")
else:
print("Colonna 'geometry' non trovata, impossibile creare la mappa")
gdf = None
# Se abbiamo un GeoDataFrame valido, possiamo visualizzare le mappe
if gdf is not None:
# Creiamo un layout con 4 mappe (2x2)
fig, axes = plt.subplots(2, 2, figsize=(20, 16))
# Mappa 1: Valore medio degli affitti OMI
if 'OMI_affitto_medio' in gdf.columns:
ax = axes[0, 0]
gdf.plot(column='OMI_affitto_medio', cmap='viridis',
legend=True, ax=ax, legend_kwds={'label': "€/m²"})
ax.set_title('Valore medio degli affitti OMI')
ax.set_axis_off()
# Mappa 2: Densità Airbnb
if 'densita_airbnb' in gdf.columns:
ax = axes[0, 1]
gdf.plot(column='densita_airbnb', cmap='Reds',
legend=True, ax=ax, legend_kwds={'label': "Unità/km²"})
ax.set_title('Densità Airbnb')
ax.set_axis_off()
# Mappa 3: Rapporto prezzi Airbnb/Affitto
if 'rapporto_prezzi_airbnb_affitto' in gdf.columns:
ax = axes[1, 0]
# Utilizziamo una colormap personalizzata per evidenziare il valore soglia 1
cmap = LinearSegmentedColormap.from_list('custom_diverging',
['blue', 'white', 'red'],
N=256)
gdf.plot(column='rapporto_prezzi_airbnb_affitto', cmap=cmap,
legend=True, ax=ax,
vmin=0, vmax=max(2, gdf['rapporto_prezzi_airbnb_affitto'].quantile(0.95)),
legend_kwds={'label': "Rapporto prezzi"})
ax.set_title('Rapporto prezzi Airbnb/Affitto')
ax.set_axis_off()
# Mappa 4: Densità abitativa
if 'densita_abitativa' in gdf.columns:
ax = axes[1, 1]
gdf.plot(column='densita_abitativa', cmap='YlOrRd',
legend=True, ax=ax, legend_kwds={'label': "Abitanti/km²"})
ax.set_title('Densità abitativa')
ax.set_axis_off()
plt.tight_layout()
plt.show()
# Creiamo un'altra figura per gli indici compositi
fig, axes = plt.subplots(1, 3, figsize=(24, 8))
# Mappa IPMA
if 'IPMA' in gdf.columns:
ax = axes[0]
gdf.plot(column='IPMA', cmap='viridis',
legend=True, ax=ax, legend_kwds={'label': "IPMA (0-100)"})
ax.set_title('Indice di Pressione del Mercato degli Affitti (IPMA)')
ax.set_axis_off()
# Mappa ISOA
if 'ISOA' in gdf.columns:
ax = axes[1]
gdf.plot(column='ISOA', cmap='plasma',
legend=True, ax=ax, legend_kwds={'label': "ISOA (0-100)"})
ax.set_title('Indice di Scarsità di Offerta in Affitto (ISOA)')
ax.set_axis_off()
# Mappa IPPL
if 'IPPL' in gdf.columns:
ax = axes[2]
gdf.plot(column='IPPL', cmap='inferno',
legend=True, ax=ax, legend_kwds={'label': "IPPL (0-100)"})
ax.set_title('Indice di Priorità per Politiche Locative (IPPL)')
ax.set_axis_off()
plt.tight_layout()
plt.show()
else:
print("Impossibile creare le mappe: dati geografici non disponibili o non validi")
Dati convertiti in formato geospaziale
In [ ]:
Preparo esportazione degli indicatori
In [19]:
cols_indicatori = [
# 'OMI_affitto_medio',
# 'pop_tot_21',
# 'area_km2',
# 'densita_abitativa',
# 'reddito_pc',
# 'indice_urbanizzazione',
# 'indice_invecchiamento',
# 'densita_airbnb',
# 'rapporto_letti_airbnb_popolazione',
'IPMA',
# 'classe_IPMA',
# 'prezzo_mensile_affitto',
# 'rapporto_prezzi_airbnb_affitto',
# 'percentuale_giovani_adulti',
'ISOA',
#'classe_ISOA',
'IPPL',
#'classe_IPPL'
]
In [20]:
map_indicatori = {
'IPMA': 'IPMA',
'ISOA': 'ISOA',
'IPPL': 'IPPL',
}
In [21]:
df_base = df.copy()
Output con classi
In [22]:
n_classes = 5
for col in cols_indicatori:
# Calcola i natural breaks di Jenks per la classificazione
breaks = jenks_breaks(df[col].values, n_classes=n_classes)
# Crea una nuova colonna con le classi di Jenks
labels = [i+1 for i in range(len(breaks)-1)]
df.loc[:, f'{col}_'] = pd.cut(
df[col],
bins=breaks,
labels=labels,
include_lowest=True
)
df[col] = df[f'{col}_'].astype(int)
del df[f'{col}_']
df.rename(columns=map_indicatori, inplace=True)
gdf = gpd.GeoDataFrame(df[list(map_indicatori.values()) + ['geometry', f'h3_{resolution:02}']], geometry='geometry', crs=32632)
gdf['geometry'] = gdf['geometry'].centroid
gdf.to_crs(4326).to_file(os.path.join(out_path, f'grid_{resolution:02}_pt_tavolo2.geojson'), driver="GeoJSON")
Output con valori assoluti
In [23]:
n_classes = 5
df = df_base.copy()
df.rename(columns=map_indicatori, inplace=True)
gdf = gpd.GeoDataFrame(df[list(map_indicatori.values()) + ['geometry', f'h3_{resolution:02}']], geometry='geometry', crs=32632)
gdf['geometry'] = gdf['geometry'].centroid
cols_indicatori = map_indicatori.values()
asc3 = gpd.read_file(os.path.join(ilab_path,'ROMA','DATA','ASC_21', 'ASC_Liv_3_WGS84.shp'))
asc3 = asc3[asc3['PRO_COM']==58091].copy()
asc3_ind = asc3.sjoin(gdf)
aggs = {col:'mean' for col in cols_indicatori}
asc3_ind_g = asc3_ind.groupby('DEN_ASC3', as_index=False).agg(aggs)
for col in cols_indicatori:
# Calcola i natural breaks di Jenks per la classificazione
breaks = jenks_breaks(asc3_ind_g[col].values, n_classes=n_classes)
# Crea una nuova colonna con le classi di Jenks
labels = [i+1 for i in range(len(breaks)-1)]
asc3_ind_g.loc[:, f'{col}_cls'] = pd.cut(
asc3_ind_g[col],
bins=breaks,
labels=labels,
include_lowest=True
)
asc3_ind_g[f'{col}_cls'] = asc3_ind_g[f'{col}_cls'].astype(int)
asc3_out = asc3.merge(asc3_ind_g, how='left', on='DEN_ASC3')
asc3_out.to_crs(4326).to_file(os.path.join(out_path, f'asc3_tavolo2.geojson'), driver="GeoJSON")