Source code for neurokit2.emg.emg_plot

# -*- coding: utf-8 -*-
from warnings import warn

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from ..misc import NeuroKitWarning


[docs] def emg_plot(emg_signals, info=None, static=True): """**EMG Graph** Visualize electromyography (EMG) data. Parameters ---------- emg_signals : DataFrame DataFrame obtained from ``emg_process()``. info : dict The information Dict returned by ``emg_process()``. Defaults to ``None``. static : bool If True, a static plot will be generated with matplotlib. If False, an interactive plot will be generated with plotly. Defaults to True. See Also -------- emg_process Returns ------- See :func:`.ecg_plot` for details on how to access the figure, modify the size and save it. Examples -------- .. ipython:: python import neurokit2 as nk # Simulate data emg = nk.emg_simulate(duration=10, sampling_rate=1000, burst_number=3) # Process signal emg_signals, info = nk.emg_process(emg, sampling_rate=1000) # Plot @savefig p_emg_plot.png scale=100% nk.emg_plot(emg_signals, info) @suppress plt.close() """ if info is None: warn( "'info' dict not provided. Some information might be missing." + " Sampling rate will be set to 1000 Hz.", category=NeuroKitWarning, ) info = { "sampling_rate": 1000, } # Mark onsets, offsets, activity onsets = np.where(emg_signals["EMG_Onsets"] == 1)[0] offsets = np.where(emg_signals["EMG_Offsets"] == 1)[0] # Sanity-check input. if not isinstance(emg_signals, pd.DataFrame): raise ValueError( "NeuroKit error: The `emg_signals` argument must" " be the DataFrame returned by `emg_process()`." ) # Determine what to display on the x-axis, mark activity. x_axis = np.linspace( 0, emg_signals.shape[0] / info["sampling_rate"], emg_signals.shape[0] ) if static is True: _emg_plot_static(emg_signals, x_axis, onsets, offsets, info["sampling_rate"]) else: return _emg_plot_interactive( emg_signals, x_axis, onsets, offsets, info["sampling_rate"] )
# ============================================================================= # Internals # ============================================================================= def _emg_plot_activity(emg_signals, onsets, offsets): activity_signal = pd.Series(np.full(len(emg_signals), np.nan)) activity_signal[onsets] = emg_signals["EMG_Amplitude"][onsets].values activity_signal[offsets] = emg_signals["EMG_Amplitude"][offsets].values activity_signal = activity_signal.bfill() if np.any(activity_signal.isna()): index = np.min(np.where(activity_signal.isna())) - 1 value_to_fill = activity_signal[index] activity_signal = activity_signal.fillna(value_to_fill) return activity_signal def _emg_plot_static(emg_signals, x_axis, onsets, offsets, sampling_rate): # Prepare figure. fig, (ax0, ax1) = plt.subplots(nrows=2, ncols=1, sharex=True) if sampling_rate is not None: ax1.set_xlabel("Time (seconds)") elif sampling_rate is None: ax1.set_xlabel("Samples") fig.suptitle("Electromyography (EMG)", fontweight="bold") plt.tight_layout(h_pad=0.2) # Plot cleaned and raw EMG. ax0.set_title("Raw and Cleaned Signal") ax0.plot(x_axis, emg_signals["EMG_Raw"], color="#B0BEC5", label="Raw", zorder=1) ax0.plot( x_axis, emg_signals["EMG_Clean"], color="#FFC107", label="Cleaned", zorder=1, linewidth=1.5, ) ax0.legend(loc="upper right") # Plot Amplitude. ax1.set_title("Muscle Activation") ax1.plot( x_axis, emg_signals["EMG_Amplitude"], color="#FF9800", label="Amplitude", linewidth=1.5, ) # Shade activity regions. activity_signal = _emg_plot_activity(emg_signals, onsets, offsets) ax1.fill_between( x_axis, emg_signals["EMG_Amplitude"], activity_signal, where=emg_signals["EMG_Amplitude"] > activity_signal, color="#f7c568", alpha=0.5, label=None, ) # Mark onsets and offsets. ax1.scatter( x_axis[onsets], emg_signals["EMG_Amplitude"][onsets], color="#f03e65", label=None, zorder=3, ) ax1.scatter( x_axis[offsets], emg_signals["EMG_Amplitude"][offsets], color="#f03e65", label=None, zorder=3, ) if sampling_rate is not None: onsets = onsets / sampling_rate offsets = offsets / sampling_rate for i, j in zip(list(onsets), list(offsets)): ax1.axvline(i, color="#4a4a4a", linestyle="--", label=None, zorder=2) ax1.axvline(j, color="#4a4a4a", linestyle="--", label=None, zorder=2) ax1.legend(loc="upper right") def _emg_plot_interactive(emg_signals, x_axis, onsets, offsets, sampling_rate): try: import plotly.graph_objects as go from plotly.subplots import make_subplots except ImportError: raise ImportError( "NeuroKit error: emg_plot(): the 'plotly' " "module is required for this feature." "Please install it first (`pip install plotly`)." ) # Prepare figure. fig = make_subplots(rows=2, cols=1, shared_xaxes=True) fig.update_layout(title="Electromyography (EMG)", font=dict(size=18), height=600) # Plot cleaned and raw EMG. fig.add_trace( go.Scatter( x=x_axis, y=emg_signals["EMG_Raw"], mode="lines", name="Raw", line=dict(color="#B0BEC5"), ), row=1, col=1, ) fig.add_trace( go.Scatter( x=x_axis, y=emg_signals["EMG_Clean"], mode="lines", name="Cleaned", line=dict(color="#FFC107"), ), row=1, col=1, ) # Plot Amplitude. fig.add_trace( go.Scatter( x=x_axis, y=emg_signals["EMG_Amplitude"], mode="lines", name="Amplitude", line=dict(color="#FF9800"), ), row=2, col=1, ) # Mark onsets and offsets. fig.add_trace( go.Scatter( x=x_axis[onsets], y=emg_signals["EMG_Amplitude"][onsets], mode="markers", name="Onsets", marker=dict(color="#f03e65", size=10), ), row=2, col=1, ) fig.add_trace( go.Scatter( x=x_axis[offsets], y=emg_signals["EMG_Amplitude"][offsets], mode="markers", name="Offsets", marker=dict(color="#f03e65", size=10), ), row=2, col=1, ) if sampling_rate is not None: onsets = onsets / sampling_rate offsets = offsets / sampling_rate fig.update_xaxes(title_text="Time (seconds)", row=2, col=1) elif sampling_rate is None: fig.update_xaxes(title_text="Samples", row=2, col=1) for i, j in zip(list(onsets), list(offsets)): fig.add_shape( type="line", x0=i, y0=0, x1=i, y1=1, line=dict(color="#4a4a4a", width=2, dash="dash"), row=2, col=1, ) fig.add_shape( type="line", x0=j, y0=0, x1=j, y1=1, line=dict(color="#4a4a4a", width=2, dash="dash"), row=2, col=1, ) fig.update_yaxes(title_text="Amplitude", row=2, col=1) return fig