MathStat1
Statistica descrittiva

G. D'Agostini

Inizializzazione

Il seguente comando è sempre reccomandato all'inizio di un notebook per resettare tutte le variabili. Tale accortezza è  importante nel caso si voglia eseguire un nuovo notebook all'interno di una sessione di lavoro. Infatti, le variabili conosciute da Mathematica sono comuni alle diverse finestre di lavoro.

ClearAll["Global`*"]

Primi dati unidimensionali

Consideriamo un insieme di dati numerici omogenei, ad esempio i voti riportati da uno studente universitario: 21, 30, 25 ... . Rappresentiamoli con una `lista' (o `vettore') di nome VotiStudente1.

VotiStudente1 = {21, 30, 25, 27, 28, 29, 25, 18, 30, 30, 24, 27, 25, 
          30, 27, 25, 30, 30, 28};

Per comodita` chiamiamo ora la lista con il nome generico dati, omettendo il punto e virgola dopo l'istruzione, per controllare, una volta tanto, che il trasferimento di informazioni avvenga correttamente.

dati = VotiStudente1

Utilizzando delle funzioni di Mathematica, possiamo eseguire delle prime analisi elementari sui dati. Ad esempio, proviamo a contarli (Lenght), elencarli in ordine crescente o decrescente (Sort), e a  trovare i valori minimo (Min) e massimo (Max):

n = Length[dati]
Sort[dati]
Sort[dati, Greater]
vmin = Min[dati]
vmax = Max[dati]

Abbiamo memorizzato in nuove variabili (n, vmin e vmax) soltanto i risultati che si pensa di utilizzare nel seguito.

Altre operazioni elementari sui dati sono il calcolo della media aritmetica e della deviazione standard. Esistono opportune funzioni di Mathematica per calcolare tali indicatori statistici. Calcoliamoni a partire dai comandi di base di Mathematica, sia per familiarizzarsi con tale linguaggio che per ripassare le definizioni di tali indicatori. Sempre per esercizio, faremo i conti in modi diversi, dando la precedenza alle procedure piu' compatte.

media = Apply[Plus, dati]/n
media = Sum[dati[[i]], {i, n}]/n

Come si vede, Mathematica tende a dare il risultato delle operazioni in forma esatta. Ma per le applicazioni di interesse in questo contesto, e` preferibile passare ai cosiddetti valori numerici mediante le funzione N.

media = N[media]
media = N[Apply[Plus, dati]/n]

Si ricorda che e` anche possibile ridefinire il numero di cifre totali con cui  N fornisce il risultato, ad esempio:

N[media, 3]          
N[media, 10]

Con Mathematica 4:

NumberForm[N[media, 3], 3]
NumberForm[N[media, 10], 10]

La deviazione standard è definita come radice quadrata della media dei quadrati degli scarti rispetto alla media. Essa dà un'indicazione della dispersione di dati intorno al valore medio. Valutiamo la deviazione applicando alla lettera la sua definizione, calcolando esplicitamente anche il vettore degli scarti.

scarti = dati - media
scarti = Table[dati[[i]] - media, {i, n}]

sscarti2 = scarti . scarti
sscarti2 = 
      Sum[scarti[[i]]^2, {i, 
          n}]

devstand=Sqrt[
        sscarti2/
          n]

Si ricorda che il simbolo `.' definisce il prodotto scalare fra due vettori. La radice quadrata (Sqrt) poteva anche scriversi con simbolo standard (Sqrt[□]), facendo uso del BasicInput palett. Così pure, si potevano usare i simboli di sommatoria e di frazione.   Avremmo scritto, quindi, l'intera espressione come

devstand = (Sqrt[(((∑_(i = 1)^n)(((dati[[i]] - media))^2))/n)])

In questo testo eviteremo l'uso dei simboli non alfanumerici in quanto appesantiscono la scrittura del notebook (si provi a scrivere l'espressione precedente per convincersene).

Per il calcolo della deviazione standard non è necessario passare attraverso il calcolo degli scarti rispetto alla media. In genere (ovvero sempre nei programmi professionali e anche all'interno delle calcolatrici tascabili) si usa la proprietà per cui la varianza (quadrato della deviazione standard) è pari alla media dei quadrati meno il quadrato della media. Ne segue

devstand = N[Sqrt[dati . dati/n - media^2]]

Funzioni di Mathematica di Statistica descrittiva

Il pacchetto standard add-on  Statistics`DescriptiveStatistics` permette di calcolare in modo rapido media e deviazione standard, insieme ad altri indicatori di interesse per dati unidimensionali immagzzinati in una lista.  Per utilizzare i pacchetti, essi devono essere caricati con il comando Needs o, piu` comodamente, con " <<":

<< Statistics`DescriptiveStatistics`

Si ricorda inoltre che, se se si vogliono conoscere i nomi delle funzioni incluse nel pacchetto, si può far uso del comando Names:

Names["Statistics`DescriptiveStatistics`*"]

Il simbolo `*' sta per tutti i nomi del pacchetto. Si sarebbe potuto chiede la lista delle funzioni  inizianti per `St' con Names["Statistics`DescriptiveStatistics`St*"]. Si può chiedere il significato delle funzione con `?', ad esempio

?Mean

Questo modo di interrogazione è valido per tutte le funzioni di Mathematica. Si può anche dare il nome parziale della funzione, ottenendo la lista di tutte le funzioni (definite fino a quel momento) che hanno in comune quella successione di lettere.

?Len*

?Length

?*Dev*

?*dev*

Si noti che Mathematica è sensibile alle maiuscole e minuscole. Inoltre i nomi delle funzioni iniziano con la maiuscola e sono raramente abbreviati. I nomi composti sono ottenuti concatenando le varie parole, ciascuna iniziante con la maiuscola.

Media aritmetica e deviazione standard, secondo le definizioni date sopra, si ottengono con Mean e StandardDeviationMLE:

Mean[dati]
StandardDeviationMLE[dati]

Quando si intende usare spesso una funzione dal nome molto lungo si puo' creare una funzione dal nome breve che chiami la funzione di sistema, ad esempio

dst[lista_] := StandardDeviationMLE[lista]
dst[dati]

Ancora una volta si noti l'importanza della funzione N per ottenere risultati `comprensibili'. N può essere utilizzato in un modo alternativo a quello visto sopra, facendo seguire `//N' all'espressione. Il vantaggio di questo secondo modo consiste nella riduzioni delle parentesi, migliorando la leggibilità delle istruzioni e riducendo il rischio di errore.

Mean[dati] // N
StandardDeviationMLE[dati] // N

Si noti che esiste anche la funzione StandardDeviation, il cui risultato differisce da quello di StandardDeviationMLE di un fattore (Sqrt[n/(n - 1)]). Facciamo la prova:

StandardDeviation[dati]*Sqrt[(n - 1)/n] // N

La ragione del famigereto "-1" è, a giudizio di chi scrive, una interferenza fra statistica descrittiva e (un modo di vedere la) statistica inferenziale. Nel seguito faremo uso della deviazione standard definita radice quadrata della media dei quadrati degli scarti, ovvero senza il "-1". Comunque, per chi vuole risparmiarsi tre caratteri da digitare, è chiaro che in StandardDeviation produce lo stesso risultato di StandardDeviationMLE per tutti i casi pratici in cui n non sia dell'ordine dell'unità.

Altri indicatori statistici di comune interesse sono la moda (il valore che si presenta più frequentemente nei dati), la mediana (divide `grosso modo' il valore centrale quando i dati sono ordinati in ordine crescente o decrescente) e i vari quantili, o percentili (i valori al di sotto dei quali si trovano, sempre `grosso modo', una certa frazione dei dati). `Grosso modo' sta and indicare che per valori discreti, l'idea intuitiva che sta dietro i concetti di mediana e di quantili e' attuata in modo convenzionale tale per cui, ad esempio, la mediana non corrisponde ad un centro ideali dei valori. Si noti inoltre che i valori di moda possono essere molteplici. Si parla allora di distribuzione statistica multimodale.

Mode[dati]
Median[dati]
Table[Quantile[dati, q], {q, 0.1, 1.0, 0.1}]
Table[Quantile[dati, q], {q, 0.25, 1.0, 0.25}]

Chiariamo il significato dell'ultimo risultato (lasciando l'interpretazione del penultimo come esercizio): il 75% circa dei voti è inferiore o uguale a 25, il 50% circa inferiore o uguale a 27, e cosi` via.

Il pacchetto contiene anche le funzioni di media geometrica (GeometricMean), media armonica (HarmonicMean), RMS (RootMeansquare), ampiezza del campione (SampleRange), insieme ad altre funzioni delle quali non ci occuperemo. Comunque, per un'occhiata rapida sui dati si possono usare due funzione che forniscono un sommario dei parametri di localizzazione e di dispersione dei dati:

LocationReport[dati] // N
DispersionReport[dati] // N

Terminiamo questo paragrafo accennando a due indicatori che forniscono informazioni concise sulla forma della distribuzione statistica dei dati: La skewness (Skewness) è definita come il momento centrale terzo, in unità del cubo della  deviazione standard. La kurtosis (Kurtosis) è definita come momento centrale quarto, in unità della quarta potenza della deviazione standard.

Skewness[dati] // N
Kurtosis[dati] // N

Si ricorda che il valore negativo della skewness indica che i dati presentano una coda `a sinistra' della media piu` pronunciata di quella a destra. Una kurtosis maggiore di 3 indica, a sua volta, un raggruppamento dei dati intorno alla media maggiore di quanto ci si aspetta da un grande campione che segue una distribuzione gaussiana (vedi nel seguito). In effetti, anticipando una funzione che descriveremo nel seguito, vediamo che ben 17 valori su 19 si trovano nell'intervallo di +/- una deviazione standard dalla media.

Count[dati, 
      x_ /; (media - devstand) <= x <= (media + devstand)]
N[17/19]

Distribuzione statistica e descrizione grafica dei dati

Finora abbiamo parlato, genericamente, di distribuzione statistica dei dati sperimentali. Con questo termine si intende la suddivisione in intervalli dei dati, associando ad ogni intervallo la frequenza con cui essi si presentano. Il caso più semplice di ampiezza dell'intervallo per dati che assumono valori interi, come nel nostro esempio dei voti, è l'intervallo unitario. Per ottenere tale classificazione dei dati si può usare la funzione di Mathematica Frequencies del package standard Statistics`DataManipulation`

<< Statistics`DataManipulation`
freq = Frequencies[dati]

La lista indica che il 18 si presenta una volta, il 21 una volta, etc., fino al 30 che si presenta 6 volte (valore modale). Una volta ottenuta la distribuzione di frequenze è facile graficare i dati mediante un diagramma a barre (BarChart del package Graphics`Graphics`):

<< Graphics`Graphics`
BarChart[freq];

Come si vede, un diagramma a barra dà un quadro d'insieme dei dati, ma si perdono le informazioni della sequenza temporale, dalla quale si possono estrarre, nell'esempio in questione, informazioni sulla curriculum dell'ipotetico studente in questione. La sequenza temporale può essere graficata facendo uso della funzione ListPlot di Graphics`Graphics`, la quale, applica ad una lista unidimensionale, grafica i valori numerici della lista sulle ordinate e il numero d'ordine sulle ascisse. Essa può essere chiamate senza ulteriori parametri, ma il risultato può essere poco attraente:

ListPlot[dati];

Il grafico può essere migliorato utilizzando opportune opzioni, molte delle quali sono le stesse di Plot. Ad esempio:

ListPlot[dati, Prolog -> PointSize[0.02], 
        PlotRange -> {{0, 20}, {0, 30}}, 
        Ticks -> {{5, 10, 15, 20}, Automatic}, AxesLabel -> {Esame, Voto}, 
        PlotLabel -> "Studente Nr. 1"];

Si noti l'utile messaggio di errore con il quale segnala che stiamo usando un nome simile ad un altro già definito e quindi potrebbe trattarsi di un errore di battitura (in genere ha ragione...).

I punti possono essere anche uniti da una spezzata, al solo fine di guidare l'occhio fra le oscillazioni.

ListPlot[dati, PlotJoined -> True, Prolog -> Thickness[0.008], 
        PlotRange -> {{0, 20}, {0, 30}}, 
        Ticks -> {{5, 10, 15, 20}, Automatic}, AxesLabel -> {Esame, Voto}, 
        PlotLabel -> "Studente Nr. 1"];

Come ultimo esempio di rappresentazione grafica di dati unidimensinali, mostriamo il cosiddetto grafico a torta (PieChart di Graphics`Graphics`). 
La frequenza di 30 (ottava classe) e` messa in evidenza con l'opzione PieExploded.

PieChart[freq, PieExploded -> {8}, 
        PlotLabel -> "Voti"];

Si noti che su questo testo stampato  i toni di grigio dei vari spicchi corrispondono sullo schermo ad una scala di colore ordinata in frequenza delle onde luminose, a partire dal rosso, associato al valore minimo.  All'occorrenza si puo` richiedere di visualizzare il grafico a torta seuendo una scala di toni del grigio, come nell'esempio seguente.

PieChart[freq, PieExploded -> {8}, PlotLabel -> "Voti", 
        PieStyle -> {GrayLevel[0.3], GrayLevel[0.4], GrayLevel[0.5], 
            GrayLevel[0.6], GrayLevel[0.7], GrayLevel[0.8], GrayLevel[0.9], 
            GrayLevel[1.0]}];

Ne seguito continueremo a lavorare con colori standard, assumendo che il lettore sia maggiormente interessato a visualizzare i dati sullo schermo.

Per rendere il grafico piu` interessante, immaginiamo altri tre studenti, con le seguenti votazioni (il numero di esami non e` necessariamente lo stesso):

VotiStudente2 = {21, 30, 27, 29, 25, 26, 25, 18, 30, 28, 24, 28, 25, 
          28, 23, 25, 30, 26, 22, 23};
VotiStudente3 = {30, 30, 28, 27, 30, 29, 25, 30, 30, 27, 27, 30, 26, 
          30, 28, 25, 30, 30};
VotiStudente4 = {18, 25, 26, 27, 28, 29, 25, 26, 24, 25, 26, 27, 28, 
          24, 27, 25, 30, 30, 28};
freq2 = Frequencies[VotiStudente2];
freq3 = Frequencies[VotiStudente3];
freq4 = Frequencies[VotiStudente4];

Nel seguito le quattro `torte' saranno definite senza farle apparire. Successivamente esse saranno mostrate  insieme sotto forma tabulare con Show[GraphicsArray[]]. Si noti l'organizzazione delle parentesi graffe all'interno di GraphicsArray per definire una matrice 2 per 2. Rendiamo inoltre automatico l'evidenziamento del voto piu` alto, che si troverà nella posizione Length[freq].

torta1 := 
      PieChart[freq, PieExploded -> {Length[freq]}, 
        PlotLabel -> "Stud. 1", DisplayFunction -> Identity]
torta2 := 
      PieChart[freq2, PieExploded -> {Length[freq2]}, 
        PlotLabel -> "Stud. 2", DisplayFunction -> Identity]
torta3 := 
      PieChart[freq3, PieExploded -> {Length[freq3]}, 
        PlotLabel -> "Stud. 3", DisplayFunction -> Identity]
torta4 := 
      PieChart[freq4, PieExploded -> {Length[freq4]}, 
        PlotLabel -> "Stud. 4", DisplayFunction -> Identity]

Si noti l'opzione DisplayFunction->Identity per non far mostrare i risultati individuali. L'opzione standard va ripristinata con DisplayFunction->$DisplayFunction.

Show[GraphicsArray[{{torta1, torta2}, {torta3, torta4}}], 
        DisplayFunction -> $DisplayFunction];

Operazioni sulle liste di dati (vettori, tabelle)

Liste unidimensionali

Abbiamo visto diversi modi di creare direttamente liste unidimensionali di valori dando dei valori fra parentesi graffe (vedi VotiStudente1, etc.), copiando una lista in un'altra (``dati= VotiStudente1''), definendo una lista come risultato di operazioni su liste (``scarti=dati-media'') o mediante la funzione Table. Oltre alle operazioni sulle liste per ottenere indicatori statistici, sono importanti le operazioni per modificare le liste stesse. Ad esempio, si può rimuovere o aggiungere un dato, selezionare valori, unire liste, estrarre una parte della lista, e così  via. Facciamo alcuni esempi, ripartendo dai dati del primo studente, che ripetiamo qui per comodità:

dati

Con le funzioni standard di Mathematica possiamo accedere al primo e all'ultimo elemento della lista (First e Last, rispettivamente), oltre che all'i-mo elemento (ad esempio, dati[[i]]). Possiamo copiare su un'altra lista una parte continua, o ad elementi alterni, della lista con la funzione Take, oppure scartarne elementi con Drop. Possiamo controllare se (MemberQ) e dove (Position) un certo valore compare nella lista, o quante volte vi compaia (Count). Ad esempio: prendiamo i primi 10 voti, scartando il primo, e verifichiamo quanti `30' ci sono fra di essi e in quali posizioni si trovano.

da2a11 = Take[dati, {2, 2 + (10 - 1)}]       
Count[da2a11, 30]                  
Position[da2a11, 30]

Come anticipato sopra, oltre che cercare l'esatta occorrenza di un valore, si puo` richiedere una piu` generale condizione sui dati, ad esempio che siano compresi in un certo intervallo.

Count[dati, x_ /; 24 <= x <= 28]

?/;

Vediamo come trasformare il comando precedente in una funzione che agisce su dati, i cui parametri sono il nome della lista dei dati e gli estremi dell'intervallo (chiuso).

frav1ev2[lista_, v1_, v2_] := 
      Count[lista, x_ /; v1 <= x <= v2]

Applichiamola in un caso particolare

frav1ev2[dati, 24, 28]

La condizione a destra di `/;' (`a condizione che') può  essere anche più complessa e si rimanda al manuale di Mathematica. Richiediamo, ad esempio, che il valore sia dispari e compreso all'interno dell'intervallo già considerato.

Count[dati, x_ /; (24 <= x <= 28)  &&  OddQ[x]]

Tramite le funzioni Prepend, Append, Insert e Delete possiamo aggiungere o cancellare elementi. Non è necessario mostrare esempi di queste semplici funzioni. Vediamo, invece, selezionare dei dati mediante Select. Ad esempio, vogliamo listare e contare il numero di voti inferiore a 27 presente nella lista originale dei voti.

Select[dati, # < 27 &]
Length[%]

?#

Per esempio, possiamo utilizzare Select per rivalutare la media scartando il voto più basso:

Mean[Select[dati, # != Min[dati]  &]] // N

Come ultima operazioni elementari sulle liste unidiminsionale, vediamo come combinare delle liste parziali in una lista globale, mediante la funzione Join. Contiamo il numero totale di esami e media dei voti dei quattro studenti. Approfittiamo per vedere la funzione Print, con la quale chiariamo il significato dei risultati. Arrotondiamo inoltre la media ad una cifra dopo la virgola.

tutti = 
        Join[VotiStudente1, VotiStudente2, VotiStudente3, 
          VotiStudente4];
Print["(Evitiamo di stampare la lista 'tutti')"]
Print["Totale esami ", Length[tutti]]
Print["Media globale ", 
      NumberForm[ N[Mean[tutti]], 3]]

(Si ricorda che usando Mathematica3 è sufficiente scrivere N[Mean[tutti],3].

Liste multidimensionali

Riprendiamo la distribuzione di frequenze dei voti del primo studente, ottenuta precedentemente con la funzione Frequencies e memorizzata nella lista freq.

freq

Essa e' costituita da 16 numeri organizzati in coppie. Si tratta di una lista di liste. Vediamo infatti cosa succede se applichiamo alcune operazioni elementari sulle liste:

Length[freq]
freq[[1]]
Length[freq[[1]]]

Essa puo` essere vista come una matrice o una tabella, a secondo del significato dei numeri in essa contenuti (nel notro caso evidentemente una tabella). Abbiamo visto nell'introduzione MatrixForm per visualizzare una tabella bidimensionale come forma di matrice. TableForm la visualizza come tabella.

TableForm[freq]

Ricordiamo che l'elemento corrispondente alla i-ma riga e j-ma colonna puo` essere ottenuto direttamente con fre[i,j], ad esempio

freq[[5, 2]]

Mentre, come visto sopra,  l'accesso compatto alle righe della colonna e` immediato,  per accedere in modo compatto alle colonne bisogna calcolare prima la `matrice' trasposta di freq:

freqtr = Transpose[freq];
TableForm[freqtr]
Length[freqtr]
freqtr[[1]]
Length[freqtr[[1]]]

Ad esempio, se vogliamo passare in modo compatto dalla distribuzione delle  frequenze dei voti a quella delle frequenze relative, basandoci sulla sola tabella freq, possiamo operare nel modo seguente (l'esercizio è puramente accademico in quanto abbiamo già altre informazione che possono essere usate direttamente).

freqrelswap = 
        Transpose[{Transpose[freq][[2]], Transpose[freq][[1]]/
                Apply[Plus, Transpose[freq][[1]]] // N}];
TableForm[freqrelswap]

Si noti anche lo scambio (`swap') fra le colonne che contengono i valori e quelle delle frequenze relative, piu` conveniente per la reppresentazione tabulare. Ma, ovviamente, se si usano le funzioni BarChart o PieChart su quest'ultima lista si ottegono risultati `assurdi', in quanto tali funzioni si aspettano l'ordine inverso.

Vediamo un altro modo per scambiare le colonne.

m = Transpose[freqrelswap];
{m[[1]], m[[2]]} = {m[[2]], m[[1]]};
freqrel = Transpose[m];
TableForm[freqrel]

Un modo più rapido per ottenere lo stesso risultato è utlizzare le funzioni TakeColumns e AppendRows dell'add-on LinearAlgebra`MatrixManipulation` , come nell'esempio che segue.

<< LinearAlgebra`MatrixManipulation`

prova = 
        AppendRows[TakeColumns[freqrelswap, {2, 2}],  
          TakeColumns[freqrelswap, {1, 1}]];
TableForm[prova]

La lista relfreq è nel formato giusto per poter essere graficata con BarChart.

BarChart[freqrel];

Abbiamo ottenuto esattamente lo stesso diagramma a barre di sopra, a parte il cambiamento di scala.

Come esempio di lista di dimensione superiore a due, possiamo combinare in un'unica lista le  distribuzioni di frequenze dei voti di ciascuno studente.

freqtot = {freq, freq2, freq3,  freq4}

Essa non va confusa con la lista ottenuta facendo un `merge' delle liste di frequenza con Join.

freqmerge = Join[freq, freq2, freq3, freq4]

Infatti

Length[freqtot]
freqtot[[1]]

Length[freqmerge]
freqmerge[[1]]

Si noti, inoltre, che mentre a partire da freqtot tsi possono riottenere i grafici di frequenze di ciascuno studente, freqmerge non produce quello che ci si potrebbe aspettare ingenuamente. Sarebbe a dire, essa non da` la distribuzione di frequenza globale, riottenibile, invece, dalla lista tutti. Vediamo il risultato delle seguenti operazioni.

BarChart[freqtot[[4]]];

BarChart[freqmerge];

BarChart[Frequencies[tutti]];

Terminiamo l'argomento della manipolazione di liste con un esercizio di statistica sulle prime 1000 cifre di π. La procedura usa volutamente, per ragioni didattiche, molti passaggi intermedi, anche se si e` evitato di stampare i parziali. Per seguire la logica si riduca il numero di cifre da analizzare, si tolgano i punto e virgola e si consulti  Mathematica sul significato dei comandi tramite '?' o l'help.

ncifre = 1000;
pi1000 = N[Pi, ncifre];
pistring = ToString[pi1000];
pichar = Characters[pistring];
pilist = ToExpression[pichar];
pilist = Select[pilist, NumberQ[#] &];
pilist = Take[pilist, ncifre];
Print["Cifra media ", 
      Mean[pilist] // N, "  Deviazione standard ", 
      StandardDeviation[pilist] // N]
pifreq = Frequencies[pilist];
Print["Frequenze delle 10 cifre:"]
TableForm[pifreq]
\(Print["Frequenza media : ", 
      Mean[\(Transpose[pifreq]\)[\([1]\)]] // 
        N, "  Deviazione standard ", 
StandardDeviation[\(Transpose[pifreq]\)[\([1]\)]] // 
        N]\)

E` normale che Mathematica generi un messaggio di errore. Ma la procedura è tale per cui il carattere che genera errore viene successimamente rimosso (alternativamente, poteva essere anche rimosso prima). Per risparmiare memoria nel seguito, resettiamo le liste usate in quest'esercizio (chiaramente, in questo contesto questa istruzione è puramente accademica, ma può essere veramente importante per notebook complicati):

ClearAll["pi*"]

Dati raggruppati

Non sempre si è in possesso dei dati individuali. A volte l'informazione di partenza è costituita da dati raggruppati, sotto forma di distribuzione di frequenze. Come esempio, riprendiamo la distribuzione dei voti di tutti gli studenti.

DisVotiTutti = Frequencies[tutti]

Come abbiamo visto, è facile graficare questa distribuzione di frequenze. Ma, purtroppo, non possiamo applicare ad  essa, le funzioni di statistica descrittiva di Mathematica, in quanto esse operano solo su liste unidimensionali di `dati sciolti'. Quindi, esse sono applicabili sono su 'tutti', ma non su `DisVotiTutti'. Se abbiamo soltanto i dati raggruppati, dobbiamo trovare un altro modo di operare. Una possibilità consiste nel produrre una lista unidimensionale a partire dai dati raggruppati.

sciolti = 
      Flatten[Table[
          Table[DisVotiTutti[[j, 2]], {i, DisVotiTutti[[j, 1]]}], {j, 
            Length[DisVotiTutti]}]]

Ne segue, ad esempio:

Mean[sciolti] // N

da confrontare con

Mean[tutti] // N

Questa procedura è molto comoda, ma è inattuabile se si considera il raggruppamento di una grande quantità di dati, ad esempio la distribuzione di età dell'intera popolazione italiana. Passare da un centinaio di coppie di numeri a sessanta milioni di dati sciolt è impensabile. Bisogna riscrivere quindi delle funzioni statistiche che agiscono sui dati raggruppati. Vediamo come fare per moda, media, deviazione standard, skewness e kurtosis. Per quanto riguarda media e deviazioni standard, si usano le formule ottenute applicando la proprietà associativa nelle sommatorie necessare per definire i vari valori medi. In pratica, le medie aritmetiche che compaiono nelle formule vengono sostituite da medie pesate, in cui il peso (statistico) è pari alla frequenza (o frequenza relativa) di quel valore. Per skewness e kurtosis vale lo stesso discorso, lasciamo la derivazione delle formula per esercizio. Per la moda la procedure è abbastanza ovvio: si cerca il valore che compare più volte. Cominciamo da quest'ultima:

tr = Transpose[DisVotiTutti];
pos = Flatten[Position[tr[[1]], Max[tr[[1]]]]];
vmoda = DisVotiTutti[[pos[[1]], 2]]

Come si capisce analizzando la procedura, in caso di distribuzione multimodale, viene dato soltanto la "prima moda che si incontra", ovvero il valore modale inferiore. La procedura va quindi modificata per tener conto di tale eventualita. Vediamo come e inseriamo il risultato all'interno di un modulo (Module)  che calcola anche il numero di classi, il numero totale di frequenze, la media e la deviazione standard. Il modulo può come una specie di `subroutine' (per chi è più familiare con questo concetto. Si noti come la lista di variabili fra parentesi graffe, primo argomento della funzione Module, sono variabili locali conosciute soltanto all'interno del modulo.  Le variabili ModaGruppi, MediaGruppi,  DevStGruppi, SkewGruppi e KurtGruppi sono invece variabili globali e sono utilizzate per immagazzinarci.

\(StatGruppi[dis_] := 
      Module[{tr, pos, x2m, x3m, x4m, mc3, mc4}, 
		NGruppi = 
          Length[dis\ ]; 
	
        tr = Transpose[dis]; 
		TotFreq = Apply[Plus, tr[\([1]\)]]; \ 

        pos = Flatten[Position[tr[\([1]\)], Max[tr[\([1]\)]]]]; 

        ModaGruppi = 
          Table[dis[\([pos[\([i]\)], 2]\)], {i, 
              Length[pos]}]; 
		MediaGruppi = 
          tr[\([1]\)] . tr[\([2]\)]/
            TotFreq; 
x2m = \((tr[\([2]\)]^2)\) . 
              tr[\([1]\)]/
            TotFreq; 
x3m = \((tr[\([2]\)]^3)\) . 
              tr[\([1]\)]/
            TotFreq; 
x4m = \((tr[\([2]\)]^4)\) . 
              tr[\([1]\)]/TotFreq; 
		DevStGruppi = 
          Sqrt[x2m - MediaGruppi^2]; 
m3 = 
          x3m - 3*x2m*MediaGruppi + 2*MediaGruppi^3; 
m4 = 
          x4m - 4*x3m*MediaGruppi + 6*x2m*MediaGruppi^2 - 
            3*MediaGruppi^4; 
SkewGruppi = 
          m3/DevStGruppi^3; 
KurtGruppi = 
          m4/DevStGruppi^4; 
Print["StatGruppi ha calcolato: ModaGruppi, MediaGruppi, 
 DevStGruppi, SkewGruppi e KurtGruppi"]
]\)

StatGruppi[DisVotiTutti];

NGruppi
TotFreq
ModaGruppi
MediaGruppi // N
DevStGruppi // N
SkewGruppi // N
KurtGruppi // N

Confrontiamo i risultati con quanto si ottiene applicando le funzioni di Mathematica sui dati sciolti.

Mean[tutti] // N
StandardDeviationMLE[tutti] // N
Skewness[tutti] // N
Kurtosis[tutti] // N

Per controllare che la procedura per tener conto di mode multiple, inseriamo il voto 19 , con la stessa frequenza del 30.

DisVotiTutti = Insert[DisVotiTutti, {20, 19}, 2]

StatGruppi[DisVotiTutti];
ModaGruppi

Rimuoviamo i venti 19 per tornare ai dati originali:

DisVotiTutti = Delete[DisVotiTutti, 2]

Import export di dati

Mathematica permette di scambiare una grande varietà di formati di dati con altri programmi, inclusi i più comuni formati di immagini e di suono. Ci occuperemo qui dei casi più elementari, consistenti nell'importare dei dati sotto forma di tabella ed esportare il prodotto finale sotto forma di grafici. Cominciamo con la parte più facile, quella di creare file contenenti grafici, limitandoci al formato `eps' (`encapsulated ostscript').

Esportazione di grafici

Per esportare un grafico bisogna innanzitutto selezionarlo cliccandoci sopra. Quindi ci sono due possibilità. La prima consiste nel cliccare su `Save selection As..' nella barra di comandi `Edit' , scegliendo quindi l'opzione desiderata. Tipicamente, il formato sara` del tipo `.eps' (Encaplulated Postscript). La seconda consiste, mediante la barra di comandi `File' di `stampare'  la selezione su file anziché su stampante, scegliendo il formato più opportuno. In genere la prima possibilità è più rapida, mentre la seconda permette più flessibilità  (soprattutto per quanto riguarda le fonts di caratteri `strani', come lettere greche o simboli vari, che compaiono nelle didascalie delle figure).   
La sola nota aggiuntiva a queste semplici istruzioni riguarda il fatto che a volte Mathematica sembra calcolare male i margini (bounding box) del grafico e quindi, una volta che il file è importato nel programma di videoscrittura, il grafico può risultare tagliato o aggirittura vuoto. Se questo dovesse accadere è necessario riaggiustare a mano i margini del bounding box editando il file eps. All'inizio del file si leggera `bounding box' seguito da quattro numeri interi. Essi rappresentano ascisse e ordinate dei margini. Per sapere quali sono le coordinate giuste, bisogna aprire il file eps con Ghostview e, posizionando il cursore nei vertici del rettangolo che definisce il contorno del grafico, leggere nella barra in alto le coordinate dei vertici. Esse vanno sostituite a quelle preesistenti. (Molto probabilmente queste istruzioni non complete, ovvero tali che chi non ha mai visto un computer se la possa cavare. Si spera che il lettore non sia così a digiuno. L'importante è aver capito il problema e in che direzione intervenire, poi, come succede spesso con i computer, si tratta di provare. Si noti inoltre che imparare a cambiare i margini delle figure può tornare utile per centrare i grafici o per selezionarne una parte. )

Importazione di dati

Negli esempi precedenti abbiamo inserito `a mano' i dati da analizzare in liste di Mathematica. Come è comprensibile, tale operazione diventa scomoda se si vogliono analizzare dati provenienti da  altri programmi o basi di dati (ad esempio Excel). Facciamo degli esempi abbastanza semplici, rimandando al manuale di Mathematica per casi piu` complicati di interesse professionale.

Il caso più semplice è rappresentato da una tabella avente nella prima colonna dei nomi di  persona e nella seconda le relative altezze espresse in centimetri:
Giacomo 181
Anna       165
Marcella  171
....          ....
Immaginiamo che i dati siano nel file stature.dat della directory \math. 
La funzione di lettura appropriata e` ReadList, con i parametri indicati nell'esempio, il cui significato è abbastanza trasparente.

stature = ReadList["\math\stature.dat", {Word, Number}]

Il risultato e' una lista bidimensionale, dalla quale possiamo, ad esempio, estrarre la lista dei nomi, il vettore delle altezze e fare la media di queste ultime.

nomi = Transpose[stature][[1]]
altezze = Transpose[stature][[2]]
Mean[altezze] // N

Ordinare in ordine alfabetico i dati è molto semplice.

Sort[stature]

Un po' più complicato è ordinarli per voto, in quanto Sort agisce sul primo elemento della `sottolista'. Riutilizziamo le funzioni di  LinearAlgebra`MatrixManipulation`.

temp = 
        Sort[AppendRows[TakeColumns[stature, {2, 2}],  
            TakeColumns[stature, {1, 1}]]];
AppendRows[TakeColumns[temp, {2, 2}],  
      TakeColumns[temp, {1, 1}]]

Chiaramente, se un'applicazione del genere capita spesso nei nostri dati, si può construire una funzione da usare in modo compatto:

SortCol2[
        lista_] := (temp = 
          Sort[AppendRows[TakeColumns[lista, {2, 2}],  
              TakeColumns[lista, {1, 1}]]]; 
AppendRows[
          TakeColumns[temp, {2, 2}],  
          TakeColumns[temp, {1, 1}]])

Si noti l'uso delle parentesi tonde per associare due espressioni a SortCol2. Applicamola a stature.

SortCol2[stature]

Come caso un  più complicato, riprendiamo l'esempio dei voti, i cui dati si trovano nel file voti.dat.  Tale file contiene anche una linea di intestazione e, per ogni studente, viene dato anche l'anno di nascita. Inoltre, tanto per complicare la vita, immaginiamo i voti siano separati fra di loro da virgole, mentre fra nome, anno di nascita e lista di voti ci siano soltanto spazi. Inoltre il numero di voti può differire da studente a studente.
Abbiamo, quindi un file di questo tipo:
Voti Studenti
Paolo 1979 21, 30, 25, 27,  ...
Luca 1980 21, 30, 27, 29, ...
Alessandra 1978 30, 30, 28, 27, ..
Giorgio 1980 18, 25, 26, 27, ...
...........  ......   .................................
In questo caso è preferibile una lettura molto generale del tipo Word invece che Number, trasformando successivamente in numeri le stringhe che sono numeri. Diamo di seguito la lista di istruzioni utilizata, commentando quelle che generano delle stampe lunghe. Il risultato sarà: la lista dei nomi degli studenti, la lista delle date di nascita, il numero di esami sostenuto da ciascuno e la media dei voti. Per capire cosa fa ciascuna riga di programma si suggerisce di togliere i vari punti e virgole, consultare  il manuale di Mathematica e, successivamente cercare di modificare il programma per ottenere gli stessi risultati in altro modo.

voti = 
        ReadList["\math\voti.dat", Word, 
          RecordLists -> True];
voti = Drop[voti, 1];
nomi = Table[voti[[i, 1]], {i, Length[voti]}]
datanascita = 
      Table[ToExpression[voti[[i, 2]]], {i, Length[voti]}]
voti = 
        Table[Drop[voti[[i]], {1, 2}], {i, Length[voti]}];
voti = 
        Table[StringReplace[voti[[i]], "," -> ""], {i, 
            Length[voti]}];
voti = ToExpression[voti];
Map[Length, 
      voti]                                       (* Si noti Map *) 
Map[Mean, voti] // N
Map[Median, voti] // N

Si noti il valore semintero della mediana nel caso di numero pari di valori. Essa è infatti definita come il valore centrale della lista di valori, preventivamente ordinati in ordine crescente (o decrescente). Tale valore corrisponde ad uno dei dati se i dati sono in numero dispari. Se il numero è pari si prende la media dei due dati centrali.