From db7f3d4cb80ddff49bd51a078bcb3d752c6d1672 Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Wed, 20 Mar 2024 18:23:16 -0700 Subject: [PATCH 01/12] Add csv as a format option --- sleap/io/convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sleap/io/convert.py b/sleap/io/convert.py index 3353a169b..bbb0fdf53 100644 --- a/sleap/io/convert.py +++ b/sleap/io/convert.py @@ -70,7 +70,7 @@ def create_parser(): help="Output format. Default ('slp') is SLEAP dataset; " "'analysis' results in analysis.h5 file; " "'analysis.nix' results in an analysis nix file;" - "'h5' or 'json' results in SLEAP dataset " + "'h5' 'csv' or 'json' results in SLEAP dataset " "with specified file format.", ) parser.add_argument( From 3ecc99a1efac534bac5e48f1f7c235053a6b77e0 Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Wed, 20 Mar 2024 18:57:40 -0700 Subject: [PATCH 02/12] Add analysis to format --- sleap/io/convert.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sleap/io/convert.py b/sleap/io/convert.py index bbb0fdf53..1b52524fa 100644 --- a/sleap/io/convert.py +++ b/sleap/io/convert.py @@ -70,7 +70,8 @@ def create_parser(): help="Output format. Default ('slp') is SLEAP dataset; " "'analysis' results in analysis.h5 file; " "'analysis.nix' results in an analysis nix file;" - "'h5' 'csv' or 'json' results in SLEAP dataset " + "'analysis.csv' results in an analysis csv file;" + "'h5' or 'json' results in SLEAP dataset " "with specified file format.", ) parser.add_argument( From c4fb39e85c07b8439c57799748ef2e38ecc930ca Mon Sep 17 00:00:00 2001 From: eberrigan Date: Wed, 20 Mar 2024 20:40:53 -0700 Subject: [PATCH 03/12] Add csv suffix to output path --- sleap/io/convert.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sleap/io/convert.py b/sleap/io/convert.py index 1b52524fa..fb228003e 100644 --- a/sleap/io/convert.py +++ b/sleap/io/convert.py @@ -106,6 +106,7 @@ def main(args: list = None): """ parser = create_parser() args = parser.parse_args(args=args) + print(args) video_callback = Labels.make_video_callback([os.path.dirname(args.input_path)]) try: @@ -136,7 +137,12 @@ def main(args: list = None): outnames = [path for path in args.outputs] if len(outnames) < len(vids): # if there are less outnames provided than videos to convert... - out_suffix = "nix" if "nix" in args.format else "h5" + if "nix" in args.format: + out_suffix = "nix" + elif "csv" in args.format: + out_suffix = "csv" + else: + out_suffix = "h5" fn = args.input_path fn = re.sub("(\.json(\.zip)?|\.h5|\.slp)$", "", fn) fn = PurePath(fn) From c998d3d1aed3d56d8604d4e7790c6a960f333408 Mon Sep 17 00:00:00 2001 From: eberrigan Date: Wed, 20 Mar 2024 20:41:14 -0700 Subject: [PATCH 04/12] Add condition for csv analysis file --- sleap/io/convert.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sleap/io/convert.py b/sleap/io/convert.py index fb228003e..19017af03 100644 --- a/sleap/io/convert.py +++ b/sleap/io/convert.py @@ -165,6 +165,20 @@ def main(args: list = None): NixAdaptor.write(outname, labels, args.input_path, video) except ValueError as e: print(e.args[0]) + + elif "csv" in args.format: + from sleap.info.write_tracking_h5 import main as write_analysis + + for video, output_path in zip(vids, outnames): + write_analysis( + labels, + output_path=output_path, + labels_path=args.input_path, + all_frames=True, + video=video, + csv=True, + ) + else: from sleap.info.write_tracking_h5 import main as write_analysis From d2b658c8473ad66c38dbe323988fd342e956780b Mon Sep 17 00:00:00 2001 From: eberrigan Date: Wed, 20 Mar 2024 21:17:50 -0700 Subject: [PATCH 05/12] Add export function to Labels class --- sleap/io/dataset.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sleap/io/dataset.py b/sleap/io/dataset.py index 45280cc54..ac4315257 100644 --- a/sleap/io/dataset.py +++ b/sleap/io/dataset.py @@ -2055,6 +2055,21 @@ def export(self, filename: str): SleapAnalysisAdaptor.write(filename, self) + + def export_csv(self, filename: str): + """Export labels to CSV format. + + Args: + filename: Output path for the CSV format file. + + Notes: + This will write the contents of the labels out as a CSV file. + """ + from sleap.io.format.csv import CSVAdaptor + + CSVAdaptor.write(filename, self) + + def export_nwb( self, filename: str, From ff7ee5180e2badfbfea89f070ea5a1a93357d620 Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Mon, 8 Apr 2024 11:49:30 -0700 Subject: [PATCH 06/12] delete print statement --- sleap/io/convert.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sleap/io/convert.py b/sleap/io/convert.py index 19017af03..7045ed71f 100644 --- a/sleap/io/convert.py +++ b/sleap/io/convert.py @@ -106,7 +106,6 @@ def main(args: list = None): """ parser = create_parser() args = parser.parse_args(args=args) - print(args) video_callback = Labels.make_video_callback([os.path.dirname(args.input_path)]) try: From f7929a29659d464d12845fa662b3673b55fb5f9c Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Mon, 8 Apr 2024 11:49:43 -0700 Subject: [PATCH 07/12] lint --- sleap/io/dataset.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sleap/io/dataset.py b/sleap/io/dataset.py index ac4315257..1b894089f 100644 --- a/sleap/io/dataset.py +++ b/sleap/io/dataset.py @@ -2055,7 +2055,6 @@ def export(self, filename: str): SleapAnalysisAdaptor.write(filename, self) - def export_csv(self, filename: str): """Export labels to CSV format. @@ -2069,7 +2068,6 @@ def export_csv(self, filename: str): CSVAdaptor.write(filename, self) - def export_nwb( self, filename: str, From 821d114811db52618926715e18296d2399a063af Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Mon, 8 Apr 2024 13:58:29 -0700 Subject: [PATCH 08/12] Add `analysis.csv` as parametrize input for `sleap-convert` tests --- tests/io/test_convert.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/io/test_convert.py b/tests/io/test_convert.py index da1971c11..738c3d625 100644 --- a/tests/io/test_convert.py +++ b/tests/io/test_convert.py @@ -8,7 +8,7 @@ import pytest -@pytest.mark.parametrize("format", ["analysis", "analysis.nix"]) +@pytest.mark.parametrize("format", ["analysis", "analysis.nix", "analysis.csv"]) def test_analysis_format( min_labels_slp: Labels, min_labels_slp_path: Labels, @@ -27,7 +27,7 @@ def generate_filenames(paths, format="analysis"): labels_path = str(slp_path) fn = re.sub("(\\.json(\\.zip)?|\\.h5|\\.slp)$", "", labels_path) fn = PurePath(fn) - out_suffix = "nix" if "nix" in format else "h5" + out_suffix = "nix" if "nix" in format else "csv" if "csv" in format else "h5" default_names = [ default_analysis_filename( labels=labels, From 3eeb9fdb35a72a7d22fd75900eba2c40dc7455b6 Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Mon, 8 Apr 2024 15:20:44 -0700 Subject: [PATCH 09/12] test `export_csv` method added to `Labels` class --- tests/io/test_dataset.py | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/io/test_dataset.py b/tests/io/test_dataset.py index 5592ae437..be5b71730 100644 --- a/tests/io/test_dataset.py +++ b/tests/io/test_dataset.py @@ -1,9 +1,11 @@ import os +import pandas as pd import pytest import numpy as np from pathlib import Path, PurePath import sleap +from sleap.info.write_tracking_h5 import get_nodes_as_np_strings from sleap.skeleton import Skeleton from sleap.instance import Instance, Point, LabeledFrame, PredictedInstance, Track from sleap.io.video import Video, MediaVideo @@ -1559,3 +1561,47 @@ def test_export_nwb(centered_pair_predictions: Labels, tmpdir): # Read from NWB file read_labels = NDXPoseAdaptor.read(NDXPoseAdaptor, filehandle.FileHandle(filename)) assert_read_labels_match(centered_pair_predictions, read_labels) + + +@pytest.mark.parametrize("labels_fixture_name", [ + "centered_pair_labels", + "centered_pair_predictions", + "min_labels", + "min_labels_slp", + "min_labels_robot" +]) +def test_export_csv(labels_fixture_name, tmpdir, request): + # Retrieve Labels fixture by name + labels_fixture = request.getfixturevalue(labels_fixture_name) + + # Generate the filename for the CSV file + csv_filename = Path(tmpdir) / (labels_fixture_name + "_export.csv") + + # Export to CSV file + labels_fixture.export_csv(str(csv_filename)) + + # Assert that the CSV file was created + assert csv_filename.is_file(), f"CSV file '{csv_filename}' was not created" + + +def test_exported_csv(tmpdir, min_labels_slp, minimal_instance_predictions_csv_path): + # Construct the filename for the CSV file + filename_csv = str(tmpdir + "\\analysis.csv") + labels = min_labels_slp + # Export to CSV file + labels.export_csv(filename_csv) + # Read the CSV file + labels_csv = pd.read_csv(filename_csv) + + # Read the csv file fixture + csv_predictions = pd.read_csv(minimal_instance_predictions_csv_path) + + assert labels_csv.equals(csv_predictions) + + # check number of cols + assert len(labels_csv.columns) - 3 == len(get_nodes_as_np_strings(labels)) * 3 + + + + + From b4bdca10aa08a948461beb349c71a6e6ba3ac7b1 Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Mon, 8 Apr 2024 15:24:16 -0700 Subject: [PATCH 10/12] black formatting --- tests/io/test_dataset.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/tests/io/test_dataset.py b/tests/io/test_dataset.py index be5b71730..10547cb08 100644 --- a/tests/io/test_dataset.py +++ b/tests/io/test_dataset.py @@ -1563,23 +1563,26 @@ def test_export_nwb(centered_pair_predictions: Labels, tmpdir): assert_read_labels_match(centered_pair_predictions, read_labels) -@pytest.mark.parametrize("labels_fixture_name", [ - "centered_pair_labels", - "centered_pair_predictions", - "min_labels", - "min_labels_slp", - "min_labels_robot" -]) +@pytest.mark.parametrize( + "labels_fixture_name", + [ + "centered_pair_labels", + "centered_pair_predictions", + "min_labels", + "min_labels_slp", + "min_labels_robot", + ], +) def test_export_csv(labels_fixture_name, tmpdir, request): # Retrieve Labels fixture by name labels_fixture = request.getfixturevalue(labels_fixture_name) - + # Generate the filename for the CSV file csv_filename = Path(tmpdir) / (labels_fixture_name + "_export.csv") - + # Export to CSV file labels_fixture.export_csv(str(csv_filename)) - + # Assert that the CSV file was created assert csv_filename.is_file(), f"CSV file '{csv_filename}' was not created" @@ -1592,7 +1595,7 @@ def test_exported_csv(tmpdir, min_labels_slp, minimal_instance_predictions_csv_p labels.export_csv(filename_csv) # Read the CSV file labels_csv = pd.read_csv(filename_csv) - + # Read the csv file fixture csv_predictions = pd.read_csv(minimal_instance_predictions_csv_path) @@ -1600,8 +1603,3 @@ def test_exported_csv(tmpdir, min_labels_slp, minimal_instance_predictions_csv_p # check number of cols assert len(labels_csv.columns) - 3 == len(get_nodes_as_np_strings(labels)) * 3 - - - - - From 79d06543f0b0562c87c352b66d7e1bb9cbb59ca4 Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Mon, 8 Apr 2024 15:51:52 -0700 Subject: [PATCH 11/12] use `Path` to construct filename --- tests/io/test_dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/io/test_dataset.py b/tests/io/test_dataset.py index 10547cb08..7f7ceb5d9 100644 --- a/tests/io/test_dataset.py +++ b/tests/io/test_dataset.py @@ -1589,7 +1589,7 @@ def test_export_csv(labels_fixture_name, tmpdir, request): def test_exported_csv(tmpdir, min_labels_slp, minimal_instance_predictions_csv_path): # Construct the filename for the CSV file - filename_csv = str(tmpdir + "\\analysis.csv") + filename_csv = Path(tmpdir) / "minimal_instance_predictions_export.csv" labels = min_labels_slp # Export to CSV file labels.export_csv(filename_csv) From e508587a750bf612bfde76432a1827c0e17d5efd Mon Sep 17 00:00:00 2001 From: Elizabeth Berrigan Date: Mon, 8 Apr 2024 15:56:01 -0700 Subject: [PATCH 12/12] add `analysis.csv` to cli guide for `sleap-convert` --- docs/guides/cli.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/guides/cli.md b/docs/guides/cli.md index 9e07c0a25..c29270299 100644 --- a/docs/guides/cli.md +++ b/docs/guides/cli.md @@ -322,7 +322,8 @@ optional arguments: analysis file for the latter video is given a default name. --format FORMAT Output format. Default ('slp') is SLEAP dataset; 'analysis' results in analysis.h5 file; 'analysis.nix' results - in an analysis nix file; 'h5' or 'json' results in SLEAP dataset + in an analysis nix file; 'analysis.csv' results + in an analysis csv file; 'h5' or 'json' results in SLEAP dataset with specified file format. --video VIDEO Path to video (if needed for conversion). ```