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

Messages #81

Merged
merged 30 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b6d0225
modify converter and tests according to issues (#57) and (#60)
julia-pfarr Apr 10, 2024
c462c0a
tmp
Remi-Gau Apr 6, 2024
3ef8156
[pre-commit.ci] pre-commit autoupdate (#65)
pre-commit-ci[bot] Apr 9, 2024
fb37409
Merge branch 'updated-spec_julia' of github.com:bids-standard/eye2bid…
julia-pfarr Apr 10, 2024
fe7df43
ignore test ipynb
julia-pfarr Apr 10, 2024
9244429
Delete eye2bids/2eyes.ipynb
julia-pfarr Apr 10, 2024
d5bef7f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 10, 2024
b5f971a
change how json dicts are combined because tests were failing
julia-pfarr Apr 11, 2024
809e192
Merge branch 'updated-spec_julia' of github.com:bids-standard/eye2bid…
julia-pfarr Apr 11, 2024
56cca43
ignore *ipynb
julia-pfarr Apr 11, 2024
f79d237
add PhysioType metadata
julia-pfarr Apr 18, 2024
a252a5d
Merge github.com:bids-standard/eye2bids into messages
julia-pfarr May 13, 2024
e0dd90e
resolve merge residuals
julia-pfarr May 13, 2024
a4e72ee
Merge branch 'main' into messages
julia-pfarr Jun 4, 2024
3490d8d
first try for physioevents.tsv.gz
julia-pfarr Jun 5, 2024
baae3c0
save pre-final physioevents.tsv.gz code
julia-pfarr Jun 5, 2024
0277a79
add tests for physioevents.tsv
julia-pfarr Jun 14, 2024
6a3e661
change dtypes of physioevents df columns and modify test
julia-pfarr Jun 19, 2024
302c84b
Merge branch 'main' into messages
Remi-Gau Jun 26, 2024
b348bd1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 26, 2024
6a771cc
lint
Remi-Gau Jun 26, 2024
8967763
rm files
Remi-Gau Jun 26, 2024
af88daf
merge main
julia-pfarr Jun 27, 2024
ab171fb
Merge branch 'messages' of github.com:bids-standard/eye2bids into mes…
julia-pfarr Jun 27, 2024
fc3f015
fix tests
julia-pfarr Jun 27, 2024
30f14ce
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 27, 2024
c3304bc
Update tests/test_edf2bids.py
Remi-Gau Jun 28, 2024
34eb3d3
rm duplciated tests
Remi-Gau Jun 28, 2024
f32b7bd
refactor tests
Remi-Gau Jun 28, 2024
1b36fc6
refactor
Remi-Gau Jun 28, 2024
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: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ tests/data/output
*events.json
*eyetrack.json
*eyetrack.tsv

2eyes.ipynb
test.ipynb

tmp

Expand Down
146 changes: 144 additions & 2 deletions eye2bids/edf2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import gzip
import re
import subprocess
from pathlib import Path
from typing import Any
Expand Down Expand Up @@ -313,6 +314,92 @@ def _load_asc_file_as_reduced_df(events_asc_file: str | Path) -> pd.DataFrame:
return pd.DataFrame(df_ms.iloc[0:, 2:])


def _df_events_after_start(events: list[str]) -> pd.DataFrame:

start_index = next(
i for i, line in enumerate(events) if re.match(r"START\s+.*", line)
)
end_index = next(
i for i in range(len(events) - 1, -1, -1) if re.match(r"END\s+.*", events[i])
)

if end_index > start_index:
data_lines = events[start_index + 1 : end_index]
return pd.DataFrame([line.strip().split("\t") for line in data_lines])
else:
return print("No 'END' found after the selected 'START'.")


def _df_physioevents(events_after_start: pd.DataFrame) -> pd.DataFrame:
events_after_start["Event_Letters"] = (
events_after_start[0].str.extractall(r"([A-Za-z]+)").groupby(level=0).agg("".join)
)
events_after_start["Event_Numbers"] = events_after_start[0].str.extract(r"(\d+)")
events_after_start[["msg_timestamp", "message"]] = events_after_start[1].str.split(
n=1, expand=True
)
events_after_start["message"] = events_after_start["message"].astype(str)

msg_mask = events_after_start["Event_Letters"] == "MSG"
events_after_start.loc[msg_mask, "Event_Numbers"] = events_after_start.loc[
msg_mask, "msg_timestamp"
]
physioevents_reordered = (
pd.concat(
[
events_after_start["Event_Numbers"],
events_after_start[2],
events_after_start["Event_Letters"],
events_after_start["message"],
],
axis=1,
ignore_index=True,
)
.replace({None: np.nan, "None": np.nan})
.rename(columns={0: "timestamp", 1: "duration", 2: "trial_type", 3: "message"})
)
return physioevents_reordered


def _physioevents_for_eye(
physioevents_reordered: pd.DataFrame, eye: str = "L"
) -> pd.DataFrame:
physioevents_eye_list = ["MSG", f"EFIX{eye}", f"ESACC{eye}", f"EBLINK{eye}"]

physioevents = physioevents_reordered[
physioevents_reordered["trial_type"].isin(physioevents_eye_list)
]

physioevents = physioevents.replace(
{f"EFIX{eye}": "fixation", f"ESACC{eye}": "saccade", "MSG": np.nan, None: np.nan}
)

physioevents["blink"] = 0
last_non_na_trial_type = None

for i in range(len(physioevents)):
current_trial_type = physioevents.iloc[i]["trial_type"]
if pd.notna(current_trial_type):
if (
current_trial_type == "saccade"
and last_non_na_trial_type == f"EBLINK{eye}"
):
physioevents.iloc[i, physioevents.columns.get_loc("blink")] = 1
last_non_na_trial_type = current_trial_type

physioevents.loc[physioevents["trial_type"].isna(), "blink"] = np.nan
physioevents["blink"] = physioevents["blink"].astype("Int64")
physioevents = physioevents[physioevents.trial_type != f"EBLINK{eye}"]

physioevents["timestamp"] = physioevents["timestamp"].astype("Int64")
physioevents["duration"] = physioevents["duration"].astype("Int64")

physioevents = physioevents[
["timestamp", "duration", "trial_type", "blink", "message"]
]
return physioevents


def generate_physio_json(
input_file: Path,
metadata_file: str | Path | None,
Expand Down Expand Up @@ -454,7 +541,7 @@ def edf2bids(

samples = pd.read_csv(samples_asc_file, sep="\t", header=None)
samples_eye1 = (
pd.DataFrame(samples.iloc[:, 0:4])
pd.DataFrame(samples.iloc[:, [2, 1, 3, 0]])
.map(lambda x: x.strip() if isinstance(x, str) else x)
.replace(".", np.nan, regex=False)
)
Expand Down Expand Up @@ -491,7 +578,62 @@ def edf2bids(

e2b_log.info(f"file generated: {output_filename_eye2}")

# Messages and events to physioevents.tsv.gz - tbc
# %%
# Messages and events to dataframes

events_after_start = _df_events_after_start(events)
physioevents_reordered = _df_physioevents(events_after_start)
physioevents_eye1 = _physioevents_for_eye(physioevents_reordered, eye="L")
physioevents_eye2 = _physioevents_for_eye(physioevents_reordered, eye="R")

# %%
# Messages and events to physioevents.tsv.gz

if not _2eyesmode(df_ms_reduced):
output_eventsfilename_eye1 = generate_output_filename(
output_dir=output_dir,
input_file=input_file,
suffix="_recording-eye1_physioevents",
extension="tsv.gz",
)
if _extract_RecordedEye(df_ms_reduced) == "Left":
data_to_save = physioevents_eye1
elif _extract_RecordedEye(df_ms_reduced) == "Right":
data_to_save = physioevents_eye2
content = data_to_save.to_csv(sep="\t", index=False, na_rep="n/a", header=None)
with gzip.open(output_eventsfilename_eye1, "wb") as f:
f.write(content.encode())

e2b_log.info(f"file generated: {output_eventsfilename_eye1}")

else:
output_eventsfilename_eye1 = generate_output_filename(
output_dir=output_dir,
input_file=input_file,
suffix="_recording-eye1_physioevents",
extension="tsv.gz",
)
content = physioevents_eye1.to_csv(
sep="\t", index=False, na_rep="n/a", header=None
)
with gzip.open(output_eventsfilename_eye1, "wb") as f:
f.write(content.encode())

e2b_log.info(f"file generated: {output_eventsfilename_eye1}")

output_eventsfilename_eye2 = generate_output_filename(
output_dir=output_dir,
input_file=input_file,
suffix="_recording-eye2_physioevents",
extension="tsv.gz",
)
content = physioevents_eye2.to_csv(
sep="\t", index=False, na_rep="n/a", header=None
)
with gzip.open(output_eventsfilename_eye2, "wb") as f:
f.write(content.encode())

e2b_log.info(f"file generated: {output_eventsfilename_eye2}")


def generate_output_filename(
Expand Down
Loading