diff --git a/figures/fig_invariance_log_hp.pdf b/figures/fig_invariance_log_hp.pdf index 1271291..72cab27 100644 Binary files a/figures/fig_invariance_log_hp.pdf and b/figures/fig_invariance_log_hp.pdf differ diff --git a/figures/fig_invariance_thresh_lp_species.pdf b/figures/fig_invariance_thresh_lp_species.pdf index 74f3ff3..eb610c4 100644 Binary files a/figures/fig_invariance_thresh_lp_species.pdf and b/figures/fig_invariance_thresh_lp_species.pdf differ diff --git a/main.aux b/main.aux index f12e891..736d2c4 100644 --- a/main.aux +++ b/main.aux @@ -248,25 +248,25 @@ \newlabel{eq:toy_log}{{12}{11}{}{}{}} \newlabel{eq:toy_highpass}{{13}{11}{}{}{}} \newlabel{eq:toy_snr}{{14}{12}{}{}{}} -\@writefile{lof}{\contentsline {figure}{\numberline {4}{\ignorespaces \textbf {Intensity invariance by logarithmic compression and adaptation is restricted by the noise floor.} Synthetic input $x_{\text {filt}}(t)$ consists of song component $s(t)$ scaled by $\alpha $ with (\textbf {c}{} and \textbf {d}) or without (\textbf {a}{} and \textbf {b}) additive noise component $\eta (t)$. Input $x_{\text {filt}}(t)$ is transformed into envelope $x_{\text {env}}(t)$, logarithmically compressed envelope $x_{\text {dB}}(t)$, and intensity-adapted envelope $x_{\text {adapt}}(t)$. \textbf {Left}:~$x_{\text {env}}(t)$, $x_{\text {dB}}(t)$, and $x_{\text {adapt}}(t)$ for different scales $\alpha $. \textbf {Right}:~Ratios of the standard deviation of $x_{\text {env}}(t)$, $x_{\text {dB}}(t)$, and $x_{\text {adapt}}(t)$ relative to the respective reference standard deviation $\sigma _{\eta }$ for input $x_{\text {filt}}(t)=\eta (t)$. \textbf {a}{} and \textbf {b}:~Ideally, if $x_{\text {filt}}(t)=\alpha \cdot s(t)$, then $x_{\text {adapt}}(t)$ is intensity-invariant across all $\alpha $. \textbf {c}{} and \textbf {d}:~In practice, if $x_{\text {filt}}(t)=\alpha \cdot s(t)+\eta (t)$, the intensity invariance of $x_{\text {adapt}}(t)$ is limited to sufficiently large $\alpha $. Shaded area indicates saturation of $x_{\text {adapt}}(t)$ at $95\,\%$ curve span. }}{12}{}\protected@file@percent } -\newlabel{fig:inv_log-hp}{{4}{12}{}{}{}} -\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Thresholding nonlinearity \& temporal averaging}{12}{}\protected@file@percent } -\@writefile{lof}{\contentsline {figure}{\numberline {5}{\ignorespaces \textbf {Intensity invariance by thresholding and temporal averaging depends on both the threshold value and the noise floor.} Synthetic input $x_{\text {adapt}}(t)$ consists of song component $s(t)$ scaled by $\alpha $ with additive noise component $\eta (t)$. Input $x_{\text {adapt}}(t)$ is transformed into kernel response $c_i(t)$, binary response $b_i(t)$, and feature $f_i(t)$. Threshold value $\Theta _i$ is set to multiples of the reference standard deviation $\sigma _{\eta }$ of $c_i(t)$ for input $x_{\text {adapt}}(t)=\eta (t)$. Darker colors correspond to higher $\Theta _i$. \textbf {Left}:~$x_{\text {adapt}}(t)$, $c_i(t)$, $b_i(t)$, and $f_i(t)$ for different scales $\alpha $ and threshold values $\Theta _i$. Left-most column is is the pure-noise reference. \textbf {Right}:~Average value of $f_i(t)$ during the song for the different $\Theta _i$. \textbf {a}:~Input $x_{\text {adapt}}(t)$. \textbf {b}-\textbf {d}:~$c_i(t)$, $b_i(t)$, and $f_i(t)$ for the different $\Theta _i$ based on the same $x_{\text {adapt}}(t)$ from \textbf {a}{}. \textbf {e}:~Average value of $f_i(t)$ during the song for the different $\Theta _i$ in \textbf {b}{}-\textbf {d}. }}{13}{}\protected@file@percent } -\newlabel{fig:inv_thresh-lp_single}{{5}{13}{}{}{}} -\@writefile{lof}{\contentsline {figure}{\numberline {6}{\ignorespaces \textbf {Feature representation of different species-specific songs saturates at different points in feature space.} }}{13}{}\protected@file@percent } -\newlabel{fig:inv_thresh-lp_species}{{6}{13}{}{}{}} -\@writefile{lof}{\contentsline {figure}{\numberline {7}{\ignorespaces \textbf {Step-wise emergence of intensity invariant song representation along the model pathway.} }}{14}{}\protected@file@percent } -\newlabel{fig:inv_thresh-lp_full}{{7}{14}{}{}{}} -\newlabel{eq:pdf_split}{{15}{14}{}{}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {4}{\ignorespaces \textbf {Intensity invariance by logarithmic compression and adaptation is restricted by the noise floor.} Synthetic input $x_{\text {filt}}(t)$ consists of song component $s(t)$ scaled by $\alpha $ with (\textbf {c}{} and \textbf {d}) or without (\textbf {a}{} and \textbf {b}) additive noise component $\eta (t)$. Input $x_{\text {filt}}(t)$ is transformed into envelope $x_{\text {env}}(t)$, logarithmically compressed envelope $x_{\text {dB}}(t)$, and intensity-adapted envelope $x_{\text {adapt}}(t)$. \textbf {Left}:~$x_{\text {env}}(t)$, $x_{\text {dB}}(t)$, and $x_{\text {adapt}}(t)$ for different scales $\alpha $. \textbf {Right}:~Ratios of the standard deviation of $x_{\text {env}}(t)$, $x_{\text {dB}}(t)$, and $x_{\text {adapt}}(t)$ relative to the respective reference standard deviation $\sigma _{\eta }$ for input $x_{\text {filt}}(t)=\eta (t)$. \textbf {a}{} and \textbf {b}:~Ideally, if $x_{\text {filt}}(t)=\alpha \cdot s(t)$, then $x_{\text {adapt}}(t)$ is intensity-invariant across all $\alpha $. \textbf {c}{} and \textbf {d}:~In practice, if $x_{\text {filt}}(t)=\alpha \cdot s(t)+\eta (t)$, the intensity invariance of $x_{\text {adapt}}(t)$ is limited to sufficiently large $\alpha $. Shaded area indicates saturation of $x_{\text {adapt}}(t)$ at $95\,\%$ curve span. }}{13}{}\protected@file@percent } +\newlabel{fig:inv_log-hp}{{4}{13}{}{}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Thresholding nonlinearity \& temporal averaging}{14}{}\protected@file@percent } +\@writefile{lof}{\contentsline {figure}{\numberline {5}{\ignorespaces \textbf {Intensity invariance by thresholding and temporal averaging depends on both the threshold value and the noise floor.} Synthetic input $x_{\text {adapt}}(t)$ consists of song component $s(t)$ scaled by $\alpha $ with additive noise component $\eta (t)$. Input $x_{\text {adapt}}(t)$ is transformed into kernel response $c_i(t)$, binary response $b_i(t)$, and feature $f_i(t)$. Threshold value $\Theta _i$ is set to multiples of the reference standard deviation $\sigma _{\eta }$ of $c_i(t)$ for input $x_{\text {adapt}}(t)=\eta (t)$. Darker colors correspond to higher $\Theta _i$. \textbf {Left}:~$x_{\text {adapt}}(t)$, $c_i(t)$, $b_i(t)$, and $f_i(t)$ for different scales $\alpha $ and threshold values $\Theta _i$. Left-most column is is the pure-noise reference. \textbf {Right}:~Average value of $f_i(t)$ during the song for the different $\Theta _i$. \textbf {a}:~Input $x_{\text {adapt}}(t)$. \textbf {b}-\textbf {d}:~$c_i(t)$, $b_i(t)$, and $f_i(t)$ for the different $\Theta _i$ based on the same $x_{\text {adapt}}(t)$ from \textbf {a}{}. \textbf {e}:~Average value of $f_i(t)$ during the song for the different $\Theta _i$ in \textbf {b}{}-\textbf {d}. }}{14}{}\protected@file@percent } +\newlabel{fig:inv_thresh-lp_single}{{5}{14}{}{}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {6}{\ignorespaces \textbf {Feature representation of different species-specific songs saturates at different points in feature space.} }}{15}{}\protected@file@percent } +\newlabel{fig:inv_thresh-lp_species}{{6}{15}{}{}{}} +\@writefile{lof}{\contentsline {figure}{\numberline {7}{\ignorespaces \textbf {Step-wise emergence of intensity invariant song representation along the model pathway.} }}{16}{}\protected@file@percent } +\newlabel{fig:inv_thresh-lp_full}{{7}{16}{}{}{}} +\newlabel{eq:pdf_split}{{15}{17}{}{}{}} +\newlabel{eq:feat_avg}{{16}{17}{}{}{}} +\newlabel{eq:feat_prop}{{17}{17}{}{}{}} \abx@aux@cite{0}{stumpner1991auditory} \abx@aux@segm{0}{0}{stumpner1991auditory} -\newlabel{eq:feat_avg}{{16}{15}{}{}{}} -\newlabel{eq:feat_prop}{{17}{15}{}{}{}} -\@writefile{toc}{\contentsline {section}{\numberline {4}Discriminating species-specific song\\patterns in feature space}{16}{}\protected@file@percent } -\@writefile{toc}{\contentsline {section}{\numberline {5}Conclusions \& outlook}{16}{}\protected@file@percent } -\abx@aux@page{73}{16} -\@writefile{lof}{\contentsline {figure}{\numberline {8}{\ignorespaces \textbf {} }}{17}{}\protected@file@percent } -\newlabel{}{{8}{17}{}{}{}} +\@writefile{toc}{\contentsline {section}{\numberline {4}Discriminating species-specific song\\patterns in feature space}{18}{}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {5}Conclusions \& outlook}{18}{}\protected@file@percent } +\abx@aux@page{73}{18} +\@writefile{lof}{\contentsline {figure}{\numberline {8}{\ignorespaces \textbf {} }}{20}{}\protected@file@percent } +\newlabel{}{{8}{20}{}{}{}} \gdef\svg@ink@ver@settings{{\m@ne }{inkscape}{\m@ne }} \abx@aux@read@bbl@mdfivesum{1380DC8C93D2855FDB132CC5A40AD52F} -\gdef \@abspage@last{17} +\gdef \@abspage@last{20} diff --git a/main.fdb_latexmk b/main.fdb_latexmk index 75dc139..088bf4c 100644 --- a/main.fdb_latexmk +++ b/main.fdb_latexmk @@ -1,14 +1,14 @@ # Fdb version 4 -["biber main"] 1774450439.36422 "main.bcf" "main.bbl" "main" 1774450447.31629 0 +["biber main"] 1774450439.36422 "main.bcf" "main.bbl" "main" 1774939289.27389 0 "cite.bib" 1770904753.08918 27483 4290db0c91f7b5055e25472ef913f6b4 "" - "main.bcf" 1774450447.19902 112931 2a478116d80ebb1ada7083a24facd6e3 "pdflatex" + "main.bcf" 1774939289.16212 112931 2a478116d80ebb1ada7083a24facd6e3 "pdflatex" (generated) "main.bbl" "main.blg" (rewritten before read) -["pdflatex"] 1774450445.69982 "/home/hartling/phd/paper/paper_2025/main.tex" "main.pdf" "main" 1774450447.31653 0 +["pdflatex"] 1774939287.87141 "/home/hartling/phd/paper/paper_2025/main.tex" "main.pdf" "main" 1774939289.27409 0 "/etc/texmf/web2c/texmf.cnf" 1761560044.43676 475 c0e671620eb5563b2130f56340a5fde8 "" - "/home/hartling/phd/paper/paper_2025/main.tex" 1774450445.38503 48022 de46789e7f0ca716a47759b334bfa10b "" + "/home/hartling/phd/paper/paper_2025/main.tex" 1774450464.03788 48022 de46789e7f0ca716a47759b334bfa10b "" "/usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map" 1577235249 3524 cb3e574dea2d1052e39280babc910dc8 "" "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm" 1246382020 1004 54797486969f23fa377b128694d548df "" "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm" 1246382020 988 bdf658c3bfc2d96d3c8b02cfc1c94c20 "" @@ -154,15 +154,15 @@ "figures/fig_auditory_pathway.pdf" 1771593904.14638 1153923 3df8539421fd21dc866cc8d320bd9b1d "" "figures/fig_feat_stages.pdf" 1774002994.98767 11091006 565fe951f1255c121429a060082398f5 "" "figures/fig_invariance_full.pdf" 1774271483.89842 149796490 a2dd04969d8a98c63c3653d20848c0b6 "" - "figures/fig_invariance_log_hp.pdf" 1774270814.43169 544402 7a2def404adc93a3e18fa8b309d452be "" + "figures/fig_invariance_log_hp.pdf" 1774862828.87982 837296 93dd5e5b25285f2b3d6c8e3c0d2fd5f1 "" "figures/fig_invariance_thresh_lp_single.pdf" 1774448531.93474 921028 cae18b62e262b42f630e219fcaa0ca09 "" - "figures/fig_invariance_thresh_lp_species.pdf" 1773741140.93995 47573 9dd8a2281ce467803dd2a7b74ff3373e "" + "figures/fig_invariance_thresh_lp_species.pdf" 1774881323.87307 397460 b03004b5f296b884384b46c1419a5edf "" "figures/fig_noise_env_sd_conversion.pdf" 1774256952.42051 45466 671a2b8fbf72b4eba6b970b4421f2521 "" "figures/fig_pre_stages.pdf" 1774002992.74268 449426 5762be15627fe5d8b6d108b7ea18db44 "" - "main.aux" 1774450447.19302 15278 2c66d13a9cb30f6854e7c7e85ce0fb43 "pdflatex" + "main.aux" 1774939289.15612 15278 8ddadc678eacbeaae96061ab2907f0b9 "pdflatex" "main.bbl" 1774450439.97007 91039 1380dc8c93d2855fdb132cc5a40ad52f "biber main" - "main.run.xml" 1774450447.20002 2335 a049bc26a7f032e842ce55de5bc38328 "pdflatex" - "main.tex" 1774450445.38503 48022 de46789e7f0ca716a47759b334bfa10b "" + "main.run.xml" 1774939289.16212 2335 a049bc26a7f032e842ce55de5bc38328 "pdflatex" + "main.tex" 1774450464.03788 48022 de46789e7f0ca716a47759b334bfa10b "" (generated) "main.aux" "main.bcf" diff --git a/main.log b/main.log index 9653d1d..dc9bab5 100644 --- a/main.log +++ b/main.log @@ -1,4 +1,4 @@ -This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023/Debian) (preloaded format=pdflatex 2025.10.28) 25 MAR 2026 15:54 +This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023/Debian) (preloaded format=pdflatex 2025.10.28) 31 MAR 2026 08:41 entering extended mode restricted \write18 enabled. file:line:error style messages enabled. @@ -764,37 +764,41 @@ File: figures/fig_feat_stages.pdf Graphic file (type pdf) Package pdftex.def Info: figures/fig_feat_stages.pdf used on input line 542. (pdftex.def) Requested size: 483.69687pt x 241.84782pt. [10 <./figures/fig_feat_stages.pdf>] [11] - + File: figures/fig_invariance_log_hp.pdf Graphic file (type pdf) Package pdftex.def Info: figures/fig_invariance_log_hp.pdf used on input line 632. -(pdftex.def) Requested size: 483.69687pt x 253.52437pt. - +(pdftex.def) Requested size: 483.69687pt x 498.70178pt. + [12] + +LaTeX Warning: Text page 13 contains only floats. + +[13 <./figures/fig_invariance_log_hp.pdf>] + File: figures/fig_invariance_thresh_lp_single.pdf Graphic file (type pdf) Package pdftex.def Info: figures/fig_invariance_thresh_lp_single.pdf used on input line 663. (pdftex.def) Requested size: 483.69687pt x 241.84782pt. - [12 <./figures/fig_invariance_log_hp.pdf>] - + File: figures/fig_invariance_thresh_lp_species.pdf Graphic file (type pdf) Package pdftex.def Info: figures/fig_invariance_thresh_lp_species.pdf used on input line 693. -(pdftex.def) Requested size: 483.69687pt x 241.84782pt. - [13 <./figures/fig_invariance_thresh_lp_single.pdf> <./figures/fig_invariance_thresh_lp_species.pdf>] - +(pdftex.def) Requested size: 483.69687pt x 483.69566pt. + [14 <./figures/fig_invariance_thresh_lp_single.pdf>] + File: figures/fig_invariance_full.pdf Graphic file (type pdf) Package pdftex.def Info: figures/fig_invariance_full.pdf used on input line 703. (pdftex.def) Requested size: 483.69687pt x 241.84782pt. - [14 + [15 <./figures/fig_invariance_thresh_lp_species.pdf>] [16 <./figures/fig_invariance_full.pdf>] [17 - <./figures/fig_invariance_full.pdf>] [15] [16] - +] [18] + File: figures/fig_noise_env_sd_conversion.pdf Graphic file (type pdf) Package pdftex.def Info: figures/fig_noise_env_sd_conversion.pdf used on input line 862. (pdftex.def) Requested size: 483.69687pt x 241.84782pt. - [17 <./figures/fig_noise_env_sd_conversion.pdf>] (./main.aux) + [19] [20 <./figures/fig_noise_env_sd_conversion.pdf>] (./main.aux) *********** LaTeX2e <2023-11-01> patch level 1 L3 programming layer <2024-01-22> @@ -812,10 +816,10 @@ Here is how much of TeX's memory you used: 1143 hyphenation exceptions out of 8191 94i,19n,93p,1496b,1732s stack positions out of 10000i,1000n,20000p,200000b,200000s -Output written on main.pdf (17 pages, 164189765 bytes). +Output written on main.pdf (20 pages, 164834440 bytes). PDF statistics: - 1521 PDF objects out of 1728 (max. 8388607) - 843 compressed objects within 9 object streams + 1638 PDF objects out of 1728 (max. 8388607) + 867 compressed objects within 9 object streams 0 named destinations out of 1000 (max. 500000) 53 words of extra memory for PDF output out of 10000 (max. 10000000) diff --git a/main.pdf b/main.pdf index 4ac98fc..d072734 100644 Binary files a/main.pdf and b/main.pdf differ diff --git a/main.synctex.gz b/main.synctex.gz index f026919..e22a075 100644 Binary files a/main.synctex.gz and b/main.synctex.gz differ diff --git a/python/fig_invariance_log-hp_backup.py b/python/fig_invariance_log-hp_backup.py index a89c5e4..acda691 100644 --- a/python/fig_invariance_log-hp_backup.py +++ b/python/fig_invariance_log-hp_backup.py @@ -4,10 +4,11 @@ import matplotlib.pyplot as plt from itertools import product from thunderhopper.filetools import search_files from thunderhopper.modeltools import load_data +from misc_functions import shorten_species, get_saturation from color_functions import load_colors -from plot_functions import hide_axis, ylimits, xlabel, ylabel, hide_ticks,\ +from plot_functions import hide_axis, ylimits, super_xlabel, ylabel, hide_ticks,\ plot_line, strip_zeros, time_bar, zoom_inset,\ - letter_subplot, title_subplot + letter_subplot, letter_subplots, title_subplot from IPython import embed def add_snip_axes(fig, grid_kwargs): @@ -30,7 +31,14 @@ def plot_snippets(axes, time, snippets, ymin=None, ymax=None, **kwargs): # GENERAL SETTINGS: target = 'Omocestus_rufipes' data_paths = search_files(target, excl='noise', dir='../data/inv/log_hp/') -species_paths = search_files('*', incl='noise', dir='../data/inv/log_hp/') +target_species = [ + 'Omocestus_rufipes', + 'Chorthippus_biguttulus', + 'Chorthippus_mollis', + 'Chrysochraon_dispar', + 'Gomphocerippus_rufus', + 'Pseudochorthippus_parallelus', + ] stages = ['env', 'log', 'inv'] load_kwargs = dict( files=stages, @@ -39,28 +47,29 @@ load_kwargs = dict( save_path = '../figures/fig_invariance_log_hp.pdf' compute_ratios = True show_diag = True -show_noise = True +show_plateaus = True # GRAPH SETTINGS: fig_kwargs = dict( figsize=(32/2.54, 32/2.54), ) -snip_rows = 1 -big_rows = 1 +# snip_rows = 1 +# big_rows = 1 super_grid_kwargs = dict( - nrows=2 * snip_rows + big_rows, + nrows=3, ncols=1, wspace=0, hspace=0, left=0, right=1, bottom=0, - top=1 + top=1, + height_ratios=[1, 1, 1] ) subfig_specs = dict( - pure=(slice(0, snip_rows), slice(None)), - noise=(slice(snip_rows, 2 * snip_rows), slice(None)), - big=(slice(-big_rows, None), slice(None)), + pure=(0, slice(None)), + noise=(1, slice(None)), + big=(2, slice(None)), ) block_height = 0.8 edge_padding = 0.08 @@ -112,6 +121,8 @@ fs = dict( bar=16, ) colors = load_colors('../data/stage_colors.npz') +species_colors = load_colors('../data/species_colors.npz') +noise_colors = [(0.5, 0.5, 0.5), (0.7, 0.7, 0.7)] lw_snippets = 1 lw_big = 3 xlabels = dict( @@ -206,15 +217,34 @@ bar_kwargs = dict( va='center', ) ) +leg_kwargs = dict( + ncols=2, + loc='upper right', + bbox_to_anchor=(0, 0.6, 1, 0.4), + frameon=False, + prop=dict( + size=12, + style='italic', + ), + borderpad=0, + borderaxespad=0, + handlelength=1, + columnspacing=1, +) diag_kwargs = dict( c=(0.75, 0.75, 0.75), lw=2, ls='--', zorder=1.9, ) -noise_rel_thresh = 0.95 -noise_kwargs = dict( - fc=(0.9, 0.9, 0.9), +plateau_settings = dict( + low=0.05, + high=0.95, + first=True, + last=True, + condense=None, +) +plateau_kwargs = dict( ec='none', lw=0, zorder=1.5, @@ -225,13 +255,13 @@ if compute_ratios: ref_data = load_data('../data/processed/white_noise_sd-1.npz', files=stages)[0] ref_measures = {k: v.std() for k, v in ref_data.items() if not k.endswith('rate')} -species_measures = [] -for species_path in species_paths: - species_measure = load_data(species_path, **load_kwargs)[0]['measure_inv'] +species_measures = {} +for species in target_species: + path = search_files(species, incl='noise', dir='../data/inv/log_hp/')[0] + measure = load_data(path, **load_kwargs)[0]['measure_inv'] if compute_ratios: - species_measure /= ref_measures['inv'] - species_measures.append(species_measure) -species_measures = np.array(species_measures).T + measure /= ref_measures['inv'] + species_measures[species] = measure # EXECUTION: for data_path in data_paths: @@ -291,14 +321,10 @@ for data_path in data_paths: ax.set_xscale('symlog', linthresh=scales[1], linscale=0.5) ax.set_yscale('symlog', linthresh=scales[1], linscale=0.5) ax.set_aspect(**anchor_kwargs) - ylabel(ax, ylabels['big'], transform=big_subfig.transSubfigure, **ylab_big_kwargs) - if i == 0: - hide_ticks(ax, 'bottom') - letter_subplot(ax, 'c', **letter_big_kwargs) - else: - xlabel(ax, xlabels['big'], transform=big_subfig.transSubfigure, **xlab_big_kwargs) - letter_subplot(ax, 'd', **letter_big_kwargs) big_axes[i] = ax + ylabel(big_axes[0], ylabels['big'], transform=big_subfig.transSubfigure, **ylab_big_kwargs) + super_xlabel(xlabels['big'], big_subfig, big_axes[0], big_axes[-1], **xlab_big_kwargs) + letter_subplots(big_axes, 'cde', **letter_big_kwargs) # Plot pure-song envelope snippets: handle = plot_snippets(pure_axes[0, :], t_full, pure_data['snip_env'], @@ -352,25 +378,26 @@ for data_path in data_paths: big_axes[1].plot(noise_scales, noise_data['measure_log'], c=colors['log'], lw=lw_big) big_axes[1].plot(noise_scales, noise_data['measure_inv'], c=colors['inv'], lw=lw_big) - # Plot species measures: - big_axes[2].plot(noise_scales, species_measures, 'k', lw=lw_big) if show_diag: # Indicate diagonal: big_axes[0].plot(pure_scales, pure_scales, **diag_kwargs) big_axes[1].plot(noise_scales, noise_scales, **diag_kwargs) - if show_noise: - # Indicate noise floor: - if compute_ratios: - span_measure = noise_data['measure_inv'][-1] - ref_measures['inv'] - thresh_measure = ref_measures['inv'] + noise_rel_thresh * span_measure - else: - span_measure = noise_data['measure_inv'][-1] - noise_data['measure_inv'][0] - thresh_measure = noise_data['measure_inv'][0] + noise_rel_thresh * span_measure - thresh_ind = np.nonzero(noise_data['measure_inv'] < thresh_measure)[0][-1] - thresh_scale = noise_scales[thresh_ind] - big_axes[1].axvspan(noise_scales[0], thresh_scale, **noise_kwargs) + if show_plateaus: + # Indicate low and high plateaus of noise invariance curve: + low_ind, high_ind = get_saturation(noise_data['measure_inv'], **plateau_settings) + big_axes[1].axvspan(noise_scales[0], noise_scales[low_ind], + fc=noise_colors[0], **plateau_kwargs) + big_axes[1].axvspan(noise_scales[low_ind], noise_scales[high_ind], + fc=noise_colors[1], **plateau_kwargs) + + # Plot species-specific noise-song measures: + for species, measure in species_measures.items(): + label = shorten_species(species) + big_axes[2].plot(noise_scales, measure, label=label, + c=species_colors[species], lw=lw_big) + big_axes[2].legend(**leg_kwargs) if save_path is not None: fig.savefig(save_path, bbox_inches='tight') diff --git a/python/fig_invariance_thresh-lp_species.py b/python/fig_invariance_thresh-lp_species.py index e9901c7..f9a5ed9 100644 --- a/python/fig_invariance_thresh-lp_species.py +++ b/python/fig_invariance_thresh-lp_species.py @@ -7,11 +7,11 @@ 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 shorten_species, get_saturation from color_functions import load_colors, shade_colors, create_listed_cmap -from plot_functions import hide_axis, title_subplot, ylimits, xlabel, ylabel, super_ylabel,\ - plot_line, plot_barcode, strip_zeros, time_bar,\ - letter_subplot, letter_subplots, hide_ticks,\ - super_xlabel, super_ylabel, assign_colors +from plot_functions import hide_axis, title_subplot, ylimits, xlabel, ylabel,\ + plot_line, time_bar,letter_subplot, letter_subplots,\ + hide_ticks, super_xlabel, reorder_by_norm from IPython import embed def force_sequence(*vars, skip_None=False, equal_size=False): @@ -126,10 +126,6 @@ def split_subplot(ax, side='right', size=10, pad=10): inputs = zip(*force_sequence(side, size, pad, equal_size=True)) return [div.append_axes(s, f'{n}%', f'{p}%') for s, n, p in inputs] -def shorten_species(name): - genus, species = name.split('_') - return genus[0] + '. ' + species - def add_cross_axes(fig, n, long='col', fill='row', **grid_kwargs): n_axes = n * (n - 1) // 2 nrows = grid_kwargs.get('nrows', None) @@ -179,7 +175,7 @@ load_kwargs = dict( ) save_path = '../figures/fig_invariance_thresh_lp_species.pdf' exclude_zero = True -show_noise = True +show_floor = True # SUBSET SETTINGS: thresh_rel = np.array([0.5, 1, 3])[0] @@ -214,7 +210,7 @@ subfig_specs = dict( feat_grid_kwargs = dict( nrows=2, ncols=n_species, - wspace=0.25, + wspace=0.35, hspace=0.1, left=0.06, right=0.985, @@ -234,17 +230,16 @@ song_grid_kwargs = dict( space_grid_kwargs = dict( nrows=None, ncols=None, - wspace=0.1, - hspace=0.3, - left=0.05, - right=1, - bottom=0.1, + wspace=0, + hspace=0.4, + left=0.15, + right=0.9, + bottom=0.13, top=0.95 ) anchor_kwargs = dict( aspect='equal', adjustable='box', - anchor=(0.5, 0.5) ) inset_kwargs = dict( y0=0.7, @@ -264,10 +259,12 @@ fs = dict( species_colors = load_colors('../data/species_colors.npz') kernel_shades = [0, 0.75] scale_shades = [1, 0] +noise_colors = [(0.5, 0.5, 0.5), (0.7, 0.7, 0.7)] lw = dict( song=0.5, feat=3, - kern=3 + kern=2.5, + plateau=3, ) zorder = dict( Omocestus_rufipes=2, @@ -285,7 +282,7 @@ xlabels = dict( space=[f'$\\mu_{{f_{i}}}$' for i in range(1, n_kernels + 1)], ) ylabels = dict( - feat='$\\mu_f$', + feat='$\\mu_{f_i}$', space=[f'$\\mu_{{f_{i}}}$' for i in range(1, n_kernels + 1)], bar='scale $\\alpha$', ) @@ -296,10 +293,10 @@ xlab_feat_kwargs = dict( va='bottom', ) xlab_space_kwargs = dict( - y=-0.3, + y=-0.2, fontsize=fs['lab_tex'], ha='center', - va='bottom', + va='top', ) ylab_feat_kwargs = dict( x=0, @@ -308,13 +305,14 @@ ylab_feat_kwargs = dict( va='top', ) ylab_space_kwargs = dict( - x=-0.2, + x=-0.3, + rotation=0, fontsize=fs['lab_tex'], - ha='center', - va='bottom', + ha='right', + va='center', ) ylab_cbar_kwargs = dict( - x=1, + x=-2, fontsize=fs['lab_norm'], ha='center', va='bottom', @@ -368,30 +366,43 @@ song_bar_kwargs = dict( color='k', lw=0, clip_on=False, - # text_pos=(-0.1, 0.5), - # text_str=f'${int(1000 * song_bar_time)}\\,\\text{{ms}}$', - # text_kwargs=dict( - # fontsize=fs['bar'], - # ha='right', - # va='center', - # ) + text_pos=(1.25, 0.5), + text_str=f'${int(song_bar_time)}\\,\\text{{s}}$', + text_kwargs=dict( + fontsize=fs['bar'], + ha='left', + va='center', + ) ) kern_bar_time = 0.05 kern_bar_kwargs = dict( dur=kern_bar_time, - y0=inset_kwargs['y0'] - 0.03, - y1=inset_kwargs['y0'], + y0=0.1, + y1=0.2, color='k', - lw=0 + lw=0, + clip_on=False, + text_pos=(0.6, -1), + text_str=f'${int(kern_bar_time * 1000)}\\,\\text{{ms}}$', + text_kwargs=dict( + fontsize=fs['bar'], + ha='center', + va='top', + ) ) -noise_kwargs = dict( +floor_kwargs = dict( fc=(0.9, 0.9, 0.9), ec='none', lw=0, zorder=0.5, ) -low_rel_thresh = 0.05 -high_rel_thresh = 0.95 +plateau_settings = dict( + low=0.05, + high=0.95, + first=True, + last=True, + condense='norm', +) # EXECUTION: @@ -450,14 +461,17 @@ letter_subplot(noise_subfig, 'e', ref=noise_axes[0], **letter_space_kwargs) # Format feature space axes: for ind, axes in enumerate(zip(pure_axes, noise_axes)): + irow, icol = row_inds[ind], col_inds[ind] for ax in axes: ax.set_xlim(0, 1) ax.set_ylim(0, 1) ax.xaxis.set_major_locator(plt.MultipleLocator(xloc['space'])) ax.yaxis.set_major_locator(plt.MultipleLocator(yloc['space'])) - ax.set_aspect(**anchor_kwargs) - xlabel(ax, xlabels['space'][col_inds[ind]], **xlab_space_kwargs) - ylabel(ax, ylabels['space'][row_inds[ind]], **ylab_space_kwargs) + anchor = space_pos[ind] / space_pos.max(axis=0) + anchor[0] = 1 - anchor[0] + ax.set_aspect(anchor=tuple(anchor[::-1]), **anchor_kwargs) + xlabel(ax, xlabels['space'][icol], **xlab_space_kwargs) + ylabel(ax, ylabels['space'][irow], **ylab_space_kwargs) # Determine area to place colorbars: rightmost = pure_axes[np.argmax(space_pos[:, 1])].get_position() @@ -479,7 +493,11 @@ kern_factors = np.linspace(*kernel_shades, n_kernels) kern_colors_bw = shade_colors((0., 0., 0.), kern_factors) # Plot results per species: -noise_feat = np.zeros((n_species, n_kernels), dtype=float) +min_noise_feat = np.zeros((n_species, n_kernels), dtype=float) +max_pure_feat = np.zeros((n_species, n_kernels), dtype=float) +max_noise_feat = np.zeros((n_species, n_kernels), dtype=float) +pure_space_handles = {ax: [] for ax in pure_axes} +noise_space_handles = {ax: [] for ax in noise_axes} for i, species in enumerate(target_species): print(f'Processing {species}') @@ -496,6 +514,7 @@ for i, species in enumerate(target_species): plot_line(song_ax, time, song, ypad=0.05, c='k', lw=lw['song']) title_subplot(song_ax, shorten_species(species), ref=song_subfig, **title_kwargs) time_bar(song_ax, **song_bar_kwargs) + song_bar_kwargs['text_pos'] = None # Fetch species-specific invariance files: pure_path = search_files(species, incl='pure', dir='../data/inv/thresh_lp/')[0] @@ -545,7 +564,8 @@ for i, species in enumerate(target_species): inset.plot(config['k_times'], kern, c=c, lw=lw['kern']) inset.set_xlim(xlims) inset.set_ylim(ylims) - time_bar(insets[0], parent=feat_axes[0, 0], **kern_bar_kwargs) + # time_bar(insets[0], parent=feat_axes[0, 0], **kern_bar_kwargs) + time_bar(insets[0], **kern_bar_kwargs) # Plot invariance curves in feature space: norm = LogNorm(vmin=scales[scales > 0][0], vmax=scales[-1]) @@ -554,60 +574,56 @@ for i, species in enumerate(target_species): pure_handle = pure_ax.scatter(pure_measure[:, icol], pure_measure[:, irow], c=scales, cmap=scale_cmap, norm=norm, zorder=zorder[species], **space_kwargs) + pure_space_handles[pure_ax].append(pure_handle) noise_handle = noise_ax.scatter(noise_measure[:, icol], noise_measure[:, irow], c=scales, cmap=scale_cmap, norm=norm, zorder=zorder[species], **space_kwargs) + noise_space_handles[noise_ax].append(noise_handle) # Indicate scale color code in pure subfigure: pure_subfig.colorbar(pure_handle, cax=pure_bars[i]) pure_bars[i].set_yscale('symlog', **symlog_kwargs) - if i < n_species - 1: - hide_ticks(pure_bars[i], 'right', ticks=False) - else: - ylabel(pure_bars[i], ylabels['bar'], transform=pure_subfig.transSubfigure, **ylab_cbar_kwargs) + hide_ticks(pure_bars[i], 'right', ticks=False) + if i == 0: + pure_bars[0].tick_params(axis='y', which='both', left=True, labelleft=True) + ylabel(pure_bars[0], ylabels['bar'], **ylab_cbar_kwargs) # Indicate scale color code in noise subfigure: noise_subfig.colorbar(noise_handle, cax=noise_bars[i]) noise_bars[i].set_yscale('symlog', **symlog_kwargs) - if i < n_species - 1: - hide_ticks(noise_bars[i], 'right', ticks=False) - else: - ylabel(noise_bars[i], ylabels['bar'], transform=noise_subfig.transSubfigure, **ylab_cbar_kwargs) + hide_ticks(noise_bars[i], 'right', ticks=False) + if i == 0: + noise_bars[0].tick_params(axis='y', which='both', left=True, labelleft=True) + ylabel(noise_bars[0], ylabels['bar'], **ylab_cbar_kwargs) - # Log feature noise floor: - noise_feat[i, :] = noise_measure.min(axis=0) + # Indicate plateaus of pure invariance curves: + low_ind, high_ind = get_saturation(pure_measure, **plateau_settings) + pure_bars[i].axhline(scales[low_ind], c=noise_colors[0], lw=lw['plateau']) + pure_bars[i].axhline(scales[high_ind], c=noise_colors[1], lw=lw['plateau']) - # Indicate low and high plateaus: - min_feat = pure_measure.min(axis=0) - span_feat = pure_measure.max(axis=0) - min_feat + # Indicate plateaus of noise invariance curves: + low_ind, high_ind = get_saturation(noise_measure, **plateau_settings) + noise_bars[i].axhline(scales[low_ind], c=noise_colors[0], lw=lw['plateau']) + noise_bars[i].axhline(scales[high_ind], c=noise_colors[1], lw=lw['plateau']) + + # Log start and end of invariance curve: + min_noise_feat[i, :] = noise_measure.min(axis=0) + max_pure_feat[i, :] = pure_measure.max(axis=0) + max_noise_feat[i, :] = noise_measure.max(axis=0) - low_thresh = min_feat + low_rel_thresh * span_feat - low_ind = np.nonzero((pure_measure >= low_thresh).all(axis=1))[0][0] - pure_bars[i].axhline(scales[low_ind], c='k', lw=3) +# Sort feature space traces by distance of endpoint to origin: +for ind, (pure_ax, noise_ax) in enumerate(zip(pure_axes, noise_axes)): + irow, icol = row_inds[ind], col_inds[ind] + reorder_by_norm(pure_space_handles[pure_ax], max_pure_feat[:, [icol, irow]]) + reorder_by_norm(noise_space_handles[noise_ax], max_noise_feat[:, [icol, irow]]) - high_thresh = min_feat + high_rel_thresh * span_feat - high_ind = np.nonzero((pure_measure >= high_thresh).any(axis=1))[0][0] - pure_bars[i].axhline(scales[high_ind], c='w', lw=3) - - # Indicate low and high plateaus: - min_feat = noise_measure.min(axis=0) - span_feat = noise_measure.max(axis=0) - min_feat - - low_thresh = min_feat + low_rel_thresh * span_feat - low_ind = np.nonzero((noise_measure >= low_thresh).all(axis=1))[0][0] - noise_bars[i].axhline(scales[low_ind], c='k', lw=3) - - high_thresh = min_feat + high_rel_thresh * span_feat - high_ind = np.nonzero((noise_measure >= high_thresh).any(axis=1))[0][0] - noise_bars[i].axhline(scales[high_ind], c='w', lw=3) - -if show_noise: +if show_floor: # Indicate feature noise floor: - noise_feat = noise_feat.mean(axis=0) + noise_feat = min_noise_feat.mean(axis=0) for ind, ax in enumerate(noise_axes): irow, icol = row_inds[ind], col_inds[ind] - ax.add_patch(plt.Rectangle((0, 0), noise_feat[icol], noise_feat[irow], **noise_kwargs)) + ax.add_patch(plt.Rectangle((0, 0), noise_feat[icol], noise_feat[irow], **floor_kwargs)) if save_path is not None: fig.savefig(save_path) diff --git a/python/fig_pathway_stages.py b/python/fig_pathway_stages.py index 1940652..d32fc51 100644 --- a/python/fig_pathway_stages.py +++ b/python/fig_pathway_stages.py @@ -6,7 +6,7 @@ from thunderhopper.modeltools import load_data from color_functions import load_colors from plot_functions import hide_axis, letter_subplots,\ ylabel, super_xlabel, plot_line, plot_barcode,\ - indicate_zoom, assign_colors, reorder_traces + indicate_zoom, assign_colors, reorder_by_sd from IPython import embed # GENERAL SETTINGS: @@ -215,10 +215,10 @@ for data_path in data_paths: signal = data['conv'][:, kern_inds] handles = plot_line(ax_full, t_full, signal, lw=lw_full['conv'], yloc=yloc_full['conv']) assign_colors(handles, kern_specs[:, 0], conv_colors) - reorder_traces(handles, signal) + reorder_by_sd(handles, signal) handles = plot_line(ax_zoom, t_zoom, signal[zoom_mask, :], lw=lw_zoom['conv'], yloc=yloc_zoom['conv']) assign_colors(handles, kern_specs[:, 0], conv_colors) - reorder_traces(handles, signal[zoom_mask, :]) + reorder_by_sd(handles, signal[zoom_mask, :]) hide_axis(ax_full, 'bottom') hide_axis(ax_zoom, 'bottom') diff --git a/python/misc_functions.py b/python/misc_functions.py new file mode 100644 index 0000000..efd0ed1 --- /dev/null +++ b/python/misc_functions.py @@ -0,0 +1,32 @@ +import numpy as np + +def shorten_species(name): + genus, species = name.split('_') + return genus[0] + '. ' + species + +def get_saturation(sigmoid, low=0.05, high=0.95, first=True, last=True, + condense=None): + if condense == 'norm' and sigmoid.ndim == 2: + sigmoid = np.linalg.norm(sigmoid, axis=1) + + min_value = sigmoid[0] if first else sigmoid.min(axis=0) + max_value = sigmoid[-1] if last else sigmoid.max(axis=0) + + span = max_value - min_value + low_value = min_value + low * span + high_value = min_value + high * span + + low_mask = sigmoid >= low_value + high_mask = sigmoid >= high_value + if sigmoid.ndim == 1: + low_ind = np.nonzero(low_mask)[0][0] + high_ind = np.nonzero(high_mask)[0][0] + elif condense == 'all': + low_ind = np.nonzero(low_mask.all(axis=1))[0][0] + high_ind = np.nonzero(high_mask.all(axis=1))[0][0] + else: + low_ind, high_ind = [], [] + for i in range(sigmoid.shape[1]): + low_ind.append(np.nonzero(low_mask[:, i])[0][0]) + high_ind.append(np.nonzero(high_mask[:, i])[0][0]) + return low_ind, high_ind \ No newline at end of file diff --git a/python/plot_functions.py b/python/plot_functions.py index dff93a8..b72bfed 100644 --- a/python/plot_functions.py +++ b/python/plot_functions.py @@ -202,8 +202,15 @@ def assign_colors(handles, types, colors): handle.set_color(colors[str(int(type_id))]) return None -def reorder_traces(handles, signal, zlow=2, zhigh=2.5): - inds = np.argsort(signal.std(axis=0)) +def reorder_by_sd(handles, data, zlow=2, zhigh=2.5): + inds = np.argsort(data.std(axis=0)) + zorders = np.linspace(zlow, zhigh, len(inds))[::-1] + for ind, z in zip(inds, zorders): + handles[ind].set_zorder(z) + return None + +def reorder_by_norm(handles, data, zlow=2, zhigh=2.5): + inds = np.argsort(np.linalg.norm(data, axis=1)) zorders = np.linspace(zlow, zhigh, len(inds))[::-1] for ind, z in zip(inds, zorders): handles[ind].set_zorder(z)