Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/run_unix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ jobs:
run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/transforms.py
- name: Downsampling Python
run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/downsampling.py
- name: Downsampling Median Regression Python
run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/downsampling_median.py
- name: ICA Python
run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/ica.py
- name: CSP Python
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/run_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ jobs:
- name: Downsampling Python Test
run: python %GITHUB_WORKSPACE%\python_package\examples\tests\downsampling.py
shell: cmd
- name: Downsampling Median Regression Python Test
run: python %GITHUB_WORKSPACE%\python_package\examples\tests\downsampling_median.py
shell: cmd
- name: CSP Python Test
run: python %GITHUB_WORKSPACE%\python_package\examples\tests\csp.py
shell: cmd
Expand Down
39 changes: 39 additions & 0 deletions python_package/examples/tests/downsampling_median.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import numpy as np

from brainflow.data_filter import AggOperations, DataFilter


def main():
# One large outlier per window, so the median and the mean of a window are far apart
# and a median that silently falls back to the mean is visible.
data = [1.0, 2.0, 3.0, 100.0, 4.0, 5.0, 6.0, 200.0]

# Period 2 is unchanged by design. With two values the median is their mean, so this
# pins that the even-period path did not shift the one case that was already correct.
period_2 = DataFilter.perform_downsampling(np.array(data), 2, AggOperations.MEDIAN.value)
assert np.allclose(period_2, [1.5, 51.5, 4.5, 103.0]), period_2

# Odd periods keep the existing single middle value.
period_3 = DataFilter.perform_downsampling(np.array(data), 3, AggOperations.MEDIAN.value)
assert np.allclose(period_3, [2.0, 5.0]), period_3

# Period 4 is the regression. Each window is the mean of the two sorted middle values,
# (2 + 3) / 2 and (5 + 6) / 2, rather than the full-window mean downsample_median
# used to return for every even period.
period_4 = DataFilter.perform_downsampling(np.array(data), 4, AggOperations.MEDIAN.value)
assert np.allclose(period_4, [2.5, 5.5]), period_4

# The same windows through MEAN, to show the two operations are now distinct where
# they used to return identical values.
mean_4 = DataFilter.perform_downsampling(np.array(data), 4, AggOperations.MEAN.value)
assert np.allclose(mean_4, [26.5, 53.75]), mean_4
assert not np.allclose(period_4, mean_4), (period_4, mean_4)

mean_3 = DataFilter.perform_downsampling(np.array(data), 3, AggOperations.MEAN.value)
assert not np.allclose(period_3, mean_3), (period_3, mean_3)

print('downsampling median regression passed')


if __name__ == '__main__':
main()
10 changes: 6 additions & 4 deletions src/data_handler/inc/downsample_operators.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@ inline double downsample_each (double *data, int len)

inline double downsample_median (double *data, int len)
{
if (len % 2 == 0)
{
return downsample_mean (data, len);
}
std::vector<double> values;
for (int i = 0; i < len; i++)
{
values.push_back (data[i]);
}
std::sort (values.begin (), values.end ());
if (len % 2 == 0)
{
// for an even number of values the median is the mean of the two middle ones,
// same convention as RollingMedian in rolling_filter.h
return (values[len / 2 - 1] + values[len / 2]) / 2.0;
}
return values[len / 2];
}