Skip to content

Instantly share code, notes, and snippets.

@aborruso
Created August 10, 2017 16:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aborruso/7dbe7937c3ebcbae172b132a876c8f85 to your computer and use it in GitHub Desktop.
Save aborruso/7dbe7937c3ebcbae172b132a876c8f85 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%bash\n",
"folder=$(pwd)\n",
"\n",
"rm \"$folder\"/azioniPianoTriennaleTMP.csv\n",
"\n",
"for i in 0 1; do\n",
" if [ \"$i\" = 0 ]; then\n",
" ammi=\"centrali\"\n",
" else\n",
" ammi=\"territoriali\"\n",
" fi\n",
" curl -sL \"https://pianotriennale-ict.italia.it/azioni/\" | \\\n",
" scrape -be \"//*[@id=\\\"panel-$i\\\"]\" | \\\n",
" xmlstarlet sel --html -t -m \"//*/div/div[2]/span\" -v \".\" -o \"|\" -v \"normalize-space(../../../../h3)\" -o \"|\" \\\n",
" -v \"normalize-space(../../div[4])\" -o \"|\" -v \"normalize-space(../../div[6]/div)\" -o \"|\" -v \"normalize-space(../../div[8])\" -o \"|$ammi\" -n | \\\n",
" csvformat -d \"|\" >> \"$folder\"/azioniPianoTriennaleTMP.csv\n",
"done\n",
"\n",
"sed -i '1s/^/Tempi,Azione,Attori,Descrizione,Risultati,Amministrazioni\\n/' \"$folder\"/azioniPianoTriennaleTMP.csv"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import pandas as pd\n",
"\n",
"# `locale` verrà usato per convertire le date con nomi dei mesi in italiano da stringhe \n",
"# a campi data (ad esempio da `1 luglio 2015` a `2015-06-01 )\n",
"import locale\n",
"\n",
"# sudo apt-get install language-pack-it\n",
"# sudo dpkg-reconfigure locales\n",
"locale.setlocale(locale.LC_ALL, 'it_IT.UTF-8')\n",
"from pandas.tseries.offsets import MonthEnd\n",
"import numpy as np\n",
"import re"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"df=pd.read_csv('azioniPianoTriennaleTMP.csv')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# creo il campo `da` per tutte le celle in cui è definito un tempo di partenza\n",
"df['da']=df[df.Tempi.str.contains('^da .*? a .*?$')]['Tempi']\n",
"df['da']=df[df.Tempi.str.contains('^da ')]['Tempi']\n",
"\n",
"# creo il campo `a` per tutte le celle in cui il tempo di fine è definito come \"da ... a dicembre 2015\"\n",
"df['a']=df[df.Tempi.str.contains('^da .*? a .*?$')]['Tempi']\n",
"\n",
"# creo il campo `entro` per tutte le celle in cui il tempo di fine è definito come \"entro dicembre 2015\"\n",
"df['entro']=df[df.Tempi.str.contains('^entro ')]['Tempi']"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"df['da']=df.da.replace({r'^(da .*?)( )(a .*?)$' : r'\\1'}, regex=True)\n",
"df['da']=df.da.replace({r'^da ' : r'1 '}, regex=True)\n",
"df['a']=df.a.replace({r'^(da .*? )(a .*?)$' : r'\\2'}, regex=True)\n",
"df['a']=df.a.replace({r'^a ' : r'25 '}, regex=True)\n",
"df['entro']=df.entro.replace({r'^entro ' : r'25 '}, regex=True)\n",
"df['da']=pd.to_datetime(df[~df['da'].isnull()]['da'], format='%d %B %Y')\n",
"df['a']=pd.to_datetime(df[~df['a'].isnull()]['a'], format='%d %B %Y')\n",
"df['entro']=pd.to_datetime(df[~df['entro'].isnull()]['entro'], format='%d %B %Y')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Tempi</th>\n",
" <th>entro</th>\n",
" <th>da</th>\n",
" <th>a</th>\n",
" <th>inCorso</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>in corso</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>da giugno 2017 a dicembre 2018</td>\n",
" <td>NaT</td>\n",
" <td>2017-06-01</td>\n",
" <td>2018-12-25</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>entro marzo 2018</td>\n",
" <td>2018-03-25</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Tempi entro da a inCorso\n",
"0 in corso NaT NaT NaT 1\n",
"1 da giugno 2017 a dicembre 2018 NaT 2017-06-01 2018-12-25 0\n",
"2 entro marzo 2018 2018-03-25 NaT NaT 0"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# creo la colonna `inCorso` per poter distingure le azioni \"in corso\" da tutte le altre\n",
"df['inCorso']=0\n",
"df.loc[df['Tempi'].str.contains('in corso'), 'inCorso'] = 1\n",
"# output di esempio\n",
"df[['Tempi','entro','da','a','inCorso']].head(3)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Tempi</th>\n",
" <th>entro</th>\n",
" <th>da</th>\n",
" <th>a</th>\n",
" <th>inCorso</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>in corso</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>da giugno 2017 a dicembre 2018</td>\n",
" <td>NaT</td>\n",
" <td>2017-06-01</td>\n",
" <td>2018-12-31</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>entro marzo 2018</td>\n",
" <td>2018-03-31</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Tempi entro da a inCorso\n",
"0 in corso NaT NaT NaT 1\n",
"1 da giugno 2017 a dicembre 2018 NaT 2017-06-01 2018-12-31 0\n",
"2 entro marzo 2018 2018-03-31 NaT NaT 0"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# riporto le colonne `entro` e `a` all'ultimo giorno del mese\n",
"df['entro'] = df['entro'] + MonthEnd(1)\n",
"df['a'] = df['a'] + MonthEnd(1)\n",
"# output di esempio\n",
"df[['Tempi','entro','da','a','inCorso']].head(3)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0 PA\n",
"1 AgID|PA\n",
"2 AgID|Consip\n",
"Name: Attori, dtype: object"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# nella colonna con gli attori, imposto il pipe come separatore tra valori\n",
"df['Attori']=df.Attori.replace({r' - ' : r'|'}, regex=True)\n",
"# esempio di output\n",
"df['Attori'][0:3]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>AzioneNumero</th>\n",
" <th>Azione</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>Attuazione delle indicazioni fornite nella Cir...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2</td>\n",
" <td>Censimento patrimonio ICT della PA</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3</td>\n",
" <td>Qualificazione di soluzioni SaaS erogabili sul...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" AzioneNumero Azione\n",
"0 1 Attuazione delle indicazioni fornite nella Cir...\n",
"1 2 Censimento patrimonio ICT della PA\n",
"2 3 Qualificazione di soluzioni SaaS erogabili sul..."
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# A ogni azione è associato un numero. Creo la colonna `AzioneNumero` per raccoglierlo\n",
"df['AzioneNumero']=df.Azione.replace({r'^([0-9]{1,})( )(.*)$' : r'\\1'}, regex=True)\n",
"# rimuovo il numero azione dalla colonna `Azione`\n",
"df['Azione']=df.Azione.replace({r'^([0-9]{1,})( )(.*)$' : r'\\3'}, regex=True)\n",
"# esempio di output\n",
"df[['AzioneNumero','Azione']][0:3]"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# cambio l'ordine delle colonne\n",
"colonne=['Tempi','entro','da','a','inCorso','AzioneNumero','Amministrazioni','Azione','Attori','Descrizione','Risultati']\n",
"df=df[colonne]"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"['(data di rilascio: giugno 2017)',\n",
" '(data di rilascio: giugno 2017)',\n",
" '(data di rilascio: settembre 2017)',\n",
" '(data di rilascio: entro marzo 2018)']"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# creo una colonna per raccogliere in una lista tutte le date di rilascio\n",
"df['Rilascio'] = df['Risultati'].str.findall(r'(\\(data di rilascio:.*?\\))')\n",
"# esempio del contenuto di una cella delle colonna `Rilascio`\n",
"df[df['Rilascio'].str.len() > 0]['Rilascio'].values[0]"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# trasformo in serie le liste archiviate nella colonna `Rilascio` e le raccolgo in un dataframe\n",
"rilasci = df['Rilascio'].apply(pd.Series)\n",
"# rinomino le colonne create sopra da 0, 1, 2, ... a Rilascio_0, Rilascio_1, Rilascio_2, ...\n",
"rilasci = rilasci.rename(columns=lambda x: re.sub('(^[0-9]$)','Rilascio_\\\\1',str(x)))\n",
"# rimuovo la stringa \"data di rilascio:\"\n",
"rilasci=rilasci.replace({r'(\\(data di rilascio:)(.*?)(\\))' : r'\\2'}, regex=True)\n",
"\n",
"# unisco il dataframe creato per contenere le varie date di rilascio, con il dataframe principale\n",
"df = pd.concat([df,rilasci], axis = 1)\n",
"\n",
"# rimuovo la colonna `Rilascio`\n",
"df.drop(['Rilascio'], axis=1, inplace=True)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Tempi</th>\n",
" <th>entro</th>\n",
" <th>da</th>\n",
" <th>a</th>\n",
" <th>inCorso</th>\n",
" <th>AzioneNumero</th>\n",
" <th>Amministrazioni</th>\n",
" <th>Azione</th>\n",
" <th>Attori</th>\n",
" <th>Descrizione</th>\n",
" <th>Risultati</th>\n",
" <th>Rilascio_0</th>\n",
" <th>Rilascio_1</th>\n",
" <th>Rilascio_2</th>\n",
" <th>Rilascio_3</th>\n",
" <th>Rilascio_4</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>22</th>\n",
" <td>entro giugno 2017</td>\n",
" <td>2017-06-30</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>0</td>\n",
" <td>23</td>\n",
" <td>centrali</td>\n",
" <td>Individuazione delle basi di dati chiave</td>\n",
" <td>AgID|Team digitale</td>\n",
" <td>Individuazione di basi di dati chiave di parti...</td>\n",
" <td>Elenco delle basi di dati chiave (data di rila...</td>\n",
" <td>giugno 2017</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>entro luglio 2017</td>\n",
" <td>2017-07-31</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>0</td>\n",
" <td>14</td>\n",
" <td>centrali</td>\n",
" <td>Pubblicazione elenco basi di dati di interesse...</td>\n",
" <td>AgID|PA</td>\n",
" <td>AgID renderà disponibile sul proprio sito isti...</td>\n",
" <td>Schede informative (data di rilascio: giugno 2...</td>\n",
" <td>giugno 2017</td>\n",
" <td>luglio 2017</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75</th>\n",
" <td>entro luglio 2017</td>\n",
" <td>2017-07-31</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>0</td>\n",
" <td>14</td>\n",
" <td>territoriali</td>\n",
" <td>Pubblicazione elenco basi di dati di interesse...</td>\n",
" <td>AgID|PA</td>\n",
" <td>AgID renderà disponibile sul proprio sito isti...</td>\n",
" <td>Schede informative (data di rilascio: giugno 2...</td>\n",
" <td>giugno 2017</td>\n",
" <td>luglio 2017</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>32</th>\n",
" <td>entro settembre 2017</td>\n",
" <td>2017-09-30</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>0</td>\n",
" <td>33</td>\n",
" <td>centrali</td>\n",
" <td>Disegno dell’architettura del sistema ComproPA</td>\n",
" <td>Ministero dell'Economia e delle Finanze|Minist...</td>\n",
" <td>MEF, MIT, ANAC, AgID, Consip, Regioni e ANCI, ...</td>\n",
" <td>Definizione delle regole e disegno dell’infras...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>90</th>\n",
" <td>entro settembre 2017</td>\n",
" <td>2017-09-30</td>\n",
" <td>NaT</td>\n",
" <td>NaT</td>\n",
" <td>0</td>\n",
" <td>33</td>\n",
" <td>territoriali</td>\n",
" <td>Disegno dell’architettura del sistema ComproPA</td>\n",
" <td>Ministero dell'Economia e delle Finanze|Minist...</td>\n",
" <td>MEF, MIT, ANAC, AgID, Consip, Regioni e ANCI, ...</td>\n",
" <td>Definizione delle regole e disegno dell’infras...</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Tempi entro da a inCorso AzioneNumero \\\n",
"22 entro giugno 2017 2017-06-30 NaT NaT 0 23 \n",
"13 entro luglio 2017 2017-07-31 NaT NaT 0 14 \n",
"75 entro luglio 2017 2017-07-31 NaT NaT 0 14 \n",
"32 entro settembre 2017 2017-09-30 NaT NaT 0 33 \n",
"90 entro settembre 2017 2017-09-30 NaT NaT 0 33 \n",
"\n",
" Amministrazioni Azione \\\n",
"22 centrali Individuazione delle basi di dati chiave \n",
"13 centrali Pubblicazione elenco basi di dati di interesse... \n",
"75 territoriali Pubblicazione elenco basi di dati di interesse... \n",
"32 centrali Disegno dell’architettura del sistema ComproPA \n",
"90 territoriali Disegno dell’architettura del sistema ComproPA \n",
"\n",
" Attori \\\n",
"22 AgID|Team digitale \n",
"13 AgID|PA \n",
"75 AgID|PA \n",
"32 Ministero dell'Economia e delle Finanze|Minist... \n",
"90 Ministero dell'Economia e delle Finanze|Minist... \n",
"\n",
" Descrizione \\\n",
"22 Individuazione di basi di dati chiave di parti... \n",
"13 AgID renderà disponibile sul proprio sito isti... \n",
"75 AgID renderà disponibile sul proprio sito isti... \n",
"32 MEF, MIT, ANAC, AgID, Consip, Regioni e ANCI, ... \n",
"90 MEF, MIT, ANAC, AgID, Consip, Regioni e ANCI, ... \n",
"\n",
" Risultati Rilascio_0 \\\n",
"22 Elenco delle basi di dati chiave (data di rila... giugno 2017 \n",
"13 Schede informative (data di rilascio: giugno 2... giugno 2017 \n",
"75 Schede informative (data di rilascio: giugno 2... giugno 2017 \n",
"32 Definizione delle regole e disegno dell’infras... NaN \n",
"90 Definizione delle regole e disegno dell’infras... NaN \n",
"\n",
" Rilascio_1 Rilascio_2 Rilascio_3 Rilascio_4 \n",
"22 NaN NaN NaN NaN \n",
"13 luglio 2017 NaN NaN NaN \n",
"75 luglio 2017 NaN NaN NaN \n",
"32 NaN NaN NaN NaN \n",
"90 NaN NaN NaN NaN "
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# ordino il dataframe\n",
"df.sort_values(by=['inCorso','entro','da','AzioneNumero','Amministrazioni'],inplace=True)\n",
"\n",
"# applico la funzione di strip a tutte le colonne di tipo testuale, rimuovendo eventuali spaziature inutili\n",
"df_obj = df.select_dtypes(['object'])\n",
"df[df_obj.columns] = df_obj.apply(lambda x: x.str.strip())\n",
"\n",
"# salvo il dataframe in CSV, rimuovento l'indice di pandas e applicando l'utf-8 come encoding\n",
"df.to_csv('azioniPianoTriennale.csv',index=False,encoding='utf-8')\n",
"#output di esempio (`NaT` è Not a Time, è il null per i campi data)\n",
"df.head(5)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:py35]",
"language": "python",
"name": "conda-env-py35-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment