{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": true }, "source": [ "

Table of Contents

\n", "
" ] }, { "cell_type": "markdown", "metadata": { "header": true }, "source": [ "\n", "
\n", "D. Malchiodi, Superhero data science. Vol 1: probabilità e statistica: Introduzione a python.\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "
\n", "\n", "# Introduzione a python\n", "\n", "Questo capitolo descrive brevemente i principali strumenti che permettono di analizzare dati in modo esplorativo usando python come linguaggio di programmazione, jupyter come ambiente di elaborazione e introducendo le principali librerie del cosiddetto _python data science stack_ via via che queste si riveleranno necessarie." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "## I tipi di dati semplici e gli operatori\n", "In python non esiste il concetto di *dichiarazione*: quando si vuole utilizzare una variabile le si assegna un valore, e quest'ultimo determina automaticamente il tipo della variabile e di conseguenza quali operazioni si possono effettuare su di essa, ovviamene solo fino a quando non vi venga memorizzato un nuovo valore o il riferimento a un oggettoIl linguaggio è comunque fortemente tipizzato, ma il _type checking_ viene fatto durante l'esecuzione.. I tipi di dati fondamentali sono quello booleano (`bool`, che fa riferimento alle costanti `True` e `False`), quello intero (`int`) e quello a virgola mobile (`float`). Ciò significa dunque che in python, a differenza di altri linguaggi, non esiste il tipo carattere: vedremo più avanti che le stringhe sono direttamente implementate come oggetti.\n", "\n", "Il tipo di dato intero permette di memorizzare numeri interi, che si esprimono come successioni di una o più cifre eventualmente precedute da `+` (che si può omettere) o da `-` per indicarne il segno. Il tipo di dato a virgola mobile permette di memorizzare numeri con la virgola; anche in questo caso si utilizza di norma la notazione tipica in ambito informatico: si indica il segno, seguito dalle cifre intere, dal carattere `.` e dalle cifre decimali. Nel caso in cui si debbano specificare dei valori molto grandi o molto piccoli risulta però più pratico l'utilizzo della notazione _scientifica_ o _esponenziale_: si indica un valore di _mantissa_ (con o senza virgola) seguito dal carattere `E` (o `e`) e da un numero intero detto _esponente_, e tale espressione genera il valore numerico pari al prodotto della mantissa per `10` elevato all'esponente. Pertanto `1E9` e `1E-9` indicano rispettivamente un miliardo e un miliardesimo." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "42" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "int(42)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se avete dubbi sul tipo di un'espressione, la funzione `type` restituisce il tipo corrispondente." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "int" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "first_appearance = 1971\n", "type(first_appearance)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "float" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "weight = 71.6\n", "type(weight)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "bool" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A partire da valori booleani, interi o a virgola mobile è possibile costruire espressioni arbitrariamente complesse utilizzando degli _operatori_. La maggior parte di quelli che considereremo è di tipo _binario_ (cioè si applicano a due argomenti) e si utilizzano in modalità _infissa_ (il che significa che l'operatore si indica in mezzo ai suoi due argomenti). Gli operatori vengono utilizzati nella maggior parte dei linguaggi per codificare le operazioni aritmetico/logiche e le relazioni aritmetiche (utilizzando `+` per l'addizione, `!=` per la relazione di non uguaglianza e così via). La tabella seguente riassume i principali simboli utilizzati in python per questo tipo di operatori binari.\n", "\n", "| Operazione | Simbolo |\n", "|--------------------|-----------|\n", "|addizione | `+` |\n", "|sottrazione | `-` |\n", "|moltiplicazione | `*` |\n", "|divisione (reale) | `/` |\n", "|divisione (intera) | `//` |\n", "|resto (modulo) | `%` |\n", "|elevamento a potenza| `**` |\n", "|uguale | `==` |\n", "|diverso | `!=` |\n", "|minore | `<` |\n", "|minore o uguale | `<=` |\n", "|maggiore | `>` |\n", "|maggiore o uguale | `>=` |\n", "\n", "Risulta opportuno sottolineare che esistono due diversi simboli per codificare la divisione tra numeri reali e quella tra numeri interi. In altre parole, la valutazione di `/` sarà sempre un numero in virgola mobile, anche nel caso in cui i due argomenti dovessero essere due interi e il primo fosse un multiplo del secondo. In molti linguaggi di programmazione (comprese le versioni di python precedenti alla 3.0) viene invece utilizzato il simbolo `/` per entrambe le divisioni, e in fase di esecuzione è il tipo degli operandi a stabilire quali delle due verrà effettivamente calcolata.\n", "\n", "Sebbene la maggior parte degli operatori che considereremo sono di tipo binario, ne esistono anche di altri tipi: il simbolo `-` è un esempio di operatore _unario_ che cambia il segno dell'espressione che lo segue (esiste anche l'analogo, ma poco utile, operatore `+`). Vedremo più avanti che esiste anche uno speciale operatore _ternario_.\n", "\n", "## I tipi di dati strutturati\n", "\n", "Il linguaggio supporta nativamente i seguenti tipi strutturati: le liste, le tuple, le stringhe, gli insiemi e i dizionari. Questi tipi sono brevemente descritti nei paragrafi seguenti." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "### Le liste\n", "In python, una lista è una struttura dati *eterogenea* e ad *accesso posizionale*: pertanto in essa viene memorizzata tipicamente una sequenza di elementi, che possono essere di tipo diverso e a cui è possibile accedere direttamente specificando la corrispondente posizione. Una lista si può indicare in modo _estensivo_ separando i suoi elementi tramite virgola e racchiudendo il tutto tra parentesi quadre." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nei nostri esperimento faremo riferimento a un _dataset_ ottenuto modificando un opportuno sottoinsieme del [Superhero database](http://www.superherodb.com). Gli esempi faranno quindi riferimento al mondo dei supereroi, ognuno dei quali sarà descritto tramite:\n", "\n", "- il suo nome,\n", "- la sua identità,\n", "- il luogo in cui è nato,\n", "- l'editore dei corrispondenti fumetti/film/serie tv,\n", "- l'altezza in cm.,\n", "- il peso in kg.,\n", "- il genere,\n", "- l'anno della prima apparizione,\n", "- il colore degli occhi,\n", "- il colore dei capelli,\n", "- un indice di forza (in una scala quantitativa da 0 a 100),\n", "- un indice di intelligenza (in una scala qualitativa i cui valori sono low, moderate, average, good, e high).\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "La proprietà di eterogeneità ci permette di usare una lista per aggregare le informazioni che descrivono un supereroe:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "list" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iron_man = ['Iron Man',\n", " 'Tony Stark',\n", " 'Long Island, New York',\n", " 'Marvel Comics',\n", " 198.51,\n", " 191.85,\n", " 'M',\n", " 1963,\n", " 'Blue',\n", " 'Black',\n", " 85,\n", " 'high']\n", "\n", "type(iron_man)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In effetti vedremo che ci sono modi molto più interessanti di codificare un _record_ di informazioni, così come ci renderemo conto che nei fatti le liste contengono di norma valori di tipo omogeneo, ma per ora quello che ci interessa è semplicemente vedere quali sono le modalità principali di utilizzo di questo tipo di struttura dati.\n", "\n", "Per accedere a un elemento in una lista, basta specificare dopo una variabile che la referenzia (ma ovviamente si potrebbe utilizzare anche la lista stessa) una coppia di parentesi quadre contenente la posizione dell'elemento, conteggiata a partire da `0`:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Tony Stark'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iron_man[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se si specifica un valore negativo per la posizione, a questo viene automaticamente sommata la lunghezza della lista. Pertanto la posizione `-1` identifica l'ultimo elemento della lista, la posizione `-2` corrisponde al penultimo e così via:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "85" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iron_man[-2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "È anche possibile indicare un intervallo di posizioni per recuperare la sottolista corrispondente: questa operazione, che prende il nome di *list slicing*, si effettua indicando tra parentesi quadre la posizione del primo elemento da inserire, seguita da un carattere di due punti e dalla posizione del primo elemento da escludere. Il peso e l'altezza di Tony Stark, essendo memorizzati in quinta e sesta posizione, si potranno quindi ottenere congiuntamente nel seguente modo (ricordando che gli indici delle posizioni partono da zero):" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[198.51, 191.85]" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iron_man[4:6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il riferimento a un _list slicing_ si può fare anche utilizzando indici negativi (o mescolando indici positivi e negativi):" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Blue', 'Black']" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iron_man[-4:-2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dover specificare la posizione del primo elemento da escludere sembra controintuitivo rispetto alla scelta più naturale di indicare la posizione dell'ultimo elemento da includere. In realtà in questo modo risulta più facile scrivere codice che elabora porzioni successive in una lista. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le liste sono inoltre una struttura dati a dimensione *dinamica*, nel senso che oltre a modificare gli elementi in essa contenuti è anche possibile rimuovere uno o più di tali elementi, oppure aggiungerne di nuovi. Python mette a disposizione varie operazioni che agiscono sulle liste: il paragrafo che segue introduce alcuni esempi, ma il suo scopo è più quello di chiarire la differenza tra i concetti di operatori, funzioni e metodi. Per approfondire l'argomento è invece possibile consultare la documentazione ufficiale, che contiene un [documento introduttivo](https://docs.python.org/3/tutorial/introduction.html#lists) e uno [più dettagliato](https://docs.python.org/3/tutorial/datastructures.html#) sull'uso delle liste." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "### Operatori, funzioni e metodi per le liste\n", "In python è possibile usare le liste (ma anche gli altri tipi di dati che vedremo più avanti) come\n", "* argomenti di operatori,\n", "* argomenti di funzioni,\n", "* oggetti su cui invocare metodi.\n", "\n", "Per esemplificare l'uso di questi tre tipi di strumenti conviene ragionare in termini di una lista utilizzata come se fosse un array, memorizzando dunque una successione di valori dello stesso tipo, per esempio i nomi di alcuni supereroi:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "names = ['Aquaman', 'Ant-Man', 'Batman', 'Black Widow',\n", " 'Captain America', 'Daredavil', 'Elektra', 'Flash',\n", " 'Green Arrow', 'Human Torch', 'Hancock', 'Iron Man',\n", " 'Mystique', 'Professor X', 'Rogue', 'Superman',\n", " 'Spider-Man', 'Thor', 'Northstar']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Operatori\n", "Python mette inoltre a disposizione l'operatore binario `in` che implementa la relazione di appartenenza: se `e` è un'espressione e `l` una lista, l'espressione `e in l` viene valutata vera se il valore dell'espressione occorre in una posizione qualsiasi della lista e falsa altrimenti." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'Thing' in names" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'Human Torch' in names" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un altro operatore (unario) specifico per le liste è `del`, che permette di eliminare un elemento da una lista indicandone la relativa posizione: per esempio, eseguendo la cella seguente viene cancellata la stringa contenuta nella prima posizione di `names`." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "del names[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Va sottolineato come `del` non restituisca un valore: successivamente alla sua esecuzione, la lista corrispondente avrà un elemento in meno, e in questo specifico caso quello che prima era il secondo elemento diventa ora il primo, e così via:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Ant-Man'" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "names[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Funzioni\n", "Python prevede inoltre alcune funzioni che elaborano liste, come per esempio `len` che restituisce il numero di elementi contenuti in una lista, a cui si fa di norma riferimento denotandolo come la _lunghezza_ della lista stessa:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "18" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(names)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Metodi\n", "Python è un linguaggio di programmazione che implementa (anche) il paradigma orientato a oggetti, e le liste (così come gli altri tipi di dati che vedremo più avanti) sono a tutti gli effetti oggetti su cui è possibile invocare metodi. Supponiamo di voler mettere in ordine alfabetico i nomi dei supereroi (la lista è quasi in ordine, l'unico elemento fuori posto è l'ultimo): la corrispondente operazione di ordinamento richiede di invocare sulla lista il metodo `sort` (usando la _dot notation tipica_ della programmazione orientata agli oggetti)." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "names.sort()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Così come l'operatore `del`, tale metodo però non restituisce alcun valore, in quanto l'ordinamento è eseguito _in place_: dopo l'invocazione, gli elementi della lista saranno stati riposizionati in modo da riflettere l'ordinamento. Possiamo convincercene facilmente visualizzando per esempio gli ultimi cinque elementi di `names`:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Professor X', 'Rogue', 'Spider-Man', 'Superman', 'Thor']" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "names[-5:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'invocazione di metodi (e di funzioni) prevede in python anche la possibilità di specificare degli argomenti _opzionali_: si tratta di argomenti, identificati da un nome, che possono essere omessi, e in tal caso assumono un valore predefinito all'atto dell'invocazione. Per poterne specificare un valore diverso da quello predefinito è sufficiente indicare, dopo gli eventuali altri argomenti, un'espressione del tipo `=`, separando tramite virgola la specificazione di più argomenti opzionali. Per esempio il metodo `sort` effettua l'ordinamento in verso non decrescente, e l'argomento opzionale `reversed` permette di invertire tale verso:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "names.sort(reverse=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un'altra caratteristica di python è quella di poter specificare una funzione come argomento di un metodo (o di un'altra funzione); ciò si può fare o indicando il nome della funzione, oppure usando una _lambda function_ o _funzione anonima_: una funzione che viene definita senza darle un nome ma definendo direttamente come i suoi argomenti devono essere trasformati nel valore da restituire. Più precisamente, la sintassi `lambda x: ` definisce una funzione che ha un argomento il cui nome simbolico è `x` e che restituisce l'espressione dopo il carattere di due punti (che di norma dipenderà da `x`).\n", "\n", "Un esempio che mette insieme l'uso di argomenti opzionali e di funzioni anonime si trova nella cella seguente, in cui la lista dei nomi viene ordinata non in modo alfabetico, bensì in funzione della lunghezza dei nomi stessi, specificando tramite l'argomento opzionale `key` una funzione anonima che trasformerà ogni elemento della lista in un valore su cui basare l'ordinamento." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "successore = lambda n: n+1\n", "successore(9)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Thor',\n", " 'Rogue',\n", " 'Flash',\n", " 'Batman',\n", " 'Hancock',\n", " 'Elektra',\n", " 'Ant-Man',\n", " 'Superman',\n", " 'Mystique',\n", " 'Iron Man',\n", " 'Northstar',\n", " 'Daredavil',\n", " 'Spider-Man',\n", " 'Professor X',\n", " 'Human Torch',\n", " 'Green Arrow',\n", " 'Black Widow',\n", " 'Captain America']" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "names.sort(key=lambda n:len(n))\n", "names" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Un altro metodo invocabile su una lista è `insert`, che permette di aggiungere un elemento a una lista esistente, specificando rispettivamente come secondo e primo argomento l'elemento da aggiungere e la posizione in cui inserirlo: per esempio nella cella seguente viene re-inserito Aquaman in modo da mantenere `names` in orine" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Thor', 'Rogue', 'Flash', 'Batman', 'Aquaman', 'Hancock']" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "names.insert(4, 'Aquaman')\n", "names[:6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "### Le tuple\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Una tupla è una lista immutabile: una volta creata non è possibile modificare i suoi contenuti. Una tupla viene indicata in modo analogo a una lista, con l'unica differenza che i suoi contenuti sono delimitati da parentesi tonde." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "rogue = ('Rogue',\n", " 'Anna Marie',\n", " 'Caldecott County, Mississippi',\n", " 'Marvel Comics',\n", " 173.1,\n", " 54.39,\n", " 'F',\n", " 1981,\n", " 'Green',\n", " 'Brown / White',\n", " 10,\n", " 'good')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'accesso a un elemento di una tupla viene fatto in modo posizionale usando la medesima sintassi introdotta per le liste:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Qualora si tenti di modificare un elemento in una tupla, l'esecuzione verrà però bloccata emettendo un errore:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Non si possono modificare gli elementi di una tupla\n" ] } ], "source": [ "try:\n", " rogue[-2] = 70\n", "except TypeError:\n", " print('Non si possono modificare gli elementi di una tupla')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Va notato che in python gli errori di esecuzione vengono emessi utilizzando il meccanismo delle eccezioni, che nella cella precedente vengono gestite in modo analogo a quanto succede per esempio in Java: il blocco di istruzioni coinvolto è quello che segue la parola chiave `try`, e le istruzioni dopo `except` vengono eseguite solo se viene lanciata un eccezione del tipo specificato. A seguito di questo errore, la tupla manterrà i suoi valori originali, restando quindi effettivamente invariata:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('Rogue',\n", " 'Anna Marie',\n", " 'Caldecott County, Mississippi',\n", " 'Marvel Comics',\n", " 173.1,\n", " 54.39,\n", " 'F',\n", " 1981,\n", " 'Green',\n", " 'Brown / White',\n", " 10,\n", " 'good')" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rogue" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Una tupla può essere utilizzata facendo ri ferimento agli stessi operatori e alle stesse funzioni messi a disposizione per le liste (come per esempio `in` e `len`), escludendo ovviamente le operazioni che modificano la tupla stessa (come `sort`)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'immutabilità delle tuple le rende da preferire rispetto alle liste in tutti i casi in cui si vuole impedire che dei dati vengano modificati, per esempio a causa di un bug; inoltre la loro elaborazione è in molti casi più efficiente di quella delle liste." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Va notato che la sintassi per la descrizione delle tuple diventa problematica quando si vuole indicare una tupla contenente un unico elemento, in quanto per esempio `(1)` viene interpretato come valore `1` tra parentesi tonde. La soluzione in casi come questi è quella di fare seguire l'unico elemento della tupla da una virgola, scrivendo per esempio `(1,)`. Come regola generale, infatti, è possibile aggiungere una virgola alla fine di una tupla (o di una lista) senza variarne i contenuti.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "### Le stringhe\n", "\n", "Le stringhe sono implementate come tuple di caratteri, e quindi su di esse è possibile eseguire tutte le operazioni che si eseguono sulle tuple:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'a'" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "name = rogue[1]\n", "name[3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si verifica facilmente come si tratti di tuple e non di liste, in quanto i contenuti non sono modificabili:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Non si possono modificare i contenuti di una stringa\n" ] } ], "source": [ "try:\n", " name[3] = 'A'\n", "except TypeError:\n", " print('Non si possono modificare i contenuti di una stringa')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Anche per quanto riguarda le liste è possibile approfondire l'argomento consultando la [documentazione ufficiale](https://docs.python.org/3/library/stdtypes.html#string-methods)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "### Gli insiemi\n", "Python implementa direttamente un tipo di dato per gli insiemi, intesi come collezione finita di elementi tra loro distinguibili e non memorizzati in un ordine particolare. A differenza delle liste e delle tuple, gli elementi non sono quindi associati a una posizione e non è possibile che un insieme contenga più di un'istanza di un medesimo elemento. Non utilizzeremo questo tipo di dato, quindi si rimanda alla [documentazione ufficiale](https://docs.python.org/3/library/stdtypes.html#set) per un approfondimento." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "### I dizionari\n", "I dizionari servono a memorizzare delle associazioni tra oggetti, in analogica con il concetto matematico di funzione. È quindi possibile pensare a essi come a insiemi di coppie (chiave, valore), dove una data chiave non occorre più di una volta.\n", "\n", "Un dizionario viene descritto indicando ogni coppia separando chiave e valore con il carattere di due punti, separando le varie coppie con delle virgole e racchiudendo il tutto tra parentesi graffe. Possiamo per esempio usare un dizionario per rappresentare un record in modo più elegante rispetto alla precedente scelta basata sulle liste:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "rogue = {'name': 'Rogue',\n", " 'identity': 'Anna Marie',\n", " 'birth_place': 'Caldecott County, Mississippi',\n", " 'publisher': 'Marvel Comics',\n", " 'height': 173.1,\n", " 'weight': 54.39,\n", " 'gender': 'F',\n", " 'first_appearance': 1981,\n", " 'eye_color': 'Green',\n", " 'hair_color': 'Brown / White',\n", " 'strength': 10,\n", " 'intelligence': 'good'}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'accesso, in lettura o scrittura, agli elementi di un dizionario viene fatto con una notazione che ricorda quella di liste e tuple: si specifica all'interno di parentesi quadre la chiave per ottenere o modificare il valore corrispondente:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Anna Marie'" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rogue['identity']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "È proprio questa modalità di accesso che fa sì che i dizionari rappresentino una scelta più elegante per memorizzare un record: `rogue['identity']` è sicuramente più leggibile di `rogue[1]`. Va notato che il prezzo da pagare per la leggibilità è un'efficienza potenzialmente minore nelle operazioni di accesso (normalmente le liste sono implementate con una logica simile a quella degli array e dunque hanno un tempo di accesso costante ai loro elementi, mentre i dizionari sono implementati tramite tabelle di hash, pertanto l'accesso è a tempo costante solo se non avvengono collisioni).\n", "\n", "Se si tenta di accedere in lettura a un dizionario specificando una chiave inesistente viene lanciata un'eccezione (`KeyError`), mentre accedendovi in scrittura la specificazione di una chiave inesistente comporterà l'aggiunta della corrispondente coppia (chiave, valore) al dizionario. \n", "\n", "L'operatore `in` introdotto per le liste può anche essere utilizzato per i dizionari: più precisamente, l'espressione `k in d` restituisce `True` se `k` è una chiave valida per il dizionario `d`.\n", "\n", "Anche nel caso dei dizionari il linguaggio mette a disposizione una serie di funzioni specifiche, e si può fare riferimento alla [documentazione ufficiale](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) di python per approfondire l'argomento." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "## Strutture di controllo\n", "Python gestisce il flusso di esecuzione tramite le tipiche strutture di controllo di sequenza, selezione e iterazione. La sequenza viene implementata semplicemente indicando le istruzioni, una per riga, in ordine di esecuzione: per esempio la cella seguente crea due liste, una con nomi di supereroi e un'altra con i corrispondenti anni di prima apparizione, e le memorizza nelle variabili `names` e `years`." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "names = ['Aquaman', 'Ant-Man', 'Batman', 'Black Widow',\n", " 'Captain America', 'Daredavil', 'Elektra', 'Flash',\n", " 'Green Arrow', 'Human Torch', 'Hancock', 'Iron Man',\n", " 'Mystique', 'Professor X', 'Rogue', 'Superman',\n", " 'Spider-Man', 'Thor', 'Northstar']\n", "\n", "years = [1941, 1962, None, None, 1941,\n", " 1964, None, 1940, 1941, 1961,\n", " None, 1963, None, 1963, 1981,\n", " None, None, 1962, 1979]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il valore speciale `None` è stato utilizzato nei casi in cui non risulta disponibile l'anno di prima apparizione di un supereroe. In queste situazioni si parla di *valori mancanti* (o si utilizza l'equivalente termine inglese *missing values*) che di norma vengono indicati con la sigla NA (dall'inglese \"not available\"). La scelta di `None` come valore per codificare gli elementi mancanti è puramente arbitraria: l'eterogeneità delle liste ci avrebbe permesso di utilizzare per esempio la stringa `'NA'` o altri valori (anche espressioni numeriche che non indicano un anno). In realtà vedremo più avanti che esistono altre modalità che permettono di memorizzare ed elaborare i dati in modo più agevole.\n", "\n", "Immaginiamo di voler conteggiare, anno per anno, il numero totale di apparizioni, calcolando quelle che in statistica vengono chiamate le _frequenze assolute_ del numero di apparizioni. Un approccio classico è quello di utilizzare un contatore per ogni anno, scandire la lista delle prime apparizioni e incrementare di volta in volta il contatore corrispondente all'anno trovato. Una struttura dati particolarmente adeguata per aggregare i contatori è un dizionario, in cui le chiavi corrispondono agli anni. La scansione di una lista viene effettuata in python da una delle due strutture iterative, il *ciclo for*. A differenza di quanto succede di norma, non si tratta di un ciclo numerato bensì di un ciclo che esegue il suo corpo in corrispondenza di ogni elemento di un *oggetto iterabile*. Liste e tuple sono appunto gli esempi più semplici di oggetti iterabili: immaginando che `lista` sia una lista da scandire (ma andrebbe bene anche una tupla) e che esista una funzione `elabora` che accetta un argomento, la sintassi\n", "```\n", "for elemento in lista:\n", " elabora(elemento)\n", "```\n", "implementa appunto un ciclo for, in cui `elemento` rappresenta una variabile che conterrà a ogni iterazione uno degli elementi. In particolare, liste e tuple vengono scandite in ordine di posizione, quindi `elemento` conterrà il primo elemento di `lista` durante la prima iterazione, il suo secondo elemento durante la seconda iterazione e così via. Notate anche che le funzioni si invocano usando la sintassi tipica basata sull'uso di parentesi tonde. Infine, va sottolineato che la seconda riga inizia più a destra della prima: in molti linguaggi questa tecnica (detta di _indentazione_) ha lo scopo puramente visuale di mettere in evidenza l'istruzione o le istruzioni che vengono ripetute nel ciclo (il _corpo_ del ciclo). In python l'indentazione è invece obbligatoria per indicare quali sono le istruzioni che compongono il corpo del ciclo (cosa che negli altri linguaggi viene fatta utilizzando per esempio le parentesi graffe). Non esiste una regola prefissata che indichi _come_ effettuare l'indentazione: si può usare un carattere di tabulazione, oppure alcuni caratteri di spazio. l'unica limitazione è quella di mantenere la stessa scelta una volta che questa è stata fatta: se si decide di indentare il corpo di un ciclo usando, per esempio, tre spazi, tutte le istruzioni del corpo dovranno essere indentate di tre spazi.\n", "\n", "Tornando al problema di effettuare il conteggio del numero di apparizioni al variare degli anni, un primo tentativo che utilizza un dizionario per memorizzare i relativi contatori potrebbe essere il seguente:\n", "\n", "```\n", "# non funziona!\n", "counts = {}\n", "for y in years:\n", " counts[y] += 1\n", "```\n", "In realtà tale codice non funzionerebbe, perché la prima istruzione crea un dizionario `counts` vuoto, e quindi il primo accesso che verrebbe fatto utilizzerebbe una chiave che non esiste, causando il lancio di un'eccezione. È pertanto necessario verificare di volta in volta che l'anno considerato sia una chiave esistente (il che significa che l'anno considerato è già stato trovato precedentemente, e quindi il corrispondente contatore esiste già e va solamente incrementato) oppure no (e dunque il contatore va inizializzato). È quindi necessario utilizzare l'operatore `in` unitamente a una struttura di selezione, e precisamente una if-else. La sintassi di questa struttura è la seguente:\n", "```\n", "if :\n", " \n", "else:\n", " \n", "```\n", "e la sua semantica è quella che ci si aspetta: la condizione tra la parola chiave `if` e il carattere di due punti viene valutata: se risulta vera viene eseguita l'istruzione alla linea seguente, altrimenti viene eseguita l'istruzione dopo la parola chiave `else`. Anche in questo caso l'indentazione permette di identificare quali istruzioni debbano essere eseguite nei due rami della selezione. La cella seguente contiene un'implementazione (stavolta funzionante) del codice che conteggia le apparizioni per anno." ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "counts = {}\n", "for y in years:\n", " if y in counts:\n", " counts[y] += 1\n", " else:\n", " counts[y] = 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il risultato è il seguente:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{1941: 3,\n", " 1962: 2,\n", " None: 7,\n", " 1964: 1,\n", " 1940: 1,\n", " 1961: 1,\n", " 1963: 2,\n", " 1981: 1,\n", " 1979: 1}" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "counts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notate che una coppia fa riferimento alla chiave `None`, che sarà relativa al numero di casi mancanti. Supponiamo di voler visualizzare i conteggi visualizzando prima l'anno con il maggior numero di apparizioni, per poi procedere in ordine decrescente. Un possibile modo di procedere è quello di \"convertire\" `counts` nella corrispondente tupla di coppie e poi ordinare quest'ultima. La prima operazione si effettua facilmente invocando sul dizionario il metodo `items`. La seconda operazione è più complessa, perché è necessario basare l'ordinamento sul secondo elemento di ogni coppia. Nella cella seguente si utilizza l'argomento opzionale `key` della funzione `sorted` e una funzione anonima per specificare il criterio su cui basare l'ordinamento. L'esempio utilizza anche l'argomento opzionale `reverse` per ottenere gli anni ordinati a partire da quello con il maggior numero di apparizioni." ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(None, 7),\n", " (1941, 3),\n", " (1962, 2),\n", " (1963, 2),\n", " (1964, 1),\n", " (1940, 1),\n", " (1961, 1),\n", " (1981, 1),\n", " (1979, 1)]" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pairs = list(counts.items())\n", "sorted(pairs, key=lambda p:p[1], reverse=True)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "## Funzioni\n", "Il calcolo delle frequenze è un'operazione che viene fatta molto spesso, quindi conviene scrivere una funzione che ci eviti di dover ricopiare ogni volta la decina di linee che abbiamo scritto (in realtà è solo una scusa per vedere come si definiscono le funzioni in python: più avanti vedremo come usare delle librerie per calcolare le frequenze). La definizione di una funzione in python (a parte il caso delle funzioni anonime) viene fatta utilizzando la parola chiave `def` seguita dal nome del metodo e dai nomi simbolici per i suoi argomenti, separati da virgole e racchiusi tra parentesi (fanno eccezione gli eventuali argomenti opzionali, ma di questo non parleremo). La definizione procede con un carattere di due punti e dal corpo della funzione le cui istruzioni devono essere indentate di un livello.\n", "\n", "La cella seguente riporta un esempio di semplice definizione di funzione che mette insieme il codice scritto finora in modo da accettare una generica lista e di restituirne le frequenze assolute ordinate dalla più grande alla più piccola." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(None, 7),\n", " (1941, 3),\n", " (1962, 2),\n", " (1963, 2),\n", " (1964, 1),\n", " (1940, 1),\n", " (1961, 1),\n", " (1981, 1),\n", " (1979, 1)]" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def get_sorted_counts(sequence):\n", " counts = {}\n", "\n", " for x in sequence:\n", " if x in counts:\n", " counts[x] += 1\n", " else:\n", " counts[x] = 1\n", "\n", " pairs = counts.items()\n", " return sorted(pairs, key=lambda p:p[1], reverse=True)\n", "\n", "get_sorted_counts(years)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "## Importare moduli\n", "Il meccanismo con cui in python si organizzano progetti software complessi e si riutilizza il codice è basato sul concetto di _modulo_. In pratica un modulo è un file che contiene la definizione di una o più funzioni o classi. L'importazione può riguardare un intero modulo oppure solo uno (o più) dei suoi elementi. Tramite i moduli è inoltre possibile utilizzare librerie standard o sviluppate da terze parti. Consideriamo per esempio la funzione `get_sorted_counts` che abbiamo appena scritto: se esistesse un dizionario in cui le chiavi inesistenti venissero automaticamente associate a un valore nullo, si potrebbe semplificare notevolmente il corpo della funzione, rendendo corretto il primo tentativo di implementazione che avevamo fatto. In effetti, una tale variante di dizionario esiste: si chiama `defaultdict` ed è disponibile nel modulo `collections` (uno dei moduli standard di python). La cella seguente importa questo nuovo tipo di dato:" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [], "source": [ "from collections import defaultdict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "e lo mette a disposizione: l'espressione `defaultdict()` crea un dizionario vuoto e il tipo indicato come argomento determina quale sarà il valore predefinito per le chiavi. Nel nostro caso, l'argomento `int` fa sì che tale valore predefinito sia `0`. Ciò permette di riscrivere la funzione `get_sorted_counts` in modo che non sia più necessario verificare preventivamente l'esistenza dei contatori." ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "def get_sorted_counts(sequence):\n", " counts = defaultdict(int)\n", "\n", " for x in sequence:\n", " counts[x] += 1\n", "\n", " pairs = counts.items()\n", " return sorted(pairs, key=lambda p:p[1], reverse=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quando è necessario importare molti elementi da uno o più moduli, potrebbe capitare che due o più elementi in moduli diversi abbiano lo stesso nome. Per evitare situazioni di questo genere, è opportuno importare un intero modulo: per esempio, l'istruzione" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "import numpy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "importa il modulo corrispondente alla libreria [numpy](http://www.numpy.org), che mette a disposizione una struttura dati simile agli array (in cui l'omogeneità dei dati ivi contenuti permette di effettuare calcoli in modo più efficiente rispetto all'uso delle liste o delle tuple). Dopo che un modulo è stato importato, è possibile accedere a un suo generico elemento usando il nome del modulo, seguito da un punto e dal nome dell'elemento in questione. Per esempio, la cella successiva calcola il cosiddetto _argmax_ della lista `index` (dopo averla modificata eliminando i valori `None` in essa presenti), e cioè l'indice in cui si trova un suo elemento massimo." ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "years = [y for y in years if y]\n", "numpy.argmax(years)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Indicare il nome di un modulo per poter accedere ai suoi elementi ha spesso l'effetto di allungare il codice, diminuendone al contempo la leggibilità. È per questo motivo che è possibile importare un modulo specificando un nome alternativo, più corto. È quello che succede nella seguente cella, che importa `numpy` e [pandas](http://pandas.pydata.org), un modulo che mette a disposizione delle classi per gestire i dati organizzandoli in serie e in tabelle." ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Questo modo di importare numpy e pandas usando i nomi alternativi `np` e `pd` fa riferimento a una convenzione molto diffusa tra gli sviluppatori. Vale la pena mantenre questa convenzione, così che chi legge il codice possa capire a colpo d'occhio a quale modulo si fa riferimento.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I moduli più complessi sono organizzati in strutture gerarchiche chiamate _package_, in modo non dissimile a quanto avviene per esempio in Java. La seguente cella importa il modulo `pyplot` che è contenuto nel modulo `matplotlib` ([matplotlib](http://matplotlib.org) è la libreria di riferimento in python per la creazione di grafici)." ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "plt.style.use('fivethirtyeight')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "L'invocazione di `plt.style.use` ha uno scopo puramente estetico: serve infatti a impostare uno stile per la visualizzazione dei grafici prodotti con matplotlib.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "## Disegnare grafici\n", "Il modulo `plt` può essere usato per produrre vari tipi di grafici. In generale le funzioni di questo modulo che generano un grafico basato su una serie di punti accettano come argomenti due liste contenenti rispettivamente le ascisse e le ordinate dei punti stessi. La funzione `get_sorted_counts` restituisce però una lista di coppie e non due liste di valori singoli. Se interpretiamo questa lista come una matrice, la trasposta di quest'ultima equivarrà a una lista che contiene esattamente le due liste che ci interessano. Per effettuare questa operazione risulta conveniente utilizzare il tipo di dato base messo a disposizione da numpy, `np.array`. Passando una lista (o una tupla, una lista di liste e così via) come argomento a `np.array` si crea un oggetto che corrisponde al corrispondente array. Su questo oggetto è possibile invocare il metodo `transpose` che restituisce il trasposto dell'array." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Tecnicamente, il metodo `transpose` restituisce una **vista** dell'array originale in modo che questo appaia trasposto. Ciò significa che non viene creato un nuovo oggetto e quindi le modifiche fatte al valore restituito andranno ad alterare anche l'array di partenza.\n", "\n", "Va anche notato il fatto che se si tenta di trasporre un array monodimensionale `transpose` restituirà una vista identica all'argomento specificato.\n", "
" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1941, 1962, 1963, 1964, 1940, 1961, 1981, 1979],\n", " [ 3, 2, 2, 1, 1, 1, 1, 1]])" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.array(get_sorted_counts(years)).transpose()" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1962, 1963, 1964, 1940, 1961, 1981, 1979],\n", " [ 2, 2, 1, 1, 1, 1, 1]])" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.array(get_sorted_counts(years)[1:]).transpose()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Va notato come la prima coppia restituita da `get_sorted_counts` sia stata scartata tramite uno _slicing_ in quanto fa riferimento a `None` e non a un anno, perché descrive il numero di casi in cui l'anno è un dato mancante. Usando una caratteristica di python è possibile assegnare le due liste ottenute direttamente a due variabili `x` e `y`:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [], "source": [ "a, b = (42, 102)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [], "source": [ "x, y = np.array(get_sorted_counts(years)[1:]).transpose()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "Questa caratteristica di python, nota con il nome di _unpacking_, permette per esempio di scambiare i contenuti di due variabili `a` e `b` senza ricorrere a una variabile temporanea, utilizzando l'assegnamento `a, b = b, a`.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Le due variabili `x` e `y` possono dunque essere passate come argomento al metodo `plt.bar` per produrre un grafico a barre che visualizzi le frequenze assolute degli anni di prima apparizione:" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.rc('figure', figsize=(5.0, 2.0))\n", "plt.bar(x, y)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vale la pena commentare in modo approfondito le righe di codice appena eseguite, specificando la differenza tra _generare_ e _visualizzare_ un grafico. In generale, invocare un metodo in matplotlib ha l'effetto di _modificare_ l'aspetto di un grafico (partendo ovviamente da un grafico vuoto). Ciò permette di sovrapporre diversi grafici, o di cambiare le etichette sugli assi e così via. Metodi come `plt.bar` visualizzano il grafico che corrisponde alla modifica apportata dal metodo eseguito, restituendo nel contempo dell'output testuale (una descrizione delle varie componenti del grafico stesso) che nella maggior parte dei casi non è particolarmente interessante. È per questo che l'ultima istruzione eseguita è `plt.show()`: questo metodo visualizza il grafico senza restituire alcunché.\n", "\n", "Infine, la seconda linea ci permette di impostare le dimensioni dei grafici: i valori predefiniti genererebbero infatti delle figure un po' troppo grandi." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "
\n", "\n", "## Leggere dati da file (e un po' di trucchi)\n", "Di solito la quantità di dati da analizzare è tale che non è pensabile di poterli immettere manualmente in una o più lista come abbiamo fatto noi. Normalmente i dati sono memorizzati su un file ed è necessario leggerli. Prendiamo in considerazione il file di testo `heroes.csv` contenuto nella directory `data`: esso contiene 735 righe, ognuna con le informazioni relative a un supereroe, separate da virgola. Le prime tre righe del file sono indicate di seguito.\n", "\n", "````\n", "name;identity;birth_place;publisher;height;weight;gender;first_appearance;eye_color;hair_color;strength\n", "A-Bomb;Richard Milhouse Jones;Scarsdale, Arizona;Marvel Comics;203;441;M;2008;Yellow;No Hair;100\n", "Agent Bob;Bob;;Marvel Comics;178;81;M;2007;Brown;Brown;10\n", "````\n", "\n", "Il formato CSV (comma separated values) indica un record su ogni riga, separando i campi corrispondenti con un carattere speciale che di norma, ma non sempre, è la virgola. Come si può vedere, nel nostro caso la prima riga indica il tipo di dati presente in ogni riga (sono gli stessi a cui abbiamo fatto riferimento finora), viene usato il punto e virgola per separare i campi (ciò permette di inserire delle virgole nei luoghi di nascita, come nel primo record) e possono esistere dei valori mancanti (quali per esempio il luogo di nascita nel secondo record).\n", "\n", "La cella seguente legge i contenuti del file e li inserisce nella lista `heroes`." ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [], "source": [ "import csv\n", "\n", "with open('data/heroes.csv', 'r') as heroes_file:\n", " heroes_reader = csv.reader(heroes_file, delimiter=';', quotechar='\"')\n", " heroes = list(heroes_reader)[1:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nella cella:\n", "\n", "* l'apertura del file è fatta utilizzando la parola chiave `with`: nelle istruzioni indentate che seguono è possibile usare `heroes_file` per fare riferimento all'oggetto che descrive il file, e quest'ultimo sarà automaticamente chiuso, anche nel caso in cui vengano lanciate eccezioni, all'uscita del corpo di `with`;\n", "* la funzione che apre il file accetta come primo argomento il pathname corrispondente e come secondo una stringa che indica come effettuare l'accesso: `'rb'` indica lettura in modalità binaria, cosa che permette di non doversi preoccupare di dover gestire come il sistema operativo indica la fine linea nei file di testo;\n", "* la lettura effettiva del file è demandata al modulo `csv` che si occupa direttamente di convertire dal formato CSV: la funzione `csv.reader` gestisce anche il fatto di avere un separatore diverso dalla virgola e permette di inserire un punto e virgola in un campo a patto di delimitare quest'ultimo tra doppi apici;\n", "* la parola chiave `list` converte il contenuto del file in una lista, e da quest'ultima si esclude la prima riga (in quanto essa contiene le intestazioni dei campi)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "In generale usando il nome di un tipo come se fosse una funzione è possibile effettuare conversioni tra tipi di dati: per esempio `int('42')` converte una stringa in intero e `str(42)` effettua la conversione inversa.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Proviamo a visualizzare i primi due record (che corrispondono alle due righe sopra mostrate):" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[['A-Bomb',\n", " 'Richard Milhouse Jones',\n", " 'Scarsdale, Arizona',\n", " 'Marvel Comics',\n", " '203.21000000000001',\n", " '441.94999999999999',\n", " 'M',\n", " '2008',\n", " 'Yellow',\n", " 'No Hair',\n", " '100',\n", " 'moderate'],\n", " ['Abraxas',\n", " 'Abraxas',\n", " 'Within Eternity ',\n", " 'Marvel Comics',\n", " '',\n", " '',\n", " 'M',\n", " '',\n", " 'Blue',\n", " 'Black',\n", " '100',\n", " 'high']]" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "heroes[:2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Si vede che tutti i dati sono indicati come stringhe (vedremo più avanti un modo più efficiente di rilevare i diversi tipi di dati in modo corretto), e che la stringa vuota è usata per codificare i dati mancanti.\n", "\n", "Per poter generare il grafico delle frequenze assolute con i nuovi dati è necessario estrarre l'anno di prima apparizione da ogni record. Potremmo farlo anche in questo caso usando il trucco di trasporre il corrispondente array, ma c'è un modo molto più efficiente che prende il nome di _list comprehension_, una sintassi specifica di python. Invece di creare una lista in modo _estensivo_ (cioè elencando i suoi elementi), la list comprehension permette di crearla in modo _intensivo_, specificando come trasformare gli elementi di un'altra lista che già abbiamo a disposizione. La sintassi di base di una _list comprehension_ è\n", "\n", "```\n", "[f(e) for e in l]\n", "```\n", "\n", "dove `f(e)` indica una funzione o un'espressione che dipende dalla variabile muta `e` e `l` è una lista di cui quindi `e` indica il generico elemento. Questa espressione permette di costruire una nuova lista in cui il primo elemento è il risultato del calcolo di `f` sul primo elemento di `l`, il secondo è il risultato di `f` secondo elemento di `l` e così via. È inoltre possibile utilizzare la sintassi `[f(e) for e in l if g(e)]`, che indica che nella creazione della nuova lista bisogna limitarsi a considerare gli elementi `e` della lista originale che rendono vera l'espressione `g(e)`. Pertanto" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "years = [int(h[7]) for h in heroes if h[7]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "assegna a `years` la lista che contiene l'anno di prima apparizione di ogni supereroe (che infatti occorre in ottava posizione), convertito da stringa a intero, ma senza considerare le stringhe vuote (in python la stringa vuota equivale a un'espressione logica falsa esattamente come `0` o `0.` in C, e le altre stringhe equivalgono a un'espressione logica vera), operazione necessaria altrimenti la conversione a intero di un dato mancante lancerebbe un'eccezione.\n", "\n", "A questo punto è possibile generare il grafico delle frequenze assolute:" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "counts = get_sorted_counts(years)\n", "x, y = np.array(counts).transpose()\n", "plt.bar(x, y)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il grafico appare \"spostato\" verso sinistra, a causa della presenza di una barra in prossimità dell'anno 2100. Potrebbe essere un supereroe effettivamente nato nel futuro, oppure si potrebbe trattare di un dato errato. Si tratta di una situazione più comune di quanto non si possa pensare: queste misurazioni affette da rumore prendono il nome di _dati fuori scala_ o _outlier_ e più avanti vedremo come gestirle. Per ora limitiamoci a vedere quale sia questo valore. Possiamo farlo usando una list comprehension appena più complicata di quella vista poco fa:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2099]" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[year for year in years if year > 2020]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In soldoni, la presenza dell'anno 2099 causa lo spostamento del grafico. Potremmo eliminare il record corrispondente dal nostro dataset, ma così facendo perderemmo i valori per gli altri campi che non è detto siano anch'essi degli outlier. Un modo molto più pratico di procedere è quello di visualizzare il grafico restringendo le ascisse all'intervallo temporale che va dal 1950 al 2015: ciò viene fatto invocando la funzione `plt.xlim` e passandole una coppia con gli estremi di questo intervallo. Già che ci siamo, possiamo anche impostare l'ampiezza dell'asse delle ordinate in modo che ci sia un po' di spazio sopra la barra che corrisponde alla frequenza massima:" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.bar(x, y)\n", "plt.xlim((1950, 2015))\n", "plt.ylim((0, 18.5))\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "In teoria, una volta ottenuta la lista `counts`, è possibile generare il grafico (ridmensionamento dei suoi assi a parte) usando l'istruzione più compatta `plt.bar(*np.array(counts[1:]).T)` in cui\n", "
    \n", "
  • l'invocazione del metodo `transpose` è sostituita dall'utilizzo della _proprietà_ `T`: si tratta essenzialmente della stessa cosa, ma permette di essere più succinti;
  • \n", "
  • invece di assegnare a due variabili `x` e `y` le liste di ascisse e ordinate, si usa l'operatore `*` per effettuare un'altra forma di _unpacking_ delle liste in modo che i due elementi della lista restituita da `T` vengano rispettivamente usati come primo e secondo argomento di `np.bar`.
  • \n", "
\n", "Va in ogni caso sottolineato che il minor numero di linee si paga con del codice più complesso e quindi meno facile da leggere e da correggere in caso di errore, quindi almeno fino a quando non si è abbastanza confidenti nel linguaggio è meglio non ricorrere a soluzioni troppo complicate.\n", "
" ] }, { "cell_type": "markdown", "metadata": { "footer": true }, "source": [ "
\n", "D. Malchiodi, Superhero data science. Vol 1: probabilità e statistica: Introduzione a python, 2017.\n", "
\n", "Powered by \"Jupyter\n", "
\n", "\n", "\n", "\n", "\n", "Quest'opera è distribuita con Licenza Creative Commons Attribuzione - Non commerciale - Non opere derivate 4.0 Internazionale.\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "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.7.3" }, "latex_envs": { "LaTeX_envs_menu_present": false, "autoclose": true, "autocomplete": true, "bibliofile": "biblio.bib", "cite_by": "apalike", "current_citInitial": 1, "eqLabelWithNumbers": true, "eqNumInitial": 1, "hotkeys": { "equation": "Ctrl-E", "itemize": "Ctrl-I" }, "labels_anchors": false, "latex_user_defs": false, "report_style_numbering": true, "user_envs_cfg": true }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": false, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": true, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }