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

Auto-detect the trigger channel based on retrieved channel names #306

Merged
merged 76 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
62ec35e
default chtrig is 0
vinferrer Sep 17, 2020
04bd62d
default chtrig is also 0
vinferrer Sep 17, 2020
c30ac75
add automatic recognition of a trigger channel by name
vinferrer Sep 17, 2020
bf37e1f
add warking if trigger channel name doesn't match any alias
vinferrer Sep 18, 2020
23bf7f4
change 'Wrong trigger' chtrig to -1
vinferrer Sep 18, 2020
e43b51a
Update phys2bids/physio_obj.py
vinferrer Sep 18, 2020
a4dff32
edit warning messages
vinferrer Sep 18, 2020
c400d23
merge with remote
vinferrer Sep 18, 2020
bee4c71
Update phys2bids/physio_obj.py
vinferrer Sep 18, 2020
fd115b9
change assigning trigger phrase
vinferrer Sep 18, 2020
0529d79
solve lintin problem
vinferrer Sep 18, 2020
2b75fd4
implement partial match
vinferrer Sep 21, 2020
c31a227
correct error
vinferrer Sep 21, 2020
cf839f9
make the auto_chtrig a method inside phys_obj.py
vinferrer Sep 21, 2020
03cde91
add docstring
vinferrer Sep 21, 2020
16629c5
use imperative mode
vinferrer Sep 21, 2020
f0d2212
now acq doesn't use chtrig
vinferrer Sep 21, 2020
37b604a
add () so the function executes
vinferrer Sep 22, 2020
a0e8c02
add test_auto_trigger_selection unit test
vinferrer Sep 22, 2020
4ec2d10
delete unused import
vinferrer Sep 22, 2020
28b0a25
merge with #308
vinferrer Oct 15, 2020
59317b5
add chtrig=0 explanation
vinferrer Oct 15, 2020
564eeea
merge with main
vinferrer Nov 23, 2020
5f99e95
0 isn't valid also
vinferrer Nov 25, 2020
77c46b8
add chtrig to the second call
vinferrer Nov 25, 2020
89d27db
Update phys2bids/cli/run.py
vinferrer Nov 30, 2020
2cdda8a
Update phys2bids/cli/run.py
vinferrer Nov 30, 2020
cd27501
Update phys2bids/io.py
vinferrer Nov 30, 2020
797e89c
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
00cf91b
Update phys2bids/io.py
vinferrer Nov 30, 2020
b39bdde
Update phys2bids/phys2bids.py
vinferrer Nov 30, 2020
3ce559c
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
ae3256a
Update phys2bids/tests/test_physio_obj.py
vinferrer Nov 30, 2020
aa66db2
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
b03a074
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
6233068
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
c8d7a63
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
c9457f3
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
169b3ae
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
3d6885c
conflict in docstring
vinferrer Nov 30, 2020
fc603a4
conflict in docstring
vinferrer Nov 30, 2020
bbdbb53
TRIGGER_NAMES is a global variable
vinferrer Nov 30, 2020
6389ca3
rename trigger_name_list as TRIGGER_NAMES in one condition
vinferrer Nov 30, 2020
7dfe632
add raises to the docstring
vinferrer Nov 30, 2020
ed6f296
add raises to the docstring
vinferrer Nov 30, 2020
ca3fe59
change re expresion
vinferrer Nov 30, 2020
fb28580
add r to the re in results
vinferrer Nov 30, 2020
a969cb4
go back to the original re expresion
vinferrer Nov 30, 2020
2beb6a1
name one of the channels CO2 so we see if a problem appears with the re
vinferrer Nov 30, 2020
320b5c8
Update phys2bids/physio_obj.py
vinferrer Nov 30, 2020
a6bda02
resolve conflicst with main
vinferrer Nov 30, 2020
b652a3b
change warnings by infos
vinferrer Nov 30, 2020
b5fdba6
add the if condition deleted when merging with main
vinferrer Dec 1, 2020
96af657
try stefano's solution but eliminate numbers frist from names
vinferrer Dec 1, 2020
5bd6a33
merge with main
vinferrer Dec 17, 2020
a390bd6
accept main incoming change
vinferrer Dec 17, 2020
71225f5
adding cirrus CI with basic enviroment
vinferrer Dec 24, 2020
381f151
wrong branch
vinferrer Dec 24, 2020
ae9d86f
Merge branch 'master' of https://github.com/physiopy/phys2bids into a…
vinferrer Jan 31, 2021
c5a9285
solve docstrings issues
vinferrer Jan 31, 2021
7fe9e78
solve linting problems
vinferrer Jan 31, 2021
d305f4d
Propose less elegant but more specific comparison
Feb 16, 2021
99b6b3f
Update phys2bids/physio_obj.py
vinferrer Feb 16, 2021
a001a00
Update phys2bids/physio_obj.py
vinferrer Feb 16, 2021
408c63d
Update phys2bids/physio_obj.py
vinferrer Feb 16, 2021
7479635
correct linting problem
vinferrer Feb 16, 2021
d8169be
Wrong way to get an int from a string!
Feb 16, 2021
82f2175
Change warning to info
Feb 16, 2021
ce4e813
Make linter happy
Feb 16, 2021
504dca4
increasing logger level to info
vinferrer Feb 16, 2021
cef1363
Merge branch 'master' of https://github.com/physiopy/phys2bids into a…
vinferrer Feb 16, 2021
1511054
add linting extra line
vinferrer Feb 16, 2021
a187f06
Merge branch 'auto_chtrig' of https://github.com/vinferrer/phys2bids …
Feb 16, 2021
52fa097
Add a few trigger names to tests
Feb 16, 2021
7e79130
Match numbers
Feb 16, 2021
2046244
Merge pull request #8 from smoia/auto_chtrig
vinferrer Feb 17, 2021
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
5 changes: 3 additions & 2 deletions phys2bids/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ def _get_parser():
type=int,
help='The column number of the trigger channel. '
'Channel numbering starts with 1. '
vinferrer marked this conversation as resolved.
Show resolved Hide resolved
'Default is 1.',
default=1)
'Default is 0. If chtrig is left as zero phys2bids will '
'perform an automatic trigger channel search by channel names.',
default=0)
optional.add_argument('-chsel', '--channel-selection',
dest='chsel',
nargs='*',
Expand Down
3 changes: 1 addition & 2 deletions phys2bids/phys2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def print_json(outfile, samp_freq, time_offset, ch_name):
description='The BIDS specification',
cite_module=True)
def phys2bids(filename, info=False, indir='.', outdir='.', heur_file=None,
sub=None, ses=None, chtrig=1, chsel=None, num_timepoints_expected=None,
sub=None, ses=None, chtrig=0, chsel=None, num_timepoints_expected=None,
tr=None, thr=None, pad=9, ch_name=[], yml='', debug=False, quiet=False):
"""
Run main workflow of phys2bids.
Expand Down Expand Up @@ -193,7 +193,6 @@ def phys2bids(filename, info=False, indir='.', outdir='.', heur_file=None,
indir = os.path.abspath(indir)
if chtrig < 1:
raise Exception('Wrong trigger channel. Channel indexing starts with 1!')
vinferrer marked this conversation as resolved.
Show resolved Hide resolved
vinferrer marked this conversation as resolved.
Show resolved Hide resolved

filename, ftype = utils.check_input_type(filename,
indir)

Expand Down
46 changes: 46 additions & 0 deletions phys2bids/physio_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
"""I/O objects for phys2bids."""

import logging
import re
from copy import deepcopy
from itertools import groupby

import numpy as np

TRIGGER_NAMES = ["trig", "trigger"]

LGR = logging.getLogger(__name__)


Expand Down Expand Up @@ -239,6 +242,12 @@ def __init__(self, timeseries, freq, ch_name, units, trigger_idx,
self.num_timepoints_found = deepcopy(num_timepoints_found)
self.thr = deepcopy(thr)
self.time_offset = deepcopy(time_offset)
if trigger_idx == 0:
self.auto_trigger_selection()
else:
if ch_name[trigger_idx] not in TRIGGER_NAMES:
LGR.warning('Trigger channel name is not in our trigger channel name alias list. '
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this point, but considering that:

  • we're not checking channel names (for the moment),
  • bids doesn't really require standard names for the channels, but if it did it would probably be 'trigger' for a trigger channel, so anything that returns "trig" is not necessarily BIDS standard,
  • we can add other standard names to the trigger list that might not contain "trig", like the default name in our institutions and any other institutions willing to pass it.

I wouldn't output this warning.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't make any damage

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, warnings mean that something peculiar is happening. At least I would consider downgrading this message to an info level.

vinferrer marked this conversation as resolved.
Show resolved Hide resolved
'Please make sure you choose the proper channel.')

@property
def ch_amount(self):
Expand Down Expand Up @@ -543,6 +552,43 @@ def print_info(self, filename):

LGR.info(info)

def auto_trigger_selection(self):
vinferrer marked this conversation as resolved.
Show resolved Hide resolved
"""
Find a trigger index matching the channels with a regular expresion.

Parameters
----------
self

vinferrer marked this conversation as resolved.
Show resolved Hide resolved
Raises
------
Exception
More than one possible trigger channel was automatically found.

Exception
No trigger channel automatically found

Notes
-----
Outcome:
trigger_idx : int
Automatically retrieved trigger index
"""
LGR.info('Running automatic trigger detection.')
no_numbers = [re.sub(r'\d+', '', x) for x in self.ch_name]
results = [re.search('|'.join(re.split(r'(\W)', case)),
'§'.join(TRIGGER_NAMES), re.IGNORECASE) for case in no_numbers]
indexes = [i for i, v in enumerate(results) if v]
if len(indexes) == 1:
self.trigger_idx = indexes[0]
LGR.info(f'{self.ch_name[self.trigger_idx]} selected as trigger channel')
if len(indexes) > 1:
vinferrer marked this conversation as resolved.
Show resolved Hide resolved
raise Exception('More than one possible trigger channel was automatically found. '
'Please run phys2bids specifying the -chtrig argument.')
if len(indexes) < 1:
vinferrer marked this conversation as resolved.
Show resolved Hide resolved
raise Exception('No trigger channel automatically found. Please run phys2bids '
'specifying the -chtrig argument.')


class BlueprintOutput():
"""
Expand Down
2 changes: 1 addition & 1 deletion phys2bids/tests/test_phys2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_raise_exception(samefreq_full_acq_file):

with raises(Exception) as errorinfo:
phys2bids.phys2bids(filename=test_filename, num_timepoints_expected=[70], tr=[1.3, 2],
indir=test_path, outdir=test_path)
indir=test_path, outdir=test_path, chtrig=3)
assert "doesn't match" in str(errorinfo.value)

with raises(Exception) as errorinfo:
Expand Down
37 changes: 36 additions & 1 deletion phys2bids/tests/test_physio_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import numpy as np
from pytest import raises

from phys2bids import physio_obj as po


Expand Down Expand Up @@ -260,3 +259,39 @@ def test_BlueprintOutput():

# Test __eq__
assert blueprint_out == blueprint_out


def test_auto_trigger_selection(caplog):
"""Test auto_trigger_selection."""
test_time = np.array([0, 1, 2, 3, 4])
test_trigger = np.array([0, 1, 2, 3, 4])
test_half = np.array([0, 1, 2])
test_twice = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
test_timeseries = [test_time, test_trigger, test_half, test_twice]
test_freq = [1, 1, 0.5, 2]
test_chn_name = ['time', 'trigger', 'half', 'CO2']
test_units = ['s', 'V', 'V', 'V']

# test when trigger is not the name of the channel
smoia marked this conversation as resolved.
Show resolved Hide resolved
test_chtrig = 2
phys_in = po.BlueprintInput(test_timeseries, test_freq, test_chn_name,
test_units, test_chtrig)
assert 'Trigger channel name is not' in caplog.text

# test when trigger is 0 and that the trigger channel is recognized by name:
test_chtrig = 0
phys_in = po.BlueprintInput(test_timeseries, test_freq, test_chn_name,
test_units, test_chtrig)
assert phys_in.trigger_idx == 1
# test when no trigger is found
test_chn_name = ['time', 'dummy', 'half', 'CO2']
with raises(Exception) as errorinfo:
phys_in = po.BlueprintInput(test_timeseries, test_freq, test_chn_name,
test_units, test_chtrig)
assert 'No trigger channel automatically found' in str(errorinfo.value)
# test when no trigger is found
test_chn_name = ['time', 'trigger', 'TRIGGER', 'CO2']
with raises(Exception) as errorinfo:
phys_in = po.BlueprintInput(test_timeseries, test_freq, test_chn_name,
test_units, test_chtrig)
assert 'More than one possible trigger channel' in str(errorinfo.value)