Regressão prática
Tipos de regressão
Temos 4 tipos de regressões possíveis:
regressao_linear(x_medidas, y_medidas)regressao_polinomial(x_medidas, y_medidas, grau)regressao_exponencial(x_medidas, y_medidas, base)regressao_potencia(x_medidas, y_medidas, x0=None)
O resultado das regressões linear e polinomial é armazenado em um objeto AjustePolinomial. Para as regressões exponencial e de lei de potência, os resultados são armazenados em AjusteExponencial e AjusteLeiDePotencia, respectivamente. Esses objetos são chamáveis e agem como funções para avaliar o ajuste em novos pontos. Na prática, você só precisará chamá-los passando suas variáveis independentes.
Warning
Lembre-se que, para regressões exponenciais, todos os valores de y precisam ser positivos. No caso da regressão de lei de potência, os valores de x também precisam ser positivos. Além disso, um valor pode não ser negativo, mas devido à incerteza associada, ele pode assumir valores negativos.
Calcular Regressão
Polinomial
A função regressao_linear é um caso especial da regressao_polinomial (com grau=1), criada somente por conveniência.
Não há muito segredo nessa parte, as funções recebem os arrays de medidas em e . É importante salientar que essas funções recebem Medidas, isso porque queremos os parâmetros da regressão tendo unidades. Como exemplo, estamos imaginando aqui um objeto em queda livre vertical, nós registramos a sua posição na vertical em função do tempo e queremos encontrar a melhor parábola que se encaixa nos dados.
tempos = lab.linspaceM(0, 5, 10, "s", 0.01)
alturas = lab.arrayM([0, 1.4, 6, 13, 24, 36, 52, 70, 95, 120], "m", 0.1)
# y=y0 + vt + 1/2gt²
parabola = lab.regressao_polinomial(tempos, alturas, grau=2)
a, b, c = parabola # é possível fazer unpacking dos coeficientes
assert (2 * a).fmt() == "(9,9 ± 0,3) m/s²"
assert b.fmt(expoente=0) == "(-0,8 ± 0,7) m/s"
assert c.fmt(expoente=0) == "(0,4 ± 0,7) m"
assert parabola(lab.Medida(10, "s")).fmt() == "(4,9 ± 0,1) × 10² m"
Exponencial
Imagine um experimento em que queremos determinar a meia-vida de um material radioativo. As escalas de massa e tempo são somente ilustrativas. Podemos acessar a amplitude do ajuste fazendo exponencial.amplitude e o expoente fazendo exponencial.expoente.
tempos = lab.linspaceM(0, 10, 11, "year", 0)
massa = lab.arrayM([1,0.47,0.23, 0.12, 0.0623, 0.0311,
0.0153, 0.0078, 0.004, 0.002, 0.001],"kg",0)
exponencial = lab.regressao_exponencial(tempos, massa, base=2)
M_0 = exponencial.amplitude
meia_vida = -1 / exponencial.expoente
assert M_0.fmt(expoente=0) == "(0,95 ± 0,01) kg"
assert meia_vida.fmt(unidade="a", expoente=0) == "(1,011 ± 0,004) a"
assert exponencial(lab.Medida(3.5, "year")).fmt() == (
"(2,98 ± 0,06) × 10⁻² kg"
)
Lei de Potência
Um exemplo clássico de lei de potência é a terceira lei de Kepler, onde o período orbital (T) se relaciona com o raio orbital (R) segundo: A regressão de lei de potência encontra a amplitude e potência a partir de dados experimentais da forma:
É usado aqui um valor de referência por uma razão técnica, ele garante que a razão seja adimensional, prevenindo que a amplitude receba tenha uma unidade com expoentes fracionários (como ), que faz com ela seja uma Medida quase que impossível de interagir e ser comparada com outra Medida. Usando , garantimos que a amplitude tem exatamente a mesma unidade física da variável .
O exemplo abaixo pega dados da NASA para demonstrar experimentalmente a terceira lei de Kepler. Eu peguei as distâncias em milhas justamente para demonstrar como que com o LabIFSC2 você não precisa se preocupar com unidades.
dados: list[dict[str, str | float]] = [
{"planeta": "Mercúrio", "distancia": 36.0, "periodo": 88.0},
{"planeta": "Vênus", "distancia": 67.2, "periodo": 224.7},
{"planeta": "Terra", "distancia": 93.0, "periodo": 365.2},
{"planeta": "Marte", "distancia": 141.6, "periodo": 687.0},
{"planeta": "Júpiter", "distancia": 483.7, "periodo": 4331.0},
{"planeta": "Saturno", "distancia": 889.8, "periodo": 10747.0},
{"planeta": "Urano", "distancia": 1781.5, "periodo": 30589.0},
{"planeta": "Netuno", "distancia": 2805.5, "periodo": 59800.0},
]
distancias = lab.arrayM(
[float(planeta["distancia"]) for planeta in dados], "Mmiles", 0
)
periodos = lab.arrayM([float(planeta["periodo"]) for planeta in dados], "days", 0)
fitting = lab.regressao_potencia(distancias, periodos)
assert fitting.potencia.fmt() == "(1,4979 ± 0,0008)"
G = lab.constantes.Newtonian_constant_of_gravitation
pi = lab.constantes.pi
massa_sol = lab.constantes.solar_mass
constante_teorica = np.sqrt(4 * pi**2 / (G * massa_sol))
assert constante_teorica.fmt(unidade="si") == (
"(5,4540 ± 0,0001) × 10⁻¹⁰ s/m¹⋅⁵"
)
assert fitting.amplitude.fmt(unidade="si") == "(5,8 ± 0,1) × 10⁻¹⁰ s"
Avaliar / Amostrar
Para avaliar um ajuste em um conjunto de pontos, basta chamá-lo como uma função. O objeto do ajuste receberá uma Medida ou uma sequência de Medida e retornará o resultado calculado com propagação de incerteza correspondente. Para obter os valores numéricos em uma unidade específica (por exemplo, para plotar com o matplotlib), use as funções nominais e incertezas.
ajuste(x)-> Retorna o(s) valor(es) ajustado(s) comoMedidaou array deMedida.
No exemplo abaixo, calculamos a nossa regressão (da seção anterior) no intervalo de distâncias dos planetas do sistema solar ([0,30] unidades astronômicas), e pedimos para ele retornar esse resultado em anos.
unidade_x = "astronomical_unit"
unidade_y = "years"
x = lab.linspaceM(0, 30, 100, "astronomical_unit", 0)
amostragem = fitting(x)

Podemos visualizar esses dados fazendo um pequeno código em matplotlib. Para ler mais sobre gráficos, vá para a seção Gráficos.
plt.style.use("ggplot")
plt.plot(
lab.nominais(x, unidade_x),
lab.nominais(amostragem, unidade_y),
color="red",
label="Teórica",
)
plt.scatter(
lab.nominais(distancias, unidade_x),
lab.nominais(periodos, unidade_y),
color="blue",
label="Dados",
)
plt.xlabel(f"Distâncias ({unidade_x})")
plt.ylabel(f"Períodos ({unidade_y})")
plt.legend()
plt.savefig("docs/images/kepler.jpg", dpi=300)
plt.cla()
Warning
Visto que as operações matemáticas com Medida realizam simulação Monte Carlo por baixo dos panos, avaliar a regressão em muitos pontos de amostragem pode causar uma lentidão no seu código. Uma boa prática é avaliar a regressão em cerca de 100 pontos. Recomendo começar com esse valor.
Outra dica: caso tenha que usar a amostragem mais de uma vez, salve o array em uma variável, assim não precisará calcular duas vezes. Eu fiz isso no código de exemplo, salvando o resultado de fitting(x) em uma variável.
curva_min e curva_max
Uma função que creio ser muito útil é aplicar curva_min e curva_max em uma regressão. Para que tudo sobre gráficos fique na sua respectiva seção, vá para a seção de gráficos para ler a documentação sobre isso.