First we perform quality control for all mother machine experiments by checking how cells are growing between switches. For each cell cycle, growth rate and \(r^2\) are computed from a fit of exponential elongation; we compare their distributions between experiments.
# extract cell cycle params for those not overlapping with a switch
mycells_btw <- myframes %>%
ungroup %>%
# filter(!(condition %in% c('glucose', 'lactose', 'mg1655'))) %>%
filter(!discard_top) %>%
filter(start_time>0, start_time>=m_start, end_type=='div') %>%
# select(condition, date, pos, gl, id, genealogy, start_time, end_time, end_type,
# time_sec, medium, m_start, m_end, gfp_nb, length_um, m_cycle, cell_num_in_lane, total_cell_in_lane) %>%
# group_by(date, pos, gl, id) %>%
partition(condition, date, pos, gl, id, cluster=mycluster) %>%
# filter(start_time>m_start, end_type=='div', # full cell cycles only
# length(unique(interaction(medium, m_cycle)))==1) %>% # not overlaping a switch
filter((unique(medium)!='glycerol' & n()>9) | # at least 10 time points (≈1/3 of cell cycle)
(unique(medium)=='glycerol' & n()>19)) %>% # requires 20 time points in glycerol
do((function(.df) {
# browser()
# .mod_ll_t <- fastLmPure( cbind(1, .df$time_sec), log(.df$length_um) )
.mod_ll_t <- lm( log(length_um)~time_sec, .df) # use fastLm() for predict
.time_birth <- first(.df$time_sec)
.time_div <- last(.df$time_sec)
.logl <- predict(.mod_ll_t, se.fit=TRUE)
data.frame(npoints=.mod_ll_t$df.residual+1,
time_birth=.time_birth, time_div=.time_div,
cell_num_from_top=mean(.df$cell_num_in_lane),
cell_num_from_bottom=mean(.df$total_cell_in_lane-.df$cell_num_in_lane),
# logl_time_slope=.mod_ll_t$coefficients[2], logl_time_slopesd=.mod_ll_t$stderr[2],
loglength_start=first(.logl$fit), loglength_startse=first(.logl$se.fit),
loglength_end=last(.logl$fit), loglength_endse=last(.logl$se.fit),
logl_time_slope=.mod_ll_t$coefficients[2], logl_time_slopesd=summary(.mod_ll_t)$coefficients[2,2],
logl_time_r2=cor(.df$time_sec, log(.df$length_um))^2 )
})(.) ) %>%
collect() %>%
arrange(condition, date, pos, gl, id) %>%
left_join(myframes %>% group_by(date, pos, gl, id) %>% slice(1) %>%
select(date, pos, gl, id, medium, m_start, m_cycle, mstep, gl_id, cell, ugen, parent_id) )
This overview of growth rates over all experiments (only 1/10th of data shown, lines are loess fits computed on the full dataset) is useful to visualise changes of growth rate between conditions, as well as the transient when cells are exposed to a new condition. In particular, one can see that:
mycells_btw %>%
filter(!is.na(medium)) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) ) %>% # %>% interaction(m_cycle)
(function(.df)
ggplot(.df, aes(time_birth, logl_time_slope/log(2) * 3600, col=factor(m_col))) +
facet_wrap(~condition+date, ncol=2, dir="v", strip.position="right",
labeller=as_labeller(rename_conds)) +
geom_point(alpha=1, size=.2, data=sample_frac(.df, size=.1)) +
geom_smooth(aes(group=interaction(m_col, m_cycle)), method='loess', show.legend=FALSE) +
scale_x_hours() +
labs(x="raw time (h)", y="growth rate (doublings/h)", col="medium type") +
guides(col = guide_legend(override.aes=list(alpha = 1, size=2))) +
# ylim(0, NA) +
theme(legend.position='top') +
background_grid(major="y", minor = "none") +
NULL
)
Next, we focus on growth rate distributions in initial conditions (M9 + 0.2% glucose or M9 + 0.4% glycerol) since we want to compare cells switching from similar physiological states on different days (and hence discard outlier experiments if any).
For each experiment, the distribution of initial growth rate is stratified by time. As noted previously, this demonstrates that steady state is reached in less than 2 hours. Note that grey lines show constant glucose as a reference.
mycells_btw %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle)) %>%
filter(m_col=='1.1') %>%
ungroup %>%
filter(!is.na(medium)) %>%
mutate(time_bin=Hmisc::cut2(time_birth/3600, cuts=(0:15)*2)) %>%
(function(.df) {
ggplot(.df, aes(logl_time_slope/log(2) * 3600)) +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds)) +
stat_ecdf(aes(col=time_bin), alpha=.6) +
stat_ecdf(lty='dotted', data=.df %>% filter(condition=='glucose') %>% select(-condition, -date)) +
# ggplot2::scale_color_discrete() +
scale_colour_viridis_d() +
coord_cartesian(xlim=c(0, 1.5)) +
labs(x='growth rate (doublings/h)', y='cdf', col='birth time (h)') +
theme(legend.position='bottom') +
NULL
})
In order to identify days where initial growth is slower than expected, distributions of growth rates are compared between 2h and 6h. Note that dotted lines show constant glucose (as a reference).
mycells_btw %>%
filter(!is.na(medium)) %>%
# left_join(condition_ts %>% select(condition, medium, m_id) %>% distinct()) %>%
# mutate(m_col=interaction(m_id, m_cycle)) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle)) %>%
filter(m_col=='1.1') %>%
ungroup %>%
filter(between(time_birth/3600, 2, 6),
medium!='glycerol') %>%
(function(.df) {
ggplot(.df, aes(logl_time_slope/log(2) * 3600)) +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds), ncol=6) +
stat_ecdf(col='grey40', data=.df %>% filter(condition=='glucose') %>% select(-condition, -date)) +
stat_ecdf(col=qual_cols[1]) +
# stat_ecdf(aes(group=gl_id), col=qual_cols[1], size=.2) +
coord_cartesian(xlim=c(0, 1.5)) +
labs(x='growth rate (doublings/h)', y='cdf', title='initial growth rates (between t=2h and t=6h)') +
# theme(strip.text=element_text(size=6)) +
NULL
})
mycells_btw %>%
ungroup %>%
filter(!is.na(medium)) %>%
# left_join(condition_ts %>% select(condition, medium, m_id) %>% distinct()) %>%
# mutate(m_col=interaction(m_id, m_cycle)) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle)) %>%
filter(m_col=='1.1') %>%
filter(medium=='glycerol') %>%
filter(time_birth> 2*3600) %>%
# pull(condition) %>% table
(function(.df) {
ggplot(.df, aes(logl_time_slope/log(2) * 3600)) +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds), ncol=6) +
stat_ecdf(col='grey40', data=.df %>% filter(condition=='glucose') %>% select(-condition, -date)) +
stat_ecdf(col=qual_cols[1]) +
# stat_ecdf(aes(group=gl_id), col=qual_cols[1], size=.2) +
coord_cartesian(xlim=c(0, 1.5)) +
labs(x='growth rate (doublings/h)', y='cdf', title='initial growth rates (t≥2h)') +
# theme(strip.text=element_text(size=6)) +
NULL
})
mycells_btw %>%
filter(!is.na(medium)) %>%
# left_join(condition_ts %>% select(condition, medium, m_id) %>% distinct()) %>%
# mutate(m_col=interaction(m_id, m_cycle)) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle)) %>%
filter(m_col=='1.1') %>%
ungroup %>%
filter(! condition %in% c('mg1655', 'lactose'),
between(time_birth/3600, 2, 6),
medium!='glycerol') %>%
mutate(type=ifelse(date %in% discarded_dates, 'discarded', 'analysed')) %>%
(function(.df) {
ggplot(.df, aes(logl_time_slope/log(2) * 3600)) +
facet_grid(type~.) +
# facet_wrap(~condition+date, labeller=as_labeller(rename_conds), ncol=6) +
stat_ecdf(aes(col=factor(date))) +
stat_ecdf(col='grey40', lty='dashed',
data=.df %>% filter(condition=='glucose') %>% select(-type, -condition, -date)) +
scale_colour_periodic(.n = 3, guide='none') +
coord_cartesian(xlim=c(0, 1.2)) +
labs(x='growth rate (doublings/h)', y='cumulative probability') +
# theme(strip.text=element_text(size=6)) +
NULL
}) +
labs(subtitle='initial growth rates (between t=2h and t=6h)')
(myplots[['gr_length_medians']] <- function(.article_ds=FALSE)
mycells_btw %>% ungroup() %>%
filter(!is.na(medium)) %>%
filter_article_ds(.article_ds) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle)) %>%
filter(m_col=='1.1') %>%
ungroup %>%
filter(! condition %in% c('mg1655', 'lactose'),
(time_birth>2*3600 & time_birth<6*3600)) %>%
left_join(condition_acq_times %>% group_by(condition, medium, m_cycle) %>% slice(1) %>%
filter(m_start==0) %>% select(condition, nutrient=medium) ) %>%
mutate(condition=fct_relabel(factor(condition), rename_conds),
type=ifelse(date %in% discarded_dates, 'discarded', 'analysed')) %>%
# nutrient='glucose',
# nutrient=ifelse(medium=='glycerol', 'glycerol', nutrient),
# nutrient=ifelse(condition=='lactose', 'lactose', nutrient)) %>%
(function(.df) {
.dfs <- .df %>% group_by(condition, nutrient, type, date) %>%
do(median_hilow(.$loglength_start, conf.int=0.5) %>%
# do(median_pi(.$loglength_start) %>%
setNames(c('lls_med', 'lls_min', 'lls_max')) ) %>%
left_join(
.df %>% group_by(date) %>%
do(median_hilow(.$logl_time_slope, conf.int=0.5) %>%
# do(median_pi(.$logl_time_slope) %>%
setNames(c('lltsl_med', 'lltsl_min', 'lltsl_max')) )
)
ggplot(.dfs) +
geom_point(aes(exp(lls_med), lltsl_med/log(2) * 3600, col=nutrient, alpha=type, shape=type)) +
geom_errorbar(aes(exp(lls_med), ymin=lltsl_min/log(2) * 3600, ymax=lltsl_max/log(2) * 3600, col=nutrient, alpha=type)) +
geom_errorbarh(aes(exp(lls_med), lltsl_med/log(2) * 3600, xmin=exp(lls_min), xmax=exp(lls_max), col=nutrient, alpha=type))+
scale_alpha_manual(values=c(1, 0.2)) +
# coord_cartesian(xlim=c(1.4, 2.), ylim=c(0, 0.85)) +
expand_limits(y=0) +
labs(x='birth length (µm)', y='growth rate (doublings/h)') +
NULL
})
)() +
labs(subtitle='initial condition (between t=2h and t=6h)')
The systematic relationship between growth rate and cell size at birth indicates that cells growing slower are in a different physiological state. Note that lower illumination during experiments with low lactose concentration induces less damage and hence leads to higher growth rate before the switch.
mycells_btw %>%
filter(!is.na(medium)) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle)) %>%
filter(m_col=='1.1') %>%
ungroup %>%
filter((time_birth>2*3600 & time_birth<6*3600)) %>%
mutate(condition=fct_relabel(condition, rename_conds)) %>%
(function(.df) {
.dfs <- .df %>% group_by(condition, date) %>%
do(median_pi(.$loglength_start) %>% setNames(c('lls_med', 'lls_pi_min', 'lls_pi_max')) ) %>%
left_join(
.df %>% group_by(date) %>%
do(median_pi(.$logl_time_slope) %>% setNames(c('lltsl_med', 'lltsl_pi_min', 'lltsl_pi_max')) )
)
ggplot(.df) +
geom_point(aes(exp(lls_med), lltsl_med/log(2) * 3600, col=condition), data=.dfs ) +
geom_errorbar(aes(exp(lls_med), ymin=lltsl_pi_min/log(2) * 3600, ymax=lltsl_pi_max/log(2) * 3600, col=condition),
alpha=.5, data=.dfs ) +
geom_errorbarh(aes(exp(lls_med), lltsl_med/log(2) * 3600, xmin=exp(lls_pi_min), xmax=exp(lls_pi_max), col=condition),
alpha=.5, data=.dfs ) +
ggrepel::geom_text_repel(aes(exp(lls_med), lltsl_med/log(2) * 3600, col=condition, label=date), alpha=0.3, data=.dfs) +
# force=4, point.padding=1) +
scale_colour_periodic(.n = 3, guide='none') +
coord_cartesian(xlim=c(1.25, 2.25), ylim=c(0, 1)) +
labs(x='birth length (µm)', y='growth rate (doublings/h)') +
# ggplot2::scale_color_discrete(guide='none') +
theme(legend.position='bottom') +
NULL
}) +
labs(subtitle='initial condition (between t=2h and t=6h)')
To the contrary, the distribution of correlation coefficients for the exponential elongation fits are difficult to interpret in order to identify outlier experiments.
mycells_btw %>%
filter(!is.na(medium)) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle)) %>%
filter(m_col=='1.1') %>%
ungroup %>%
filter((time_birth>2*3600 & time_birth<6*3600)) %>%
filter(npoints>19) %>%
(function(.df) {
ggplot(.df, aes(logl_time_r2)) +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds), ncol=6) +
stat_ecdf(col='grey40', data=.df %>% filter(condition=='glucose') %>% select(-condition, -date)) +
stat_ecdf(col=qual_cols[1], alpha=.6) +
coord_cartesian(xlim=c(0.95, 1)) +
labs(x='Squared Pearson correl. coeff.', title='initial growth curves (between t=2h and t=6h)') +
NULL
})
Based on slow growth after inoculation in M9 + 0.2% glucose, we discard the following dates: 20161021, 20170901, 20180313, 20151218, 20170108, 20160526. Discarding 20160526 and 20170901 is also supported by poor fit quality as indicated by their \(r^2\) distribution.
A similar analysis of growth rate after the first exposure to lactose doesn’t reveal any additional outliers.
mycells_btw %>%
filter(!date %in% discarded_dates) %>%
filter(!is.na(medium)) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle)) %>%
filter(m_col=='2.1') %>%
ungroup %>%
filter(time_birth>m_start+1.5*3600, time_birth<m_start+4*3600) %>%
ggplot(aes(logl_time_slope/log(2) * 3600)) +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds), ncol=6) +
stat_ecdf(col='grey40', data=mycells_btw %>% ungroup %>% filter(condition=='lactose') %>%
filter(time_birth>m_start+7.5*3600, time_birth<m_start+10*3600) %>% select(-condition, -date)) +
stat_ecdf(col=qual_cols[1], alpha=.6) +
coord_cartesian(xlim=c(0, 1.5)) +
labs(x='growth rate (doublings/h)', y='cdf', title='after first switch (between t=1.5h and t=4h)') +
# theme(strip.text=element_text(size=6)) +
NULL
mycells_btw %>%
filter(!date %in% discarded_dates) %>%
filter(!is.na(medium)) %>%
group_by(condition) %>%
mutate(m_col=as.numeric(fct_inorder(factor(medium))) %>% interaction(m_cycle),
m_col=ifelse(condition=='lactose', '2.1', m_col),
time_birth=ifelse(condition=='lactose', time_birth-6*3600, time_birth)) %>%
filter(m_col=='2.1') %>%
ungroup %>%
filter(time_birth>m_start+1.5*3600, time_birth<m_start+4*3600) %>%
mutate(condition=fct_relabel(condition, rename_conds)) %>%
(function(.df) {
.dfs <- .df %>% group_by(condition, date) %>%
do(median_pi(.$loglength_start) %>% setNames(c('lls_med', 'lls_pi_min', 'lls_pi_max')) ) %>%
left_join(
.df %>% group_by(date) %>%
do(median_pi(.$logl_time_slope) %>% setNames(c('lltsl_med', 'lltsl_pi_min', 'lltsl_pi_max')) )
)
ggplot(.df) +
# facet_wrap(~condition+date) +
# geom_point(aes(exp(loglength_start), log(2)/logl_time_slope / 60, col=factor(date)), alpha=.2, size=.2) +
geom_point(aes(exp(lls_med), lltsl_med/log(2) * 3600, col=condition), data=.dfs ) +
geom_errorbar(aes(exp(lls_med), ymin=lltsl_pi_min/log(2) * 3600, ymax=lltsl_pi_max/log(2) * 3600, col=condition),
alpha=.5, data=.dfs ) +
geom_errorbarh(aes(exp(lls_med), lltsl_med/log(2) * 3600, xmin=exp(lls_pi_min), xmax=exp(lls_pi_max), col=condition),
alpha=.5, data=.dfs ) +
ggrepel::geom_text_repel(aes(exp(lls_med), lltsl_med/log(2) * 3600, col=condition, label=date), alpha=0.3, data=.dfs) +
# force=4, point.padding=1) +
coord_cartesian(xlim=c(1.25, 2.25), ylim=c(0, 1.2)) +
labs(x='birth length (µm)', y='growth rate (doublings/h)', title='after first switch (between t=1.5h and t=4h)') +
ggplot2::scale_color_discrete(guide='none') +
theme(legend.position='bottom') +
NULL
})
In addition to outlier experiments where all cells grow slowly, we suspect that cells in growth arrest before being exposed to lactose tend to have very long lags. The growth rate before the switch is estimated using at least 5 data points; for cells with less data available (due to a recent division before the switch), the growth rate of the parent cell cycle is used. Based on the relationship between growth rate before the switch, we set a minimum growth rate threshold below which arrested cells are discarded from downstream analysis.
(myplots[['lags_gr_before']] <- function(.size_n=TRUE)
mycells_switching %>%
filter(condition != 'switch_gly_lac') %>%
filter(! date %in% discarded_dates) %>%
left_join(condition_acq_times %>% group_by(condition, medium, m_cycle) %>% slice(1) %>%
filter(m_start==0) %>% select(condition, medium_ini=medium) ) %>%
ggplot() +
(if (.size_n) {
geom_point(aes(logl_time_slope_before / log(2) * 3600, lag_200/60, size=n_preswitch, col=medium_ini), alpha=0.2, stroke=0)
} else
geom_point(aes(logl_time_slope_before / log(2) * 3600, lag_200/60, col=medium_ini), alpha=0.2, stroke=0)
) +
geom_vline(xintercept = min_growth_rate / log(2) * 3600, linetype='dotted') +
coord_cartesian(xlim=c(0, 1.5)) +
labs(x='growth rate before the switch (doublings/h)', y=lac_lags_label, col='initial condition') +
(if (.size_n) labs(size='points before\nthe switch') ) +
guides(col=guide_legend(override.aes = list(alpha=1)))
)()
(myplots[['gr_hist_before']] <-
mycells_switching %>%
filter(condition != 'switch_gly_lac') %>%
filter(! date %in% discarded_dates) %>%
filter(switch_idx == 1) %>%
left_join(condition_acq_times %>% group_by(condition, medium, m_cycle) %>% slice(1) %>%
filter(m_start==0) %>% select(condition, medium_ini=medium) ) %>%
# mutate(isna=is.na(lag_200)) %>%
# group_by(medium_ini) %>% summarise(n=n(), inf=sum(is.infinite(lag_200)))
ggplot(aes(x=logl_time_slope_before / log(2) * 3600)) +
# facet_grid(isna~.) +
geom_freqpoly(aes(y=..density.., col=medium_ini), binwidth=.05) +
geom_vline(xintercept = min_growth_rate / log(2) * 3600, linetype='dotted') +
coord_cartesian(xlim=c(0, 1.5)) +
labs(x='growth rate before the switch (doublings/h)', col='initial condition') +
NULL
)
In most experiments, only a few cells are arrested before the switch:
mycells_switching %>%
filter(condition != 'switch_gly_lac') %>%
filter(! date %in% discarded_dates) %>%
filter(switch_idx == 1) %>%
group_by(condition, date) %>%
summarize(n=sum(logl_time_slope_before <= min_growth_rate, na.rm=TRUE)) %>%
arrange(date) %>%
# TODO: check NAs for all conditions
ungroup() %>% mutate(condition=rename_conds(condition)) %>%
knitr::kable() %>%
kableExtra::kable_styling()
condition | date | n |
---|---|---|
memory 4h | 20150703 | 0 |
memory 4h | 20150708 | 1 |
memory 6h | 20151204 | 4 |
memory 16h | 20160912 | 2 |
memory 24h | 20161007 | 3 |
memory 20h | 20161014 | 4 |
withIPTG1uM | 20161130 | 0 |
withIPTG5uM | 20161207 | 2 |
lactose priming | 20161212 | 3 |
glcLac lac | 20171114 | 0 |
ramp40min | 20171121 | 1 |
glcLac lac | 20180108 | 4 |
lacIoe | 20180116 | 0 |
lacIoe withIPTG10uM | 20180119 | 0 |
withIPTG5uM | 20180122 | 0 |
memory 8h | 20180206 | 1 |
memory 12h | 20180207 | 0 |
lacIoe | 20180214 | 1 |
memory 12h | 20180216 | 1 |
lacIPTG5uM | 20180316 | 0 |
ramp40min | 20180319 | 1 |
preIPTG5uM | 20180514 | 0 |
late | 20180516 | 0 |
preIPTG5uM | 20180531 | 0 |
lacIoe preIPTG10uM | 20180604 | 1 |
lac001 6h | 20190605 | 1 |
lac001 10h | 20190614 | 1 |
We want to visualise lags of naive cells to verify that there is no systematic effect, such as all cells having short or long lags in a given GL, or the bottom cell taking always longer that other cells in the same GL.
mycells_switching %>%
ungroup %>%
filter(! date %in% discarded_dates) %>%
filter(condition!='switch_long_lac_hiExpr') %>%
filter(switch_idx==1) %>%
gather(variable, lag, growth_lag, gfp_lag, lag_200, factor_key=TRUE) %>%
# mutate(lag=ifelse(!is.infinite(lag), lag, max(lag[is.finite(lag)])+60),
# lag=ifelse(is.na(lag), -1, lag)) %>%
filter(variable=='lag_200') %>%
# filter(condition=='switch_∆lacA') %>%
ggplot() +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds), scales='free_x') +
geom_segment(aes(x=interaction(date, pos, gl), xend=interaction(date, pos, gl),
y=cell_num_from_bottom, yend=cell_num_from_bottom+1, col=lag/60),
size=1) +
viridis::scale_color_viridis(
trans='log2', direction=-1,
name=expression(paste(italic('lac'), ' induction lag (min)'))) +
theme_void() +
theme(legend.position='top') +
NULL
Analysing lags of naive cells reveals that their distribution is bimodal: let’s visualise the different types of lags graphically.
mycells_switching %>%
ungroup %>%
filter(! date %in% discarded_dates) %>%
filter(condition!='switch_long_lac_hiExpr') %>%
filter(switch_idx==1) %>%
gather(variable, lag, growth_lag, gfp_lag, lag_200, factor_key=TRUE) %>%
mutate(lag_categ=NA, lag_categ=ifelse(is.infinite(lag), 'Inf', lag_categ),
lag_categ=ifelse(!is.infinite(lag) & lag<48*60, 'short', lag_categ),
lag_categ=ifelse(!is.infinite(lag) & lag>=48*60, 'long', lag_categ)) %>%
filter(variable=='lag_200') %>%
# filter(condition=='switch_∆lacA') %>%
ggplot() +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds), scales='free_x') + # , strip.position='right'
geom_segment(aes(x=interaction(date, pos, gl), xend=interaction(date, pos, gl),
y=cell_num_from_bottom, yend=cell_num_from_bottom+1, col=lag_categ),
size=1) +
viridis::scale_color_viridis(
discrete=TRUE, na.value='gray65',
name=expression(paste(italic('lac'), ' induction lag'))) +
theme_void() +
theme(legend.position='top') +
NULL
(myplots[['naive_lags_per_pos']] <- function(.rh=c(0.9, 1.1))
plot_grid(
mycells_switching %>%
ungroup %>%
filter(! date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
gather(variable, lag, growth_lag, gfp_lag, lag_200, factor_key=TRUE) %>%
# mutate(lag=ifelse(!is.infinite(lag), lag, max(lag[is.finite(lag)])+60),
# lag=ifelse(is.na(lag), -1, lag)) %>%
filter(variable=='lag_200') %>%
group_by(cell_num_from_top) %>% filter(n()>20) %>%
ggplot() +
stat_ecdf(aes(lag/60, 1-..y.., col=as.factor(cell_num_from_top))) +
scale_color_viridis_d(direction=-1) +
expand_limits(x=0) +
labs(x='', y='reverse cumul. proba.', col='cell rank\n(from open end)') +
# theme(axis.title.x = element_blank()) +
NULL,
mycells_switching %>%
ungroup %>%
filter(! date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
gather(variable, lag, growth_lag, gfp_lag, lag_200, factor_key=TRUE) %>%
# mutate(lag=ifelse(!is.infinite(lag), lag, max(lag[is.finite(lag)])+60),
# lag=ifelse(is.na(lag), -1, lag)) %>%
filter(variable=='lag_200') %>%
group_by(cell_num_from_bottom) %>% filter(n()>20) %>%
ggplot() +
# facet_grid(switch_idx~.) +
stat_ecdf(aes(lag/60, 1-..y.., col=as.factor(cell_num_from_bottom))) +
scale_color_viridis_d() +
expand_limits(x=0) +
labs(x=lac_lags_label, y='reverse cumul. proba.', col='cell rank\n(from closed end)') +
NULL,
ncol=1, rel_heights = .rh, align='v'
) )()
# growth rate vs position
# cf controls2.Rmd
This is a negative control for the memory experiments where the second switch to lactose occurs after a variable duration in glucose. It demonstrates that the distribution of lags in naive cells does not change after cells have been growing in the mother machine (and illuminated) for many divisions.
# xxx todo: remove gfp_lag
# issue: data is plotted following its order in the data frame
# but apparently this order isn't conserved by stat_ecdf
mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(switch_idx==1) %>%
filter(str_detect(condition, '^switch_[0-9]+h$') |
condition %in% c('switch_late') ) %>% # 'switch_long_lac',
gather(variable, lag, growth_lag, gfp_lag, lag_200, factor_key=TRUE) %>%
# mutate(lag=ifelse(!is.infinite(lag), lag, max(lag[is.finite(lag)])+60),
# lag=ifelse(is.na(lag), -1, lag)) %>%
mutate(condition=ifelse(condition=='switch_late', 'late switch', 'control')) %>%
(function(.df)
ggplot(.df, aes(x=lag/60)) +
facet_grid(.~variable) +
stat_ecdf(aes(y=1-..y.., col=condition, group=date)) +
# stat_ecdf(aes(y=1-..y.., col=condition, group=date),
# data=filter(.df, condition=='late switch')) +
labs(x='lag after the switch (min)', y='reverse cumulative probability') +
expand_limits(x=0) +
NULL
)
Another negative control is to verify that the long memory observed after the first exposure to lactose does not come from lactose diffusing in the PDMS and being slowly released between the two lactose episodes. Lactose was flown overnight in a chip before inoculation, then rinced and inoculated before running our default protocol. The distribution of induction lags at the first switch is not affected.
Retrospectively this a not a well founded concern: if lactose was diffusing in PDMS, so would glucose. Then switching to lactose would be unlikely to induce the lac operon as cells would see a mixture of both nutrients.
mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(switch_idx==1) %>%
filter(str_detect(condition, '^switch_[0-9]+h$') |
condition %in% c('switch_lactose_priming') ) %>% # 'switch_long_lac',
gather(variable, lag, growth_lag, gfp_lag, lag_200, factor_key=TRUE) %>%
# mutate(lag=ifelse(!is.infinite(lag), lag, max(lag[is.finite(lag)])+60),
# lag=ifelse(is.na(lag), -1, lag)) %>%
mutate(condition=ifelse(condition=='switch_lactose_priming', 'lactose primed', 'control')) %>%
(function(.df)
ggplot(.df, aes(x=lag/60)) +
facet_grid(.~variable) +
stat_ecdf(aes(y=1-..y.., col=condition, group=date)) +
stat_ecdf(aes(y=1-..y.., col=condition, group=date),
data=filter(.df, condition=='lactose primed')) +
labs(x='lag after the switch (min)', y='reverse cumulative probability') +
expand_limits(x=0) +
NULL
)