Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change assumed shape of time series to timepoints*channels #11

Merged
merged 2 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions sktime_neuro/examples/Motor_Imagery_Example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@
")\n",
"\n",
"raw = read_raw_bids(bids_path=bids_path, verbose=False)\n",
"data = raw.get_data()"
"\n",
"# mne assumes shape channels*timepoints;sktime assumes shape timepoints*channels\n",
"data = raw.get_data().transpose()"
]
},
{
Expand Down Expand Up @@ -78,7 +80,7 @@
"metadata": {},
"outputs": [],
"source": [
"detrended_data = Detrender().fit_transform(pd.DataFrame(downsampled_data.transpose()))"
"detrended_data = Detrender().fit_transform(pd.DataFrame(downsampled_data))"
]
},
{
Expand All @@ -89,7 +91,7 @@
"outputs": [],
"source": [
"filtered_data = FilterforSeries(l_freq=8, h_freq=16, sfreq=250).fit_transform(\n",
" detrended_data.transpose()\n",
" detrended_data\n",
")"
]
},
Expand Down
7 changes: 4 additions & 3 deletions sktime_neuro/tests/test_filterforseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ def test_filter_kwargs(kwargs):
@pytest.mark.parametrize("hfreq", [11, 20, None])
def test_filter_results(sfreq, lfreq, hfreq):
np.random.seed(42)
X = 0.02 * np.random.randn(30, 1000)
X = 0.02 * np.random.randn(1000, 30)

# sklearn
# sktime
Filter = FilterforSeries(sfreq=sfreq, l_freq=lfreq, h_freq=hfreq)
Xt1 = Filter.fit_transform(X)

# mne
Xt2 = filter.filter_data(X, sfreq=sfreq, l_freq=lfreq, h_freq=hfreq)
X = X.transpose()
Xt2 = filter.filter_data(X, sfreq=sfreq, l_freq=lfreq, h_freq=hfreq).transpose()

assert np.allclose(Xt1, Xt2)
9 changes: 8 additions & 1 deletion sktime_neuro/transformations/series/filterforseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ def transform(self, Z, x=None) -> np.array:
Parameters
----------
Z : 2D numpy array, pd.Series or pd.DataFrame
(will get coerced to numpy)
(will get coerced to numpy); needs to be of
shape timepoints*channels

Returns
-------
Expand All @@ -79,8 +80,14 @@ def transform(self, Z, x=None) -> np.array:
if isinstance(z, (pd.DataFrame, pd.Series)):
z = z.to_numpy()

# mne needs timepoints to be the last dimension
z = z.transpose()

# z is now of shape channels * timepoints
z = filter.filter_data(
z, sfreq=self.sfreq, l_freq=self.l_freq, h_freq=self.h_freq, **self.kwargs
)

# transpose back to have sktime shape again (timepoints*channels)
z = z.transpose()
return z
10 changes: 8 additions & 2 deletions sktime_neuro/transformations/series/seriesdownsampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from sktime_neuro.transformations.base import _SeriesToSeriesTransformer
from sktime.utils.validation.series import check_series
import numpy as np
import pandas as pd


class SeriesDownsampling(_SeriesToSeriesTransformer):
Expand Down Expand Up @@ -37,7 +38,8 @@ def transform(self, Z, y=None) -> np.array:
Parameters
_________
X : np.array
shape: channels*timepoints
shape: timepoints*channels
(also accepts pd.DataFrame that will get coerced to numpy)

Returns
________
Expand All @@ -51,7 +53,11 @@ def transform(self, Z, y=None) -> np.array:
if self.factor > Z.shape[1]:
raise ValueError("Factor too high for shape of Series.")

z = z[:, 0 :: self.factor]
# deals with numpy arrays:
if isinstance(z, (pd.DataFrame, pd.Series)):
z = z.to_numpy()

z = z[0 :: self.factor, :]
# do we need a warning about this?
# print("The new sampling frequency is:" + str(self.fs / self.factor))
return z
19 changes: 12 additions & 7 deletions sktime_neuro/transformations/series_to_panel/eeg_epoching.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ def epoch(Z, annotation, labels, interval, sfreq) -> (np.array, np.array):
"""
Parameters
_________
Z : np.array
time series to be epoched
shape: timepoints*channels
annotation : pd.DataFrame,
one row per event with columns "onset", "duration"
and "descritption"
Expand All @@ -32,7 +35,7 @@ def epoch(Z, annotation, labels, interval, sfreq) -> (np.array, np.array):
Z = check_series(Z)

# create shape of final data
n_channels = Z.shape[0]
n_channels = Z.shape[1]
n_timepoints = int(interval[1] * sfreq) - int(interval[0] * sfreq)
n_trials = len(annotation.loc[lambda df: df["description"].isin(labels)]["onset"])
X = np.zeros((n_trials, n_channels, n_timepoints))
Expand All @@ -48,12 +51,14 @@ def epoch(Z, annotation, labels, interval, sfreq) -> (np.array, np.array):
# iterate over onsets and add them to the datacontainer (X) and labels (y)
for onset in onsets_of_label:
offset = int(sfreq * onset)
X[idx] = Z[
:,
(int(interval[0] * sfreq) + offset) : (
int(interval[1] * sfreq) + offset
),
]
X[idx] = (
Z[
(int(interval[0] * sfreq) + offset) : (
int(interval[1] * sfreq) + offset
),
:,
]
).transpose()
y.append(label)
idx += 1
return X, np.asarray(y)