-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathesame.py
More file actions
299 lines (223 loc) · 15.7 KB
/
esame.py
File metadata and controls
299 lines (223 loc) · 15.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
"""
STANDARD:
Dati un numero n di anni, si trovano il primo e l'ultimo valore per ogni mese non nulli e viene calcolata la media in base a questi, ignorando la possibilità che ci siano valori non definiti all'interno dell'intervallo, in quanto non servono per il calcolo della media mensile.
VARIANTE:
Dati un numero n di anni, si trovano il primo e l'ultimo valore per ogni mese non nulli e viene calcolata la media in base a questi, controllando però che non ci siano valori non definiti all'interno dell'intervallo. Quando all'interno dell'intervallo si riscontra un valore nullo (nella lista di liste matrice_dati è stato inserito 0 per ogni valore mancante), il numero per cui dividere non incrementa.
In pratica, STANDARD usa un divisore fissato, dato dal delta tra il primo e l'ultimo anno nell'intervallo richiesto di cui ho valori, mentre VARIANTE lo calcola in base al numero di valori non nulli all'interno dell'intervallo.
Esempio
1949-01,150
1950-01, 130
1952-01, 200
STANDARD: (200-150)/3 = 16.6
VARIANTE: (200-150) / 2 = 25
"""
class ExamException(Exception):
pass
class CSVTimeSeriesFile:
def __init__(self, name):
#name è il nome del file
self.name = name
def get_data(self):
#provo ad aprire il file
try:
my_file = open(self.name, 'r')
my_file.readline()
#se non riesco ad aprire il file, perché non lo trovo, alzo un'eccezione:
except:
raise ExamException("Errore in apertura del file {}".format(self.name))
# Inizializzo una lista vuota per salvare tutti i dati
time_series = []
# last_date è il mese precedente al primo che mi potrebbe venir assegnato mi serve per verificare che l'input sia ordinato e non contenga righe doppie
last_date = "1948-12"
# Apro il file
my_file = open(self.name, 'r')
# Leggo il file linea per linea
for line in my_file:
# Faccio lo split di ogni linea sulla virgola
if "," in line:
elements = line.split(',')
is_data = False
is_int = True
if len(elements) < 2:
#non lavoro su questi dati e li ignoro
is_data = False
is_int = False
elif len(elements) == 2:
# Con la funzione strip() tolgo il carattere di new line:
elements[1] = elements[1].strip()
else:
while len(elements)>2:
elements.pop(-1)
elements[1].strip()
#datum è una lista contentente la prima parte di elements, cioè la data
datum = [0, 0]
#provo a dividere sulla "-" che so essere l'elemento
if "-" in elements[0]:
datum = elements[0].split('-')
#quando non è presente, l'elemento che sto analizzando non è una data nel formato richiesto e quindi lo ignoro o è la riga di intestazione e quindi la ignoro
is_data = True
datum[0] = int(datum[0])
datum[1] = int(datum[1])
if datum[0] not in range(1949, 1960):
is_data = False
if datum[1] not in range(1, 13):
is_data = False
# is_data mi permette di controllare se si tratta dell'intestazione, di righe fuori dal range o se si tratta di pezzi di testo che non c'entrano nulla
if is_data is True:
#controllo che il valore sia un intero
try:
elements[1] = int(elements[1])
except:
is_int = False
if is_int == True and is_data == True:
#controllo che il valore non sia negativo o nullo
if elements[1] > 0:
if elements[0] > last_date:
# Aggiungo alla lista gli elementi di questa linea
time_series.append(elements)
last_date = elements[0]
elif elements[0] == last_date:
raise ExamException("L'elemento {} è duplicato".format(
elements[0]))
elif elements[0] < last_date:
raise ExamException("L'elemento {} è fuori sequenza".format(elements[0]))
#altrimenti ignoro, come da consegna
# Chiudo il file
my_file.close()
# Quando ho processato tutte le righe, ritorno i dati
return time_series
def compute_avg_monthly_difference(time_series, first_year, last_year):
#time_series è la variabile con la lista di liste tratta dal File
# se first_year o last_year non sono una stringa, alzo un'eccezione
if type(first_year) is not str:
raise ExamException("L'anno di inizio non è stato inserito come stringa")
if type(last_year) is not str:
raise ExamException("L'anno di fine non è stato inserito come stringa")
#se l'anno di fine è successivo all'anno di inizio, vengono invertiti
if last_year < first_year:
last_year, first_year = first_year, last_year
#i dati del file iniziano con il 1949, quindi non è possibile che l'anno di inizio sia antecedente a questo, viene quindi alzata un'eccezione:
if first_year < "1949":
raise ExamException("L'anno di inizio è antecedente al 1949")
#i dati del file finiscono con il 1960, quindi non è possibile che l'anno di fine sia successivo a questo, viene quindi alzata un'eccezione:
if last_year > "1960":
raise ExamException("L'anno di fine è successivo al 1960")
#se l'anno di inizio e l'anno di fine coincidono, viene alzata un'eccezione in quanto non è possibile effettuare i calcoli
if last_year == first_year:
raise ExamException("L'anno di inizio e di fine coincidono, non è possibile effettuare la media richiesta")
#l'anno con cui iniziare a costruire la matrice è first_year, viene convertito a intero in modo da permettere il confronto con dati successivi
first_year = int (first_year)
#l'anno con cui finire a costruire la matrice è last_year, viene convertito a intero in modo da permettere il confronto con dati successivi
last_year = int (last_year)
#totale dati mi permette di sapere la quante coppie di dati ho in time_series e quindi iterare più facilmente
totale_dati = len(time_series)
#curr_year_n_month è una lista contentente come primo elemento l'anno e come secondo il mese della coppia di dati di time_series che sto analizzando. Le dò come primo valore il primo della lista time_series in modo da controllare se è minore del primo anno
curr_year_n_month = time_series[0] [0].split("-")
#converto l'anno a intero per permettere il confronto
curr_year_n_month [0] = int(curr_year_n_month[0])
#se il primo anno di cui ho valori è successivo al primo anno da cui devo partire, alzo un'eccezione
if curr_year_n_month [0] > first_year:
raise ExamException ("Valore non presente nell'intervallo di dati")
#curr_year_n_month è una lista contentente come primo elemento l'anno e come secondo il mese della coppia di dati di time_series che sto analizzando. Le dò come valore ora l'ultimo della lista time_series in modo da controllare se è maggiore dell'ultimo anno
curr_year_n_month = time_series[len(time_series)-1] [0].split("-")
#converto curr_year_n_month a intero per permettere il confronto
curr_year_n_month [0] = int(curr_year_n_month[0])
#se l'ultimo anno di cui ho valori è precedente all'ultimo anno con cui devo fare la media, alzo un'eccezione:
if curr_year_n_month [0] < last_year:
raise ExamException ("Valore non presente nell'intervallo di dati")
#creo la lista di liste matrice_dati (la posso immaginare come una matrice) dove inserire solamente i valori degli anni di cui ho bisogno per fare i calcoli, se non ho qualche valore lo sostituisco con 0
matrice_dati = []
#delta mi permette di scoprire di quante righe ho bisogno per la mia matrice
delta = last_year - first_year + 1
#creo un numero di righe uguali a delta
for i in range (0, delta):
matrice_dati.append([])
#riempio con dodici elementi uguali a 0 ciascuna "riga" della matrice
for j in range (0, 12):
matrice_dati[i].append(0)
#VARIANTE: creo una lista di dodici elementi in cui segno quanti dati non nulli ho per ciascun mese. La riempio di valori uguali a -1 perché con n dati il divisore è (n-1)
#STANDARD: divisore viene ugualmente utilizzato, ma solamente per non scorrere tutte le colonne della matrice quando si sa a priori che non è possibile effettuare il calcolo
divisore = []
for i in range (0, 12):
divisore.append(-1)
#inizio a riempire la matrice
#scorro tutta la lista che mi è stata data in imput dall'utente
for i in range (0, totale_dati):
#come prima, creo una lista, curr_year_n_month per sapere in che anno e in che mese mi trovo. Converto gli elementi in interi in modo da permettermi confronti con altri numeri
curr_year_n_month = time_series [i] [0].split("-")
curr_year_n_month[0] = int(curr_year_n_month[0])
curr_year_n_month[1] = int (curr_year_n_month[1])
#se l'anno che sto guardando è antecedente all'anno da cui devo partire per fare la media, lo ignoro
if curr_year_n_month [0] < first_year:
pass
#se l'anno che sto analizzando è successivo all'anno con cui devo finire di fare la media, esco dal ciclo, in quanto so che la lista è stata ordinata cronologicamente e quindi non ho altri elementi da analizzare
elif curr_year_n_month [0] > last_year:
break
#se sono in uno degli anni che mi interessa vado a compilare la matrice
else:
#indice_anno mi permette di capire in che riga della matrice sono, infatti è dato dalla differenza tra l'anno che sto analizzando e l'anno da cui devo partire
indice_anno = curr_year_n_month[0] - first_year
#indice_mese è uguale al mese attuale - 1, infatti, gennaio occuperebbe la posizione 0 nella lista
indice_mese = curr_year_n_month [1] - 1
#cambio il valore nella riga indice_anno nella colonna indice_mese di matrice_dati con il valore di time_series che sto analizzando
matrice_dati [indice_anno] [indice_mese] = time_series [i] [1]
#VARIANTE e STANDARD: per ogni elemento che trovo in un mese incremento di uno
divisore [indice_mese] += 1
#creo "risultati" una lista dove inserire i risultati delle medie
risultati = []
valore = 0
#RAGIONAMENTO PER IL PROSSIMO CICLO:
#La formula della differenza media è data da:
#(b-a)+(c-b)+...+(z-y) / num
#a causa della formula tutti gli elementi che non sono il primo (a) e l'ultimo (z) vengono semplificati: le proprietà delle operazioni infatti mi permettono di togliere le parentesi e riordinare, immaginando i - come +(-), ottenendo quindi al numeratore:
#b+(-a)+c+(-b)+...+z+(-y) =>
#-a + b + (-b) + c + (-c) + ... + y + (-y) + z =>
#-a+z = z-a
#cioè al numeratore ho la differenza tra l'ultimo e il primo elemento
#riuscendo a sapere quanto vale il denumeratore, posso evitare di effettuare tutti i calcoli intermedi e andare a lavorare solo sul primo e sull'ultimo, gestendo quando eventualmente uno dei due o entrambi sono uguali a zero
#itero su ciascuna colonna
for i in range (0, 12):
#se nella colonna ho più di un valore non nullo, allora posso fare i calcoli:
if divisore [i] > 0:
#la prima riga da cui devo partire è messa a -1 perché non appena entro nel while l'incremento di uno e quindi diventa zero. Se l'avessi messa a 0, avrei dovuto mettere nel while:
#inizio = matrice_dati [r_ini] [i]
#r_ini += 1
#E quindi fuori dal while, dentro all'else, r_ini-=1. Ho pensato che per mantenere il codice sarebbe stato più semplice avere meno cambiamenti della variabile da controllare
r_ini = -1
#l'ultima riga da cui devo partire è uguale a len(matrice_dati) e non a (len(matrice_dati) - 1) per un ragionamento analogo a quello su r_ini
r_fin = len(matrice_dati)
#inizializzo inizio e fine a 0, sono rispettivamente il primo e l'ultimo valore non nulli della colonna e quelli utilizzati per la media
inizio = 0
fine = 0
#fino a quando non ho un valore non nullo nella colonna o fino a quando non termino di analizzare i dati nella colonna, cerco il valore di inizio
#nelle condizioni del while "r_ini < r_fin - 1" perché viene aumentato immediatamente dopo il valore di r_ini e se accettassi che r_ini sia minore stretto di r_fin (r_fin = len (matrice_dati)), avrei che nel caso di una colonna composta interamente da zeri, l'indice sarebbe uguale a len(matrice_dati), ma nella lista non ci sono valori in quell'indice, risultando in un errore
while inizio == 0 and r_ini < r_fin - 1 :
r_ini += 1
inizio = matrice_dati [r_ini] [i]
#se r_ini è uguale alla lunghezza della lista meno uno, vuol dire che l'ho attraversata tutta e, indifferentemente se il valore ultimo di inizio è uguale a zero o meno, non mi è possibile effettuare calcoli, quindi, come per istruzioni, il valore corrispondente a quel mese che verrà ritornato nella lista sarà uguale a 0
if r_ini == (r_fin - 1):
valore = 0
#altrimenti, vuol dire che almeno per l'inizio posso effettuare i calcoli
else:
#fino a quanod non ho un valore non nullo nella colonna o fino a quando non termino di analizzare i dati nella colonna, cerco il valore di fine non nullo
#nelle condizioni del while "r_fin > 0", così l'ultima volta che entro nel ciclo il suo valore sarà 1, e verrà quindi modificato in 0, rimanendo all'interno della lista
#parto dall'ultima riga della matrice in maniera da trovare l'ultimo valore non nullo della colonna della matrice
while fine == 0 and r_fin > 0:
r_fin -= 1
fine = matrice_dati [r_fin] [i]
#se r_fin (cioè l'ultima riga della matrice nella cui colonna ho un valore non nullo) è uguale a r_ini (cioè la prima riga della matrice nella cui colonna ho un valore non nullo), il valore corrispondente a quel mese è 0, in quanto ho solamente un dato non nullo in tutta la colonna
if r_fin == r_ini:
valore = 0
#altrimenti significa che ho almeno due valori con la riga distinta tra loro che mi permettono di effettuare i calcoli
else:
#STANDARD:
"""
valore = (fine - inizio) / (r_fin - r_ini)
"""
#VARIANTE:
valore = (fine - inizio) / divisore [i]
else:
valore = 0
#aggiungo il valore che ho calcolato alla lista contentente i risultati
risultati.append(valore)
return risultati