Skip to content

Commit

Permalink
deploy: efd2065
Browse files Browse the repository at this point in the history
  • Loading branch information
qian-chu committed Oct 6, 2024
1 parent 0ea7541 commit 0e62588
Show file tree
Hide file tree
Showing 24 changed files with 671 additions and 606 deletions.
Binary file removed _images/tutorials_read_recording_17_1.png
Binary file not shown.
Binary file added _images/tutorials_read_recording_17_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion _sources/reference/data.rst.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Classes for individual data types
=================================

.. automodule:: pyneon.data
.. automodule:: pyneon.tabular
:members:
:show-inheritance:

Expand Down
2 changes: 1 addition & 1 deletion _sources/tutorials/index.rst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ started with PyNeon.
:maxdepth: 1

read_recording
resample_and_concat
interpolate_and_concat
video
export_to_bids
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Resample Data and Concatenate Channels\n",
"# Interpolate Data and Concatenate Channels\n",
"\n",
"Informative as it is, raw Neon data is not always easy to work with. Different data streams (e.g., gaze, eye states, IMU) are sampled at different rates, don't necessarily share a common start timestamp, and within each stream data might not have been sampled at a constant rate. This tutorial demonstrates how to deal with these issues by resampling data streams and concatenating them into a single DataFrame.\n",
"Informative as it is, raw Neon data is not always easy to work with. Different data streams (e.g., gaze, eye states, IMU) are sampled at different rates, don't necessarily share a common start timestamp, and within each stream data might not have been sampled at a constant rate. This tutorial demonstrates how to deal with these issues by interpolating data streams and concatenating them into a single DataFrame.\n",
"\n",
"We will use the same ``OfficeWalk`` dataset as in the [previous tutorial](read_recording.ipynb)."
]
Expand Down Expand Up @@ -69,20 +69,10 @@
}
],
"source": [
"# Get the data points\n",
"gaze_ts = gaze.data[\"timestamp [ns]\"].values\n",
"eye_states_ts = eye_states.data[\"timestamp [ns]\"].values\n",
"imu_ts = imu.data[\"timestamp [ns]\"].values\n",
"\n",
"# Calculate the distances between subsequent data points\n",
"gaze_diff = np.diff(gaze_ts)\n",
"eye_states_diff = np.diff(eye_states_ts)\n",
"imu_diff = np.diff(imu_ts)\n",
"\n",
"# Unique values\n",
"gaze_diff_unique = np.unique(gaze_diff)\n",
"eye_states_diff_unique = np.unique(eye_states_diff)\n",
"imu_diff_unique = np.unique(imu_diff)\n",
"gaze_diff_unique = np.unique(gaze.ts_diff)\n",
"eye_states_diff_unique = np.unique(eye_states.ts_diff)\n",
"imu_diff_unique = np.unique(imu.ts_diff)\n",
"\n",
"print(\n",
" f\"Unique gaze time differences: {len(gaze_diff_unique)}, max: {np.max(gaze_diff_unique)/1e6}ms\"\n",
Expand Down Expand Up @@ -125,15 +115,15 @@
"\n",
"fig, axs = plt.subplots(3, 1, tight_layout=True)\n",
"\n",
"axs[0].hist(gaze_diff, bins=50)\n",
"axs[0].hist(gaze.ts_diff, bins=50)\n",
"axs[0].axvline(gaze_nominal_diff, color=\"red\", label=\"Nominal\")\n",
"axs[0].set_title(\"Gaze\")\n",
"\n",
"axs[1].hist(eye_states_diff, bins=50)\n",
"axs[1].hist(eye_states.ts_diff, bins=50)\n",
"axs[1].axvline(eye_states_nominal_diff, color=\"red\", label=\"Nominal\")\n",
"axs[1].set_title(\"Eye states\")\n",
"\n",
"axs[2].hist(imu_diff, bins=50)\n",
"axs[2].hist(imu.ts_diff, bins=50)\n",
"axs[2].axvline(imu_nominal_diff, color=\"red\", label=\"Nominal\")\n",
"axs[2].set_title(\"IMU\")\n",
"\n",
Expand All @@ -149,9 +139,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Resampling data streams\n",
"## Interpolating data streams\n",
"\n",
"Given the unequal sampling, if we want to obtain continuous data streams, interpolation is necessary. PyNeon uses the `scipy.interpolate.interp1d` function to interpolate data streams. We can resample each stream by simply calling `resample()` which returns a new DataFrame with the resampled data."
"Given the unequal sampling, if we want to obtain continuous data streams, interpolation is necessary. PyNeon uses the `scipy.interpolate.interp1d` function to interpolate data streams. We can interpolate each stream by simply calling `interpolate()` which returns a copy of the object with the interpolated data."
]
},
{
Expand All @@ -163,32 +153,15 @@
"name": "stdout",
"output_type": "stream",
"text": [
" timestamp [ns] time [s] gaze x [px] gaze y [px] worn fixation id \\\n",
"0 1725032224852161732 0.000 1067.486000 620.856000 True 1 \n",
"1 1725032224857161732 0.005 1066.920463 617.120061 True 1 \n",
"2 1725032224862161732 0.010 1072.699000 615.780000 True 1 \n",
"3 1725032224867161732 0.015 1067.447000 617.062000 True 1 \n",
"4 1725032224872161732 0.020 1071.564000 613.158000 True 1 \n",
"\n",
" blink id azimuth [deg] elevation [deg] \n",
"0 <NA> 16.213030 -0.748998 \n",
"1 <NA> 16.176315 -0.511927 \n",
"2 <NA> 16.546413 -0.426618 \n",
"3 <NA> 16.210049 -0.508251 \n",
"4 <NA> 16.473521 -0.260388 \n",
"Only one unique time difference: [5000000]\n"
]
}
],
"source": [
"# Resample to the nominal sampling frequency\n",
"gaze_resampled_data = gaze.interpolate()\n",
"print(gaze_resampled_data.head())\n",
"gaze_resampled = gaze.interpolate()\n",
"\n",
"ts = gaze_resampled_data[\"timestamp [ns]\"].values\n",
"ts_diffs = np.diff(ts)\n",
"ts_diff_unique = np.unique(ts_diffs)\n",
"print(f\"Only one unique time difference: {np.unique(ts_diffs)}\")"
"print(f\"Only one unique time difference: {np.unique(gaze_resampled.ts_diff)}\")"
]
},
{
Expand All @@ -211,15 +184,17 @@
"text": [
"Original gaze data length: 18769\n",
"Original IMU data length: 10919\n",
"Data length after resampling to IMU: 10919\n"
"Gaze data length after resampling to IMU: 10919\n"
]
}
],
"source": [
"print(f\"Original gaze data length: {gaze.data.shape[0]}\")\n",
"print(f\"Original IMU data length: {imu.data.shape[0]}\")\n",
"gaze_resampled_to_imu_data = gaze.interpolate(new_ts=imu.ts)\n",
"print(f\"Data length after resampling to IMU: {gaze_resampled_to_imu_data.shape[0]}\")"
"gaze_resampled_to_imu = gaze.interpolate(new_ts=imu.ts)\n",
"print(\n",
" f\"Gaze data length after resampling to IMU: {gaze_resampled_to_imu.data.shape[0]}\"\n",
")"
]
},
{
Expand All @@ -235,7 +210,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 9,
"metadata": {},
"outputs": [
{
Expand All @@ -249,48 +224,55 @@
"Using lowest sampling rate: 110 Hz (['imu'])\n",
"Using latest start timestamp: 1725032224878547732 (['imu'])\n",
"Using earliest last timestamp: 1725032319533909732 (['imu'])\n",
" timestamp [ns] time [s] gaze x [px] gaze y [px] worn fixation id \\\n",
"0 1725032224878547732 0.000000 1073.410354 611.095861 True 1 \n",
"1 1725032224887638641 0.009091 1069.801082 613.382535 True 1 \n",
"2 1725032224896729550 0.018182 1070.090109 613.439696 True 1 \n",
"3 1725032224905820459 0.027273 1069.891351 612.921757 True 1 \n",
"4 1725032224914911368 0.036364 1069.692588 612.403803 True 1 \n",
" gaze x [px] gaze y [px] worn fixation id blink id \\\n",
"1725032224878547732 1073.410354 611.095861 True 1 <NA> \n",
"1725032224887638641 1069.801082 613.382535 True 1 <NA> \n",
"1725032224896729550 1070.090109 613.439696 True 1 <NA> \n",
"1725032224905820459 1069.891351 612.921757 True 1 <NA> \n",
"1725032224914911368 1069.692588 612.403803 True 1 <NA> \n",
"\n",
" azimuth [deg] elevation [deg] pupil diameter left [mm] \\\n",
"1725032224878547732 16.591703 -0.129540 5.036588 \n",
"1725032224887638641 16.360605 -0.274666 5.093205 \n",
"1725032224896729550 16.379116 -0.278283 5.078107 \n",
"1725032224905820459 16.366379 -0.245426 5.077680 \n",
"1725032224914911368 16.353641 -0.212567 5.077253 \n",
"\n",
" blink id azimuth [deg] elevation [deg] pupil diameter left [mm] ... \\\n",
"0 <NA> 16.591703 -0.129540 5.036588 ... \n",
"1 <NA> 16.360605 -0.274666 5.093205 ... \n",
"2 <NA> 16.379116 -0.278283 5.078107 ... \n",
"3 <NA> 16.366379 -0.245426 5.077680 ... \n",
"4 <NA> 16.353641 -0.212567 5.077253 ... \n",
" pupil diameter right [mm] eyeball center left x [mm] \\\n",
"1725032224878547732 4.254749 -31.742046 \n",
"1725032224887638641 4.358431 -31.746807 \n",
"1725032224896729550 4.439704 -31.700505 \n",
"1725032224905820459 4.442760 -31.711353 \n",
"1725032224914911368 4.445815 -31.722201 \n",
"\n",
" acceleration x [g] acceleration y [g] acceleration z [g] roll [deg] \\\n",
"0 0.063477 -0.058594 0.940430 -2.550682 \n",
"1 0.057486 -0.048916 0.942273 -2.602498 \n",
"2 0.051494 -0.039238 0.944117 -2.654314 \n",
"3 0.046721 -0.045800 0.944336 -2.699777 \n",
"4 0.042113 -0.054556 0.944336 -2.744383 \n",
" ... acceleration x [g] acceleration y [g] \\\n",
"1725032224878547732 ... 0.063477 -0.058594 \n",
"1725032224887638641 ... 0.057486 -0.048916 \n",
"1725032224896729550 ... 0.051494 -0.039238 \n",
"1725032224905820459 ... 0.046721 -0.045800 \n",
"1725032224914911368 ... 0.042113 -0.054556 \n",
"\n",
" pitch [deg] yaw [deg] quaternion w quaternion x quaternion y \\\n",
"0 -4.461626 -172.335180 0.067636 -0.024792 0.037342 \n",
"1 -4.493445 -172.562393 0.065682 -0.025185 0.037639 \n",
"2 -4.525265 -172.789613 0.063728 -0.025579 0.037936 \n",
"3 -4.556964 -173.013398 0.061802 -0.025917 0.038238 \n",
"4 -4.588647 -173.236726 0.059879 -0.026247 0.038541 \n",
" acceleration z [g] roll [deg] pitch [deg] yaw [deg] \\\n",
"1725032224878547732 0.940430 -2.550682 -4.461626 -172.335180 \n",
"1725032224887638641 0.942273 -2.602498 -4.493445 -172.562393 \n",
"1725032224896729550 0.944117 -2.654314 -4.525265 -172.789613 \n",
"1725032224905820459 0.944336 -2.699777 -4.556964 -173.013398 \n",
"1725032224914911368 0.944336 -2.744383 -4.588647 -173.236726 \n",
"\n",
" quaternion z \n",
"0 -0.996703 \n",
"1 -0.996810 \n",
"2 -0.996917 \n",
"3 -0.997017 \n",
"4 -0.997115 \n",
" quaternion w quaternion x quaternion y quaternion z \n",
"1725032224878547732 0.067636 -0.024792 0.037342 -0.996703 \n",
"1725032224887638641 0.065682 -0.025185 0.037639 -0.996810 \n",
"1725032224896729550 0.063728 -0.025579 0.037936 -0.996917 \n",
"1725032224905820459 0.061802 -0.025917 0.038238 -0.997017 \n",
"1725032224914911368 0.059879 -0.026247 0.038541 -0.997115 \n",
"\n",
"[5 rows x 36 columns]\n"
"[5 rows x 34 columns]\n"
]
}
],
"source": [
"concat_data = recording.concat_streams([\"gaze\", \"eye_states\", \"imu\"])\n",
"print(concat_data.head())"
"concat_stream = recording.concat_streams([\"gaze\", \"eye_states\", \"imu\"])\n",
"print(concat_stream.data.head())"
]
},
{
Expand Down Expand Up @@ -329,8 +311,8 @@
"source": [
"start_time = 5\n",
"end_time = 5.3\n",
"start_ts = time_to_ts(start_time, concat_data)\n",
"end_ts = time_to_ts(end_time, concat_data)\n",
"start_ts = time_to_ts(start_time, concat_stream)\n",
"end_ts = time_to_ts(end_time, concat_stream)\n",
"\n",
"raw_gaze_data_slice = gaze.data[\n",
" (gaze.data[\"timestamp [ns]\"] >= start_ts) & (gaze.data[\"timestamp [ns]\"] <= end_ts)\n",
Expand All @@ -342,9 +324,9 @@
"raw_imu_data_slice = imu.data[\n",
" (imu.data[\"timestamp [ns]\"] >= start_ts) & (imu.data[\"timestamp [ns]\"] <= end_ts)\n",
"]\n",
"concat_data_slice = concat_data[\n",
" (concat_data[\"timestamp [ns]\"] >= start_ts)\n",
" & (concat_data[\"timestamp [ns]\"] <= end_ts)\n",
"concat_data_slice = concat_stream[\n",
" (concat_stream[\"timestamp [ns]\"] >= start_ts)\n",
" & (concat_stream[\"timestamp [ns]\"] <= end_ts)\n",
"]\n",
"\n",
"# plot all data in the same scatter plot\n",
Expand Down Expand Up @@ -448,7 +430,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.6"
"version": "3.12.4"
}
},
"nbformat": 4,
Expand Down
Loading

0 comments on commit 0e62588

Please sign in to comment.