<jupyter_start><jupyter_text>Basi di Python e Numpy per Data ScienceRiferimento:https://docs.python.org/3/tutorial/introduction.htmlusing-python-as-a-calculatorUseremo il Jupyter Notebook come *REPL* (Read-Eval-Print-Loop): un approccio efficiente per l'analisi esplorativa.Questo notebook serve come traccia di quello che abbiamo visto in aula. Non rappresenta un testo completo, ma un invito a esplorare i diversi argomenti nelle celle di codice, e a leggere la documentazione linkata.  Basi- commenti- tipi numerici (int, float)- operazioni su numeri (divisione, divisione intera) - variabili<jupyter_code># commento
a = 5.0 # float
b = 5 # int
print(type(a), type(b))
print(b/2) # divisione float by default (diverso da Java/C)
print(b//2) # forza la divisione intera<jupyter_output><class 'float'> <class 'int'>
2.5
2<jupyter_text>- stringhe ed f-strings ([tutorial](https://realpython.com/python-f-strings/))<jupyter_code>#le stringhe sono oggetti immutabili
s = "Corso di 'Data Science'"
print(f"Stiamo seguendo il {s}" + ". Evviva!")<jupyter_output>Stiamo seguendo il Corso di 'Data Science'. Evviva!<jupyter_text>- liste ed iterabili, len(...)- indexing``` +---+---+---+---+---+---+ | P | y | t | h | o | n | +---+---+---+---+---+---+ 0   1   2   3   4   5   6-6  -5  -4  -3  -2  -1```- slicing- operazioni su liste<jupyter_code>lista = [1, "stringa", ("un'altra lista!", 0)] # Nota che i tipi nella lista possono essere eterogenei
lista.append("un altro elemento") # Una lista e' mutabile
len(lista) # lunghezza di una lista<jupyter_output><empty_output><jupyter_text>Altre collezioni: tuple (immutabili), insiemi (senza ordine, senza duplicati: come `Set` in Java)<jupyter_code>lista = [1, 2, 3]   # list([1, 2, 3])
insieme = {1, 2, 3, 3, 3, 3} # set([1, 2, 3, 3, 3, 3])
tupla = (1, 2, 3) 
print(lista, insieme, tupla)<jupyter_output>[1, 2, 3] {1, 2, 3} (1, 2, 3)
Object `pass` not found.<jupyter_text>Controllo di flussohttps://docs.python.org/3/tutorial/controlflow.html(nota il significato semantico dell'indentazione!)- if- for- range- break/continue- pass<jupyter_code>for i in range(10):
    print(f"Valore {i}")<jupyter_output>Valore 0
Valore 1
Valore 2
Valore 3
Valore 4
Valore 5
Valore 6
Valore 7
Valore 8
Valore 9<jupyter_text>Definizione di funzioniArgomenti di default, positional arguments, keyword arguments. [Documentazione](https://docs.python.org/3.6/tutorial/controlflow.htmldefining-functions)  Argomenti, return values<jupyter_code># definiamo una funzione che restituisca la lista delle parole in una stringa, mantenendone l'ordine, ma rimuovendo i duplicati
def parole_in_stringa(s):
    r = []
    for parola in s.split(" "):
        if parola not in r:
            r.append(parola)
    return r

parole_in_stringa("questa e' una stringa stringa")<jupyter_output><empty_output><jupyter_text>Valori di default per gli argomentiQuando un argomento ha un valore di default, puo' anche non essere specificato esplicitamente dal chiamante.<jupyter_code>def ask_ok(prompt, retries=4, reminder='Please try again!'):
    while True:
        ok = input(prompt)
        if ok in ('y', 'ye', 'yes'):
            return True
        if ok in ('n', 'no', 'nop', 'nope'):
            return False
        retries = retries - 1
        if retries < 0:
            raise ValueError('invalid user response')
        print(reminder)
ask_ok(reminder="riprova", prompt="dimmi")<jupyter_output>dimmiecco
riprova
dimmiyes<jupyter_text>Keyword argumentsGli argomenti si assegnano con notazione posizionale (ordine degli argomenti) e/o utilizzando la notazione `kwarg=value` (keyword arguments).  Tuttavia, attenzione a:- definire tutti gli argomenti required- definire prima gli argomenti posizionali, poi quelli keyword- non definire un valore duplicato per lo stesso argomento<jupyter_code># Prova a violare ciascuna di queste regole per la funzione ask_ok definita sopra, e interpreta il messaggio di errore risultante

#def test(a=1, p): pass
#test(1)

#def test(a=1, a=2): pass
#test(1)<jupyter_output><empty_output><jupyter_text>Passaggio per valore degli argomentiSe passo a una funzione un oggetto immutabile (ad esempio un `int` un `float`, una stringa o una tupla), la funzione non puo' modificare la variabile del chiamante; se l'oggetto e' mutabile (ad esempio una lista), la funzione puo' modificarlo.[Spiegazione completa](https://www.scipy-lectures.org/intro/language/functions.htmlpassing-by-value)<jupyter_code>def try_to_modify(x, y, z, w):
    x = 23
    y.append(42)
    z = [99] # new reference
    w = w.upper()
    print(x)
    print(y)
    print(z)
    print(w)
a = 77    # immutable variable
b = [99]  # mutable variable
c = [28]
d = "miao "
try_to_modify(a, b, c, d)
print(a)
print(b)
print(c)
print(d)<jupyter_output>77
[99, 42]
[28]
miao<jupyter_text>Array numpy![image.png](attachment:image.png)[Documentazione](https://www.scipy-lectures.org/intro/numpy/array_object.html) Costruzione<jupyter_code>import numpy as np
a = np.array([1, 2, 3])
a<jupyter_output><empty_output><jupyter_text>Basi<jupyter_code>a.ndim
a.shape
len(a)<jupyter_output><empty_output><jupyter_text>Array 2D<jupyter_code># Creo un array con 2 righe e 3 colonne
b = np.array([[1, 2, 3], [4, 5, 6]])
b
b.ndim
b.shape
assert(b.ndim == len(b.shape)) # Questo vale sempre. Ma visto che siamo principianti del linguaggio, controlliamo.<jupyter_output><empty_output><jupyter_text>(Parentesi: [cosa sono le asserzioni e quando ha senso usarle?](https://mail.python.org/pipermail/python-list/2013-November/660568.html))  Costruttori utili<jupyter_code>np.arange(10)
np.arange(1, 9, 2) # start, end (exclusive), step
import numpy as np 
np.linspace(0, 10, 11)
np.ones((2,2,2)) # Nota: tupla di un elemento
#np.ones((2,)) # Nota: tupla di un elemento
#np.ones((2,2)) # Nota: tupla di un elemento
np.random.rand(5)<jupyter_output><empty_output><jupyter_text>Tipi di dati negli array numpy[Documentazione](https://www.scipy-lectures.org/intro/numpy/array_object.htmlbasic-data-types)<jupyter_code>a.dtype
b = np.array([10, 20, 30, 40])
print(b, b.dtype)<jupyter_output>[10 20 30 40] int32<jupyter_text>Indicizzazione 1D[Documentazione](https://www.scipy-lectures.org/intro/numpy/array_object.htmlindexing-and-slicing)Possiamo indicizzare gli array 1D come se fossero liste<jupyter_code>print(b[0], b[1], b[-1])<jupyter_output>10 20 40<jupyter_text>Slicing<jupyter_code>print(b[0:3], b[:2], b[2:], b[:-2])<jupyter_output>[10 20 30] [10 20] [30 40] [10 20]<jupyter_text>Indicizzazione 2D<jupyter_code>a = np.array([[1, 2, 3], [4, 5, 6]])

print(a[0,1], a[-1,-2])
print(a[1,:], a[:,2])
print(a[:,[1]], a[:,1])<jupyter_output>[[2]
 [5]] [2 5]<jupyter_text>![image.png](attachment:image.png)  EsercizioCreiamo questo array:```[[0., 0., 0., 0., 0.], [2., 0., 0., 0., 0.], [0., 3., 0., 0., 0.], [0., 0., 4., 0., 0.], [0., 0., 0., 5., 0.], [0., 0., 0., 0., 6.]]```<jupyter_code>a = np.zeros((6,5))
for i in range(5):
    a[i+1,i] = i+2
a<jupyter_output><empty_output><jupyter_text>Fancy IndexingPossiamo indicizzare un array con un array di interi[Documentazione](https://www.scipy-lectures.org/intro/numpy/array_object.htmlfancy-indexing)<jupyter_code>b = np.arange(10) * 10
print(b)
print(b[[0,2,-1, -2, 0]])
b[[0,2,-1]] = -1 # Vale anche per settare i valori, non solo per leggerli
print(b)<jupyter_output>[-1 10 -1 30 40 50 60 70 80 -1]<jupyter_text>... oppure con un array di booleani (boolean mask) della stessa dimensione del mio array<jupyter_code>b
b[[True, False, False, True, False, False, False, False, False, False]]<jupyter_output><empty_output><jupyter_text>Esempio: restituisci solo i valori maggiori di 55<jupyter_code>mask = b > 55
print(mask)
print(b[mask])<jupyter_output>[False False False False False False  True  True  True False]
[60 70 80]<jupyter_text>Esempi di fancy indexing![image.png](attachment:image.png)  Operazioni sugli array[Documentazione completa](https://www.scipy-lectures.org/intro/numpy/operations.html)Di queste ci interessano due classi di operazioni:- Operazioni elemento per elemento (somma, sottrazione, prodotto...)<jupyter_code>a = np.array([[1,2,3],[4,5,6]])
print(a)
print(a+1)
print(a*2)<jupyter_output>[[ 2  4  6]
 [ 8 10 12]]<jupyter_text>- Riduzioni (ad esempio, `sum`, `max`, `min`)<jupyter_code>a
print(np.sum(a)) # Somma di tutti gli elementi
print(np.sum(a, axis=0)) # Somma di ciascuna colonna (agisce lungo l'asse delle righe, ovvero l'asse 0)
print(np.sum(a, axis=1)) # Somma di ciascuna riga (agisce lungo l'asse delle colonne, ovvero l'asse 1)
print(np.max(a))
print(np.max(a, axis=1))<jupyter_output>6
[3 6]
{'fraction_non_alphanumeric': 0.14879701004438214, 'fraction_numerical': 0.035739313244569026, 'mean_word_length': 5.009122807017544, 'pattern_counts': {'":': 0, '<': 82, '<?xml version=': 0, '>': 82, 'https://': 11, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '23', 'dir': 'jupyter-structured-clean-dedup', 'id': '457764', 'license': 'MIT', 'n_tokens_mistral': 3595, 'n_tokens_neox': 3386, 'n_words': 870, 'path': 'notebook/02 - Basi di python e numpy.ipynb', 'repo_name': 'code-mds/datascience', 'size': '8562'}