# -*- coding: utf-8 -*-
"""
:Author: Dominic Hunt
"""
import collections
import numpy as np
import utils
[docs]def qualFuncIdent(value, **kwargs):
if isinstance(value, collections.abc.Callable):
fitness = value
elif value == "BIC":
fitness = BIC2(**kwargs)
elif value == "r2":
fitness = r2(**kwargs)
elif value == "bayesFactor":
fitness = bayesFactor(**kwargs)
elif value == "BIC2norm":
fitness = BIC2norm(**kwargs)
elif value == "BIC2normBoot":
fitness = BIC2normBoot(**kwargs)
elif value == "WBIC2":
fitness = WBIC2(**kwargs)
elif value == "-2log":
fitness = logprob
elif value == "-loge":
fitness = logeprob
elif value == "-2AvLog":
fitness = logAverageProb
elif value == "1-prob":
fitness = maxprob
else:
fitness = simpleSum
return fitness
[docs]def simpleSum(modVals):
r"""
Generates a fit quality value based on :math:`\sum {\\vec x}`
Returns
-------
fit : float
The sum of the model values returned
"""
return np.sum(modVals)
[docs]def logprob(modVals):
# type: (Union[ndarray, list]) -> float
r"""
Generates a fit quality value based on :math:`f_{\mathrm{mod}}\left(\\vec x\right) = \sum -2\mathrm{log}_2(\\vec x)`
Returns
-------
fit : float
The sum of the model values returned
"""
logModCoiceprob = np.log2(modVals)
probs = -2*logModCoiceprob
fit = np.sum(probs)
return fit
[docs]def logeprob(modVals):
# type: (Union[ndarray, list]) -> float
r"""
Generates a fit quality value based on :math:`f_{\mathrm{mod}}\left(\\vec x\right) = \sum -\mathrm{log}_e(\\vec x)`
Returns
-------
fit : float
The sum of the model values returned
"""
logModCoiceprob = np.log(modVals)
probs = -logModCoiceprob
fit = np.sum(probs)
return fit
[docs]def logAverageProb(modVals):
# type: (Union[ndarray, list]) -> float
r"""
Generates a fit quality value based on :math:`\sum -2\mathrm{log}_2(\\vec x)`
Returns
-------
fit : float
The sum of the model values returned
"""
correctedVals = utils.movingaverage(modVals, 3, edgeCorrection=True)
logModCoiceprob = np.log2(correctedVals)
probs = -2 * logModCoiceprob
fit = np.sum(probs)
return fit
[docs]def maxprob(modVals):
# type: (Union[ndarray, list]) -> float
r"""
Generates a fit quality value based on :math:`\sum 1-{\\vec x}`
Returns
-------
fit : float
The sum of the model values returned
"""
fit = np.sum(1-modVals)
return fit
[docs]def BIC2(**kwargs):
# type : (**Union[int, float]) -> Callable[[Union[ndarray, list]], float]
r"""
Generates a function that calculates the Bayesian Information Criterion (BIC)
:math:`\lambda \mathrm{log}_2(T)+ f_{\mathrm{mod}}\left(\\vec x\right)`
Parameters
----------
kwargs
Returns
-------
"""
numParams = kwargs.get("numParams", 2)
def BICfunc(modVals, **kwargs):
# type: (Union[ndarray, list]) -> float
numSamples = kwargs.get('numSamples', np.amax(np.shape(modVals)))
BICmod = numParams * np.log2(numSamples) + logprob(modVals)
return BICmod
BICfunc.Name = "BIC2"
BICfunc.Params = {"numParams": numParams}
return BICfunc
[docs]def bayesRand(**kwargs):
# type : (**Union[int, float]) -> Callable[[Union[ndarray, list]], float]
randActProb = kwargs.get("randActProb", 1 / 2)
def BICfunc(modVals, **kwargs):
numSamples = kwargs.get('numSamples', np.amax(np.shape(modVals)))
BICrand = logprob(np.ones(numSamples) * randActProb)
return BICrand
BICfunc.Name = "bayesRand"
BICfunc.Params = {"randActProb": randActProb}
return BICfunc
[docs]def bayesFactor(**kwargs):
# type : (**Union[int, float]) -> Callable[[Union[ndarray, list]], float]
r"""
:math:`2^{\frac{x}{2}}`
Parameters
----------
kwargs
Returns
-------
"""
numParams = kwargs.get("numParams", 2)
randActProb = kwargs.get("randActProb", 1 / 2)
BICmodfunc = BIC2(numParams=numParams)
BICrandfunc = bayesRand(randActProb=randActProb)
def bayesFunc(modVals, **kwargs):
numSamples = kwargs.get('numSamples', np.amax(np.shape(modVals)))
BICmod = kwargs.get('BICmod', BICmodfunc(modVals, numSamples=numSamples))
BICrandom = kwargs.get('BICrand', BICrandfunc(modVals, numSamples=numSamples))
bayesF = 2 ** ((BICrandom - BICmod) / 2)
return bayesF
bayesFunc.Name = "bayesFactor"
bayesFunc.Params = {"randActProb": randActProb,
"numParams": numParams}
return bayesFunc
[docs]def bayesInv(**kwargs):
# type : (**Union[int, float]) -> Callable[[Union[ndarray, list]], float]
r"""
Parameters
----------
numParams : int, optional
The number of parameters used by the model used for the fitters process. Default 2
qualityThreshold : float, optional
The BIC minimum fit quality criterion used for determining if a fit is valid. Default 20.0
number_actions: int or list of ints the length of the number of trials being fitted, optional
The number of actions the participant can choose between for each trialstep of the task. May need to be
specified for each trial if the number of action choices varies between trials. Default 2
randActProb: float or list of floats the length of the number of trials being fitted. Optional
The prior probability of an action being randomly chosen. May need to be specified for each trial if the number
of action choices varies between trials. Default ``1/number_actions``
Returns
-------
"""
# Set the values that will be fixed for the whole fitters process
numParams = kwargs.get("numParams", 2)
qualityThreshold = kwargs.get("qualityThreshold", 20)
number_actions = kwargs.get("number_actions", 2)
randActProb = kwargs.get("randActProb", 1/number_actions)
BICmodfunc = BIC2(numParams=numParams)
BICrandfunc = bayesRand(randActProb=randActProb)
def BICfunc(modVals, **kwargs):
# type: (Union[ndarray, list]) -> float
r"""
Generates a fit quality value based on :math:`\mathrm{exp}^{\frac{\mathrm{numParams}\mathrm{log2}\left(\mathrm{numSamples}\right) + \mathrm{BICval}}{\mathrm{BICrandom}} - 1}`
The function is a modified version of the Bayesian Information Criterion
It provides a fit such that when a value is less than one it is a valid fit
Returns
-------
fit : float
The sum of the model values returned
"""
numSamples = kwargs.get('numSamples', np.amax(np.shape(modVals)))
# We define the Bayesian Information Criteria for the probability of the model given the data, relative a
# guessing model
BICmod = kwargs.get('BICmod', BICmodfunc(modVals, numSamples=numSamples))
BICrandom = kwargs.get('BICrand', BICrandfunc(modVals, numSamples=numSamples))
fit = qualityThreshold * 2**((BICmod-BICrandom) / 2)
return fit
BICfunc.Name = "bayesInv"
BICfunc.Params = {"numParams": numParams,
"qualityThreshold": qualityThreshold,
"number_actions": number_actions,
"randActProb": randActProb}
return BICfunc
[docs]def r2(**kwargs):
# type : (**Union[int, float]) -> Callable[[Union[ndarray, list]], float]
numParams = kwargs.get("numParams", 2)
randActProb = kwargs.get("randActProb", 1 / 2)
BICmodfunc = BIC2(numParams=numParams)
BICrandfunc = bayesRand(randActProb=randActProb)
def r2func(modVals, **kwargs):
numSamples = kwargs.get('numSamples', np.amax(np.shape(modVals)))
BICmod = kwargs.get('BICmod', BICmodfunc(modVals, numSamples=numSamples))
BICrandom = kwargs.get('BICrand', BICrandfunc(modVals, numSamples=numSamples))
r = BICmod/BICrandom - 1
return r
r2func.Name = "r2"
r2func.Params = {"randActProb": randActProb,
"numParams": numParams}
return r2func
[docs]def BIC2norm(**kwargs):
# type : (**Union[int, float]) -> Callable[[Union[ndarray, list]], float]
"""
Parameters
----------
numParams : int, optional
The number of parameters used by the model used for the fits process. Default 2
qualityThreshold : float, optional
The BIC minimum fit quality criterion used for determining if a fit is valid. Default 20.0
number_actions: int or list of ints the length of the number of trials being fitted, optional
The number of actions the participant can choose between for each trialstep of the task. May need to be
specified for each trial if the number of action choices varies between trials. Default 2
randActProb: float or list of floats the length of the number of trials being fitted. Optional
The prior probability of an action being randomly chosen. May need to be specified for each trial if the number
of action choices varies between trials. Default ``1/number_actions``
Returns
-------
"""
# Set the values that will be fixed for the whole fits process
numParams = kwargs.get("numParams", 2)
qualityThreshold = kwargs.get("qualityThreshold", 20)
number_actions = kwargs.get("number_actions", 2)
randActProb = kwargs.get("randActProb", 1/number_actions)
BICmodfunc = BIC2(numParams=numParams)
BICrandfunc = bayesRand(randActProb=randActProb)
def BICfunc(modVals, **kwargs):
# type: (Union[ndarray, list]) -> float
r"""
Generates a fit quality value based on :math:`\mathrm{exp}^{\frac{\mathrm{numParams}\mathrm{log2}\left(\mathrm{numSamples}\right) + \mathrm{BICval}}{\mathrm{BICrandom}} - 1}`
The function is a modified version of the Bayesian Information Criterion
It provides a fit such that when a value is less than one it is a valid fit
Returns
-------
fit : float
The sum of the model values returned
"""
numSamples = kwargs.get('numSamples', np.amax(np.shape(modVals)))
# We define the Bayesian Information Criteria for the probability of the model given the data, relative a
# guessing model
BICmod = kwargs.get('BICmod', BICmodfunc(modVals, numSamples=numSamples))
BICrandom = kwargs.get('BICrand', BICrandfunc(modVals, numSamples=numSamples))
qualityConvertor = qualityThreshold**(2/BICrandom)
fit = qualityConvertor * 2**(BICmod/BICrandom - 1)
return fit
BICfunc.Name = "BIC2norm"
BICfunc.Params = {"numParams": numParams,
"qualityThreshold": qualityThreshold,
"number_actions": number_actions,
"randActProb": randActProb}
return BICfunc
[docs]def BIC2normBoot(**kwargs):
# type : (**Union[int, float]) -> Callable[[Union[ndarray, list]], float]
"""
An attempt at looking what would happen if the samples were resampled. It was hoped that by doing this, the
difference between different sample distributions would become more pronounced. This was not found to be true.
Parameters
----------
numParams : int, optional
The number of parameters used by the model used for the fits process. Default 2
qualityThreshold : float, optional
The BIC minimum fit quality criterion used for determining if a fit is valid. Default 20.0
number_actions: int or list of ints the length of the number of trials being fitted, optional
The number of actions the participant can choose between for each trialstep of the task. May need to be
specified for each trial if the number of action choices varies between trials. Default 2
randActProb: float or list of floats the length of the number of trials being fitted. Optional
The prior probability of an action being randomly chosen. May need to be specified for each trial if the number
of action choices varies between trials. Default ``1/number_actions``
numSamples: int, optional
The number of samples that will be randomly resampled from ``modVals``. Default 100
sampleLen: int, optional
The length of the random sample. Default 1
Returns
-------
"""
# Set the values that will be fixed for the whole fits process
numParams = kwargs.pop("numParams", 2)
qualityThreshold = kwargs.pop("qualityThreshold", 20)
number_actions = kwargs.pop("number_actions", 2)
randActProb = kwargs.pop("randActProb", 1/number_actions)
numSamples = kwargs.pop("numSamples", 100)
sampleLen = kwargs.pop("sampleLen", 1)
def BICfunc(modVals):
# type: (Union[ndarray, list]) -> float
r"""
Generates a fit quality value based on :math:`\mathrm{exp}^{\frac{\mathrm{numParams}\mathrm{log2}\left(\mathrm{numSamples}\right) + \mathrm{BICval}}{\mathrm{BICrandom}} - 1}`
The function is a modified version of the Bayesian Informaiton Criterion
It provides a fit such that when a value is less than one it is a valid fit
Returns
-------
fit : float
The sum of the model valaues returned
"""
sample = np.array(modVals).squeeze()
# Calculate the resampled modVals
numVals = sample.shape
T = max(numVals)
probdist = np.linspace(2 / (T * (T + 1)), 2 / (T + 1), T)
choices = np.random.choice(list(range(T)), size=numSamples, p=probdist)
modValsExtra = np.array([sample[i: i + sampleLen] for i in choices]).squeeze()
extendedSample = np.concatenate((sample, modValsExtra))
numTrials = np.shape(extendedSample)
# We define the Bayesian Information Criteria for the probability of the model given the data, relative a
# guessing model
BICval = numParams * np.log2(np.amax(numTrials)) + logprob(extendedSample)
BICrandom = logprob(np.ones(numTrials) * randActProb)
qualityConvertor = qualityThreshold**(2/BICrandom)
fit = qualityConvertor * 2**(BICval/BICrandom - 1)
return fit
BICfunc.Name = "BIC2normBoot"
BICfunc.Params = {"numParams": numParams,
"qualityThreshold": qualityThreshold,
"number_actions": number_actions,
"randActProb": randActProb,
"numSamples": numSamples,
"sampleLen": sampleLen}
return BICfunc
[docs]def WBIC2(**kwargs):
# type : (**Union[int, float]) -> Callable[[Union[ndarray, list]], float]
"""
Unfinished WBIC implementation
Parameters
----------
Returns
-------
"""
def WBICfunc(modVals):
# type: (Union[ndarray, list]) -> float
r"""
Generates a fit quality value based on :math:`\mathrm{exp}^{\frac{\mathrm{numParams}\mathrm{log2}\left(\mathrm{numSamples}\right) + \mathrm{BICval}}{\mathrm{BICrandom}} - 1}`
The function is a modified version of the Bayesian Information Criterion
It provides a fit such that when a value is less than one it is a valid fit
Returns
-------
fit : float
The sum of the model values returned
"""
numSamples = np.shape(modVals)
temp = 1 / (np.log2(np.amax(numSamples)))
fit = 1 + logprob(modVals) / np.prod(modVals**temp)
return fit
WBICfunc.Name = "WBIC2"
WBICfunc.Params = {}
return WBICfunc