Creating a feature

Features are derived from multiple analytes within a single trial. For example, product yield is a function of the substrate consumed, and product produced. Features are registered to the SingleTrial class and are composed of two components: the feature, and the feature manager.

The feature manager ensures that the feature is created as soon as all the required analytes are available. Here we build the OD normalization feature as an example

First we import the base features:


In [1]:
from impact.core.features import BaseAnalyteFeature, BaseAnalyteFeatureFactory


C:\Users\Naveen\Anaconda3\lib\site-packages\IPython\html.py:14: ShimWarning: The `IPython.html` package has been deprecated since IPython 4.0. You should import from `notebook` instead. `IPython.html.widgets` has moved to `ipywidgets`.
  "`IPython.html.widgets` has moved to `ipywidgets`.", ShimWarning)

Then, we can build our features


In [2]:
class ODNormalizedData(BaseAnalyteFeature):
    # The constructor should accept all required analytes as parameters 
    def __init__(self, biomass, reporter):
        self.biomass = biomass
        self.reporter = reporter
        self.normalized_data = None
    
    # This data property assures that the data is returned, or calculated as needed
    @property
    def data(self):
        if self.normalized_data is None: self.calculate()
        return self.normalized_data
    
    # This is where the property is actually calculated and set
    def calculate(self):
        self.normalized_data = self.reporter.data_vector/self.biomass.data_vector
        
# The feature factory watches for those analytes
class ODNormalizedDataFactory(BaseAnalyteFeatureFactory):
    # define what the feature
    requires = ['biomass','reporter']
    name = 'od_normalized_data'
    
    # constructor should initialize variables until all required analytes are present,
    # this will ensure that despite the order analytes are added, feature will be calculated appropriately
    def __init__(self):
        self.biomass = None
        self.reporter = None
    
    # define how to handle new analytes
    def add_analyte_data(self, analyte_data):
        if analyte_data.trial_identifier.analyte_type == 'reporter':
            self.reporter = analyte_data
        elif analyte_data.trial_identifier.analyte_type == 'biomass':
            self.biomass = analyte_data   
            
        if self.reporter is not None and self.biomass is not None:
            setattr(analyte_data,self.name,ODNormalizedData(biomass,reporter))

Finally, we register the feature


In [3]:
import impact.core.SingleTrial as SingleTrial
SingleTrial.register_feature(ODNormalizedDataFactory)

and test it


In [4]:
from impact.core.AnalyteData import Biomass, Reporter
from impact.core.TrialIdentifier import ReplicateTrialIdentifier as TI

t = [0,1,2,3,4]
biomass_data = [0.1,0.2,0.4,0.8,0.8]
reporter_data = [1000,2000,3000,4000,5000]

biomass = Biomass()
biomass.time_vector = t
biomass.data_vector = biomass_data
ti = TI()
ti.analyte_name = 'OD'
ti.analyte_type = 'biomass'
biomass.trial_identifier = ti

reporter = Reporter()
reporter.time_vector = t
reporter.data_vector = reporter_data
ti = TI()
ti.analyte_name = 'gfp'
ti.analyte_type = 'reporter'
reporter.trial_identifier = ti

trial = SingleTrial()
trial.add_analyte_data(biomass)
trial.add_analyte_data(reporter)

In [5]:
import numpy as np

calculated_data = trial.analyte_dict['gfp'].od_normalized_data.data
expected_data = np.array(reporter_data)/biomass_data

print(calculated_data)
print(expected_data)
print(all(calculated_data==expected_data))


[ 10000.  10000.   7500.   5000.   6250.]
[ 10000.  10000.   7500.   5000.   6250.]
True