Skip to content

Commit

Permalink
use timestamp as index
Browse files Browse the repository at this point in the history
  • Loading branch information
qian-chu committed Oct 4, 2024
1 parent 5ad8f84 commit cca296a
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 16 deletions.
15 changes: 13 additions & 2 deletions pyneon/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
import pandas as pd


class NeonData:
class NeonTabular:
"""
Base for Neon tabular data. It reads from a CSV file and stores the data
as a pandas DataFrame (with section and recording IDs removed).
as a pandas DataFrame (with section and recording IDs removed). The `timestamp [ns]`
(for streams) or `start timestamp [ns]` (for events) column is set as the index.
"""

def __init__(self, file: Path):
Expand All @@ -24,6 +25,16 @@ def __init__(self, file: Path):
if data["section id"].nunique() > 1:
raise ValueError(f"{file.name} contains multiple section IDs")
self.data.drop(columns=["section id"], inplace=True)

# Set the timestamp column as the index
if "timestamp [ns]" in self.data.columns:
self.data.set_index("timestamp [ns]", inplace=True)
elif "start timestamp [ns]" in self.data.columns:
self.data.set_index("start timestamp [ns]", inplace=True)
else:
raise ValueError(f"{file.name} does not contain a timestamp column")
assert pd.api.types.is_integer_dtype(self.data.index.dtype)
self.data.sort_index(inplace=True)

def __len__(self) -> int:
return self.data.shape[0]
5 changes: 2 additions & 3 deletions pyneon/events.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from .data import NeonData
from .data import NeonTabular
import pandas as pd


class NeonEV(NeonData):
class NeonEV(NeonTabular):
"""
Base for Neon event data (blinks, fixations, saccades, "events" messages).
"""
Expand Down Expand Up @@ -57,7 +57,6 @@ def __init__(self, file):
self.data = self.data.astype(
{
"saccade id": "Int32",
"start timestamp [ns]": "Int64",
"end timestamp [ns]": "Int64",
"duration [ms]": "Int64",
"amplitude [px]": float,
Expand Down
11 changes: 3 additions & 8 deletions pyneon/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
from numbers import Number
from typing import Union, Literal

from .data import NeonData
from .data import NeonTabular
from .preprocess import crop, interpolate


class NeonStream(NeonData):
class NeonStream(NeonTabular):
"""
Base for Neon continuous data (gaze, eye states, IMU).
It must contain a ``timestamp [ns]`` column.
Expand Down Expand Up @@ -52,13 +52,11 @@ def _get_attributes(self):
"""
Get attributes given self.data DataFrame.
"""
self.data.sort_values(by=["timestamp [ns]"], inplace=True)
self.timestamps = self.data["timestamp [ns]"].to_numpy()
self.timestamps = self.data.index.to_numpy()
self.ts = self.timestamps
self.first_ts = int(self.ts[0])
self.last_ts = int(self.ts[-1])
self.times = (self.ts - self.first_ts) / 1e9
self.data.set_index(pd.to_datetime(self.ts, unit="ns"), inplace=True)
self.data["time [s]"] = self.times
self.duration = float(self.times[-1] - self.times[0])
self.sampling_freq_effective = self.data.shape[0] / self.duration
Expand Down Expand Up @@ -153,7 +151,6 @@ def __init__(self, file: Path):
self.sampling_freq_nominal = int(200)
self.data = self.data.astype(
{
"timestamp [ns]": "Int64",
"gaze x [px]": float,
"gaze y [px]": float,
"worn": bool,
Expand All @@ -176,7 +173,6 @@ def __init__(self, file: Path):
self.sampling_freq_nominal = 200
self.data = self.data.astype(
{
"timestamp [ns]": "Int64",
"pupil diameter left [mm]": float,
"pupil diameter right [mm]": float,
"eyeball center left x [mm]": float,
Expand Down Expand Up @@ -206,7 +202,6 @@ def __init__(self, file: Path):
self.sampling_freq_nominal = int(110)
self.data = self.data.astype(
{
"timestamp [ns]": "Int64",
"gyro x [deg/s]": float,
"gyro y [deg/s]": float,
"gyro z [deg/s]": float,
Expand Down
6 changes: 3 additions & 3 deletions source/tutorials/read_recording.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"In this tutorial, we will show how to load a single Neon recording downloaded from [Pupil Cloud](https://docs.pupil-labs.com/neon/pupil-cloud/).\n",
"\n",
"## Reading sample data\n",
"We will use a sample recording produced by the NCC Lab called `OfficeWalk`. It's a project with 2 recordings and multiple enrichments and can be downloaded with the `get_sample_data()` function. It returns a [`Pathlib.Path`](https://docs.python.org/3/library/pathlib.html) object to the downloaded & unzipped directory."
"We will use a sample recording produced by the NCC Lab called `OfficeWalk`. It's a project with 2 recordings and multiple enrichments and can be downloaded with the `get_sample_data()` function. It returns a `Pathlib.Path` object to the downloaded & unzipped directory."
]
},
{
Expand Down Expand Up @@ -46,7 +46,7 @@
"└── OfficeWalk_STATIC-IMAGE-MAPPER_ManualMap_csv\n",
"```\n",
"\n",
"The `Timeseries Data` folder contains what PyNeon calls a [`NeonDataset`](https://ncc-brain.github.io/PyNeon/reference/dataset.html#pyneon.NeonDataset). It contains multiple recordings, each with its own `info.json` file and data files. These recordings can either be loaded individually as a [`NeonRecording`](https://ncc-brain.github.io/PyNeon/reference/recording.html#pyneon.NeonRecording) or as a wholist `NeonDataset`.\n",
"The `Timeseries Data` folder contains what PyNeon calls a `NeonDataset`. It contains multiple recordings, each with its own `info.json` file and data files. These recordings can either be loaded individually as a `NeonRecording` as a wholist `NeonDataset`.\n",
"\n",
"If loading a `NeonDataset`, specify the path to the `Timeseries Data` folder to create a `NeonDataset` object:"
]
Expand Down Expand Up @@ -451,7 +451,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "pyneon",
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
Expand Down

0 comments on commit cca296a

Please sign in to comment.