Source code for intan.plotting._waterfall

"""
Waterfall Plotting Module
"""
import matplotlib.pyplot as plt
import numpy as np


[docs] def waterfall(ax=None, data=None, channel_indices=None, time_vector=None, offset_increment=200, edges=[], plot_title="", line_width=0.2, colormap='rainbow', downsampling_factor=1, verbose=False): """ Creates a waterfall plot for the specified channels with a user-defined title, custom color styling, and scale bars for time and voltage. Parameters: ax (matplotlib.axes.Axes): The axes to plot on. If None, a new figure and axes are created. data (numpy.ndarray): 2D array of shape (num_channels, num_samples). channel_indices (list): List of channel indices to plot. time_vector (numpy.ndarray): 1D array of time values corresponding to the samples. offset_increment (float): Increment for offsetting each channel's plot vertically. edges (list): List of edges to plot as vertical lines. plot_title (str): Title for the plot. line_width (float): Width of the lines in the plot. colormap (str): Colormap for the plot. downsampling_factor (int): Factor by which to downsample the data for plotting. verbose (bool): If True, prints additional information. """ if ax is None: fig, ax = plt.subplots(figsize=(10, 12)) if data is None or time_vector is None or channel_indices is None: raise ValueError("Data, time_vector, and channel_indices must be provided.") offset = 0 # Start with no offset cmap = plt.get_cmap(colormap) # You can also experiment with other color maps like 'jet', 'viridis', etc. num_channels = len(channel_indices) # Downsample the data by the specified factor data = data[:, ::downsampling_factor] time_vector = time_vector[::downsampling_factor] for i, channel_idx in enumerate(channel_indices): channel_data = data[channel_idx, :] # Use colormap to assign a color based on channel index color = cmap(i / num_channels) ax.plot(time_vector, channel_data + offset, color=color, linewidth=line_width) offset += offset_increment # If edges are provided, plot them as vertical lines for edge in edges: ax.axvline(x=edge, color='red', linestyle='--', linewidth=1) # Labeling and visualization # ax.set_xlabel('Time (s)') # ax.set_ylabel('Amplitude + Offset') ax.set_title(plot_title, fontsize=14, fontweight='bold') # Custom scale bar add_scalebars(ax) # Turn off x and y axis numbers ax.set_xticks([]) ax.set_yticks([]) # Remove the black box around the plot for spine in ax.spines.values(): spine.set_visible(False) # Insert text labels for channel 0 and channel 128 insert_channel_labels(ax, time_vector, num_channels) # Insert vertical labels for "Extensor" and "Flexor" insert_vertical_labels(ax) plt.show()
[docs] def add_scalebars(ax, scale_time=5, scale_voltage=10): """ Adds time and voltage scale bars to the plot in the lower left corner. Parameters: ax (matplotlib.axes.Axes): The axes to add the scale bars to. scale_time (float): Length of the timescale bar in seconds. scale_voltage (float): Length of the voltage scale bar in mV. """ # Add horizontal time scale bar (5 sec) ax.plot([0, scale_time], [-1000, -1000], color='gray', lw=3) ax.text(scale_time / 2, -1500, '5 sec', va='center', ha='center', fontsize=12, color='gray') # Add vertical voltage scale bar (10 mV) ax.plot([0, 0], [-1000, -1000 + scale_voltage * 10], color='gray', lw=3) ax.text(-0.5, -500, '10 mV', va='center', ha='center', rotation='vertical', fontsize=12, color='gray')
# ax.text(-0.5, voltage_scale_length / 2 - 1000, '10 mV', va='center', ha='center', rotation='vertical', fontsize=12, color='gray') # def insert_channel_labels(ax, time_vector, num_channels): # """ # Inserts text labels to indicate specific channels on the plot. # # Args: # ax: The plot axes to add the text to. # time_vector: The time vector for placing the text appropriately. # num_channels: Total number of channels being plotted. # """ # # Position the text for Channel 0 (near the bottom) # x_pos = time_vector[-1] # Position at the end of the time range # ax.text(x_pos + 1, 200, 'Channel 0', fontsize=8, va='center', ha='left', color='black', fontweight='bold') # # # Position the text for Channel 128 (near the top) # ax.text(x_pos + 1, 25500, 'Channel 128', fontsize=8, va='center', ha='left', color='black', fontweight='bold')
[docs] def insert_channel_labels(ax, time_vector, num_channels, num_labels=2, label_color='black', font_size=8): """ Inserts evenly spaced text labels on a multichannel plot to indicate selected channels. Parameters: ax: Matplotlib Axes object. time_vector: Time vector (used for x position of labels). num_channels: Total number of channels plotted vertically. num_labels: Number of labels to insert (default is 2). label_color: Text color. font_size: Size of the text. """ if num_labels > num_channels: raise ValueError("Number of labels cannot exceed total number of channels.") x_pos = time_vector[-1] + 1 # Place labels slightly past the right edge # Evenly space label positions from bottom to top channel_indices = np.linspace(0, num_channels - 1, num_labels, dtype=int) y_offsets = np.linspace(200, 25500, num_channels) # Example scaling (adjust if needed) for ch in channel_indices: y = y_offsets[ch] ax.text(x_pos, y, f"Channel {ch}", fontsize=font_size, va='center', ha='left', color=label_color, fontweight='bold')
[docs] def insert_vertical_labels(ax): """ Inserts vertical labels "Extensor" and "Flexor" for the two groups of channels. Parameters: ax (matplotlib.axes.Axes): The axes to plot on. If None, a new figure and axes are created. """ # Insert "Extensor" label vertically for the first 64 channels (left side) ax.text(-1, 5000, 'Extensor', fontsize=12, va='center', ha='center', color='black', rotation='vertical', fontweight='bold') # Insert "Flexor" label vertically for channels 65-128 (left side) ax.text(-1, 22000, 'Flexor', fontsize=12, va='center', ha='center', color='black', rotation='vertical', fontweight='bold')