import plotstyle_plt import numpy as np import matplotlib.pyplot as plt from itertools import product from thunderhopper.filetools import search_files from thunderhopper.modeltools import load_data from thunderhopper.filtertools import find_kern_specs from misc_functions import get_saturation from color_functions import load_colors from plot_functions import hide_axis, ylimits, super_xlabel, ylabel, title_subplot,\ plot_line, strip_zeros, time_bar, assign_colors,\ letter_subplot, letter_subplots, reorder_by_sd from IPython import embed def plot_snippets(axes, time, snippets, ymin=None, ymax=None, **kwargs): ymin, ymax = ylimits(snippets, minval=ymin, maxval=ymax, pad=0.05) handles = [] for i, ax in enumerate(axes): handles.append(plot_line(ax, time, snippets[:, ..., i], ymin=ymin, ymax=ymax, **kwargs)) return handles def plot_curves(ax, scales, measures, fill_kwargs={}, **kwargs): if measures.ndim == 1: ax.plot(scales, measures, **kwargs)[0] return measures median_measure = np.nanmedian(measures, axis=1) spread_measure = [np.nanpercentile(measures, 25, axis=1), np.nanpercentile(measures, 75, axis=1)] ax.plot(scales, median_measure, **kwargs)[0] ax.fill_between(scales, *spread_measure, **fill_kwargs) return median_measure def exclude_zero_scale(data, stages): inds = data['scales'] > 0 data['scales'] = data['scales'][inds] for stage in stages: data[f'mean_{stage}'] = data[f'mean_{stage}'][inds, ...] return data def reduce_kernel_set(data, inds, keyword, stages=['conv', 'feat']): for stage in stages: key = f'{keyword}_{stage}' data[key] = data[key][:, inds, ...] return data # GENERAL SETTINGS: target_species = [ 'Chorthippus_biguttulus', 'Chorthippus_mollis', 'Chrysochraon_dispar', 'Euchorthippus_declivus', 'Gomphocerippus_rufus', 'Omocestus_rufipes', 'Pseudochorthippus_parallelus', ][5] example_file = { 'Chorthippus_biguttulus': 'Chorthippus_biguttulus_GBC_94-17s73.1ms-19s977ms', 'Chorthippus_mollis': 'Chorthippus_mollis_DJN_41_T28C-46s4.58ms-1m15s697ms', 'Chrysochraon_dispar': 'Chrysochraon_dispar_DJN_26_T28C_DT-32s134ms-34s432ms', 'Euchorthippus_declivus': 'Euchorthippus_declivus_FTN_79-2s167ms-2s563ms', 'Gomphocerippus_rufus': 'Gomphocerippus_rufus_FTN_91-3-884ms-10s427ms', 'Omocestus_rufipes': 'Omocestus_rufipes_DJN_32-40s724ms-48s779ms', 'Pseudochorthippus_parallelus': 'Pseudochorthippus_parallelus_GBC_88-6s678ms-9s32.3ms' }[target_species] stages = ['filt', 'env', 'inv', 'conv', 'feat'] raw_path = search_files(target_species, incl='unnormed', dir='../data/inv/short/condensed/')[0] base_path = search_files(target_species, incl='base', dir='../data/inv/short/condensed/')[0] range_path = search_files(target_species, incl='range', dir='../data/inv/short/condensed/')[0] snip_path = search_files(example_file, dir='../data/inv/short/')[0] save_path = '../figures/fig_invariance_short.pdf' # ANALYSIS SETTINGS: exclude_zero = True # SUBSET SETTINGS: types = np.array([1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8, 9, -9, 10, -10]) sigmas = np.array([0.001, 0.002, 0.004, 0.008, 0.016, 0.032]) # types = [1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8, 9, -9, 10, -10] # sigmas = [0.001, 0.002, 0.004, 0.008, 0.016, 0.032] kernels = np.array([ [1, 0.002], [-1, 0.002], [2, 0.004], [-2, 0.004], [3, 0.032], [-3, 0.032] ]) kernels = None # GRAPH SETTINGS: fig_kwargs = dict( figsize=(32/2.54, 32/2.54), ) super_grid_kwargs = dict( nrows=2, ncols=1, wspace=0, hspace=0, left=0, right=1, bottom=0, top=1, height_ratios=[3, 2] ) subfig_specs = dict( snip=(0, 0), big=(1, 0), ) snip_grid_kwargs = dict( nrows=len(stages), ncols=None, wspace=0.1, hspace=0.4, left=0.11, right=0.98, bottom=0.08, top=0.95 ) big_grid_kwargs = dict( nrows=1, ncols=3, wspace=0.4, hspace=0, left=snip_grid_kwargs['left'], right=snip_grid_kwargs['right'], bottom=0.13, top=0.98 ) # PLOT SETTINGS: fs = dict( lab_norm=16, lab_tex=20, letter=22, tit_norm=16, tit_tex=20, bar=16, ) colors = load_colors('../data/stage_colors.npz') conv_colors = load_colors('../data/conv_colors_all.npz') feat_colors = load_colors('../data/feat_colors_all.npz') lw = dict( filt=0.25, env=0.25, conv=0.25, inv=0.25, feat=1, big=3, plateau=1.5, ) xlabels = dict( big='scale $\\alpha$', ) ylabels = dict( filt='$x_{\\text{filt}}$', env='$x_{\\text{env}}$', inv='$x_{\\text{adapt}}$', conv='$c_i$', feat='$f_i$', big=['measure', 'rel. measure', 'norm. measure'] ) xlab_big_kwargs = dict( y=0, fontsize=fs['lab_norm'], ha='center', va='bottom', ) ylab_snip_kwargs = dict( x=0, fontsize=fs['lab_tex'], rotation=0, ha='left', va='center' ) ylab_big_kwargs = dict( x=-0.2, fontsize=fs['lab_norm'], ha='center', va='bottom', ) yloc = dict( filt=3000, env=1000, inv=1000, conv=30, feat=1, ) title_kwargs = dict( x=0.5, yref=1, ha='center', va='top', fontsize=fs['tit_norm'], ) letter_snip_kwargs = dict( x=0, yref=0.5, ha='left', va='center', fontsize=fs['letter'], ) letter_big_kwargs = dict( x=0, y=1, ha='left', va='bottom', fontsize=fs['letter'], ) bar_time = 5 bar_kwargs = dict( dur=bar_time, y0=-0.25, y1=-0.1, xshift=1, color='k', lw=0, clip_on=False, text_pos=(-0.1, 0.5), text_str=f'${bar_time}\\,\\text{{s}}$', text_kwargs=dict( fontsize=fs['bar'], ha='right', va='center', ) ) plateau_settings = dict( low=0.05, high=0.95, first=True, last=True, condense=None, ) plateau_line_kwargs = dict( lw=lw['plateau'], ls='--', zorder=1, ) plateau_dot_kwargs = dict( marker='o', markersize=8, markeredgewidth=1, clip_on=False, ) # EXECUTION: # Load raw (unnormed) invariance data: data, config = load_data(raw_path, files='scales', keywords='mean') if exclude_zero: data = exclude_zero_scale(data, stages) scales = data['scales'] # Load snippet data: snip, _ = load_data(snip_path, files='example_scales', keywords='snip') t_full = np.arange(snip['snip_filt'].shape[0]) / config['rate'] snip_scales = snip['example_scales'] # Optional kernel subset: reduce_kernels = False if any(var is not None for var in [kernels, types, sigmas]): kern_inds = find_kern_specs(config['k_specs'], kernels, types, sigmas) data = reduce_kernel_set(data, kern_inds, keyword='mean') snip = reduce_kernel_set(snip, kern_inds, keyword='snip') reduce_kernels = True # Adjust grid parameters: snip_grid_kwargs['ncols'] = snip_scales.size # Prepare overall graph: fig = plt.figure(**fig_kwargs) super_grid = fig.add_gridspec(**super_grid_kwargs) # Prepare stage-specific snippet axes: snip_subfig = fig.add_subfigure(super_grid[subfig_specs['snip']]) snip_grid = snip_subfig.add_gridspec(**snip_grid_kwargs) snip_axes = np.zeros((snip_grid.nrows, snip_grid.ncols), dtype=object) for i, j in product(range(snip_grid.nrows), range(snip_grid.ncols)): ax = snip_subfig.add_subplot(snip_grid[i, j]) ax.set_xlim(t_full[0], t_full[-1]) ax.yaxis.set_major_locator(plt.MultipleLocator(yloc[stages[i]])) hide_axis(ax, 'bottom') if i == 0: title = title_subplot(ax, f'$\\alpha={strip_zeros(snip_scales[j])}$', ref=snip_subfig, **title_kwargs) if j == 0: ylabel(ax, ylabels[stages[i]], **ylab_snip_kwargs, transform=snip_subfig.transSubfigure) else: hide_axis(ax, 'left') snip_axes[i, j] = ax time_bar(snip_axes[-1, -1], **bar_kwargs) letter_subplot(snip_subfig, 'a', ref=title, **letter_snip_kwargs) # Prepare analysis axes: big_subfig = fig.add_subfigure(super_grid[subfig_specs['big']]) big_grid = big_subfig.add_gridspec(**big_grid_kwargs) big_axes = np.zeros((big_grid.ncols,), dtype=object) for i in range(big_grid.ncols): ax = big_subfig.add_subplot(big_grid[0, i]) ax.set_xlim(scales[0], scales[-1]) ax.set_xscale('symlog', linthresh=scales[1], linscale=0.5) ax.set_yscale('symlog', linthresh=0.01, linscale=0.1) ylabel(ax, ylabels['big'][i], **ylab_big_kwargs) if i < (big_grid.ncols - 1): ax.set_ylim(scales[0], scales[-1]) else: ax.set_ylim(0, 1) big_axes[i] = ax super_xlabel(xlabels['big'], big_subfig, big_axes[0], big_axes[-1], **xlab_big_kwargs) letter_subplots(big_axes, 'bcd', **letter_big_kwargs) if True: # Plot filtered snippets: plot_snippets(snip_axes[0, :], t_full, snip['snip_filt'], c=colors['filt'], lw=lw['filt']) # Plot envelope snippets: plot_snippets(snip_axes[1, :], t_full, snip['snip_env'], ymin=0, c=colors['env'], lw=lw['env']) # Plot "adapted" snippets: plot_snippets(snip_axes[2, :], t_full, snip['snip_inv'], c=colors['inv'], lw=lw['inv']) # Plot kernel response snippets: all_handles = plot_snippets(snip_axes[3, :], t_full, snip['snip_conv'], c=colors['conv'], lw=lw['conv']) for i, handles in enumerate(all_handles): assign_colors(handles, config['k_specs'][:, 0], conv_colors) reorder_by_sd(handles, snip['snip_conv'][..., i]) # Plot feature snippets: all_handles = plot_snippets(snip_axes[4, :], t_full, snip['snip_feat'], ymin=0, ymax=1, c=colors['feat'], lw=lw['feat']) for i, handles in enumerate(all_handles): assign_colors(handles, config['k_specs'][:, 0], feat_colors) reorder_by_sd(handles, snip['snip_feat'][..., i]) del snip # Remember saturation points: crit_inds, crit_scales = {}, {} # Unnormed measures: for stage in stages: # Plot average intensity measure across recordings: curve = plot_curves(big_axes[0], scales, data[f'mean_{stage}'].mean(axis=-1), c=colors[stage], lw=lw['big'], fill_kwargs=dict(color=colors[stage], alpha=0.25)) # Indicate saturation point: if stage == 'feat': ind = get_saturation(curve, **plateau_settings)[1] scale = scales[ind] big_axes[0].plot(scale, 0, c='w', alpha=1, zorder=5.5, **plateau_dot_kwargs, transform=big_axes[0].get_xaxis_transform()) big_axes[0].plot(scale, 0, mfc=colors[stage], mec='k', alpha=0.75, zorder=6, **plateau_dot_kwargs, transform=big_axes[0].get_xaxis_transform()) big_axes[0].vlines(scale, big_axes[0].get_ylim()[0], curve[ind], color=colors[stage], **plateau_line_kwargs) # Log saturation point: crit_inds[stage] = ind crit_scales[stage] = scale del data # Noise baseline-related measures: data, _ = load_data(base_path, files='scales', keywords='mean') if exclude_zero: data = exclude_zero_scale(data, stages) if reduce_kernels: data = reduce_kernel_set(data, kern_inds, keyword='mean') for stage in stages: # Plot average intensity measure across recordings: curve = plot_curves(big_axes[1], scales, data[f'mean_{stage}'].mean(axis=-1), c=colors[stage], lw=lw['big'], fill_kwargs=dict(color=colors[stage], alpha=0.25)) # Indicate saturation point: if stage == 'feat': ind, scale = crit_inds[stage], crit_scales[stage] big_axes[1].plot(scale, 0, c='w', alpha=1, zorder=5.5, **plateau_dot_kwargs, transform=big_axes[1].get_xaxis_transform()) big_axes[1].plot(scale, 0, mfc=colors[stage], mec='k', alpha=0.75, zorder=6, **plateau_dot_kwargs, transform=big_axes[1].get_xaxis_transform()) big_axes[1].vlines(scale, big_axes[1].get_ylim()[0], curve[ind], color=colors[stage], **plateau_line_kwargs) del data # Min-max normalized measures: data, _ = load_data(range_path, files='scales', keywords='mean') if exclude_zero: data = exclude_zero_scale(data, stages) if reduce_kernels: data = reduce_kernel_set(data, kern_inds, keyword='mean') for stage in ['feat']: # Plot average intensity measure across recordings: curve = plot_curves(big_axes[2], scales, data[f'mean_{stage}'].mean(axis=-1), c=colors[stage], lw=lw['big'], fill_kwargs=dict(color=colors[stage], alpha=0.25)) # Indicate saturation point: if stage == 'feat': ind, scale = crit_inds[stage], crit_scales[stage] big_axes[2].plot(scale, 0, c='w', alpha=1, zorder=5.5, **plateau_dot_kwargs, transform=big_axes[2].get_xaxis_transform()) big_axes[2].plot(scale, 0, mfc=colors[stage], mec='k', alpha=0.75, zorder=6, **plateau_dot_kwargs, transform=big_axes[2].get_xaxis_transform()) big_axes[2].vlines(scale, big_axes[2].get_ylim()[0], curve[ind], color=colors[stage], **plateau_line_kwargs) del data # Save graph: if save_path is not None: file_name = save_path.replace('.pdf', f'_{target_species}.pdf') fig.savefig(file_name) plt.show() print('Done.') embed()