# --- # jupyter: # jupytext: # formats: ipynb,py:light # text_representation: # extension: .py # format_name: light # format_version: '1.5' # jupytext_version: 1.4.0 # kernelspec: # display_name: Python 3 # language: python # name: python3 # --- import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec import os # setting Font to Arial import matplotlib as mpl mpl.rcParams['font.family'] = "Arial" mpl.rcParams['font.sans-serif'] = "Arial" mpl.rcParams['font.size'] = "12" mpl.font_manager._rebuild() def no_spines(ax): ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) return ax def wf_sim(N, t_max, n_traj, f0=None): ''' Simulate n_traj neutral Wright-Fisher frequency trajectories, starting at frequency f0. If f0==None, simulate new mutations with a reflective lower bound at zero. ''' f = np.zeros((t_max, n_traj)) if f0: f[0] = f0 for i in range(1, t_max): if f0: f[i] = np.random.binomial(N, f[i-1]) / N else: f[i] = np.clip(np.random.binomial(N, f[i-1]), 1, N) / N return f def bounds(f0, t): '''Bounds on the typical frequency evolution starting at frequency f0.''' if f0 == 0: return (0, t) else: envelope = np.sqrt(f0*(1-f0)*t) return (np.clip(f0 - envelope,0,1), np.clip(f0 + envelope,0,1)) def p_ext(f0, t): '''Approximate extinction probability starting at frequency f0''' return np.piecewise(t, [t <= 0, t > 0], [0, lambda x: np.exp(-f0/x)]) # ## Run simulations f_common = 0.25 N = 1000 t_max = 0.5 gens = int(N*t_max) t = np.arange(0, gens)/N n_traj = 5 np.random.seed(101) traj_common = wf_sim(N, gens, n_traj, f_common) plt.plot(t, traj_common) np.random.seed(102) traj_new = wf_sim(N, gens, n_traj) plt.plot(t, traj_new) # ## Test subplots def plot_envelopes(ax, alpha=0.15, downsample=4, lw=1.5): c_new = 'C1' c_common = 'C0' ax.fill_between(t, *bounds(0,t), alpha=alpha, color=c_new) ax.plot(t[::downsample], traj_new[::downsample], color=c_new, lw=lw) ax.fill_between(t, *bounds(f_common,t), alpha=alpha, color=c_common) ax.plot(t[::downsample], traj_common[::downsample], color=c_common, lw=lw) ax.set_ylabel('Allele frequency') ax.set_xlabel('Scaled time, $t/2N$') return ax # + fig = plt.figure(figsize=(3,3)) ax = fig.add_subplot(111) plot_envelopes(ax, lw=1.5, alpha=0.1) ax.set_xlim([0, 0.51]) ax.set_xticks([0, 0.25, 0.5]) # - def plot_extinction(ax, lw=1.5): ax.plot(t, p_ext(f_common, t)) ax.set_ylabel('Extinction prob.') ax.set_xlabel('Scaled time, $T/2N$') return ax fig = plt.figure(figsize=(3,1.5)) ax = fig.add_subplot(111) plot_extinction(ax) ax.set_xlim([0, 0.51]) ax.set_xticks([0, 0.25, 0.5]) # ## Full plot # + fig = plt.figure(figsize=(2.75, 4.5)) lw = 1.5 spec = gridspec.GridSpec(ncols=1, nrows=2, figure=fig, height_ratios=[2,1]) ax1 = fig.add_subplot(spec[0]) ax2 = fig.add_subplot(spec[1]) plot_envelopes(ax1, lw=lw) ax1.set_xticklabels([]) ax1.set_xlabel(None) ax1.set_ylim([0, 0.5]) ax1.set_yticks(np.linspace(0, 0.5, 3)) plot_extinction(ax2, lw=lw) ax2.set_ylim([0, 0.7]) ax2.set_yticks(np.linspace(0,0.7,2)) ax2.set_yticklabels(map(lambda x: f"{x:.2f}", ax2.get_yticks())) for ax in fig.axes: ax.vlines([0.05,0.5], 0, 1, linestyle='dashed', lw=lw, zorder=3) ax.set_xlim([0, 0.51]) ax.set_xticks([0.05,0.5]) ax.xaxis.set_tick_params(width=lw) no_spines(ax) figdir = '../plots/figure4/' os.makedirs(figdir, exist_ok=True) plt.savefig(figdir + 'fig4BC.pdf', bbox_inches='tight') # -