Synthetic Data¶
For testing and demonstrational purposes it is always good to work with synthetic data. Syncopy brings its own suite of synthetic data generators, but it is also possible to devise your own synthetic data and conveniently analyze it with Syncopy.
General Recipe¶
To create a synthetic data set follow these steps:
write a function which returns a single trial with desired shape
(nSamples, nChannels)
, such that each trial is a 2d-ndarray
collect all the trials into a Python
list
, for example with a list comprehension or simply a for loopInstantiate an
AnalogData
object by passing this list holding the trials and set the samplerate
In (pseudo-)Python code:
def generate_trial(nSamples, nChannels):
trial = .. something fancy ..
# These should evaluate to True
isinstance(trial, np.ndarray)
trial.shape == (nSamples, nChannels)
return trial
# here we use a list comprehension
trial_list = [generate_trial(nSamples, nChannels) for _ in range(nTrials)]
my_fancy_data = spy.AnalogData(trial_list, samplerate=my_samplerate)
Note
The same recipe can be used to generally instantiate Syncopy data objects from NumPy arrays.
Built-in Generators¶
These generators return single-trial NumPy arrays, so to import them into Syncopy use the General Recipe described above.
- syncopy.tests.synth_data.phase_diffusion(freq, eps=0.1, fs=1000, nChannels=2, nSamples=1000, return_phase=False)[source]¶
Linear (harmonic) phase evolution + a Brownian noise term inducing phase diffusion around the deterministic phase drift with slope
2pi * freq
(angular frequency).The linear phase increments are given by
dPhase = 2pi * freq/fs
, the Brownian increments are scaled with eps relative to these phase increments.- Parameters:
freq (float) – Harmonic frequency in Hz
eps (float) – Scaled Brownian increments 1 means the single Wiener step has on average the size of the harmonic increments
fs (float) – Sampling rate in Hz
nChannels (int) – Number of channels
nSamples (int) – Number of samples in time
return_phase (bool, optional) – If set to true returns the phases in radians
- Returns:
phases – Synthetic nSamples x nChannels data array simulating noisy phase evolution/diffusion
- Return type:
- syncopy.tests.synth_data.AR2_network(AdjMat=None, nSamples=1000, alphas=[0.55, - 0.8])[source]¶
Simulation of a network of coupled AR(2) processes
With the default parameters the individual processes (as in Dhamala 2008) have a spectral peak at 40Hz with a sampling frequency of 200Hz.
- NOTE: There is no check for stability: setting the
alphas ad libitum and/or defining large and dense (many connections) systems will almost surely lead to an unstable system
- Parameters:
AdjMat (np.ndarray or None) – nChannel x nChannel adjacency matrix where entry
(i,j)
is the coupling strength from channeli -> j
. If left at None, the default 2 Channel system with unidirectional2 -> 1
coupling is generated.nSamples (int, optional) – Number of samples in time
alphas (2-element sequence, optional) – The AR(2) parameters for lag1 and lag2
- Returns:
sol – The nSamples x nChannel solution of the network dynamics
- Return type:
Example: Noisy Harmonics¶
We can easily create custom synthetic datasets using basic NumPy functionality and Syncopy’s AnalogData
.
Let’s create two harmonics and add some white noise to it:
import numpy as np
import syncopy as spy
def generate_noisy_harmonics(nSamples, nChannels, samplerate):
f1, f2 = 20, 50 # the harmonic frequencies in Hz
# the sampling times vector
tvec = np.arange(nSamples) * 1 / samplerate
# define the two harmonics
harm1 = np.cos(2 * np.pi * f1 * tvec)
harm2 = np.cos(2 * np.pi * f2 * tvec)
# add some white noise
trial = 0.5 * np.random.randn(nSamples, nChannels)
# add 1st harmonic to 1st channel
trial[:, 0] += harm1
# add 2nd harmonic to 2nd channel
trial[:, 1] += 0.5 * harm2
return trial
nTrials = 50
nSamples = 1000
nChannels = 3
samplerate = 500 # in Hz
trials = []
for _ in range(nTrials):
trial = generate_noisy_harmonics(nSamples, nChannels, samplerate)
trials.append(trial)
synth_data = spy.AnalogData(trials, samplerate=samplerate)
Here we first defined the number of trials and then the number of samples and channels per trial. With a sampling rate of 500Hz and 1000 samples this gives us a trial length of two seconds. The function generate_noisy_harmonics
adds white noise to all channels, a 20Hz harmonic on the 1st channel and a 50Hz harmonic on the 2nd channel. Every trial got collected into a Python list
, which at the last line was used to initialize our AnalogData
object. Note that data instantiated that way always has a default trigger offset of -1 seconds.
Now we can directly run a simple FFT analysis and plot the power spectra of all 3 channels:
spectrum = spy.freqanalysis(synth_data, foilim=[0,80], keeptrials=False)
spectrum.singlepanelplot()
As constructed, we have two harmonic peaks at the respective frequencies (20Hz and 50Hz) and the white noise floor on all channels.