From mother machine experiments, we obtain time series of cell length (used as a proxy for biomass) and LacZ-GFP. Here is an example obtained in a given growth channel.
# gfp_nb offset ≈ 100 to avoid stripping values and to keep background fluctuations visually small xxx
(myplots[['raw_traces']] <- function(.ylabel=1.3)
myframes %>% ungroup() %>%
filter(date==20180206) %>%
group_by(date, pos, gl) %>%
ggCustomTJ::slice_groups(c(1)) %>%
filter(!discard_start, !discard_top,
time_sec>2*3600) %>%
filter(total_cell_in_lane-cell_num_in_lane<5,
end_type %in% c('div', 'lost')) %>%
# ungroup() %>% mutate(gfp_nb=gfp_nb - 2*min(gfp_nb)) %>%
ungroup() %>% mutate(gfp_nb=gfp_nb + 135) %>%
gather(variable, value, length_um, gfp_nb) %>%
ggplot() +
# facet_grid(pos+gl~.) +
facet_wrap(~variable, scales='free_y', ncol=1, strip.position='left',
labeller=as_labeller(function(.str) {
.str <- str_replace(.str, "gfp_nb", "LacZ-GFP\nmolecules")
.str <- str_replace(.str, "length_um", "cell length \n(µm)")
return(.str)
}) ) +
geom_line(aes(time_sec-2*3600, value, col=ugen)) +
scale_colour_periodic(.n=3, guide='none') +
# show medium bar
geom_vline(aes(xintercept=m_start - 2*3600, group=1), alpha=.2, size=.5,
data=condition_acq_times %>% group_by(condition, medium, m_cycle) %>% slice(1) %>%
filter(condition=='switch_08h', m_cycle<=2)) +
geom_rect(aes(xmin=m_start - 2*3600, xmax=m_end - 2*3600, ymin=.ylabel, ymax=1.5, fill=medium, group=1), show.legend=FALSE, #size=0.2,
data=condition_acq_times %>% group_by(condition, medium, m_cycle) %>% slice(1) %>%
filter(condition=='switch_08h', m_cycle<=2) %>% mutate(variable='length_um')) +
geom_text(aes(x=m_start+(m_end-m_start)/2 - 2*3600, y=.ylabel, label=medium, group=1), size=4, col='white', hjust=0.5, vjust=-0.5,
data=condition_acq_times %>% group_by(condition, medium, m_cycle) %>% slice(1) %>%
filter(condition=='switch_08h') %>% mutate(variable='length_um', m_start=ifelse(m_start<2*3600, 2*3600, m_start))) +
# scale_fill_manual(values=c('glucose'=.fill[1], 'lactose'=.fill[2]), guide='none') +
coord_cartesian(xlim=c(-3600*.4, 20.2*3600), expand=FALSE) +
scale_x_hours(4) +
scale_y_continuous(trans='log10', breaks=c(2, 3, 4, 100, 500, 2500)) +
theme(strip.placement='outside', strip.background.y=element_blank(),
axis.title.y=element_blank()) +
NULL
)()
We are interested in the time taken by cells to induce the lac operon when exposed to lactose as the only nutrient. This “lac induction lag” is estimated from the LacZ-GFP time series of each cell after the switch. Note that at the first switch, all experiments with variable duration in glucose before the second switch are equivalent and are used together to plot the distribution of lags in “naive” cells (which have not seen lactose before).
(myplots[['naive_lags_hist']] <- mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
# mutate(lag=ifelse(lag<240*60, lag, 245*60),
# lag=ifelse(is.na(lag), -10, lag)) %>%
ggplot(aes(x=lag_200/60)) +
geom_density(stat='bin', fill=qual_cols[2], col='transparent', alpha=.2, binwidth=3) +
geom_freqpoly(col=qual_cols[2], binwidth=3, show.legend=FALSE) +
labs(x=lac_lags_label) +
coord_cartesian(xlim=c(-10, 240), expand=FALSE) +
NULL
) +
labs(subtitle='naive cells (9 independent experiments)')
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
mutate(
lag_type=lag_200,
lag_type=as.character(lag_type), lag_type=ifelse(is.na(lag_type), 'NA', lag_type),
lag_type=ifelse(is.finite(as.numeric(lag_type)) & as.numeric(lag_type)<50*60, 'short lags', lag_type),
lag_type=ifelse(is.finite(as.numeric(lag_type)) & as.numeric(lag_type)>=50*60, 'long lags', lag_type) ) %>%
group_by(lag_type) %>%
summarise(n=n(), m=mean(lag_200/60), sd=sd(lag_200/60)) %>%
ungroup() %>% mutate(p=n/sum(n), p_se=sqrt(p*(1-p)/n)) %>%
knitr::kable(digits=3) %>%
kableExtra::kable_styling() %>%
identity()
lag_type | n | m | sd | p | p_se |
---|---|---|---|---|---|
Inf | 52 | Inf | NaN | 0.032 | 0.024 |
long lags | 1061 | 89.601 | 33.097 | 0.650 | 0.015 |
NA | 106 | NA | NA | 0.065 | 0.024 |
short lags | 414 | 34.261 | 7.056 | 0.254 | 0.021 |
Although the distributions vary from day to day, the bimodality is obserevd in all replicates (particularly visible in the panel with overlayed distributions).
(myplots[['naive_lags_per_expt_facet']] <- function(na.rm=TRUE)
mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
mutate(lag=lag_200,
lag=ifelse(is.finite(lag) & lag>240*60, 240*60, lag),
lag=ifelse(!is.infinite(lag), lag, max(lag[is.finite(lag)])+60),
lag=ifelse(!na.rm & is.na(lag), -1, lag)) %>%
# (function(.df) bind_rows(.df, .df %>% mutate(date=NA, condition='all')) ) %>%
(function(.df)
ggplot(.df, aes(x=lag/60)) +
facet_wrap(~condition+date, ncol=2, dir='v',
strip.position='right', labeller=as_labeller(rename_conds)) +
# geom_freqpoly(aes(col=lag/60 > 236), binwidth=6, show.legend=FALSE) +
geom_freqpoly(breaks=seq(0, 240, 6)-3, pad=TRUE, show.legend=FALSE) +
geom_freqpoly(aes(col=factor(date_col)), breaks=seq(0, 240, 6)-3, pad=TRUE, show.legend=FALSE,
data=.df %>% mutate(date_col=date, date=NA, condition='all')) +
labs(x=lac_lags_label) +
coord_cartesian(xlim=c(-10, 240), expand=FALSE) +
scale_colour_periodic() +
NULL
)
)(FALSE) +
labs(subtitle='naive cells')
Noticeably a small fraction of cells don’t induce even after 4h; here we check that this fraction is not higher in bottom cells (otherwise it would indicate that this fraction is overall underestimated since bottom cells don’t have the bias of long lags being more truncated).
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$') ) %>%
filter(switch_idx==1) %>%
(function(.df) bind_rows(
.df %>% mutate(sample='all cells'),
.df %>% filter(cell_num_from_bottom==0) %>% mutate(sample='bottom cells') )) %>%
mutate(lag_200=as.character(lag_200), lag_200=ifelse(is.na(lag_200), 'NA', lag_200),
lag_200=ifelse(is.finite(as.numeric(lag_200)) & as.numeric(lag_200)<50*60, 'short lags', lag_200),
lag_200=ifelse(is.finite(as.numeric(lag_200)) & as.numeric(lag_200)>=50*60, 'long lags', lag_200) ) %>%
group_by(sample, lag_200) %>%
summarise(n=n()) %>%
group_by(sample) %>% mutate(p=n/sum(n), p_se=sqrt(p*(1-p)/n)) %>%
rename("lag type"=lag_200) %>%
knitr::kable(digits=3) %>%
kableExtra::kable_styling() %>%
identity()
sample | lag type | n | p | p_se |
---|---|---|---|---|
all cells | Inf | 52 | 0.032 | 0.024 |
all cells | long lags | 1061 | 0.650 | 0.015 |
all cells | NA | 106 | 0.065 | 0.024 |
all cells | short lags | 414 | 0.254 | 0.021 |
bottom cells | Inf | 14 | 0.049 | 0.058 |
bottom cells | long lags | 195 | 0.689 | 0.033 |
bottom cells | short lags | 74 | 0.261 | 0.051 |
Using a gradual transition for glucose to lactose over 40 minutes, we verify that the bimodal distribution of lac induction lags is not a consequence of the abrupt change.
NB: since the concentration at which cells start to sense the low glucose / lactose is unknown, the lag in the gradual transition experiments are shifted so as to match the value of the step condition.
(myplots[['lags_hist_ramp']] <-
# xxx add n per condition
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(switch_idx==1) %>%
filter(str_detect(condition, '^switch_[0-9]+h$') | condition=='switch_ramp40min') %>%
mutate(condition=ifelse(str_detect(condition, '^switch_[0-9]+h$'), 'step (control)', 'ramp (40 min)'),
condition=fct_relevel(condition, 'step') ) %>%
mutate(lag=lag_200,
# lag=ifelse(lag>240*60, Inf, lag),
# lag=ifelse(!is.infinite(lag), lag, 241*60),
# lag=ifelse(is.na(lag), -200, lag)
) %>%
(function(.df)
ggplot(.df, aes(x=lag/60, y=..density..)) +
geom_density(stat='bin', fill=qual_cols[1], col='transparent', alpha=.2, binwidth=6,
data=(filter(.df, condition=='ramp (40 min)')) ) +
geom_freqpoly(aes(col=condition), binwidth=6, position='identity') +
labs(x=lac_lags_label) +
# xlim(0, 240) +
# theme(strip.background=element_rect(fill='gray95', colour='transparent'),
# legend.key.size=unit(.8, "lines") ) +
theme(legend.position=c(1, 1), legend.justification=c(1, 1) ) +
coord_cartesian(xlim=c(0, 240)) +
NULL)
)
mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1, !is.na(lag_200)) %>%
mutate(type=ifelse(lag_200/60<50, 'short', 'long')) %>%
ggplot(aes(gfp_ini, ..density..)) +
facet_grid(date~.) +
geom_freqpoly(aes(col=type), binwidth=10) +
NULL
(myplots[['lags_gfp_scatter']] <- function(.fit=FALSE)
mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1, !is.na(lag_200)) %>%
mutate(type=ifelse(lag_200/60<50, 'short', 'long')) %>%
group_by(date) %>%
mutate(gfp_ini=(gfp_ini-mean(gfp_ini))) %>%
ggplot(aes(gfp_ini, lag_200/60)) +
geom_hline(yintercept = 50, lty='dashed') +
geom_point(aes(col=type), size=1, alpha=.2, stroke=0) +
(if (.fit) stat_smooth(method=MASS::rlm, col='black', fullrange = TRUE)) +
labs(x='fluorescence at the switch\n(centered per experiment; equivalent GFP molecules)', y=lac_lags_label) +
guides(col=guide_legend(override.aes = list(size=2, alpha=1))) +
coord_cartesian(xlim=c(-60, 120)) +
NULL
)()
(myplots[['lags_gfp_diff_cdf']] <- mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1, !is.na(lag_200)) %>%
mutate(type=ifelse(lag_200/60<50, 'short', 'long')) %>%
select(date, type, gfp_ini) %>%
bind_rows(myframes %>% ungroup %>%
filter(condition=='mg1655') %>%
filter(!discard_start, !discard_top) %>%
filter(between(length_um, 2, 3.5)) %>%
select(date, gfp_ini=gfp_nb) %>% mutate(type='control'), .) %>%
group_by(date) %>%
mutate(gfp_ini=(gfp_ini-mean(gfp_ini))) %>%
ggplot(aes(gfp_ini, col=type)) +
# facet_grid(date~.) +
stat_ecdf(aes(y=1-..y..)) +
# geom_freqpoly(aes(y=..density..), binwidth=10) +
scale_color_manual(name='type of lac\ninduction lag', breaks=c('control', 'long', 'short'), values=c('gray50', qual_cols[1], qual_cols[2])) +
labs(x='fluorescence at the switch\n(centered per experiment; equivalent GFP molecules)', y='reverse cumulative\nprobability') +
coord_cartesian(xlim=c(-60, 120)) +
NULL)
mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1, !is.na(lag_200)) %>%
mutate(type=ifelse(lag_200/60<50, 'short', 'long')) %>%
group_by(date) %>%
mutate(gfp_ini=(gfp_ini-mean(gfp_ini))) %>%
with(t.test(gfp_ini[type=='short'], gfp_ini[type=='long'], var.equal=TRUE, alternative = 'greater'))
##
## Two Sample t-test
##
## data: gfp_ini[type == "short"] and gfp_ini[type == "long"]
## t = 4.4089, df = 1525, p-value = 5.558e-06
## alternative hypothesis: true difference in means is greater than 0
## 95 percent confidence interval:
## 4.170343 Inf
## sample estimates:
## mean of x mean of y
## 4.850311 -1.804159
(myplots[['naive_lags_correl']] <- mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1, !is.na(lag_200)) %>%
mutate(type=ifelse(lag_200/60<50, 'short', 'long')) %>%
left_join(myframes_switching %>% ungroup %>% #filter(ugen %in% unique(ugen)[1:5]) %>%
filter(time_sec<t_lac_switch) %>%
# ggplot() + geom_line(aes(time_sec-t_lac_switch, length_um, lty=type, col=ugen)) +
# scale_colour_periodic_brewer(guide='none')
group_by(ugen) %>%
filter(time_sec==min(time_sec), time_sec>0) %>%
select(ugen, length_um_birth=length_um) ) %>%
mutate(cc=(t_lac_switch-time_birth) /
# average growth rate in glucose
( mycells_constant %>% filter(condition=='glucose') %>%
mutate(dt=time_div-time_birth) %>% pull(dt) %>% mean ),
dl=length_ini - length_um_birth,
growth_rate=logl_time_slope_before/log(2)*3600) %>%
select(date, lag_200, type, length_ini, gfp_ini, growth_rate, cc, dl) %>%
# group_by(date) %>%
# mutate(gfp_ini=(gfp_ini-mean(gfp_ini))) %>%
filter(cc < 1.5) %>%
gather(variable, value, -date, -lag_200, -type) %>%
group_by(variable) %>%
filter(between(value, quantile(value, 0.01, na.rm=TRUE), quantile(value, 0.99, na.rm=TRUE))) %>%
(function(.df)
ggplot(.df, aes(value, lag_200/60)) +
facet_wrap(~variable, nrow=1, scales='free_x', strip.position='bottom',
labeller=as_labeller(function(.str) {
.str <- str_replace(.str, "cc", "cell cycle")
.str <- str_replace(.str, "dl", "added length\n(µm)")
.str <- str_replace(.str, "gfp_ini", "LacZ-GFP")
.str <- str_replace(.str, "growth_rate", "growth rate\n(dbl/h)")
return(.str)
})) +
geom_hline(yintercept = 50, lty='dashed') +
geom_point(aes(col=type), size=1, alpha=.2, stroke=0) +
# stat_smooth(method=MASS::rlm, col='black', fullrange = TRUE) +
geom_text(aes(-Inf, 0, label=r2str), size=5, hjust=-0.1, vjust=0, parse=TRUE,
data=.df %>% filter(is.finite(lag_200)) %>% group_by(variable) %>% summarise(r2=cor(value, lag_200)^2) %>% mutate(r2str=sprintf("r^2 == '%.4f'", round(r2, digits=4)))) +
scale_x_continuous(breaks = scales::pretty_breaks(n = 3)) +
expand_limits(y=0) +
guides(col=guide_legend(override.aes = list(size=2, alpha=1))) +
labs(y=lac_lags_label, col=expression(paste("type of ", italic("lac"), " induction lag (min)"))) +
theme(legend.position = 'top',
strip.placement='outside', strip.background.x=element_blank(),
axis.title.x=element_blank(), strip.text.x=element_text(size=rel(1.2), vjust = 1)) +
NULL )
)
All naive cells stop growing for at least 9’, 99% for at least 15’.
(myplots[['naive_arrest_cdf']] <-
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
# mutate(lag=ifelse(lag<240*60, lag, 245*60),
# lag=ifelse(is.na(lag), -10, lag)) %>%
ggplot(aes(x=growth_lag/60)) +
stat_ecdf(aes(y=1-..y.., col=switch_idx), show.legend=FALSE) +
labs(x='time after the switch (min)', y='fraction of growth arrested cells') +
coord_cartesian(xlim=c(0, 240), ylim=c(0, 1.05)) +
scale_x_continuous(expand = expand_scale(0, 0)) +
scale_y_continuous(expand = expand_scale(0, 0)) +
NULL
) +
labs(subtitle='naive cells (9 independent experiments)')
(myplots[['naive_arrest_hist']] <-
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
# mutate(lag=ifelse(lag<240*60, lag, 245*60),
# lag=ifelse(is.na(lag), -10, lag)) %>%
ggplot(aes(x=growth_lag/60)) +
geom_density(stat='bin', fill=qual_cols[1], col='transparent', alpha=.2, binwidth=3) +
geom_freqpoly(aes(col='growth lag'), binwidth=3) +
geom_freqpoly(aes(lag_200/60, col='induction lag'), alpha=.4, binwidth=3) +
labs(x='growth lag (min)', col='lag type') +
# coord_cartesian(xlim=c(-10, 240), expand=FALSE) +
theme(legend.position = c(1, 1), legend.justification = c(1,1)) +
NULL
) +
labs(subtitle='naive cells (9 independent experiments)')
At the second switch, we observe that there is no effect of the lag of the ancestor at the first switch.
mypairs_memory <- mycells_switching %>%
group_by(condition, date, pos, gl) %>%
do((function(.df_gl){
# browser()
# if(.df_gl$ugen[1]=='20150703.4.15.0:BBBBBBB') browser()
.geneals_sw1 <- .df_gl %>% filter(switch_idx==1) %>% with(genealogy)
if (nrow(.df_gl %>% filter(switch_idx==2)) == 0) return(data.frame())
.df_gl %>% filter(switch_idx==2) %>%
select(genealogy_2=genealogy, lag_2=lag_200, discard_arrested_2=discard_arrested,
logl_time_slope_pre_2=logl_time_slope_pre) %>% group_by(genealogy_2) %>%
# with(which_is_parent_cid(genealogy, .geneals_sw1))
mutate(genealogy_1=which_is_parent_cid(genealogy_2, .geneals_sw1),
genealogy_1=ifelse(genealogy_1>0, .geneals_sw1[genealogy_1], NA) )
})(.)) %>%
left_join(mycells_switching %>% ungroup %>%
select(date, pos, gl, genealogy_1=genealogy, lag_1=lag_200, discard_arrested_1=discard_arrested,
logl_time_slope_pre_1=logl_time_slope_pre) )
(myplots[['lag_memory']] <-
mypairs_memory %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$') | condition=='switch_long_lac') %>%
filter(!discard_arrested_1, !discard_arrested_2) %>%
# filter(logl_time_slope_pre_1 > min_growth_rate, logl_time_slope_pre_2 > min_growth_rate) %>%
ggplot() +
facet_wrap(~condition, strip.position='right', labeller=as_labeller(rename_conds)) +
# geom_abline(lty='dotted', alpha=.5) +
geom_hline(yintercept = 50, lty='dotted', alpha=.5) +
geom_point(aes(lag_1/60, lag_2/60), alpha=.2, stroke=0) +
expand_limits(x=0, y=0) +
labs(x="ancestor's lag at first switch (min)", y="lag at second switch (min)") +
NULL
)
(myplots[['lag_memory_aggreg']] <-
mypairs_memory %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$') | condition=='switch_long_lac') %>%
filter(!discard_arrested_1, !discard_arrested_2) %>%
group_by(condition, lag_1, lag_2) %>%
summarise(n=n()) %>%
(function(.df)
ggplot(.df) +
facet_wrap(~condition, strip.position='right', labeller=as_labeller(rename_conds)) +
# geom_abline(lty='dotted', alpha=.5) +
geom_hline(yintercept = 50, lty='dotted', alpha=.5) +
geom_point(aes(lag_1/60, lag_2/60, size=n)) +
scale_size_continuous(range=c(1.2/max(.df$n), 1.2), breaks=c(2, 5, 8)) +
expand_limits(x=0, y=0) +
labs(x="ancestor's lag at first switch (min)", y="lag at second switch (min)") +
NULL
)
)
This is also true when looking only at bottom cells (which are present at both switch, or which might be different in case of molecular deterimnants of the lag would be accumulated at the old cell pole), or when looking at the average in each growth channel (since lags are highly correlated within a lineage)
(myplots[['lag_memory_bottom']] <-
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(!discard_arrested) %>%
filter(cell_num_from_bottom==0) %>% # bottom_cell
gather(variable, lag, growth_lag, gfp_lag, lag_200, factor_key=TRUE) %>%
select(condition, date, pos, gl, switch_idx, variable, lag) %>%
mutate(name_col=paste0('lag_', switch_idx), switch_idx=NULL) %>%
spread(name_col, lag) %>%
filter(variable=='lag_200') %>%
ggplot() +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds), strip.position='right') +
# geom_abline(lty='dotted', alpha=.5) +
geom_hline(yintercept = 50, lty='dotted', alpha=.5) +
geom_point(aes(lag_1/60, lag_2/60), alpha=.5, stroke=0) +
scale_x_continuous(breaks=seq(0, 1000, by=100)) +
scale_y_continuous(breaks=seq(0, 1000, by=100)) +
expand_limits(x=0, y=0) +
labs(x='lag at first switch (min)', y='lag at second switch (min)') +
NULL
) +
labs(subtitle='bottom cells')
(myplots[['lag_memory_avg']] <-
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
gather(variable, lag, growth_lag, gfp_lag, lag_200, factor_key=TRUE) %>%
filter(is.finite(lag)) %>%
# compute stats per GL, per switch, per variable
group_by(condition, date, pos, gl, variable, switch_idx) %>%
summarise(mean_lag=mean(lag)) %>%
mutate(name_col=paste0('mean_lag_', switch_idx), switch_idx=NULL) %>%
spread(name_col, mean_lag) %>%
filter(variable=='lag_200') %>%
ggplot(aes(mean_lag_1/60, mean_lag_2/60)) +
facet_wrap(~condition+date, labeller=as_labeller(rename_conds), strip.position='right') +
# facet_grid(condition~variable, labeller=as_labeller(rename_conds)) +
# geom_abline(lty='dotted', alpha=.5, stroke=0) +
geom_hline(yintercept = 50, lty='dotted', alpha=.5) +
geom_point(alpha=.4) +
scale_x_continuous(breaks=seq(0, 1000, by=100)) +
labs(x='average lag at first switch (min)', y='average lag at second switch (min)') +
NULL
) +
labs(subtitle='average in growth channel')
In contrastm, we can analyse the impact of LacZ-GFP molecules inherited from the ancestor’s induction during the first switch.
First, let’s look at the distribution of induction lags at the second switch. The medians are ordered according to the delay between the two lactose exposures (which supports the idea of a molecular memory). It takes surprising long (16 to 20h i.e. 12 to 16 divisions) to relax to the lag distribution observed in naive cells.
(myplots[['memory_cdfs_facets']] <-
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$') | condition=='switch_late') %>%
mutate(lag=lag_200,
lag=ifelse(!is.infinite(lag), lag, max(lag[is.finite(lag)])+60),
lag=ifelse(is.na(lag), -1, lag)) %>%
filter(switch_idx %in% 1:2) %>%
ggplot(aes(x=lag/60)) +
facet_grid(switch_idx~., labeller=as_labeller(function(.str) paste0('switch: ', .str)) ) +
stat_ecdf(aes(y=1-..y.., col=condition, group=date), geom='line') +
labs(x=lac_lags_label, y='reverse cumulative probability', col='condition') +
scale_color_hue() +
scale_color_manual(name='delay between\nlactose exposure',
limits=c(sprintf('switch_%02dh', seq(4, 24, 4)), 'switch_late'),
labels=c(sprintf('%dh', seq(4, 24, 4)), 'control (late)'),
values=c(scales::viridis_pal()(6), 'gray70') ) +
NULL
)
(myplots[['memory_cdfs']] <-
mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$') | condition=='switch_late') %>%
mutate(lag=lag_200,
lag=ifelse(lag>240*60, Inf, lag),
# lag=ifelse(!is.infinite(lag), lag, max(lag[is.finite(lag)])+1e6),
# lag=ifelse(is.na(lag), -1e6, lag),
condition = ifelse(str_detect(condition, "switch_\\d+h"),
str_replace(condition, "switch_0?", ""), condition),
) %>%
# group_by(condition, switch_idx) %>% summarise(n())
(function(.df)
ggplot(data=NULL, aes(x=lag/60, y=1-..y.., col=condition, lty=condition)) +
stat_ecdf(aes(col='control', lty='control'), geom='line', size=rel(0.75), show.legend = FALSE,
data=filter(.df, switch_idx==1, condition != 'switch_late')) +
stat_ecdf(aes(col='control (late)', lty='control (late)'), geom='line', size=rel(0.75), show.legend = FALSE,
data=filter(.df, switch_idx==1, condition == 'switch_late')) +
stat_ecdf(geom='line', #size=.3,
data=filter(.df, switch_idx==2)) +
# geom_rect(aes(x=NULL), xmin=-Inf, xmax=0, ymin=-Inf, ymax=Inf, fill='white', col='transparent',
# data=data.frame()) +
scale_color_manual(
name='delay between\nlactose exposures',
limits=c(sprintf('%dh', seq(4, 24, 4)), 'control', 'control (late)'),
values=c(scales::viridis_pal()(6), 'gray40', 'gray70') ) +
scale_linetype_manual(
name='delay between\nlactose exposures',
limits=c(sprintf('%dh', seq(4, 24, 4)), 'control', 'control (late)'),
values=c("11", "41", "4111", "11", "41", "4111", "solid", "solid")) +
# values=c(3, 2, 4, 3, 2, 4, 1, 1)) +
guides(col=guide_legend(ncol=2), lty=guide_legend(ncol=2)) +
coord_cartesian(xlim=c(-10, 240), ylim=c(-.01, 1.01), expand=FALSE) +
labs(x=lac_lags_label, y='reverse cumulative\nprobability') +
theme_cowplot_legend_inset() +
theme(axis.line.x=element_line(), axis.line.y=element_line(),
strip.background=element_rect(fill='gray95', colour='transparent'),
legend.position = c(1, 1), legend.justification = c(1,1),
legend.margin=margin(),
legend.key.width = unit(1.8, "lines"),
# legend.key.height=unit(0.7, "lines")
) +
NULL
))
# mycells_switching %>% ungroup() %>%
# filter(!date %in% discarded_dates) %>%
# filter(discard_arrested) %>%
# filter(str_detect(condition, '^switch_[0-9]+h$') | condition=='switch_late') %>%
# ggplot(aes()) +
# geom_freqpoly(
In order to estimate the inherited LacZ-GFP of a given cell at the second switch, we compute the maximum LacZ-GFP level of its ancestor (at the end of the first lactose exposure) and account for the dilution given the number of division until the second switch (assuming even partitioning at each division).
mycells_switching_memory <- mycells_switching %>% ungroup() %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==2) %>%
select(date, pos, gl, ugen, genealogy) %>%
group_by(date, pos, gl, ugen, genealogy) %>%
# do(parents=get_all_parents_cid(.$genealogy))
do(tibble(parent=get_all_parents_cid(.$genealogy))) %>%
inner_join(
myframes %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(medium=='lactose', m_cycle==1) %>%
filter(abs(time_sec-(m_end-dt))<1) %>%
select(date, pos, gl, parent=genealogy, parent_gfp=gfp_nb) %>%
mutate(parent=str_replace(parent, ":", "")),
by=c("date", "pos", "gl", "parent") ) %>%
mutate(divs_since_par = str_length(genealogy) - str_length(parent)) %>%
left_join(
mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==2),
by=c("date", "pos", "gl", "genealogy") )
(myplots[['memory_elapsed_divs']] <-
mycells_switching_memory %>%
ggplot(aes(divs_since_par, ..density.., col=condition)) +
geom_freqpoly(binwidth=1) +
scale_color_manual(name='delay between\nlactose exposure',
limits=c(sprintf('switch_%02dh', seq(4, 24, 4))),
labels=c(sprintf('%dh', seq(4, 24, 4))),
values=c(scales::viridis_pal()(6)) ) +
labs(x="divisions since fully induced parent") +
NULL
)
(myplots[['lags_inherited_gfp']] <-
mycells_switching_memory %>%
ungroup %>%
mutate(gfp_inherit=parent_gfp/2^divs_since_par,
gfp_inherit_bin=as.numeric(as.character(Hmisc::cut2(gfp_inherit, cuts=c(-Inf, 10^seq(-1.1, 3.8, .6)), levels.mean=TRUE))) ) %>%
gather(variable, lag, growth_lag, lag_200, factor_key=TRUE) %>%
filter(variable=='lag_200') %>%
(function(.df)
ggplot(.df, aes(gfp_inherit, lag/60)) +
# facet_grid(.~variable) +
geom_violin(aes(group=factor(gfp_inherit_bin)), show.legend = FALSE) +
geom_hline(yintercept = 50, lty='dashed') +
# geom_segment(x=0, xend=2, y=100, yend=100, data=NULL) +
geom_segment(aes(x=pmax(0, gfp_inherit-sqrt(gfp_inherit)), xend=gfp_inherit+sqrt(gfp_inherit),
yend=lag/60, col=condition), alpha=.05, show.legend=FALSE) +
geom_point(aes(col=condition), pch=20, alpha=.1) +
# stat_smooth(aes(group=1), method='loess', span=.2, col='gray60', alpha=.2) +
# geom_pointrange(aes(gfp_inherit_bin, mean, ymin=mean-se, ymax=mean+se, size=n),
# data=.df %>% group_by(gfp_inherit_bin, variable) %>% summarise(n=n(), mean=mean(lag/60, na.rm=TRUE), se=sd(lag/60, na.rm=TRUE)/sqrt(n)),
# show.legend = FALSE) +
scale_color_manual(name='delay between\nlactose exposure',
limits=c(sprintf('switch_%02dh', seq(4, 24, 4))),
labels=c(sprintf('%dh', seq(4, 24, 4))),
values=c(scales::viridis_pal()(6)) ) +
guides(col=guide_legend(override.aes=list(alpha=1, size=2))) +
# scale_size_continuous(range=c(0.1, 1)) +
scale_size_continuous(range=c(0.02, 0.2)) +
scale_x_log10() +
coord_cartesian(xlim=c(0.1, 1.8e3), ylim=c(0, 110)) +
labs(x='inherited LacZ-GFP (molecules)', y=lac_lags_label) +
NULL
))
Importantly, as long as a cell inherits more than a few molecules from the first exposure to lactose, it is granted to have a short lag at the second switch. This reveals that the threshold of Lac proteins required to be readily sensitive to lactose as an inducer is on the order of 1 molecule.
(myplots[['memory_frac_short']] <-
mycells_switching_memory %>% ungroup() %>%
mutate(gfp_inherit=parent_gfp/2^divs_since_par,
gfp_inherit_bin=as.numeric(as.character(Hmisc::cut2(gfp_inherit, cuts=c(-Inf, 10^seq(-1.1, 3.8, .3)), levels.mean=TRUE))) ) %>%
filter(!is.na(lag_200)) %>%
(function(.df)
ggplot() +
geom_hline(aes(yintercept=prop), lty='dashed',
data=mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(!discard_arrested) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
filter(!is.na(lag_200)) %>%
summarise(n=n(), prop=sum(lag_200/60<50)/n)
) +
geom_rect(aes(xmin=0, xmax=Inf, ymin=prop-sqrt(prop*(1-prop)/n), ymax=prop+sqrt(prop*(1-prop)/n)),
fill='grey50', alpha=0.1,
data=mycells_switching %>% ungroup %>%
filter(!date %in% discarded_dates) %>%
filter(logl_time_slope_pre > min_growth_rate) %>%
filter(str_detect(condition, '^switch_[0-9]+h$')) %>%
filter(switch_idx==1) %>%
filter(!is.na(lag_200)) %>%
group_by(date) %>%
summarise(n=n(), prop=sum(lag_200/60<50)/n)
) +
geom_pointrange(aes(gfp_inherit_bin, prop, size=n, ymin=prop-se, ymax=prop+se),
data=.df %>% group_by(gfp_inherit_bin) %>% summarise(n=n(), prop=sum(lag_200/60<50)/n, se=sqrt(prop*(1-prop)/n))) +
scale_size_continuous(range=c(0.05, .5), breaks=c(50, 150)) +
scale_x_continuous(breaks=10^(0:3),
trans=scales::trans_new(name='log10_rev',
transform=function(x) -log(x, 10),
inverse=function(x) 10^(-x))
) +
coord_cartesian(xlim=c(0.1, 3e3)) +
expand_limits(y=0) +
labs(x='inherited LacZ-GFP molecules', y='fraction of short lags', size='sample size') +
guides(size=guide_legend(direction='horizontal')) +
theme_cowplot_legend_inset() +
theme(legend.position=c(0, 0), legend.justification=c(-0.1, -0.5), #'top',
legend.margin=margin(),
) +
# theme(legend.position=c(0.99, 0.99), legend.justification=c(1, 1)) +
NULL
))