# Generate plots of LFP (local field potentials) and related analyses
import math
from ..analysis.utils import exception # , loadData
from ..analysis.tools import loadData
from .plotter import LinesPlotter
from .plotter import ImagePlotter
from .plotter import MetaFigure
from .plotter import MultiPlotter
import numpy as np
[docs]
@exception
def plotLFPPSD(
PSDData=None,
axis=None,
timeRange=None,
electrodes=['avg', 'all'],
pop=None,
separation=1.0,
roundOffset=True,
NFFT=256,
noverlap=128,
nperseg=256,
minFreq=1,
maxFreq=100,
stepFreq=1,
smooth=0,
logy=False,
normSignal=False,
normPSD=False,
filtFreq=False,
filtOrder=3,
detrend=False,
transformMethod='morlet',
orderInverse=True,
legend=True,
colorList=None,
returnPlotter=False,
**kwargs
):
"""Function to produce a line plot of LFP electrode signals
NetPyNE Options
---------------
sim : NetPyNE sim object
The *sim object* from which to get data.
*Default:* ``None`` uses the current NetPyNE sim object
Parameters
----------
PSDData : dict, str
The data necessary to plot the LFP PSD.
*Default:* ``None`` uses ``analysis.preparePSD`` to produce ``PSDData`` using the current NetPyNE sim object.
If a *str* it must represent a file path to previously saved data.
axis : matplotlib axis
The axis to plot into, allowing overlaying of plots.
*Default:* ``None`` produces a new figure and axis.
timeRange : list
Time range to include in the raster: ``[min, max]``.
*Default:* ``None`` uses the entire simulation
electrodes : list
A *list* of the electrodes to plot from.
*Default:* ``['avg', 'all']`` plots each electrode as well as their average
pop : str
A population name to calculate PSD from.
*Default:* ``None`` uses all populations.
separation : float
Use to increase or decrease distance between signals on the plot.
*Default:* ``1.0``
roundOffset : bool
Attempts to line up PSD signals with gridlines
*Default:* ``True``
NFFT : int (power of 2)
Number of data points used in each block for the PSD and time-freq FFT.
**Default:** ``256``
noverlap : int (<nperseg)
Number of points of overlap between segments for PSD and time-freq.
**Default:** ``128``
nperseg : int
Length of each segment for time-freq.
**Default:** ``256``
minFreq : float
Minimum frequency shown in plot for PSD and time-freq.
**Default:** ``1``
maxFreq : float
Maximum frequency shown in plot for PSD and time-freq.
**Default:** ``100``
stepFreq : float
Step frequency.
**Default:** ``1``
smooth : int
Window size for smoothing LFP; no smoothing if ``0``
**Default:** ``0``
logy : bool
Whether to use a log axis.
*Default:* ``False``
normSignal : bool
Whether to normalize the LFP data.
*Default:* ``False``
normPSD : bool
Whether to normalize the PSD data.
*Default:* ``False``
filtFreq : int or list
Frequency for low-pass filter (int) or frequencies for bandpass filter in a list: [low, high]
*Default:* ``None`` does not filter the data
filtOrder : int
Order of the filter defined by `filtFreq`.
*Default:* ``3``
detrend : bool
Whether to detrend the data.
*Default:* ``False``
transformMethod : str
The transformation method to use, either 'morlet' or 'fft'.
*Default:* ``'morlet'``
orderInverse : bool
Whether to invert the order of plotting.
*Default:* ``True``
legend : bool
Whether or not to add a legend to the plot.
*Default:* ``True`` adds a legend.
colorList : list
A *list* of colors to draw from when plotting.
*Default:* ``None`` uses the default NetPyNE colorList.
returnPlotter : bool
Whether to return the figure or the NetPyNE MetaFig object.
*Default:* ``False`` returns the figure.
Plot Options
------------
showFig : bool
Whether to show the figure.
*Default:* ``False``
saveFig : bool
Whether to save the figure.
*Default:* ``False``
overwrite : bool
whether to overwrite existing figure files.
*Default:* ``True`` overwrites the figure file
*Options:* ``False`` adds a number to the file name to prevent overwriting
legendKwargs : dict
a *dict* containing any or all legend kwargs. These include ``'title'``, ``'loc'``, ``'fontsize'``, ``'bbox_to_anchor'``, ``'borderaxespad'``, and ``'handlelength'``.
rcParams : dict
a *dict* containing any or all matplotlib rcParams. To see all options, execute ``import matplotlib; print(matplotlib.rcParams)`` in Python. Any options in this *dict* will be used for this current figure and then returned to their prior settings.
title : str
the axis title
xlabel : str
label for x-axis
ylabel : str
label for y-axis
linewidth : int
line width
alpha : float
line opacity (0-1)
Returns
-------
LFPPSDPlot : *matplotlib figure*
By default, returns the *figure*. If ``returnPlotter`` is ``True``, instead returns the NetPyNE MetaFig.
"""
# If there is no input data, get the data from the NetPyNE sim object
if PSDData is None:
if 'sim' not in kwargs:
from .. import sim
else:
sim = kwargs['sim']
PSDData = sim.analysis.preparePSD(
PSDData=PSDData,
sim=sim,
timeRange=timeRange,
electrodes=electrodes,
pop=pop,
NFFT=NFFT,
noverlap=noverlap,
nperseg=nperseg,
minFreq=minFreq,
maxFreq=maxFreq,
stepFreq=stepFreq,
smooth=smooth,
logy=logy,
normSignal=normSignal,
normPSD=normPSD,
filtFreq=filtFreq,
filtOrder=filtOrder,
detrend=detrend,
transformMethod=transformMethod,
**kwargs
)
print('Plotting LFP power spectral density (PSD)...')
return plotTimeSeriesPSD(
PSDData=PSDData,
label='LFPPSD',
title='LFP Power Spectral Density',
yLabel=r'Power (mV$^2$ / Hz )',
axis=axis,
pop=pop,
separation=separation,
roundOffset=roundOffset,
orderInverse=orderInverse,
legend=legend,
colorList=colorList,
returnPlotter=returnPlotter,
**kwargs
)
[docs]
@exception
def plotCSDPSD(
PSDData=None,
LFPData=None,
axis=None,
timeRange=None,
electrodes=['avg', 'all'],
pop=None,
separation=1.0,
roundOffset=True,
NFFT=256,
noverlap=128,
nperseg=256,
minFreq=1,
maxFreq=100,
stepFreq=1,
smooth=0,
logy=False,
normSignal=False,
normPSD=False,
filtFreq=False,
filtOrder=3,
detrend=False,
transformMethod='morlet',
orderInverse=True,
legend=True,
colorList=None,
returnPlotter=False,
**kwargs
):
"""Function to produce a line plot of LFP electrode signals
NetPyNE Options
---------------
sim : NetPyNE sim object
The *sim object* from which to get data.
*Default:* ``None`` uses the current NetPyNE sim object
Parameters
----------
PSDData : dict, str
The data necessary to plot the LFP PSD.
*Default:* ``None`` uses ``analysis.preparePSD`` to produce ``PSDData`` using the current NetPyNE sim object.
If a *str* it must represent a file path to previously saved data.
axis : matplotlib axis
The axis to plot into, allowing overlaying of plots.
*Default:* ``None`` produces a new figure and axis.
timeRange : list
Time range to include in the raster: ``[min, max]``.
*Default:* ``None`` uses the entire simulation
electrodes : list
A *list* of the electrodes to plot from.
*Default:* ``['avg', 'all']`` plots each electrode as well as their average
pop : str
A population name to calculate PSD from.
*Default:* ``None`` uses all populations.
separation : float
Use to increase or decrease distance between signals on the plot.
*Default:* ``1.0``
roundOffset : bool
Attempts to line up PSD signals with gridlines
*Default:* ``True``
NFFT : int (power of 2)
Number of data points used in each block for the PSD and time-freq FFT.
**Default:** ``256``
noverlap : int (<nperseg)
Number of points of overlap between segments for PSD and time-freq.
**Default:** ``128``
nperseg : int
Length of each segment for time-freq.
**Default:** ``256``
minFreq : float
Minimum frequency shown in plot for PSD and time-freq.
**Default:** ``1``
maxFreq : float
Maximum frequency shown in plot for PSD and time-freq.
**Default:** ``100``
stepFreq : float
Step frequency.
**Default:** ``1``
smooth : int
Window size for smoothing LFP; no smoothing if ``0``
**Default:** ``0``
logy : bool
Whether to use a log axis.
*Default:* ``False``
normSignal : bool
Whether to normalize the LFP data.
*Default:* ``False``
normPSD : bool
Whether to normalize the PSD data.
*Default:* ``False``
filtFreq : int or list
Frequency for low-pass filter (int) or frequencies for bandpass filter in a list: [low, high]
*Default:* ``None`` does not filter the data
filtOrder : int
Order of the filter defined by `filtFreq`.
*Default:* ``3``
detrend : bool
Whether to detrend the data.
*Default:* ``False``
transformMethod : str
The transformation method to use, either 'morlet' or 'fft'.
*Default:* ``'morlet'``
orderInverse : bool
Whether to invert the order of plotting.
*Default:* ``True``
legend : bool
Whether or not to add a legend to the plot.
*Default:* ``True`` adds a legend.
colorList : list
A *list* of colors to draw from when plotting.
*Default:* ``None`` uses the default NetPyNE colorList.
returnPlotter : bool
Whether to return the figure or the NetPyNE MetaFig object.
*Default:* ``False`` returns the figure.
Plot Options
------------
showFig : bool
Whether to show the figure.
*Default:* ``False``
saveFig : bool
Whether to save the figure.
*Default:* ``False``
overwrite : bool
whether to overwrite existing figure files.
*Default:* ``True`` overwrites the figure file
*Options:* ``False`` adds a number to the file name to prevent overwriting
legendKwargs : dict
a *dict* containing any or all legend kwargs. These include ``'title'``, ``'loc'``, ``'fontsize'``, ``'bbox_to_anchor'``, ``'borderaxespad'``, and ``'handlelength'``.
rcParams : dict
a *dict* containing any or all matplotlib rcParams. To see all options, execute ``import matplotlib; print(matplotlib.rcParams)`` in Python. Any options in this *dict* will be used for this current figure and then returned to their prior settings.
title : str
the axis title
xlabel : str
label for x-axis
ylabel : str
label for y-axis
linewidth : int
line width
alpha : float
line opacity (0-1)
Returns
-------
LFPPSDPlot : *matplotlib figure*
By default, returns the *figure*. If ``returnPlotter`` is ``True``, instead returns the NetPyNE MetaFig.
"""
# If there is no input data, get the data from the NetPyNE sim object
if PSDData is None:
if 'sim' not in kwargs:
from .. import sim
else:
sim = kwargs['sim']
PSDData = sim.analysis.preparePSD(
LFPData=LFPData,
CSD=True,
sim=sim,
timeRange=timeRange,
electrodes=electrodes,
pop=pop,
NFFT=NFFT,
noverlap=noverlap,
nperseg=nperseg,
minFreq=minFreq,
maxFreq=maxFreq,
stepFreq=stepFreq,
smooth=smooth,
logy=logy,
normSignal=normSignal,
normPSD=normPSD,
filtFreq=filtFreq,
filtOrder=filtOrder,
detrend=detrend,
transformMethod=transformMethod,
**kwargs
)
print('Plotting CSD power spectral density (PSD)...')
from .plotTimeSeriesPSD import plotTimeSeriesPSD
return plotTimeSeriesPSD(
PSDData=PSDData,
label='CSDPSD',
title='CSD Power Spectral Density',
yLabel='Power',
axis=axis,
pop=pop,
separation=separation,
roundOffset=roundOffset,
orderInverse=orderInverse,
legend=legend,
colorList=colorList,
returnPlotter=returnPlotter,
**kwargs
)
[docs]
def plotTimeSeriesPSD(
PSDData,
label='PSD',
title='Power Spectral Density',
yLabel=None,
axis=None,
pop=None,
separation=1.0,
roundOffset=True,
orderInverse=True,
legend=True,
colorList=None,
returnPlotter=False,
**kwargs
):
# If input is a dictionary, pull the data out of it
if type(PSDData) == dict:
freqs = PSDData['psdFreqs']
freq = freqs[0]
psds = PSDData['psdSignal']
names = PSDData['psdNames']
colors = PSDData.get('colors')
linewidths = PSDData.get('linewidths')
alphas = PSDData.get('alphas')
axisArgs = PSDData.get('axisArgs')
# Set up colors, linewidths, and alphas for the plots
if not colors:
if not colorList:
from .plotter import colorList
colors = colorList[0 : len(psds)]
if not linewidths:
linewidths = [1.0 for name in names]
if not alphas:
alphas = [1.0 for name in names]
# Create a dictionary to hold axis inputs
if not axisArgs:
axisArgs = {}
if pop:
title += ' - Population: ' + pop
axisArgs['title'] = title
axisArgs['xlabel'] = 'Frequency (Hz)'
if yLabel:
axisArgs['ylabel'] = yLabel
if axis != 'multi':
axisArgs['grid'] = {'which': 'both'}
# Link colors to traces, make avg plot black, add separation to traces
plotColors = []
legendLabels = []
colorIndex = 0
offset = np.absolute(psds).max() * separation
if axis == 'multi':
offset = 0
roundOffset = False
if roundOffset:
sigfigs = 1
if type(roundOffset) == int:
sigfigs = roundOffset
offset = round(offset, sigfigs - int(math.floor(math.log10(abs(offset)))) - 1)
for index, (name, psd) in enumerate(zip(names, psds)):
legendLabels.append(name)
if orderInverse:
psds[index] = index * offset - psd
axisArgs['invert_yaxis'] = True
else:
psds[index] = index * offset + psd
if name == 'avg':
plotColors.append('black')
else:
plotColors.append(colors[colorIndex])
colorIndex += 1
# Create a dictionary with the inputs for a line plot
linesData = {}
linesData['x'] = freq
linesData['y'] = psds
linesData['color'] = plotColors
linesData['marker'] = None
linesData['markersize'] = None
linesData['linewidth'] = None
linesData['alpha'] = None
linesData['label'] = legendLabels
# If a kwarg matches a lines input key, use the kwarg value instead of the default
for kwarg in list(kwargs.keys()):
if kwarg in linesData:
linesData[kwarg] = kwargs[kwarg]
kwargs.pop(kwarg)
# create Plotter object
if axis != 'multi':
plotter = LinesPlotter(data=linesData, kind=label, axis=axis, **axisArgs, **kwargs)
else:
plotter = MultiPlotter(data=linesData, kind=label, metaFig=None, **axisArgs, **kwargs)
metaFig = plotter.metafig
# remove the y-axis tick labels
# plotter.axis.get_yaxis().set_ticklabels([])
# Set up the default legend settings
legendKwargs = {}
legendKwargs['title'] = 'Electrodes'
# legendKwargs['bbox_to_anchor'] = (1.025, 1)
legendKwargs['loc'] = 'upper right'
# legendKwargs['borderaxespad'] = 0.0
# legendKwargs['handlelength'] = 0.5
legendKwargs['fontsize'] = 'small'
# legendKwargs['title_fontsize'] = 'small' ## EYG commenting out to resolve error 12/06/22
# add the legend
if legend:
axisArgs['legend'] = legendKwargs
# Generate the figure
PSDPlot = plotter.plot(**axisArgs, **kwargs)
# Default is to return the figure, but you can also return the plotter
if returnPlotter:
return metaFig
else:
return PSDPlot