data/
- data directory for sample experimentsingle-wavelength-probe-assay.py
- example script for analyzing a single-wavelength assay with a single fluorescent probe compound
single-wavelength-competition-assay.py
- example script for analyzing a single-wavelength competition assay
Note that we are not yet feeding the GEF:Abl data (without IMA) into the inference engine, so this will not work yet.
We may need to add a new helper class (e.g. SingleWavelengthCompetitionAssay
) in order to make it easy to provide both GEF:Abl and GEF:IMA:Abl datasets.
single-wavelength-competition-assay-emcee.py
- example script for analyzing a single-wavelength competition assay withemcee
To use this, you mst have installed emcee
package with
conda install --yes -c omnia emcee
Note that, while emcee
is very quick in sampling from the posterior, the functions to analyze the emcee
output have not yet been written
A number of helper classes are provided to simplify setting up an experiment.
To set up a single-inhibitor (non-competition) plate, use SingleWavelengthAssay
from assaytools.experiments
:
from autoprotocol.unit import Unit
from assaytools.experiments import SingleWavelengthAssay
The necessary information can be provided via a dict
containing all of the necessary parameters for the assay:
#
# This information is different for each experiment.
# We use a 'dict' so that we can later store this information in a JSON database or something.
#
params = {
'd300_xml_filename' : 'data/Src_Bos_Ima_96well_Mar2015 2015-03-07 1736.DATA.xml', # HP D300 dispense simulated DATA file
'infinite_xml_filename' : 'data/Abl Gef gain 120 bw1020 2016-01-19 15-59-53_plate_1.xml', # Tecan Infinite plate reader output data
'dmso_stocks_csv_filename' : 'data/DMSOstocks-Sheet1.csv', # CSV file of DMSO stock inventory
'hpd300_fluids' : ['GEF001', 'IMA001', 'DMSO'], # uuid of DMSO stocks from dmso_stocks_csv_filename (or 'DMSO' for pure DMSO) used to define HP D300 XML <Fluids> block
'hpd300_plate_index' : 1, # plate index for HP D300 dispense script
'receptor_species' : 'Abl(D382N)', # receptor name (just used for convenience)
'protein_absorbance' : 4.24, # absorbance reading of concentrated protein stock before dilution
'protein_extinction_coefficient' : Unit(49850, '1/(moles/liter)/centimeter'), # 1/M/cm extinction coefficient for protein
'protein_molecular_weight' : Unit(41293.2, 'daltons'), # g/mol protein molecular weight
'protein_stock_volume' : Unit(165.8, 'microliters'), # uL protein stock solution used to make 1 uM protein stock
'buffer_volume' : Unit(14.0, 'milliliters'), # mL buffer used to make 1 uM protein stock
'rows_to_analyze' : ['A', 'B'], # rows to analyze
'assay_volume' : Unit(100.0, 'microliters'), # quantity of protein or buffer dispensed into plate
'measurements_to_analyze' : ['fluorescence top'], # which measurements to analyze (if specified -- this is optional)
'wavelengths_to_analyze' : ['280:nanometers', '480:nanometers'], # which wavelengths to analyze (if specified -- this is optional)
}
# Create a single-wavelength assay.
assay = SingleWavelengthAssay(**params)
Once the assay is set up, the experiment
field can be accessed to fit the data, run MCMC, and generate figures:
# Fit the maximum a posteriori (MAP) estimate
map_fit = assay.experiment.map_fit()
# Run some MCMC sampling and return the MCMC object
trace = assay.experiment.run_mcmc(map_fit=map_fit)
# Show summary
assay.experiment.show_summary(mcmc, map_fit)
# Generate plots
plots_filename = 'plots.pdf'
assay.experiment.generate_plots(trace, pdf_filename=plots_filename)
Under the hood, the API looks like this:
from assaytools.analysis import CompetitiveBindingAnalysis
# Define an experiment by specifying source solutions, WellGroup to analyze, and names of receptor and ligand components for competitive binding model.
experiment = CompetitiveBindingAnalysis(solutions=solutions, wells=container.all_wells(), receptor_name=['Abl'], ligand_names=['bosutinib', 'erlotinib', 'gefinitb', 'bosutinib isomer'])
# Determine the maximum a posteriori (MAP) estimate.
map_fit = experiment.map_fit()
# Run MCMC sampling and return the MCMC object.
mcmc = experiment.run_mcmc()
Solution
objects describe the main source solution stocks from which wells are filled.
Solution
objects specify at most the concentration of a single component (such as protein or ligand) and its corresponding uncertainty.
Each Solution
has a name
, a species
that is dissolved in a buffer
at a given concentration
, and concentration uncertainty
.
There are three types of Solution
:
Buffer
: A buffer that contains no compound species.ProteinSolution
: A single protein species prepared spectrophotometrically from a high-concentration stock solution whoseabsorbance
measurement was taken, with corresponding concentration calculated automatically fromextinction_coefficient
andmolecular_weight
. A quantityul_protein_stock
microliters of high-concentration stock solution is added toml_buffer
milliliters of buffer, with the sourceBufferSolution
specified as thebuffer
attribute.DMSOStockSolution
: A DMSO stock solution prepared graviemetrically. This is currently constructed from adict
object specifying a compound stock solution through a number of required parameters ({'compound name', 'compound mass (mg)', 'molecular weight', 'purity', 'solvent mass (g)'}
)
The solutions
argument of CompetitiveBindingAnalysis
is a dict
specifying a solution key associated with a Solution
object, such as
solutions = dict()
solutions['buffer'] = Buffer(name='20 mM Tris buffer')
solutions['Abl'] = ProteinSolution(name='1 uM Abl', species='Abl', buffer=solutions['buffer'], absorbance=4.24, extinction_coefficient=49850, molecular_weight=41293.2, ul_protein_stock=165.8, ml_buffer=14.0)
solutions['BOS'] = DMSOStockSolution(dmso_stocks['BOS001'])
solutions['BSI'] = DMSOStockSolution(dmso_stocks['BOI001'])
solutions['GEF'] = DMSOStockSolution(dmso_stocks['GEF001'])
solutions['ERL'] = DMSOStockSolution(dmso_stocks['ERL001'])
where the dmso_stocks
dictionary is auto-populated from a DMSO stock inventory spreadsheet (read here in CSV format):
import csv
dmso_stocks_csv_filename = 'DMSOstocks-Sheet1.csv'
dmso_stocks = dict()
with open(dmso_stocks_csv_filename, 'rb') as csvfile:
csvreader = csv.DictReader(csvfile, delimiter=',', quotechar='|')
for row in csvreader:
if row['id'] != '':
for key in ['compound mass (mg)', 'purity', 'solvent mass (g)', 'molecular weight']:
row[key] = float(row[key])
dmso_stocks[row['id']] = row
The autoprotocol.container
API is used to create a WellGroup
for analysis of any spectroscopic data available within the specified wells. If a whole Container
is specified (as plate
, for example), all wells can be selected with container.all_wells()
.
Each Well
has a properties
attribute that is a dict
allowing additional well properties to be specified. We use this to hold two important additional pieces of information:
contents
: Adict
specifying the contents of each well, in terms of the volume of eachSolution
that was addedmeasurements
: Adict
specifying any spectroscopic measurements made of the well, which need not be made for every well of theWellGroup
analyzed
The well.properties['contents']
is a dict
specifying how much of each Solution
was added to the well using (volume, stderr)
:
from autoprotocol.unit import Unit
CV_D300 = 0.08 # CV of D300 dispensing
CV_EVO = 0.004 # CV of EVO dispensing 100 uL
well.properties['contents'] = {
'Abl' : (Unit(100, 'microliters'), CV_EVO * Unit(100, 'microliters')),
'BOS' : (Unit(2, 'microliters'), CV_D300 * Unit(2, 'microliters'))
}
Solutions that were not added need to be specified.
The well.properties['measurements']
is a dict
specifying which measurements, if any, were performed for the corresponding well.
The analysis
framework supports arbitrary sets of measurements for individual wells---the same set of measurements need not be made across all wells in the specified WellSet
used for analysis.
Supported measurements include:
absorbance
: the absorbance of a well at one or more wavelengthsfluorescence
: the fluorescence of a well at one or more excitation/emission wavelengths and geometries ({top, bottom}
)
Absorbance measurements are specified with the corresponding wavelength
from autoprotocol.unit import Unit
well.properties['measurements']['absorbance'] = { '280:nanometers' : 0.437 }
NOTE: Unit
objects, such as Unit(280, 'nanometers')
, cannot be used as not unique keys, so we are forced to use the string representations, such as '280:nanometers'
, as keys.
Fluorescence measurements are specified with the corresponding excitation wavelength, emission wavelength, and detection geometry as a tuple:
from autoprotocol.unit import Unit
well.properties['measurements']['fluorescence'] = {
('280:nanometers', '450:nanometers', 'top') : 12425,
('280:nanometers', '450:nanometers', 'bottom') : 1425
}
Once fully populated, the overall properties
attribute of a given Well
may therefore look like this:
{
'measurements': {
'absorbance': {'480:nanometers': 0.0413, '350:nanometers': 0.0723, '280:nanometers': 0.639},
'fluorescence': {
('280:nanometers', '450:nanometers', 'bottom'): 17775.0,
('350:nanometers', '450:nanometers', 'top'): 89.0,
('280:nanometers', '450:nanometers', 'top'): 8671.0,
('350:nanometers', '450:nanometers', 'bottom'): 172.0
}
},
'contents': {
'buffer': Unit(100.0, 'microliter'),
'DMSO': Unit(300.0, 'nanoliter'),
'IMA001': Unit(100.0, 'nanoliter')
},
'area': Unit(31.1724531052, 'millimeter ** 2')
}