Repository: aeye-lab/pymovements
Pull request #306 (2023-03-27 20:13:09)

Dataset: Add DatasetPaths and use it in Dataset
## Description

This PR changes the way we specify paths for datasets.
This is done as the passing of the `root` argument was confusing and the behavior between `Dataset` and `PublicDataset` was different. Also, all the dirname arguments which will be seldomly used crowded the `Dataset.__init__()` method.

The PR aims to have the most simple specification for everyday-use.
I have therefore added a `path` argument which specifies the exact path to the dataset.
If the user really wants to fiddle around and customize, a new `DatasetPaths` object can be used.

The `root` argument is completely gone together with all the dirname stuff.
Instead we now have a `path` argument. If we just specify this, this will be the path to the dataset directory, omitting any root directory specification.

```python
dataset = pm.Dataset('ToyDataset', path='data/ToyDataset')
```

In case we only want to specify a root directory we have to create a `DatasetPaths` instance first:
```python
paths = pm.DatasetPaths(root='/my/data')
dataset = pm.Dataset('ToyDataset', path=paths)
```

This will then have `my/data/ToyDataset` as the dataset directory.

There's still the simpler alternative without using the new class:
```python
dataset = pm.Dataset('ToyDataset', path=f'data/{dataset_name}')
```

If we really want we can also adjust the directory structure of the dataset:
```python
paths = pm.DatasetPaths(root='/my/data', events='custom_events')
dataset = pm.Dataset('ToyDataset', path=paths)
```

Additionally, we now have a `Dataset.paths` attribute which holds all the paths to the subdirectories like `Dataset.paths.raw` and `Dataset.paths.downloads`.


## Implemented changes

Insert a description of the changes implemented in the pull request.

- [x] Moved all the path property methods from `Dataset` to `DatasetPaths`
- [x] Moved all the path property methods from `PublicDataset` to `DatasetPaths`

## Type of change

- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [x] This change is or requires a documentation update

## How Has This Been Tested?

- [x] All existing tests were adjusted
- [x] New test cases for specifying only a string as path

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream modules
- [x] I have checked my code and corrected any misspellings


codecov-commenter: ## [Codecov](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) Report
Patch coverage: **`95.74`**% and project coverage change: **`-0.26`** :warning:
> Comparison is base [(`948bb41`)](https://codecov.io/gh/aeye-lab/pymovements/commit/948bb41213f19f42a0ea65b8d219ccb52409daf5?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) 100.00% compared to head [(`9cd71fc`)](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) 99.74%.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##              main     #306      +/-   ##
===========================================
- Coverage   100.00%   99.74%   -0.26%     
===========================================
  Files           38       39       +1     
  Lines         1534     1541       +7     
  Branches       348      348              
===========================================
+ Hits          1534     1537       +3     
- Misses           0        2       +2     
- Partials         0        2       +2     
```


| [Impacted Files](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) | Coverage Δ | |
|---|---|---|
| [src/pymovements/datasets/gazebase.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXRzL2dhemViYXNlLnB5) | `100.00% <ø> (ø)` | |
| [src/pymovements/datasets/judo1000.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXRzL2p1ZG8xMDAwLnB5) | `100.00% <ø> (ø)` | |
| [src/pymovements/datasets/toy\_dataset.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXRzL3RveV9kYXRhc2V0LnB5) | `100.00% <ø> (ø)` | |
| [src/pymovements/utils/paths.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL3V0aWxzL3BhdGhzLnB5) | `95.55% <33.33%> (-4.45%)` | :arrow_down: |
| [src/pymovements/dataset/dataset.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXQvZGF0YXNldC5weQ==) | `99.08% <92.59%> (-0.92%)` | :arrow_down: |
| [src/pymovements/\_\_init\_\_.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL19faW5pdF9fLnB5) | `100.00% <100.00%> (ø)` | |
| [src/pymovements/dataset/\_\_init\_\_.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXQvX19pbml0X18ucHk=) | `100.00% <100.00%> (ø)` | |
| [src/pymovements/dataset/dataset\_definition.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXQvZGF0YXNldF9kZWZpbml0aW9uLnB5) | `100.00% <100.00%> (ø)` | |
| [src/pymovements/dataset/dataset\_paths.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXQvZGF0YXNldF9wYXRocy5weQ==) | `100.00% <100.00%> (ø)` | |
| [src/pymovements/dataset/public\_dataset.py](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXQvcHVibGljX2RhdGFzZXQucHk=) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/aeye-lab/pymovements/pull/306?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab).


Patch:
diff --git a/docs/source/tutorials/R-tutorial.md b/docs/source/tutorials/R-tutorial.md
index 3cc04a5..c5a63ce 100644
--- a/docs/source/tutorials/R-tutorial.md
+++ b/docs/source/tutorials/R-tutorial.md
@@ -27,7 +27,7 @@ Access functions and data within python modules and classes via the `$` operator
 To test, you can proceed with the "pymovements in 10 minutes" tutorial,
 for example this is how you download the ToyDataset:
 ```r
-dataset = pm$PublicDataset('ToyDataset', root='data_directory')
+dataset = pm$PublicDataset('ToyDataset', path='data/ToyDataset')
 dataset$download()
 ```
 
diff --git a/docs/source/tutorials/detecting-events.ipynb b/docs/source/tutorials/detecting-events.ipynb
index 44a21b6..e276126 100644
--- a/docs/source/tutorials/detecting-events.ipynb
+++ b/docs/source/tutorials/detecting-events.ipynb
@@ -62,7 +62,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "dataset.download()\n",
     "dataset.load()"
    ]
diff --git a/docs/source/tutorials/event-properties.ipynb b/docs/source/tutorials/event-properties.ipynb
index 9f30212..e67f110 100644
--- a/docs/source/tutorials/event-properties.ipynb
+++ b/docs/source/tutorials/event-properties.ipynb
@@ -59,7 +59,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "dataset.download()\n",
     "dataset.load()"
    ]
diff --git a/docs/source/tutorials/heatmap.ipynb b/docs/source/tutorials/heatmap.ipynb
index 518900f..9255696 100644
--- a/docs/source/tutorials/heatmap.ipynb
+++ b/docs/source/tutorials/heatmap.ipynb
@@ -47,7 +47,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "dataset.download()\n",
     "dataset.load()"
    ]
diff --git a/docs/source/tutorials/local-dataset.ipynb b/docs/source/tutorials/local-dataset.ipynb
index 346fb14..3298a7f 100644
--- a/docs/source/tutorials/local-dataset.ipynb
+++ b/docs/source/tutorials/local-dataset.ipynb
@@ -67,7 +67,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "toy_dataset = pm.PublicDataset('ToyDataset', root='data/', dataset_dirname='my_dataset')\n",
+    "toy_dataset = pm.PublicDataset('ToyDataset', path='data/my_dataset')\n",
     "toy_dataset.download(remove_finished=True)\n",
     "\n",
     "del toy_dataset"
@@ -208,7 +208,7 @@
   },
   {
    "cell_type": "markdown",
-   "id": "37c70a20",
+   "id": "b869d6c5",
    "metadata": {},
    "source": [
     "Next we use all these definitions and create a `DatasetDefinition` by passing in the root directory, Experiment instance, and other optional parameters such as the filename regular expression and custom CSV reading parameters."
@@ -217,7 +217,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "1aa28e15",
+   "id": "7a9e7ac4",
    "metadata": {},
    "outputs": [],
    "source": [
@@ -236,7 +236,7 @@
    "id": "190ce9f6",
    "metadata": {},
    "source": [
-    "Finally we create a `Dataset` instance by using the `DatasetDefinition`. The `dataset_dirname`, `raw_dirname`, `preprocessed_dirname`, and `events_dirname` parameters define the names of the directories for the dataset, raw data, preprocessed data, and events data, respectively."
+    "Finally we create a `Dataset` instance by using the `DatasetDefinition` and specifying the directory path."
    ]
   },
   {
@@ -246,17 +246,39 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "# Define the path to the dataset directory\n",
-    "dataset_dir = 'data/my_dataset/'\n",
+    "dataset = pm.Dataset(\n",
+    "    definition=dataset_definition,\n",
+    "    path='data/my_dataset/',\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "73774d9c",
+   "metadata": {},
+   "source": [
+    "If we have a root data directory which holds all your local datasets we can further need to define the paths of the dataset.\n",
+    "\n",
+    "The `dataset`, `raw`, `preprocessed`, and `events` parameters define the names of the directories for the dataset, raw data, preprocessed data, and events data, respectively."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5ebeb02a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dataset_paths = pm.DatasetPaths(\n",
+    "    root='data/',\n",
+    "    raw='raw',\n",
+    "    preprocessed='preprocessed',\n",
+    "    events='events',\n",
+    ")\n",
     "\n",
-    "# Set up the Dataset object\n",
     "dataset = pm.Dataset(\n",
     "    definition=dataset_definition,\n",
-    "    root=dataset_dir,\n",
-    "    dataset_dirname='.',\n",
-    "    raw_dirname='raw',\n",
-    "    preprocessed_dirname='preprocessed',\n",
-    "    events_dirname='events',\n",
+    "    path=dataset_paths,\n",
     ")"
    ]
   },
@@ -265,7 +287,7 @@
    "id": "2ce1117c-b90c-44d9-8701-f12c01d47409",
    "metadata": {},
    "source": [
-    "Now we can load the dataset. Here we select a subset including the first page of texts with ID 1 and 2."
+    "Now let's load the dataset into memory. Here we select a subset including the first page of texts with ID 1 and 2."
    ]
   },
   {
diff --git a/docs/source/tutorials/plot-main-sequence.ipynb b/docs/source/tutorials/plot-main-sequence.ipynb
index d5c3aed..437e92c 100644
--- a/docs/source/tutorials/plot-main-sequence.ipynb
+++ b/docs/source/tutorials/plot-main-sequence.ipynb
@@ -55,7 +55,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "dataset.download()\n",
     "dataset.load()"
    ]
diff --git a/docs/source/tutorials/preprocessing-raw-data.ipynb b/docs/source/tutorials/preprocessing-raw-data.ipynb
index f53b737..be0527f 100644
--- a/docs/source/tutorials/preprocessing-raw-data.ipynb
+++ b/docs/source/tutorials/preprocessing-raw-data.ipynb
@@ -60,7 +60,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "dataset.download()\n",
     "dataset.load()"
    ]
diff --git a/docs/source/tutorials/public-datasets.ipynb b/docs/source/tutorials/public-datasets.ipynb
index 758da95..8219174 100644
--- a/docs/source/tutorials/public-datasets.ipynb
+++ b/docs/source/tutorials/public-datasets.ipynb
@@ -85,7 +85,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "\n",
     "dataset.path"
    ]
@@ -95,7 +95,9 @@
    "id": "b6d791e5",
    "metadata": {},
    "source": [
-    "If you don't want to create this additional directory and just use the root path as your dataset path, you can specify the `dataset_dirname` explicitly and set it to `.`:"
+    "If you only want to specify a root directory which contains all your datasets, you can pass a `DatasetPaths` instance.\n",
+    "\n",
+    "The directory of your dataset will have the same name as in the dataset definition."
    ]
   },
   {
@@ -105,15 +107,33 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset(\n",
-    "    'ToyDataset',\n",
-    "    root='data/',\n",
-    "    dataset_dirname='.',\n",
-    ")\n",
+    "dataset_paths = pm.DatasetPaths(root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path=dataset_paths)\n",
     "\n",
     "dataset.path"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "fe8e3189",
+   "metadata": {},
+   "source": [
+    "Can also specify an alternative dataset directory for your downloaded dataset."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "31d6980d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "dataset_paths_alt = pm.DatasetPaths(root='data/', dataset='my_dataset')\n",
+    "dataset_alt = pm.PublicDataset('ToyDataset', path=dataset_paths_alt)\n",
+    "\n",
+    "dataset_alt.path"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "3f644b5b",
@@ -147,7 +167,7 @@
    "source": [
     "As we see from the download message, the dataset resource has been downloaded to a downloads directory.\n",
     "\n",
-    "You can get the path to this directory from the `downloads_rootpath` attribute:"
+    "You can get the path to this directory from the `Datset.paths.downloads` attribute:"
    ]
   },
   {
@@ -157,7 +177,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset.downloads_rootpath"
+    "dataset.paths.downloads"
    ]
   },
   {
@@ -175,13 +195,10 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset_2 = pm.PublicDataset(\n",
-    "    'ToyDataset',\n",
-    "    root='data/',\n",
-    "    downloads_dirname='new_downloads',\n",
-    ")\n",
+    "dataset_paths_3 = pm.DatasetPaths(root='data/', downloads='new_downloads')\n",
+    "dataset_3 = pm.PublicDataset('ToyDataset', path=dataset_paths_3)\n",
     "\n",
-    "dataset_2.downloads_rootpath"
+    "dataset_3.paths.downloads"
    ]
   },
   {
@@ -189,7 +206,7 @@
    "id": "8310fc82",
    "metadata": {},
    "source": [
-    "By default, all archives are recursively extracted to the `raw_rootpath`:"
+    "By default, all archives are recursively extracted to `Dataset.paths.raw`:"
    ]
   },
   {
@@ -199,7 +216,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset.raw_rootpath"
+    "dataset.paths.raw"
    ]
   },
   {
diff --git a/docs/source/tutorials/pymovements-in-10-minutes.ipynb b/docs/source/tutorials/pymovements-in-10-minutes.ipynb
index 861e070..61c5c4a 100644
--- a/docs/source/tutorials/pymovements-in-10-minutes.ipynb
+++ b/docs/source/tutorials/pymovements-in-10-minutes.ipynb
@@ -88,7 +88,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "dataset.download()"
    ]
   },
@@ -115,7 +115,7 @@
    "id": "b82bcfe3",
    "metadata": {},
    "source": [
-    "Archive files are automatically extracted into the `dataset.raw_rootpath`:"
+    "Archive files are automatically extracted into the path specified by `Dataset.paths.raw`:"
    ]
   },
   {
@@ -125,7 +125,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset.raw_rootpath"
+    "dataset.paths.raw"
    ]
   },
   {
@@ -464,7 +464,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "preprocessed_dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "preprocessed_dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "\n",
     "dataset.load(events=True, preprocessed=True, subset=subset)\n",
     "\n",
diff --git a/docs/source/tutorials/saving-loading-events.ipynb b/docs/source/tutorials/saving-loading-events.ipynb
index 9597b72..6dce996 100644
--- a/docs/source/tutorials/saving-loading-events.ipynb
+++ b/docs/source/tutorials/saving-loading-events.ipynb
@@ -60,7 +60,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "dataset.download()\n",
     "dataset.load()"
    ]
@@ -167,7 +167,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset.events_rootpath"
+    "dataset.paths.events"
    ]
   },
   {
@@ -185,7 +185,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "print(list(dataset.events_rootpath.glob('*/*/*')))"
+    "print(list(dataset.paths.events.glob('*/*/*')))"
    ]
   },
   {
@@ -193,7 +193,7 @@
    "id": "e8a95584",
    "metadata": {},
    "source": [
-    "All of the files have been saved into the `events_rootpath` as `feather` files.\n",
+    "All of the files have been saved into the `Dataset.paths.events` as `feather` files.\n",
     "\n",
     "If we want to save the data into an alternative directory and also use a different file format like `csv` we can use the following:"
    ]
@@ -252,7 +252,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "preprocessed_dataset = pm.PublicDataset('ToyDataset', root='data/')"
+    "preprocessed_dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')"
    ]
   },
   {
diff --git a/docs/source/tutorials/saving-loading-preprocessed.ipynb b/docs/source/tutorials/saving-loading-preprocessed.ipynb
index 0a7d7f3..a9cd684 100644
--- a/docs/source/tutorials/saving-loading-preprocessed.ipynb
+++ b/docs/source/tutorials/saving-loading-preprocessed.ipynb
@@ -60,7 +60,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset = pm.PublicDataset('ToyDataset', root='data/')\n",
+    "dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')\n",
     "dataset.download()\n",
     "dataset.load()"
    ]
@@ -135,7 +135,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "dataset.preprocessed_rootpath"
+    "dataset.paths.preprocessed"
    ]
   },
   {
@@ -153,7 +153,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "print(list(dataset.preprocessed_rootpath.glob('*/*/*')))"
+    "print(list(dataset.paths.preprocessed.glob('*/*/*')))"
    ]
   },
   {
@@ -161,7 +161,7 @@
    "id": "90645ecc",
    "metadata": {},
    "source": [
-    "All of the files have been saved into the `preprocessed_rootpath` as `feather` files.\n",
+    "All of the files have been saved into the `Dataset.paths.preprocessed` as `feather` files.\n",
     "\n",
     "If we want to save the data into an alternative directory and also use a different file format like `csv` we can use the following:"
    ]
@@ -220,7 +220,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "preprocessed_dataset = pm.PublicDataset('ToyDataset', root='data/')"
+    "events_dataset = pm.PublicDataset('ToyDataset', path='data/ToyDataset')"
    ]
   },
   {
@@ -238,9 +238,9 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "preprocessed_dataset.load(preprocessed=True)\n",
+    "events_dataset.load(preprocessed=True)\n",
     "\n",
-    "dataset.gaze[0].frame.head()"
+    "events_dataset.gaze[0].frame.head()"
    ]
   },
   {
@@ -266,12 +266,12 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "preprocessed_dataset.load(\n",
+    "events_dataset.load(\n",
     "    preprocessed=True,\n",
     "    preprocessed_dirname='preprocessed_csv',\n",
     "    extension='csv',\n",
     ")\n",
-    "dataset.gaze[0].frame.head()"
+    "events_dataset.gaze[0].frame.head()"
    ]
   },
   {
diff --git a/src/pymovements/__init__.py b/src/pymovements/__init__.py
index 1a07098..8e0e6d5 100644
--- a/src/pymovements/__init__.py
+++ b/src/pymovements/__init__.py
@@ -27,6 +27,7 @@ from pymovements import utils  # noqa: F401
 from pymovements.dataset import Dataset  # noqa: F401
 from pymovements.dataset import DatasetDefinition  # noqa: F401
 from pymovements.dataset import DatasetLibrary  # noqa: F401
+from pymovements.dataset import DatasetPaths  # noqa: F401
 from pymovements.dataset import PublicDataset  # noqa: F401
 from pymovements.dataset import register_dataset  # noqa: F401
 from pymovements.gaze.experiment import Experiment  # noqa: F401
diff --git a/src/pymovements/dataset/__init__.py b/src/pymovements/dataset/__init__.py
index d1e1f1e..9d43a73 100644
--- a/src/pymovements/dataset/__init__.py
+++ b/src/pymovements/dataset/__init__.py
@@ -28,10 +28,12 @@
     pymovements.dataset.Dataset
     pymovements.dataset.DatasetDefinition
     pymovements.dataset.DatasetLibrary
+    pymovements.dataset.DatasetPaths
     pymovements.dataset.PublicDataset
 """
 from pymovements.dataset.dataset import Dataset  # noqa: F401
 from pymovements.dataset.dataset_definition import DatasetDefinition  # noqa: F401
 from pymovements.dataset.dataset_library import DatasetLibrary  # noqa: F401
 from pymovements.dataset.dataset_library import register_dataset  # noqa: F401
+from pymovements.dataset.dataset_paths import DatasetPaths  # noqa: F401
 from pymovements.dataset.public_dataset import PublicDataset  # noqa: F401
diff --git a/src/pymovements/dataset/dataset.py b/src/pymovements/dataset/dataset.py
index c01d440..45a19cf 100644
--- a/src/pymovements/dataset/dataset.py
+++ b/src/pymovements/dataset/dataset.py
@@ -21,6 +21,7 @@
 from __future__ import annotations
 
 import re
+from copy import deepcopy
 from pathlib import Path
 from typing import Any
 
@@ -29,6 +30,7 @@ from tqdm.auto import tqdm
 
 from pymovements.dataset.dataset_definition import DatasetDefinition
 from pymovements.dataset.dataset_library import DatasetLibrary
+from pymovements.dataset.dataset_paths import DatasetPaths
 from pymovements.events.event_processing import EventGazeProcessor
 from pymovements.events.events import EventDataFrame
 from pymovements.events.events import EventDetectionCallable
@@ -41,42 +43,18 @@ class Dataset:
 
     def __init__(
             self,
-            definition: str | DatasetDefinition | type[DatasetDefinition] = DatasetDefinition,
-            *,
-            root: str | Path,
-            dataset_dirname: str | None = '.',
-            raw_dirname: str = 'raw',
-            preprocessed_dirname: str = 'preprocessed',
-            events_dirname: str = 'events',
+            definition: str | DatasetDefinition | type[DatasetDefinition],
+            path: str | Path | DatasetPaths,
     ):
         """Initialize the dataset object.
 
-        You can set up a custom directory structure by populating the particular dirname attributes.
-        See :py:attr:`~pymovements.dataset.Dataset.dataset_dirname`,
-        :py:attr:`~pymovements.dataset.Dataset.raw_dirname`,
-        :py:attr:`~pymovements.dataset.Dataset.preprocessed_dirname` and
-        :py:attr:`~pymovements.dataset.Dataset.events_dirname` for details.
-
         Parameters
         ----------
         definition : str, DatasetDefinition
             Dataset definition to initialize dataset with.
-        root : str, Path
-            Path to the root directory of the dataset.
-        dataset_dirname : str, optional
-            Dataset directory name under root path. Can be `.` if dataset is located in root path.
-            Default: `.`
-        raw_dirname ; str, optional
-            Name of directory under dataset path that contains raw data. Can be `.` if raw data is
-            located in dataset path. We advise the user to keep the original raw data separate from
-            the preprocessed / event data. Default: `raw`
-        preprocessed_dirname : str, optional
-            Name of directory under dataset path that will be used to store preprocessed data. We
-            advise the user to keep the preprocessed data separate from the original raw data.
-            Default: `preprocessed`
-        events_dirname : str, optional
-            Name of directory under dataset path that will be used to store event data. We advise
-            the user to keep the event data separate from the original raw data. Default: `events`
+        path : DatasetPaths, optional
+            Path to the dataset directory. You can set up a custom directory structure by passing a
+            :py:class:`~pymovements.DatasetPaths` instance.
         """
         self.fileinfo: pl.DataFrame = pl.DataFrame()
         self.gaze: list[GazeDataFrame] = []
@@ -84,20 +62,16 @@ class Dataset:
 
         if isinstance(definition, str):
             definition = DatasetLibrary.get(definition)()
-
         if isinstance(definition, type):
             definition = definition()
+        self.definition = deepcopy(definition)
 
-        if dataset_dirname is None:
-            dataset_dirname = definition.name
-
-        self.definition = definition
-
-        self._root = Path(root)
-        self.dataset_dirname = dataset_dirname
-        self.raw_dirname = raw_dirname
-        self.preprocessed_dirname = preprocessed_dirname
-        self.events_dirname = events_dirname
+        if isinstance(path, (str, Path)):
+            self.paths = DatasetPaths(root=path, dataset='.')
+        else:
+            self.paths = deepcopy(path)
+        # Fill dataset directory name with dataset definition name if specified.
+        self.paths.fill_name(self.definition.name)
 
     def load(
             self,
@@ -174,13 +148,13 @@ class Dataset:
         """
         # Get all filepaths that match regular expression.
         fileinfo_dicts = match_filepaths(
-            path=self.raw_rootpath,
+            path=self.paths.raw,
             regex=re.compile(self.definition.filename_regex),
             relative=True,
         )
 
         if len(fileinfo_dicts) == 0:
-            raise RuntimeError(f'no matching files found in {self.raw_rootpath}')
+            raise RuntimeError(f'no matching files found in {self.paths.raw}')
 
         # Create dataframe from all fileinfo records.
         fileinfo_df = pl.from_dicts(data=fileinfo_dicts, infer_schema_length=1)
@@ -298,10 +272,10 @@ class Dataset:
         # Read gaze files from fileinfo attribute.
         for fileinfo in tqdm(self.fileinfo.to_dicts()):
             filepath = Path(fileinfo['filepath'])
-            filepath = self.raw_rootpath / filepath
+            filepath = self.paths.raw / filepath
 
             if preprocessed:
-                filepath = self._raw_to_preprocessed_filepath(
+                filepath = self.paths.get_preprocessed_filepath(
                     filepath, preprocessed_dirname=preprocessed_dirname,
                     extension=extension,
                 )
@@ -360,9 +334,9 @@ class Dataset:
         # read and preprocess input files
         for fileinfo in tqdm(self.fileinfo.to_dicts()):
             filepath = Path(fileinfo['filepath'])
-            filepath = self.raw_rootpath / filepath
+            filepath = self.paths.raw / filepath
 
-            filepath = self._raw_to_event_filepath(
+            filepath = self.paths.raw_to_event_filepath(
                 filepath, events_dirname=events_dirname,
                 extension=extension,
             )
@@ -674,8 +648,8 @@ class Dataset:
         disable_progressbar = not verbose
 
         for file_id, event_df in enumerate(tqdm(self.events, disable=disable_progressbar)):
-            raw_filepath = self.raw_rootpath / Path(self.fileinfo[file_id, 'filepath'])
-            events_filepath = self._raw_to_event_filepath(
+            raw_filepath = self.paths.raw / Path(self.fileinfo[file_id, 'filepath'])
+            events_filepath = self.paths.raw_to_event_filepath(
                 raw_filepath, events_dirname=events_dirname,
                 extension=extension,
             )
@@ -736,8 +710,8 @@ class Dataset:
         disable_progressbar = not verbose
 
         for file_id, gaze_df in enumerate(tqdm(self.gaze, disable=disable_progressbar)):
-            raw_filepath = self.raw_rootpath / Path(self.fileinfo[file_id, 'filepath'])
-            preprocessed_filepath = self._raw_to_preprocessed_filepath(
+            raw_filepath = self.paths.raw / Path(self.fileinfo[file_id, 'filepath'])
+            preprocessed_filepath = self.paths.get_preprocessed_filepath(
                 raw_filepath, preprocessed_dirname=preprocessed_dirname,
                 extension=extension,
             )
@@ -774,121 +748,28 @@ class Dataset:
         Example
         -------
 
-        The default behavior is to locate the dataset in the root path without assuming a further
-        dataset directory:
-        >>> dataset = Dataset(root='/path/to/your/dataset', dataset_dirname='.')
-        >>> dataset.path  # doctest: +SKIP
-        Path('/path/to/your/dataset')
-
-        To locate the dataset you can also specify an explicit dataset directory name in the root
-        path:
-        >>> dataset = Dataset(root='path/to/all/your/datasets/', dataset_dirname='your_dataset')
-        >>> dataset.path  # doctest: +SKIP
-        Path('path/to/all/your/datasets/your_dataset')
-        """
-        return self.root / self.dataset_dirname
-
-    @property
-    def root(self) -> Path:
-        """The root path to your dataset.
-
-        Example
-        -------
-        >>> dataset = Dataset(root='/path/to/your/dataset')
-        >>> dataset.root  # doctest: +SKIP
+        By passing a `str` or a `Path` as `path` during initialization you can explicitly set the
+        directory path of the dataset:
+        >>> import pymovements as pm
+        >>>
+        >>> dataset = pm.Dataset("ToyDataset", path='/path/to/your/dataset')
+        >>> dataset.path# doctest: +SKIP
         Path('/path/to/your/dataset')
 
-        The root stays unaffected by the dataset directory name:
-        >>> dataset = Dataset(root='/path/to/', dataset_dirname='your_dataset')
-        >>> dataset.root  # doctest: +SKIP
-        Path('/path/to')
+        If you just want to specify the root directory path which holds all your local datasets, you
+        can create pass a :py:class:`~pymovements.DatasetPaths` object and set the `root`:
+        >>> paths = pm.DatasetPaths(root='/path/to/your/common/root/')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.path# doctest: +SKIP
+        Path('/path/to/your/common/root/ToyDataset')
+
+        You can also specify an alternative dataset directory name:
+        >>> paths = pm.DatasetPaths(root='/path/to/your/common/root/', dataset='my_dataset')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.path# doctest: +SKIP
+        Path('/path/to/your/common/root/my_dataset')
         """
-        return self._root
-
-    @property
-    def events_rootpath(self) -> Path:
-        """The path to the directory of the event data.
-
-        The path points to the events directory under the root path.
-
-        Example
-        -------
-
-        >>> dataset = Dataset(root='/path/to/your/datasets/', dataset_dirname='your_dataset')
-        >>> dataset.events_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/events')
-
-        You can also explicitely specify the event directory name. The default is `events`.
-        >>> dataset = Dataset(
-        ...     root='/path/to/your/datasets/',
-        ...     dataset_dirname='your_dataset',
-        ...     events_dirname='your_events',
-        ... )
-        >>> dataset.events_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/your_events')
-        """
-        return self.path / self.events_dirname
-
-    @property
-    def preprocessed_rootpath(self) -> Path:
-        """The path to the directory of the preprocessed gaze data.
-
-        The path points to the preprocessed data directory under the root path.
-
-        Example
-        -------
-
-        >>> dataset = Dataset(root='/path/to/your/datasets/', dataset_dirname='your_dataset')
-        >>> dataset.preprocessed_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/preprocessed')
-
-        You can also explicitely specify the preprocessed directory name. The default is
-        `preprocessed`.
-        >>> dataset = Dataset(
-        ...     root='/path/to/your/datasets/',
-        ...     dataset_dirname='your_dataset',
-        ...     preprocessed_dirname='your_preprocessed_data',
-        ... )
-        >>> dataset.preprocessed_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/your_preprocessed_data')
-        """
-        return self.path / self.preprocessed_dirname
-
-    @property
-    def raw_rootpath(self) -> Path:
-        """The path to the directory of the raw data.
-
-        The path points to the raw data directory under the root path.
-
-        Example
-        -------
-
-        >>> dataset = Dataset(root='/path/to/your/datasets/', dataset_dirname='your_dataset')
-        >>> dataset.preprocessed_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/raw')
-
-        You can also explicitely specify the preprocessed directory name. The default is `raw`.
-        >>> dataset = Dataset(
-        ...     root='/path/to/your/datasets/',
-        ...     dataset_dirname='your_dataset',
-        ...     raw_dirname='your_raw_data',
-        ... )
-        >>> dataset.raw_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/your_raw_data')
-
-        If your raw data is not in a separate directory under the root path then you can also
-        specify `.` as the directory name. We discourage this and advise the user to keep raw data
-        and preprocessed data separated.
-        ... dataset = Dataset(
-        >>> dataset = Dataset(
-        ...     root='/path/to/your/datasets/',
-        ...     dataset_dirname='your_dataset',
-        ...     raw_dirname='.',
-        ... )
-        >>> dataset.raw_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/your_raw_data')
-        """
-        return self.path / self.raw_dirname
+        return self.paths.dataset
 
     def _check_fileinfo(self) -> None:
         """Check if fileinfo attribute is set and there is at least one row present."""
@@ -906,90 +787,6 @@ class Dataset:
         if len(self.gaze) == 0:
             raise AttributeError('no files present in gaze attribute')
 
-    def _raw_to_preprocessed_filepath(
-            self,
-            raw_filepath: Path,
-            preprocessed_dirname: str | None = None,
-            extension: str = 'feather',
-    ) -> Path:
-        """Get preprocessed filepath in accordance to filepath of the raw file.
-
-        The preprocessed filepath will point to a feather file.
-
-        Parameters
-        ----------
-        raw_filepath : Path
-            The Path to the raw file.
-        preprocessed_dirname : str
-            One-time usage of an alternative directory name to save data relative to
-            :py:meth:`pymovements.Dataset.path`.
-            This argument is used only for this single call and does not alter
-            :py:meth:`pymovements.Dataset.preprocessed_rootpath`.
-        extension:
-            extension specifies the fileformat to store the data
-
-        Returns
-        -------
-        Path
-            The Path to the preprocessed feather file.
-        """
-        relative_raw_dirpath = raw_filepath.parent
-        relative_raw_dirpath = relative_raw_dirpath.relative_to(self.raw_rootpath)
-
-        if preprocessed_dirname is None:
-            preprocessed_rootpath = self.preprocessed_rootpath
-        else:
-            preprocessed_rootpath = self.path / preprocessed_dirname
-
-        preprocessed_file_dirpath = preprocessed_rootpath / relative_raw_dirpath
-
-        # Get new filename for saved feather file.
-        preprocessed_filename = raw_filepath.stem + '.' + extension
-
-        return preprocessed_file_dirpath / preprocessed_filename
-
-    def _raw_to_event_filepath(
-        self,
-        raw_filepath: Path,
-        events_dirname: str | None = None,
-        extension: str = 'feather',
-    ) -> Path:
-        """Get event filepath in accordance to filepath of the raw file.
-
-        The event filepath will point to file with the specified extension.
-
-        Parameters
-        ----------
-        raw_filepath : Path
-            The Path to the raw file.
-        events_dirname : str
-            One-time usage of an alternative directory name to save data relative to
-            :py:meth:`pymovements.Dataset.path`.
-            This argument is used only for this single call and does not alter
-            :py:meth:`pymovements.Dataset.events_rootpath`.
-        extension:
-            extension specifies the fileformat to store the data
-
-        Returns
-        -------
-        Path
-            The Path to the event feather file.
-        """
-        relative_raw_dirpath = raw_filepath.parent
-        relative_raw_dirpath = relative_raw_dirpath.relative_to(self.raw_rootpath)
-
-        if events_dirname is None:
-            events_rootpath = self.events_rootpath
-        else:
-            events_rootpath = self.path / events_dirname
-
-        events_file_dirpath = events_rootpath / relative_raw_dirpath
-
-        # Get new filename for saved feather file.
-        events_filename = raw_filepath.stem + '.' + extension
-
-        return events_file_dirpath / events_filename
-
     def _add_fileinfo(self, df: pl.DataFrame, fileinfo: dict[str, Any]) -> pl.DataFrame:
         """Add columns from fileinfo to dataframe.
 
diff --git a/src/pymovements/dataset/dataset_paths.py b/src/pymovements/dataset/dataset_paths.py
new file mode 100644
index 0000000..a2b1394
--- /dev/null
+++ b/src/pymovements/dataset/dataset_paths.py
@@ -0,0 +1,368 @@
+# Copyright (c) 2023 The pymovements Project Authors
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+"""DatasetPaths module."""
+from __future__ import annotations
+
+from pathlib import Path
+
+
+class DatasetPaths:
+    """Defines the paths of a dataset."""
+
+    def __init__(
+            self,
+            *,
+            root: str | Path = 'data',
+            dataset: str | None = None,
+            raw: str = 'raw',
+            events: str = 'events',
+            preprocessed: str = 'preprocessed',
+            downloads: str = 'downloads',
+    ):
+        """Initialize a set of dataset paths.
+
+        Parameters
+        ----------
+        root : str, Path
+            Path to the root directory of the dataset.
+        dataset : str, optional
+            Dataset directory name under root path. Can be `.` if dataset is located in root path.
+            Default: `.`
+        raw ; str, optional
+            Name of directory under dataset path that contains raw data. Can be `.` if raw data is
+            located in dataset path. We advise the user to keep the original raw data separate from
+            the preprocessed / event data. Default: `raw`
+        preprocessed : str, optional
+            Name of directory under dataset path that will be used to store preprocessed data. We
+            advise the user to keep the preprocessed data separate from the original raw data.
+            Default: `preprocessed`
+        events : str, optional
+            Name of directory under dataset path that will be used to store event data. We advise
+            the user to keep the event data separate from the original raw data. Default: `events`
+        downloads : str, optional
+            Name of directory to store downloaded data.Default: `downloads`
+        """
+        self._root = Path(root)
+        self._dataset = dataset
+        self._raw = raw
+        self._events = events
+        self._preprocessed = preprocessed
+        self._downloads = downloads
+
+    def get_preprocessed_filepath(
+            self,
+            raw_filepath: Path,
+            *,
+            preprocessed_dirname: str | None = None,
+            extension: str = 'feather',
+    ) -> Path:
+        """Get preprocessed filepath in accordance to filepath of the raw file.
+
+        The preprocessed filepath will point to a feather file.
+
+        Parameters
+        ----------
+        raw_filepath : Path
+            The Path to the raw file.
+        preprocessed_dirname : str
+            One-time usage of an alternative directory name to save data relative to
+            :py:meth:`pymovements.Dataset.path`.
+            This argument is used only for this single call and does not alter
+            :py:meth:`pymovements.Dataset.preprocessed_rootpath`.
+        extension:
+            extension specifies the fileformat to store the data
+
+        Returns
+        -------
+        Path
+            The Path to the preprocessed feather file.
+        """
+        relative_raw_dirpath = raw_filepath.parent
+        relative_raw_dirpath = relative_raw_dirpath.relative_to(self.raw)
+
+        if preprocessed_dirname is None:
+            preprocessed_rootpath = self.preprocessed
+        else:
+            preprocessed_rootpath = self.dataset / preprocessed_dirname
+
+        preprocessed_file_dirpath = preprocessed_rootpath / relative_raw_dirpath
+
+        # Get new filename for saved feather file.
+        preprocessed_filename = raw_filepath.stem + '.' + extension
+
+        return preprocessed_file_dirpath / preprocessed_filename
+
+    def raw_to_event_filepath(
+            self,
+            raw_filepath: Path,
+            *,
+            events_dirname: str | None = None,
+            extension: str = 'feather',
+    ) -> Path:
+        """Get event filepath in accordance to filepath of the raw file.
+
+        The event filepath will point to file with the specified extension.
+
+        Parameters
+        ----------
+        raw_filepath : Path
+            The Path to the raw file.
+        events_dirname : str
+            One-time usage of an alternative directory name to save data relative to
+            :py:meth:`pymovements.Dataset.path`.
+            This argument is used only for this single call and does not alter
+            :py:meth:`pymovements.Dataset.events_rootpath`.
+        extension:
+            extension specifies the fileformat to store the data
+
+        Returns
+        -------
+        Path
+            The Path to the event feather file.
+        """
+        relative_raw_dirpath = raw_filepath.parent
+        relative_raw_dirpath = relative_raw_dirpath.relative_to(self.raw)
+
+        if events_dirname is None:
+            events_rootpath = self.events
+        else:
+            events_rootpath = self.dataset / events_dirname
+
+        events_file_dirpath = events_rootpath / relative_raw_dirpath
+
+        # Get new filename for saved feather file.
+        events_filename = raw_filepath.stem + '.' + extension
+
+        return events_file_dirpath / events_filename
+
+    @property
+    def root(self) -> Path:
+        """The root path to your dataset.
+
+        Example
+        -------
+        >>> import pymovements as pm
+        >>>
+        >>> dataset = pm.Dataset("ToyDataset", path='/path/to/your/dataset')
+        >>> dataset.paths.root# doctest: +SKIP
+        Path('/path/to/your/dataset')
+
+        This is the same as:
+        >>> paths = pm.DatasetPaths(root='/path/to/your/dataset', dataset='.')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.paths.root# doctest: +SKIP
+        Path('/path/to/your/dataset')
+
+        The root stays unaffected by the dataset directory name:
+        >>> paths = pm.DatasetPaths(root='/path/to/your/dataset', dataset='your_dataset')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.paths.root# doctest: +SKIP
+        Path('/path/to/your/dataset')
+        """
+        return self._root
+
+    @property
+    def dataset(self) -> Path:
+        """The path to the dataset directory.
+
+        Example
+        -------
+
+        By passing a `str` or a `Path` as `path` during initialization you can explicitly set the
+        directory path of the dataset:
+        >>> import pymovements as pm
+        >>>
+        >>> dataset = pm.Dataset("ToyDataset", path='/path/to/your/dataset')
+        >>> dataset.path# doctest: +SKIP
+        Path('/path/to/your/dataset')
+
+        If you just want to specify the root directory path which holds all your local datasets, you
+        can create pass a :py:class:`~pymovements.DatasetPaths` object and set the `root`:
+        >>> paths = pm.DatasetPaths(root='/path/to/your/common/root/')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.path# doctest: +SKIP
+        Path('/path/to/your/common/root/ToyDataset')
+
+        You can also specify an alternative dataset directory name:
+        >>> paths = pm.DatasetPaths(root='/path/to/your/common/root/', dataset='my_dataset')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.path# doctest: +SKIP
+        Path('/path/to/your/common/root/my_dataset')
+
+        If your dataset is not in a separate directory under the root path then you can also
+        specify `.` as the directory name. We discourage this and advise the user to have a
+        directory which holds all datasets with sub-directories using the registered dataset names.
+        ... dataset = Dataset(
+        >>> paths = pm.DatasetPaths(root='/path/to/your/dataset/', dataset='.')
+        >>> dataset = pm.PublicDataset("ToyDataset", path=paths)
+        >>> dataset.path# doctest: +SKIP
+        Path('/path/to/your/dataset')
+        """
+        if self._dataset is None:
+            return self._root
+        return self._root / self._dataset
+
+    def fill_name(self, name: str) -> None:
+        """Fill dataset directory name with dataset name.
+
+        Dataset directory name will only be updated if :py:attr:pymovements.DatasetPaths.dataset` is
+        ``None``.
+
+        Parameters
+        ----------
+        name
+            Name to update dataset directory name with. The dataset directory name will only be
+            updated if :py:attr:pymovements.DatasetPaths.dataset` is ``None``.
+        """
+        if self._dataset is None:
+            self._dataset = name
+
+    @property
+    def events(self) -> Path:
+        """The path to the directory of the event data.
+
+        The path points to the events directory under the dataset path.
+
+        Example
+        -------
+
+        >>> import pymovements as pm
+        >>>
+        >>> dataset = pm.PublicDataset("ToyDataset", path='/path/to/your/dataset/')
+        >>> dataset.paths.events# doctest: +SKIP
+        Path('/path/to/your/dataset/events')
+
+        If you just want to specify the root directory path which holds all your local datasets, you
+        can create pass a :py:class:`~pymovements.DatasetPaths` object and set the `root`:
+        >>> paths = pm.DatasetPaths(root='/path/to/your/common/root/')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.paths.events# doctest: +SKIP
+        Path('/path/to/your/common/root/ToyDataset/events')
+
+        This way you can also explicitely specify the events directory name. The default is
+        `events`.
+        >>> paths = pm.DatasetPaths(root='/path/to/your/datasets/', events='my_events')
+        >>> dataset = pm.PublicDataset("ToyDataset", path=paths)
+        >>> dataset.paths.events# doctest: +SKIP
+        Path('/path/to/your/datasets/ToyDataset/my_events')
+        """
+        return self.dataset / self._events
+
+    @property
+    def preprocessed(self) -> Path:
+        """The path to the directory of the preprocessed gaze data.
+
+        The path points to the preprocessed data directory under the dataset path.
+
+        Example
+        -------
+
+        >>> import pymovements as pm
+        >>>
+        >>> dataset = pm.PublicDataset("ToyDataset", path='/path/to/your/dataset/')
+        >>> dataset.paths.preprocessed# doctest: +SKIP
+        Path('/path/to/your/dataset/preprocessed')
+
+        If you just want to specify the root directory path which holds all your local datasets, you
+        can create pass a :py:class:`~pymovements.DatasetPaths` object and set the `root`:
+        >>> paths = pm.DatasetPaths(root='path/to/your/common/root/')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.paths.preprocessed# doctest: +SKIP
+        Path('path/to/your/common/root/ToyDataset/preprocessed')
+
+        This way you can also explicitely specify the events directory name. The default is
+        `preprocessed`.
+        >>> paths = pm.DatasetPaths(
+        ...     root='/path/to/your/datasets/', preprocessed='my_preprocessed_data',
+        ... )
+        >>> dataset = pm.PublicDataset("ToyDataset", path=paths)
+        >>> dataset.paths.preprocessed# doctest: +SKIP
+        Path('/path/to/your/datasets/ToyDataset/my_preprocessed_data')
+        """
+        return self.dataset / self._preprocessed
+
+    @property
+    def raw(self) -> Path:
+        """The path to the directory of the raw data.
+
+        The path points to the raw data directory under the dataset path.
+
+        Example
+        -------
+
+        >>> import pymovements as pm
+        >>>
+        >>> dataset = pm.PublicDataset("ToyDataset", path='/path/to/your/dataset/')
+        >>> dataset.paths.raw# doctest: +SKIP
+        Path('/path/to/your/dataset/raw')
+
+        If you just want to specify the root directory path which holds all your local datasets, you
+        can create pass a :py:class:`~pymovements.DatasetPaths` object and set the `root`:
+        >>> paths = pm.DatasetPaths(root='path/to/your/common/root/')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.paths.raw# doctest: +SKIP
+        Path('path/to/your/common/root/ToyDataset/raw')
+
+        This way you can also explicitely specify the raw directory name. The default is `raw`.
+        >>> paths = pm.DatasetPaths(root='/path/to/your/datasets/', raw='my_raw')
+        >>> dataset = pm.PublicDataset("ToyDataset", path=paths)
+        >>> dataset.paths.raw# doctest: +SKIP
+        Path('/path/to/your/datasets/ToyDataset/my_raw')
+
+        If your raw data is not in a separate directory under the root path then you can also
+        specify `.` as the directory name. We discourage this and advise the user to keep raw data
+        and preprocessed data separated.
+        ... dataset = Dataset(
+        >>> paths = pm.DatasetPaths(root='/path/to/your/datasets/', raw='.')
+        >>> dataset = pm.PublicDataset("ToyDataset", path=paths)
+        >>> dataset.paths.raw# doctest: +SKIP
+        Path('/path/to/your/datasets/ToyDataset')
+        """
+        return self.dataset / self._raw
+
+    @property
+    def downloads(self) -> Path:
+        """The path to the directory of the raw data.
+
+        The download path points to the download directory under the root path.
+
+        Example
+        -------
+        >>> import pymovements as pm
+        >>>
+        >>> dataset = pm.PublicDataset("ToyDataset", path='/path/to/your/dataset/')
+        >>> dataset.paths.downloads# doctest: +SKIP
+        Path('/path/to/your/dataset/downloads')
+
+        If you just want to specify the root directory path which holds all your local datasets, you
+        can create pass a :py:class:`~pymovements.DatasetPaths` object and set the `root`:
+        >>> paths = pm.DatasetPaths(root='path/to/your/common/root/')
+        >>> dataset = pm.Dataset("ToyDataset", path=paths)
+        >>> dataset.paths.downloads# doctest: +SKIP
+        Path('path/to/your/common/root/ToyDataset/downloads')
+
+        This way you can also explicitely specify the download directory name. The default is
+        `downloads`.
+        >>> paths = pm.DatasetPaths(root='/path/to/your/datasets/', downloads='my_downloads')
+        >>> dataset = pm.PublicDataset("ToyDataset", path=paths)
+        >>> dataset.paths.downloads# doctest: +SKIP
+        Path('/path/to/your/datasets/ToyDataset/my_downloads')
+        """
+        return self.dataset / self._downloads
diff --git a/src/pymovements/dataset/public_dataset.py b/src/pymovements/dataset/public_dataset.py
index 27298d4..9f06241 100644
--- a/src/pymovements/dataset/public_dataset.py
+++ b/src/pymovements/dataset/public_dataset.py
@@ -26,6 +26,7 @@ from urllib.error import URLError
 
 from pymovements.dataset.dataset import Dataset
 from pymovements.dataset.dataset_definition import DatasetDefinition
+from pymovements.dataset.dataset_paths import DatasetPaths
 from pymovements.utils.archives import extract_archive
 from pymovements.utils.downloads import download_file
 
@@ -41,61 +42,25 @@ class PublicDataset(Dataset, metaclass=ABCMeta):
     def __init__(
             self,
             definition: str | DatasetDefinition | type[DatasetDefinition],
-            *,
-            root: str | Path,
-            dataset_dirname: str | None = None,
-            downloads_dirname: str = 'downloads',
-            raw_dirname: str = 'raw',
-            preprocessed_dirname: str = 'preprocessed',
-            events_dirname: str = 'events',
+            path: str | Path | DatasetPaths,
     ):
         """Initialize the public dataset object.
 
-        You can set up a custom directory structure by populating the particular dirname attributes.
-        See :py:attr:`~pymovements.dataset.PublicDataset.dataset_dirname`,
-        :py:attr:`~pymovements.dataset.PublicDataset.raw_dirname`,
-        :py:attr:`~pymovements.dataset.PublicDataset.preprocessed_dirname` and
-        :py:attr:`~pymovements.dataset.PublicDataset.events_dirname` and
-        :py:attr:`~pymovements.dataset.PublicDataset.downloads_dirname` for details.
-
         Parameters
         ----------
         definition : str, DatasetDefinition
             Dataset definition to initialize dataset with.
-        root : str, Path
-            Path to the root directory of the dataset.
-        dataset_dirname : str, optional
-            Dataset directory name under root path. Can be `.` if dataset is located in root path.
-            Default: `.`
-        downloads_dirname : str, optional
-            Name of directory to store downloaded data.Default: `downloads`
-        raw_dirname ; str, optional
-            Name of directory under dataset path that contains raw data. Can be `.` if raw data is
-            located in dataset path. We advise the user to keep the original raw data separate from
-            the preprocessed / event data. Default: `raw`
-        preprocessed_dirname : str, optional
-            Name of directory under dataset path that will be used to store preprocessed data. We
-            advise the user to keep the preprocessed data separate from the original raw data.
-            Default: `preprocessed`
-        events_dirname : str, optional
-            Name of directory under dataset path that will be used to store event data. We advise
-            the user to keep the event data separate from the original raw data. Default: `events`
+        path : DatasetPaths, optional
+            Path to the directory of the dataset. You can set up a custom directory structure by
+            passing a :py:class:`~pymovements.DatasetPaths` instance.
         """
-        super().__init__(
-            definition=definition,
-            root=root,
-            dataset_dirname=dataset_dirname,
-            raw_dirname=raw_dirname,
-            preprocessed_dirname=preprocessed_dirname,
-            events_dirname=events_dirname,
-        )
-        self.downloads_dirname = downloads_dirname
+        super().__init__(definition=definition, path=path)
 
     def download(self, *, extract: bool = True, remove_finished: bool = False) -> Dataset:
-        """Download dataset.
+        """Download dataset resources.
 
         This downloads all resources of the dataset. Per default this also extracts all archives
-        into :py:meth:`Dataset.raw_rootpath`,
+        into :py:meth:`Dataset.paths.raw`,
         To save space on your device you can remove the archive files after
         successful extraction with ``remove_finished=True``.
 
@@ -130,7 +95,7 @@ class PublicDataset(Dataset, metaclass=ABCMeta):
         if len(self.definition.resources) == 0:
             raise AttributeError('number of resources must not be zero to download dataset')
 
-        self.raw_rootpath.mkdir(parents=True, exist_ok=True)
+        self.paths.raw.mkdir(parents=True, exist_ok=True)
 
         for resource in self.definition.resources:
             success = False
@@ -142,7 +107,7 @@ class PublicDataset(Dataset, metaclass=ABCMeta):
                 try:
                     download_file(
                         url=url,
-                        dirpath=self.downloads_rootpath,
+                        dirpath=self.paths.downloads,
                         filename=resource['filename'],
                         md5=resource['md5'],
                     )
@@ -168,7 +133,7 @@ class PublicDataset(Dataset, metaclass=ABCMeta):
         return self
 
     def extract(self, remove_finished: bool = False) -> Dataset:
-        """Extract dataset archives.
+        """Extract downloaded dataset archive files.
 
         Parameters
         ----------
@@ -180,107 +145,13 @@ class PublicDataset(Dataset, metaclass=ABCMeta):
         PublicDataset
             Returns self, useful for method cascading.
         """
-        self.raw_rootpath.mkdir(parents=True, exist_ok=True)
+        self.paths.raw.mkdir(parents=True, exist_ok=True)
 
         for resource in self.definition.resources:
             extract_archive(
-                source_path=self.downloads_rootpath / resource['filename'],
-                destination_path=self.raw_rootpath,
+                source_path=self.paths.downloads / resource['filename'],
+                destination_path=self.paths.raw,
                 recursive=True,
                 remove_finished=remove_finished,
             )
         return self
-
-    @property
-    def path(self) -> Path:
-        """The path to the dataset directory.
-
-        The dataset path points to the dataset directory under the root path. Per default the
-        dataset directory name is equal to the class name.
-
-        Example
-        -------
-        Let's define a custom class first with one mirror and one resource.
-        >>> class CustomPublicDataset(DatasetDefinition):
-        ...     mirrors = ['https://www.example.com/']
-        ...     resources = [
-        ...         {
-        ...             'resource': 'relative_path_to_resource',
-        ...             'filename': 'example_dataset.zip',
-        ...             'md5': 'md5md5md5md5md5md5md5md5md5md5md',
-        ...         },
-        ...     ]
-
-        The default behaviour is to locate the data set in a directory under the root path with the
-        same name as the class name:
-        >>> dataset = PublicDataset(CustomPublicDataset, root='/path/to/all/your/datasets')
-        >>> dataset.path  # doctest: +SKIP
-        Path('/path/to/all/your/datasets/CustomPublicDataset')
-
-        You can specify an explicit dataset directory name:
-        >>> dataset = PublicDataset(
-        ...     CustomPublicDataset,
-        ...     root='/path/to/all/your/datasets',
-        ...     dataset_dirname='custom_dataset',
-        ... )
-        >>> dataset.path  # doctest: +SKIP
-        Path('/path/to/all/your/datasets/custom_dataset')
-
-        You can specify to use the root path to be the actual dataset directory:
-        >>> dataset = PublicDataset(
-        ...     CustomPublicDataset,
-        ...     root='/path/to/your/dataset/',
-        ...     dataset_dirname='.',
-        ... )
-        >>> dataset.path  # doctest: +SKIP
-        Path('/path/to/your/dataset')
-        """
-        return super().path
-
-    @property
-    def downloads_rootpath(self) -> Path:
-        """The path to the directory of the raw data.
-
-        The download path points to the download directory under the root path.
-
-        Example
-        -------
-        Let's define a custom class first with one mirror and one resource.
-        >>> class CustomPublicDataset(DatasetDefinition):
-        ...     mirrors: tuple[str] = ('https://www.example.com/', )
-        ...     resources: tuple[dict[str, str]] = (
-        ...         {
-        ...             'resource': 'relative_path_to_resource',
-        ...             'filename': 'example_dataset.zip',
-        ...             'md5': 'md5md5md5md5md5md5md5md5md5md5md',
-        ...         },
-        ...     )
-
-        >>> dataset = PublicDataset(CustomPublicDataset, root='/path/to/your/datasets/')
-        >>> dataset.downloads_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/CustomPublicDataset/downloads')
-
-        You can also explicitely specify the preprocessed directory name. The default is `raw`.
-        >>> dataset = PublicDataset(
-        ...     CustomPublicDataset,
-        ...     root='/path/to/your/datasets/',
-        ...     dataset_dirname='your_dataset',
-        ...     downloads_dirname='your_raw_data',
-        ... )
-        >>> dataset.downloads_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/your_raw_data')
-
-        If your raw data is not in a separate directory under the root path then you can also
-        specify `.` as the directory name. We discourage this and advise the user to keep raw data
-        and preprocessed data separated.
-        ... dataset = Dataset(
-        >>> dataset = PublicDataset(
-        ...     CustomPublicDataset,
-        ...     root='/path/to/your/datasets/',
-        ...     dataset_dirname='your_dataset',
-        ...     raw_dirname='.',
-        ... )
-        >>> dataset.preprocessed_rootpath  # doctest: +SKIP
-        Path('/path/to/your/datasets/your_dataset/your_raw_data')
-        """
-        return self.path / self.downloads_dirname
diff --git a/src/pymovements/datasets/gazebase.py b/src/pymovements/datasets/gazebase.py
index 12fa4df..7979e54 100644
--- a/src/pymovements/datasets/gazebase.py
+++ b/src/pymovements/datasets/gazebase.py
@@ -86,7 +86,7 @@ class GazeBase(DatasetDefinition):
 
     >>> import pymovements as pm
     >>>
-    >>> dataset = pm.PublicDataset("GazeBase", root='data/')
+    >>> dataset = pm.PublicDataset("GazeBase", path='data/GazeBase')
 
     Download the dataset resources resources:
 
diff --git a/src/pymovements/datasets/judo1000.py b/src/pymovements/datasets/judo1000.py
index 23bc64e..8781b6a 100644
--- a/src/pymovements/datasets/judo1000.py
+++ b/src/pymovements/datasets/judo1000.py
@@ -79,7 +79,7 @@ class JuDo1000(DatasetDefinition):
 
     >>> import pymovements as pm
     >>>
-    >>> dataset = pm.PublicDataset("JuDo1000", root='data/')
+    >>> dataset = pm.PublicDataset("JuDo1000", path='data/JuDo1000')
 
     Download the dataset resources resources:
 
diff --git a/src/pymovements/datasets/toy_dataset.py b/src/pymovements/datasets/toy_dataset.py
index 7690294..a7459c8 100644
--- a/src/pymovements/datasets/toy_dataset.py
+++ b/src/pymovements/datasets/toy_dataset.py
@@ -77,7 +77,7 @@ class ToyDataset(DatasetDefinition):
 
     >>> import pymovements as pm
     >>>
-    >>> dataset = pm.PublicDataset("ToyDataset", root='data/')
+    >>> dataset = pm.PublicDataset("ToyDataset", path='data/ToyDataset')
 
     Download the dataset resources resources:
 
diff --git a/src/pymovements/utils/paths.py b/src/pymovements/utils/paths.py
index e9b0e63..95cf565 100644
--- a/src/pymovements/utils/paths.py
+++ b/src/pymovements/utils/paths.py
@@ -110,8 +110,12 @@ def match_filepaths(
         If ``path`` does not point to a directory.
     """
     path = Path(path)
+
+    if not path.exists():
+        raise ValueError(f'path does not exist (path = {path})')
+
     if not path.is_dir():
-        raise ValueError(f'path must point to a directory, but points to a file (path = {path})')
+        raise ValueError(f'path must point to a directory (path = {path})')
 
     if relative and relative_anchor is None:
         relative_anchor = path


Test patch:
diff --git a/tests/dataset/dataset_paths_test.py b/tests/dataset/dataset_paths_test.py
new file mode 100644
index 0000000..15071a5
--- /dev/null
+++ b/tests/dataset/dataset_paths_test.py
@@ -0,0 +1,125 @@
+# Copyright (c) 2023 The pymovements Project Authors
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+"""Test DatasetPaths."""
+from pathlib import Path
+
+import pytest
+
+import pymovements as pm
+
+
+@pytest.mark.parametrize(
+    'init_kwargs, expected_paths',
+    [
+        pytest.param(
+            {'root': '/data/set/path'},
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/'),
+                'raw': Path('/data/set/path/raw'),
+                'preprocessed': Path('/data/set/path/preprocessed'),
+                'events': Path('/data/set/path/events'),
+                'downloads': Path('/data/set/path/downloads'),
+            },
+            id='only_root',
+        ),
+        pytest.param(
+            {'root': '/data/set/path', 'dataset': '.'},
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/'),
+                'raw': Path('/data/set/path/raw'),
+                'preprocessed': Path('/data/set/path/preprocessed'),
+                'events': Path('/data/set/path/events'),
+                'downloads': Path('/data/set/path/downloads'),
+            },
+            id='dataset_dot',
+        ),
+        pytest.param(
+            {'root': '/data/set/path', 'dataset': 'dataset'},
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/dataset'),
+                'raw': Path('/data/set/path/dataset/raw'),
+                'preprocessed': Path('/data/set/path/dataset/preprocessed'),
+                'events': Path('/data/set/path/dataset/events'),
+                'downloads': Path('/data/set/path/dataset/downloads'),
+            },
+            id='explicit_dataset',
+        ),
+        pytest.param(
+            {'root': '/data/set/path', 'dataset': 'dataset', 'events': 'custom_events'},
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/dataset'),
+                'raw': Path('/data/set/path/dataset/raw'),
+                'preprocessed': Path('/data/set/path/dataset/preprocessed'),
+                'events': Path('/data/set/path/dataset/custom_events'),
+                'downloads': Path('/data/set/path/dataset/downloads'),
+            },
+            id='explicit_events',
+        ),
+        pytest.param(
+            {'root': '/data/set/path', 'dataset': 'dataset', 'preprocessed': 'custom_preprocessed'},
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/dataset'),
+                'raw': Path('/data/set/path/dataset/raw'),
+                'preprocessed': Path('/data/set/path/dataset/custom_preprocessed'),
+                'events': Path('/data/set/path/dataset/events'),
+                'downloads': Path('/data/set/path/dataset/downloads'),
+            },
+            id='explicit_preprocessed',
+        ),
+        pytest.param(
+            {'root': '/data/set/path', 'dataset': 'dataset', 'raw': 'custom_raw'},
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/dataset'),
+                'raw': Path('/data/set/path/dataset/custom_raw'),
+                'preprocessed': Path('/data/set/path/dataset/preprocessed'),
+                'events': Path('/data/set/path/dataset/events'),
+                'downloads': Path('/data/set/path/dataset/downloads'),
+            },
+            id='explicit_raw',
+        ),
+        pytest.param(
+            {'root': '/data/set/path', 'dataset': 'dataset', 'downloads': 'custom_downloads'},
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/dataset'),
+                'raw': Path('/data/set/path/dataset/raw'),
+                'preprocessed': Path('/data/set/path/dataset/preprocessed'),
+                'events': Path('/data/set/path/dataset/events'),
+                'downloads': Path('/data/set/path/dataset/custom_downloads'),
+            },
+            id='explicit_downloads',
+        ),
+    ],
+)
+def test_dataset_paths(init_kwargs, expected_paths):
+    paths = pm.DatasetPaths(**init_kwargs)
+
+    assert paths.root == expected_paths['root']
+    assert paths.dataset == expected_paths['dataset']
+    assert paths.raw == expected_paths['raw']
+    assert paths.preprocessed == expected_paths['preprocessed']
+    assert paths.events == expected_paths['events']
+    assert paths.downloads == expected_paths['downloads']
diff --git a/tests/dataset/dataset_test.py b/tests/dataset/dataset_test.py
index 3edec36..14b2af3 100644
--- a/tests/dataset/dataset_test.py
+++ b/tests/dataset/dataset_test.py
@@ -28,13 +28,7 @@ import polars as pl
 import pytest
 from polars.testing import assert_frame_equal
 
-from pymovements import exceptions
-from pymovements.dataset.dataset import Dataset
-from pymovements.dataset.dataset_definition import DatasetDefinition
-from pymovements.events.detection.engbert import microsaccades
-from pymovements.events.detection.ivt import ivt
-from pymovements.events.events import EventDataFrame
-from pymovements.gaze.experiment import Experiment
+import pymovements as pm
 
 
 def create_raw_gaze_files_from_fileinfo(gaze_dfs, fileinfo, rootpath):
@@ -226,8 +220,8 @@ def mock_toy(rootpath, raw_fileformat, eyes):
 
     create_event_files_from_fileinfo(event_dfs, fileinfo, rootpath / 'events')
 
-    dataset_definition = DatasetDefinition(
-        experiment=Experiment(
+    dataset_definition = pm.DatasetDefinition(
+        experiment=pm.Experiment(
             screen_width_px=1280,
             screen_height_px=1024,
             screen_width_cm=38,
@@ -243,7 +237,7 @@ def mock_toy(rootpath, raw_fileformat, eyes):
     return {
         'init_kwargs': {
             'definition': dataset_definition,
-            'root': rootpath,
+            'path': pm.DatasetPaths(root=rootpath, dataset='.'),
         },
         'fileinfo': fileinfo,
         'raw_gaze_dfs': gaze_dfs,
@@ -274,7 +268,7 @@ def fixture_dataset(request, tmp_path):
 
 
 def test_load_correct_fileinfo(dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
 
     expected_fileinfo = dataset_configuration['fileinfo']
@@ -282,7 +276,7 @@ def test_load_correct_fileinfo(dataset_configuration):
 
 
 def test_load_correct_raw_gaze_dfs(dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
 
     expected_gaze_dfs = dataset_configuration['raw_gaze_dfs']
@@ -291,7 +285,7 @@ def test_load_correct_raw_gaze_dfs(dataset_configuration):
 
 
 def test_load_correct_preprocessed_gaze_dfs(dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load(preprocessed=True)
 
     expected_gaze_dfs = dataset_configuration['preprocessed_gaze_dfs']
@@ -300,7 +294,7 @@ def test_load_correct_preprocessed_gaze_dfs(dataset_configuration):
 
 
 def test_load_correct_event_dfs(dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load(events=True)
 
     expected_event_dfs = dataset_configuration['event_dfs']
@@ -324,7 +318,7 @@ def test_load_correct_event_dfs(dataset_configuration):
     ],
 )
 def test_load_subset(subset, fileinfo_idx, dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load(subset=subset)
 
     expected_fileinfo = dataset_configuration['fileinfo']
@@ -364,7 +358,7 @@ def test_load_subset(subset, fileinfo_idx, dataset_configuration):
 )
 def test_load_exceptions(init_kwargs, load_kwargs, exception, dataset_configuration):
     init_kwargs = {**dataset_configuration['init_kwargs'], **init_kwargs}
-    dataset = Dataset(**init_kwargs)
+    dataset = pm.Dataset(**init_kwargs)
 
     with pytest.raises(exception):
         dataset.load(**load_kwargs)
@@ -383,7 +377,7 @@ def test_load_exceptions(init_kwargs, load_kwargs, exception, dataset_configurat
 )
 def test_save_gaze_exceptions(init_kwargs, save_kwargs, exception, dataset_configuration):
     init_kwargs = {**dataset_configuration['init_kwargs'], **init_kwargs}
-    dataset = Dataset(**init_kwargs)
+    dataset = pm.Dataset(**init_kwargs)
 
     with pytest.raises(exception):
         dataset.load()
@@ -405,14 +399,14 @@ def test_save_gaze_exceptions(init_kwargs, save_kwargs, exception, dataset_confi
 )
 def test_load_events_exceptions(init_kwargs, load_kwargs, exception, dataset_configuration):
     init_kwargs = {**dataset_configuration['init_kwargs'], **init_kwargs}
-    dataset = Dataset(**init_kwargs)
+    dataset = pm.Dataset(**init_kwargs)
 
     with pytest.raises(exception):
         dataset.load()
         dataset.pix2deg()
         dataset.pos2vel()
         dataset.detect_events(
-            method=ivt,
+            method=pm.events.ivt,
             velocity_threshold=45,
             minimum_duration=55,
         )
@@ -433,14 +427,14 @@ def test_load_events_exceptions(init_kwargs, load_kwargs, exception, dataset_con
 )
 def test_save_events_exceptions(init_kwargs, save_kwargs, exception, dataset_configuration):
     init_kwargs = {**dataset_configuration['init_kwargs'], **init_kwargs}
-    dataset = Dataset(**init_kwargs)
+    dataset = pm.Dataset(**init_kwargs)
 
     with pytest.raises(exception):
         dataset.load()
         dataset.pix2deg()
         dataset.pos2vel()
         dataset.detect_events(
-            method=ivt,
+            method=pm.events.ivt,
             velocity_threshold=45,
             minimum_duration=55,
         )
@@ -449,10 +443,10 @@ def test_save_events_exceptions(init_kwargs, save_kwargs, exception, dataset_con
 
 def test_load_no_files_raises_exception(dataset_configuration):
     init_kwargs = {**dataset_configuration['init_kwargs']}
-    dataset = Dataset(**init_kwargs)
+    dataset = pm.Dataset(**init_kwargs)
 
-    shutil.rmtree(dataset.raw_rootpath, ignore_errors=True)
-    dataset.raw_rootpath.mkdir()
+    shutil.rmtree(dataset.paths.raw, ignore_errors=True)
+    dataset.paths.raw.mkdir()
 
     with pytest.raises(RuntimeError):
         dataset.load()
@@ -466,14 +460,14 @@ def test_load_no_files_raises_exception(dataset_configuration):
 )
 @pytest.mark.parametrize('dataset_configuration', ['ToyMat'], indirect=['dataset_configuration'])
 def test_load_mat_file_exception(exception, dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
 
     with pytest.raises(exception):
         dataset.load()
 
 
 def test_pix2deg(dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
 
     original_schema = dataset.gaze[0].schema
@@ -494,7 +488,7 @@ def test_pix2deg(dataset_configuration):
 
 
 def test_pos2vel(dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
 
@@ -518,7 +512,7 @@ def test_pos2vel(dataset_configuration):
     [
         pytest.param(
             {
-                'method': microsaccades,
+                'method': pm.events.microsaccades,
                 'threshold': 1,
                 'eye': 'auto',
             },
@@ -527,14 +521,14 @@ def test_pos2vel(dataset_configuration):
     ],
 )
 def test_detect_events_auto_eye(detect_event_kwargs, dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
     dataset.pos2vel()
     dataset.detect_events(**detect_event_kwargs)
 
     expected_schema = {
-        'subject_id': pl.Int64, **EventDataFrame._minimal_schema, 'duration': pl.Int64,
+        'subject_id': pl.Int64, **pm.events.EventDataFrame._minimal_schema, 'duration': pl.Int64,
     }
     for result_event_df in dataset.events:
         assert result_event_df.schema == expected_schema
@@ -545,7 +539,7 @@ def test_detect_events_auto_eye(detect_event_kwargs, dataset_configuration):
     [
         pytest.param(
             {
-                'method': microsaccades,
+                'method': pm.events.microsaccades,
                 'threshold': 1,
                 'eye': 'left',
             },
@@ -553,7 +547,7 @@ def test_detect_events_auto_eye(detect_event_kwargs, dataset_configuration):
         ),
         pytest.param(
             {
-                'method': microsaccades,
+                'method': pm.events.microsaccades,
                 'threshold': 1,
                 'eye': 'right',
             },
@@ -561,7 +555,7 @@ def test_detect_events_auto_eye(detect_event_kwargs, dataset_configuration):
         ),
         pytest.param(
             {
-                'method': microsaccades,
+                'method': pm.events.microsaccades,
                 'threshold': 1,
                 'eye': 'eye',
             },
@@ -570,7 +564,7 @@ def test_detect_events_auto_eye(detect_event_kwargs, dataset_configuration):
     ],
 )
 def test_detect_events_explicit_eye(detect_event_kwargs, dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
     dataset.pos2vel()
@@ -586,7 +580,9 @@ def test_detect_events_explicit_eye(detect_event_kwargs, dataset_configuration):
         dataset.detect_events(**detect_event_kwargs)
 
         expected_schema = {
-            'subject_id': pl.Int64, **EventDataFrame._minimal_schema, 'duration': pl.Int64,
+            'subject_id': pl.Int64,
+            **pm.events.EventDataFrame._minimal_schema,
+            'duration': pl.Int64,
         }
 
         for result_event_df in dataset.events:
@@ -602,30 +598,38 @@ def test_detect_events_explicit_eye(detect_event_kwargs, dataset_configuration):
     [
         pytest.param(
             {
-                'method': microsaccades,
+                'method': pm.events.microsaccades,
                 'threshold': 1,
                 'eye': 'auto',
             },
             {
-                'method': microsaccades,
+                'method': pm.events.microsaccades,
                 'threshold': 1,
                 'eye': 'auto',
             },
-            {'subject_id': pl.Int64, **EventDataFrame._minimal_schema, 'duration': pl.Int64},
+            {
+                'subject_id': pl.Int64,
+                **pm.events.EventDataFrame._minimal_schema,
+                'duration': pl.Int64,
+            },
             id='two-saccade-runs',
         ),
         pytest.param(
             {
-                'method': microsaccades,
+                'method': pm.events.microsaccades,
                 'threshold': 1,
                 'eye': 'auto',
             },
             {
-                'method': ivt,
+                'method': pm.events.ivt,
                 'velocity_threshold': 1,
                 'minimum_duration': 1,
             },
-            {'subject_id': pl.Int64, **EventDataFrame._minimal_schema, 'duration': pl.Int64},
+            {
+                'subject_id': pl.Int64,
+                **pm.events.EventDataFrame._minimal_schema,
+                'duration': pl.Int64,
+            },
             id='one-saccade-one-fixation-run',
         ),
     ],
@@ -634,7 +638,7 @@ def test_detect_events_multiple_calls(
         detect_event_kwargs_1, detect_event_kwargs_2,
         expected_schema, dataset_configuration,
 ):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
     dataset.pos2vel()
@@ -646,7 +650,7 @@ def test_detect_events_multiple_calls(
 
 
 def test_detect_events_attribute_error(dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
 
     try:
@@ -665,7 +669,7 @@ def test_detect_events_attribute_error(dataset_configuration):
         pass
 
     detect_event_kwargs = {
-        'method': microsaccades,
+        'method': pm.events.microsaccades,
         'threshold': 1,
         'eye': 'auto',
     }
@@ -683,27 +687,27 @@ def test_detect_events_attribute_error(dataset_configuration):
             id='empty_list_stays_empty_list',
         ),
         pytest.param(
-            [EventDataFrame()],
-            [EventDataFrame()],
+            [pm.events.EventDataFrame()],
+            [pm.events.EventDataFrame()],
             id='empty_df_stays_empty_df',
         ),
         pytest.param(
-            [EventDataFrame(name='event', onsets=[0], offsets=[99])],
-            [EventDataFrame()],
+            [pm.events.EventDataFrame(name='event', onsets=[0], offsets=[99])],
+            [pm.events.EventDataFrame()],
             id='single_instance_filled_df_gets_cleared_to_empty_df',
         ),
         pytest.param(
             [
-                EventDataFrame(name='event', onsets=[0], offsets=[99]),
-                EventDataFrame(name='event', onsets=[0], offsets=[99]),
+                pm.events.EventDataFrame(name='event', onsets=[0], offsets=[99]),
+                pm.events.EventDataFrame(name='event', onsets=[0], offsets=[99]),
             ],
-            [EventDataFrame(), EventDataFrame()],
+            [pm.events.EventDataFrame(), pm.events.EventDataFrame()],
             id='two_instance_filled_df_gets_cleared_to_two_empty_dfs',
         ),
     ],
 )
-def test_clear_events(events_init, events_expected):
-    dataset = Dataset('ToyDataset', root='data')
+def test_clear_events(events_init, events_expected, tmp_path):
+    dataset = pm.Dataset('ToyDataset', path=tmp_path)
     dataset.events = events_init
     dataset.clear_events()
 
@@ -719,21 +723,21 @@ def test_clear_events(events_init, events_expected):
     'detect_event_kwargs, events_dirname, expected_save_dirpath, save_kwargs',
     [
         pytest.param(
-            {'method': microsaccades, 'threshold': 1, 'eye': 'auto'},
+            {'method': pm.events.microsaccades, 'threshold': 1, 'eye': 'auto'},
             None,
             'events',
             {},
             id='none_dirname',
         ),
         pytest.param(
-            {'method': microsaccades, 'threshold': 1, 'eye': 'auto'},
+            {'method': pm.events.microsaccades, 'threshold': 1, 'eye': 'auto'},
             'events_test',
             'events_test',
             {},
             id='explicit_dirname',
         ),
         pytest.param(
-            {'method': microsaccades, 'threshold': 1, 'eye': 'auto'},
+            {'method': pm.events.microsaccades, 'threshold': 1, 'eye': 'auto'},
             None,
             'events',
             {'extension': 'csv'},
@@ -748,7 +752,7 @@ def test_save_events(
         save_kwargs,
         dataset_configuration,
 ):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
     dataset.pos2vel()
@@ -769,21 +773,21 @@ def test_save_events(
     'detect_event_kwargs, events_dirname, expected_save_dirpath, load_save_kwargs',
     [
         pytest.param(
-            {'method': microsaccades, 'threshold': 1, 'eye': 'auto'},
+            {'method': pm.events.microsaccades, 'threshold': 1, 'eye': 'auto'},
             None,
             'events',
             {},
             id='none_dirname',
         ),
         pytest.param(
-            {'method': microsaccades, 'threshold': 1, 'eye': 'auto'},
+            {'method': pm.events.microsaccades, 'threshold': 1, 'eye': 'auto'},
             'events_test',
             'events_test',
             {},
             id='explicit_dirname',
         ),
         pytest.param(
-            {'method': microsaccades, 'threshold': 1, 'eye': 'auto'},
+            {'method': pm.events.microsaccades, 'threshold': 1, 'eye': 'auto'},
             None,
             'events',
             {'extension': 'csv'},
@@ -798,7 +802,7 @@ def test_load_previously_saved_events_gaze(
         load_save_kwargs,
         dataset_configuration,
 ):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
     dataset.pos2vel()
@@ -837,7 +841,7 @@ def test_load_previously_saved_events_gaze(
     ],
 )
 def test_save_preprocessed(preprocessed_dirname, expected_save_dirpath, dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
     dataset.pos2vel()
@@ -894,12 +898,12 @@ def test_save_creates_correct_directory(
         save_kwargs,
         dataset_configuration,
 ):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
     dataset.pos2vel()
 
-    detect_events_kwargs = {'method': microsaccades, 'threshold': 1, 'eye': 'auto'}
+    detect_events_kwargs = {'method': pm.events.microsaccades, 'threshold': 1, 'eye': 'auto'}
     dataset.detect_events(**detect_events_kwargs)
 
     preprocessed_dirname = save_kwargs.get('preprocessed_dirname', 'preprocessed')
@@ -942,12 +946,12 @@ def test_save_files_have_correct_extension(
         save_kwargs,
         dataset_configuration,
 ):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
     dataset.pix2deg()
     dataset.pos2vel()
 
-    detect_events_kwargs = {'method': microsaccades, 'threshold': 1, 'eye': 'auto'}
+    detect_events_kwargs = {'method': pm.events.microsaccades, 'threshold': 1, 'eye': 'auto'}
     dataset.detect_events(**detect_events_kwargs)
 
     preprocessed_dirname = save_kwargs.get('preprocessed_dirname', 'preprocessed')
@@ -978,72 +982,67 @@ def test_save_files_have_correct_extension(
 
 
 @pytest.mark.parametrize(
-    'init_kwargs, expected_paths',
+    'init_path, expected_paths',
     [
         pytest.param(
-            {'root': '/data/set/path'},
+            '/data/set/path',
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/'),
                 'raw': Path('/data/set/path/raw'),
                 'preprocessed': Path('/data/set/path/preprocessed'),
                 'events': Path('/data/set/path/events'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': '.'},
+            pm.DatasetPaths(root='/data/set/path', dataset='.'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/'),
                 'raw': Path('/data/set/path/raw'),
                 'preprocessed': Path('/data/set/path/preprocessed'),
                 'events': Path('/data/set/path/events'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': 'dataset'},
+            pm.DatasetPaths(root='/data/set/path', dataset='dataset'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/dataset'),
+                'dataset': Path('/data/set/path/dataset'),
                 'raw': Path('/data/set/path/dataset/raw'),
                 'preprocessed': Path('/data/set/path/dataset/preprocessed'),
                 'events': Path('/data/set/path/dataset/events'),
             },
         ),
         pytest.param(
-            {
-                'root': '/data/set/path', 'dataset_dirname': 'dataset',
-                'events_dirname': 'custom_events',
-            },
+            pm.DatasetPaths(root='/data/set/path', dataset='dataset', events='custom_events'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/dataset'),
+                'dataset': Path('/data/set/path/dataset'),
                 'raw': Path('/data/set/path/dataset/raw'),
                 'preprocessed': Path('/data/set/path/dataset/preprocessed'),
                 'events': Path('/data/set/path/dataset/custom_events'),
             },
         ),
         pytest.param(
-            {
-                'root': '/data/set/path', 'dataset_dirname': 'dataset',
-                'preprocessed_dirname': 'custom_preprocessed',
-            },
+            pm.DatasetPaths(
+                root='/data/set/path',
+                dataset='dataset',
+                preprocessed='custom_preprocessed',
+            ),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/dataset'),
+                'dataset': Path('/data/set/path/dataset'),
                 'raw': Path('/data/set/path/dataset/raw'),
                 'preprocessed': Path('/data/set/path/dataset/custom_preprocessed'),
                 'events': Path('/data/set/path/dataset/events'),
             },
         ),
         pytest.param(
-            {
-                'root': '/data/set/path', 'dataset_dirname': 'dataset',
-                'raw_dirname': 'custom_raw',
-            },
+            pm.DatasetPaths(root='/data/set/path', dataset='dataset', raw='custom_raw'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/dataset'),
+                'dataset': Path('/data/set/path/dataset'),
                 'raw': Path('/data/set/path/dataset/custom_raw'),
                 'preprocessed': Path('/data/set/path/dataset/preprocessed'),
                 'events': Path('/data/set/path/dataset/events'),
@@ -1051,14 +1050,15 @@ def test_save_files_have_correct_extension(
         ),
     ],
 )
-def test_paths(init_kwargs, expected_paths):
-    dataset = Dataset('ToyDataset', **init_kwargs)
+def test_paths(init_path, expected_paths):
+    dataset = pm.Dataset('ToyDataset', path=init_path)
 
-    assert dataset.root == expected_paths['root']
-    assert dataset.path == expected_paths['path']
-    assert dataset.raw_rootpath == expected_paths['raw']
-    assert dataset.preprocessed_rootpath == expected_paths['preprocessed']
-    assert dataset.events_rootpath == expected_paths['events']
+    assert dataset.paths.root == expected_paths['root']
+    assert dataset.paths.dataset == expected_paths['dataset']
+    assert dataset.path == expected_paths['dataset']
+    assert dataset.paths.raw == expected_paths['raw']
+    assert dataset.paths.preprocessed == expected_paths['preprocessed']
+    assert dataset.paths.events == expected_paths['events']
 
 
 @pytest.mark.parametrize(
@@ -1068,8 +1068,8 @@ def test_paths(init_kwargs, expected_paths):
         pytest.param([], AttributeError),
     ],
 )
-def test_check_fileinfo(new_fileinfo, exception):
-    dataset = Dataset('ToyDataset', root='data')
+def test_check_fileinfo(new_fileinfo, exception, tmp_path):
+    dataset = pm.Dataset('ToyDataset', path=tmp_path)
 
     dataset.fileinfo = new_fileinfo
 
@@ -1084,8 +1084,8 @@ def test_check_fileinfo(new_fileinfo, exception):
         pytest.param([], AttributeError),
     ],
 )
-def test_check_gaze_dataframe(new_gaze, exception):
-    dataset = Dataset('ToyDataset', root='data')
+def test_check_gaze_dataframe(new_gaze, exception, tmp_path):
+    dataset = pm.Dataset('ToyDataset', path=tmp_path)
 
     dataset.gaze = new_gaze
 
@@ -1096,7 +1096,7 @@ def test_check_gaze_dataframe(new_gaze, exception):
 @pytest.mark.parametrize('dataset_configuration', ['ToyBino'], indirect=['dataset_configuration'])
 def test_check_experiment(dataset_configuration):
     dataset_configuration['init_kwargs']['definition'].experiment = None
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load()
 
     with pytest.raises(AttributeError):
@@ -1105,7 +1105,7 @@ def test_check_experiment(dataset_configuration):
 
 @pytest.mark.parametrize('dataset_configuration', ['ToyBino'], indirect=['dataset_configuration'])
 def test_velocity_columns(dataset_configuration):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load(preprocessed=True)
 
     expected_velocity_columns = ['x_left_vel', 'y_left_vel', 'x_right_vel', 'y_right_vel']
@@ -1120,7 +1120,7 @@ def test_velocity_columns(dataset_configuration):
     [
         pytest.param(
             {'event_properties': 'foo'},
-            exceptions.InvalidProperty,
+            pm.exceptions.InvalidProperty,
             ('foo', 'invalid', 'valid', 'peak_velocity'),
             id='invalid_property',
         ),
@@ -1129,7 +1129,7 @@ def test_velocity_columns(dataset_configuration):
 def test_event_dataframe_add_property_raises_exceptions(
         dataset_configuration, property_kwargs, exception, msg_substrings,
 ):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load(preprocessed=True, events=True)
 
     with pytest.raises(exception) as excinfo:
@@ -1147,7 +1147,7 @@ def test_event_dataframe_add_property_raises_exceptions(
     ],
 )
 def test_event_dataframe_add_property_has_expected_height(dataset_configuration, property_kwargs):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load(preprocessed=True, events=True)
 
     expected_heights = [len(event_df) for event_df in dataset.events]
@@ -1165,7 +1165,7 @@ def test_event_dataframe_add_property_has_expected_height(dataset_configuration,
             {'event_properties': 'peak_velocity'},
             {
                 'subject_id': pl.Int64,
-                **EventDataFrame._minimal_schema,
+                **pm.events.EventDataFrame._minimal_schema,
                 'duration': pl.Int64,
                 'peak_velocity': pl.Float64,
             },
@@ -1176,7 +1176,7 @@ def test_event_dataframe_add_property_has_expected_height(dataset_configuration,
 def test_event_dataframe_add_property_has_expected_schema(
         dataset_configuration, property_kwargs, expected_schema,
 ):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load(preprocessed=True, events=True)
 
     dataset.compute_event_properties(**property_kwargs)
@@ -1198,7 +1198,7 @@ def test_event_dataframe_add_property_has_expected_schema(
 def test_event_dataframe_add_property_effect_property_columns(
         dataset_configuration, property_kwargs, expected_property_columns,
 ):
-    dataset = Dataset(**dataset_configuration['init_kwargs'])
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
     dataset.load(preprocessed=True, events=True)
 
     dataset.compute_event_properties(**property_kwargs)
diff --git a/tests/dataset/public_dataset_test.py b/tests/dataset/public_dataset_test.py
index 2e7f43e..9be6445 100644
--- a/tests/dataset/public_dataset_test.py
+++ b/tests/dataset/public_dataset_test.py
@@ -26,16 +26,14 @@ from unittest import mock
 
 import pytest
 
-from pymovements.dataset import DatasetDefinition
-from pymovements.dataset import PublicDataset
-from pymovements.dataset import register_dataset
+import pymovements as pm
 
 
 @pytest.fixture(name='dataset_definition')
 def dataset_definition_fixture():
     @dataclass
-    @register_dataset
-    class CustomPublicDataset(DatasetDefinition):
+    @pm.register_dataset
+    class CustomPublicDataset(pm.DatasetDefinition):
         name: str = 'CustomPublicDataset'
 
         mirrors: tuple[str, ...] = (
@@ -55,55 +53,69 @@ def dataset_definition_fixture():
 
 
 @pytest.mark.parametrize(
-    'init_kwargs, expected_paths',
+    'init_path, expected_paths',
     [
         pytest.param(
-            {'root': '/data/set/path'},
+            '/data/set/path',
+            {
+                'root': Path('/data/set/path'),
+                'dataset': Path('/data/set/path'),
+                'downloads': Path('/data/set/path/downloads'),
+            },
+            id='no_paths',
+        ),
+        pytest.param(
+            pm.DatasetPaths(root='/data/set/path'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/CustomPublicDataset'),
-                'download': Path('/data/set/path/CustomPublicDataset/downloads'),
+                'dataset': Path('/data/set/path/CustomPublicDataset'),
+                'downloads': Path('/data/set/path/CustomPublicDataset/downloads'),
             },
+            id='no_paths',
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': '.'},
+            pm.DatasetPaths(root='/data/set/path', dataset='.'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/'),
-                'download': Path('/data/set/path/downloads'),
+                'dataset': Path('/data/set/path/'),
+                'downloads': Path('/data/set/path/downloads'),
             },
+            id='dataset_dot',
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': 'dataset'},
+            pm.DatasetPaths(root='/data/set/path', dataset='dataset'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/dataset'),
-                'download': Path('/data/set/path/dataset/downloads'),
+                'dataset': Path('/data/set/path/dataset'),
+                'downloads': Path('/data/set/path/dataset/downloads'),
             },
+            id='explicit_dataset_dirname',
         ),
         pytest.param(
-            {'root': '/data/set/path', 'downloads_dirname': 'custom_download_dirname'},
+            pm.DatasetPaths(root='/data/set/path', downloads='custom_downloads'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/CustomPublicDataset'),
-                'download': Path('/data/set/path/CustomPublicDataset/custom_download_dirname'),
+                'dataset': Path('/data/set/path/CustomPublicDataset'),
+                'downloads': Path('/data/set/path/CustomPublicDataset/custom_downloads'),
             },
+            id='explicit_download_dirname',
         ),
     ],
 )
-def test_paths(init_kwargs, expected_paths, dataset_definition):
-    dataset = PublicDataset(dataset_definition, **init_kwargs)
+def test_paths(init_path, expected_paths, dataset_definition):
+    dataset = pm.PublicDataset(dataset_definition, path=init_path)
 
-    assert dataset.root == expected_paths['root']
-    assert dataset.path == expected_paths['path']
-    assert dataset.downloads_rootpath == expected_paths['download']
+    assert dataset.paths.root == expected_paths['root']
+    assert dataset.paths.dataset == expected_paths['dataset']
+    assert dataset.paths.downloads == expected_paths['downloads']
 
 
 @mock.patch('pymovements.dataset.public_dataset.download_file')
 def test_dataset_download_both_mirrors_fail(mock_download_file, tmp_path, dataset_definition):
     mock_download_file.side_effect = OSError()
 
-    dataset = PublicDataset(dataset_definition, root=tmp_path, dataset_dirname='.')
+    paths = pm.DatasetPaths(root=tmp_path, dataset='.')
+    dataset = pm.PublicDataset(dataset_definition, path=paths)
 
     with pytest.raises(RuntimeError):
         dataset.download()
@@ -128,7 +140,8 @@ def test_dataset_download_both_mirrors_fail(mock_download_file, tmp_path, datase
 def test_dataset_download_first_mirror_fails(mock_download_file, tmp_path, dataset_definition):
     mock_download_file.side_effect = [OSError(), None]
 
-    dataset = PublicDataset(dataset_definition, root=tmp_path, dataset_dirname='.')
+    paths = pm.DatasetPaths(root=tmp_path, dataset='.')
+    dataset = pm.PublicDataset(dataset_definition, path=paths)
     dataset.download(extract=False)
 
     mock_download_file.assert_has_calls([
@@ -151,7 +164,8 @@ def test_dataset_download_first_mirror_fails(mock_download_file, tmp_path, datas
 def test_dataset_download_file_not_found(mock_download_file, tmp_path, dataset_definition):
     mock_download_file.side_effect = RuntimeError()
 
-    dataset = PublicDataset(dataset_definition, root=tmp_path, dataset_dirname='.')
+    paths = pm.DatasetPaths(root=tmp_path, dataset='.')
+    dataset = pm.PublicDataset(dataset_definition, path=paths)
 
     with pytest.raises(RuntimeError):
         dataset.download()
@@ -170,7 +184,8 @@ def test_dataset_download_file_not_found(mock_download_file, tmp_path, dataset_d
 def test_dataset_download_no_extract(mock_download_file, tmp_path, dataset_definition):
     mock_download_file.return_value = 'path'
 
-    dataset = PublicDataset(dataset_definition, root=tmp_path, dataset_dirname='.')
+    paths = pm.DatasetPaths(root=tmp_path, dataset='.')
+    dataset = pm.PublicDataset(dataset_definition, path=paths)
     dataset.download(extract=False)
 
     mock_download_file.assert_has_calls([
@@ -191,7 +206,8 @@ def test_dataset_extract_remove_finished_true(
 ):
     mock_extract_archive.return_value = 'path'
 
-    dataset = PublicDataset(dataset_definition, root=tmp_path, dataset_dirname='.')
+    paths = pm.DatasetPaths(root=tmp_path, dataset='.')
+    dataset = pm.PublicDataset(dataset_definition, path=paths)
     dataset.extract(remove_finished=True)
 
     mock_extract_archive.assert_has_calls([
@@ -212,7 +228,8 @@ def test_dataset_extract_remove_finished_false(
 ):
     mock_extract_archive.return_value = 'path'
 
-    dataset = PublicDataset(dataset_definition, root=tmp_path, dataset_dirname='.')
+    paths = pm.DatasetPaths(root=tmp_path, dataset='.')
+    dataset = pm.PublicDataset(dataset_definition, path=paths)
     dataset.extract()
 
     mock_extract_archive.assert_has_calls([
@@ -233,7 +250,7 @@ def test_dataset_download_default_extract(
     mock_extract.return_value = None
     mock_download.return_value = None
 
-    PublicDataset(dataset_definition, root=tmp_path).download()
+    pm.PublicDataset(dataset_definition, path=tmp_path).download()
 
     mock_download.assert_called_once()
     mock_extract.assert_called_once()
@@ -241,8 +258,7 @@ def test_dataset_download_default_extract(
 
 def test_dataset_download_no_mirrors_raises_exception(tmp_path):
     @dataclass
-    @register_dataset
-    class NoMirrorsDefinition(DatasetDefinition):
+    class NoMirrorsDefinition(pm.DatasetDefinition):
         name: str = 'CustomPublicDataset'
 
         mirrors: tuple[str, ...] = ()
@@ -256,7 +272,7 @@ def test_dataset_download_no_mirrors_raises_exception(tmp_path):
         )
 
     with pytest.raises(AttributeError) as excinfo:
-        PublicDataset(NoMirrorsDefinition, root=tmp_path).download()
+        pm.PublicDataset(NoMirrorsDefinition, path=tmp_path).download()
 
     msg, = excinfo.value.args
 
@@ -267,8 +283,7 @@ def test_dataset_download_no_mirrors_raises_exception(tmp_path):
 
 def test_dataset_download_no_resources_raises_exception(tmp_path):
     @dataclass
-    @register_dataset
-    class NoResourcesDefinition(DatasetDefinition):
+    class NoResourcesDefinition(pm.DatasetDefinition):
         name: str = 'CustomPublicDataset'
 
         mirrors: tuple[str, ...] = (
@@ -279,7 +294,7 @@ def test_dataset_download_no_resources_raises_exception(tmp_path):
         resources: tuple[dict[str, str], ...] = ()
 
     with pytest.raises(AttributeError) as excinfo:
-        PublicDataset(NoResourcesDefinition, root=tmp_path).download()
+        pm.PublicDataset(NoResourcesDefinition, path=tmp_path).download()
 
     msg, = excinfo.value.args
 
@@ -289,7 +304,7 @@ def test_dataset_download_no_resources_raises_exception(tmp_path):
 
 
 def test_public_dataset_registered_correct_attributes(tmp_path, dataset_definition):
-    dataset = PublicDataset('CustomPublicDataset', root=tmp_path)
+    dataset = pm.PublicDataset('CustomPublicDataset', path=tmp_path)
 
     assert dataset.definition.mirrors == dataset_definition.mirrors
     assert dataset.definition.resources == dataset_definition.resources
diff --git a/tests/datasets/gazebase_test.py b/tests/datasets/gazebase_test.py
index eed1ae7..693b574 100644
--- a/tests/datasets/gazebase_test.py
+++ b/tests/datasets/gazebase_test.py
@@ -22,50 +22,58 @@ from pathlib import Path
 
 import pytest
 
-from pymovements.dataset.public_dataset import PublicDataset
-from pymovements.datasets import GazeBase
+import pymovements as pm
 
 
 @pytest.mark.parametrize(
-    'init_kwargs, expected_paths',
+    'init_path, expected_paths',
     [
         pytest.param(
-            {'root': '/data/set/path'},
+            '/data/set/path',
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/GazeBase'),
+                'dataset': Path('/data/set/path/'),
+                'download': Path('/data/set/path/downloads'),
+            },
+        ),
+        pytest.param(
+            pm.DatasetPaths(root='/data/set/path'),
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/GazeBase'),
                 'download': Path('/data/set/path/GazeBase/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': '.'},
+            pm.DatasetPaths(root='/data/set/path', dataset='.'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/'),
                 'download': Path('/data/set/path/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': 'dataset'},
+            pm.DatasetPaths(root='/data/set/path', dataset='dataset'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/dataset'),
+                'dataset': Path('/data/set/path/dataset'),
                 'download': Path('/data/set/path/dataset/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'downloads_dirname': 'custom_download_dirname'},
+            pm.DatasetPaths(root='/data/set/path', downloads='custom_downloads'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/GazeBase'),
-                'download': Path('/data/set/path/GazeBase/custom_download_dirname'),
+                'dataset': Path('/data/set/path/GazeBase'),
+                'download': Path('/data/set/path/GazeBase/custom_downloads'),
             },
         ),
     ],
 )
-def test_paths(init_kwargs, expected_paths):
-    dataset = PublicDataset(GazeBase, **init_kwargs)
+def test_paths(init_path, expected_paths):
+    dataset = pm.PublicDataset(pm.datasets.GazeBase, path=init_path)
 
-    assert dataset.root == expected_paths['root']
-    assert dataset.path == expected_paths['path']
-    assert dataset.downloads_rootpath == expected_paths['download']
+    assert dataset.paths.root == expected_paths['root']
+    assert dataset.path == expected_paths['dataset']
+    assert dataset.paths.dataset == expected_paths['dataset']
+    assert dataset.paths.downloads == expected_paths['download']
diff --git a/tests/datasets/judo1000_test.py b/tests/datasets/judo1000_test.py
index 5677a3b..751e263 100644
--- a/tests/datasets/judo1000_test.py
+++ b/tests/datasets/judo1000_test.py
@@ -22,50 +22,58 @@ from pathlib import Path
 
 import pytest
 
-from pymovements.dataset.public_dataset import PublicDataset
-from pymovements.datasets import JuDo1000
+import pymovements as pm
 
 
 @pytest.mark.parametrize(
-    'init_kwargs, expected_paths',
+    'init_path, expected_paths',
     [
         pytest.param(
-            {'root': '/data/set/path'},
+            '/data/set/path',
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/JuDo1000'),
+                'dataset': Path('/data/set/path/'),
+                'download': Path('/data/set/path/downloads'),
+            },
+        ),
+        pytest.param(
+            pm.DatasetPaths(root='/data/set/path'),
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/JuDo1000'),
                 'download': Path('/data/set/path/JuDo1000/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': '.'},
+            pm.DatasetPaths(root='/data/set/path', dataset='.'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/'),
                 'download': Path('/data/set/path/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': 'dataset'},
+            pm.DatasetPaths(root='/data/set/path', dataset='dataset'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/dataset'),
+                'dataset': Path('/data/set/path/dataset'),
                 'download': Path('/data/set/path/dataset/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'downloads_dirname': 'custom_download_dirname'},
+            pm.DatasetPaths(root='/data/set/path', downloads='custom_downloads'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/JuDo1000'),
-                'download': Path('/data/set/path/JuDo1000/custom_download_dirname'),
+                'dataset': Path('/data/set/path/JuDo1000'),
+                'download': Path('/data/set/path/JuDo1000/custom_downloads'),
             },
         ),
     ],
 )
-def test_paths(init_kwargs, expected_paths):
-    dataset = PublicDataset(JuDo1000, **init_kwargs)
+def test_paths(init_path, expected_paths):
+    dataset = pm.PublicDataset(pm.datasets.JuDo1000, path=init_path)
 
-    assert dataset.root == expected_paths['root']
-    assert dataset.path == expected_paths['path']
-    assert dataset.downloads_rootpath == expected_paths['download']
+    assert dataset.paths.root == expected_paths['root']
+    assert dataset.path == expected_paths['dataset']
+    assert dataset.paths.dataset == expected_paths['dataset']
+    assert dataset.paths.downloads == expected_paths['download']
diff --git a/tests/datasets/toy_dataset_test.py b/tests/datasets/toy_dataset_test.py
index 9b66654..03ef4d6 100644
--- a/tests/datasets/toy_dataset_test.py
+++ b/tests/datasets/toy_dataset_test.py
@@ -22,50 +22,58 @@ from pathlib import Path
 
 import pytest
 
-from pymovements.dataset.public_dataset import PublicDataset
-from pymovements.datasets import ToyDataset
+import pymovements as pm
 
 
 @pytest.mark.parametrize(
-    'init_kwargs, expected_paths',
+    'init_path, expected_paths',
     [
         pytest.param(
-            {'root': '/data/set/path'},
+            '/data/set/path',
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/ToyDataset'),
+                'dataset': Path('/data/set/path/'),
+                'download': Path('/data/set/path//downloads'),
+            },
+        ),
+        pytest.param(
+            pm.DatasetPaths(root='/data/set/path'),
+            {
+                'root': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/ToyDataset'),
                 'download': Path('/data/set/path/ToyDataset/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': '.'},
+            pm.DatasetPaths(root='/data/set/path', dataset='.'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/'),
+                'dataset': Path('/data/set/path/'),
                 'download': Path('/data/set/path/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'dataset_dirname': 'dataset'},
+            pm.DatasetPaths(root='/data/set/path', dataset='dataset'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/dataset'),
+                'dataset': Path('/data/set/path/dataset'),
                 'download': Path('/data/set/path/dataset/downloads'),
             },
         ),
         pytest.param(
-            {'root': '/data/set/path', 'downloads_dirname': 'custom_download_dirname'},
+            pm.DatasetPaths(root='/data/set/path', downloads='custom_downloads'),
             {
                 'root': Path('/data/set/path/'),
-                'path': Path('/data/set/path/ToyDataset'),
-                'download': Path('/data/set/path/ToyDataset/custom_download_dirname'),
+                'dataset': Path('/data/set/path/ToyDataset'),
+                'download': Path('/data/set/path/ToyDataset/custom_downloads'),
             },
         ),
     ],
 )
-def test_paths(init_kwargs, expected_paths):
-    dataset = PublicDataset(ToyDataset, **init_kwargs)
+def test_paths(init_path, expected_paths):
+    dataset = pm.PublicDataset(pm.datasets.ToyDataset, path=init_path)
 
-    assert dataset.root == expected_paths['root']
-    assert dataset.path == expected_paths['path']
-    assert dataset.downloads_rootpath == expected_paths['download']
+    assert dataset.paths.root == expected_paths['root']
+    assert dataset.path == expected_paths['dataset']
+    assert dataset.paths.dataset == expected_paths['dataset']
+    assert dataset.paths.downloads == expected_paths['download']
diff --git a/tests/utils/paths_test.py b/tests/utils/paths_test.py
index e80e9f2..0443c50 100644
--- a/tests/utils/paths_test.py
+++ b/tests/utils/paths_test.py
@@ -176,6 +176,17 @@ def test_match_filepaths_not_relative(tmp_path):
     case.assertCountEqual(result_dicts, expected_dicts)
 
 
+def test_match_filepaths_not_exists_raises_value_error(tmp_path):
+    filepath = tmp_path / 'not_existing'
+
+    with pytest.raises(ValueError) as excinfo:
+        match_filepaths(path=filepath, regex=re.compile('.*'))
+    msg, = excinfo.value.args
+
+    assert 'not exist' in msg
+    assert str(filepath) in msg
+
+
 def test_match_filepaths_no_directory_raises_value_error(tmp_path):
     create_directory(tmp_path, ['tmp_dir'], ['foo.txt'])
     filepath = tmp_path / 'tmp_dir' / 'foo.txt'