FLIMParams classes
The FLIMParams class, and their variants,
stores parameters for fitting FLIM data.
Initialization of the FLIMParams class
The flim params class can be initialized
either using the from_tuple class method or by
passing a list of flim parameter instances.
The flim parameter classes are UNITFUL –
for some purposes it’s easiest to work with indices of the arrival
time bins and sometimes it’s easiest to work with real SI units.
from siffpy.core.flim import FLIMParams, Exp, Irf
fp = FLIMParams.from_tuple(
(
0.5, 0.3, # tau, fraction in state 1
2.2, 0.5, # tau, fraction in state 2
6.2, 0.2, # tau, fraction in state 3
4.1, 0.03 # IRF mean offset and breadth
),
units = 'nanoseconds'
)
# Equivalent
fp = FLIMParams(
# any of these params could be defined
# with their own units if desired
Exp(0.5, 0.3, units = 'nanoseconds'),
Exp(2.2, 0.5, units = 'nanoseconds'),
Exp(6.2, 0.2, units = 'nanoseconds'),
Irf(4.1, 0.03, units = 'nanoseconds'),
)
FlimUnits and the as_units context manager
The flim params class provides a context
manager for its units internally so that most
methods (other than convert_units) do not
cryptically change the units (and thus the numerical
values used for the various parameters). To use the
context manager, use with fp.as_units('unit'):
where fp is a flim params instance and
unit is the desired unit. unit may be a
string or a flim units unit.
from siffpy.core.flim import FLIMParams, Exp, Irf
fp = FLIMParams(
Exp(0.5, 0.3, units = 'nanoseconds'),
Exp(2.6, 0.7, units = 'nanoseconds'),
Irf(4.1, 0.03, units = 'nanoseconds'),
)
print(fp.units) # nanoseconds
with fp.as_units('countbins'):
# do some processing
analyses_with_fp(fp, ...)
print(fp.units) # still nanoseconds
Saving FLIMParams
The flim params class can be saved to a file
using the save method. The file format is a simple JSON file, but
uses the suffix .flimparams. These data can be loaded as a dict
and used to instantiate a new flim params instance
with the from_dict class method, or the load class method with
a path to file.
from siffpy.core.flim import FLIMParams, Exp, Irf
fp = FLIMParams(
Exp(0.5, 0.3, units = 'nanoseconds'),
Exp(2.6, 0.7, units = 'nanoseconds'),
Irf(4.1, 0.03, units = 'nanoseconds'),
)
fp.save('my_params.flimparams')
# later
fp = FLIMParams.load('my_params.flimparams')
# OR:
import json
with open('my_params.flimparams', 'r') as f:
data = json.load(f)
fp = FLIMParams.from_dict(data)
- class siffpy.core.flim.flimunits.FlimUnits(value)
An enumeration.
- static convert_flimunits(in_array: ndarray | float, from_units: FlimUnits | Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless'], to_units: FlimUnits | Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless']) ndarray | float
Converts an array or float in_array from one type of FLIMUnit to another.
UNITLESS is a special case, where the input is returned unchanged.
- class siffpy.core.flim.flimparams.FLIMParams(*args: List[FLIMParameter] | Tuple[float], color_channel: int | None = None, noise: float = 0.0, name: str | None = None)
A class for storing parameters related to fitting distributions of fluorescence lifetime or photon arrival data.
Currently only implements combinations of exponentials.
The FLIMParams object uses units, specifically the FlimUnits enum, to keep track of the units of the parameters.
- property T_O: float
Back-compatibility
- property allow_noise: bool
Whether or not the noise parameter is allowed
- as_units(to_units: FlimUnits | Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless'])
Context manager that temporarily converts the units of this FLIMParams object to the units of the first parameter, and then converts them back to the original units when the context is exited.
Example
import numpy as np from siffpy.core.flim import FLIMParams, Exp, Irf
- flim_params = FLIMParams(
Exp(tau=0.5, frac=0.5, units=’nanoseconds’), Exp(tau=3, frac=0.5, units=’nanoseconds’), Irf(tau_offset=1, tau_g=0.1, units=’nanoseconds’)
)
- with flim_params.as_units(‘picoseconds’):
# do some stuff while this is in picoseconds print(flim_params)
print(flim_params) >>> FLIMParams object:
- Parameters:
Exp
UNITS: FlimUnits.PICOSECONDS tau : 500.0 frac : 0.2
Exp
UNITS: FlimUnits.PICOSECONDS tau : 3000.0 frac : 0.8
Irf
UNITS: FlimUnits.PICOSECONDS tau_offset : 3000.0 tau_g : 100.0
Noise: 0.0 Color channel: None
FLIMParams object:
- Parameters:
Exp
UNITS: FlimUnits.NANOSECONDS tau : 0.5 frac : 0.2
Exp
UNITS: FlimUnits.NANOSECONDS tau : 3.0 frac : 0.8
Irf
UNITS: FlimUnits.NANOSECONDS tau_offset : 3.0 tau_g : 0.1
Noise: 0.0 Color channel: None
- property bounds: Bounds
Returns a bounds object from the scipy optimization library that can be used to constrain the parameters of this FLIMParams
- property constraints: List[LinearConstraint]
Exponential fractions sum to one, taus in increasing order
- convert_units(to_units: FlimUnits | Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless'], flim_info=None)
Converts the units of all the parameters of this FLIMParams to the requested unit type of to_units.
Arguments
to_units : FlimUnits
A FlimUnits object specifying the units into which the FLIMParams will be transformed.
flim_info : FlimInfo
A FlimInfo object that is necessary to determine how to interchange between arrival_bins and real time units like picoseconds and nanoseconds. If converting between real time units, this parameter can be ignored. NOT YET IMPLEMENTED ACTUALLY. TODO!!
Returns
None
Example
```python import numpy as np from siffpy.core.flim import FLIMParams, Exp, Irf
- flim_params = FLIMParams(
Exp(tau=0.5, frac=0.5, units=’nanoseconds’), Exp(tau=3, frac=0.5, units=’nanoseconds’), Irf(tau_offset=1, tau_g=0.1, units=’nanoseconds’)
)
flim_params.convert_units(‘picoseconds’) flim_params
>>> FLIMParams object:
- Parameters:
Exp
UNITS: FlimUnits.PICOSECONDS tau : 500.0 frac : 0.5
Exp
UNITS: FlimUnits.PICOSECONDS tau : 3000.0 frac : 0.5
Irf
UNITS: FlimUnits.PICOSECONDS tau_offset : 1000.0 tau_g : 100.0
Noise: 0.0 Color channel: None
- fit_params_to_data(data: ~numpy.ndarray, initial_guess: ~numpy.ndarray | None = None, loss_function: LossFunction = <class 'siffpy.core.flim.loss_functions.MSE'>, solver: ~typing.Callable | None = None, x_range: ~numpy.ndarray | None = None, optimization_units: FlimUnits | ~typing.Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless'] = FlimUnits.NANOSECONDS, **kwargs) OptimizeResult
Takes in the data and adjusts the internal parameters of this FLIMParams object to minimize the metric input. Default is CHI-SQUARED.
Stores new parameter values IN PLACE, but will return the scipy OptimizeResult object.
ACTUALLY NO LONGER USING LOSS_FUNCTION AND SOLVER — FUNCTION CALL OVERHEAD WAS MAKING IT VERY SLOW. TO DO: FIGURE OUT A WAY TO PRESERVE THAT FLEXIBILITY!!
Inputs
data : np.ndarray
A numpy array of the arrival time histogram. Data[n] = number of photons arriving in bin n
initial_guess : tuple
Guess for initial params in FLIMParams.param_tuple format. Presumed to be in the same units as the optimization_units (if not None).
loss_function : LossFunction
Defines the cost function for curve fitting. Defaults to chi-squared
Argument 1: DATA (1d-ndarray) as above
Argument 2: PARAMS (tuple)
All other arguments must be KWARGS.
solver : Callable
A function that takes the metric and an initial guess and returns some object that has an attribute called ‘x’ that is a tuple with the same format as the FIT result of the param_tuple. This is the format of the default scipy.optimize.minimize functions.
x_range : np.ndarray
The range of the x-axis in the same units as the FLIMParams. If not provided, assumes the data is in countbins and uses np.arange(len(data)) as the x_range.
**kwargs
Passed to the scipy.optimize.minimize opts kwarg.
Returns
fit : scipy.optimize.OptimizeResult
The OptimizeResult object for diagnostics on the fit.
Examples
### Create a FLIMParams object that generates the data, ### sample from it, then fit a new FLIMParams object to ### those data.
```python from siffpy.core.flim import FLIMParams, Exp, Irf import numpy as np
- flim_params = FLIMParams(
Exp(tau=0.4, frac=0.5, units=’nanoseconds’), Exp(tau=3, frac=0.5, units=’nanoseconds’), Irf(tau_offset=1, tau_g=0.05, units=’nanoseconds’)
)
# 12.5 nanoseconds in 10 ps increments arrival_time_axis = np.arange(0.01,12.5, 0.01) # Sample from the distribution, using a seed to make sure your result matches the example data = flim_params.sample(arrival_time_axis, 1000000, seed = 120, as_histogram = True)
# A particularly bad guess fit_params = FLIMParams(
Exp(tau=0.1, frac=0.5, units=’nanoseconds’), Exp(tau=0.2, frac=0.5, units=’nanoseconds’), Irf(tau_offset=5, tau_g=0.1, units=’nanoseconds’)
)
fit_params.fit_params_to_data(data.astype(float), x_range = arrival_time_axis[1:])
# A not-so-bad result fit_params
>>> FLIMParams object:
- Parameters:
Exp
UNITS: FlimUnits.NANOSECONDS tau : 0.39989360953557634 frac : 0.5006910298045519
Exp
UNITS: FlimUnits.NANOSECONDS tau : 2.9891959985672134 frac : 0.49930897019544795
Irf
UNITS: FlimUnits.NANOSECONDS tau_offset : 1.0102523399410983 tau_g : 0.05007446535532903
Noise: 0.0 Color channel: None
- property fraction_bounds: Bounds
Fraction-only bounds
- property fraction_constraints: List[LinearConstraint]
For when the taus and IRF are fixed
- fraction_to_empirical(fractions: ndarray) ndarray
Converts the fractions argument into the equivalent empirical lifetime using the exponential parameter fits.
## Arguments
fractions : np.ndarray
An array of fractions of the total photons in each bin. Should be dimensions (…, self.n_exps - 1), with the presumption that the final bin adds up to 1 (i.e. fractions.sum(axis=-1) <= 1)
## Returns
empirical : np.ndarray
An array of the empirical lifetimes corresponding to the fractions in the input array.
- classmethod from_dict(data_dict: Dict[str, Any]) FLIMParams
Converts a JSON-compatible dictionary to a FLIMParams
## Example code:
- flim_params = FLIMParams.from_dict(
- dict(
- exps = [
dict(tau=2, frac=0.5, units=”NANOSECONDS”), dict(tau=3, frac=0.5, units=”NANOSECONDS”),
], irf = dict(tau_offset=1, tau_g=2), color_channel = 0, noise = 0.1, name = “Test FLIMParams”,
)
)
- classmethod from_tuple(param_tuple: Tuple, units: FlimUnits | Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless'] = FlimUnits.COUNTBINS, noise: float = 0.0)
Instantiate a FLIMParams from the parameter tuple. Order is:
(tau, frac, tau, frac, … , mean, sigma).
Example code:
```python flim_params = FLIMParams.from_tuple(
(2, 0.5, 3, 0.5, 5, 1, 2), units = FlimUnits.PICOSECONDS,
)
- classmethod load(path: str | Path) FLIMParams
Load from a json file with different extension
- property n_exp: int
Number of exponentials in this FLIMParams
- property ncomponents: int
Number of exponentials in this FLIMParams
- property noise: float
The noise parameter – fraction of photons presumed to arise from uniformly distributed arrival times.
- property offsets: List[Tuple[float, float]]
All offsets in a list format paired with their corresponding weights. For a single IRF, this is a weight of 1, but if there are multiple laser pulses the weights will sum to one.
# Returns
- offsets : List[Tuple[float, float]]
[(offset, weight), …]
- property param_tuple: Tuple
Returns a tuple for all of the parameters together so they can be passed into numerical solvers.
- property params: List[FLIMParameter]
Returns a list of all FLIMParameter objects contained by this FLIMParams
- pdf(x_range: ndarray) ndarray
Return the fit value’s probability distribution. To plot against a data set, rescale this by the total number of photons in the data set. Assumes x_range is in the same units as the FLIMParams.
Alias for probability_dist
## Arguments
x_range : np.ndarray (1-dimensional)
The x values you want the output probabilities of. Usually this will be something like np.arange(MAX_BIN_VALUE), e.g. np.arange(1024)
## Returns
p_out : np.ndarray (1-dimensional)
The probability of observing a photon in each corresponding bin of x_range.
## Example
Create a FLIMParams object and get the probability distribution of a photon arriving in any of the time bins provided
```python import numpy as np from siffpy.core.flim import FLIMParams, Exp, Irf
- flim_params = FLIMParams(
Exp(tau=2, frac=0.5, units=’nanoseconds’), Exp(tau=3, frac=0.5, units=’nanoseconds’), Irf(tau_offset=1, tau_g=0.1, units=’nanoseconds’)
)
flim_params.pdf(np.arange(0,12.5, 0.01))
>>> array([4.42152784e-05, 4.40548485e-05, 4.38950189e-05, ..., 4.51206365e-05, 4.49570342e-05, 4.47940435e-05]) ```
- probability_dist(x_range: ndarray) ndarray
Return the fit value’s probability distribution. To plot against a data set, rescale this by the total number of photons in the data set. Assumes x_range is in the same units as the FLIMParams.
## Arguments
x_range : np.ndarray (1-dimensional)
The x values you want the output probabilities of. Usually this will be something like np.arange(MAX_BIN_VALUE), e.g. np.arange(1024)
## Returns
p_out : np.ndarray (1-dimensional)
The probability of observing a photon in each corresponding bin of x_range.
## Example
Create a FLIMParams object and get the probability distribution of a photon arriving in any of the time bins provided
```python import numpy as np from siffpy.core.flim import FLIMParams, Exp, Irf
- flim_params = FLIMParams(
Exp(tau=2, frac=0.5, units=’nanoseconds’), Exp(tau=3, frac=0.5, units=’nanoseconds’), Irf(tau_offset=1, tau_g=0.1, units=’nanoseconds’)
)
flim_params.pdf(np.arange(0,12.5, 0.01))
>>> array([4.42152784e-05, 4.40548485e-05, 4.38950189e-05, ..., 4.51206365e-05, 4.49570342e-05, 4.47940435e-05]) ```
- sample(x_range: ndarray, n: int = 10000, seed: int | None = None, as_histogram: bool = True) ndarray
Sample the arrival time distribution of this FLIMParams
Arguments
x_range : np.ndarray
The range of the x-axis in the same units as the FLIMParams. If using as_histogram, this will return a histogram of the samples binned as in x_range (so the returned array will have shape (len(x_range)-1,) if as_histogram is True).
n : int
The number of samples to take.
seed : int
The seed for the numpy random number generator.
as_histogram : bool
If True, returns a histogram of the samples, binned as in x_range. If False, returns the samples themselves.
Returns
samples : np.ndarray
An array of n samples from the arrival time distribution if as_histogram is False. If as_histogram is True, returns a histogram of the samples binned as in x_range of length len(x_range) - 1.
Example
### Return a sample of 3 arrival times from the FLIMParams object.
```python import numpy as np from siffpy.core.flim import FLIMParams, Exp, Irf
- flim_params = FLIMParams(
Exp(tau=2, frac=0.5, units=’nanoseconds’), Exp(tau=3, frac=0.5, units=’nanoseconds’), Irf(tau_offset=1, tau_g=0.1, units=’nanoseconds’)
)
flim_params.sample(np.arange(0,12.5,1000), 3, seed = 15, as_histogram = False)
>>> array([5.73, 6.9 , 3.95])
samps = flim_params.sample(np.arange(0,12.5,1), 100000, seed = 15, as_histogram = True)
print(samps.shape, np.arange(0,12.5,1).shape)
>>> (12,) (13,) ```
### Return a histogram of 100000 samples from the FLIMParams object, ### binned into 1 nanosecond bins
```python import numpy as np from siffpy.core.flim import FLIMParams, Exp, Irf
- flim_params = FLIMParams(
Exp(tau=0.5, frac=0.5, units=’nanoseconds’), Exp(tau=3, frac=0.5, units=’nanoseconds’), Irf(tau_offset=1, tau_g=0.1, units=’nanoseconds’)
)
flim_params.sample(np.arange(0,12.5,1), 100000, seed = 15, as_histogram = True)
>>> array([ 367, 46941, 23666, 9627, 5890, 4053, 2939, 2114, 1536, 1098, 789, 980]) ```
- save(path: str | Path)
Save to a json file with different extension
- to_dict() Dict[str, Any]
Converts the FLIMParams to a JSON-compatible dictionary
- class siffpy.core.flim.multi_pulse.MultiPulseFLIMParams(*args: List[FLIMParameter] | Tuple[float], **kwargs)
Specialized FLIMParams that permits more than one IRF. Much of the functionality is re-written, which makes inheritance seem… silly. I’ll have to think about a better way at some point.
- property bounds: Bounds
Returns the bounds for the parameters
- property constraints: List[LinearConstraint]
Returns the constraints for the parameters
- fit_params_to_data(data: ~numpy.ndarray, initial_guess: ~numpy.ndarray | None = None, loss_function: LossFunction = <class 'siffpy.core.flim.loss_functions.MSE'>, solver: ~typing.Callable | None = None, x_range: ~numpy.ndarray | None = None, optimization_units: FlimUnits | ~typing.Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless'] = FlimUnits.NANOSECONDS, **kwargs) OptimizeResult
Takes in the data and adjusts the internal parameters of this FLIMParams object to minimize the metric input. Default is CHI-SQUARED.
Stores new parameter values IN PLACE, but will return the scipy OptimizeResult object.
ACTUALLY NO LONGER USING LOSS_FUNCTION AND SOLVER — FUNCTION CALL OVERHEAD WAS MAKING IT VERY SLOW. TO DO: FIGURE OUT A WAY TO PRESERVE THAT FLEXIBILITY!!
Inputs
data : np.ndarray
A numpy array of the arrival time histogram. Data[n] = number of photons arriving in bin n
initial_guess : tuple
Guess for initial params in FLIMParams.param_tuple format. Presumed to be in the same units as the FLIMParams when the function is called (if not None).
loss_function : LossFunction
Defines the cost function for curve fitting. Defaults to chi-squared
Argument 1: DATA (1d-ndarray) as above
Argument 2: PARAMS (tuple)
All other arguments must be KWARGS.
solver : Callable
A function that takes the metric and an initial guess and returns some object that has an attribute called ‘x’ that is a tuple with the same format as the FIT result of the param_tuple. This is the format of the default scipy.optimize.minimize functions.
x_range : np.ndarray
The range of the x-axis in the same units as the FLIMParams. If not provided, assumes the data is in countbins and uses np.arange(len(data)) as the x_range.
**kwargs
Passed to the metric function.
Returns
fit : scipy.optimize.OptimizeResult
The OptimizeResult object for diagnostics on the fit.
- classmethod from_dict(data: Dict[str, Any]) MultiPulseFLIMParams
Reconstructs the MultiPulseFLIMParams from a dictionary
- classmethod from_tuple(param_tuple: Tuple, n_pulses: int, units: FlimUnits | Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless'] = FlimUnits.COUNTBINS, noise: float = 0.0)
Instantiate a MultiPulseFLIMParams object from a tuple of parameters. Because this could have any number of pulses, the values are not uniquely determined simply by the length of the param_tuple itself, and so the number of pulses must be given.
Tuple form required: (tau, frac, tau, frac, …, mean, sigma, irf_frac, mean, sigma, irf_frac…)
Example code:
```python # Instantiate a MultiPulseFLIMParams object # with 2 pulses, 2 exps, and 2 irfs params = MultiPulseFLIMParams.from_tuple(
(1.0, 0.5, 2.0, 0.5, 100.0, 10.0, 0.5, 100.0, 10.0, 0.5), 2, units = ‘nanoseconds’, noise = 0.1
)
- property irfs: MultiIrf
Returns the MultiIrf object
- property n_params: int
Number of parameters in the model
- property n_pulses: int
Number of laser pulses in the model
- property offsets: List[Tuple[float, float]]
All offsets in a list format paired with their corresponding weights. For a single IRF, this is a weight of 1, but if there are multiple laser pulses the weights will sum to one.
# Returns
- offsets : List[Tuple[float, float]]
[(offset, weight), …]
- property param_tuple: Tuple
Returns a tuple for all of the parameters together so they can be passed into numerical solvers.
- pdf(x_range: ndarray) ndarray
Returns the PDF at the given x value
- probability_dist(x_range: ndarray) ndarray
Returns the probability distribution at the given x value
- class siffpy.core.flim.flimparams.FLIMParameter(units: FlimUnits = FlimUnits.COUNTBINS, **params)
Base class for the various types of parameters.
Doesn’t do anything special, just a useful organizer for shared behavior.
FLIMParameters can be converted between units, and can be serialized to and from JSON.
The units attribute is used to keep track of the units of the parameters. The unitful_params attribute is used to keep track of which parameters are unitful and need to be converted when the units are changed.
The aliases attribute is used to keep track of aliases for the parameters, so that they can be accessed with different names (each of which is more intuitive to certain users)
- convert_units(to_units: FlimUnits | Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless']) None
Converts unitful params
- classmethod from_dict(data_dict: Dict) FLIMParameter
Converts a JSON-compatible dictionary to a FLIMParameter
- nondimensionalize() None
Converts unitful params to nondimensional
Alias for convert_units(FlimUnits.UNITLESS)
- redimensionalize(to_units: FlimUnits | Literal['picoseconds', 'nanoseconds', 'countbins', 'unknown', 'unitless'])
Converts UNITLESS params to to_units
Alias for convert_units
- to_dict() Dict
JSON-parsable string