def plot_curves(df_pcr, df_melt): sgrnas = 'on', 'off', 'rxb 11,1', 'mhf 30' n = len(sgrnas) fig, axes = plt.subplots(n, 2, sharex='col', figsize=(8, 3*n)) for i, sgrna in enumerate(sgrnas): ax_pcr = axes[i,0] ax_melt = axes[i,1] # Plot the PCR curves. label = matplotlib.offsetbox.AnchoredText(sgrna, loc="upper left") ax_pcr.add_artist(label) q = df_pcr[df_pcr.sgrna == sgrna] for key, well in q.groupby(['promoter', 'primers', 'Well Position']): promoter, primers, label = key ax_pcr.semilogy( well.Cycle, well.Rn, label=label, basey=2, **pick_style(promoter, primers), ) ax_pcr.set_ylim(2**-1, 2**4) ax_pcr.set_xlim(1, ax_pcr.get_xlim()[1]) ax_pcr.set_ylabel("Rn") ax_pcr.grid(True) # Plot the melting curves. q = df_melt[df_melt.sgrna == sgrna] for key, well in q.groupby(['promoter', 'primers', 'Well Position']): promoter, primers, label = key ax_melt.plot( well.Temperature, well.Derivative / 10000, label=label, **pick_style(promoter, primers), ) x = well.Temperature ax_melt.set_xlim(x.min(), x.max()) ax_melt.set_ylim(0, ax_melt.get_ylim()[1]) ax_melt.set_ylabel("dFluor/dT (×10⁴)") ax_melt.grid(True) make_legend(axes[0,1], loc="upper left") ax_pcr.set_xlabel("Cycle") ax_melt.set_xlabel("Temperature (°C)") fig.tight_layout() return fig
def plot_mic_curve(ax, df, time_hr=12): # Pick a time to display. t = df['minutes'].unique() dt = abs(t - (60 * time_hr)) time_min = t[dt.argmin()] df = df.query('minutes == @time_min') for key, group in df.groupby(['sgrna', 'theo_mM']): sgrna, theo_mM = key style = pick_style(sgrna, theo_mM, color_controls=True) # Drop points with only one measurement. q = group.groupby('tmp_ug_mL').filter(lambda x: len(x) > 1) reps = q.groupby('tmp_ug_mL') concs = reps.apply(lambda x: x.name) means = reps.mean()['read'] stdevs = reps.std()['read'] ## Growth curves. ax.semilogy( concs, means, **style, ) ## Error bars: # I can't just use `ax.errorbar()`, because it doesn't have allow the # error bar lines to be styled, and I need the style to distinguish apo # from holo. x_err = np.array([concs, concs]) y_err = np.array([means + stdevs / 2, means - stdevs / 2]) if style['color'] == 'black': style['linestyle'] = '-' ax.semilogy( x_err, y_err, marker='_', **style, ) label = AnchoredText(f"{int(time_hr)} hr", loc='upper right') ax.add_artist(label) ax.set_xlabel('TMP (µg/mL)') ax.set_xlim(0, 64) ax.set_xticks([0, 4, 8, 16, 32, 48, 64]) #ax.xaxis.set_minor_locator(MultipleLocator(4)) ax.set_ylabel('OD600') ax.set_ylim(0.053, 1.2) ax.yaxis.set_major_formatter(StrMethodFormatter("{x:.1f}"))
def make_legend(ax, **kwargs): ax.legend( handles=[ matplotlib.lines.Line2D( [], [], label="J23150", **pick_style('j23150', 'sgrna'), ), matplotlib.lines.Line2D( [], [], label="J23119", **pick_style('j23119', 'sgrna'), ), matplotlib.lines.Line2D( [], [], label="16S rRNA", **pick_style('', '16s'), ), ], **kwargs, )
def plot_timecourse(ax, df, mid, sgrna, ligand): lig0 = ligand.split('→')[0] try: q0 = df.loc[sgrna, lig0] q1 = df.loc[sgrna, ligand] q = pd.concat([q0, q1]) except KeyError: return x = q['minutes'] y = q['fold_change'] std = q['fold_change_err'] x_err = np.array([x, x]) y_err = np.array([y + std / 2, y - std / 2]) styles = { 'apo→apo': pick_style('control', 0, color_controls=True), 'apo→holo': pick_style(sgrna, 1, color_controls=True), 'holo→apo': pick_style(sgrna, 0, color_controls=True), 'holo→holo': pick_style('control', 1, color_controls=True), } style = styles[ligand] ax.semilogy(x, y, **style) ax.semilogy(x_err, y_err, marker='_', **style) if (sgrna, ligand) in mid: x_mid = mid[sgrna, ligand] y_mid = np.interp(x_mid, x, y) y_0 = 1e-3 ax.axvline( x_mid, linestyle=':', color=style['color'], zorder=-10, )
def plot_growth_curves(ax, df, sgrnas, ref_theo_mM=1): df_sgrnas = df[df.sgrna.isin(sgrnas)] for key, sele in df_sgrnas.groupby(['sgrna', 'theo_mM', 'path', 'well']): sgrna, theo_mM, well, path = key style = pick_style(sgrna, theo_mM, color_controls=True, standard_mM=ref_theo_mM) #ax.semilogy(sele.minutes/60, sele.read, **style) #time_hr = sele.minutes / 60 #smoothed_reads = savgol_filter(sele.read, 21, 1) sele = sele.sort_values('minutes') hours, reads = sele.minutes / 60, sele.read ax.semilogy(hours, reads, **style)
'fol1 mhf 30': 'ligRNA⁺', } fig, axes = plt.subplots(2, 2, sharex=True, sharey=True) for ax, ligrna in zip(axes.flat, ligrnas): legend_entries = [] for theo in [True, False]: q = df.query('ligrna == @ligrna and theo == @theo') q_mean = q.groupby('well').agg({ 'od600': 'mean', 'rel_od600': 'mean', 'x': 'mean', }) trend_style = pick_style(ligrna, not theo) trend_style['label'] = '_nolabel_' data_style = { 'linestyle': 'none', 'marker': 'o', 'markeredgecolor': 'none' if theo else trend_style['color'], 'markerfacecolor': trend_style['color'] if theo else 'none', } ax.set_title(titles[ligrna]) data, = ax.plot(q.x, q.rel_od600, **data_style) trend, = ax.plot(q_mean.x, q_mean.rel_od600, **trend_style) legend_entries.append(( (data, trend), '1 mM theo' if theo else '0 mM theo',
def plot_growth_curve(ax, df, tmp_ug_mL=32.0): # Pick a TMP concentration to display. c = df['tmp_ug_mL'].unique() dc = abs(c - (tmp_ug_mL)) tmp_ug_mL = c[dc.argmin()] df = df.query('tmp_ug_mL == @tmp_ug_mL') for key, group in df.groupby(['sgrna', 'theo_mM']): sgrna, theo_mM = key style = pick_style(sgrna, theo_mM, color_controls=True) reps = group.groupby('minutes') times = reps.apply(lambda x: x.name) / 60 means = reps.mean()['read'] log_means = 10**reps['read'].agg(lambda x: np.mean(np.log10(x))) medians = reps.median()['read'] stdevs = reps.std()['read'] x = times y = medians ## Growth curves. # Plotting the means is smoother, but more affected by outliers # (especially for the positive control on a log-scale). ax.semilogy(x, y, **style) ## Error bars: # I can't just use `ax.errorbar()`, because it doesn't have allow the # error bar lines to be styled, and I need the style to distinguish apo # from holo. x_err = np.array([times, times]) y_err = np.array([y + stdevs / 2, y - stdevs / 2]) # Stagger the apo and holo error bars, so they don't overlap. k = 6 i = k // 2 * int(theo_mM) if style['color'] == 'black': style['linestyle'] = '-' ax.semilogy( x_err[:, i::k], y_err[:, i::k], marker='_', **style, ) label = AnchoredText(f"{tmp_ug_mL} µg/mL TMP", loc='upper left') ax.add_artist(label) ## Axis decorations: ax.set_xlabel('time (h)') ax.set_xticks([0, 4, 8, 12, 16, 20]) ax.set_xlim(0, 20) ax.set_ylabel('OD600') ax.set_ylim(0.053, 1.00) ax.yaxis.set_major_formatter(StrMethodFormatter("{x:.1f}"))
ligands = sorted(df.index.get_level_values('ligand_after').unique(), key=lambda x: [False, True].index(x)) times = df.index.get_level_values('time').unique() fig, axes = plt.subplots( len(ligands), len(sgrnas), figsize=(4*len(sgrnas), 4*len(ligands)), sharex=True, squeeze=False, ) for i, ligand in enumerate(ligands): for j, sgrna in enumerate(sgrnas): ax = axes[i,j] fit_style = pick_style(sgrna, ligand) data_style = pick_data_style(sgrna, ligand) q = df.loc[sgrna, ligand] t = q.index.get_level_values('time') y = q['fold_change'] ax.plot(t, y, label='_', **data_style) fit = fit_decay(q) t_fit = np.linspace(0, max(t), 500) y_fit = decay(t_fit, fit.k, fit.y0, fit.y_inf) label = f'''\ k={fit.k:.1e} ± {fit.k_std:.1e} y0={fit.y0:.1e} ± {fit.y0_std:.1e}
def plot_fits(rxns, stem, subtract_intercept=False, figure_mode=False): rows, cols = load_keys(rxns) if figure_mode: subtract_intercept = True size_in = 1.5 if figure_mode else 3.0 fig_size_in = size_in * len(cols), size_in * len(rows) fig, axes = plt.subplots( len(rows), len(cols), figsize=fig_size_in, squeeze=False, sharex=True, sharey=figure_mode, ) max_t = 0 min_a420 = {x: inf for x in itertools.product(rows.values(), cols.values())} max_a420 = {x: -inf for x in itertools.product(rows.values(), cols.values())} for _, rxn in iter_reactions(rxns): b = rxn.fit[1] if subtract_intercept else 0 t, y = rxn.t_min, rxn.a420 - b i, j = rxn.i_min, rxn.i_max fit_style = pick_style(rxn.sgrna, not rxn.ligand) fit_style['label'] = rxn.well data_style = fit_style.copy(); data_style.pop('dashes', None) data_style.update(marker='+', linestyle='none', label='_nolabel_') trim_style = data_style.copy() trim_style.update(marker='_', markerfacecolor='none') row_key, col_key = rxn.keys row = rows[rxn.meta[row_key]] col = cols[rxn.meta[col_key]] ax = axes[row,col] ax.plot(t.iloc[:i], y.iloc[:i], **trim_style) ax.plot(t.iloc[i:j], y.iloc[i:j], **data_style) ax.plot(t.iloc[j:], y.iloc[j:], **trim_style) if rxn.miller != nan: ax.plot(t, rxn.linear_fit(t) - b, **fit_style) if figure_mode: min_a420[row,col] = 0 max_a420[row,col] = 1 else: min_a420[row,col] = min(min_a420[row,col], min(rxn.a420 - b)) max_a420[row,col] = max(max_a420[row,col], max(rxn.a420 - b)) if figure_mode: max_t = 20 else: max_t = max(max_t, max(rxn.t_min)) label = '\n'.join(textwrap.wrap( f'{rxn.meta[row_key]} {rxn.meta[col_key]}', width=9, break_long_words=False, )) if not figure_mode: ax.legend(title=label, loc='upper left') for i, row in enumerate(axes): for j, ax in enumerate(row): ax.set_ylim(min_a420[i,j], max_a420[i,j]) for ax in axes[-1,:]: ax.set_xlabel('time (min)') for ax in axes[:,-1 if figure_mode else 0]: box = dict(facecolor='yellow') ax.set_ylabel('ΔA420' if subtract_intercept else 'A420') for ax in axes.flat: ax.set_xlim(0, max_t) if figure_mode: for ax in axes.flat: ax.yaxis.set_tick_params(left=False, labelleft=False) for ax in axes[:,-1]: ax.yaxis.set_tick_params(right=True, labelright=True) ax.yaxis.set_label_position("right") finalize_plot(fig, stem, 'fits')