Skip to content

Commit bbcc3f9

Browse files
committed
Add add_started parsing key, i.e. use absolute timestamps for columns of interest. Add docs + minor fixes here and there
1 parent 94d9040 commit bbcc3f9

File tree

12 files changed

+65
-28
lines changed

12 files changed

+65
-28
lines changed

.github/workflows/tests.yaml renamed to .github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
if: runner.os != 'Windows'
3737
run: |
3838
if [ "$RUNNER_OS" == "Linux" ]; then
39-
sudo apt update && sudo apt install qt6-base-dev
39+
apt update && apt install qt6-base-dev
4040
mkdir dcm2niix_install/ && cd dcm2niix_install/
4141
curl -fLO https://github.com/rordenlab/dcm2niix/releases/latest/download/dcm2niix_lnx.zip
4242
unzip dcm2niix*.zip

bidscoin/bcoin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ def test_bidscoin(bidsmapfile, options: dict=None, testplugins: bool=True, testg
481481
root = tk.Tk()
482482
root.withdraw() # Don't show the window
483483
except tk.TclError as display_error:
484-
LOGGER.error(f"Cannot open a grahical display on your system:\n{display_error}")
484+
LOGGER.error(f"Cannot open a graphical display on your system:\n{display_error}")
485485
success = False
486486
try:
487487
from PyQt6.QtWidgets import QApplication, QPushButton

bidscoin/heuristics/bidsmap_bids2bids.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Options:
2020
# General BIDScoin and plugin options
2121
# --------------------------------------------------------------------------------
2222
bidscoin:
23-
version: 4.5.1.dev # BIDScoin version (should correspond with the version in pyproject.toml)
23+
version: 4.5.2.dev # BIDScoin version (should correspond with the version in pyproject.toml)
2424
subprefix: sub- # The subject prefix of the source data
2525
sesprefix: ses- # The session prefix of the source data
2626
bidsignore: [extra_data/, sub-*_ct.*] # List of entries that are added to the .bidsignore file (for more info, see BIDS specifications), e.g. [extra_data/, pet/, myfile.txt, yourfile.csv]

bidscoin/heuristics/bidsmap_dccn.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Options:
2121
# General BIDScoin and plugin options
2222
# -----------------------------------
2323
bidscoin:
24-
version: 4.5.1.dev # BIDScoin version (should correspond with the version in pyproject.toml)
24+
version: 4.5.2.dev # BIDScoin version (should correspond with the version in pyproject.toml)
2525
subprefix: sub- # The subject prefix of the source data
2626
sesprefix: ses- # The session prefix of the source data
2727
bidsignore: [extra_data/, sub-*_ct.*] # List of entries that are added to the .bidsignore file (for more info, see BIDS specifications), e.g. [extra_data/, pet/, myfile.txt, yourfile.csv]
@@ -1161,7 +1161,8 @@ Psychopy:
11611161
events: &psychopy_events
11621162
parsing: # The settings to parse the source table from the log file
11631163
table: [long-wide, pivot, 1] # The raw source table or a pivoted 'onset', 'duration', 'event_type' version
1164-
expand: scannerPulse.rt # Expands lists into columns for each array item
1164+
add_started: # Columns for which the .started time is added, i.e. that are converted to absolute timestamps (e.g. the .rt columns)
1165+
expand: # Expands lists into columns for each array item
11651166
columns: # Columns that are included in the output table, i.e. {output column: input column}
11661167
- onset: onset # The mapping for the first required column 'onset'
11671168
- duration: duration # The mapping for the second required column 'duration'

bidscoin/heuristics/bidsmap_sst.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Options:
2020
# General BIDScoin and plugin options
2121
# -----------------------------------
2222
bidscoin:
23-
version: 4.5.1.dev # BIDScoin version (should correspond with the version in pyproject.toml)
23+
version: 4.5.2.dev # BIDScoin version (should correspond with the version in pyproject.toml)
2424
subprefix: sub- # The subject prefix of the source data
2525
sesprefix: ses- # The session prefix of the source data
2626
bidsignore: [extra_data/, sub-*_ct.*] # List of entries that are added to the .bidsignore file (for more info, see BIDS specifications), e.g. [extra_data/, pet/, myfile.txt, yourfile.csv]
@@ -1163,7 +1163,8 @@ Psychopy:
11631163
events: &psychopy_events
11641164
parsing: # The settings to parse the source table from the log file
11651165
table: [long-wide, pivot, 1] # The raw source table or a pivoted 'onset', 'duration', 'event_type' version
1166-
expand: scannerPulse.rt # Expands lists into columns for each array item
1166+
add_started: # Columns for which the .started time is added, i.e. that are converted to absolute timestamps (e.g. the .rt columns)
1167+
expand: # Expands lists into columns for each array item
11671168
columns: # Columns that are included in the output table, i.e. {output column: input column}
11681169
- onset: onset # The mapping for the first required column 'onset'
11691170
- duration: duration # The mapping for the second required column 'duration'
@@ -1173,6 +1174,8 @@ Psychopy:
11731174
event_type: '.*'
11741175
time:
11751176
cols: ['(?i).*time.*', '(?i).*duration.*', '(?i).*onset.*', '(?i).*start.*', '(?i).*stop.*', '.*\.rt']
1177+
start:
1178+
event_type: # E.g. scannerPulse.rt_0
11761179

11771180
eeg: # ----------------------- All EEG run-items ---------------------------
11781181
- attributes: *psychopy_attr

bidscoin/plugins/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def eventstable(self) -> pd.DataFrame:
285285
# Add the matching rows to the grand rows group
286286
rows |= rowgroup
287287

288-
return df.loc[rows].sort_values(by='onset')
288+
return df.loc[rows].sort_values(by='onset', key=lambda x: pd.to_numeric(x, errors='coerce'))
289289

290290
@property
291291
def parsing(self) -> dict:

bidscoin/plugins/events2bids.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,21 +316,35 @@ def logtable(self) -> pd.DataFrame:
316316
"""Returns the Psychopy log-table"""
317317

318318
# Start with a fresh data frame
319-
df = self._sourcetable.copy()
319+
df: pd.DataFrame = self._sourcetable.copy()
320320
if not len(df):
321321
return df
322322

323323
# Get the table name
324324
table = self.parsing.get('table', ['long-wide', 'pivot', 1])
325325
table = table[table[-1]]
326326

327+
# Add .started, i.e. use absolute timestamps
328+
def add_started(time, started: float):
329+
try:
330+
if isinstance(time, str) and time.startswith('[') and time.endswith(']'):
331+
return [float(val) + started for val in ast.literal_eval(time)]
332+
return float(time) + started
333+
except (ValueError, TypeError, SyntaxError) as error:
334+
return time
335+
336+
# Identify columns that match the regex and have a corresponding `.started` column, and apply the add_started transformations
337+
for add2col, started in {col: f"{col.rsplit('.', 1)[0]}.started" for col in df
338+
if re.fullmatch(self.parsing.get('add_started') or '', col) and f"{col.rsplit('.', 1)[0]}.started" in df}.items():
339+
df[add2col] = df.apply(lambda row: add_started(row[add2col], row[started]), axis=1)
340+
327341
# Expand the array items in the source data, e.g. scannerPulse.rt = ["[493.15245039993897, 494.6524632999208, 496.15445999987423, 497.65245070005767, 499.1524501000531]"]
328342
try:
329343
for expand in set(col for col in df if re.fullmatch(self.parsing.get('expand') or '', col)):
330-
ds = df[expand].apply(lambda x: ast.literal_eval(x) if isinstance(x,str) and x.startswith('[') else []) # Convert string representation of lists into actual Python lists
331-
df_ = ds.apply(pd.Series).add_prefix(f"{expand}{'.started' if '.' in expand and table=='pivot' else ''}_") # Append `.started` to pivot the data into the onset column
344+
ds = df[expand].apply(lambda x: ast.literal_eval(x) if isinstance(x,str) and x.startswith('[') else x) # Convert string representation of lists into actual Python lists
345+
df_ = ds.apply(pd.Series).add_prefix(f"{expand}{'.started' if '.' in expand and table=='pivot' else ''}_") # Expand each item into its own column and append `.started` to pivot the data into the onset column
332346
if '.' in expand: # Time columns should have a `.` in their name
333-
df_ = df_.rename(columns=lambda col: re.sub(r'(.*)\.(\w+)_(\d+)', r'\1_\3.\2', col)) # Put e.g. `.rt` or `.started` back at the end
347+
df_ = df_.rename(columns=lambda col: re.sub(r'(.*)\.(\w+)_(\d+)', r'\1_\3.\2', col)) # Put e.g. `.rt` or `.started` back at the end of the column name
334348
if not df_.empty:
335349
df = pd.concat([df.drop(columns=[expand]), df_], axis=1)
336350
except (re.error, TypeError) as pattern_error:

docs/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
*All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)*
44

5-
## [4.5.1.dev]
5+
## [4.5.2.dev]
66

77
## [4.5.0] - 2025-02-05
88

docs/plugins.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ Nibabel2bids: a generic plugin for imaging data
2424

2525
The nibabel2bids plugin wraps around the versatile `nibabel <https://nipy.org/nibabel>`__ tool to convert a wide variety of data formats into NIfTI-files. Currently, the default template bidsmap is tailored to NIfTI source data only (but this can readily be extended), and BIDS sidecar files are not automatically produced by nibabel (but see the note further below). Please cite: `DOI: 10.5281/zenodo.591597 <https://doi.org/10.5281/zenodo.591597>`__
2626

27-
Events2bids: a plugin for NeuroBS Presentation log data
28-
-------------------------------------------------------
27+
Events2bids: a plugin for stimulus presentation log data
28+
--------------------------------------------------------
2929

30-
The events2bids plugin parses `NeuroBS <https://www.neurobs.com/>`__ stimulus Presentation log files to BIDS task events files. See the `workflow page <./workflow.html#stimulus-events>`__ for usage.
30+
The events2bids plugin parses `NeuroBS <https://www.neurobs.com/>`__ Presentation, `PsychoPy <https://psychopy.org/>`__, as well as generic behavioural log files to BIDS task events files. See the `workflow page <./workflow.html#stimulus-events>`__ for usage.
3131

3232
.. note::
3333
Out of the box, BIDScoin plugins typically produce sidecar files that contain metadata from the source headers. However, when such metadata is missing (e.g. as for nibabel2bids), or when it needs to be appended or overruled, then users can add sidecar files to the source data (as explained `here <./bidsmap_indepth.html#run-items>`__) or add that metadata using the bidseditor (the latter takes precedence).

docs/workflow.rst

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,24 +176,43 @@ Finally, if all BIDS output names in the main window look correct, click the [Sa
176176

177177
Stimulus events
178178
```````````````
179-
If your dataset contain (stimulus) events logfiles and you are using e.g. the `events2bids <./plugins.html#events2bids-a-plugin-for-neurobs-presentation-log-data>`__ plugin to convert them to `BIDS events <https://bids-specification.readthedocs.io/en/stable/modality-specific-files/task-events.html>`__, you will get a ``Presentation`` tab in the main window. The edit window for the run-items in there will include an additional ``BIDS output data`` table, providing a preview of the output data. ``Edit`` the output data if needed.
179+
If your dataset contain behavioural/stimulus presentation logfiles and you are using e.g. the `events2bids <./plugins.html#events2bids-a-plugin-for-stimulus-presentation-log-data>`__ plugin to convert them to `BIDS events <https://bids-specification.readthedocs.io/en/stable/modality-specific-files/task-events.html>`__, you will get a ``Presentation``, ``PsychoPy`` and/or a generic ``Logdata`` tab in the main window. If you open a run-items in there, you will get the same edit window as explained before, except that an extra ``BIDS output data`` table is appended on the right side. This table provides a preview of your log data after conversion to BIDS. Click on the ``Edit`` button next to the output table to adjust its content to your needs.
180180

181181
.. dropdown:: More details...
182182

183-
The edit window will then show the input data (left column), the mapping tables to convert the input data (middle column), and the preview of the converted output data. Tweak the mapping tables until the conversion is correct, and click on ``Done``. The mapping tables include tables for selecting the ``Columns`` and ``Rows``, along with a ``Timing`` table for calibration of the clock:
183+
The edit window displays the parsed input data on the left, BIDS conversion settings in the center, and a preview of the BIDS output on the right.
184184

185-
* The **'Columns'** table specifies which input column names are included (left) and how they should be named in the output table (right). You can add, edit and remove column names as needed
186-
* The **'Rows'** table specifies which input rows are included in the output table. A ``condition`` (left) is a dictionary with columns names as keys and regular expression patterns as values. Rows are included if the pattern matches with the column value, e.g. when an experimental condition is met. Note that the patterns within a condition act as AND operators, i.e. the more patterns you add, the less rows your include in the output table. The ``output column`` is optional and can be useful, e.g. to create a new output column or (contrast) regressor for your design matrix (see the screenshot below). Each row in the rows table defines a condition. Conditions acts as OR operators, i.e. the more conditions you add to the table, the more rows are included in the output table.
187-
* The **'Timing'** table contains settings for converting input time values to BIDS compliant output values:
185+
- **Left panel**: Configure how your raw log files are transformed into a tabular format, which is then processed using the settings from the center panel.
188186

189-
* **columns** -- A list of input column names that hold time values.
190-
* **units/sec** -- The number of source data time units per second (e.g., 10000 for clock times with a precision of 0.1 milliseconds).
191-
* **start** -- A dictionary with column names and event-codes that define the start of the run (time zero), e.g. the column name and event-code that log the scanner pulses.
187+
- For **Presentation log files**, you can select from the drop-down menu which table within your log file to use.
188+
- For **PsychoPy log files**, you can select from the drop-down menu whether to use the raw long-wide data, or to use a pivoted version in which all ``.started`` and ``.stopped`` timestamps are stored in the ``onset``, ``duration``, and ``event_type`` columns.
189+
- Additionally, for **PsychoPy**, you can specify values for ``add_started`` and ``expand`` as regex patterns to select relevant input columns.
190+
191+
- **add_started** columns: The corresponding ``.started`` times will be added to these columns. This is useful for converting relative times (e.g., ``.rt`` reaction times) into absolute timestamps.
192+
- **expand** columns: If a column contains list data (e.g., ``"['0', '2.1', '4.2']"``), it will be split into multiple new columns. These expanded columns are assumed to store time onsets and will be included when pivoting the data.
193+
194+
- **Center panel**: Define the mapping parameters that transform the input table (left panel) into the output table (right panel).
195+
The mapping parameters are organized into three tables:
196+
197+
1. **'Columns' table** – Specifies which input column names are included (left) and how they should be named in the output table (right). You can add, edit, and remove column names as needed.
198+
2. **'Rows' table** – Specifies which input rows are included in the output table. A **condition** (left) is a dictionary where column names serve as keys and values as regular expression patterns. Rows are included if the pattern matches a column value, i.e. when a specific experimental condition is met.
199+
200+
- Within a condition, patterns act as **AND** operators, meaning that the more patterns you add to the dictionary, the fewer rows are included in the output.
201+
- Between conditions, they act as **OR** operators, meaning that adding more conditions increases the number of included rows.
202+
- The **'output column'** is optional and can be used, for example, to create new output columns or contrast regressors for a design matrix (see the screenshot below).
203+
204+
3. **'Timing' table** – Contains settings for converting input time values to BIDS-compliant output values:
205+
206+
- **columns** – A list of input column names that contain time values.
207+
- **units/sec** – The number of time units per second in the source data (e.g., 10000 for clock times with a precision of 0.1 milliseconds).
208+
- **start** – A dictionary specifying column names and event codes that define the start of the run (time zero), such as the column name and event code that log scanner pulses.
192209

193210
.. figure:: ./_static/bidseditor_edit_events.png
194211
:width: 100%
195212

196-
*Edit window for conversion of Presentation log data to BIDS output data. Note that, since the first row condition has a non-selective matching pattern* ``.*``, *all input rows are included. Also note that, for selected rows, each of the two subsequent conditions add data ("go" and "stop") to the new* ``task`` *output column.*
213+
*Edit window for converting Presentation log data to BIDS output. Note that, since the first row condition uses the non-selective matching pattern* ``.*``, *all input rows are included. Additionally, for selected rows, each of the two subsequent conditions adds data ("go" and "stop") to the new* ``task`` *output column.*
214+
215+
Adjust the mapping tables until the transformation is correct, then click on **"Done"**.
197216

198217
.. tip::
199218
The BIDScoin GUI provides several tools to help you set the correct values:

0 commit comments

Comments
 (0)