diff --git a/plotting.py b/plotting.py index 6953dc24f19bd163b2d1a65a683539de4aa773a4..5861b828ff4a2d50e884eb6be04ce91028d84354 100644 --- a/plotting.py +++ b/plotting.py @@ -1,3 +1,5 @@ +from copy import copy + import matplotlib.cm import matplotlib.pylab as plt import numpy as np @@ -290,3 +292,155 @@ def plot_replicate_array( plt.grid() if show: plt.show(block=False) + + +def plothist( + t, + da, + num_fine=200, + vmax_factor=1, + norm="log", + edges=None, + title=None, + figsize=None, +): + """ + Plot the distribution of fluorescence as a function of time. + + Time is on the x-axis; the support of the distribution is on the y-axis; + and shading represents the height of the distribution at each y value. + + Parameters + ---------- + t: 1D array + An array of time points. + da: 2D array + An array of the fluorescence data, with each row containing data from + a single cell. + num_fine: integer (optional) + The number of data points to include in a finer spacing of time points. + Linear interpolation is used to find the extra data values. + vmax_factor: float (optional) + Used to rescale the maximal data value, which is then passed to the vmax + argument in plt.pcolormesh to change the range of the colour bar. + norm: str (optional) + Passed to the norm argument in plt.pcolormesh for mapping the data + values to the colour map. Default is "log". + edges: list of arrays (optional) + Specifies the bins in time and in fluorescence values and is used to plot + different data sets on axes with the same range. + title: str (optional) + Title for the plot. + figsize: tuple (optional) + Sets the width and height of the figure. + + Examples + -------- + Load data: + >>> from wela.dataloader import dataloader + >>> from wela.plotting import plothist + >>> dl = dataloader() + >>> dl.load("1334_2023_03_28_pyrTo2Gal_01glc_00", use_tsv=True) + >>> dlc = dataloader() + >>> dlc.load("1005_2023_03_09_exp_00_pyrToGalInduction, use_tsv="True") + + Get time-series data: + >>> t, d = dl.get_time_series("median_GFP") + >>> tc, dc = dlc.get_time_series("median_GFP") + + Plot both data sets using axes with the same range: + >>> edges = plothist(t, dc, title="2% Gal", figsize=(4, 3)) + >>> plothist(t, d, title="2% Gal and 0.1% Glu", edges=edges, figsize=(4, 3)) + """ + # interp to a new grid and replace NaN + t_fine = np.linspace(t.min(), t.max(), num_fine) + s_fine = np.empty((da.shape[0], num_fine), dtype=float) + for i in range(da.shape[0]): + s = da[i, :] + s_fine[i, :] = np.interp(t_fine, t[~np.isnan(s)], s[~np.isnan(s)]) + t_fine = np.matlib.repmat(t_fine, da.shape[0], 1) + # make histogram + cmap = copy(plt.cm.viridis) + cmap.set_bad(cmap(0)) + if edges is not None: + h, xedges, yedges = np.histogram2d( + t_fine.flatten(), + s_fine.flatten(), + bins=edges, + ) + else: + h, xedges, yedges = np.histogram2d( + t_fine.flatten(), + s_fine.flatten(), + bins=[int(num_fine / 2), 50], + ) + vmax = np.nanmax(da) / vmax_factor + # plot using pcolormesh + if figsize is not None: + plt.figure(figsize=figsize) + else: + plt.figure() + pcm = plt.pcolormesh( + xedges, + yedges, + h.T, + cmap=cmap, + rasterized=True, + vmax=vmax, + norm=norm, + ) + plt.colorbar(pcm, label="number of cells", pad=0.02) + plt.xlabel("time (h)") + plt.ylabel("fluorescence") + plt.ylim(top=1200) + if title: + plt.title(title) + plt.show(block=False) + return [xedges, yedges] + + +def plot_cuml_divisions_per_cell(t, buddings, nboots=30, col="b", label=None): + """ + Plot the mean cumulative number of divisions per cell over time. + + No figure is automatically generated. See example. + + Parameters + ---------- + t: 1D array + An array of time points. + buddings: 2D array + A binary array where a budding event is denoted by a one and each row is + a time series of budding events for one cell. + nboots: integer (optional) + The number of bootstraps to use to estimate errors. + col: str (optional) + Color of the line to plot. + label: str (optional) + Label for the legend. + + Example + ------- + >>> from wela.plotting import plot_cuml_divisions_per_cell + >>> plt.figure() + >>> plot_cuml_divisions_per_cell(t, b, label="Gal Glu") + >>> plt.show(block=False) + """ + + def find_cuml(b): + return ( + np.array([np.nansum(b[:, :i]) for i in range(b.shape[1])]) + / b.shape[0] + ) + + def sample(b): + return np.array( + [b[i, :] for i in np.random.randint(0, b.shape[0], b.shape[0])] + ) + + cuml = find_cuml(buddings) + err_cuml = 2 * np.std( + [find_cuml(sample(buddings)) for i in range(nboots)], 0 + ) + plt.plot(t, cuml, color=col, label=label) + plt.fill_between(t, cuml - err_cuml, cuml + err_cuml, color=col, alpha=0.2)