Wave basis functions

This tutorial demonstrates how to explore spatial wave activity using the WaveSpace toolbox. We will import wave data, extract spatial basis functions, and visualize them.

Setup

First, add the project root directory to the Python path when working with source code (not necessary if WaveSpace is already installed as a package):

import sys
import os
path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, path )
print(path)

Import Required Packages

We import the relevant modules from WaveSpace along with some utilities for data handling and visualization:

from WaveSpace.WaveAnalysis import WaveActivity as wa
from WaveSpace.Utils import ImportHelpers
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

Load Data

Load an example wave data object from file:

waveData = ImportHelpers.load_wavedata_object("ExampleData/Output/complexData")

Exploring Basis Functions for a Subset

We can look at the spatial basis functions for a subset of trials (belonging to the same condition) and at a particular frequency of interest. Here, we specify the indices into the data bucket:

nBases = 3
dataInd = (slice(0,1), slice(10,12), slice(None), slice(None), slice(None))
wa.find_wave_activity(waveData, dataBucketName="complexData", dataInd=dataInd, nBases=nBases)

bases = waveData.get_data('Bases')

Now, visualize the phase maps of the extracted bases:

fig, axs = plt.subplots(1, nBases, figsize=(nBases*6, 6))
if nBases == 1:
    axs = [axs]
for b in range(nBases):
    im = axs[b].imshow(
        np.angle(bases[:, :, b]),
        cmap='hsv',
        vmin=-np.pi,
        vmax=np.pi,
        origin='lower',
        aspect='auto'
    )
    axs[b].set_title(f'wave map {b+1}')
    axs[b].set_xlabel('posy')
    axs[b].set_ylabel('posx')
    fig.colorbar(im, ax=axs[b], fraction=0.046, pad=0.04, label='Phase (rad)')

plt.tight_layout()
plt.show()

This produces a series of phase maps that represent the spatial basis functions extracted from the subset of trials.

Extracting Bases from All Data

Alternatively, we can calculate the bases on all data at once and then sort out the weights later. This provides a more global view of wave activity.

nBases = 5
dataInd = None
wa.find_wave_activity(waveData, dataBucketName="complexData", dataInd=dataInd, nBases=nBases)

bases = waveData.get_data('Bases')

fig, axs = plt.subplots(1, nBases, figsize=(nBases*6, 6))
if nBases == 1:
    axs = [axs]
for b in range(nBases):
    im = axs[b].imshow(
        np.angle(bases[:, :, b]),
        cmap='hsv',
        vmin=-np.pi,
        vmax=np.pi,
        origin='lower',
        aspect='auto'
    )
    axs[b].set_title(f'wave map {b+1}')
    axs[b].set_xlabel('posy')
    axs[b].set_ylabel('posx')
    fig.colorbar(im, ax=axs[b], fraction=0.046, pad=0.04, label='Phase (rad)')

plt.tight_layout()
plt.show()

Interpreting the Results

When using the full dataset, the bases represent linear combinations of all included waves, rather than just those from a restricted subset. This allows for more general spatial modes but may mix different underlying dynamics.