This notebook is an interactive version of the media cost analysis by Liz Specht. This is something I put together mostly as an excuse to learn Jupyter widgets, rather than as a public resource. That said, if you find it useful, great!
Liz's analysis explores how the cost of Essential 8 media would change under various scenarios in which the costs and/or concentrations of individual components are reduced. It does not look at alternative media formulations using e.g. plant hydrolysates, which could further bring down the cost of culture media.
In this notebook, I built sliders just for the costs of individual components. Concentrations could have sliders too, but I didn't get around to putting these in.
import ipywidgets as widgets
from ipywidgets import interact
import matplotlib.pyplot as plt
# Costs expressed as $ per gram, except DMEM/F12 which is per 50L (see table 1 of Liz's cost analysis)
# Concentrations are mg/L, except for DMEM/F12 which is 1x
cost_bm = 156
cost_aa2p = 7.84
cost_nahco3 = 0.01
cost_selenite = 0.1
cost_insulin = 340
cost_transferrin = 400
cost_fgf = 2005000
cost_tgf = 80900000
conc_bm = 1
conc_aa2p = 64
conc_nahco3 = 543
conc_selenite = 0.014
conc_insulin = 19.4
conc_transferrin = 10.7
conc_fgf = 0.1
conc_tgf = 0.002
costs = [cost_aa2p, cost_nahco3, cost_selenite, cost_insulin, cost_transferrin, cost_fgf, cost_tgf]
concs = [conc_aa2p, conc_nahco3, conc_selenite, conc_insulin, conc_transferrin, conc_fgf, conc_tgf]
labels = ['AA2P', 'NaHCO3', 'Na Selenite', 'Insulin', 'Transferrin', 'FGF', 'TGFbeta']
def get_cost(costs, concs, cost_bm = 156, conc_bm = 1, return_breakdown = False):
breakdown = [(cost_bm * conc_bm) / 50]
for cost, conc in zip (costs, concs):
breakdown.append(cost * conc / 1000)
cost_L = sum(breakdown)
if return_breakdown:
return cost_L, breakdown
else:
return cost_L
def show_outputs(costs, new_costs, output):
output.clear_output()
with output:
cost, breakdown = get_cost(costs, concs, return_breakdown = True)
pie1 = plt.pie(breakdown, labels = ['Basal media'] + labels, wedgeprops=dict(width=0.5))
plt.gca().set_title('Original cost: ' + str(round(cost, 2)) + ' (USD)')
plt.show()
new_cost, new_breakdown = get_cost(new_costs, concs, return_breakdown = True)
pie2 = plt.pie(new_breakdown, labels = ['Basal media'] + labels, wedgeprops=dict(width=0.5))
plt.gca().set_title('New cost: ' + str(round(new_cost, 2)) + ' (USD)')
plt.show()
def make_plot(cost_dict, to_plot, cost_range, plot_output):
plot_output.clear_output()
with plot_output:
y = []
for component_cost in cost_range:
cost_dict[dropdown.value] = component_cost
y.append(get_cost(cost_dict.values(), concs))
plt.plot(cost_range, y)
plt.xlabel(dropdown.value + ' cost per gram')
plt.ylabel('Media cost per liter')
plt.show()
output = widgets.Output()
plot_output = widgets.Output()
# Make text box widgets
t_aa2p = widgets.FloatText(value = cost_aa2p, description = 'AA2P')
t_nahco3 = widgets.FloatText(value = cost_nahco3, description = 'NaHCO3')
t_selenite = widgets.FloatText(value = cost_selenite, description = 'Na Selenite')
t_insulin = widgets.IntText(value = cost_insulin, description = 'Insulin')
t_transferrin = widgets.IntText(value = cost_transferrin, description = 'Transferrin')
t_fgf = widgets.IntText(value = cost_fgf, description = 'FGF')
t_tgf = widgets.IntText(value = cost_tgf, description = 'TGFbeta')
t_widgets = [t_aa2p, t_nahco3, t_selenite, t_insulin, t_transferrin, t_fgf, t_tgf]
widget_dict = {label: widget for label, widget in zip(labels, t_widgets)}
# Could easily add a second column to represent concentrations
dropdown = widgets.Dropdown(options = labels, description = 'Plot')
lower_bound_widget = widgets.FloatText(value = 0, description = 'Lower bound')
upper_bound_widget = widgets.FloatText(value = widget_dict[dropdown.value].value, description = 'Upper bound')
plot_widgets = [dropdown, lower_bound_widget, upper_bound_widget]
def handler(change):
new_costs = [t.value for t in t_widgets]
show_outputs(costs, new_costs, output)
def plot_handler(change):
new_costs = [t.value for t in t_widgets]
cost_dict = {label: cost for label, cost in zip(labels, new_costs)}
to_plot = dropdown.value
cost_range = [lower_bound_widget.value, upper_bound_widget.value]
make_plot(cost_dict, to_plot, cost_range, plot_output)
def widget_handler(change):
lower_bound_widget.value = 0
upper_bound_widget.value = widget_dict[dropdown.value].value
for t_widget in t_widgets:
t_widget.observe(handler, names = 'value')
t_widget.observe(plot_handler, names = 'value')
for plot_widget in plot_widgets:
plot_widget.observe(plot_handler, names = 'value')
dropdown.observe(widget_handler, names = 'value')
static_title = widgets.HTML('<b>Calculate a single scenario</b>')
range_title = widgets.HTML('<b>Calculate costs over a range (will use costs from the single scenario except for the selected component)</b>')
input_widgets = widgets.HBox([widgets.VBox([static_title] + t_widgets), widgets.VBox([range_title] + plot_widgets)])
tab = widgets.GridBox([output, plot_output], layout=widgets.Layout(grid_template_columns="repeat(2, 400px)"))
dashboard = widgets.VBox([input_widgets, tab])
handler(None)
plot_handler(None)
display(dashboard)
VBox(children=(HBox(children=(VBox(children=(HTML(value='<b>Calculate a single scenario</b>'), FloatText(value…