diff --git a/notebooks/01_Insert_Data.ipynb b/notebooks/01_Insert_Data.ipynb index 911c65dc0..6cb991994 100644 --- a/notebooks/01_Insert_Data.ipynb +++ b/notebooks/01_Insert_Data.ipynb @@ -667,9 +667,9 @@ "By adding diagrams together, of adding and subtracting levels, we can visualize\n", "key parts of Spyglass.\n", "\n", - "_Note:_ Notice the *Selection* tables. This is a design pattern that selects a \n", + "_Note:_ Notice the _Selection_ tables. This is a design pattern that selects a\n", "subset of upstream items for further processing. In some cases, these also pair\n", - "the selected data with processing parameters." + "the selected data with processing parameters.\n" ] }, { @@ -2147,57 +2147,16 @@ "metadata": {}, "source": [ "By default, DataJoint is cautious about deletes and will prompt before deleting.\n", - "To delete, respond `yes` in the prompt.\n" + "To delete, uncomment the cell below and respond `yes` in the prompt.\n" ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[2023-07-18 18:51:35,382][INFO]: Deleting 4 rows from `common_behav`.`_raw_position`\n", - "INFO:datajoint:Deleting 4 rows from `common_behav`.`_raw_position`\n", - "[2023-07-18 18:51:35,388][INFO]: Deleting 4 rows from `common_behav`.`position_source`\n", - "INFO:datajoint:Deleting 4 rows from `common_behav`.`position_source`\n", - "[2023-07-18 18:51:35,393][INFO]: Deleting 7 rows from `common_dio`.`_d_i_o_events`\n", - "INFO:datajoint:Deleting 7 rows from `common_dio`.`_d_i_o_events`\n", - "[2023-07-18 18:51:35,406][INFO]: Deleting 128 rows from `common_ephys`.`_electrode`\n", - "INFO:datajoint:Deleting 128 rows from `common_ephys`.`_electrode`\n", - "[2023-07-18 18:51:35,408][INFO]: Deleting 1 rows from `common_ephys`.`_electrode_group`\n", - "INFO:datajoint:Deleting 1 rows from `common_ephys`.`_electrode_group`\n", - "[2023-07-18 18:51:35,412][INFO]: Deleting 1 rows from `common_ephys`.`_raw`\n", - "INFO:datajoint:Deleting 1 rows from `common_ephys`.`_raw`\n", - "[2023-07-18 18:51:35,415][INFO]: Deleting 1 rows from `common_ephys`.`_sample_count`\n", - "INFO:datajoint:Deleting 1 rows from `common_ephys`.`_sample_count`\n", - "[2023-07-18 18:51:35,426][INFO]: Deleting 2 rows from `common_task`.`_task_epoch`\n", - "INFO:datajoint:Deleting 2 rows from `common_task`.`_task_epoch`\n", - "[2023-07-18 18:51:35,434][INFO]: Deleting 7 rows from `common_interval`.`interval_list`\n", - "INFO:datajoint:Deleting 7 rows from `common_interval`.`interval_list`\n", - "[2023-07-18 18:51:35,441][INFO]: Deleting 1 rows from `common_session`.`_session__data_acquisition_device`\n", - "INFO:datajoint:Deleting 1 rows from `common_session`.`_session__data_acquisition_device`\n", - "[2023-07-18 18:51:35,442][INFO]: Deleting 1 rows from `common_session`.`_session`\n", - "INFO:datajoint:Deleting 1 rows from `common_session`.`_session`\n", - "[2023-07-18 18:51:36,986][INFO]: Deletes committed.\n", - "INFO:datajoint:Deletes committed.\n" - ] - }, - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "session_entry.delete()" + "# session_entry.delete()" ] }, { @@ -2459,13 +2418,21 @@ } ], "source": [ - "# Let's delete the entry\n", - "(sgc.Nwbfile & {\"nwb_file_name\": nwb_copy_file_name}).delete()" + "# Uncomment to delete\n", + "# (sgc.Nwbfile & {\"nwb_file_name\": nwb_copy_file_name}).delete()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the file (ends with `_.nwb`) has not been deleted, even if the entry\n", + "was deleted above.\n" ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -2484,10 +2451,16 @@ } ], "source": [ - "# Note that the file (ends with _.nwb) has not been deleted, even though the entry is\n", "!ls $SPYGLASS_BASE_DIR/raw" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can clean these files with the `cleanup` method\n" + ] + }, { "cell_type": "code", "execution_count": 55, @@ -2502,7 +2475,6 @@ } ], "source": [ - "# We clean it up\n", "sgc.Nwbfile().cleanup(delete_files=True)" ] }, @@ -2526,7 +2498,6 @@ } ], "source": [ - "# Now the file is gone as well\n", "!ls $SPYGLASS_BASE_DIR/raw" ] }, diff --git a/notebooks/12_LFP.ipynb b/notebooks/12_LFP.ipynb index c0a3db540..10de96a89 100644 --- a/notebooks/12_LFP.ipynb +++ b/notebooks/12_LFP.ipynb @@ -71,7 +71,7 @@ "import warnings\n", "\n", "warnings.simplefilter(\"ignore\", category=DeprecationWarning)\n", - "warnings.simplefilter(\"ignore\", category=ResourceWarning)\n" + "warnings.simplefilter(\"ignore\", category=ResourceWarning)" ] }, { @@ -97,7 +97,7 @@ }, "outputs": [], "source": [ - "nwb_file_name = \"minirec20230622_.nwb\"\n" + "nwb_file_name = \"minirec20230622_.nwb\"" ] }, { @@ -282,7 +282,7 @@ ], "source": [ "sgc.FirFilterParameters().create_standard_filters()\n", - "sgc.FirFilterParameters()\n" + "sgc.FirFilterParameters()" ] }, { @@ -366,7 +366,7 @@ " .loc[:, [\"nwb_file_name\", \"electrode_id\", \"region_name\"]]\n", " .sort_values(by=\"electrode_id\")\n", ")\n", - "electrodes_df\n" + "electrodes_df" ] }, { @@ -401,7 +401,7 @@ " nwb_file_name=nwb_file_name,\n", " group_name=lfp_electrode_group_name,\n", " electrode_list=lfp_electrode_ids,\n", - ")\n" + ")" ] }, { @@ -512,7 +512,7 @@ "source": [ "lfp.lfp_electrode.LFPElectrodeGroup.LFPElectrode() & {\n", " \"nwb_file_name\": nwb_file_name\n", - "}\n" + "}" ] }, { @@ -653,7 +653,7 @@ } ], "source": [ - "sgc.IntervalList & {\"nwb_file_name\": nwb_file_name}\n" + "sgc.IntervalList & {\"nwb_file_name\": nwb_file_name}" ] }, { @@ -683,7 +683,7 @@ "sgc.IntervalList.insert1(\n", " interval_key,\n", " skip_duplicates=True,\n", - ")\n" + ")" ] }, { @@ -783,7 +783,7 @@ "sgc.IntervalList() & {\n", " \"nwb_file_name\": nwb_file_name,\n", " \"interval_list_name\": interval_list_name,\n", - "}\n" + "}" ] }, { @@ -817,7 +817,7 @@ " }\n", ")\n", "\n", - "lfp.v1.LFPSelection.insert1(lfp_s_key, skip_duplicates=True)\n" + "lfp.v1.LFPSelection.insert1(lfp_s_key, skip_duplicates=True)" ] }, { @@ -922,7 +922,7 @@ } ], "source": [ - "lfp.v1.LFPSelection() & lfp_s_key\n" + "lfp.v1.LFPSelection() & lfp_s_key" ] }, { @@ -966,7 +966,7 @@ } ], "source": [ - "lfp.v1.LFPV1().populate(lfp_s_key)\n" + "lfp.v1.LFPV1().populate(lfp_s_key)" ] }, { @@ -1083,7 +1083,7 @@ } ], "source": [ - "lfp.LFPOutput.LFPV1() & lfp_s_key\n" + "lfp.LFPOutput.LFPV1() & lfp_s_key" ] }, { @@ -1104,7 +1104,7 @@ ], "source": [ "lfp_key = {\"merge_id\": (lfp.LFPOutput.LFPV1() & lfp_s_key).fetch1(\"merge_id\")}\n", - "lfp_key\n" + "lfp_key" ] }, { @@ -1197,7 +1197,7 @@ } ], "source": [ - "lfp.LFPOutput & lfp_key\n" + "lfp.LFPOutput & lfp_key" ] }, { @@ -1316,7 +1316,7 @@ ], "source": [ "lfp_df = (lfp.LFPOutput & lfp_key).fetch1_dataframe()\n", - "lfp_df\n" + "lfp_df" ] }, { @@ -1468,7 +1468,7 @@ "sgc.common_filter.FirFilterParameters() & {\n", " \"filter_name\": filter_name,\n", " \"filter_sampling_rate\": lfp_sampling_rate,\n", - "}\n" + "}" ] }, { @@ -1595,7 +1595,7 @@ } ], "source": [ - "sgc.IntervalList()\n" + "sgc.IntervalList()" ] }, { @@ -1730,7 +1730,7 @@ " lfp_band_sampling_rate=lfp_band_sampling_rate,\n", ")\n", "\n", - "lfp_band.LFPBandSelection()\n" + "lfp_band.LFPBandSelection()" ] }, { @@ -1773,7 +1773,7 @@ " \"lfp_band_sampling_rate\": lfp_band_sampling_rate,\n", " }\n", ").fetch1(\"KEY\")\n", - "lfp_band_key\n" + "lfp_band_key" ] }, { @@ -1896,7 +1896,7 @@ } ], "source": [ - "lfp_band.LFPBandSelection() & lfp_band_key\n" + "lfp_band.LFPBandSelection() & lfp_band_key" ] }, { @@ -2028,7 +2028,7 @@ ], "source": [ "lfp_band.LFPBandV1().populate(lfp_band.LFPBandSelection() & lfp_band_key)\n", - "lfp_band.LFPBandV1()\n" + "lfp_band.LFPBandV1()" ] }, { @@ -2063,7 +2063,7 @@ "orig_elect_indices = sgc.get_electrode_indices(\n", " orig_eseries, lfp_band_electrode_ids\n", ")\n", - "orig_timestamps = np.asarray(orig_eseries.timestamps)\n" + "orig_timestamps = np.asarray(orig_eseries.timestamps)" ] }, { @@ -2076,7 +2076,7 @@ "lfp_elect_indices = sgc.get_electrode_indices(\n", " lfp_eseries, lfp_band_electrode_ids\n", ")\n", - "lfp_timestamps = np.asarray(lfp_eseries.timestamps)\n" + "lfp_timestamps = np.asarray(lfp_eseries.timestamps)" ] }, { @@ -2091,14 +2091,15 @@ "lfp_band_elect_indices = sgc.get_electrode_indices(\n", " lfp_band_eseries, lfp_band_electrode_ids\n", ")\n", - "lfp_band_timestamps = np.asarray(lfp_band_eseries.timestamps)\n" + "lfp_band_timestamps = np.asarray(lfp_band_eseries.timestamps)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Get a list of times for the first run epoch and then select a 2 second interval 100 seconds from the beginning\n" + "Get a list of times for the first run epoch and then select a 2 second interval\n", + "100 seconds from the beginning\n" ] }, { @@ -2107,7 +2108,7 @@ "metadata": {}, "outputs": [], "source": [ - "plottimes = [valid_times[0][0] + 1, valid_times[0][0] + 8]\n" + "plottimes = [valid_times[0][0] + 1, valid_times[0][0] + 8]" ] }, { @@ -2131,12 +2132,12 @@ " lfp_band_timestamps > plottimes[0],\n", " lfp_band_timestamps < plottimes[1],\n", " )\n", - ")[0]\n" + ")[0]" ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -2171,7 +2172,8 @@ "plt.xlabel(\"Time (sec)\")\n", "plt.ylabel(\"Amplitude (AD units)\")\n", "\n", - "plt.show()\n" + "# Uncomment to see plot\n", + "# plt.show()" ] }, { diff --git a/notebooks/20_Position_Trodes.ipynb b/notebooks/20_Position_Trodes.ipynb index 3af4174bf..fa765059d 100644 --- a/notebooks/20_Position_Trodes.ipynb +++ b/notebooks/20_Position_Trodes.ipynb @@ -5,7 +5,7 @@ "id": "7aaa1352", "metadata": {}, "source": [ - "# Trodes Position" + "# Trodes Position\n" ] }, { @@ -58,19 +58,10 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 18, "id": "a06e21db", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[2023-08-01 11:16:06,882][INFO]: Connecting root@localhost:3306\n", - "[2023-08-01 11:16:06,909][INFO]: Connected root@localhost:3306\n" - ] - } - ], + "outputs": [], "source": [ "import os\n", "import datajoint as dj\n", @@ -97,20 +88,40 @@ "id": "6250ed23-6c95-4f72-984d-5b8490c66c9d", "metadata": {}, "source": [ - "## Loading the data\n", - "\n", - "First, we'll grab let us make sure that the session we want to analyze is inserted into the `RawPosition` table" + "## Loading the data\n" + ] + }, + { + "cell_type": "markdown", + "id": "09aa57f7", + "metadata": {}, + "source": [ + "First, we'll grab let us make sure that the session we want to analyze is inserted into the `RawPosition` table\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "3d2a2b0b-5e13-432d-a145-7cb2736b54d0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "DataJointError", + "evalue": "fetch1 should only return one tuple. 0 tuples found", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDataJointError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m nwb_copy_file_name \u001b[39m=\u001b[39m (sgc\u001b[39m.\u001b[39;49mNwbfile \u001b[39m&\u001b[39;49m \u001b[39m'\u001b[39;49m\u001b[39mnwb_file_name LIKE \u001b[39;49m\u001b[39m\"\u001b[39;49m\u001b[39mmid\u001b[39;49m\u001b[39m%\u001b[39;49m\u001b[39m\"\u001b[39;49m\u001b[39m'\u001b[39;49m)\u001b[39m.\u001b[39;49mfetch1(\n\u001b[1;32m 2\u001b[0m \u001b[39m\"\u001b[39;49m\u001b[39mnwb_file_name\u001b[39;49m\u001b[39m\"\u001b[39;49m\n\u001b[1;32m 3\u001b[0m )\n\u001b[1;32m 4\u001b[0m sgc\u001b[39m.\u001b[39mcommon_behav\u001b[39m.\u001b[39mRawPosition() \u001b[39m&\u001b[39m {\u001b[39m\"\u001b[39m\u001b[39mnwb_file_name\u001b[39m\u001b[39m\"\u001b[39m: nwb_copy_file_name}\n", + "File \u001b[0;32m~/wrk/datajoint-python/datajoint/fetch.py:352\u001b[0m, in \u001b[0;36mFetch1.__call__\u001b[0;34m(self, squeeze, download_path, *attrs)\u001b[0m\n\u001b[1;32m 348\u001b[0m result \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_expression\u001b[39m.\u001b[39mproj(\u001b[39m*\u001b[39mattributes)\u001b[39m.\u001b[39mfetch(\n\u001b[1;32m 349\u001b[0m squeeze\u001b[39m=\u001b[39msqueeze, download_path\u001b[39m=\u001b[39mdownload_path, \u001b[39mformat\u001b[39m\u001b[39m=\u001b[39m\u001b[39m\"\u001b[39m\u001b[39marray\u001b[39m\u001b[39m\"\u001b[39m\n\u001b[1;32m 350\u001b[0m )\n\u001b[1;32m 351\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mlen\u001b[39m(result) \u001b[39m!=\u001b[39m \u001b[39m1\u001b[39m:\n\u001b[0;32m--> 352\u001b[0m \u001b[39mraise\u001b[39;00m DataJointError(\n\u001b[1;32m 353\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mfetch1 should only return one tuple. \u001b[39m\u001b[39m%d\u001b[39;00m\u001b[39m tuples found\u001b[39m\u001b[39m\"\u001b[39m \u001b[39m%\u001b[39m \u001b[39mlen\u001b[39m(result)\n\u001b[1;32m 354\u001b[0m )\n\u001b[1;32m 355\u001b[0m return_values \u001b[39m=\u001b[39m \u001b[39mtuple\u001b[39m(\n\u001b[1;32m 356\u001b[0m \u001b[39mnext\u001b[39m(to_dicts(result[\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_expression\u001b[39m.\u001b[39mprimary_key]))\n\u001b[1;32m 357\u001b[0m \u001b[39mif\u001b[39;00m is_key(attribute)\n\u001b[1;32m 358\u001b[0m \u001b[39melse\u001b[39;00m result[attribute][\u001b[39m0\u001b[39m]\n\u001b[1;32m 359\u001b[0m \u001b[39mfor\u001b[39;00m attribute \u001b[39min\u001b[39;00m attrs\n\u001b[1;32m 360\u001b[0m )\n\u001b[1;32m 361\u001b[0m ret \u001b[39m=\u001b[39m return_values[\u001b[39m0\u001b[39m] \u001b[39mif\u001b[39;00m \u001b[39mlen\u001b[39m(attrs) \u001b[39m==\u001b[39m \u001b[39m1\u001b[39m \u001b[39melse\u001b[39;00m return_values\n", + "\u001b[0;31mDataJointError\u001b[0m: fetch1 should only return one tuple. 0 tuples found" + ] + } + ], "source": [ - "nwb_file_name = \"chimi20200216_new.nwb\"\n", - "nwb_copy_file_name = sgu.nwb_helper_fn.get_nwb_copy_filename(nwb_file_name)\n", + "nwb_copy_file_name = (sgc.Nwbfile & 'nwb_file_name LIKE \"mid%\"').fetch1(\n", + " \"nwb_file_name\"\n", + ")\n", "sgc.common_behav.RawPosition() & {\"nwb_file_name\": nwb_copy_file_name}" ] }, @@ -119,37 +130,43 @@ "id": "516e6433-61f2-47a6-8ad9-bdd87cecbfe4", "metadata": {}, "source": [ - "## Setting parameters \n", - "\n", + "## Setting parameters\n" + ] + }, + { + "cell_type": "markdown", + "id": "51e4680a", + "metadata": {}, + "source": [ "Parameters are set by the `TrodesPosParams` table, with a `default` set\n", "available. To adjust the default, insert a new set into this table. The\n", "parameters are...\n", "\n", "- `max_separation`, default 9 cm: maximium acceptable distance between red and\n", - " green LEDs. \n", - " - If exceeded, the times are marked as NaNs and inferred by interpolation. \n", - " - Useful when the inferred LED position tracks a reflection instead of the\n", - " true position.\n", - "- `max_speed`, default 300.0 cm/s: maximum speed the animal can move. \n", - " - If exceeded, times are marked as NaNs and inferred by interpolation. \n", - " - Useful to prevent big jumps in position. \n", + " green LEDs.\n", + " - If exceeded, the times are marked as NaNs and inferred by interpolation.\n", + " - Useful when the inferred LED position tracks a reflection instead of the\n", + " true position.\n", + "- `max_speed`, default 300.0 cm/s: maximum speed the animal can move.\n", + " - If exceeded, times are marked as NaNs and inferred by interpolation.\n", + " - Useful to prevent big jumps in position.\n", "- `position_smoothing_duration`, default 0.100 s: LED position smoothing before\n", - " computing average position to get head position. \n", + " computing average position to get head position.\n", "- `speed_smoothing_std_dev`, default 0.100 s: standard deviation of the Gaussian\n", " kernel used to smooth the head speed.\n", "- `front_led1`, default 1 (True), use `xloc`/`yloc`: Which LED is the front LED\n", " for calculating the head direction.\n", - " - 1: LED corresponding to `xloc`, `yloc` in the `RawPosition` table is the\n", - " front, `xloc2`, `yloc2` as the back.\n", - " - 0: LED corresponding to `xloc2`, `yloc2` in the `RawPosition` table is the\n", - " front, `xloc`, `yloc` as the back.\n", + " - 1: LED corresponding to `xloc`, `yloc` in the `RawPosition` table is the\n", + " front, `xloc2`, `yloc2` as the back.\n", + " - 0: LED corresponding to `xloc2`, `yloc2` in the `RawPosition` table is the\n", + " front, `xloc`, `yloc` as the back.\n", "\n", - "We can see these defaults with `TrodesPosParams.get_default`." + "We can see these defaults with `TrodesPosParams.get_default`.\n" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "id": "99be8696-a232-451c-829f-56ef1be9a8ed", "metadata": {}, "outputs": [ @@ -178,7 +195,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "id": "db52bfce", "metadata": {}, "outputs": [ @@ -263,7 +280,7 @@ " (Total: 2)" ] }, - "execution_count": 4, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -282,7 +299,7 @@ "id": "41a0db13", "metadata": {}, "source": [ - "## Select interval" + "## Select interval\n" ] }, { @@ -291,17 +308,138 @@ "metadata": {}, "source": [ "Later, we'll pair the above parameters with an interval from our NWB file and\n", - "insert into `TrodesPosSelection`. \n", + "insert into `TrodesPosSelection`.\n", "\n", "First, let's select an interval from the `IntervalList` table.\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "3ffbf5a0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " Time intervals used for analysis\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "

nwb_file_name

\n", + " name of the NWB file\n", + "
\n", + "

interval_list_name

\n", + " descriptive name of this interval list\n", + "
\n", + "

valid_times

\n", + " numpy array with start and end times for each interval\n", + "
minirec20230622_.nwb01_s1=BLOB=
minirec20230622_.nwb01_s1_first9=BLOB=
minirec20230622_.nwb01_s1_first9 lfp band 100Hz=BLOB=
minirec20230622_.nwb02_s2=BLOB=
minirec20230622_.nwblfp_test_01_s1_first9_valid times=BLOB=
minirec20230622_.nwbminirec20230622_.nwb_01_s1_first9_0_default_hippocampus=BLOB=
minirec20230622_.nwbminirec20230622_.nwb_01_s1_first9_0_default_hippocampus_none_artifact_removed_valid_times=BLOB=
minirec20230622_.nwbpos 0 valid times=BLOB=
minirec20230622_.nwbpos 1 valid times=BLOB=
minirec20230622_.nwbpos 2 valid times=BLOB=
minirec20230622_.nwbpos 3 valid times=BLOB=
minirec20230622_.nwbraw data valid times=BLOB=
\n", + " \n", + "

Total: 12

\n", + " " + ], + "text/plain": [ + "*nwb_file_name *interval_list valid_time\n", + "+------------+ +------------+ +--------+\n", + "minirec2023062 01_s1 =BLOB= \n", + "minirec2023062 01_s1_first9 =BLOB= \n", + "minirec2023062 01_s1_first9 l =BLOB= \n", + "minirec2023062 02_s2 =BLOB= \n", + "minirec2023062 lfp_test_01_s1 =BLOB= \n", + "minirec2023062 minirec2023062 =BLOB= \n", + "minirec2023062 minirec2023062 =BLOB= \n", + "minirec2023062 pos 0 valid ti =BLOB= \n", + "minirec2023062 pos 1 valid ti =BLOB= \n", + "minirec2023062 pos 2 valid ti =BLOB= \n", + "minirec2023062 pos 3 valid ti =BLOB= \n", + "minirec2023062 raw data valid =BLOB= \n", + " (Total: 12)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "sgc.IntervalList & {\"nwb_file_name\": nwb_copy_file_name}" ] @@ -317,15 +455,129 @@ "the video itself.\n", "\n", "`fetch1_dataframe` returns the position of the LEDs as a pandas dataframe where\n", - "time is the index. " + "time is the index.\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "0de9bdcd-79d1-4099-9503-8bec1a3a4014", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
xlocyloc
time
1.687475e+09445567
1.687475e+09445567
1.687475e+09445567
1.687475e+09444568
1.687475e+09444568
.........
1.687475e+09479536
1.687475e+09480534
1.687475e+09480533
1.687475e+09481530
1.687475e+09481530
\n", + "

267 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " xloc yloc\n", + "time \n", + "1.687475e+09 445 567\n", + "1.687475e+09 445 567\n", + "1.687475e+09 445 567\n", + "1.687475e+09 444 568\n", + "1.687475e+09 444 568\n", + "... ... ...\n", + "1.687475e+09 479 536\n", + "1.687475e+09 480 534\n", + "1.687475e+09 480 533\n", + "1.687475e+09 481 530\n", + "1.687475e+09 481 530\n", + "\n", + "[267 rows x 2 columns]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "interval_list_name = \"pos 0 valid times\" # pos # is epoch # minus 1\n", "raw_position_df = (\n", @@ -343,19 +595,41 @@ "id": "6e990ae3-78c7-4ece-9229-9ebb0800276e", "metadata": {}, "source": [ - "Let's just quickly plot the two LEDs to get a sense of the inputs to the pipeline:" + "Let's just quickly plot the two LEDs to get a sense of the inputs to the pipeline:\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "df3d7797-6514-4ddc-9e5d-012a2b8a3e0c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Raw Position')" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1wAAANxCAYAAADw17gsAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAADM10lEQVR4nOzdd1QUV/8G8Gd3qVJVREUQK9iwK8WCIiooGrti7zF21ESNvcQYY+zd2LvRxIa9oYjYC9iwYAUEld7Znd8f/NzXVUBYWHaB53POnsPM3Ln3O4T3lYc7c0ckCIIAIiIiIiIiynNidRdARERERERUWDFwERERERERqQgDFxERERERkYowcBEREREREakIAxcREREREZGKMHARERERERGpCAMXERERERGRijBwERERERERqQgDFxERERERkYowcBERERVCL1++hEgkkn+2bt2qknEuXryoMM7FixdVMg4RUUGlpe4CiIgKq5cvX6JixYpZthGJRDA2NoapqSlsbW3RsGFD9OrVC3Z2dvlUZeEkEom+e9zQ0BCmpqaoVq0aGjduDE9PT9SsWTOfKiQioqKCgYuISI0EQUB0dDSio6Px6tUrnD59GgsWLICrqys2btyIChUqqLvEQkkQBMTGxiI2NhZv3rzBmTNn8Ntvv8Hd3R0bNmyApaWlukvMV3fv3sWhQ4fk2+PHj4epqana6iEiKkwYuIiI8pFEIlHYFgQBMpnsm3Znz55FvXr1cPnyZdSqVSu/yiu0vv6+A4BUKv1m34kTJ1CnTh1cvnwZNWrUyI/SNMLdu3cxZ84c+fbAgQMZuIiI8gif4SIiyifOzs5IS0tT+EilUsTExMDPzw+jR4+Gtra2vH1UVBQ6duyIlJQUNVZd8FlbW3/zfU9LS0NcXBz8/f0xfvx46OjoyNt/+vQJ7dq1Q0JCghqrzr0KFSpAEAT5Z+DAgSoZp0WLFgrjtGjRQiXjEBEVVAxcRERqZmRkBEdHR6xcuRJnzpyBrq6u/FhwcDD+/vtvNVZXeBkYGMDe3h5Lly7FxYsXUaxYMfmxV69eYdWqVWqsjoiICgsGLiIiDeLs7IzJkycr7Pv333/VVE3R4ejoiAULFijs27Vrl5qqISKiwoSBi4hIw3x965efn596CilihgwZAi2t/z3aHBAQgE+fPqmxIiIiKgy4aAYRkYapWLEijIyMEBsbCwBITExETEwMjI2Ns3V+dHQ0AgICEBQUhE+fPiElJQWmpqYoXbo07O3ti9wKfNllaGgIW1tbPHjwAED6giYhISEoUaJElufdu3cPAQEBCA8PR2pqKszNzVG5cmU4OjoqPJOXE2lpabh//z4CAwPx4cMHxMfHQ09PD6amprC2tkbNmjVRrlw5pfrWNAkJCfD19cXr16/x4cMH6Ovrw9zcHI0aNUKVKlXybBypVApfX188f/4c79+/h7GxMSpVqgRnZ2eF20mJiPKcQEREKhEcHCwAkH+cnZ2zfW65cuUUzn379m2W7R8+fCjMnDlTaNCggSAWixXO/fpTq1YtYevWrYJUKs2yT19fX4Xzzp8/n2V7b2/vb8Z69+5dlucsXrxY3lZLS0uIjo7Osn12fVmDtbV1ts9zcnJSONfX1zfDdgkJCcLvv/8uWFhYZPp9NjY2Fn766SchLCws2+NHR0cLv/zyi1CqVKks/xsCECwtLYWRI0cK4eHhGfb19c/fli1bvmnzvTEy+ly4cEGhjwsXLmR5PDNPnjwRevToIejp6WU6VtWqVYW///77uz+rn1lbW8vPHTBggCAIgiCVSoU//vgj0/9Wenp6ws8//yzExcVlawwiopziLYVERBooOjpaYdvExCTL9o6Ojpg7dy5u3bqV4TLzXwoMDMTAgQPRsWNHxMTEZNrO3t4eRkZG8u0zZ85k2e/Zs2eztS+z440aNcr2LJ6qREVFKWxn9H0PCgpCrVq1MHXqVISEhGTaV0xMDNauXYuqVavi5MmT3x07KCgIdnZ2WLRoESIiIr7b/u3bt1izZg2eP3/+3baaZu3atahZsyb279+PpKSkTNs9ffoUQ4cORdOmTfHhw4ccjxMVFYVWrVph8uTJmf63SkpKwp9//glXV9cs//dARKQs3lJIRKRhnj59iri4OPl2+fLlYWhomO3zbWxsUKNGDVSoUAFGRkYQBAERERG4e/curl+/DkEQAADe3t7o37+/wgtvv6SlpQVnZ2ccO3YMQHo4+nphiS9lFrj69++fYfuUlBRcvnxZvu3q6prdS1SJ2NhYBAUFKeyzsLBQ2H769CmaNGmi8Mu/lpYWWrZsCTs7O+jq6uL58+c4ceKE/JbQ2NhYdOjQAf/++y86dOiQ4djJycno0KEDXr9+Ld9naGiI5s2bw8bGBiYmJkhJSUFkZCQePXqEO3fu5Ek4+Px+MuGr98Fl9N6yz0QiUa7GXLp0KSZMmKCwz8zMDG5ubrC2tkZcXBxu3rwJPz8/+c/q1atX0bRpU/j7+2f7/WBpaWno0aMHLl68CACoUqUKWrRogTJlyiAxMRHXrl2Dr6+vvL2/vz8mTJjAVUGJKO+pd4KNiKjwUvaWwilTpiicN2rUqO+e07ZtW2Hjxo1CSEhIlu1evHghdOjQQaH/vXv3Ztp+2bJl8nZisVj49OlThu3ev38viEQiAYAgkUjk51hYWGTa98WLFxXq8PHx+e51ZteX/Wb3lsIlS5Z8c+vll1JTU4VGjRoptGnSpInw7Nmzb/qKjo4WBg4cqNC2RIkSmd4aum3bNoW2gwcPzvL2ypSUFOHcuXNCz549hZs3b2bYJju3FH62ZcsWhbbBwcGZtv1aTm4pvHHjhqCtrS1vKxKJhBkzZgjJyckZtrWxsVHou2fPnlnW8uUthTo6OgIAoWTJksK///6bYfszZ84IxsbGCvVk9N+TiCg3GLiIiFREmcB1+vRphV9IdXV1hefPn+dpXVKpVPDw8JCP4eDgkGnbwMBAhWs4cOBAhu127dolb+Pm5iaULVtWvv3gwYMMz5k+fbq8jYGBgZCSkpIn1ycIOQ9cfn5+QrFixRTO++233xTabN68WeF448aNhfj4+Cz7HTRo0DdBKiP9+vWTt7Gxscn2M0tZ0cTA1bx5c4W2f/75Z5Z9v3v3TrC0tFQ459KlS5m2/zJwARAMDQ2FwMDALMfYvn27wjmzZs3Ksj0RUU7xGS4iIjUSBAFRUVG4fPkyfvrpJ7i7uyM1NRVA+q1b69atQ6VKlfJ0TLFYjFmzZsm3/f398fHjxwzb1qxZE2XLlpVvZ/ZM1pf7W7dujVatWuXonObNmyu9op+yEhIScP36dXh5eaFFixZISEiQH7OwsMDYsWMV2q9YsUL+tUQiwaZNm767ut2KFSsUbkvcvXt3hs8ivX//Xv51vXr1IBYXvn+e7927h0uXLsm3HRwcMHHixCzPsbCwUPi+A8Dy5cuzPebcuXNRs2bNLNv07t0bZcqUkW9fvXo12/0TEWVH4ft/dCIiDeXj4wORSKTwEYvFKF68OJo3b45169ZBKpUCAKysrHDo0KFv3smVV+zs7BS2r127lmlbFxcX+deZhadz587Jv3Z1dVV4Hiujc2JiYnDjxg359pcBLa+9evUKWlpa33wMDAxgb2+PZcuWISUlRd7e2NgYx44dU3hu7vXr17h79658u02bNqhVq9Z3xzY0NMRPP/0k305KSsKpU6cybPfZvXv3vrvwSUF0+PBhhW0vL69sPQ/WuXNnheXhjx8/Lv+jRFZ0dHQwbNiw77aTSCRo0qSJfPvRo0ffPYeIKCcYuIiINIyzszMCAwPRsWPHHJ8bFxeHHTt2YPDgwWjYsCHKlSsHIyMjaGtrfxM2vvT27dtM+/wyPD179gwvX75UOP706VP5Yg+lS5eGnZ2dwjk+Pj5IS0tTOOfChQvycPn1GKoglUq/+WSkRYsWuHPnDurVq6ew/+tZjx9++CHbY3fp0kVhO6MXWTdu3Fj+9ePHjzFkyJBC99LlL7+HEokEHh4e2T63c+fO8q8TExMVwm9mGjRokO3FZipUqCD/OjIyMtt1ERFlBwMXEVE+kkgkCp+M+Pj4oFmzZtlaGvyztLQ0LF68GBYWFujfvz+2bNmCW7duISQkBHFxcUhLS8sycGT1S+bXYejrGasvt1u1agWRSIRy5cqhWrVqANJns65fv57pOaVKlULt2rWzfa15xdDQEJaWlmjZsiUmT56M27dv48KFCxnewvnkyROF7a8DWVaqVasGfX19+fbjx4+/aTN48GCFJei3bt2KcuXKoWPHjli5ciXu3LmTaUgsKL78HtrY2OToZcP169dX2M7oe/i1nLwY+ss/QHy5QigRUV5g4CIiyifOzs5IS0tT+MTExCAwMBDz58+Hubm5vO39+/fRunVrJCYmfrfftLQ09O7dGz///LN8KfKcyupdSJaWlrC1tZVvZxW4vgxnWd1WmFFIUxVra2sI6YtEKXxiY2Px5s0bnD9/HgsXLswyRH0dSL985ud7xGKxwn/bjMJtqVKlcPDgQYXQlZSUhKNHj2Ls2LGoX78+TE1N4e7ujuXLlyM0NDTb42uKL687J9+/jNpnZxbq61ncrKjy54+IiIGLiEiNjIyMULNmTUybNg0BAQGoUaOG/Ni9e/cwefLk7/axZMkS/PPPP/JtXV1d9O/fH7t27cLdu3cRERGBhIQEyGQyhcDxpa+3v/ZleDp37py8vUwmw4ULFzJsl1ngevfuncIMhbrfv5UdXwfZnPwyDyg+o5VZKG7VqhUCAwPx448/Krxw+rO4uDicPHkS48ePR/ny5TFkyJBMFzvRRF9ed26+f1/3RUSk6Ri4iIg0hLm5OQ4fPqxwq9Xq1auzfF4lJSVF4WXEZcqUwe3bt7Ft2zb07t0bderUgZmZGfT19RX+ip/T26a+XNTiw4cP8ppu3boln22wtbWFlZWVvF3Lli3lt036+/vLx/x6tkuVC2bkla8DUHx8fI7O//L7nVGY+szS0hLr1q3D+/fvceLECUydOhUtWrRQuCURSJ/V3Lx5M+rVq4c3b97kqBZ1+fK6c/P9+7ovIiJNx8BFRKRBqlSpgunTp8u3ZTIZpk6dmmn7y5cvIzo6Wr69cOFChVmyzHy5DHl2fBmegP+Fpq9XJ/ySsbExGjVqBABITU2Fj48PAODMmTPyNpUrV1ZYsEBTFS9eXGE7LCws2+fKZDKF5/G+7isj+vr6cHNzw4IFC3DhwgVER0fjwoULGDlypMJsz5s3bzBo0KBs16JOX153Tr5/GbXPzveQiEhTMHAREWmY8ePHK7y76eTJk/D398+w7deLObi7u2drjJs3b+aoJlNTUzRo0EC+/Tk0Zfb8Vkb7Prc9f/58ludooi+fYQOAO3fuZPvcx48fK7zj6/NiIjmhra2NFi1aYPXq1Xjw4IHCz8e5c+cQHByc4z7z25ffw6CgIIXvyffcvn1bYVuZ7yERkbowcBERaRh9ff1vnt2aO3duhm2joqIUtrP7l//9+/fnuK4vw5Gvry+ioqJw5coVAOmrL7Zs2TLLc86ePYvAwECFBR8KSuBycnJS2P76nVJZ+e+//7LsK6fKly//zaznvXv3ctXn1y+dVsWKiF9et1QqxbFjx7J97qFDh+Rf6+vro27dunlYGRGRajFwERFpoOHDh6Ns2bLy7RMnTmQ4K/X1syxfvyMrIwEBATkKDJ99GY4SExOxaNEi+eqGjRo1Ulhh7zNHR0f5AgmBgYHYtWuX/JhYLFZ4qbIms7KyUljF8PTp0wgMDPzuefHx8Vi3bp18W09PD23bts11PZUrV1bY/vLFzcr4+udIFe+i+vrdZUuXLv3uYi0AcOTIETx9+lS+7eHh8U1AJCLSZAxcREQaSE9PDz///LPCvoxmuWrWrKmwvXHjxiz7jYyMRJ8+fZSawXByclJYvGH58uXyrzObqdLR0UGzZs3k2ytWrJB/XbduXZQoUSLHdajL2LFj5V9LpVIMGTLku7fFeXl5KbxUum/fvjAzM/um3dfvKfueS5cuKWzn9jm4r8+/ceNGrvrLSO3ateHs7Czf9vf3x19//ZXlOaGhoRg9erTCvnHjxuV5bUREqsTARUSkoUaMGIHSpUvLt48ePfrNioVNmjRR+AX+r7/+wpo1azKcObh58yaaN2+OgICAHC/LDaQvN9+0aVP59pdho3Xr1pme92UY+/KcgnI74Wd9+/ZF48aN5dvXr1+Hm5tbhs9PxcbGYujQoQoBuESJEpg9e3aGfffo0QN2dnZYvnx5lqsOymQybNiwAYsXL5bvs7KyQsOGDZW4ov+pUaOGwgzl/PnzceLEiWy9By4n/vrrL4XZqV9++QWzZ89GamrqN21v374NFxcXhe9Hz5490aRJkzytiYhI1bTUXQAREWVMX18fkyZNUpjpmjdvHg4ePCjf1tXVxfTp0zF+/HgA6b+Qjxo1CsuXL4erqyvMzMwQGRkJf39/hVmL5cuXY+jQoTmuydXVVWGVQSD9nUqOjo5ZnpOT/ZpKS0sLO3fuhJOTEz58+AAgfZVIGxsbuLi4oHbt2tDR0cHz589x4sQJxMTEKJy7detWlCtXLtP+AwMDMX78eHh5eaFy5cqoV68eLC0tYWJiguTkZLx+/RoXL17Eu3fvFM5bvnw5xOLc/f1US0sLgwYNwrJlywAAISEhaNeuHYD0n8Mv+z9x4oTCrGVONGjQAH/88QcmTJgAIP39b3PmzMGaNWvg7u6O8uXLIz4+Hjdu3MCVK1cU/nBga2urcHsmEVGBIRARkUoEBwcLAOQfZ2fnHPcRFxcnlCpVSt6HSCQSAgICvmnXv39/hbEy+4hEImHBggWCIAgK+2fNmpWtem7duvVNn+7u7lmeI5PJBHNzc4VzdHV1hYSEhBx/P7Lry7Gsra3ztO/Hjx8LFStWzNb3G4BgZGQkHD9+PMs+ra2ts93f54+Ojo7w999/Z9rn1z9/W7ZsybKGuLg4oWnTpt8d98KFCwrnXbhwIcvjGVmzZo2gpaWV7Wt1cHAQIiIivtvvl9/HAQMGfLf9Z7NmzVIYj4goL/GWQiIiDWZgYICJEyfKtwVBwLx5875pt23bNqxevRplypTJsB+xWIyWLVvi3LlzWb7X63vq1auHkiVLKuz73kyVSCT65uXGTZo0+eZlvgWFra0tHjx4gN9++01hefavGRkZYcSIEQgKCvrucv07duyAl5cXatasqfCC6owYGhqiX79+ePDgAYYMGaLUNWTEwMAAFy9exN69e9GjRw/Y2NjAyMgo17NnGfnpp5/w4MEDdO/eHbq6upm2q1q1KjZu3AhfX98Mn30jIioIRIKQjSWCiIioQEhNTcW1a9dw//59REVFwdTUFGXLloW9vX2W4YCUd/fuXdy/fx8RERFITU1FqVKlUKVKFTg5OSm1ml5UVBQCAwPx4sULREREIDExEXp6eihZsiRq1KiBOnXqQE9PTwVXoh4JCQm4fPkyXr16hY8fP0JfXx/m5uZo2LAhbGxs1F0eEVGuMXARERERERGpCG8pJCIiIiIiUhEGLiIiIiIiIhVh4CIiIiIiIlIRBi4iIiIiIiIVYeAiIiIiIiJSES11F1CQyGQyhISEwMjI6LvvSSEiIiIiosJLEATExsbCwsIiy3cWMnDlQEhICKysrNRdBhERERERaYg3b97A0tIy0+MMXDlgZGQEIP2bamxsrOZqiIiIiIhIXWJiYmBlZSXPCJlh4MqBz7cRGhsbM3AREREREdF3HzXiohlEREREREQqwsBFRERERESkIgxcREREREREKsLARUREREREpCIMXERERERERCrCwEVERERERKQiDFxEREREREQqwsBFRERERESkIgxcREREREREKsLARUREREREpCIMXERERERERCrCwEVERERERKQiDFxEREREREQqwsBFRERERESkIgxcREREREREKsLARUREREREpCIMXERERERERCrCwEVERERERKQiDFxEREREREQqwsBFRERERESkIgxcREREREREKsLARUREREREpCIMXERERERERCrCwEVERERERKQiDFxEREREREQqwsBFRERERESkIgxcREREREREKsLARUREREREpCIMXERERERERCrCwEVERERERKQiWuougEgTvIx6iduht3PVx92wu7A2sUZx/eJK91FMuxhcKrpAR6KTq1qIiIiISDMwcFGRlyZLQ8MNDfEx8aO6SwEAzG0xFzOcZ6i7DCIiIiLKAwxcVOSlSFPkYcvB0gESkSTHfVx5c0X+dROrJkrV8Tr6Nd7EvMHbmLdKnU9EREREmoeBi+gLZ/qdgaGOYY7P+/Xcr/jd93cAgO9gX6XGnuczDzMvzlTqXCIiIiLSTFw0g4iIiIiISEUYuIiIiIiIiFSEgYuIiIiIiEhFGLiIiIiIiIhUhIGLiIiIiIhIRRi4iIiIiIiIVISBi4iIiIiISEUYuIiIiIiIiFSEgYuIiIiIiEhFGLiIiIiIiIhUhIGLiIiIiIhIRRi4iIiIiIiIVISBi4q8CssqyL+2WmoFQRBy3IeJrkkeVkREREREhQUDFxVZabI0iOaIEJEQId8XlRQF8VwxUqWpOeprvMN4AMDUplPzskQiIiIiKuC01F0AkTpEJ0XD9A/TTI/rzNdB5ORImOpl3uZLulq6EGblfGaMiIiIiAo3znBRkRMcGawQtlpVbJXh18X/KI4XkS/yszQiIiIiKmQYuKhI8Xvjh0orKsm3pzebjiOeR+Tbh3odwszmM+XblVdUhu9r33ytkYiIiIgKDwYuKjJ23t+JJpub/G+7807Mc5n3Tbs5LedgV5dd8u1mW5phx70d+VIjERERERUuDFxUJEw/Px39/usn374y+Ar61O6Tafvedr3hN9hPvt3/UH9MOzdNpTUSERERUeHDwEWFXsc9HfHb5d/k2y/GvoCTldN3z3O0ckTwuGD59gLfBfDY7aGSGomIiIiocGLgokJLEASUXlwaR4OOyvdFTY5CxeIVs91HBdMKiJ4SLd/2fuoNs0VmSr2ri4iIiIiKHgYuKpRSpakQzxUjPD78f/tmpMJEL+cvKDbWNUbqjP+9l+tj4kel3tVFREREREUPAxcVOlFJUdCZryPftjS2hDBLgJZY+dfOaYm1IMwSYGVsJd+nM18HkYmRuaqViIiIiAo3Bi4qVJ5/eo7ifxSXb3eu1hlvvN7kWf+vvV6ja/Wu8u0Si0rg2adnedY/ERERERUuDFxUaFx+dRlVVlaRb892no1/e/6b5+Mc6HEAc1vMlW9XXVkVPi998nwcIiIiIir4GLioUNh+bzuab20u397TdQ9mtZilsvFmOM/Avm775NsttrXA1rtbVTYeERERERVMyj/UQpQHwuLCsOHWBiSkJijdx8WXF3Ht3TX59tUhV+Fg6ZAX5WWpR80esDaxhsOm9LEGHR6EN9FvMMN5hsrHJiIiIqKCgYGL1Grp1aVY5Lcoz/p7Oe4lrE2t86y/77G3tMer8a9gvSx9zJkXZzJwEREREZEcAxepVWxKLADAwdIBTpbffxlxRpb4LwEAHPU8mq9h67PyJuUxtelU/O77e76PTURERESajYGLNIJbZTeln7k6F3wO997fg65EN4+rIiIiIiLKHS6aQUREREREpCIMXERERERERCrCwEVERERERKQiDFxEREREREQqwsBFRERERESkIgxcREREREREKsLARUREREREpCIMXERERERERCrCwEVERERERKQiDFxEREREREQqwsBFRERERESkIgxcRF8QBEGp81KkKXlcCREREREVBgxcVORpi7VhpGMEABh6dCiS05Jz3Mcy/2V5XBURERERFQYMXFTkaUu0saHDBmiLtbH/wX603dkWUUlROepDKkhVUxwRERERFWgMXEQAetXqheN9jsNIxwg+r3zQdHNTvIl+o+6yiIiIiKiAY+Ai+n+ulVxxadAllDUsiwcRD+C4yREB7wPUXRYRERERFWAMXERfqFumLq4OuYrqZtXxLvYdmm5pigvBF9RdFhEREREVUAxcRF+xNrWG72BfNC3fFDHJMXDb5Ya9gXvVXRYRERERFUAMXEQZKKFfAmf6nUHX6l2RIk2B50FP/OX3l9LLxhMRERFR0cTARZQJPS097Ou2D2MbjwUATDozCV6nvCATZGqujIiIiIgKCgYuoixIxBIsc1uGP1v/CQBYfm05eh3ohaS0JDVXRkREREQFAQMX0XeIRCJMcpqE3V12Q1usjX8e/oO2O9siMjFS3aURERERkYZj4CLKJk87T5zsexLGusa49OoSmm5pitfRr9VdFhERERFpMAYuohxwqeiCy4Muo5xROTyMeAjHTY64//6+ussiIiIiIg3FwEWUQ7VL18bVIVdRs1RNhMSGoNmWZuouiYiIiIg0FAMXkRKsTKxwedBlNLdujpjkGHWXQ0REREQaSkvdBVDRtvbmWgDAbJ/ZuPjqolJ9PPv0LA8ryr7i+sVxqu8p9P+vP/55+I98v2iOKFf93n1/N5eVEREREZGmYOAitdl+b7vC9sWXF3PVn4WRRa7Oz6mktCTsvL8TBx8dzNN+r7+7nqf9EREREZH6MHBRvhMEAb/7/o5p56cp7N/bda/SfVqbWqOmec3clpYt7+PeY+3NtVhzYw0iEiK+Od65Wmel+v3v8X8AgAF1BuSqPiIiIiLSHAxclK+kMilGHx+NdbfWfXOsZ62eaqgo+wLDA7H06lLsCtiFZGnyN8drl66Nuz/ehUik3C2F83zmYebFmdCV6Oa2VCIiIiLSEFw0g/JNQmoCuuzvgnW31kEEEVa4rVB3Sd8lCAJO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m8xatWqZksyHHnrITE5OTrVver/zEydO2Ivw0KFDU42VK1eumFu3bjUHDBhg7tmzJ92fFYULgNUoXADgBFatWmW/YjR37lzTNE1z3LhxpiTTzc3N3L17d5aPffOPeEm3XZUwTdO8du2a+cADD5iSzCeeeCLVul27dtn3nTFjRprHv1nI/Pz8zLi4OPvy8ePHm5LMFi1aZCqvIwvXmTNnTJvNZkoyR40alea+Q4cOtf/cz507l+b571SKateubUoy+/Xrd8eMacls4XJ3dzd///3329afP3/eLFq0qCnJHDBgQLr7p3dlqGvXrvbydP78efP69etmjRo1TElm3bp1b7v6FxYWZhqGYRYsWND89ddf0zxmdHS0Wbp0aVOSuWzZslTr0vudf/3116Yks2LFiun/MO6CwgXAajzDBQBOoHXr1hoyZIikG2/pW7Bggd58801J0gcffKCHH374ns/RsGFDNWvW7LblBQoU0IgRIyRJa9asUVRUlH3d4sWLJUmlS5dWv3790jzuu+++K0m6ePGi1q9fb19epEgRSdKFCxeUnJx8z/mzw9KlS5WUlCRPT0+NHDkyzW3+/e9/y8PDQ4mJiVqyZEma25QpU0bPPfdcmuvat28vSfr111+zJ/QddOrUSVWqVLlteYkSJfTiiy9Kkr7++utMH3fGjBkqX768zp8/r549e2r48OE6ePCgChcurEWLFsnNzS3V9qGhoTJNU23atFG1atXSPGbhwoXtc6etXbs2QzlujqGYmJgsP/8FAFajcAGAkxg3bpxq166t2NhY9ejRQ4mJiWrRooWGDRuWLcdv2rTpXdelpKRo37599uV79uyRJDVp0kQuLmn/T0aVKlVUqlSpVNtLUkhIiDw9PbV//3499thjCg0N1fHjx+/5e9yLm/nq1Kkjb2/vNLfx9fW1F9xbv8+t6tSpk+7PIzAwUJJy5EUOGfmdXrp0KdM/d29vby1atEg2m01r1qzR1KlTJUnTpk1ThQoVbtt+27ZtkqTVq1crICAg3c+cOXMkSSdPnsxQjrp168rPz0/h4eGqV6+epk6dqiNHjsg0zUx9HwCwEoULAJyEu7u75s6da//vPj4+9rfxpSW9P2pfffXVNLe/WYruti4yMvK2/3ynfaUbV8D+uW+FChX0xRdfqFChQtqxY4f69eunChUqqESJEurSpYu+//77HP/D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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "fig, ax = plt.subplots(1, 1, figsize=(10, 10))\n", "ax.plot(raw_position_df.xloc, raw_position_df.yloc, color=\"green\")\n", - "ax.plot(raw_position_df.xloc2, raw_position_df.yloc2, color=\"red\")\n", + "# Uncomment for multiple LEDs\n", + "# ax.plot(raw_position_df.xloc2, raw_position_df.yloc2, color=\"red\")\n", "ax.set_xlabel(\"x-position [pixels]\", fontsize=18)\n", "ax.set_ylabel(\"y-position [pixels]\", fontsize=18)\n", "ax.set_title(\"Raw Position\", fontsize=28)" @@ -366,7 +640,7 @@ "id": "3325c776", "metadata": {}, "source": [ - "## Pairing interval and parameters" + "## Pairing interval and parameters\n" ] }, { @@ -375,17 +649,17 @@ "metadata": {}, "source": [ "To associate a set of parameters with a given interval, insert them into the\n", - "`TrodesPosSelection` table." + "`TrodesPosSelection` table.\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "9757d864-942e-4f4f-b7eb-5e454ff7151b", "metadata": {}, "outputs": [], "source": [ - "sgp.TrodesPosSelection.insert1(\n", + "sgp.v1.TrodesPosSelection.insert1(\n", " {\n", " \"nwb_file_name\": nwb_copy_file_name,\n", " \"interval_list_name\": interval_list_name,\n", @@ -400,19 +674,19 @@ "id": "e2050880-e140-43d5-bb19-b847e94685cc", "metadata": {}, "source": [ - "Now let's check to see if we've inserted the parameters correctly:" + "Now let's check to see if we've inserted the parameters correctly:\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "0fcc42c3-ae95-4d56-be13-a7bc740d0dda", "metadata": {}, "outputs": [], "source": [ - "sgp.TrodesPosSelection()\n", + "sgp.v1.TrodesPosSelection()\n", "trodes_key = (\n", - " sgp.TrodesPosSelection()\n", + " sgp.v1.TrodesPosSelection()\n", " & {\n", " \"nwb_file_name\": nwb_copy_file_name,\n", " \"interval_list_name\": interval_list_name,\n", @@ -435,17 +709,49 @@ "metadata": {}, "source": [ "We can run the pipeline for our chosen interval/parameters by using the\n", - "`TrodesPos.populate`." + "`TrodesPos.populate`.\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "75412566-c9ea-4590-a7d0-c6b4af214fcf", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Computing position for: {'nwb_file_name': 'minirec20230622_.nwb', 'interval_list_name': 'pos 0 valid times', 'trodes_pos_params_name': 'default'}\n", + "Writing new NWB file minirec20230622_T3GNPZC8A2.nwb\n" + ] + }, + { + "ename": "KeyError", + "evalue": "\"['xloc2', 'yloc2'] not in index\"", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[15], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m sgp\u001b[39m.\u001b[39;49mv1\u001b[39m.\u001b[39;49mTrodesPosV1\u001b[39m.\u001b[39;49mpopulate(trodes_key)\n", + "File \u001b[0;32m~/wrk/datajoint-python/datajoint/autopopulate.py:241\u001b[0m, in \u001b[0;36mAutoPopulate.populate\u001b[0;34m(self, suppress_errors, return_exception_objects, reserve_jobs, order, limit, max_calls, display_progress, processes, make_kwargs, *restrictions)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[39mif\u001b[39;00m processes \u001b[39m==\u001b[39m \u001b[39m1\u001b[39m:\n\u001b[1;32m 238\u001b[0m \u001b[39mfor\u001b[39;00m key \u001b[39min\u001b[39;00m (\n\u001b[1;32m 239\u001b[0m tqdm(keys, desc\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m\u001b[39m__class__\u001b[39m\u001b[39m.\u001b[39m\u001b[39m__name__\u001b[39m) \u001b[39mif\u001b[39;00m display_progress \u001b[39melse\u001b[39;00m keys\n\u001b[1;32m 240\u001b[0m ):\n\u001b[0;32m--> 241\u001b[0m error \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_populate1(key, jobs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mpopulate_kwargs)\n\u001b[1;32m 242\u001b[0m \u001b[39mif\u001b[39;00m error \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m 243\u001b[0m error_list\u001b[39m.\u001b[39mappend(error)\n", + "File \u001b[0;32m~/wrk/datajoint-python/datajoint/autopopulate.py:292\u001b[0m, in \u001b[0;36mAutoPopulate._populate1\u001b[0;34m(self, key, jobs, suppress_errors, return_exception_objects, make_kwargs)\u001b[0m\n\u001b[1;32m 290\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m\u001b[39m__class__\u001b[39m\u001b[39m.\u001b[39m_allow_insert \u001b[39m=\u001b[39m \u001b[39mTrue\u001b[39;00m\n\u001b[1;32m 291\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m--> 292\u001b[0m make(\u001b[39mdict\u001b[39;49m(key), \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49m(make_kwargs \u001b[39mor\u001b[39;49;00m {}))\n\u001b[1;32m 293\u001b[0m \u001b[39mexcept\u001b[39;00m (\u001b[39mKeyboardInterrupt\u001b[39;00m, \u001b[39mSystemExit\u001b[39;00m, \u001b[39mException\u001b[39;00m) \u001b[39mas\u001b[39;00m error:\n\u001b[1;32m 294\u001b[0m \u001b[39mtry\u001b[39;00m:\n", + "File \u001b[0;32m~/wrk/spyglass/src/spyglass/position/v1/position_trodes_position.py:133\u001b[0m, in \u001b[0;36mTrodesPosV1.make\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m 131\u001b[0m \u001b[39m# calculate the processed position\u001b[39;00m\n\u001b[1;32m 132\u001b[0m spatial_series \u001b[39m=\u001b[39m raw_position[\u001b[39m\"\u001b[39m\u001b[39mraw_position\u001b[39m\u001b[39m\"\u001b[39m]\n\u001b[0;32m--> 133\u001b[0m position_info \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mcalculate_position_info_from_spatial_series(\n\u001b[1;32m 134\u001b[0m spatial_series,\n\u001b[1;32m 135\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39mmax_separation\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 136\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39mmax_speed\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 137\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39mspeed_smoothing_std_dev\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 138\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39mposition_smoothing_duration\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 139\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39morient_smoothing_std_dev\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 140\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39mled1_is_front\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 141\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39mis_upsampled\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 142\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39mupsampling_sampling_rate\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 143\u001b[0m position_info_parameters[\u001b[39m\"\u001b[39;49m\u001b[39mupsampling_interpolation_method\u001b[39;49m\u001b[39m\"\u001b[39;49m],\n\u001b[1;32m 144\u001b[0m )\n\u001b[1;32m 145\u001b[0m \u001b[39m# create nwb objects for insertion into analysis nwb file\u001b[39;00m\n\u001b[1;32m 146\u001b[0m position\u001b[39m.\u001b[39mcreate_spatial_series(\n\u001b[1;32m 147\u001b[0m name\u001b[39m=\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mposition\u001b[39m\u001b[39m\"\u001b[39m,\n\u001b[1;32m 148\u001b[0m timestamps\u001b[39m=\u001b[39mposition_info[\u001b[39m\"\u001b[39m\u001b[39mtime\u001b[39m\u001b[39m\"\u001b[39m],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 153\u001b[0m description\u001b[39m=\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mx_position, y_position\u001b[39m\u001b[39m\"\u001b[39m,\n\u001b[1;32m 154\u001b[0m )\n", + "File \u001b[0;32m~/wrk/spyglass/src/spyglass/position/v1/position_trodes_position.py:248\u001b[0m, in \u001b[0;36mTrodesPosV1.calculate_position_info_from_spatial_series\u001b[0;34m(spatial_series, max_LED_separation, max_plausible_speed, speed_smoothing_std_dev, position_smoothing_duration, orient_smoothing_std_dev, led1_is_front, is_upsampled, upsampling_sampling_rate, upsampling_interpolation_method)\u001b[0m\n\u001b[1;32m 245\u001b[0m \u001b[39m# Get spatial series properties\u001b[39;00m\n\u001b[1;32m 246\u001b[0m time \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39masarray(spatial_series\u001b[39m.\u001b[39mtimestamps) \u001b[39m# seconds\u001b[39;00m\n\u001b[1;32m 247\u001b[0m position \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39masarray(\n\u001b[0;32m--> 248\u001b[0m pd\u001b[39m.\u001b[39;49mDataFrame(\n\u001b[1;32m 249\u001b[0m spatial_series\u001b[39m.\u001b[39;49mdata,\n\u001b[1;32m 250\u001b[0m columns\u001b[39m=\u001b[39;49mspatial_series\u001b[39m.\u001b[39;49mdescription\u001b[39m.\u001b[39;49msplit(\u001b[39m\"\u001b[39;49m\u001b[39m, \u001b[39;49m\u001b[39m\"\u001b[39;49m),\n\u001b[1;32m 251\u001b[0m )\u001b[39m.\u001b[39;49mloc[:, [\u001b[39m\"\u001b[39;49m\u001b[39mxloc\u001b[39;49m\u001b[39m\"\u001b[39;49m, \u001b[39m\"\u001b[39;49m\u001b[39myloc\u001b[39;49m\u001b[39m\"\u001b[39;49m, \u001b[39m\"\u001b[39;49m\u001b[39mxloc2\u001b[39;49m\u001b[39m\"\u001b[39;49m, \u001b[39m\"\u001b[39;49m\u001b[39myloc2\u001b[39;49m\u001b[39m\"\u001b[39;49m]]\n\u001b[1;32m 252\u001b[0m ) \u001b[39m# meters\u001b[39;00m\n\u001b[1;32m 254\u001b[0m \u001b[39m# remove NaN times\u001b[39;00m\n\u001b[1;32m 255\u001b[0m is_nan_time \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39misnan(time)\n", + "File \u001b[0;32m~/miniconda3/envs/spy/lib/python3.9/site-packages/pandas/core/indexing.py:1097\u001b[0m, in \u001b[0;36m_LocationIndexer.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1095\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_is_scalar_access(key):\n\u001b[1;32m 1096\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mobj\u001b[39m.\u001b[39m_get_value(\u001b[39m*\u001b[39mkey, takeable\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_takeable)\n\u001b[0;32m-> 1097\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_getitem_tuple(key)\n\u001b[1;32m 1098\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m 1099\u001b[0m \u001b[39m# we by definition only have the 0th axis\u001b[39;00m\n\u001b[1;32m 1100\u001b[0m axis \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39maxis \u001b[39mor\u001b[39;00m \u001b[39m0\u001b[39m\n", + "File \u001b[0;32m~/miniconda3/envs/spy/lib/python3.9/site-packages/pandas/core/indexing.py:1289\u001b[0m, in \u001b[0;36m_LocIndexer._getitem_tuple\u001b[0;34m(self, tup)\u001b[0m\n\u001b[1;32m 1286\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_multi_take_opportunity(tup):\n\u001b[1;32m 1287\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_multi_take(tup)\n\u001b[0;32m-> 1289\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_getitem_tuple_same_dim(tup)\n", + "File \u001b[0;32m~/miniconda3/envs/spy/lib/python3.9/site-packages/pandas/core/indexing.py:955\u001b[0m, in \u001b[0;36m_LocationIndexer._getitem_tuple_same_dim\u001b[0;34m(self, tup)\u001b[0m\n\u001b[1;32m 952\u001b[0m \u001b[39mif\u001b[39;00m com\u001b[39m.\u001b[39mis_null_slice(key):\n\u001b[1;32m 953\u001b[0m \u001b[39mcontinue\u001b[39;00m\n\u001b[0;32m--> 955\u001b[0m retval \u001b[39m=\u001b[39m \u001b[39mgetattr\u001b[39;49m(retval, \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mname)\u001b[39m.\u001b[39;49m_getitem_axis(key, axis\u001b[39m=\u001b[39;49mi)\n\u001b[1;32m 956\u001b[0m \u001b[39m# We should never have retval.ndim < self.ndim, as that should\u001b[39;00m\n\u001b[1;32m 957\u001b[0m \u001b[39m# be handled by the _getitem_lowerdim call above.\u001b[39;00m\n\u001b[1;32m 958\u001b[0m \u001b[39massert\u001b[39;00m retval\u001b[39m.\u001b[39mndim \u001b[39m==\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mndim\n", + "File \u001b[0;32m~/miniconda3/envs/spy/lib/python3.9/site-packages/pandas/core/indexing.py:1332\u001b[0m, in \u001b[0;36m_LocIndexer._getitem_axis\u001b[0;34m(self, key, axis)\u001b[0m\n\u001b[1;32m 1329\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mhasattr\u001b[39m(key, \u001b[39m\"\u001b[39m\u001b[39mndim\u001b[39m\u001b[39m\"\u001b[39m) \u001b[39mand\u001b[39;00m key\u001b[39m.\u001b[39mndim \u001b[39m>\u001b[39m \u001b[39m1\u001b[39m:\n\u001b[1;32m 1330\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mValueError\u001b[39;00m(\u001b[39m\"\u001b[39m\u001b[39mCannot index with multidimensional key\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m-> 1332\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_getitem_iterable(key, axis\u001b[39m=\u001b[39;49maxis)\n\u001b[1;32m 1334\u001b[0m \u001b[39m# nested tuple slicing\u001b[39;00m\n\u001b[1;32m 1335\u001b[0m \u001b[39mif\u001b[39;00m is_nested_tuple(key, labels):\n", + "File \u001b[0;32m~/miniconda3/envs/spy/lib/python3.9/site-packages/pandas/core/indexing.py:1272\u001b[0m, in \u001b[0;36m_LocIndexer._getitem_iterable\u001b[0;34m(self, key, axis)\u001b[0m\n\u001b[1;32m 1269\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_validate_key(key, axis)\n\u001b[1;32m 1271\u001b[0m \u001b[39m# A collection of keys\u001b[39;00m\n\u001b[0;32m-> 1272\u001b[0m keyarr, indexer \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_get_listlike_indexer(key, axis)\n\u001b[1;32m 1273\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mobj\u001b[39m.\u001b[39m_reindex_with_indexers(\n\u001b[1;32m 1274\u001b[0m {axis: [keyarr, indexer]}, copy\u001b[39m=\u001b[39m\u001b[39mTrue\u001b[39;00m, allow_dups\u001b[39m=\u001b[39m\u001b[39mTrue\u001b[39;00m\n\u001b[1;32m 1275\u001b[0m )\n", + "File \u001b[0;32m~/miniconda3/envs/spy/lib/python3.9/site-packages/pandas/core/indexing.py:1462\u001b[0m, in \u001b[0;36m_LocIndexer._get_listlike_indexer\u001b[0;34m(self, key, axis)\u001b[0m\n\u001b[1;32m 1459\u001b[0m ax \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mobj\u001b[39m.\u001b[39m_get_axis(axis)\n\u001b[1;32m 1460\u001b[0m axis_name \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mobj\u001b[39m.\u001b[39m_get_axis_name(axis)\n\u001b[0;32m-> 1462\u001b[0m keyarr, indexer \u001b[39m=\u001b[39m ax\u001b[39m.\u001b[39;49m_get_indexer_strict(key, axis_name)\n\u001b[1;32m 1464\u001b[0m \u001b[39mreturn\u001b[39;00m keyarr, indexer\n", + "File \u001b[0;32m~/miniconda3/envs/spy/lib/python3.9/site-packages/pandas/core/indexes/base.py:5876\u001b[0m, in \u001b[0;36mIndex._get_indexer_strict\u001b[0;34m(self, key, axis_name)\u001b[0m\n\u001b[1;32m 5873\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m 5874\u001b[0m keyarr, indexer, new_indexer \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_reindex_non_unique(keyarr)\n\u001b[0;32m-> 5876\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_raise_if_missing(keyarr, indexer, axis_name)\n\u001b[1;32m 5878\u001b[0m keyarr \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mtake(indexer)\n\u001b[1;32m 5879\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39misinstance\u001b[39m(key, Index):\n\u001b[1;32m 5880\u001b[0m \u001b[39m# GH 42790 - Preserve name from an Index\u001b[39;00m\n", + "File \u001b[0;32m~/miniconda3/envs/spy/lib/python3.9/site-packages/pandas/core/indexes/base.py:5938\u001b[0m, in \u001b[0;36mIndex._raise_if_missing\u001b[0;34m(self, key, indexer, axis_name)\u001b[0m\n\u001b[1;32m 5935\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mKeyError\u001b[39;00m(\u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mNone of [\u001b[39m\u001b[39m{\u001b[39;00mkey\u001b[39m}\u001b[39;00m\u001b[39m] are in the [\u001b[39m\u001b[39m{\u001b[39;00maxis_name\u001b[39m}\u001b[39;00m\u001b[39m]\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[1;32m 5937\u001b[0m not_found \u001b[39m=\u001b[39m \u001b[39mlist\u001b[39m(ensure_index(key)[missing_mask\u001b[39m.\u001b[39mnonzero()[\u001b[39m0\u001b[39m]]\u001b[39m.\u001b[39munique())\n\u001b[0;32m-> 5938\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mKeyError\u001b[39;00m(\u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m{\u001b[39;00mnot_found\u001b[39m}\u001b[39;00m\u001b[39m not in index\u001b[39m\u001b[39m\"\u001b[39m)\n", + "\u001b[0;31mKeyError\u001b[0m: \"['xloc2', 'yloc2'] not in index\"" + ] + } + ], "source": [ - "sgp.TrodesPos.populate(trodes_key)" + "sgp.v1.TrodesPosV1.populate(trodes_key)" ] }, { @@ -570,11 +876,12 @@ "To retrieve the results as a pandas DataFrame with time as the index, we use `IntervalPositionInfo.fetch1_dataframe`.\n", "\n", "This dataframe has the following columns:\n", + "\n", "- `head_position_{x,y}`: X or Y position of the head in cm.\n", "- `head_orientation`: Direction of the head relative to the bottom left corner\n", " in radians\n", "- `head_velocity_{x,y}`: Directional change in head position over time in cm/s\n", - "- `head_speed`: the magnitude of the change in head position over time in cm/s" + "- `head_speed`: the magnitude of the change in head position over time in cm/s\n" ] }, { @@ -787,7 +1094,7 @@ "id": "4d7b3667-5ec1-42c1-8794-db88155aadec", "metadata": {}, "source": [ - "`.index` on the pandas dataframe gives us timestamps." + "`.index` on the pandas dataframe gives us timestamps.\n" ] }, { @@ -837,7 +1144,7 @@ "\n", "### Plots\n", "\n", - "Let's plot some of the variables first:" + "Let's plot some of the variables first:\n" ] }, { @@ -960,7 +1267,7 @@ "### Video\n", "\n", "These look reasonable but we can visualize further by plotting the results on\n", - " the video, which will appear in the current working directory." + "the video, which will appear in the current working directory.\n" ] }, { @@ -1017,7 +1324,7 @@ "source": [ "## Upsampling position\n", "\n", - "Sometimes we need the position data in smaller in time bins, which can be \n", + "Sometimes we need the position data in smaller in time bins, which can be\n", "achieved with upsampling using the following parameters.\n", "\n", "- `is_upsampled`, default 0 (False): If 1, perform upsampling.\n", @@ -1025,7 +1332,7 @@ " 33 Hz video might be upsampled to 500 Hz).\n", "- `upsampling_interpolation_method`, default linear: interpolation method. See\n", " [pandas.DataFrame.interpolate](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.interpolate.html)\n", - " for alternate methods." + " for alternate methods.\n" ] }, { diff --git a/notebooks/py_scripts/01_Insert_Data.py b/notebooks/py_scripts/01_Insert_Data.py index 3c8143882..d8c0daa8b 100644 --- a/notebooks/py_scripts/01_Insert_Data.py +++ b/notebooks/py_scripts/01_Insert_Data.py @@ -92,9 +92,10 @@ # By adding diagrams together, of adding and subtracting levels, we can visualize # key parts of Spyglass. # -# _Note:_ Notice the *Selection* tables. This is a design pattern that selects a +# _Note:_ Notice the _Selection_ tables. This is a design pattern that selects a # subset of upstream items for further processing. In some cases, these also pair # the selected data with processing parameters. +# # ## Example data # @@ -307,10 +308,12 @@ session_entry # By default, DataJoint is cautious about deletes and will prompt before deleting. -# To delete, respond `yes` in the prompt. +# To delete, uncomment the cell below and respond `yes` in the prompt. # -session_entry.delete() +# + +# session_entry.delete() +# - # We can check that delete worked, both for `Session` and `IntervalList` # @@ -332,16 +335,22 @@ # _Note:_ this also applies to deleting files from `AnalysisNwbfile` table. # -# Let's delete the entry -(sgc.Nwbfile & {"nwb_file_name": nwb_copy_file_name}).delete() +# + +# Uncomment to delete +# (sgc.Nwbfile & {"nwb_file_name": nwb_copy_file_name}).delete() +# - + +# Note that the file (ends with `_.nwb`) has not been deleted, even if the entry +# was deleted above. +# -# Note that the file (ends with _.nwb) has not been deleted, even though the entry is # !ls $SPYGLASS_BASE_DIR/raw -# We clean it up +# We can clean these files with the `cleanup` method +# + sgc.Nwbfile().cleanup(delete_files=True) -# Now the file is gone as well # !ls $SPYGLASS_BASE_DIR/raw # In the [next notebook](./02_Spike_Sorting.ipynb), we'll dive into the Spike diff --git a/notebooks/py_scripts/12_LFP.py b/notebooks/py_scripts/12_LFP.py index 68efaa5bc..b62f5b5fa 100644 --- a/notebooks/py_scripts/12_LFP.py +++ b/notebooks/py_scripts/12_LFP.py @@ -53,7 +53,6 @@ warnings.simplefilter("ignore", category=DeprecationWarning) warnings.simplefilter("ignore", category=ResourceWarning) - # - # ## Select data @@ -64,7 +63,6 @@ nwb_file_name = "minirec20230622_.nwb" - # ## Create Filters # @@ -74,7 +72,6 @@ sgc.FirFilterParameters().create_standard_filters() sgc.FirFilterParameters() - # ## Electrode Group # @@ -93,7 +90,6 @@ ) electrodes_df - # For a larger dataset, we might want to filter by region, but our example # data only has one electrode. # @@ -117,7 +113,6 @@ group_name=lfp_electrode_group_name, electrode_list=lfp_electrode_ids, ) - # - # We can verify the electrode list as follows @@ -127,7 +122,6 @@ "nwb_file_name": nwb_file_name } - # ## `IntervalList` # @@ -138,7 +132,6 @@ sgc.IntervalList & {"nwb_file_name": nwb_file_name} - # + n = 9 orig_interval_list_name = "01_s1" @@ -162,7 +155,6 @@ interval_key, skip_duplicates=True, ) - # - sgc.IntervalList() & { @@ -170,7 +162,6 @@ "interval_list_name": interval_list_name, } - # ## `LFPSelection` # @@ -189,12 +180,10 @@ ) lfp.v1.LFPSelection.insert1(lfp_s_key, skip_duplicates=True) - # - lfp.v1.LFPSelection() & lfp_s_key - # ## Populate LFP # @@ -210,27 +199,22 @@ lfp.v1.LFPV1().populate(lfp_s_key) - # We can now look at the LFP table to see the data we've extracted # lfp.LFPOutput.LFPV1() & lfp_s_key - lfp_key = {"merge_id": (lfp.LFPOutput.LFPV1() & lfp_s_key).fetch1("merge_id")} lfp_key - lfp.LFPOutput & lfp_key - # From the Merge Table, we can get the keys for the LFP data we want to see # lfp_df = (lfp.LFPOutput & lfp_key).fetch1_dataframe() lfp_df - # ## LFP Band # # Now that we've created the LFP object we can perform a second level of filtering for a band of interest, in this case the theta band. We first need to create the filter. @@ -255,12 +239,10 @@ "filter_name": filter_name, "filter_sampling_rate": lfp_sampling_rate, } - # - sgc.IntervalList() - # We can specify electrodes of interest, and desired sampling rate. # @@ -281,7 +263,6 @@ ) lfp_band.LFPBandSelection() - # - # Next we add an entry for the LFP Band and the electrodes we want to filter @@ -297,17 +278,14 @@ ).fetch1("KEY") lfp_band_key - # Check to make sure it worked # lfp_band.LFPBandSelection() & lfp_band_key - lfp_band.LFPBandV1().populate(lfp_band.LFPBandSelection() & lfp_band_key) lfp_band.LFPBandV1() - # ## Plotting # @@ -327,14 +305,12 @@ ) orig_timestamps = np.asarray(orig_eseries.timestamps) - lfp_eseries = lfp.LFPOutput.fetch_nwb(lfp_key)[0]["lfp"] lfp_elect_indices = sgc.get_electrode_indices( lfp_eseries, lfp_band_electrode_ids ) lfp_timestamps = np.asarray(lfp_eseries.timestamps) - lfp_band_eseries = (lfp_band.LFPBandV1 & lfp_band_key).fetch_nwb()[0][ "lfp_band" ] @@ -343,13 +319,12 @@ ) lfp_band_timestamps = np.asarray(lfp_band_eseries.timestamps) - -# Get a list of times for the first run epoch and then select a 2 second interval 100 seconds from the beginning +# Get a list of times for the first run epoch and then select a 2 second interval +# 100 seconds from the beginning # plottimes = [valid_times[0][0] + 1, valid_times[0][0] + 8] - # + # get the time indices for each dataset orig_time_ind = np.where( @@ -368,7 +343,6 @@ ) )[0] - # + import matplotlib.pyplot as plt @@ -390,8 +364,8 @@ plt.xlabel("Time (sec)") plt.ylabel("Amplitude (AD units)") -plt.show() - +# Uncomment to see plot +# plt.show() # - # ## Next Steps diff --git a/notebooks/py_scripts/20_Position_Trodes.py b/notebooks/py_scripts/20_Position_Trodes.py index bb6faf809..e1e00dcfb 100644 --- a/notebooks/py_scripts/20_Position_Trodes.py +++ b/notebooks/py_scripts/20_Position_Trodes.py @@ -13,6 +13,7 @@ # --- # # Trodes Position +# # ## Overview # @@ -70,38 +71,43 @@ # ## Loading the data # + # First, we'll grab let us make sure that the session we want to analyze is inserted into the `RawPosition` table +# -nwb_file_name = "chimi20200216_new.nwb" -nwb_copy_file_name = sgu.nwb_helper_fn.get_nwb_copy_filename(nwb_file_name) +nwb_copy_file_name = (sgc.Nwbfile & 'nwb_file_name LIKE "mid%"').fetch1( + "nwb_file_name" +) sgc.common_behav.RawPosition() & {"nwb_file_name": nwb_copy_file_name} # ## Setting parameters # + # Parameters are set by the `TrodesPosParams` table, with a `default` set # available. To adjust the default, insert a new set into this table. The # parameters are... # # - `max_separation`, default 9 cm: maximium acceptable distance between red and # green LEDs. -# - If exceeded, the times are marked as NaNs and inferred by interpolation. -# - Useful when the inferred LED position tracks a reflection instead of the -# true position. +# - If exceeded, the times are marked as NaNs and inferred by interpolation. +# - Useful when the inferred LED position tracks a reflection instead of the +# true position. # - `max_speed`, default 300.0 cm/s: maximum speed the animal can move. -# - If exceeded, times are marked as NaNs and inferred by interpolation. -# - Useful to prevent big jumps in position. +# - If exceeded, times are marked as NaNs and inferred by interpolation. +# - Useful to prevent big jumps in position. # - `position_smoothing_duration`, default 0.100 s: LED position smoothing before # computing average position to get head position. # - `speed_smoothing_std_dev`, default 0.100 s: standard deviation of the Gaussian # kernel used to smooth the head speed. # - `front_led1`, default 1 (True), use `xloc`/`yloc`: Which LED is the front LED # for calculating the head direction. -# - 1: LED corresponding to `xloc`, `yloc` in the `RawPosition` table is the -# front, `xloc2`, `yloc2` as the back. -# - 0: LED corresponding to `xloc2`, `yloc2` in the `RawPosition` table is the -# front, `xloc`, `yloc` as the back. +# - 1: LED corresponding to `xloc`, `yloc` in the `RawPosition` table is the +# front, `xloc2`, `yloc2` as the back. +# - 0: LED corresponding to `xloc2`, `yloc2` in the `RawPosition` table is the +# front, `xloc`, `yloc` as the back. # # We can see these defaults with `TrodesPosParams.get_default`. +# # + from pprint import pprint @@ -118,6 +124,7 @@ sgp.v1.TrodesPosParams() # ## Select interval +# # Later, we'll pair the above parameters with an interval from our NWB file and # insert into `TrodesPosSelection`. @@ -134,6 +141,7 @@ # # `fetch1_dataframe` returns the position of the LEDs as a pandas dataframe where # time is the index. +# interval_list_name = "pos 0 valid times" # pos # is epoch # minus 1 raw_position_df = ( @@ -146,20 +154,24 @@ raw_position_df # Let's just quickly plot the two LEDs to get a sense of the inputs to the pipeline: +# fig, ax = plt.subplots(1, 1, figsize=(10, 10)) ax.plot(raw_position_df.xloc, raw_position_df.yloc, color="green") -ax.plot(raw_position_df.xloc2, raw_position_df.yloc2, color="red") +# Uncomment for multiple LEDs +# ax.plot(raw_position_df.xloc2, raw_position_df.yloc2, color="red") ax.set_xlabel("x-position [pixels]", fontsize=18) ax.set_ylabel("y-position [pixels]", fontsize=18) ax.set_title("Raw Position", fontsize=28) # ## Pairing interval and parameters +# # To associate a set of parameters with a given interval, insert them into the # `TrodesPosSelection` table. +# -sgp.TrodesPosSelection.insert1( +sgp.v1.TrodesPosSelection.insert1( { "nwb_file_name": nwb_copy_file_name, "interval_list_name": interval_list_name, @@ -169,10 +181,11 @@ ) # Now let's check to see if we've inserted the parameters correctly: +# -sgp.TrodesPosSelection() +sgp.v1.TrodesPosSelection() trodes_key = ( - sgp.TrodesPosSelection() + sgp.v1.TrodesPosSelection() & { "nwb_file_name": nwb_copy_file_name, "interval_list_name": interval_list_name, @@ -185,8 +198,9 @@ # We can run the pipeline for our chosen interval/parameters by using the # `TrodesPos.populate`. +# -sgp.TrodesPos.populate(trodes_key) +sgp.v1.TrodesPosV1.populate(trodes_key) # Each NWB file, interval, and parameter set is now associated with a new analysis file and object ID. # @@ -196,11 +210,13 @@ # To retrieve the results as a pandas DataFrame with time as the index, we use `IntervalPositionInfo.fetch1_dataframe`. # # This dataframe has the following columns: +# # - `head_position_{x,y}`: X or Y position of the head in cm. # - `head_orientation`: Direction of the head relative to the bottom left corner # in radians # - `head_velocity_{x,y}`: Directional change in head position over time in cm/s # - `head_speed`: the magnitude of the change in head position over time in cm/s +# position_info = ( sgc.IntervalPositionInfo() @@ -213,6 +229,7 @@ position_info # `.index` on the pandas dataframe gives us timestamps. +# position_info.index @@ -224,6 +241,7 @@ # ### Plots # # Let's plot some of the variables first: +# fig, ax = plt.subplots(1, 1, figsize=(10, 10)) ax.plot(position_info.head_position_x, position_info.head_position_y) @@ -247,7 +265,8 @@ # ### Video # # These look reasonable but we can visualize further by plotting the results on -# the video, which will appear in the current working directory. +# the video, which will appear in the current working directory. +# # + from spyglass.common.common_position import PositionVideo @@ -272,6 +291,7 @@ # - `upsampling_interpolation_method`, default linear: interpolation method. See # [pandas.DataFrame.interpolate](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.interpolate.html) # for alternate methods. +# # + sgc.PositionInfoParameters.insert1( diff --git a/src/spyglass/data_import/insert_sessions.py b/src/spyglass/data_import/insert_sessions.py index 0a80d0a52..c732151c9 100644 --- a/src/spyglass/data_import/insert_sessions.py +++ b/src/spyglass/data_import/insert_sessions.py @@ -39,7 +39,7 @@ def insert_sessions(nwb_file_names: Union[str, List[str]]): if len(Nwbfile() & {"nwb_file_name": out_nwb_file_name}): warnings.warn( f"Cannot insert data from {nwb_file_name}: {out_nwb_file_name}" - + "is already in Nwbfile table." + + " is already in Nwbfile table." ) continue diff --git a/src/spyglass/position/v1/position_trodes_position.py b/src/spyglass/position/v1/position_trodes_position.py index 68f43526e..f20359c98 100644 --- a/src/spyglass/position/v1/position_trodes_position.py +++ b/src/spyglass/position/v1/position_trodes_position.py @@ -132,15 +132,7 @@ def make(self, key): spatial_series = raw_position["raw_position"] position_info = self.calculate_position_info_from_spatial_series( spatial_series, - position_info_parameters["max_separation"], - position_info_parameters["max_speed"], - position_info_parameters["speed_smoothing_std_dev"], - position_info_parameters["position_smoothing_duration"], - position_info_parameters["orient_smoothing_std_dev"], - position_info_parameters["led1_is_front"], - position_info_parameters["is_upsampled"], - position_info_parameters["upsampling_sampling_rate"], - position_info_parameters["upsampling_interpolation_method"], + **position_info_parameters, ) # create nwb objects for insertion into analysis nwb file position.create_spatial_series( @@ -230,8 +222,8 @@ def make(self, key): @staticmethod def calculate_position_info_from_spatial_series( spatial_series, - max_LED_separation, - max_plausible_speed, + max_separation, + max_speed, speed_smoothing_std_dev, position_smoothing_duration, orient_smoothing_std_dev, @@ -274,7 +266,7 @@ def calculate_position_info_from_spatial_series( # Set points to NaN where the front and back LEDs are too separated dist_between_LEDs = get_distance(back_LED, front_LED) - is_too_separated = dist_between_LEDs >= max_LED_separation + is_too_separated = dist_between_LEDs >= max_separation back_LED[is_too_separated] = np.nan front_LED[is_too_separated] = np.nan @@ -294,8 +286,8 @@ def calculate_position_info_from_spatial_series( ) # Set to points to NaN where the speed is too fast - is_too_fast = (front_LED_speed > max_plausible_speed) | ( - back_LED_speed > max_plausible_speed + is_too_fast = (front_LED_speed > max_speed) | ( + back_LED_speed > max_speed ) back_LED[is_too_fast] = np.nan front_LED[is_too_fast] = np.nan diff --git a/src/spyglass/utils/dj_merge_tables.py b/src/spyglass/utils/dj_merge_tables.py index f79dde154..e56deea4c 100644 --- a/src/spyglass/utils/dj_merge_tables.py +++ b/src/spyglass/utils/dj_merge_tables.py @@ -6,7 +6,7 @@ from datajoint.condition import make_condition from datajoint.errors import DataJointError from datajoint.preview import repr_html -from datajoint.utils import from_camel_case, to_camel_case +from datajoint.utils import from_camel_case, to_camel_case, get_master from IPython.core.display import HTML from spyglass.common.common_nwbfile import AnalysisNwbfile @@ -701,20 +701,7 @@ def delete_downstream_merge( ) descendants = _unique_descendants(table, recurse_level) - - # Adapted from Spyglass PR 535 - # dj.utils.get_master could maybe help here, but it uses names, not objs - merge_table_pairs = [ # get each merge/part table - (master, descendant.restrict(restriction)) - for descendant in descendants - for master in descendant.parents(as_objects=True) # and those parents - # if is a part table (using a dunder not immediately after schema name) - if "__" in descendant.full_table_name.replace("`.`__", "") - # and it is not in not in direct descendants - and master.full_table_name not in descendants - # and it uses our reserved primary key in attributes - and RESERVED_PRIMARY_KEY in master.heading.attributes.keys() - ] + merge_table_pairs = _master_table_pairs(descendants, restriction) # restrict the merge table based on uuids in part merge_pairs = [ @@ -742,11 +729,11 @@ def _unique_descendants( recurse_level: int The maximum level of descendants to find. return_names: bool - If True, return names of descendants found. + If True, return names of descendants found. Else return Table objects. Returns ------- - List[dj.Table] + List[dj.Table, str] List descendants found when recurisively called to recurse_level """ @@ -764,7 +751,55 @@ def recurse_descendants(sub_table, level): recurse_descendants(table, recurse_level) - if return_names: - return list(descendants.keys()) + return ( + list(descendants.keys()) if return_names else list(descendants.values()) + ) + + +def _master_table_pairs( + table_list: list, + restriction: str = True, + connection: dj.connection.Connection = None, +) -> list: + """ + Given list of tables, return a list of master table pairs. + + Returns a list of tuples, with master and part. Part will have restriction + applied. If restriction yield empty list, skip. + + Parameters + ---------- + table_list : List[dj.Table] + A list of datajoint tables. + restriction : str + A restriction string. Defalt True, no restriction. + connection : datajoint.connection.Connection + A database connection. Default None, use connection from first table. + + Returns + ------- + List[Tuple[dj.Table, dj.Table]] + A list of master table pairs. + """ + conn = connection or table_list[0].connection + + master_table_pairs = [] + # Adapted from Spyglass PR 535 + for table in table_list: + master_name = get_master(table.full_table_name) + if not master_name: # then it's not a part table + continue + + master = dj.FreeTable(conn, master_name) + + if RESERVED_PRIMARY_KEY not in master.heading.attributes.keys(): + continue + + restricted_table = table.restrict(restriction) + + if not restricted_table: + continue + + master_table_pairs.append((master, restricted_table)) - return list(descendants.values()) + return master_table_pairs