Repository: mir-dataset-loaders/mirdata

Issue:
'Fold' field missing in tinysol
While it is loaded into the metadata, the accessor function for "fold" is missing from the class. It should be added in.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/mir-dataset-loaders/mirdata/pull/538?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mir-dataset-loaders) Report
> Merging [#538](https://codecov.io/gh/mir-dataset-loaders/mirdata/pull/538?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mir-dataset-loaders) (92542a5) into [master](https://codecov.io/gh/mir-dataset-loaders/mirdata/commit/fa934ffe6c71d021385ea46f7da9da066fd051b6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mir-dataset-loaders) (fa934ff) will **decrease** coverage by `0.06%`.
> The diff coverage is `91.22%`.


```diff
@@            Coverage Diff             @@
##           master     #538      +/-   ##
==========================================
- Coverage   96.56%   96.49%   -0.07%     
==========================================
  Files          48       48              
  Lines        5850     5880      +30     
==========================================
+ Hits         5649     5674      +25     
- Misses        201      206       +5     
```


Patch:
diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst
index d2e872c..c279185 100644
--- a/docs/source/contributing.rst
+++ b/docs/source/contributing.rst
@@ -734,6 +734,10 @@ If a file format needed for a dataset is not included in this list, please see `
 
 Track Attributes
 ----------------
+If the dataset has an official e.g. train/test split, use the reserved attribute `Track.split`, or `MultiTrack.split`
+which will enable some dataset-level helper functions like `dataset.get_track_splits`. If there is no official split,
+do not use this attribute.
+
 Custom track attributes should be global, track-level data.
 For some datasets, there is a separate, dataset-level metadata file
 with track-level metadata, e.g. as a csv. When a single file is needed
diff --git a/docs/source/contributing_examples/example.py b/docs/source/contributing_examples/example.py
index de4928a..398a155 100644
--- a/docs/source/contributing_examples/example.py
+++ b/docs/source/contributing_examples/example.py
@@ -122,6 +122,10 @@ class Track(core.Track):
         self.audio_path = self.get_path("audio")
         self.annotation_path = self.get_path("annotation")
 
+        # -- if the dataset has an *official* e.g. train/test split, use this
+        # -- reserved attribute (can be a property if needed)
+        self.split = ...
+
     # -- If the dataset has metadata that needs to be accessed by Tracks,
     # -- such as a table mapping track ids to composers for the full dataset,
     # -- add them as properties like instead of in the __init__.
@@ -211,6 +215,10 @@ class MultiTrack(core.MultiTrack):
         self.mix_path = ...  # this can be called whatever makes sense for the datasets
         self.annotation_path = ...
 
+        # -- if the dataset has an *official* e.g. train/test split, use this
+        # -- reserved attribute (can be a property if needed)
+        self.split = ...
+
     # If you want to support multitrack mixing in this dataset, set this property
     @property
     def track_audio_property(self):
diff --git a/mirdata/core.py b/mirdata/core.py
index 2530090..419561f 100644
--- a/mirdata/core.py
+++ b/mirdata/core.py
@@ -279,14 +279,15 @@ class Dataset(object):
         """
         return self.multitrack(random.choice(self.mtrack_ids))
 
-    def _get_partitions(self, items, splits, seed):
+    def _get_partitions(self, items, splits, seed, partition_names=None):
         """Helper function to get the indexes needed to split a set of ids into partitions
         Args:
             items (list): list of items to partition
             splits (list of float): a list of floats that should sum up 1. It will return as many splits as elements in the list
             seed (int): the seed used for the random generator, in order to enhance reproducibility.
+            partition_names (list): list of keys to use in the output dictionary
         Returns:
-            list: a list containing the partition indexes
+            dict: a dictionary containing the partitions
         """
         if not np.isclose(np.sum(splits), 1):
             raise ValueError(
@@ -295,39 +296,110 @@ class Dataset(object):
                 )
             )
 
+        if partition_names and len(partition_names) != len(splits):
+            raise ValueError(
+                "If partition_names is provided, it should have the same length as splits"
+            )
+
         rng = np.random.default_rng(seed=seed)
         shuffled_items = rng.permutation(items)
 
+        if not partition_names:
+            partition_names = np.arange(len(splits))
+
         # Method from https://stackoverflow.com/a/14281094
         cdf = np.cumsum(splits)
         partitions = list(map(lambda x: int(np.ceil(x)), cdf * len(items)))
-        return [shuffled_items[a:b] for a, b in zip([0] + partitions, partitions)]
+        return {
+            name: shuffled_items[a:b]
+            for name, a, b in zip(partition_names, [0] + partitions, partitions)
+        }
+
+    def get_track_splits(self):
+        """Get predetermined track splits (e.g. train/ test)
+        released alongside this dataset
+
+        Raises:
+            AttributeError: If this dataset does not have tracks
+            NotImplementedError: If this dataset does not have predetermined splits
+
+        Returns:
+            dict: splits, keyed by split name and with values of lists of track_ids
+        """
+        if self._track_class is None:
+            raise AttributeError("This dataset does not have tracks")
+
+        if not hasattr(self.choice_track(), "split"):
+            raise NotImplementedError(
+                f"The {self.name} dataset does not have an official split. Use"
+                " get_random_track_splits instead."
+            )
 
-    def get_track_splits(self, splits, seed=42):
+        splits = {}
+        for track_id in self.track_ids:
+            track = self.track(track_id)
+            if track.split in splits:
+                splits[track.split].append(track_id)
+            else:
+                splits[track.split] = [track_id]
+        return splits
+
+    def get_random_track_splits(self, splits, seed=42, split_names=None):
         """Split the tracks into partitions e.g. training, validation, test
 
         Args:
             splits (list of float): a list of floats that should sum up 1. It will return as many splits as elements in the list
             seed (int): the seed used for the random generator, in order to enhance reproducibility. Defaults to 42
+            split_names (list): list of keys to use in the output dictionary
 
         Returns:
-            list of lists: a list of lists containing the elements in each split
+            dict: a dictionary containing the elements in each split
         """
-
         if self._track_class is None:
             raise AttributeError("This dataset does not have tracks")
 
-        return self._get_partitions(self.track_ids, splits, seed)
+        return self._get_partitions(self.track_ids, splits, seed, split_names)
+
+    def get_mtrack_splits(self):
+        """Get predetermined multitrack splits (e.g. train/ test)
+        released alongside this dataset.
+
+        Raises:
+            AttributeError: If this dataset does not have multitracks
+            NotImplementedError: If this dataset does not have predetermined splits
+
+        Returns:
+            dict: splits, keyed by split name and with values of lists of mtrack_ids
+        """
+        if self._multitrack_class is None:
+            raise AttributeError("This dataset does not have multitracks")
+
+        if not hasattr(self.choice_multitrack(), "split"):
+            raise NotImplementedError(
+                f"The {self.name} dataset does not have an official split. Use"
+                " get_random_mtrack_splits instead."
+            )
+
+        splits = {}
+        for mtrack_id in self.mtrack_ids:
+            mtrack = self.multitrack(mtrack_id)
+            if mtrack.split in splits:
+                splits[mtrack.split].append(mtrack_id)
+            else:
+                splits[mtrack.split] = [mtrack_id]
+
+        return splits
 
-    def get_mtrack_splits(self, splits, seed=42):
+    def get_random_mtrack_splits(self, splits, seed=42, split_names=None):
         """Split the multitracks into partitions, e.g. training, validation, test
 
         Args:
             splits (list of float): a list of floats that should sum up 1. It will return as many splits as elements in the list
             seed (int): the seed used for the random generator, in order to enhance reproducibility. Defaults to 42
+            split_names (list): list of keys to use in the output dictionary
 
         Returns:
-            list of lists: a list of lists containing the elements in each split
+            dict: a dictionary containing the elements in each split
         """
 
         if self._multitrack_class is None:
diff --git a/mirdata/datasets/irmas.py b/mirdata/datasets/irmas.py
index 4bab3d3..453d602 100644
--- a/mirdata/datasets/irmas.py
+++ b/mirdata/datasets/irmas.py
@@ -175,6 +175,7 @@ class Track(core.Track):
         train (bool): flag to identify if the track is from the training of the testing dataset
         genre (str): string containing the namecode of the genre of the track.
         drum (bool): flag to identify if the track contains drums or not.
+        split (str): data split ("train" or "test")
 
     Cached Properties:
         instrument (list): list of predominant instruments as str
@@ -200,49 +201,31 @@ class Track(core.Track):
         self.annotation_path = self.get_path("annotation")
 
         self.audio_path = self.get_path("audio")
-
-        # Dataset attributes
-        self.predominant_instrument = None
-        self.genre = None
-        self.drum = None
-        self.train = True
-
         self._audio_filename = self._track_paths["audio"][0]
 
-        # TRAINING TRACKS
-        if "__" in track_id:
-            self.predominant_instrument = os.path.basename(
-                os.path.dirname(self.audio_path)
-            )
-            assert (
-                self.predominant_instrument in INST_DICT
-            ), "Instrument {} not in instrument dict".format(
-                self.predominant_instrument
-            )
-
-            # Drum presence annotation is present
-            if "dru" in self._audio_filename or "nod" in self._audio_filename:
-                self.genre = (
-                    self._audio_filename.split(".")[0].split("[")[3].split("]")[0]
-                )
-                assert self.genre in GENRE_DICT, "Genre {} not in genre dict".format(
-                    self.genre
-                )
-                self.drum = [True if "dru" in self._audio_filename else False][0]
-
-            # Drum presence annotation not present
-            else:
-                self.genre = (
-                    self._audio_filename.split(".")[0].split("[")[2].split("]")[0]
-                )
-                assert self.genre in GENRE_DICT, "Genre {} not in genre dict".format(
-                    self.genre
-                )
-                self.drum = None
-
-        # TESTING TRACKS
+        self.split = "train" if "__" in track_id else "test"
+
+        # Dataset attributes
+        self.predominant_instrument = (
+            os.path.basename(os.path.dirname(self.audio_path))
+            if self.split == "train"
+            else None
+        )
+        if self.split == "train" and (
+            "dru" in self._audio_filename or "nod" in self._audio_filename
+        ):
+            self.genre = self._audio_filename.split(".")[0].split("[")[3].split("]")[0]
+            self.drum = [True if "dru" in self._audio_filename else False][0]
+        elif self.split == "train" and not (
+            "dru" in self._audio_filename or "nod" in self._audio_filename
+        ):
+            self.genre = self._audio_filename.split(".")[0].split("[")[2].split("]")[0]
+            self.drum = None
         else:
-            self.train = False
+            self.genre = None
+            self.drum = None
+
+        self.train = True if self.split == "train" else False
 
     @core.cached_property
     def instrument(self):
diff --git a/mirdata/datasets/mtg_jamendo_autotagging_moodtheme.py b/mirdata/datasets/mtg_jamendo_autotagging_moodtheme.py
index 6ee5b73..b0442dd 100644
--- a/mirdata/datasets/mtg_jamendo_autotagging_moodtheme.py
+++ b/mirdata/datasets/mtg_jamendo_autotagging_moodtheme.py
@@ -259,6 +259,26 @@ class Dataset(core.Dataset):
     def load_audio(self, *args, **kwargs):
         return load_audio(*args, **kwargs)
 
+    def get_track_splits(self, split_number=0):
+        """Get predetermined track splits released alongside this dataset
+
+        Args:
+            split_number (int): which split split_number to use (0, 1, 2, 3 or 4)
+
+        Returns:
+            dict: splits, keyed by split name and with values of lists of track_ids
+        """
+        if split_number not in [0, 1, 2, 3, 4]:
+            raise ValueError(
+                f"split_number must be 0, 1, 2, 3, or 4, got {split_number}"
+            )
+
+        return self._metadata["splits"][split_number]
+
+    @deprecated(
+        reason="Use mirdata.datasets.mtg_jamendo_autotagging_moodtheme.get_track_splits",
+        version="0.3.6",
+    )
     def get_track_ids_for_split(self, split_number):
         """Load a MTG jamendo autotagging moodtheme pre-defined split. There are five different train/validation/tests splits.
         Args:
@@ -267,7 +287,4 @@ class Dataset(core.Dataset):
             * dict: {"train": [...], "validation": [...], "test": [...]} - the train split
 
         """
-        if not (0 <= split_number <= 4):
-            raise Exception("Splits avaiables from num 0 to 4")
-
-        return self._metadata["splits"][split_number]
+        return self.get_track_splits(split_number=split_number)
diff --git a/mirdata/datasets/slakh.py b/mirdata/datasets/slakh.py
index 4ff2642..321e60a 100644
--- a/mirdata/datasets/slakh.py
+++ b/mirdata/datasets/slakh.py
@@ -90,10 +90,11 @@ class Track(core.Track):
     Attributes:
         audio_path (str or None): path to the track's audio file. For some unusual tracks,
             such as sound effects, there is no audio and this attribute is None.
-        data_split (str or None): one of 'train', 'validation', 'test', or 'omitted'.
+        split (str or None): one of 'train', 'validation', 'test', or 'omitted'.
             'omitted' tracks are part of slakh2100-redux which were found to be
             duplicates in the original slakh2011.
             In baby slakh there are no splits, so this attribute is None.
+        data_split (str or None): equivalent to split (deprecated in 0.3.6)
         metadata_path (str): path to the multitrack's metadata file
         midi_path (str or None): path to the track's midi file. For some unusual tracks,
             such as sound effects, there is no midi and this attribute is None.
@@ -135,14 +136,14 @@ class Track(core.Track):
         self.metadata_path = self.get_path("metadata")
 
         # split (train/validation/test/omitted) is part of the relative filepath in the index
-        self.data_split = None  # for baby_slakh, there are no data splits - set to None
+        self.split = None  # for baby_slakh, there are no data splits - set to None
         if index["version"] == "2100-redux":
-            self.data_split = self._track_paths["metadata"][0].split(os.sep)[1]
+            self.split = self._track_paths["metadata"][0].split(os.sep)[1]
             assert (
-                self.data_split in SPLITS
-            ), "{} not a valid split - should be one of {}.".format(
-                self.data_split, SPLITS
-            )
+                self.split in SPLITS
+            ), "{} not a valid split - should be one of {}.".format(self.split, SPLITS)
+
+        self.data_split = self.split  # deprecated in 0.3.6
 
     @core.cached_property
     def _track_metadata(self) -> dict:
@@ -231,9 +232,10 @@ class MultiTrack(core.MultiTrack):
         mix_path (str): path to the multitrack mix audio
         midi_path (str): path to the full midi data used to generate the mixture
         metadata_path (str): path to the multitrack metadata file
-        data_split (str or None): one of 'train', 'validation', 'test', or 'omitted'.
+        split (str or None): one of 'train', 'validation', 'test', or 'omitted'.
             'omitted' tracks are part of slakh2100-redux which were found to be
             duplicates in the original slakh2011.
+        data_split (str or None): equivalent to split (deprecated in 0.3.6)
         uuid (str): File name of the original MIDI file from Lakh, sans extension
         lakh_midi_dir (str): Path to the original MIDI file from a fresh download of Lakh
         normalized (bool): whether the mix and stems were normalized according to the ITU-R BS.1770-4 spec
@@ -262,10 +264,12 @@ class MultiTrack(core.MultiTrack):
         self.metadata_path = self.get_path("metadata")
 
         # split (train/validation/test) is determined by the relative filepath in the index
-        self.data_split = None  # for baby_slakh, there are no data splits - set to None
+        self.split = None  # for baby_slakh, there are no data splits - set to None
         if index["version"] == "2100-redux":
-            self.data_split = self._multitrack_paths["mix"][0].split(os.sep)[1]
-            assert self.data_split in SPLITS, "{} not in SPLITS".format(self.data_split)
+            self.split = self._multitrack_paths["mix"][0].split(os.sep)[1]
+            assert self.split in SPLITS, "{} not in SPLITS".format(self.split)
+
+        self.data_split = self.split  # deprecated in 0.3.6
 
     @property
     def track_audio_property(self) -> str:
diff --git a/mirdata/datasets/tinysol.py b/mirdata/datasets/tinysol.py
index 44defb2..672b38f 100644
--- a/mirdata/datasets/tinysol.py
+++ b/mirdata/datasets/tinysol.py
@@ -132,6 +132,10 @@ class Track(core.Track):
 
         self.audio_path = self.get_path("audio")
 
+    @property
+    def split(self):
+        return self._track_metadata.get("Fold")
+
     @property
     def family(self):
         return self._track_metadata.get("Family")


Test patch:
diff --git a/tests/datasets/test_irmas.py b/tests/datasets/test_irmas.py
index 7e15430..5120f25 100644
--- a/tests/datasets/test_irmas.py
+++ b/tests/datasets/test_irmas.py
@@ -20,6 +20,7 @@ def test_track():
         "predominant_instrument": None,
         "genre": None,
         "drum": None,
+        "split": "test",
         "train": False,
     }
     expected_attributes_train = {
@@ -31,6 +32,7 @@ def test_track():
         "predominant_instrument": "cla",
         "genre": "cla",
         "drum": None,
+        "split": "train",
         "train": True,
     }
 
diff --git a/tests/datasets/test_mtg_jamendo_autotagging_moodtheme.py b/tests/datasets/test_mtg_jamendo_autotagging_moodtheme.py
index db3e733..33a3150 100644
--- a/tests/datasets/test_mtg_jamendo_autotagging_moodtheme.py
+++ b/tests/datasets/test_mtg_jamendo_autotagging_moodtheme.py
@@ -12,7 +12,9 @@ def test_track():
     track = dataset.track(default_trackid)
 
     expected_attributes = {
-        "audio_path": "tests/resources/mir_datasets/mtg_jamendo_autotagging_moodtheme/audios/48/948.mp3",
+        "audio_path": (
+            "tests/resources/mir_datasets/mtg_jamendo_autotagging_moodtheme/audios/48/948.mp3"
+        ),
         "track_id": "track_0000948",
     }
 
@@ -59,25 +61,19 @@ def test_to_jams():
     assert jam["sandbox"].tags == "mood/theme---background"
 
 
-def test_get_track_ids_for_split():
+def test_get_track_splits():
+
     dataset = mtg_jamendo_autotagging_moodtheme.Dataset(
         "tests/resources/mir_datasets/mtg_jamendo_autotagging_moodtheme"
     )
-    assert len(dataset.get_track_ids_for_split(0)["train"]) == 1
-    assert len(dataset.get_track_ids_for_split(0)["validation"]) == 1
-    assert len(dataset.get_track_ids_for_split(0)["test"]) == 1
-    assert len(dataset.get_track_ids_for_split(1)["train"]) == 1
-    assert len(dataset.get_track_ids_for_split(1)["validation"]) == 1
-    assert len(dataset.get_track_ids_for_split(1)["test"]) == 1
-    assert len(dataset.get_track_ids_for_split(2)["train"]) == 1
-    assert len(dataset.get_track_ids_for_split(2)["validation"]) == 1
-    assert len(dataset.get_track_ids_for_split(2)["test"]) == 1
-    assert len(dataset.get_track_ids_for_split(3)["train"]) == 1
-    assert len(dataset.get_track_ids_for_split(3)["validation"]) == 1
-    assert len(dataset.get_track_ids_for_split(3)["test"]) == 1
-    assert len(dataset.get_track_ids_for_split(4)["train"]) == 1
-    assert len(dataset.get_track_ids_for_split(4)["validation"]) == 1
-    assert len(dataset.get_track_ids_for_split(4)["test"]) == 1
-
-    with pytest.raises(Exception):
-        dataset.get_track_ids_for_split(-1)
+    for i in range(5):
+        splits = dataset.get_track_splits(split_number=i)
+        assert len(splits["train"]) == 1
+        assert len(splits["validation"]) == 1
+        assert len(splits["test"]) == 1
+
+    with pytest.raises(ValueError):
+        dataset.get_track_splits(-1)
+
+    # deprecated
+    splits = dataset.get_track_ids_for_split(0)
diff --git a/tests/datasets/test_slakh.py b/tests/datasets/test_slakh.py
index 502add9..8c5f791 100644
--- a/tests/datasets/test_slakh.py
+++ b/tests/datasets/test_slakh.py
@@ -1,5 +1,4 @@
 import pretty_midi
-import pytest
 
 from mirdata import annotations
 from mirdata.datasets import slakh
@@ -18,7 +17,9 @@ def test_track():
         "mtrack_id": "Track00001",
         "audio_path": "tests/resources/mir_datasets/slakh/babyslakh_16k/Track00001/stems/S00.wav",
         "midi_path": "tests/resources/mir_datasets/slakh/babyslakh_16k/Track00001/MIDI/S00.mid",
-        "metadata_path": "tests/resources/mir_datasets/slakh/babyslakh_16k/Track00001/metadata.yaml",
+        "metadata_path": (
+            "tests/resources/mir_datasets/slakh/babyslakh_16k/Track00001/metadata.yaml"
+        ),
         "instrument": "Guitar",
         "integrated_loudness": -12.82074180245363,
         "is_drum": False,
@@ -27,6 +28,7 @@ def test_track():
         "program_number": 30,
         "mixing_group": "guitar",
         "data_split": None,
+        "split": None,
     }
 
     expected_property_types = {
@@ -74,8 +76,12 @@ def test_track_full():
         "track_id": "Track00001-S00",
         "mtrack_id": "Track00001",
         "audio_path": "tests/resources/mir_datasets/slakh/slakh2100_flac_redux/train/Track00001/stems/S00.flac",
-        "midi_path": "tests/resources/mir_datasets/slakh/slakh2100_flac_redux/train/Track00001/MIDI/S00.mid",
-        "metadata_path": "tests/resources/mir_datasets/slakh/slakh2100_flac_redux/train/Track00001/metadata.yaml",
+        "midi_path": (
+            "tests/resources/mir_datasets/slakh/slakh2100_flac_redux/train/Track00001/MIDI/S00.mid"
+        ),
+        "metadata_path": (
+            "tests/resources/mir_datasets/slakh/slakh2100_flac_redux/train/Track00001/metadata.yaml"
+        ),
         "instrument": "Guitar",
         "integrated_loudness": -12.82074180245363,
         "is_drum": False,
@@ -84,6 +90,7 @@ def test_track_full():
         "program_number": 30,
         "mixing_group": "guitar",
         "data_split": "train",
+        "split": "train",
     }
 
     expected_property_types = {
@@ -159,7 +166,9 @@ def test_multitrack():
         "mtrack_id": "Track00001",
         "midi_path": "tests/resources/mir_datasets/slakh/babyslakh_16k/Track00001/all_src.mid",
         "mix_path": "tests/resources/mir_datasets/slakh/babyslakh_16k/Track00001/mix.wav",
-        "metadata_path": "tests/resources/mir_datasets/slakh/babyslakh_16k/Track00001/metadata.yaml",
+        "metadata_path": (
+            "tests/resources/mir_datasets/slakh/babyslakh_16k/Track00001/metadata.yaml"
+        ),
         "data_split": None,
         "track_ids": [
             "Track00001-S00",
@@ -173,10 +182,13 @@ def test_multitrack():
             "Track00001-S09",
             "Track00001-S10",
         ],
-        "lakh_midi_dir": "lmd_matched/O/O/H/TROOHTB128F931F9DF/1a81ae092884234f3264e2f45927f00a.mid",
+        "lakh_midi_dir": (
+            "lmd_matched/O/O/H/TROOHTB128F931F9DF/1a81ae092884234f3264e2f45927f00a.mid"
+        ),
         "normalized": True,
         "overall_gain": 0.18270259567062658,
         "uuid": "1a81ae092884234f3264e2f45927f00a",
+        "split": None,
     }
 
     expected_property_types = {
diff --git a/tests/datasets/test_tinysol.py b/tests/datasets/test_tinysol.py
index 38cc00b..9f96cf8 100644
--- a/tests/datasets/test_tinysol.py
+++ b/tests/datasets/test_tinysol.py
@@ -27,6 +27,7 @@ def test_track():
         "instance_id": 0,
         "is_resampled": True,
         "string_id": None,
+        "split": 0,
     }
 
     expected_property_types = {
diff --git a/tests/test_core.py b/tests/test_core.py
index 8ac49cf..b1534f7 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -666,11 +666,17 @@ def test_dataset_splits():
 
     # test the case where there are no tracks
     with pytest.raises(AttributeError):
-        empty_dataset.get_track_splits([0.9, 0.1])
+        empty_dataset.get_random_track_splits([0.9, 0.1])
 
     # test the case where there are no multitracks
     with pytest.raises(AttributeError):
-        empty_dataset.get_mtrack_splits([0.9, 0.1])
+        empty_dataset.get_random_mtrack_splits([0.9, 0.1])
+
+    with pytest.raises(AttributeError):
+        empty_dataset.get_track_splits()
+
+    with pytest.raises(AttributeError):
+        empty_dataset.get_mtrack_splits()
 
     # test the partition function
     items = [i for i in range(100)]
@@ -692,20 +698,40 @@ def test_dataset_splits():
         # check that the right number of splits are created
         assert len(splits) == len(right_combination)
         # check that the number of total items matches
-        assert len(items) == sum([len(i) for i in splits])
+        assert len(items) == sum([len(i) for i in splits.values()])
         # check that all items are used
-        assert set(items) == set([i for split_items in splits for i in split_items])
+        assert set(items) == set(
+            [i for split_items in splits.values() for i in split_items]
+        )
         # check that splits are nonoverlapping
         used = set()
-        for split in splits:
+        for split in splits.values():
             this_split = set(split)
             assert not this_split.intersection(used)
             used.update(this_split)
         # check that the split is reproducable
         splits2 = empty_dataset._get_partitions(items, right_combination, 42)
-        for split, split2 in zip(splits, splits2):
+        for split, split2 in zip(splits.values(), splits2.values()):
             assert np.array_equal(split, split2)
 
+    # test partition names
+    with pytest.raises(ValueError):
+        splits = empty_dataset._get_partitions(
+            items, [0.1, 0.9], 42, partition_names=["asdf"]
+        )
+
+    splits = empty_dataset._get_partitions(items, [0.1, 0.9], 42)
+    assert set(splits.keys()) == set([0, 1])
+    assert len(splits[0]) == 10
+    assert len(splits[1]) == 90
+
+    splits = empty_dataset._get_partitions(
+        items, [0.1, 0.9], 42, partition_names=["test", "train"]
+    )
+    assert set(splits.keys()) == set(["test", "train"])
+    assert len(splits["test"]) == 10
+    assert len(splits["train"]) == 90
+
     list_not_sum_up_1 = [
         [0.8, 0.1, 0.3, 0.2],
         [0.3, 0.1, 0.3, 0.5],
@@ -721,3 +747,24 @@ def test_dataset_splits():
     for wrong_combination in list_not_sum_up_1:
         with pytest.raises(ValueError):
             empty_dataset._get_partitions(items, wrong_combination, 42)
+
+    track_mtrack_dataset = core.Dataset(
+        name="test",
+        indexes={"default": core.Index("slakh_index_baby.json")},
+        track_class=core.Track,
+        multitrack_class=core.MultiTrack,
+    )
+
+    with pytest.raises(NotImplementedError):
+        track_mtrack_dataset.get_track_splits()
+
+    with pytest.raises(NotImplementedError):
+        track_mtrack_dataset.get_mtrack_splits()
+
+    # test one real dataset
+    test_dataset = mirdata.initialize("slakh", version="2100-redux")
+    splits = test_dataset.get_track_splits()
+    assert set(splits.keys()) == set(["train", "validation", "test", "omitted"])
+
+    splits = test_dataset.get_mtrack_splits()
+    assert set(splits.keys()) == set(["train", "validation", "test", "omitted"])
diff --git a/tests/test_full_dataset.py b/tests/test_full_dataset.py
index 47503b1..4cbb23c 100644
--- a/tests/test_full_dataset.py
+++ b/tests/test_full_dataset.py
@@ -127,3 +127,36 @@ def test_index(skip_remote, dataset):
                 okeys
             )
         )
+
+
+def test_predetermined_splits(dataset):
+    if dataset is None:
+        pytest.skip()
+
+    # test custom get_track_splits functions
+    try:
+        splits = dataset.get_track_splits()
+        assert isinstance(splits, dict)
+        used_tracks = set()
+        for k in splits:
+            assert all([t in dataset.track_ids for t in splits[k]])
+            this_split = set(splits[k])
+            assert not used_tracks.intersection(this_split)
+            used_tracks.update(this_split)
+        assert used_tracks == set(dataset.track_ids)
+    except (AttributeError, NotImplementedError):
+        pass
+
+    # test custom get_mtrack_splits functions
+    try:
+        splits = dataset.get_mtrack_splits()
+        assert isinstance(splits, dict)
+        used_tracks = set()
+        for k in splits:
+            assert all([t in dataset.mtrack_ids for t in splits[k]])
+            this_split = set(splits[k])
+            assert not used_tracks.intersection(this_split)
+            used_tracks.update(this_split)
+        assert used_tracks == set(dataset.mtrack_ids)
+    except (AttributeError, NotImplementedError):
+        pass
diff --git a/tests/test_loaders.py b/tests/test_loaders.py
index 9c3c029..c41721a 100644
--- a/tests/test_loaders.py
+++ b/tests/test_loaders.py
@@ -324,6 +324,7 @@ def test_track_placeholder_case():
     data_home_dir = "not/a/real/path"
 
     for dataset_name in DATASETS:
+        print(dataset_name)
         data_home = os.path.join(data_home_dir, dataset_name)
         dataset = mirdata.initialize(dataset_name, data_home, version="test")
 
@@ -478,18 +479,54 @@ def test_random_splits():
         # check wrong type of split function
         if dataset._track_class is None:
             with pytest.raises(AttributeError):
-                dataset.get_track_splits(split)
+                dataset.get_random_track_splits(split)
 
         if dataset._multitrack_class is None:
             with pytest.raises(AttributeError):
-                dataset.get_mtrack_splits(split)
+                dataset.get_random_mtrack_splits(split)
 
         # check splits for tracks
         if dataset._track_class:
-            splits = dataset.get_track_splits(split)
-            assert len(dataset.track_ids) == sum([len(i) for i in splits])
+            splits = dataset.get_random_track_splits(split)
+            assert len(dataset.track_ids) == sum([len(i) for i in splits.values()])
 
         # check splits for multitracks
         if dataset._multitrack_class:
-            splits = dataset.get_mtrack_splits(split)
-            assert len(dataset.mtrack_ids) == sum([len(i) for i in splits])
+            splits = dataset.get_random_mtrack_splits(split)
+            assert len(dataset.mtrack_ids) == sum([len(i) for i in splits.values()])
+
+
+def test_predetermined_splits():
+    required_track = ["irmas", "mtg_jamendo_autotagging_moodtheme", "slakh", "tinysol"]
+    required_mtrack = ["slakh"]
+    for dataset_name in DATASETS:
+        print(dataset_name)
+        dataset = mirdata.initialize(
+            dataset_name, os.path.join(TEST_DATA_HOME, dataset_name), version="test"
+        )
+
+        # test custom get_track_splits functions
+        try:
+            splits = dataset.get_track_splits()
+            assert isinstance(splits, dict)
+            used_tracks = set()
+            for k in splits:
+                assert all([t in dataset.track_ids for t in splits[k]])
+                this_split = set(splits[k])
+                assert not used_tracks.intersection(this_split)
+                used_tracks.update(this_split)
+        except (AttributeError, NotImplementedError):
+            assert dataset_name not in required_track
+
+        # test custom get_mtrack_splits functions
+        try:
+            splits = dataset.get_mtrack_splits()
+            assert isinstance(splits, dict)
+            used_tracks = set()
+            for k in splits:
+                assert all([t in dataset.mtrack_ids for t in splits[k]])
+                this_split = set(splits[k])
+                assert not used_tracks.intersection(this_split)
+                used_tracks.update(this_split)
+        except (AttributeError, NotImplementedError):
+            assert dataset_name not in required_mtrack