Elezioni Regionali: Emilia-Romagna del 17-18 novembre 2024¶

Dal sito eligendo... https://elezioni.interno.gov.it/risultati/20241117/regionali/elenchi/italia/08

In [1]:
import os
import requests
import json
import folium
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import geopandas as gpd
import pandas as pd
from datetime import datetime
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

# from itables import init_notebook_mode, show
# init_notebook_mode(all_interactive=True)
In [2]:
id_regione = '08'
regione_sel = 'emilia'
data_ele = '20241117'
In [3]:
elezioni_path =  os.path.join(os.path.expanduser('~'),'ILAB_DATA',f'ELEZIONI_{regione_sel.upper()}_2024')
In [4]:
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0',
    'Accept': 'application/json, text/javascript, */*; q=0.01',
    'Referer': 'https://elezioni.interno.gov.it/'
}

Reperimento informazioni territoriali (comuni della regione)¶

In [5]:
url = f'https://elezioni.interno.gov.it/mappe/comuni_{id_regione}.geojson'
comuni = gpd.read_file(url)
comuni = comuni.to_dict('records')
In [6]:
variables = [ 'dt_ele', 'cod_sez', 'desc_sez', 'desc_com',  'ele_m', 'ele_f',
        'ele_t', 'vot_m', 'vot_f', 'vot_t', 'perc_vot', 'sz_perv', 'sz_tot', 
        'fine_rip', 'sk_bianche', 'sk_nulle','sk_contestate', 'tot_vot_lis', 
        'non_valid',  'osp', 'data_prec_elez','circ_sto', 'reg_sto', 'prov_sto', 'comu_sto', 'sez_sto']

Scraping dei dati degli scrutini¶

In [7]:
scrutini_path = os.path.join(elezioni_path,f'CSV/scrutini_{regione_sel}.csv')

if not os.path.exists(scrutini_path):
    n_comuni = len(comuni)
    errors = 0
    comuni_out = []
    for i, comune_key in enumerate(comuni):
        comune_name = comune_key['name']
        minint_elettorale = comune_key['minint_elettorale']
        cod_reg = minint_elettorale[:3]
        cod_prov = minint_elettorale[3:6]
        cod_com = minint_elettorale[6:]
        print(f'Sto elaborando {comune_name} ({i+1}/{n_comuni})')
        try:
            api_endpoint = f'https://eleapi.interno.gov.it/siel/PX/scrutiniR/DE/{data_ele}/TE/07/RE/{id_regione}/PR/{cod_prov}/CM/{cod_com}'
            response = requests.get(api_endpoint, verify=True, headers=headers).json()
            comune = {k:v for k,v in response['int'].items() if k in variables}
            comune['cod_prov'] = cod_prov
            comune['cod_com'] = cod_com
            comune['minint_elettorale'] = minint_elettorale
            comune['dt_ele'] = datetime.strptime(str(comune['dt_ele']),'%Y%m%d%H%M%S').strftime('%Y-%m-%d')
            if comune['ele_t'] < comune['vot_t']:
                comune['perc_vot'] = '0'
            comune['perc_vot'] = float(comune['perc_vot'].replace(',','.'))
            for p in response['cand']:
                nome_cand = p['nome'] + ' ' + p['cogn']
                comune[f'{nome_cand}_voti'] = p['voti']
                comune[f'{nome_cand}_perc'] = float(p['perc'].replace(',','.'))
            comuni_out.append(comune)
            
        except Exception as e:
            print(e)
            errors+=1

    df = pd.DataFrame(comuni_out) 
    print(df.shape)
    df.to_csv(scrutini_path, sep=';',index=False)

Visualizzazione dei dati¶

In [8]:
comune_sel = 'BOLOGNA'
In [9]:
types = {'cod_com':str, 'cod_prov':str,'minint_elettorale':str}
df = pd.read_csv(os.path.join(elezioni_path,f'CSV/scrutini_{regione_sel}.csv'), dtype=types, sep=';')
url = f'https://elezioni.interno.gov.it/mappe/comuni_{id_regione}.geojson'
comuni = gpd.read_file(url)

Chi ha vinto?

In [10]:
lista_candidati = [cand for cand in df.columns if '_voti' in cand]
sums = list()
for cand in lista_candidati:
    d = {'candidato':cand.replace('_voti',''),
         'voti':df[cand].sum()}
    sums.append(d)
sums = pd.DataFrame(sums)
sums['r'] = sums['voti'].rank(ascending=False)
candidati = sums['candidato'].to_list()
candidato1 = candidati[0]
candidato2 = candidati[1]
In [11]:
df[df['desc_com']==comune_sel].to_dict('records')
Out[11]:
[{'dt_ele': '2024-11-17',
  'desc_com': 'BOLOGNA',
  'ele_m': 147381,
  'ele_f': 162396,
  'ele_t': 309777,
  'vot_m': 78037,
  'vot_f': 85970,
  'vot_t': 164007,
  'perc_vot': 52.94,
  'sz_tot': 445,
  'fine_rip': 'S',
  'sk_bianche': 666,
  'sk_nulle': 1863,
  'sk_contestate': 41,
  'tot_vot_lis': 146364,
  'non_valid': nan,
  'data_prec_elez': 20200126000000.0,
  'reg_sto': 8.0,
  'prov_sto': 13.0,
  'comu_sto': 60.0,
  'circ_sto': 13.0,
  'cod_prov': '013',
  'cod_com': '0060',
  'minint_elettorale': '1080130060',
  'ELENA UGOLINI_voti': 51567,
  'ELENA UGOLINI_perc': 31.94,
  'MICHELE DE PASCALE_voti': 102589,
  'MICHELE DE PASCALE_perc': 63.55,
  'LUCA TEODORI_voti': 1733,
  'LUCA TEODORI_perc': 1.07,
  'FEDERICO SERRA_voti': 5548,
  'FEDERICO SERRA_perc': 3.44}]

Selezione delle colonne del dataset degli scrutini¶

In [12]:
df = df[['minint_elettorale',f'{candidato1}_voti',f'{candidato1}_perc',f'{candidato2}_voti',f'{candidato2}_perc']].copy()

Attribuzione delle informazioni geografiche¶

In [13]:
df_adv = comuni[['name','minint_elettorale','geometry','com_istat_code']].merge(df, left_on='minint_elettorale', right_on='minint_elettorale')

Suddivisione nei due dataset per maggioranza dei voti¶

In [14]:
cand1 = df_adv[df_adv[f'{candidato1}_voti']>=df_adv[f'{candidato2}_voti']].copy()
cand2 = df_adv[df_adv[f'{candidato1}_voti']<df_adv[f'{candidato2}_voti']].copy()

Visualizzazione su base comunale¶

In [15]:
m = cand1.explore(column=f'{candidato1}_perc',cmap='Blues', name=f'{candidato1.title()}')
m = cand2.explore(m=m,column=f'{candidato2}_perc', cmap='Reds', name=f'{candidato2.title()}')
folium.LayerControl().add_to(m)
m
Out[15]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Utilizzo della tecnica "sample_points"¶

Creazione di un numero casuale di punti all'interno dei poligoni dei comuni, il numero di punti è proporzionale alla percentuale di voti ottenuta dal candidato

In [16]:
seed = 1234512345
p_voti = cand1[f'{candidato1}_perc'].mul(1).astype(int).values
cand1['p_voti'] = cand1.sample_points(p_voti,rng=seed)
cand1_p = cand1.set_geometry('p_voti')
p_voti = cand2[f'{candidato2}_perc'].mul(1).astype(int).values
cand2['p_voti'] = cand2.sample_points(p_voti,rng=seed)
cand2_p = cand2.set_geometry('p_voti')

Visualizzazione¶

In [17]:
m = cand1_p.explode(index_parts=True).explore(column=f'{candidato1}_perc',cmap='Blues',name=f'{candidato1}'.title())
m = cand2_p.explode(index_parts=True).explore(m=m,column=f'{candidato2}_perc', cmap='Reds', name=f'{candidato2}'.title())
folium.LayerControl().add_to(m)
m
Out[17]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Integrazione con i dati ISTAT (popolazione per classi di età) e Corine Land Cover¶

In [18]:
istat_path = os.path.join(os.path.expanduser('~'),'ILAB_DATA','ISTAT','DATA')
comuni_adv = gpd.read_parquet(os.path.join(istat_path,'comuni_adv.parquet'))
comuni_pop_age = pd.read_parquet(os.path.join(istat_path,'comuni_pop_age_italia.parquet'))
comuni_adv = comuni_adv.merge(comuni_pop_age[(comuni_pop_age['SEX']=='total')&
                                             (comuni_pop_age['TIME_PERIOD']=='2024')], left_on='pro_com_t', right_on='REF_AREA')
In [19]:
dati_ele = comuni_adv.merge(df_adv[['com_istat_code',f'{candidato1}_voti',f'{candidato1}_perc',f'{candidato2}_voti',f'{candidato2}_perc']], 
                 left_on='pro_com_t', right_on='com_istat_code')
data = dati_ele[['pro_com_t','comune','area_km2','females',
       'males', 'total', 'AGRICULTURAL',
       'ARTIFICIAL_NON_AGRICULTURAL_VEGETATED', 'FOREST_AND_SEMI_NATURAL',
       'INDUSTRIAL_COMMERCIAL_AND_TRANSPORT', 'MINE_DUMP_AND_CONSTRUCTION',
       'URBAN', 'WATER_BODIES', 'WETLANDS', 'Y0-14', 'Y15-64', 'Y65-109',
       f'{candidato1}_perc',f'{candidato2}_perc']].copy()
data.set_index('pro_com_t',inplace=True)
In [20]:
data
Out[20]:
comune area_km2 females males total AGRICULTURAL ARTIFICIAL_NON_AGRICULTURAL_VEGETATED FOREST_AND_SEMI_NATURAL INDUSTRIAL_COMMERCIAL_AND_TRANSPORT MINE_DUMP_AND_CONSTRUCTION URBAN WATER_BODIES WETLANDS Y0-14 Y15-64 Y65-109 ELENA UGOLINI_perc MICHELE DE PASCALE_perc
pro_com_t
033001 Agazzano 36.145013 1025 984 2009 92.223894 0.000000 5.543820 0.000000 0.000000 2.232286 0.0 0.0 206 1241 562 59.73 37.02
033002 Alseno 55.269294 2377 2349 4726 93.189090 1.261686 1.682648 0.745076 0.000000 3.121499 0.0 0.0 589 2944 1193 65.44 32.60
033003 Besenzone 23.947042 456 494 950 100.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.0 0.0 104 607 239 73.33 25.51
033004 Bettola 122.370652 1294 1353 2647 44.999551 0.000000 54.258338 0.000000 0.000000 0.742111 0.0 0.0 221 1511 915 67.65 29.99
033005 Bobbio 106.521541 1765 1657 3422 40.879574 0.000000 58.195471 0.000000 0.000000 0.924955 0.0 0.0 269 1937 1216 58.75 39.38
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
099027 Talamello 10.588681 545 516 1061 42.519350 0.000000 49.646369 3.174552 2.901721 1.758008 0.0 0.0 114 664 283 48.29 48.56
099028 Poggio Torriana 34.735142 2569 2573 5142 69.983142 0.000000 26.501103 1.789200 0.052202 1.674353 0.0 0.0 674 3354 1114 35.67 59.87
099029 Montescudo-Monte Colombo 32.348106 3400 3509 6909 86.651644 0.000000 13.141450 0.000000 0.000000 0.206906 0.0 0.0 984 4652 1273 55.27 41.17
099030 Montecopiolo 35.811562 524 523 1047 57.503544 0.000000 40.951192 0.000000 0.000000 1.545264 0.0 0.0 119 592 336 57.36 41.37
099031 Sassofeltrio 21.078426 716 656 1372 77.595005 0.000000 20.482731 0.000000 0.000000 1.922264 0.0 0.0 162 884 326 61.65 35.76

327 rows × 18 columns

In [21]:
data['females'] = data['females'] / data['total'] *100
data['males'] = data['males'] / data['total'] *100
data['Y0-14'] = data['Y0-14'] / data['total'] *100
data['Y15-64'] = data['Y15-64'] / data['total'] *100
data['Y65-109'] = data['Y65-109'] / data['total'] *100
In [22]:
data.to_csv(os.path.join(elezioni_path,'CSV',f'data_{regione_sel}.csv'),sep=';')
del data['comune']
In [23]:
metadati = [
    {'variabile':'metadati'},
    {'pro_com_t':'Codice ISTAT del comune'},
    {'area_km2':'Superficie del comune in chilometri quadrati'},
    {'females':'Numero di donne nel comune'},
    {'males':'Numero di uomini nel comune'},
    {'total':'Popolazione del comune'},
    {'AGRICULTURAL':'Percentuale di superificie agricola rispetto alla superificie del comune'},
    {'ARTIFICIAL_NON_AGRICULTURAL_VEGETATED':'Percentuale di superificie artificiale vegetata non agricola rispetto alla superificie del comune'},
    {'FOREST_AND_SEMI_NATURAL':'Percentuale di superificie forestale e semi naturale rispetto alla superificie del comune'},
    {'INDUSTRIAL_COMMERCIAL_AND_TRANSPORT':'Percentuale di superificie industriale: commerciale e legata ai trasporti rispetto alla superificie del comune'},
    {'MINE_DUMP_AND_CONSTRUCTION':'Percentuale di superificie dedicata a siti di miniera e costruzione rispetto alla superificie del comune'},
    {'URBAN':'Percentuale di superificie urbana rispetto alla superificie del comune'},
    {'WATER_BODIES':'Percentuale di superificie legata ai corpi idrici rispetto alla superificie del comune'},
    {'WETLANDS':'Percentuale di superificie umida rispetto alla superificie del comune'},
    {'Y0-14':'Numero di abitanti del comune di età compresa tra 0 e 14 anni'},
    {'Y15-64':'Numero di abitanti del comune di età compresa tra 15 e 64 anni'},
    {'Y65-109':'Numero di abitanti del comune di età compresa tra 65 e 109 anni'},
    {f'{candidato1}_perc': f'Percentuale di votanti che hanno votato {candidato1.title()} alle ultime elezioni in {regione_sel.title()}'},
    {f'{candidato2}_perc': f'Percentuale di votanti che hanno votato {candidato2.title()} alle ultime elezioni in {regione_sel.title()}'}
]
metadati = pd.DataFrame(metadati)
metadati.to_csv(os.path.join(elezioni_path,'CSV',f'metadati_elezioni_{regione_sel}.csv'),sep=';',index=False)
In [24]:
corr_matrix = data.corr()
corr_v = 0.7
corr_matrix.style.apply(lambda x: ["background-color: red"
                          if (v > corr_v or v < -corr_v) and (v != 1)
                          else "" for v in x], axis = 1)
Out[24]:
  area_km2 females males total AGRICULTURAL ARTIFICIAL_NON_AGRICULTURAL_VEGETATED FOREST_AND_SEMI_NATURAL INDUSTRIAL_COMMERCIAL_AND_TRANSPORT MINE_DUMP_AND_CONSTRUCTION URBAN WATER_BODIES WETLANDS Y0-14 Y15-64 Y65-109 ELENA UGOLINI_perc MICHELE DE PASCALE_perc
area_km2 1.000000 0.083746 -0.083746 0.479923 -0.149690 -0.011183 0.147786 -0.089860 -0.085029 -0.133192 0.199565 0.237209 -0.230020 -0.238346 0.247084 0.015725 -0.010727
females 0.083746 1.000000 -1.000000 0.300196 0.411402 0.233710 -0.512424 0.322264 0.030929 0.418354 0.087655 0.156783 0.408655 0.419215 -0.436180 -0.354845 0.357340
males -0.083746 -1.000000 1.000000 -0.300196 -0.411402 -0.233710 0.512424 -0.322264 -0.030929 -0.418354 -0.087655 -0.156783 -0.408655 -0.419215 0.436180 0.354845 -0.357340
total 0.479923 0.300196 -0.300196 1.000000 0.095121 0.246216 -0.222665 0.394952 0.051981 0.385565 0.036381 0.058152 0.114328 0.145253 -0.140484 -0.250653 0.253006
AGRICULTURAL -0.149690 0.411402 -0.411402 0.095121 1.000000 -0.058943 -0.947248 0.187333 0.051075 0.110104 0.031130 0.116420 0.587611 0.602708 -0.627132 -0.336862 0.328903
ARTIFICIAL_NON_AGRICULTURAL_VEGETATED -0.011183 0.233710 -0.233710 0.246216 -0.058943 1.000000 -0.139854 0.211018 -0.005820 0.661805 0.033650 0.044558 0.030934 0.112671 -0.086422 -0.112369 0.112556
FOREST_AND_SEMI_NATURAL 0.147786 -0.512424 0.512424 -0.222665 -0.947248 -0.139854 1.000000 -0.399967 -0.109030 -0.386455 -0.140319 -0.186703 -0.625508 -0.648121 0.671895 0.399415 -0.393014
INDUSTRIAL_COMMERCIAL_AND_TRANSPORT -0.089860 0.322264 -0.322264 0.394952 0.187333 0.211018 -0.399967 1.000000 0.216033 0.517658 0.016432 0.016700 0.355966 0.327308 -0.354964 -0.358102 0.362669
MINE_DUMP_AND_CONSTRUCTION -0.085029 0.030929 -0.030929 0.051981 0.051075 -0.005820 -0.109030 0.216033 1.000000 0.045931 0.024086 -0.032502 0.190862 0.197412 -0.204785 -0.146252 0.143987
URBAN -0.133192 0.418354 -0.418354 0.385565 0.110104 0.661805 -0.386455 0.517658 0.045931 1.000000 0.038945 0.010113 0.267448 0.279741 -0.289013 -0.280764 0.282247
WATER_BODIES 0.199565 0.087655 -0.087655 0.036381 0.031130 0.033650 -0.140319 0.016432 0.024086 0.038945 1.000000 0.423599 -0.031192 0.028323 -0.006508 0.092067 -0.092287
WETLANDS 0.237209 0.156783 -0.156783 0.058152 0.116420 0.044558 -0.186703 0.016700 -0.032502 0.010113 0.423599 1.000000 0.004487 0.058482 -0.040339 0.001629 -0.001623
Y0-14 -0.230020 0.408655 -0.408655 0.114328 0.587611 0.030934 -0.625508 0.355966 0.190862 0.267448 -0.031192 0.004487 1.000000 0.799845 -0.918239 -0.501443 0.501393
Y15-64 -0.238346 0.419215 -0.419215 0.145253 0.602708 0.112671 -0.648121 0.327308 0.197412 0.279741 0.028323 0.058482 0.799845 1.000000 -0.972147 -0.456173 0.448086
Y65-109 0.247084 -0.436180 0.436180 -0.140484 -0.627132 -0.086422 0.671895 -0.354964 -0.204785 -0.289013 -0.006508 -0.040339 -0.918239 -0.972147 1.000000 0.496798 -0.491442
ELENA UGOLINI_perc 0.015725 -0.354845 0.354845 -0.250653 -0.336862 -0.112369 0.399415 -0.358102 -0.146252 -0.280764 0.092067 0.001629 -0.501443 -0.456173 0.496798 1.000000 -0.996785
MICHELE DE PASCALE_perc -0.010727 0.357340 -0.357340 0.253006 0.328903 0.112556 -0.393014 0.362669 0.143987 0.282247 -0.092287 -0.001623 0.501393 0.448086 -0.491442 -0.996785 1.000000
In [25]:
# Plotting the correlation matrix for visual representation
plt.figure(figsize=(12, 10))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt=".2f", linewidths=0.5)
plt.title("Correlation Matrix Heatmap")
plt.show()
No description has been provided for this image
In [26]:
del data['males']
del data[f'{candidato2}_perc']

Analisi Cluster¶

In [27]:
data_kmeans = data.copy()
In [28]:
scaler = StandardScaler()
data_std = scaler.fit_transform(data_kmeans)
kmeans = KMeans(n_clusters=4,init='k-means++',random_state=42)
kmeans.fit(data_std)
data_kmeans.reset_index(inplace=True)
data_kmeans['Segment k-means'] = kmeans.labels_
data_kmeans['segment'] = data_kmeans['Segment k-means'].map(
    {
       0:'primo',
       1:'secondo',
       2:'terzo',
       3:'quarto'  
    }
)
In [29]:
data_out = comuni_adv[['pro_com_t','geometry']].merge(data_kmeans)
data_out.explore(column='segment',cmap=['green','red','pink','magenta'],
            legend=True,legend_kwds={'caption':'Cluster'})
Out[29]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Analisi Cluster - PCA¶

In [30]:
data = pd.read_csv(os.path.join(elezioni_path,'CSV',f'data_{regione_sel}.csv'),sep=';',dtype={'pro_com_t':str})
data.set_index('pro_com_t',inplace=True)
In [31]:
data.columns
Out[31]:
Index(['comune', 'area_km2', 'females', 'males', 'total', 'AGRICULTURAL',
       'ARTIFICIAL_NON_AGRICULTURAL_VEGETATED', 'FOREST_AND_SEMI_NATURAL',
       'INDUSTRIAL_COMMERCIAL_AND_TRANSPORT', 'MINE_DUMP_AND_CONSTRUCTION',
       'URBAN', 'WATER_BODIES', 'WETLANDS', 'Y0-14', 'Y15-64', 'Y65-109',
       'ELENA UGOLINI_perc', 'MICHELE DE PASCALE_perc'],
      dtype='object')
In [32]:
to_drop =[f'{candidato2}_perc',
                       'males',
                       'MINE_DUMP_AND_CONSTRUCTION',
                       'WATER_BODIES',
                       'WETLANDS','comune']
In [33]:
to_drop = ['area_km2',  'males', 'AGRICULTURAL',
       'ARTIFICIAL_NON_AGRICULTURAL_VEGETATED', 'FOREST_AND_SEMI_NATURAL',
       'INDUSTRIAL_COMMERCIAL_AND_TRANSPORT', 'MINE_DUMP_AND_CONSTRUCTION',
        'WATER_BODIES', 'WETLANDS', f'{candidato2}_perc','comune']
In [34]:
X = data.drop(columns=to_drop)
scaler = StandardScaler()
X_std = scaler.fit_transform(X)
pca = PCA()
pca.fit(X_std)
Out[34]:
PCA()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
PCA()
In [35]:
plt.figure(figsize=(10,8))
plt.plot(range(1,len(pca.explained_variance_ratio_)+1),pca.explained_variance_ratio_.cumsum(), marker='o',linestyle='--')
plt.xlabel('Number of components')
plt.ylabel('Cumulative explained variance')
plt.title('Explained variance by components')
Out[35]:
Text(0.5, 1.0, 'Explained variance by components')
No description has been provided for this image
In [36]:
componenti_pca = 5
pca = PCA(n_components=componenti_pca)
pca.fit(X_std)
scores_pca = pca.transform(X_std)
In [37]:
cos2 = pd.DataFrame(np.square(pca.components_))
cos2.columns = X.columns
cos2['component'] = cos2.reset_index().index +1
In [38]:
wcss = list()
for i in range(1,21):
    kmeans_pca = KMeans(n_clusters=i,init='k-means++',random_state=42)
    kmeans_pca.fit(scores_pca)
    wcss.append(kmeans_pca.inertia_)
In [39]:
plt.figure(figsize=(10,8))
plt.plot(range(1,21), wcss,marker='o',linestyle='--')
plt.xlabel('Number of clusters')
plt.ylabel('WCSS')
plt.title('k-means with PCA Clustering')
Out[39]:
Text(0.5, 1.0, 'k-means with PCA Clustering')
No description has been provided for this image
In [40]:
numero_cluster = 4
kmeans_pca = KMeans(n_clusters=numero_cluster,init='k-means++',random_state=42)
kmeans_pca.fit(scores_pca)
Out[40]:
KMeans(n_clusters=4, random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
KMeans(n_clusters=4, random_state=42)
In [41]:
X_pca_kmeans = pd.concat([data.reset_index(),pd.DataFrame(scores_pca)], axis='columns')
columns_rename = {k:f'Componente {k+1}' for k in range(componenti_pca)}
X_pca_kmeans.rename(columns=columns_rename, inplace=True)
X_pca_kmeans['Segment k-means PCA'] = kmeans_pca.labels_
X_pca_kmeans['segment'] = X_pca_kmeans['Segment k-means PCA'].map(
    {
       0:'primo',
       1:'secondo',
       2:'terzo',
       3:'quarto',
       4:'quinto'
    }
)
In [42]:
# Calcola i loadings
loadings = pca.components_.T * np.sqrt(pca.explained_variance_)
loadings_df = pd.DataFrame(loadings, columns=[f'PC{i+1}' for i in range(len(pca.components_))], index=X.columns)
In [43]:
# Plotting the correlation matrix for visual representation
plt.figure(figsize=(12, 10))
sns.heatmap(loadings_df, annot=True, cmap='coolwarm', fmt=".2f", linewidths=0.5)
plt.title("Correlation Matrix Heatmap")
plt.show()
No description has been provided for this image
In [44]:
# x_axis = X_pca_kmeans['Componente 2']
# y_axis = X_pca_kmeans['Componente 1']
plt.figure(figsize=(10,8))
sns.scatterplot(
    data = X_pca_kmeans,
    x='Componente 2',
    y='Componente 1',
    hue='segment')
plt.title('Clusters by PCA Components')
plt.show()
No description has been provided for this image
In [45]:
X_out = comuni_adv[['pro_com_t','comune','geometry']].merge(X_pca_kmeans)
X_out.explore(column='segment',legend=True,legend_kwds={'caption':'Cluster by PCA'})
Out[45]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [ ]: