Skip to content

Commit

Permalink
classes para regressões
Browse files Browse the repository at this point in the history
pequenas alterações de assinatura
  • Loading branch information
viniciusdutra314 committed Jan 23, 2025
1 parent 15e7023 commit 314d34c
Show file tree
Hide file tree
Showing 21 changed files with 210 additions and 268 deletions.
12 changes: 6 additions & 6 deletions LabIFSC2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
Documentação completa em https://viniciusdutra314.github.io/LabIFSC2/
'''

MCSamples=100_000

from ._arrays import (arrayM, curva_max, curva_min, incertezas, linspaceM,
nominais)
from ._medida import Comparacao, Medida, comparar_medidas, montecarlo
from ._regressões import (MExponencial, MPolinomio, regressao_exponencial,
regressao_linear, regressao_polinomial,
regressao_potencia)
from ._arrays import arrayM, incertezas, linspaceM, nominais
from ._medida import (Comparacao, Medida, alterar_monte_carlo_samples,
comparar_medidas)
from ._regressões import (regressao_exponencial, regressao_linear,
regressao_polinomial, regressao_potencia)
from .constantes import constantes
44 changes: 2 additions & 42 deletions LabIFSC2/_arrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,50 +61,10 @@ def incertezas(arrayMedidas : np.ndarray,unidade:str) -> np.ndarray:
else:
return np.array([medida._incerteza.to(unidade).magnitude for medida in arrayMedidas],dtype=float)

@obrigar_tipos
def curva_min(arrayMedidas : np.ndarray,unidade:str,sigma: float | int =2) -> np.ndarray:
'''Usada para auxiliar o plot de curvas teóricas
com erros, com quantidade de sigmas usados personalizável
curva_min=lab.get_nominais(arrayM) - sigmas*lab.get_incertezas(arrayM)
Args:
arrayMedida (Sequence): iterável com Medidas
sigma (Number): confiança estatística
Returns:
arrayCurva (np.ndarray): array com a curva de Medidas
'''
if not (isinstance(arrayMedidas[0],Medida)):
raise TypeError('Os valores do array não são Medidas')
result:np.ndarray=nominais(arrayMedidas,unidade) -sigma*incertezas(arrayMedidas,unidade)
return result

@obrigar_tipos
def curva_max(arrayMedidas : np.ndarray,unidade:str,sigma:float | int=2)-> np.ndarray:
'''Usada para auxiliar o plot de curvas teóricas
com erros, quantidade de sigmas usados personalizável
CurvaMin=Nominais(arrayM) + sigmas*Incertezas(arrayM)
Args:
arrayM (Sequence[Medida]) (array,lista,...) com Medidas
sigma (Number, default=2) relacionada com a confiança estatística
Returns:
arrayCurva (np.ndarray[Medida]): array com a curva de Medidas
'''
if not (isinstance(arrayMedidas[0],Medida)):
raise TypeError('Os valores do array não são Medidas')
result:np.ndarray=nominais(arrayMedidas,unidade) +sigma*incertezas(arrayMedidas,unidade)
return result


@obrigar_tipos
def linspaceM(a:Real,b:Real,n : int,
incertezas : Real ,unidade : str) -> np.ndarray:
unidade : str,incertezas : Real ,) -> np.ndarray:
"""Gera um array com N Medidas de valor nominal [a,b]
A incerteza será constante caso 'incertezas' for um número,
mas se ela for um array cada Medida terá a respectiva incerteza.
Expand Down Expand Up @@ -139,7 +99,7 @@ def linspaceM(a:Real,b:Real,n : int,


@obrigar_tipos
def arrayM(nominais:np.ndarray | Sequence ,incerteza:Real,unidade:str) ->np.ndarray:
def arrayM(nominais:np.ndarray | Sequence ,unidade:str,incerteza:Real) ->np.ndarray:
'''Converte um array de números em um array de Medidas
Args:
Expand Down
42 changes: 23 additions & 19 deletions LabIFSC2/_medida.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@
from pint.facets.plain import PlainQuantity
from pint.util import UnitsContainer

from . import MCSamples
from ._tipagem_forte import obrigar_tipos


@obrigar_tipos
def alterar_monte_carlo_samples(novo_valor:int) -> None:
global MCSamples
assert novo_valor>0, "MCSamples deve ser maior que 0"
MCSamples=novo_valor
return None

ureg = UnitRegistry()


def montecarlo(func : Callable,
*parametros : 'Medida',N:int=100_000,
histograma_completo: bool=True) -> 'Medida':
def montecarlo(func : Callable,*parametros : 'Medida',) -> 'Medida':
x_samples=np.empty(len(parametros),dtype=Quantity)
for index,parametro in enumerate(parametros):
x_samples[index]=parametro.histograma
Expand All @@ -31,7 +38,6 @@ def montecarlo(func : Callable,
resultado=Medida(mean.magnitude,std.magnitude,str(histograma.units))
else:
resultado=Medida(mean,std,"")
if histograma_completo: resultado._histograma=histograma
return resultado

class Medida:
Expand Down Expand Up @@ -64,12 +70,13 @@ def _erro_por_mudar_atributo(self: 'Medida') -> None:
Caso precise de uma nova Medida, crie outra com o \
construtor padrão Medida(nominal, incerteza, unidade)")

@obrigar_tipos
def nominal(self:'Medida',unidade:str) -> float:
if unidade.lower()=='si':
return float(self._nominal.to_base_units().magnitude)
else:
return float(self._nominal.to(unidade).magnitude)

@obrigar_tipos
def incerteza(self:'Medida',unidade:str) -> float:
if unidade.lower()=='si':
return float(self._incerteza.to_base_units().magnitude)
Expand All @@ -85,20 +92,13 @@ def histograma(self:'Medida') -> Any:
if self._histograma is None:
if self._incerteza.magnitude!=0:
self._histograma=np.random.normal(self._nominal.magnitude,
self._incerteza.magnitude,size=100_000)*self._nominal.units
self._incerteza.magnitude,
size=MCSamples)*self._nominal.units
else:
self._histograma=self._nominal
return self._histograma

@histograma.setter
def histograma(self:'Medida',value:Any) -> None:
self._erro_por_mudar_atributo()

@histograma.deleter
def histograma(self:'Medida') -> None:
self._erro_por_mudar_atributo()


@obrigar_tipos
def __format__(self, format_spec:str) -> str:


Expand Down Expand Up @@ -172,11 +172,14 @@ def __format__(self, format_spec:str) -> str:
unidade=f"{nominal_pint.units:~L}" if latex else f"{nominal_pint.units:~P}"
return template.substitute(nominal=arred_nominal_str,incerteza=arred_incerteza_str,
potencia=potencia_bonita,unidade=unidade)

def __str__(self) -> str:
return self.__format__('')
printado:str=self.__format__('')
return printado
def __repr__(self) -> str:
return self.__format__('')
representacao:str=self.__format__('')
return representacao

'''O método abaixo faz a magia que basicamente qualquer função do numpy possa
ser aplicada diretamente em uma medida
'''
Expand Down Expand Up @@ -344,7 +347,8 @@ def probabilidade_de_estar_entre(self,a:Real,b:Real,unidade:str) -> float:
probabilidade= np.mean((self._histograma >= a_quantidade) & (self._histograma <= b_quantidade),dtype=float)
return float(probabilidade)

def intervalo_de_confiança(self:'Medida',p:Real,unidade:str) -> list[float]:
@obrigar_tipos
def intervalo_de_confiança(self:'Medida',p:Real,unidade:str) -> list:
''' Retorna o intervalo de confiança para a Medida
com base no histograma
Expand Down
97 changes: 65 additions & 32 deletions LabIFSC2/_regressões.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ._tipagem_forte import obrigar_tipos


@obrigar_tipos
def _aplicar_funcao_sem_passar_pelo_sistema_de_unidades(
array_medidas:np.ndarray,lab_func:Callable)->np.ndarray:
'''
Expand All @@ -29,42 +30,59 @@ def _aplicar_funcao_sem_passar_pelo_sistema_de_unidades(
incerteza=medida_intermediaria._incerteza._magnitude
medidas_novas.append(Medida(nominal,incerteza,unidade))
return np.array(medidas_novas)
def _forcar_troca_de_unidade(medida:Medida|np.ndarray,unidade:str)-> Medida | np.ndarray:
if isinstance(medida,np.ndarray):
return np.array([Medida(med._nominal.magnitude,med._incerteza.magnitude,unidade) for med in medida])
else:
return Medida(medida._nominal.magnitude,medida._incerteza.magnitude,unidade)
@obrigar_tipos
def _forcar_troca_de_unidade(array_medidas:np.ndarray,unidade:str)-> np.ndarray:
return np.array([Medida(med._nominal.magnitude,med._incerteza.magnitude,unidade) for med in array_medidas])

class ABCRegressao(ABC):

def __init__(self)->None:
self._amostragem_pre_calculada_nominal: np.ndarray = np.array([])
self._amostragem_pre_calculada_incerteza: np.ndarray = np.array([])
self._valores: Iterator = iter([])
self._sigmas:float=2

def _retornar(self,y:np.ndarray|Medida,unidade_y:str,retornar_como_medidas:bool=False)->np.ndarray|Medida:
if isinstance(y,Medida): y=np.array([y])
self._amostragem_pre_calculada_nominal=nominais(y,unidade_y)
self._amostragem_pre_calculada_incerteza=incertezas(y,unidade_y)
if y.size==1: y=y[0]
if retornar_como_medidas: return y
else: return self._amostragem_pre_calculada_nominal

def _verificar_tipo_de_x(self,x:np.ndarray|Medida)->None:
if isinstance(x,np.ndarray):
if not isinstance(x[0],Medida):
raise TypeError("x precisa ser um array de medidas ou uma medida \
mesmo que com incerteza 0, pois precisamos das unidades")
return None

@abstractmethod
def __repr__(self)->str:...

@abstractmethod
def amostrar(self, x:np.ndarray,unidade_x:str,unidade_y:str) -> np.ndarray:...
def amostrar(self, x:np.ndarray|Medida,
unidade_y:str,retornar_medidas:bool=False) -> np.ndarray|Medida:...

def __iter__(self)->Iterator[object]:
return self._valores

def curva_min(self,sigmas:float | int=2)->np.ndarray:
if not isinstance(sigmas,Real):
raise TypeError('sigmas precisa ser um número real')
def mudar_intervalo_de_confianca(self,sigmas:float | int)->None:
self._sigmas=sigmas

@property
def curva_min(self)->np.ndarray:
if not self._amostragem_pre_calculada_nominal.size:
raise ValueError('É necessário amostrar a regressão antes de calcular a curva min')
return self._amostragem_pre_calculada_nominal-sigmas*self._amostragem_pre_calculada_incerteza
y:np.ndarray=self._amostragem_pre_calculada_nominal-self._sigmas*self._amostragem_pre_calculada_incerteza
return y

def curva_max(self,sigmas:float | int=2)->np.ndarray:
if not isinstance(sigmas,Real):
raise TypeError('sigmas precisa ser um número real')
@property
def curva_max(self)->np.ndarray:
if not self._amostragem_pre_calculada_incerteza.size:
raise ValueError('É necessaŕio amostrar a regressão antes de calcular a curva min')
return self._amostragem_pre_calculada_nominal+sigmas*self._amostragem_pre_calculada_incerteza

y:np.ndarray=self._amostragem_pre_calculada_nominal+self._sigmas*self._amostragem_pre_calculada_incerteza
return y


class MPolinomio(ABCRegressao):
Expand All @@ -77,14 +95,21 @@ def __init__(self,coeficientes:np.ndarray):
for index,coef in enumerate(coeficientes):
self._coeficientes.append(coef)
setattr(self,string.ascii_lowercase[index],coef)
self._grau=len(coeficientes)-1
self.grau=len(coeficientes)-1

@obrigar_tipos
def amostrar(self:'MPolinomio',
x:np.ndarray | Medida,unidade_y:str,retornar_como_medidas:bool=False) -> np.ndarray | Medida:
self._verificar_tipo_de_x(x)
y=Medida(0,0,unidade_y)
for index,coef in enumerate(self._coeficientes):y+=coef*x**(self.grau-index)
return self._retornar(y,unidade_y,retornar_como_medidas)

def __iter__(self) -> Iterator[Medida]:
return iter(self._coeficientes)

def __repr__(self) -> str:
return f"MPolinomio(coefs={self._coeficientes},grau={self._grau})"
return f"MPolinomio(coefs={self._coeficientes},grau={self.grau})"



Expand All @@ -98,7 +123,13 @@ def __init__(self,a:Medida,k:Medida,base:Real):
self.a=a
self.base=base
self.k=k
self._valores=(a,k,base)
self._valores=iter((a,k,base))

@obrigar_tipos
def amostrar(self:'MExponencial', x:np.ndarray|Medida, unidade_y:str,retornar_como_medidas:bool=False)->np.ndarray|Medida:
self._verificar_tipo_de_x(x)
y:np.ndarray|Medida=np.power(float(self.base),(self.k*x))*self.a
return self._retornar(y,unidade_y,retornar_como_medidas)

def __repr__(self)->str:
return f'MExponencial(a={self.a},k={self.k},base={self.base})'
Expand All @@ -117,16 +148,18 @@ def __init__(self, a: Medida, n: Medida,y_unidade:pint.Quantity):
self._y_unidade=y_unidade

@obrigar_tipos
def amostrar(self, x:np.ndarray,unidade_x:str,unidade_y:str) -> np.ndarray:
def amostrar(self:'MLeiDePotencia',
x:np.ndarray|Medida,unidade_y:str,retornar_como_medidas:bool=False) -> np.ndarray|Medida:
self._verificar_tipo_de_x(x)
if isinstance(x,Medida):x=np.array([x])
unidade_expoente=str((x[0]._nominal**self.n._nominal).units)
x=_forcar_troca_de_unidade(x,'')
expoente=x**self.n
expoente_medida=_forcar_troca_de_unidade(expoente,str(ureg(unidade_x)**self.n._nominal))
expoente_medida=_forcar_troca_de_unidade(expoente,unidade_expoente)
y=expoente_medida*self.a
if isinstance(y,np.ndarray):#essa linha é só para o mpy não reclamar que y não é indexável
if not y[0]._nominal.is_compatible_with(self._y_unidade):
raise ValueError(f'Unidade de x não está correta')
self._amostragem_pre_calculada_nominal=nominais(y,unidade_y)
self._amostragem_pre_calculada_incerteza=incertezas(y,unidade_y)
return self._amostragem_pre_calculada_nominal
if not y[0]._nominal.is_compatible_with(self._y_unidade):
raise ValueError(f'Unidade de x não está correta')
return self._retornar(y,unidade_y,retornar_como_medidas)

def __repr__(self)->str:
return f'MLeiDePotencia(a={self.a}, b={self.n})'
Expand Down Expand Up @@ -172,9 +205,9 @@ def regressao_exponencial(x_medidas:np.ndarray,y_medidas:np.ndarray,
k=polinomio.a
a=_aplicar_funcao_sem_passar_pelo_sistema_de_unidades(np.array([polinomio.b]),exp)[0]

k=_forcar_troca_de_unidade(k,str((1/x_medidas[0]._nominal).units))
a=_forcar_troca_de_unidade(a,str(y_medidas[0]._nominal.units))
return MExponencial(a,k,base)
k=_forcar_troca_de_unidade(np.array([k]),str((1/x_medidas[0]._nominal).units))
a=_forcar_troca_de_unidade(np.array([a]),str(y_medidas[0]._nominal.units))
return MExponencial(a[0],k[0],base)

@obrigar_tipos
def regressao_potencia(x_medidas:np.ndarray, y_medidas:np.ndarray) -> MLeiDePotencia:
Expand All @@ -186,6 +219,6 @@ def regressao_potencia(x_medidas:np.ndarray, y_medidas:np.ndarray) -> MLeiDePote
polinomio=regressao_linear(log_x_medidas,log_y_medidas)
a=_aplicar_funcao_sem_passar_pelo_sistema_de_unidades(np.array([polinomio.b]),exp)[0]
n=polinomio.a
a=_forcar_troca_de_unidade(a,str((y_medidas[0]._nominal/x_medidas[0]._nominal**n._nominal.magnitude).units))
n=_forcar_troca_de_unidade(n,"dimensionless")
return MLeiDePotencia(a,n,y_medidas[0]._nominal)
a=_forcar_troca_de_unidade(np.array([a]),str((y_medidas[0]._nominal/x_medidas[0]._nominal**n._nominal.magnitude).units))
n=_forcar_troca_de_unidade(np.array([n]),"dimensionless")
return MLeiDePotencia(a[0],n[0],y_medidas[0]._nominal)
7 changes: 2 additions & 5 deletions LabIFSC2/_tipagem_forte.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
def checar_argumento(arg:Any,nome_argumento:str,tipo_esperado:Any,
func_name:str)->None:

if isinstance(tipo_esperado, str):
return None
get_origin_result=get_origin(tipo_esperado)
get_args_result=get_args(tipo_esperado)

Expand All @@ -24,11 +26,6 @@ def checar_argumento(arg:Any,nome_argumento:str,tipo_esperado:Any,
raise TypeError(f"Argumento {nome_argumento} (da função {func_name}) deve ser de um dos tipos {get_args_result} \
e não {type(arg)}")

elif (get_args_result and get_origin_result is not None): #tipos compostos np.ndarray[Number]
#Como vamos usar sempre np.ndarray, precisamos só checar um elemento
if not (isinstance(arg[0],get_args_result) and issubclass(type(arg),get_origin_result)):
raise TypeError(f"Argumento {nome_argumento} (da função {func_name}) precisa ser do tipo {tipo_esperado} \
e não {type(arg)}")
else: #tipos simples
if not (isinstance(arg, tipo_esperado) or issubclass(type(arg),tipo_esperado)):
raise TypeError(f"Argumento {nome_argumento} (da função {func_name}) precisa ser do tipo {tipo_esperado} \
Expand Down
Loading

0 comments on commit 314d34c

Please sign in to comment.