#!/usr/bin/env python3.8
# coding: latin-1
# (c) Massachusetts Institute of Technology 2015-2018
# (c) Brian Teague 2018-2022
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
cytoflow.operations.ratio
-------------------------
Creates a new "channel" that is the ratio of the measurements in two other
channels. `ratio` has one class:
`RatioOp` -- applies the operation.
"""
import numpy as np
from traits.api import (HasStrictTraits, Str, Constant, provides)
import cytoflow.utility as util
from .i_operation import IOperation
[docs]@provides(IOperation)
class RatioOp(HasStrictTraits):
"""
Create a new "channel" from the ratio of two other channels.
Attributes
----------
name : Str
The operation name. Also becomes the name of the new channel.
numerator : Str
The channel that is the numerator of the ratio.
denominator : Str
The channel that is the denominator of the ratio.
Examples
--------
>>> ratio_op = flow.RatioOp()
>>> ratio_op.numerator = "FITC-A"
>>> ex5 = ratio_op.apply(ex4)
"""
# traits
id = Constant('edu.mit.synbio.cytoflow.operations.ratio')
friendly_id = Constant("Ratio")
name = Str
numerator = Str
denominator = Str
[docs] def apply(self, experiment):
"""Applies the ratio operation to an experiment
Parameters
----------
experiment : `Experiment`
the `Experiment` to which this operation is applied
Returns
-------
Experiment
a new experiment with the new ratio channel
The new channel also has the following new metadata:
- **numerator** : Str
What was the numerator channel for the new one?
- **denominator** : Str
What was the denominator channel for the new one?
"""
if experiment is None:
raise util.CytoflowOpError('experiment',
"No experiment specified")
# make sure name got set!
if not self.name:
raise util.CytoflowOpError('name',
"Must specify a name for the derived channel")
if self.numerator not in experiment.channels:
raise util.CytoflowOpError('numerator',
"Channel {0} not in the experiment"
.format(self.numerator))
if self.denominator not in experiment.channels:
raise util.CytoflowOpError('denominator',
"Channel {0} not in the experiment"
.format(self.denominator))
if self.name != util.sanitize_identifier(self.name):
raise util.CytoflowOpError('name',
"Name can only contain letters, numbers and underscores."
.format(self.name))
if self.name in experiment.channels:
raise util.CytoflowOpError('name',
"New channel {0} is already in the experiment"
.format(self.name))
new_experiment = experiment.clone(deep = False)
new_experiment.add_channel(self.name,
experiment[self.numerator] / experiment[self.denominator])
new_experiment.data.replace([np.inf, -np.inf], np.nan, inplace = True)
new_experiment.data.dropna(inplace = True)
new_experiment.history.append(self.clone_traits(transient = lambda t: True))
new_experiment.metadata[self.name]['numerator'] = self.numerator
new_experiment.metadata[self.name]['denominator'] = self.denominator
return new_experiment