-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ENH] - Plotting updates #343
Changes from 11 commits
c8c8758
b1190f5
417f4dd
8965c75
63048b6
905b1df
6fa2811
4976524
da60bcf
a6f3234
17ab4e8
778bd30
5fc3944
4d3f956
f688202
af23327
fee9757
5470350
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
"""Plotting functions for neurodsp.aperiodic.""" | ||
|
||
from neurodsp.plts.style import style_plot | ||
from neurodsp.plts.utils import check_ax, savefig, prepare_multi_plot | ||
|
||
#################################################################################################### | ||
#################################################################################################### | ||
|
||
@savefig | ||
@style_plot | ||
def plot_autocorr(timepoints, autocorrs, labels=None, colors=None, ax=None, **kwargs): | ||
"""Plot autocorrelation results. | ||
|
||
Parameters | ||
---------- | ||
timepoints : 1d array | ||
Time points, in samples, at which autocorrelations are computed. | ||
autocorrs : array | ||
Autocorrelation values, across time lags. | ||
labels : str or list of str, optional | ||
Labels for each time series. | ||
colors : str or list of str | ||
Colors to use to plot lines. | ||
ax : matplotlib.Axes, optional | ||
Figure axes upon which to plot. | ||
**kwargs | ||
Keyword arguments for customizing the plot. | ||
""" | ||
|
||
ax = check_ax(ax, figsize=kwargs.pop('figsize', (6, 5))) | ||
|
||
for time, ac, label, color in zip(*prepare_multi_plot(timepoints, autocorrs, labels, colors)): | ||
ax.plot(time, ac, label=label, color=color) | ||
|
||
ax.set(xlabel='Lag (Samples)', ylabel='Autocorrelation') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,10 @@ | ||
"""Plotting functions for neurodsp.spectral.""" | ||
|
||
from itertools import repeat, cycle | ||
|
||
import numpy as np | ||
import matplotlib.pyplot as plt | ||
|
||
from neurodsp.plts.style import style_plot | ||
from neurodsp.plts.utils import check_ax, savefig | ||
from neurodsp.plts.utils import check_ax, savefig, prepare_multi_plot | ||
|
||
################################################################################################### | ||
################################################################################################### | ||
|
@@ -47,18 +45,8 @@ def plot_power_spectra(freqs, powers, labels=None, colors=None, ax=None, **kwarg | |
|
||
ax = check_ax(ax, figsize=kwargs.pop('figsize', (6, 6))) | ||
|
||
freqs = repeat(freqs) if isinstance(freqs, np.ndarray) and freqs.ndim == 1 else freqs | ||
powers = [powers] if isinstance(powers, np.ndarray) and powers.ndim == 1 else powers | ||
|
||
if labels is not None: | ||
labels = [labels] if not isinstance(labels, list) else labels | ||
else: | ||
labels = repeat(labels) | ||
|
||
colors = repeat(colors) if not isinstance(colors, list) else cycle(colors) | ||
|
||
for freq, power, color, label in zip(freqs, powers, colors, labels): | ||
ax.loglog(freq, power, color=color, label=label) | ||
for freq, power, label, color in zip(*prepare_multi_plot(freqs, powers, labels, colors)): | ||
ax.loglog(freq, power, label=label, color=color) | ||
|
||
ax.set_xlabel('Frequency (Hz)') | ||
ax.set_ylabel('Power ($V^2/Hz$)') | ||
|
@@ -235,3 +223,68 @@ def plot_spectral_hist(freqs, power_bins, spectral_hist, spectrum_freqs=None, | |
if spectrum is not None: | ||
plt_inds = np.logical_and(spectrum_freqs >= freqs[0], spectrum_freqs <= freqs[-1]) | ||
ax.plot(spectrum_freqs[plt_inds], np.log10(spectrum[plt_inds]), color='w', alpha=0.8) | ||
|
||
|
||
@savefig | ||
@style_plot | ||
def plot_spectra_3D(freqs, powers, log_freqs=False, log_powers=True, | ||
TomDonoghue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
colors=None, orientation=(20, -50), **kwargs): | ||
"""Plot a series of power spectra in a 3D plot. | ||
|
||
Parameters | ||
---------- | ||
freqs : 1d or 2d array or list of 1d array | ||
Frequency vector. | ||
powers : 2d array or list of 1d array | ||
Power values. | ||
log_freqs : bool, optional, default: False | ||
Whether to plot the frequency values in log10 space. | ||
log_powers : bool, optional, default: True | ||
Whether to plot the power values in log10 space. | ||
colors : str or list of str | ||
Colors to use to plot lines. | ||
orientation : tuple of int | ||
Orientation to set the 3D plot. | ||
**kwargs | ||
Keyword arguments for customizing the plot. | ||
|
||
Examples | ||
-------- | ||
Plot power spectra in 3D: | ||
|
||
>>> from neurodsp.sim import sim_combined | ||
>>> from neurodsp.spectral import compute_spectrum | ||
>>> sig1 = sim_combined(n_seconds=10, fs=500, | ||
... components={'sim_powerlaw': {'exponent' : -1}, | ||
... 'sim_bursty_oscillation' : {'freq': 10}}) | ||
>>> sig2 = sim_combined(n_seconds=10, fs=500, | ||
... components={'sim_powerlaw': {'exponent' : -1.5}, | ||
... 'sim_bursty_oscillation' : {'freq': 10}}) | ||
>>> freqs1, powers1 = compute_spectrum(sig1, fs=500) | ||
>>> freqs2, powers2 = compute_spectrum(sig2, fs=500) | ||
>>> plot_spectra_3D([freqs1, freqs2], [powers1, powers2]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed the z-axis label is cutoff when I copy/paste the example into a notebook. I tried plt.tight_layout() but it gave an error about the size of the fig not having large enough margins. Maybe a solution would be to add an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm, this seems to be an issue with how jupyter notebooks do inline plotting: When saving out, the figure looks fine, so I don't think we are messing up the plot per se. I'm not sure if there's an action item here - I don't think we want to overtune too much to address a quirk in notebooks. More broadly, I do think it makes sense to add more access to ax / fig - so I've added a Also, it seems with the current notebook quirk, changing the zoom can fix the render (https://stackoverflow.com/questions/77577613/matplotlib-3d-plot-z-label-cut-off), so, for example the following make the whole plot visible: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added an argument to |
||
""" | ||
|
||
fig = plt.figure() | ||
ax = fig.add_subplot(projection='3d') | ||
|
||
n_spectra = len(powers) | ||
|
||
for ind, (freq, power, _, color) in \ | ||
enumerate(zip(*prepare_multi_plot(freqs, powers, None, colors))): | ||
ax.plot(xs=np.log10(freq) if log_freqs else freq, | ||
ys=[ind] * len(freq), | ||
zs=np.log10(power) if log_powers else power, | ||
color=color, | ||
**kwargs) | ||
|
||
ax.set( | ||
xlabel='Frequency (Hz)', | ||
ylabel='Channels', | ||
zlabel='Power', | ||
ylim=[0, n_spectra - 1], | ||
) | ||
|
||
yticks = list(range(n_spectra)) | ||
ax.set_yticks(yticks, yticks) | ||
ax.view_init(*orientation) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
"""Tests for neurodsp.plts.aperiodic.""" | ||
|
||
from neurodsp.aperiodic.autocorr import compute_autocorr | ||
|
||
from neurodsp.tests.settings import TEST_PLOTS_PATH, FS | ||
from neurodsp.tests.tutils import plot_test | ||
|
||
from neurodsp.plts.aperiodic import * | ||
|
||
################################################################################################### | ||
################################################################################################### | ||
|
||
def tests_plot_autocorr(tsig, tsig_comb): | ||
|
||
times1, acs1 = compute_autocorr(tsig, max_lag=150) | ||
times2, acs2 = compute_autocorr(tsig_comb, max_lag=150) | ||
|
||
plot_autocorr(times1, acs1, | ||
save_fig=True, file_path=TEST_PLOTS_PATH, | ||
file_name='test_plot_autocorr-1.png') | ||
|
||
plot_autocorr([times1, times2], [acs1, acs2], | ||
labels=['first', 'second'], colors=['k', 'r'], | ||
save_fig=True, file_path=TEST_PLOTS_PATH, | ||
file_name='test_plot_autocorr-2.png') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only thought here is whether to include it in aperiodic.py or somewhere else, since acf isn't restricted to aperiodic signals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeh.... agreed. I wasn't sure where to put it, but since we already have
neurodsp/aperiodic/autocorr
as the home of the function to compute autocorrelation, this seemed like the most consistent spot for the plot function. I don't know what a better name / place for these things is - so unless we want to re-org, move both I think this works best for now?