-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPy5.py
More file actions
351 lines (240 loc) · 10.9 KB
/
Py5.py
File metadata and controls
351 lines (240 loc) · 10.9 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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
import marimo
__generated_with = "0.23.6"
app = marimo.App()
@app.cell
def _():
import marimo as mo
return (mo,)
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
---
**Universidade da Costa Rica** | Escola de Engenharia Elétrica
*IE0405 - Modelos Probabilísticos de Sinais e Sistemas*
### `PyX` - Série de tutoriais em Python para análise de dados
# `Py5` - *Curvas de ajuste de dados*
> Modelos para descrever um fenômeno e seus parâmetros podem ser obtidos a partir de uma amostra de dados. Devido ao grande número de modelos probabilísticos disponíveis, muitas vezes é necessário fazer uma comparação adequada entre muitos deles.
*Fabian Abarca Calderón* \
*Jonathan Rojas Sibaja*
---
""")
return
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
---
## 5.1. Ajuste de modelo com SciPy/Stats
Objetos criados com SciPy/Stats possuem vários métodos, incluindo:
| Método | Descrição |
|----------------------------------|------------------------------------------------------------|
| `rvs()` | Gerador de números aleatórios. |
| `pdf(x)` | Função densidade de probabilidade |
| `cdf(x)` | Função de probabilidade cumulativa |
| `stats(momentos=’mv’)` | Média('m'), variância('v'), inclinação('s'), curtose('k'). |
| `ajustar(dados)` | Estimativa dos parâmetros de melhor ajuste. |
| `mediana()` | Distribuição mediana. |
| `média()` | Meio dia distribuído. |
| `var()` | Variância distributiva. |
| `std()` | Desvio padrão da distribuição. |
A função `fit()`, especificamente, encontra os parâmetros de melhor ajuste para um determinado conjunto de dados.
#### Exemplo
A seguir, será gerada uma distribuição exponencial com parâmetro $\lambda = 2$ e obtidos os parâmetros de melhor ajuste para os dados aleatórios.
**Nota**: SciPy/Stats nem sempre usa os mesmos parâmetros teóricos usuais. Exemplo: na função exponencial diz:
> Uma parametrização comum para expon é em termos do parâmetro de taxa lambda, tal que pdf = lambda * exp(-lambda * x). Esta parametrização corresponde ao uso de escala = 1/lambda.
Portanto, neste caso o valor $\lambda$ procurado é `lambda = 1/scale` e, portanto, `scale = 1/lambda = 1/2 = 0,5`, para este exemplo.
""")
return
@app.cell
def _():
from scipy import stats
# Crear objeto aleatorio de ejemplo
X = stats.expon(0, 1 / 2)
# Generar números aleatorios a partir de X
data = X.rvs(400)
# Estimar parametros de ajuste de dados aleatorios
params = stats.expon.fit(data)
print("Los parametros son: loc = {}, scale = {}.".format(params[0], params[1]))
return (stats,)
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
É possível perceber que o valor da `escala` está razoavelmente próximo do valor esperado de 0,5.
""")
return
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
---
## 5.2. Pacote de ajuste
O pacote `fitter` automatiza e simplifica a obtenção de modelos de melhor ajuste para um conjunto de dados, usando todas as distribuições SciPy/Stats disponíveis (ou algumas, dependendo da especificação).
Sejam `dados` um conjunto de dados com medidas numéricas de alguma variável de interesse. com
```python
from fitter import Fitter
f = Fitter(data)
f.fit()
```
O programa percorrerá todas as distribuições SciPy/Stats e encontrará os parâmetros mais adequados.
**Observação**: Como há muitas distribuições possíveis, isso pode levar um tempo considerável.
Então, com:
```python
f = Fitter(data, distributions=['expon', 'norm', 'gamma'])
```
É possível limitar o ajuste a apenas algumas distribuições.
Com:
```python
f.summary()
```
É possível obter uma tabela com o ranking de melhor ajuste (baseado no critério da soma dos quadrados do erro) e um gráfico Matplotlib com o histograma dos dados juntamente com as curvas das funções de densidade (PDF) das distribuições da tabela.
Para obter os parâmetros dessas distribuições, eles estão acessíveis via:
```python
f.fitted_param
```
que eh dá forma:
```python
{'expon': (1.5516548212580765, 3.837313054949372), 'norm': (5.388967876207449, 2.6612762163716237), 'gamma': (2.059695507081982, 1.4639030411838343, 1.9056503608256832)}
```
Ou seja, um dicionário com o nome da distribuição e seus parâmetros de melhor ajuste. Caso seja necessário obter os parâmetros de uma distribuição específica, é possível escrever, por exemplo:
```python
f.fitted_param['gamma']
```
e obtenha:
```python
(2.059695507081982, 1.4639030411838343, 1.9056503608256832)
```
**Nota**: observe que o número de parâmetros varia dependendo da distribuição. Além disso, os parâmetros SciPy/Stats não são necessariamente os mesmos que aparecem na teoria.
É ainda possível obter os parâmetros da curva de melhor ajuste indicando apenas:```python
f.get_best()
```que retorna o nome da distribuição com melhor ajuste e seus parâmetros como um dicionário.
No exemplo a seguir será gerada uma amostra de 1000 pontos com uma distribuição de exemplo, e então será utilizado o `fitter`, que irá revisar as mais de 80 distribuições SciPy e exibir um resumo com as distribuições e seus parâmetros.
""")
return
@app.cell
def _(stats):
from fitter import Fitter
data_1 = stats.gamma.rvs(2, loc=1.5, scale=2, size=1000)
f = Fitter(data_1, distributions=["expon", "norm", "rayleigh", "uniform"])
# Crear los dados de ejemplo
f.fit()
# Definir cuáles distribuiçõeh va a evaluar
# Realizar el ajuste para las distribuiçõeh seleccionadas
# Mostrar principales resultados y grafico
f.summary()
return (f,)
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
Os parâmetros de melhor ajuste de todas as distribuições utilizadas estão em:
""")
return
@app.cell
def _(f):
params_1 = f.fitted_param
print(params_1)
return
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
---
## 5.3. Polinômio se ajusta ao módulo `numpy`
Embora não sejam uma distribuição de probabilidade em si, às vezes funções polinomiais genéricas podem ser usadas para descrever alguns dados. Isto pode ser particularmente útil em distribuições "multimodais", ou seja, que possuem mais de uma concentração de valores, como as seguintes:
<img src="https://upload.wikimedia.org/wikipedia/commons/e/e2/Bimodal.png"largura="300px">
Com a função `polyfit()` da biblioteca `numpy` é possível ajustar dados experimentais a polinômios de qualquer ordem. Esta função retorna os parâmetros da linha para um modelo linear na forma:
$$
f(x) = mx + b
$$
Isto é no caso de um polinômio de grau 1. Um exemplo de utilização deste método é o seguinte:
""")
return
app._unparsable_cell(
r"""
from numpy import *
import matplotlib.pyplot as plt
# Dados experimentales
x = array([ 0., 1., 2., 3., 4.])
y = array([ 10.2 , 12.1, 15.5 , 18.3, 20.6 ])
# Ajustar a uma recta (polinomio de grado 1)
p = polyfit(x, y, 1)
# Crear la recta de ajuste obtenida.
y_ajuste = p[0]*x + p[1]
# Graficar los dados experimentales
p_dados, = plt.plot(x, y, 'b.')
# Agregar la recta de ajuste
p_ajuste, = plt.plot(x, y_ajuste, 'r-')
plt.title('Ajuste linear por mÃnimos quadrados')
plt.xlabel('Eixo x')
plt.ylabel('Eixo y')
plt.legend(('Dados experimentales', 'Ajuste linear'), loc="upper left")
plt.show()
""",
name="_",
)
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
## 5.4. Métricas de desempenho
**(Opcional)**
Para avaliar o ajuste de dois modelos probabilísticos, normalmente são usados um ou mais dos seguintes indicadores de erro.
Nas definições a seguir, $\hat{x}$ são os dados previstos ou valor previsto e $x$ são os dados medidos ou valor verdadeiro.
### Erro máximo
O erro máximo (ME) captura o maior valor absoluto de erro entre todas as previsões e medições.
\começo{equação}
\mathrm{ME} = \max_{1 \leq i \leq n} \{ | \hat{x}_i - x_i | \}
\label{E:eu}
\fim{equação}
### Erro médio absoluto
O erro médio absoluto (MAE) encontra a magnitude média do erro para cada medição.
\começo{equação}
\mathrm{MAE} = \frac{1}{n} \sum _{i = 1}^n | \hat{x}_i - x_i |
\label{E:mae}
\fim{equação}
### Erro mediano absoluto
O erro absoluto mediano (MedAE) encontra o valor médio de dois valores de erro ordenados para cada medição.
\começo{equação}
\mathrm{MedAE} = \mathrm{mediana} (| \hat{x}_1 - x_1 |, \ldots, | \hat{x}_i - x_i |)
\label{E:medae}
\fim{equação}
### Erro percentual médio absoluto
O erro percentual médio absoluto (MAPE), também chamado de erro relativo médio \acrdef{mre}, é uma modificação do erro absoluto médio que é calculado em termos relativos (porcentagem) ao valor verdadeiro.
\começo{equação}
\mathrm{MAPE} = \frac{1}{n} \sum _{i = 1}^n \left| \frac{\hat{x}_i - x_i}{x_i} \right|
\label{E:mapa}
\fim{equação}
### Erro médio percentual absoluto simétrico
O erro percentual médio absoluto simétrico (SMAPE) fornece resultados entre 0\% e 200\%.
\começo{equação}
\mathrm{SMAPE} = \frac{200\%}{n} \sum _{i = 1}^n \frac{|\hat{x}_i - x_i|}{|\hat{x}_i| + |x_i|}
\label{E:smape}
\fim{equação}
### Erro quadrático médio
O erro quadrático médio (MSE) é a média aritmética do quadrado do erro. Penaliza erros grandes com mais rigor do que erros pequenos, dependendo do quadrado da sua magnitude.
\começo{equação}
\mathrm{MSE} = \frac{1}{n} \sum _{i = 1}^n ( \hat{x}_i - x_i )^2
\label{E:mse}
\fim{equação}
### Raiz do erro quadrático médio
A raiz do erro quadrático médio (RMSE) é sempre positiva e menos sensível que o MSE a valores atipicamente grandes.
\começo{equação}
\mathrm{RMSE} = \sqrt{\frac{1}{n} \sum _{i = 1}^n ( \hat{x}_i - x_i )^2}
\label{E:rmse}
\fim{equação}
""")
return
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
---
###Mais informações
* [Estatísticas SciPy](https://docs.scipy.org/doc/scipy/reference/stats.html)* [Ajustador](https://fitter.readthedocs.io/en/latest/index.html)
""")
return
@app.cell(hide_code=True)
def _(mo):
mo.md(r"""
---
**Universidade da Costa Rica** | Faculdade de Engenharia | Escola de Engenharia Elétrica
© 2022
---
""")
return
if __name__ == "__main__":
app.run()