Questioni di genere: ecco quali ruoli ricoprono le donne nelle aziende italiane

Questo documento racconta la metodologia usata per rispondere a questa domanda: ci sono differenze di genere nelle posizioni di responsabilità delle società di capitale italiane?
Questo percorso, che è un notebook IPython, è il processo che abbiamo fatto per rispondere alla domanda, a partire dai dati di Atoka (atoka.io), il nostro servizio di Sales Intelligence.

Un pizzico di data science, alcuni script in Python, la libreria Pandas, ottima per le analisi dei dati ed ecco la formula per capire le differenze di genere nelle aziende.

Indice dei contenuti

  1. Come abbiamo lavorato
  2. Una prima occhiata ai dati
  3. Donne al comando
  4. Distribuzione di genere per area geografica
  5. Il mondo delle StartUp
  6. Distribuzione di genere per settore d'attività
  7. Distribuzione di genere per ordini di fatturato

Come abbiamo lavorato

Lo scopo di questa ricerca è capire la distribuzione di genere nelle aziende di capitale, in base a questi fattori:

  • posizione geografica (provincia e regione);
  • settore di attività (secondo la categoria ATECO);
  • fatturato (secondo la classificazione europea);
  • iscrizione al registro delle StartUp.


Come abbiamo raccontato in un approfondimento nel blog, Atoka si basa su un'infrastruttura particolarmente complessa, che permette di gestire la grande mole di dati (Big Data) delle 6 milioni di aziende italiane.
Per garantire scalabilità e velocità nei tempi di risposta del servizio, utilizziamo un cluster Elastic Search per l'indicizzazione dei dati.
La fonte dei dati presenti in questo documento è uno degli indici ES di Atoka (ES corrisponde a Elastic Search, per brevità).

Dopo l'interrogazione di questo indice, sono risultate circa un milione di aziende con forma giuridica pari a "società di capitale", usando questa query (le query fatte su ES presentano una sintassi JSON-style):

{  
   "query":{  
      "constant_score":{  
         "filter":{  
            "term":{  
               "legalStatusClass":"società di capitale"
            }
         }
      }
   }
}

Di ciascuna azienda di capitale, abbiamo poi analizzato gli esponenti che ricoprono queste cariche (secondo la fonte ufficiale (Cerved)), con la relativa numerosità:

  • amministratore unico (1.018.930)
  • consigliere (939.394)
  • presidente del consiglio di amministrazione (297.024)
  • socio unico (180.805)
  • amministratore (135.957)
  • amministratore delegato (107.407)
  • vice presidente del consiglio di amministrazione (100.127)
  • consigliere delegato (73.707)
  • presidente (27.069)
  • vice presidente (15.451)
  • membro del consiglio direttivo (6.708)
  • presidente del consiglio direttivo (5.058)
  • membro del comitato direttivo (3.818)

Tenendo presente che alcune persone possono ricoprire più cariche, ad esempio, essere consigliere e amministratore delegato allo stesso tempo.

Per ciascuna delle aziende selezionate, abbiamo estratto il numero di persone che ricoprono i ruoli sopra indicati, dividendoli per genere.
Abbiamo poi calcolato la percentuale di aziende (divisa per tipologia), in cui una donna ricopre uno dei ruoli indicati (oppure un loro sottoinsieme).

In [1]:
import json
In [2]:
from collections import Counter
In [3]:
import pandas as pd
In [4]:
soc_cap = pd.read_csv('societa_capitale.csv.bz2')

Queste sono le colonne relative alla nostra estrazione, così si inizia a capire di cosa stiamo parlando:

In [5]:
soc_cap.columns.values.tolist()
Out[5]:
['acheneID',
 'ateco_code',
 'province',
 'isStartup',
 'last_revenue',
 'amministratore_unico_M',
 'amministratore_unico_F',
 'consigliere_M',
 'consigliere_F',
 'presidente_consiglio_amministrazione_M',
 'presidente_consiglio_amministrazione_F',
 'socio_unico_M',
 'socio_unico_F',
 'amministratore_M',
 'amministratore_F',
 'amministratore_delegato_M',
 'amministratore_delegato_F',
 'vice_presidente_consiglio_amministrazione_M',
 'vice_presidente_consiglio_amministrazione_F',
 'consigliere_delegato_M',
 'consigliere_delegato_F',
 'presidente_M',
 'presidente_F',
 'vice_presidente_M',
 'vice_presidente_F',
 'membro_consiglio_direttivo_M',
 'membro_consiglio_direttivo_F',
 'presidente_consiglio_direttivo_M',
 'presidente_consiglio_direttivo_F',
 'membro_comitato_direttivo_M',
 'membro_comitato_direttivo_F']

Nel file estratto, troviamo anche l'acheneID, un codice identificativo che usiamo internamente a SpazioDati, e che rimuoveremo prima di iniziare l'analisi.
Per capire nel dettaglio cosa abbiamo estratto, osserviamo ad esempio il risultato dell'interrogazione dell'azienda SpazioDati:

In [6]:
SD = soc_cap[soc_cap['acheneID'] == 'http://data.spaziodati.eu/resource/6da785b3adf219770c9eed00219c595f69dc2fde']
In [7]:
zip(SD.columns.values.tolist()[1:], SD.values[0].tolist()[1:])
Out[7]:
[('ateco_code', '62.01'),
 ('province', 'TN'),
 ('isStartup', True),
 ('last_revenue', 243000),
 ('amministratore_unico_M', 0),
 ('amministratore_unico_F', 0),
 ('consigliere_M', 5),
 ('consigliere_F', 0),
 ('presidente_consiglio_amministrazione_M', 1),
 ('presidente_consiglio_amministrazione_F', 0),
 ('socio_unico_M', 0),
 ('socio_unico_F', 0),
 ('amministratore_M', 0),
 ('amministratore_F', 0),
 ('amministratore_delegato_M', 1),
 ('amministratore_delegato_F', 0),
 ('vice_presidente_consiglio_amministrazione_M', 1),
 ('vice_presidente_consiglio_amministrazione_F', 0),
 ('consigliere_delegato_M', 0),
 ('consigliere_delegato_F', 0),
 ('presidente_M', 0),
 ('presidente_F', 0),
 ('vice_presidente_M', 0),
 ('vice_presidente_F', 0),
 ('membro_consiglio_direttivo_M', 0),
 ('membro_consiglio_direttivo_F', 0),
 ('presidente_consiglio_direttivo_M', 0),
 ('presidente_consiglio_direttivo_F', 0),
 ('membro_comitato_direttivo_M', 0),
 ('membro_comitato_direttivo_F', 0)]
In [8]:
soc_cap = soc_cap.drop(['acheneID'], axis=1)

Con il comando drop di Pandas, abbiamo eliminato, a questo punto, la colonna contenente l'acheneID, che non è rilevante ai fini della ricerca.

In [9]:
soc_cap.head(20)
Out[9]:
ateco_code province isStartup last_revenue amministratore_unico_M amministratore_unico_F consigliere_M consigliere_F presidente_consiglio_amministrazione_M presidente_consiglio_amministrazione_F ... presidente_M presidente_F vice_presidente_M vice_presidente_F membro_consiglio_direttivo_M membro_consiglio_direttivo_F presidente_consiglio_direttivo_M presidente_consiglio_direttivo_F membro_comitato_direttivo_M membro_comitato_direttivo_F
0 63.91 RM False 190000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1 43.99.02 LT False 3749000 0 1 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
2 93.12 MO False -1 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
3 47.11.2 VT False -1 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
4 14.1 RM False 160000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
5 46.51 RM False -1 0 1 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
6 68.20.01 RM False 2000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
7 47.71 CS False -1 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
8 81.21 AO False 8000 0 0 0 0 1 0 ... 0 0 0 0 0 0 0 0 0 0
9 86.90.21 BA False 236000 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
10 47.62.2 BN False 60000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
11 43.22 RM False 772000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
12 46.42 NaN False 531000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
13 71.12.1 PO False 105000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
14 45.11.01 BO False 404000 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
15 52.29.1 VA False 376000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
16 38.31.1 CE False -1 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
17 71.12.1 MI False -1 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
18 47.77 SS False 469000 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
19 70.1 VI False 0 1 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0

20 righe × 30 colonne

Nota tecnica: nella colonna last_revenue, i valori -1 segnalano la mancanza di dati per quella determinata azienda, il valore 0 indica che il valore esiste, è un valore di controllo interno.


Il comando head della libreria Pandas ci mostra i primi 20 risultati presenti, ed è utile per un'analisi sommaria dei dati.

Una prima occhiata ai dati

Dopo aver estratto il valore assoluto delle aziende e dei ruoli interessati in esse ricoperti, analizziamo la distribuzione in base al genere per ciascuna tipologia di ruolo, inserendo anche il valore percentuale relativo.

I valori percentuali si riferiscono al totale delle aziende in cui figura il ruolo in questione. Ad esempio, per quanto riguarda gli amministratori unici si scopre che:

  • in 783.164 aziende (almeno) un uomo ricopre la carica di amministratore unico, ovvero nel 77.01% delle aziende;
  • in 234.116 aziende (almeno) una donna ricopre la carica di amministratore unico, ovvero nel 23.02% delle aziende.

In questo caso quindi la percentuale del 77.01% indica la percentuale rispetto al totale delle aziende che hanno (almeno) un amministratore unico.

In [10]:
for i in range(4, 30, 2):
    fields = soc_cap.columns.values[i: i+2]
    role = fields[0][:-2].replace('_', ' ')
    print role.upper()
    
    total = soc_cap[(soc_cap[fields[0]] != 0) | (soc_cap[fields[1]] != 0)].shape[0]
    uomini = soc_cap[soc_cap[fields[0]] != 0].shape[0]
    donne = soc_cap[soc_cap[fields[1]] != 0].shape[0]
    
    print 'in {0} aziende (almeno) un uomo ricopre la carica di {1}, \novvero nel {2}% dei casi'.format(uomini, 
                                                                                role, 
                                                                                round(100.0*uomini/float(total), 2))
    print 'in {0} aziende (almeno) una donna ricopre la carica di {1}, \novvero nel {2}% dei casi'.format(donne, 
                                                                                role, 
                                                                                round(100.0*donne/float(total), 2))
    print '\n'
AMMINISTRATORE UNICO
in 783164 aziende (almeno) un uomo ricopre la carica di amministratore unico, 
ovvero nel 77.01% dei casi
in 234116 aziende (almeno) una donna ricopre la carica di amministratore unico, 
ovvero nel 23.02% dei casi


CONSIGLIERE
in 289398 aziende (almeno) un uomo ricopre la carica di consigliere, 
ovvero nel 93.58% dei casi
in 144803 aziende (almeno) una donna ricopre la carica di consigliere, 
ovvero nel 46.82% dei casi


PRESIDENTE CONSIGLIO AMMINISTRAZIONE
in 249606 aziende (almeno) un uomo ricopre la carica di presidente consiglio amministrazione, 
ovvero nel 83.95% dei casi
in 47746 aziende (almeno) una donna ricopre la carica di presidente consiglio amministrazione, 
ovvero nel 16.06% dei casi


SOCIO UNICO
in 134719 aziende (almeno) un uomo ricopre la carica di socio unico, 
ovvero nel 74.82% dei casi
in 45381 aziende (almeno) una donna ricopre la carica di socio unico, 
ovvero nel 25.2% dei casi


AMMINISTRATORE
in 61116 aziende (almeno) un uomo ricopre la carica di amministratore, 
ovvero nel 91.7% dei casi
in 23141 aziende (almeno) una donna ricopre la carica di amministratore, 
ovvero nel 34.72% dei casi


AMMINISTRATORE DELEGATO
in 60210 aziende (almeno) un uomo ricopre la carica di amministratore delegato, 
ovvero nel 89.59% dei casi
in 16934 aziende (almeno) una donna ricopre la carica di amministratore delegato, 
ovvero nel 25.2% dei casi


VICE PRESIDENTE CONSIGLIO AMMINISTRAZIONE
in 74233 aziende (almeno) un uomo ricopre la carica di vice presidente consiglio amministrazione, 
ovvero nel 76.13% dei casi
in 23786 aziende (almeno) una donna ricopre la carica di vice presidente consiglio amministrazione, 
ovvero nel 24.39% dei casi


CONSIGLIERE DELEGATO
in 37533 aziende (almeno) un uomo ricopre la carica di consigliere delegato, 
ovvero nel 88.1% dei casi
in 12891 aziende (almeno) una donna ricopre la carica di consigliere delegato, 
ovvero nel 30.26% dei casi


PRESIDENTE
in 23279 aziende (almeno) un uomo ricopre la carica di presidente, 
ovvero nel 89.85% dei casi
in 2669 aziende (almeno) una donna ricopre la carica di presidente, 
ovvero nel 10.3% dei casi


VICE PRESIDENTE
in 12163 aziende (almeno) un uomo ricopre la carica di vice presidente, 
ovvero nel 83.76% dei casi
in 2458 aziende (almeno) una donna ricopre la carica di vice presidente, 
ovvero nel 16.93% dei casi


MEMBRO CONSIGLIO DIRETTIVO
in 1776 aziende (almeno) un uomo ricopre la carica di membro consiglio direttivo, 
ovvero nel 96.84% dei casi
in 631 aziende (almeno) una donna ricopre la carica di membro consiglio direttivo, 
ovvero nel 34.41% dei casi


PRESIDENTE CONSIGLIO DIRETTIVO
in 4552 aziende (almeno) un uomo ricopre la carica di presidente consiglio direttivo, 
ovvero nel 89.68% dei casi
in 525 aziende (almeno) una donna ricopre la carica di presidente consiglio direttivo, 
ovvero nel 10.34% dei casi


MEMBRO COMITATO DIRETTIVO
in 1078 aziende (almeno) un uomo ricopre la carica di membro comitato direttivo, 
ovvero nel 95.91% dei casi
in 304 aziende (almeno) una donna ricopre la carica di membro comitato direttivo, 
ovvero nel 27.05% dei casi


E' interessante osservare la percentuale di aziende in cui almeno una donna ricopre il ruolo di amministratore delegato.
Ruolo che può essere ricoperto da più di un unico amministratore delegato per la stessa azienda.
A questo punto, è meglio ricalcolare le percentuali rimuovendo tutte quelle aziende che hanno diversi amministratori delegati, in modo da verificare chi è da solo al "comando" di un'azienda.

Donne al comando

In [11]:
fields = ['amministratore_delegato_M', 'amministratore_delegato_F']
one_ceo_companies = soc_cap[(soc_cap[fields[0]] == 1) ^ (soc_cap[fields[1]] == 1)]
n_one_ceo_companies = one_ceo_companies.shape[0]

n_uomini = one_ceo_companies[one_ceo_companies[fields[0]] == 1].shape[0]
n_donne = one_ceo_companies[one_ceo_companies[fields[1]] == 1].shape[0]

print 'Gli uomini soli al comando sono {0}, ovvero il {1}% dei casi'.format(n_uomini, 
100.0*n_uomini/n_one_ceo_companies)
print 'Le donne sole al comando sono {0}, ovvero il {1}% dei casi'.format(n_donne, 
100.0*n_donne/n_one_ceo_companies)
Gli uomini soli al comando sono 33.561, ovvero il 79,5322053178% dei casi, mentre
le donne sole al comando sono 8.637, ovvero il 20,4677946822% dei casi.
	

Entrambe le percentuali vengono ridimensionate rispetto al calcolo fatto in precedenza, in particolare la presenza per quanto riguarda le donne, scende a 1 su 5.

In [12]:
fields = ['amministratore_delegato_M', 'amministratore_delegato_F']
donne =  soc_cap[soc_cap[fields[1]] > 0]
n_donne = donne.shape[0]
shared = donne[(donne[fields[1]] > 1) | (donne[fields[0]] > 0)].shape[0]
print 'Su un totale di {0} casi, il {1}% delle volte le donne sono affiancate da qualcun altro.'.format(n_donne,
                                                                                                        round(100*shared/float(n_donne), 2))


Quindi il 64,79% delle volte le donne sono affiancate da qualcun altro, su un totale di 16.934 casi.

Vediamo cosa accade se calcoliamo la stessa statistica per gli uomini.

In [13]:
fields = ['amministratore_delegato_M', 'amministratore_delegato_F']
uomini =  soc_cap[soc_cap[fields[0]] > 0]
n_uomini = uomini.shape[0]
shared = uomini[(uomini[fields[0]] > 1) | (uomini[fields[1]] > 0)].shape[0]
print 'Su un totale di {0} casi, il {1}% delle volte gli uomini sono affiancati da qualcun altro'.format(n_uomini, 
                                                                                                         round(100*shared/float(n_uomini), 2))
Su un totale di 60.210 casi, il 45.58% delle volte gli uomini sono affiancati da qualcun altro.

Distribuzione di genere per area geografica

Aggreghiamo ora i dati per provincia e regione, analizzando eventuali disomogeneità legate alla distribuzione geografica delle aziende.

Per semplicità non filtreremo tra i ruoli, ma valuteremo se una società presenta una qualche donna o meno tra i membri del suo consiglio di amministrazione (allargato).

Come prima cosa, è necessario eliminare dai risultati quelle aziende di cui non è presente la provincia (nella fonte sono 86.458). La provincia che consideriamo è quella che risulta dal registro delle imprese (ovvero il campo REA.provinceCode presente nell'indice di Atoka).

Aggiungiamo ora il dato sulle regioni nel file di partenza.

In [14]:
province_file = open('province.csv', 'r')
In [15]:
province_labels = {}
In [16]:
for line in province_file.readlines():
    regione, codice, provincia = line.strip().split(',')
    province_labels[codice] = {
        'regione': regione,
        'provincia': provincia
    }
In [17]:
df_geo = soc_cap[pd.notnull(soc_cap['province'])]
In [18]:
df_geo.loc[:, 'regione'] = df_geo.loc[:, 'province'].apply(lambda p: province_labels[p]['regione'])

Province

In [19]:
grouped_by_provincia = df_geo.groupby('province')
In [20]:
campi_uomini = soc_cap.columns.values[range(4, 30, 2)]
campi_donne = soc_cap.columns.values[range(5, 30, 2)]
In [21]:
df_provincia_aggs = pd.DataFrame()
for group in grouped_by_provincia:
    totale_provincia = group[1].shape[0]
    totale_uomini = group[1][group[1].apply(lambda row: sum(row[campi_uomini]) > 0, axis=1)].shape[0]
    totale_donne = group[1][group[1].apply(lambda row: sum(row[campi_donne]) > 0, axis=1)].shape[0]
    df_provincia_aggs = df_provincia_aggs.append([{
                'provincia': group[0],
                'provincia_full': province_labels[group[0]]['provincia'],
                'totale_provincia': totale_provincia,
                'totale_uomini': totale_uomini,
                'totale_donne': totale_donne,
                'perc_uomini': round(totale_uomini/float(totale_provincia), 2),
                'perc_donne': round(totale_donne/float(totale_provincia), 2)
            }])
In [22]:
df_provincia_aggs.shape
Out[22]:
(104, 7)
In [23]:
df_provincia_aggs.head(5)
Out[23]:
perc_donne perc_uomini provincia provincia_full totale_donne totale_provincia totale_uomini
0 0.30 0.83 AG Agrigento 2063 6961 5758
0 0.34 0.84 AL Alessandria 2359 6957 5876
0 0.34 0.83 AN Ancona 3248 9420 7813
0 0.32 0.87 AO Aosta 709 2248 1960
0 0.29 0.83 AP Ascoli Piceno 1526 5322 4427

Ordiniamo i risultati, in base alle province che presentano il maggior numero di aziende con una donna che ricopre almeno uno tra i ruoli considerati.

In [24]:
df_provincia_aggs.sort(columns='totale_provincia', ascending=False)[['provincia', 
                                                                     'provincia_full', 
                                                                     'totale_donne']][:20]
Out[24]:
provincia provincia_full totale_donne
0 RM Roma 51229
0 MI Milano 41332
0 TO Torino 12264
0 BA Bari 7904
0 BS Brescia 9232
0 BG Bergamo 8228
0 FI Firenze 8497
0 SA Salerno 6534
0 BO Bologna 8729
0 CT Catania 6555
0 PD Padova 6783
0 VR Verona 6555
0 VI Vicenza 6996
0 PA Palermo 6455
0 CE Caserta 5052
0 MB Monza-Brianza 6105
0 MO Modena 6626
0 TV Treviso 6152
0 VA Varese 5858
0 GE Genova 5216

Calcoliamo ora le percentuali.

In [25]:
df_provincia_aggs.sort(columns='perc_donne', ascending=False)[['provincia', 
                                                               'provincia_full',
                                                               'perc_donne']][:20]
Out[25]:
provincia provincia_full perc_donne
0 BO Bologna 0.36
0 BI Biella 0.36
0 SI Siena 0.36
0 SV Savona 0.36
0 LC Lecco 0.35
0 CN Cuneo 0.35
0 TR Terni 0.35
0 AR Arezzo 0.35
0 AT Asti 0.34
0 PO Prato 0.34
0 GR Grosseto 0.34
0 AL Alessandria 0.34
0 NO Novara 0.34
0 TO Torino 0.34
0 PG Perugia 0.34
0 LI Livorno 0.34
0 VA Varese 0.34
0 AN Ancona 0.34
0 MO Modena 0.34
0 FI Firenze 0.33

A questo punto, calcoliamo quali sono le province con il minor numero di donne presenti nelle posizioni analizzate.

In [26]:
df_provincia_aggs.sort(columns='perc_donne', ascending=True)[['provincia', 
                                                              'provincia_full',
                                                              'perc_donne']][:20]
Out[26]:
provincia provincia_full perc_donne
0 VV Vibo Valentia 0.24
0 FG Foggia 0.24
0 CE Caserta 0.25
0 KR Crotone 0.25
0 CS Cosenza 0.25
0 BA Bari 0.25
0 CZ Catanzaro 0.25
0 RC Reggio di Calabria 0.26
0 SA Salerno 0.26
0 PZ Potenza 0.26
0 BZ Bolzano 0.26
0 AV Avellino 0.26
0 BN Benevento 0.27
0 MT Matera 0.27
0 BR Brindisi 0.27
0 TE Teramo 0.27
0 RM Roma 0.27
0 EN Enna 0.28
0 CH Chieti 0.28
0 TA Taranto 0.28

Il dato geografico sembra netto.

Anche se il nostro scopo è osservare la presenza delle donne, per completezza di ricerca analizziamo le percentuali di presenze maschili nei consigli di amministrazioni allargati: quali sono le 20 province che hanno più uomini in questi ruoli? Quali le 20 province che ne hanno meno?

In [27]:
df_provincia_aggs.sort(columns='perc_uomini', ascending=False)[['provincia', 
                                                                'provincia_full',
                                                                'perc_uomini']][:20]
Out[27]:
provincia provincia_full perc_uomini
0 BZ Bolzano 0.92
0 BL Belluno 0.90
0 TN Trento 0.89
0 PN Pordenone 0.89
0 CN Cuneo 0.89
0 SO Sondrio 0.88
0 RE Reggio nell'Emilia 0.88
0 UD Udine 0.87
0 TS Trieste 0.87
0 BI Biella 0.87
0 TV Treviso 0.87
0 GE Genova 0.87
0 VI Vicenza 0.87
0 AO Aosta 0.87
0 TO Torino 0.86
0 FO Forli-Cesena 0.86
0 GO Gorizia 0.86
0 RA Ravenna 0.86
0 BS Brescia 0.86
0 LC Lecco 0.86
In [28]:
df_provincia_aggs.sort(columns='perc_uomini', ascending=True)[['provincia', 
                                                               'provincia_full',
                                                               'perc_uomini']][:20]
Out[28]:
provincia provincia_full perc_uomini
0 LT Latina 0.76
0 FR Frosinone 0.77
0 TA Taranto 0.77
0 BN Benevento 0.78
0 RI Rieti 0.78
0 CT Catania 0.79
0 PE Pescara 0.79
0 TP Trapani 0.79
0 VT Viterbo 0.79
0 AV Avellino 0.79
0 LE Lecce 0.79
0 SR Siracusa 0.79
0 IS Isernia 0.79
0 RM Roma 0.79
0 CE Caserta 0.80
0 CA Cagliari 0.80
0 SA Salerno 0.80
0 NU Nuoro 0.80
0 AQ L'Aquila 0.80
0 TE Teramo 0.80

Regioni

Passiamo ad un livello più macroscopico: le regioni.

In [29]:
grouped_by_regione = df_geo.groupby('regione')
In [30]:
df_regione_aggs = pd.DataFrame()
for group in grouped_by_regione:
    totale_regione = group[1].shape[0]
    totale_uomini = group[1][group[1].apply(lambda row: sum(row[campi_uomini]) > 0, axis=1)].shape[0]
    totale_donne = group[1][group[1].apply(lambda row: sum(row[campi_donne]) > 0, axis=1)].shape[0]
    df_regione_aggs = df_regione_aggs.append([{
                'regione': group[0],
                'totale_regione': totale_regione,
                'totale_uomini': totale_uomini,
                'totale_donne': totale_donne,
                'perc_uomini': round(totale_uomini/float(totale_regione), 2),
                'perc_donne': round(totale_donne/float(totale_regione), 2)
            }])
In [31]:
df_regione_aggs
Out[31]:
perc_donne perc_uomini regione totale_donne totale_regione totale_uomini
0 0.28 0.80 Abruzzo 8486 30053 24079
0 0.26 0.82 Basilicata 2799 10643 8754
0 0.25 0.81 Calabria 7783 30639 24747
0 0.26 0.80 Campania 16004 61583 49011
0 0.33 0.85 Emilia-Romagna 33930 101781 86979
0 0.32 0.87 Friuli-Venezia Giulia 6832 21026 18387
0 0.28 0.79 Lazio 62545 226315 177932
0 0.32 0.85 Liguria 9099 28352 24124
0 0.31 0.85 Lombardia 85333 279284 236110
0 0.31 0.83 Marche 10356 33619 28047
0 0.28 0.80 Molise 1764 6251 5011
0 0.34 0.86 Piemonte 22505 66555 57065
0 0.26 0.81 Puglia 19686 75402 60900
0 0.30 0.80 Sardegna 9671 32048 25733
0 0.30 0.80 Sicilia 28526 94099 75488
0 0.33 0.83 Toscana 29813 89639 74458
0 0.28 0.91 Trentino-Alto Adige 5190 18614 16885
0 0.34 0.83 Umbria 6348 18482 15319
0 0.32 0.87 Valle d'Aosta 709 2248 1960
0 0.32 0.86 Veneto 33706 105234 90758

Il mondo delle StartUp

Concentriamoci sul mondo delle StartUp e verifichiamo se si concentrano davvero nei settori IT, analizzando i primi dieci codici ATECO, ordinati per numerosità di aziende.

In [32]:
ateco_labels = json.load(open('ateco.json', 'r'))
In [33]:
for code, fq in Counter(soc_cap[soc_cap['isStartup'] == True]['ateco_code'].values).most_common(10):
    print ateco_labels[code]
    print fq
produzione di software non connesso all'edizione
1027
portali web
317
ricerca e sviluppo sperimentale nel campo delle altre scienze naturali e dell'ingegneria
309
ricerca e sviluppo sperimentale nel campo delle scienze naturali e dell'ingegneria
192
consulenza nel settore delle tecnologie dell'informatica
187
altre attività dei servizi connessi alle tecnologie dell'informatica nca
158
attività degli studi di architettura, ingegneria ed altri studi tecnici
133
altre attività di consulenza imprenditoriale e altra consulenza amministrativo-gestionale e pianificazione aziendale
111
ricerca e sviluppo sperimentale nel campo delle biotecnologie
104
commercio al dettaglio di qualsiasi tipo di prodotto effettuato via internet
100

Calcoliamo i valori percentuali ed assoluti sulla presenza maschile/femminile, assieme alla percentuale relativa alle posizioni apicali inserite in questo elenco (che indicheremo nei dati con un _top):

  • amministratore unico,
  • presidente del consiglio di amministrazione,
  • socio unico,
  • amministratore,
  • amministratore delegato,
  • vice presidente del consiglio di amministrazione,
  • presidente,
  • vice presidente,
  • presidente del consiglio direttivo.
In [34]:
grouped_by_isStartup = soc_cap.groupby('isStartup')
In [35]:
df_isStartup_aggs = pd.DataFrame()
for group in grouped_by_isStartup:
    totale_classe = group[1].shape[0]
    totale_uomini = group[1][group[1].apply(lambda row: sum(row[campi_uomini]) > 0, axis=1)].shape[0]
    totale_donne = group[1][group[1].apply(lambda row: sum(row[campi_donne]) > 0, axis=1)].shape[0]
    totale_uomini_top = group[1][group[1].apply(lambda row: sum(row[campi_uomini[[0,2,3,4,5,6,8,9,11]]]) > 0, axis=1)].shape[0]
    totale_donne_top = group[1][group[1].apply(lambda row: sum(row[campi_donne[[0,2,3,4,5,6,8,9,11]]]) > 0, axis=1)].shape[0]
    df_isStartup_aggs = df_isStartup_aggs.append([{
                'isStartup': group[0],
                'totale_classe': totale_classe,
                'totale_uomini': totale_uomini,
                'totale_donne': totale_donne,
                'totale_uomini_top': totale_uomini_top,
                'totale_donne_top': totale_donne_top, 
                'perc_uomini': round(totale_uomini/float(totale_classe), 2),
                'perc_donne': round(totale_donne/float(totale_classe), 2),
                'perc_uomini_top': round(totale_uomini_top/float(totale_classe), 2),
                'perc_donne_top': round(totale_donne_top/float(totale_classe), 2)
            }])
In [36]:
df_isStartup_aggs
Out[36]:
isStartup perc_donne perc_donne_top perc_uomini perc_uomini_top totale_classe totale_donne totale_donne_top totale_uomini totale_uomini_top
0 False 0.30 0.24 0.83 0.81 1413637 421445 343469 1166864 1139474
0 True 0.25 0.18 0.90 0.87 4688 1173 839 4196 4063

Distribuzione di genere per settore d'attività

Ora calcoliamo la distribuzione di genere per settore d'attività, usando come indicatore il codice ATECO. Anche qui, approfondiamo l'analisi sui ruoli al vertice. Prima, però, bisogna rimuovere alcune aziende dall'insieme di dati iniziale, il cui codice ATECO è ignoto.

In [37]:
df_ateco = soc_cap[pd.notnull(soc_cap['ateco_code'])]
In [38]:
grouped_by_ateco = df_ateco.groupby('ateco_code')
In [39]:
df_ateco_aggs = pd.DataFrame()
for group in grouped_by_ateco:
    totale_ateco = group[1].shape[0]
    totale_uomini = group[1][group[1].apply(lambda row: sum(row[campi_uomini]) > 0, axis=1)].shape[0]
    totale_donne = group[1][group[1].apply(lambda row: sum(row[campi_donne]) > 0, axis=1)].shape[0]
    totale_uomini_top = group[1][group[1].apply(lambda row: sum(row[campi_uomini[[0,2,3,4,5,6,8,9,11]]]) > 0, axis=1)].shape[0]
    totale_donne_top = group[1][group[1].apply(lambda row: sum(row[campi_donne[[0,2,3,4,5,6,8,9,11]]]) > 0, axis=1)].shape[0]
    df_ateco_aggs = df_ateco_aggs.append([{
                'ateco_code': group[0],
                'ateco_label': ateco_labels[group[0]],
                'totale_ateco': totale_ateco,
                'totale_uomini': totale_uomini,
                'totale_donne': totale_donne,
                'totale_uomini_top': totale_uomini_top,
                'totale_donne_top': totale_donne_top, 
                'perc_uomini': round(totale_uomini/float(totale_ateco), 2),
                'perc_donne': round(totale_donne/float(totale_ateco), 2),
                'perc_uomini_top': round(totale_uomini_top/float(totale_ateco), 2),
                'perc_donne_top': round(totale_donne_top/float(totale_ateco), 2)
            }])

Dato che alcuni settori ATECO sono scarsamente popolati nel territorio italiano, filtriamo un numero minimo di risultati, pari ad almeno 500 aziende italiane presenti in quel settore di attività.
Per capire il contesto di questa soglia, è utile avere un'idea di massima sulla distribuzione di aziende per settore:

In [40]:
df_ateco_aggs['totale_ateco'].describe()
Out[40]:
count      1602.000000
mean        831.455056
std        4275.519332
min           1.000000
25%          39.000000
50%         166.000000
75%         596.000000
max      115044.000000
Name: totale_ateco, dtype: float64

E finalmente possiamo rispondere alla domanda iniziale e capire i settori più o meno rosa.

In [41]:
df_ateco_aggs[df_ateco_aggs['totale_donne'] > 500].sort(columns='perc_donne', ascending=False)[['ateco_code',
                                                                                                'ateco_label',
                                                                                                'perc_donne',
                                                                                                'totale_donne',
                                                                                                'perc_donne_top',
                                                                                                'totale_donne_top']][:20]
Out[41]:
ateco_code ateco_label perc_donne totale_donne perc_donne_top totale_donne_top
0 88.91 servizi di asili nido; assistenza diurna per m... 0.86 1466 0.81 1392
0 88 assistenza sociale non residenziale 0.76 2718 0.59 2113
0 85.1 istruzione prescolastica 0.75 798 0.69 737
0 87.9 altre strutture di assistenza sociale residenz... 0.71 665 0.58 545
0 88.1 assistenza sociale non residenziale per anzian... 0.71 1521 0.59 1275
0 88.99 altre attività di assistenza sociale non resid... 0.69 1194 0.55 957
0 96.02.02 servizi degli istituti di bellezza 0.62 1673 0.61 1631
0 87.3 strutture di assistenza residenziale per anzia... 0.59 751 0.49 620
0 47.71.2 commercio al dettaglio di confezioni per bambi... 0.57 671 0.55 647
0 87.1 strutture di assistenza infermieristica reside... 0.54 693 0.45 577
0 47.71.3 commercio al dettaglio di biancheria personale... 0.53 711 0.51 683
0 96.04.1 servizi di centri per il benessere fisico (esc... 0.52 651 0.50 618
0 79.11 attività delle agenzie di viaggio 0.48 2341 0.42 2047
0 93.29.9 altre attività di intrattenimento e di diverti... 0.47 859 0.39 709
0 70.21 pubbliche relazioni e comunicazione 0.47 1873 0.29 1154
0 86.90.12 laboratori di analisi cliniche 0.46 684 0.39 578
0 82.99.4 richiesta certificati e disbrigo pratiche 0.46 811 0.39 684
0 96.02.01 servizi dei saloni di barbiere e parrucchiere 0.45 1010 0.42 945
0 47.75.1 commercio al dettaglio di articoli di profumer... 0.44 757 0.41 700
0 86.22 servizi degli studi medici specialistici 0.44 636 0.37 535
In [42]:
df_ateco_aggs[df_ateco_aggs['totale_donne'] > 500].sort(columns='perc_donne', ascending=True)[['ateco_code',
                                                                                               'ateco_label',
                                                                                               'perc_donne',
                                                                                               'totale_donne',
                                                                                               'perc_donne_top',
                                                                                               'totale_donne_top']][:20]
Out[42]:
ateco_code ateco_label perc_donne totale_donne perc_donne_top totale_donne_top
0 35.11 produzione di energia elettrica 0.18 1918 0.13 1375
0 41.2 costruzione di edifici residenziali e non resi... 0.19 22092 0.17 19445
0 43.21 installazione di impianti elettrici 0.20 806 0.16 645
0 46.51 commercio all'ingrosso di computer, apparecchi... 0.21 1011 0.16 756
0 62.02 consulenza nel settore delle tecnologie dell'i... 0.21 970 0.16 738
0 62.01 produzione di software non connesso all'edizione 0.21 3010 0.14 2090
0 45.1 commercio di autoveicoli 0.21 672 0.19 592
0 43.99.09 altre attività di lavori specializzati di cost... 0.21 608 0.18 511
0 43.22 installazione di impianti idraulici, di riscal... 0.21 977 0.16 731
0 43.21.01 installazione di impianti elettrici in edifici... 0.21 2547 0.17 2074
0 71.12.1 attività degli studi di ingegneria 0.22 551 0.16 389
0 46.31 commercio all'ingrosso di frutta e ortaggi fre... 0.23 1198 0.17 877
0 45.20.2 riparazione di carrozzerie di autoveicoli 0.23 605 0.18 476
0 45.20.1 riparazioni meccaniche di autoveicoli 0.23 788 0.18 611
0 45.11.01 commercio all'ingrosso e al dettaglio di autov... 0.23 2378 0.19 1954
0 43.32.02 posa in opera di infissi, arredi, controsoffit... 0.23 560 0.20 489
0 43.22.01 installazione di impianti idraulici, di riscal... 0.23 1140 0.18 878
0 42.11 costruzione di strade e autostrade 0.23 797 0.19 634
0 25.12.1 fabbricazione di porte, finestre e loro telai,... 0.24 908 0.20 741
0 71.1 attività degli studi di architettura, ingegner... 0.24 2630 0.18 2017

Distribuzione di genere per fatturato

Per finire, ripetiamo l'analisi tenendo conto della divisione micro/piccola/media/grande impresa, come da definizione europea, ovvero:

In [43]:
mapRevenue_Size = {
    'micro': [0, 2*10**6],
    'piccola': [2*10**6, 10*10**6],
    'media': [10*10**6, 50*10**6],
    'grande': [50*10**6, float('Inf')]
}

Escludiamo tutte le aziende che hanno un valore di fatturato assente oppure nullo nell'insieme dei dati di partenza.

In [44]:
df_revenues = soc_cap[soc_cap['last_revenue'] > 0]
In [45]:
df_revenues.shape
Out[45]:
(795535, 30)
In [46]:
def revenue_label(revenue):
    for k, v in mapRevenue_Size.items():
        if v[0] < revenue <= v[1]:
            return k
In [47]:
df_revenues.loc[:,'size'] = df_revenues.loc[:,'last_revenue'].apply(lambda revenue: revenue_label(revenue))
In [48]:
grouped_by_size = df_revenues.groupby('size')
In [49]:
df_size_aggs = pd.DataFrame()
for group in grouped_by_size:
    totale_size = group[1].shape[0]
    totale_uomini = group[1][group[1].apply(lambda row: sum(row[campi_uomini]) > 0, axis=1)].shape[0]
    totale_donne = group[1][group[1].apply(lambda row: sum(row[campi_donne]) > 0, axis=1)].shape[0]
    totale_uomini_top = group[1][group[1].apply(lambda row: sum(row[campi_uomini[[0,2,3,4,5,6,8,9,11]]]) > 0, axis=1)].shape[0]
    totale_donne_top = group[1][group[1].apply(lambda row: sum(row[campi_donne[[0,2,3,4,5,6,8,9,11]]]) > 0, axis=1)].shape[0]
    df_size_aggs = df_size_aggs.append([{
                'size': group[0],
                'totale_size': totale_size,
                'totale_uomini': totale_uomini,
                'totale_donne': totale_donne,
                'totale_uomini_top': totale_uomini_top,
                'totale_donne_top': totale_donne_top, 
                'perc_uomini': round(totale_uomini/float(totale_size), 2),
                'perc_donne': round(totale_donne/float(totale_size), 2),
                'perc_uomini_top': round(totale_uomini_top/float(totale_size), 2),
                'perc_donne_top': round(totale_donne_top/float(totale_size), 2)
            }])
In [50]:
df_size_aggs
Out[50]:
perc_donne perc_donne_top perc_uomini perc_uomini_top size totale_donne totale_donne_top totale_size totale_uomini totale_uomini_top
0 0.42 0.16 0.99 0.95 grande 2242 852 5349 5285 5102
0 0.37 0.18 0.96 0.93 media 8103 3821 21627 20792 20027
0 0.33 0.27 0.81 0.79 micro 221736 180836 681179 555022 539591
0 0.34 0.20 0.92 0.88 piccola 29394 17774 87380 79957 77136

Le percentuali delle presenze di donne e uomini sono calcolate sul totale delle aziende (la colonna totale_size), a loro volta divise per fatturato secondo la classificazione europea. Le due percentuali relative alla presenza femminile hanno tendenze divergenti. Sembra che le aziende con ricavi maggiori vantino una presenza femminile maggiore, ma allo stesso tempo più marginale.
Le micro imprese (spesso citate come la spina dorsale del paese dai media) hanno un comportamento apparentemente più coerente: dai dati risulta che le donne presenti in questa tipologia di aziende ricoprono spesso ruoli di responsabilità, a parità di presenza.