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
No description has been provided for this image

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()
No description has been provided for this image
No description has been provided for this image

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()
No description has been provided for this image
No description has been provided for this image

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()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

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")
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
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
No description has been provided for this image
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
No description has been provided for this image
No description has been provided for this image
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")