causalchamber 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- causalchamber-0.0.1/LICENSE +21 -0
- causalchamber-0.0.1/PKG-INFO +18 -0
- causalchamber-0.0.1/README.md +139 -0
- causalchamber-0.0.1/pyproject.toml +27 -0
- causalchamber-0.0.1/setup.cfg +4 -0
- causalchamber-0.0.1/src/README.md +3 -0
- causalchamber-0.0.1/src/causalchamber/__init__.py +0 -0
- causalchamber-0.0.1/src/causalchamber/datasets/__init__.py +1 -0
- causalchamber-0.0.1/src/causalchamber/datasets/main.py +345 -0
- causalchamber-0.0.1/src/causalchamber/datasets/sampling.py +76 -0
- causalchamber-0.0.1/src/causalchamber/datasets/utils.py +291 -0
- causalchamber-0.0.1/src/causalchamber/ground_truth.py +445 -0
- causalchamber-0.0.1/src/causalchamber/models/__init__.py +11 -0
- causalchamber-0.0.1/src/causalchamber/models/image_capture.py +167 -0
- causalchamber-0.0.1/src/causalchamber/models/light_tunnel_models.py +36 -0
- causalchamber-0.0.1/src/causalchamber/models/wind_tunnel_models.py +166 -0
- causalchamber-0.0.1/src/causalchamber/models/wind_tunnel_simulators.py +124 -0
- causalchamber-0.0.1/src/causalchamber.egg-info/PKG-INFO +18 -0
- causalchamber-0.0.1/src/causalchamber.egg-info/SOURCES.txt +20 -0
- causalchamber-0.0.1/src/causalchamber.egg-info/dependency_links.txt +1 -0
- causalchamber-0.0.1/src/causalchamber.egg-info/requires.txt +2 -0
- causalchamber-0.0.1/src/causalchamber.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Juan L Gamella
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: causalchamber
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Access the datasets and models from the causal chambers.
|
|
5
|
+
Author-email: Juan L Gamella <juangamella@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://causalchamber.org
|
|
7
|
+
Project-URL: Repository, https://github.com/juangamella/causal-chamber
|
|
8
|
+
Project-URL: Issues, https://github.com/juangamella/causal-chamber/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
|
|
16
|
+
# `causalchamber` package
|
|
17
|
+
|
|
18
|
+
TODO.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# The Causal Chambers: Dataset Repository
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
This repository contains datasets collected from the _causal chambers_, the two devices described in the 2024 paper [*The Causal Chambers: Real Physical Systems as a Testbed for AI Methodology*](<https://placehold.co/600x400?text=Placeholder:\nArxiv link!>) by Juan L. Gamella, Jonas Peters and Peter Bühlmann. The repository is updated as we collect new datasets from the chambers.
|
|
6
|
+
|
|
7
|
+
The datasets are publicly available through a permissive [CC BY 4.0 license](https://creativecommons.org/licenses/by/4.0/). This means you are free to use, share and modify the datasets as long as you give appropriate credit and communicate changes. If you use the datasets in your scientific work, please consider citing:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
@article{gamella2024chamber,
|
|
11
|
+
title={The Causal Chambers: Real Physical Systems as a Testbed for AI Methodology},
|
|
12
|
+
author={Gamella, Juan L. and B\"uhlmann, Peter and Peters, Jonas},
|
|
13
|
+
journal={arXiv preprint arXiv:TODO},
|
|
14
|
+
year={2024}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This repository also contains the source code for the `causalchamber` [package](<https://placehold.co/600x400?text=Placeholder:\nPyPi link!>) to directly [import the datasets into your Python code](#downloading-the-datasets). The package also provides Python implementations of the [mechanistic models](#mechanistic-models) described in appendix [XX] of the original [paper](<https://placehold.co/600x400?text=Placeholder:\nArxiv link!>).
|
|
19
|
+
|
|
20
|
+
Here you can also find the resources to [build the chambers](#building-the-chambers), and the datasheets for all chamber components (see [`hardware/`](hardware/)).
|
|
21
|
+
|
|
22
|
+
The code to reproduce the case studies in the original paper can be found in the separate [paper repository](https://github.com/juangamella/causal-chamber-paper).
|
|
23
|
+
|
|
24
|
+
## Available datasets
|
|
25
|
+
|
|
26
|
+
<!--  -->
|
|
27
|
+
|
|
28
|
+
Each dataset is described in detail in its corresponding page (click the dataset name). The chamber configurations are described in Fig. 3 of the manuscript.
|
|
29
|
+
|
|
30
|
+
| Dataset name | Notes | Chamber | Config. |
|
|
31
|
+
|--------:|:--------------------------------|:--------:|:--------:|
|
|
32
|
+
| [lt_camera_walks_v1](datasets/lt_camera_walks_v1/) | Image data for the ICA case study (task d3, Fig. 6). | Light tunnel | camera |
|
|
33
|
+
| [lt_color_regression_v1](datasets/lt_color_regression_v1/) | Image data for task b2 in the OOD case study (Fig. 5) | Light tunnel | camera |
|
|
34
|
+
| [lt_interventions_standard_v1](datasets/lt_interventions_standard_v1/) | Observational and interventional data from the light tunnel, used for the causal discovery case study in Fig. 5. | Light tunnel | standard |
|
|
35
|
+
| [lt_walks_v1](datasets/lt_walks_v1/) | Random and deterministic walks of the light-tunnel actuators. Used in the ICA case study (task d1), Fig. 6. | Light tunnel | standard |
|
|
36
|
+
| [wt_walks_v1](datasets/wt_walks_v1/) | Random and deterministic walks of the wind-tunnel actuators. Used in the causal discovery (task a3) and ICA (task d2) case studies. | Wind tunnel | standard |
|
|
37
|
+
| [lt_malus_v1](datasets/lt_malus_v1/) | Measurements of light intensity displaying Malus' law, used in the symbolic regression task in Fig. XX. | Light tunnel | standard |
|
|
38
|
+
| [wt_bernoulli_v1](datasets/wt_bernoulli_v1/) | Measurements of air pressure displaying Bernoulli's principle, used in the symbolic regression task in Fig. XX. | Wind tunnel | standard |
|
|
39
|
+
| [wt_changepoints_v1](datasets/wt_changepoints_v1/) | Used for the change point detection case study in Fig. 5. | Wind tunnel | standard |
|
|
40
|
+
| [wt_intake_impulse_v1](datasets/wt_intake_impulse_v1/) | Barometric pressure curves used in task 2c, Fig. 5. | Wind tunnel | standard |
|
|
41
|
+
| [wt_pressure_control_v1](datasets/wt_pressure_control_v1/) | Data from the pressure-control configuration of the wind tunnel. | Wind tunnel | pressure-control |
|
|
42
|
+
| [lt_test_v1](datasets/lt_test_v1/) | Experiments to characterize some of the physical effects of the light tunnel. Shown in figures XX-XX of the manuscript. | Light tunnel | standard |
|
|
43
|
+
| [wt_test_v1](datasets/wt_test_v1/) | Experiments to characterize some of the physical effects of the wind tunnel. Shown in figures XX-XX of the manuscript. | Wind tunnel | standard |
|
|
44
|
+
| [lt_camera_test_v1](datasets/lt_camera_test_v1/) | Experiments to characterize some of the physical effects of the camera system in the light tunnel. | Light tunnel | camera |
|
|
45
|
+
| [wt_validate_v1](datasets/wt_validate_v1/) | Randomized control experiments to validate the causal ground-truth graph of the wind tunnel in its _standard_ configuration (appendix XX of the manuscript). | Wind tunnel | standard |
|
|
46
|
+
| [wt_pc_validate_v1](datasets/wt_validate_v1/) | Randomized control experiments to validate the causal ground-truth graph of the wind tunnel in its _pressure-control_ configuration (appendix XX of the manuscript). | Wind tunnel | pressure-control |
|
|
47
|
+
| [lt_validate_v1](datasets/lt_validate_v1/) | Randomized control experiments to validate the causal ground-truth graphs of the light tunnel in its _standard_ configuration (appendix XX of the manuscript). | Light tunnel | standard |
|
|
48
|
+
| [lt_camera_validate_v1](datasets/lt_validate_v1/) | Randomized control experiments to validate the causal ground-truth graphs of the light tunnel in its _camera_ configuration (appendix XX of the manuscript). | Light tunnel | standard |
|
|
49
|
+
|
|
50
|
+
## Downloading the datasets
|
|
51
|
+
|
|
52
|
+
For each dataset, you can simply download a `.zip` file with all the data, including the images at different resolutions. The link and checksum (to verify integrity) are available on the page of each dataset (click on the dataset name in the table above).
|
|
53
|
+
|
|
54
|
+
If you use Python, you can directly import a dataset into your code through the `causalchamber` [package](<https://placehold.co/600x400?text=Placeholder:\nPyPi link!>). You can install it using pip, e.g. by typing
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
pip install causalchamber
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
in an appropriate shell. Datasets can then be accessed directly from your Python code. For example, you can access the image data from task d3 of the ICA case study ([Fig. 6](<https://placehold.co/600x400?text=Placeholder:\nArxiv link!>)) as follows:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from causalchamber.datasets import Dataset
|
|
64
|
+
|
|
65
|
+
# Download the dataset and store in e.g. /tmp
|
|
66
|
+
dataset = Dataset(name='lt_camera_walks_v1',
|
|
67
|
+
root='/tmp')
|
|
68
|
+
|
|
69
|
+
# Load the observations and images (at 50x50 pixels)
|
|
70
|
+
experiment = dataset.get_experiment(name='actuator_mix')
|
|
71
|
+
|
|
72
|
+
observations = experiment.as_pandas_dataframe()
|
|
73
|
+
images = experiment.as_image_array(size='50')
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For the available experiment names, see the page for each dataset (click on the dataset name in the table above) or run
|
|
77
|
+
```python
|
|
78
|
+
dataset.available_experiments()
|
|
79
|
+
|
|
80
|
+
# Output:
|
|
81
|
+
# ['actuator_mix',
|
|
82
|
+
# 'color_mix']
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
To force-download a fresh copy of a dataset, can call `Dataset(...)` above with the flag `force_download=True`. Update the `causalchamber` [package](<https://placehold.co/600x400?text=Placeholder:\nPyPi link!>) for access to the latest datasets.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## Mechanistic models
|
|
89
|
+
|
|
90
|
+
The `causalchamber` [package](<https://placehold.co/600x400?text=Placeholder:\nPyPi link!>) also contains Python implementations of the mechanistic models described in appendix [XX] of the original [paper](<https://placehold.co/600x400?text=Placeholder:\nArxiv link!>). The models follow the same nomenclature as in the paper, e.g., to import and run model A1 of the steady-state fan speed:
|
|
91
|
+
```Python
|
|
92
|
+
from causalchamber.models import model_a1
|
|
93
|
+
model_a1(L=np.linspace(0,1,10), L_min=0.1, omega_max=314.15)
|
|
94
|
+
|
|
95
|
+
# Output:
|
|
96
|
+
|
|
97
|
+
# array([ 31.415 , 34.90555556, 69.81111111, 104.71666667,
|
|
98
|
+
# 139.62222222, 174.52777778, 209.43333333, 244.33888889,
|
|
99
|
+
# 279.24444444, 314.15 ])
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The implementations can be found in the [`src/causalchamber/models`](src/causalchamber/models) directory. You can find examples of using the models in the (case_studies/mechanistic_models.ipynb)[XX] notebook in the separate [paper repository](https://github.com/juangamella/causal-chamber-paper).
|
|
103
|
+
|
|
104
|
+
## Causal ground-truth graphs
|
|
105
|
+
|
|
106
|
+
The graphs for the causal ground truths given in Fig. 3 of the original [paper](<https://placehold.co/600x400?text=Placeholder:\nArxiv link!>) can be found as adjacency matrices in the `ground_truths/` directory. The adjacencies can also be loaded through the `causalchamber` [package](<https://placehold.co/600x400?text=Placeholder:\nPyPi link!>), e.g.,
|
|
107
|
+
```python
|
|
108
|
+
from causalchamber.ground_truth import graph
|
|
109
|
+
graph(chamber="lt", configuration="standard")
|
|
110
|
+
|
|
111
|
+
# Output:
|
|
112
|
+
|
|
113
|
+
# red green blue osr_c v_c current pol_1 pol_2 osr_angle_1 \
|
|
114
|
+
# red 0 0 0 0 0 1 0 0 0
|
|
115
|
+
# green 0 0 0 0 0 1 0 0 0
|
|
116
|
+
# blue 0 0 0 0 0 1 0 0 0
|
|
117
|
+
# osr_c 0 0 0 0 0 1 0 0 0
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
To make it easier to plot graphs and reference them back to the original paper, the latex representation of each variable can be obtained by calling the `latex_name` function. For example, to obtain the latex representation $\theta_1$ of the `pol_1` variable, you can run
|
|
121
|
+
```python
|
|
122
|
+
from causalchamber.ground_truth import latex_name
|
|
123
|
+
latex_name('pol_1', enclose=True)
|
|
124
|
+
|
|
125
|
+
# Output:
|
|
126
|
+
|
|
127
|
+
# '$\\theta_1$'
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Building the chambers
|
|
131
|
+
|
|
132
|
+
You can find the resources to build the chambers in [`hardware/`](hardware/), together with the datasheets for all physical components (see appendix [XX]) of the original [paper](<https://placehold.co/600x400?text=Placeholder:\nArxiv link!>).
|
|
133
|
+
|
|
134
|
+
## Licenses
|
|
135
|
+
|
|
136
|
+
All images and `.csv` files in the datasets are licensed under a [CC BY 4.0 license](https://creativecommons.org/licenses/by/4.0/). A copy of the license can be found in [LICENSE_DATASETS.txt](LICENSE_DATASETS.txt).
|
|
137
|
+
|
|
138
|
+
The code, e.g., for the `causalchamber` package and mechanistic models, is shared under the [MIT license](https://opensource.org/license/mit/). A copy of the license can also be found in [LICENSE_SOFTWARE.txt](LICENSE_SOFTWARE.txt).
|
|
139
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "causalchamber"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Juan L Gamella", email="juangamella@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Access the datasets and models from the causal chambers."
|
|
12
|
+
readme = "src/README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"numpy>=1.17.0",
|
|
21
|
+
"pandas>=1.2.1"
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://causalchamber.org"
|
|
26
|
+
Repository = "https://github.com/juangamella/causal-chamber"
|
|
27
|
+
Issues = "https://github.com/juangamella/causal-chamber/issues"
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .main import *
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2023 Juan L. Gamella
|
|
4
|
+
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
import causalchamber.datasets.utils as utils
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
import pandas as pd
|
|
26
|
+
import yaml
|
|
27
|
+
from PIL import Image
|
|
28
|
+
import numpy as np
|
|
29
|
+
import os
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Maybe have this as a downloadable YAML file
|
|
33
|
+
directory_path = Path(Path(__file__).parents[0], "directory.yaml")
|
|
34
|
+
with open(directory_path, "r") as f:
|
|
35
|
+
directory = yaml.load(f, Loader=yaml.Loader)
|
|
36
|
+
f.close()
|
|
37
|
+
|
|
38
|
+
# --------------------------------------------------------------------
|
|
39
|
+
# Base Dataset and Experiment classes
|
|
40
|
+
|
|
41
|
+
citation = """
|
|
42
|
+
@article{gamella2024chamber,
|
|
43
|
+
title={The Causal Chambers: Real Physical Systems as a Testbed for AI Methodology},
|
|
44
|
+
author={Gamella, Juan L. and B\"uhlmann, Peter and Peters, Jonas},
|
|
45
|
+
journal={(work in progress)},
|
|
46
|
+
year={2023}
|
|
47
|
+
}
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
print(f"If you use our datasets for your work please consider citing:\n{citation}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Dataset:
|
|
54
|
+
def __init__(self, name, root, download=True):
|
|
55
|
+
available_datasets = directory["datasets"].keys()
|
|
56
|
+
if name not in available_datasets:
|
|
57
|
+
string = ""
|
|
58
|
+
for d in available_datasets:
|
|
59
|
+
string += ' "' + d + '"\n'
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f'Dataset "{name}" is not available. Available datasets:\n{string}'
|
|
62
|
+
)
|
|
63
|
+
# Store attributes
|
|
64
|
+
self.name = name
|
|
65
|
+
self.root = root
|
|
66
|
+
self.image = directory["datasets"][name]["image"]
|
|
67
|
+
# Check if dataset has already been downloaded to root
|
|
68
|
+
dataset_dir = Path(self.root, self.name)
|
|
69
|
+
if os.path.isdir(dataset_dir):
|
|
70
|
+
print(f'Dataset {self.name} found in "{dataset_dir}".')
|
|
71
|
+
else:
|
|
72
|
+
if download:
|
|
73
|
+
# Download, verify and extract
|
|
74
|
+
self.url = directory["datasets"][name]["url"]
|
|
75
|
+
self.checksum = directory["datasets"][name]["md5"]
|
|
76
|
+
utils.download_and_extract(self.url, self.root, self.checksum)
|
|
77
|
+
else:
|
|
78
|
+
raise FileNotFoundError(
|
|
79
|
+
f'Could not find dataset directory "{dataset_dir}". Set download=True or choose another root directory (root).'
|
|
80
|
+
)
|
|
81
|
+
# Load available experiments
|
|
82
|
+
# If not an image dataset, experiments are just .csv files in the dataset directory
|
|
83
|
+
# If an image dataset, each experiment is a folder
|
|
84
|
+
# containing the .csv file with measurements and a subfolder
|
|
85
|
+
# with the images
|
|
86
|
+
|
|
87
|
+
if self.image:
|
|
88
|
+
experiment_names = [p.name for p in Path(self.root).glob(f"{self.name}/*")]
|
|
89
|
+
csv_paths = [
|
|
90
|
+
Path(self.root, self.name, e, f"{e}.csv") for e in experiment_names
|
|
91
|
+
]
|
|
92
|
+
experiments = [ImageExperiment(self.name, path) for path in csv_paths]
|
|
93
|
+
else:
|
|
94
|
+
csv_paths = [p for p in Path(self.root).glob(f"{self.name}/*.csv")]
|
|
95
|
+
experiments = [Experiment(self.name, path) for path in csv_paths]
|
|
96
|
+
# Store experiment dictionary
|
|
97
|
+
assert len(experiments) > 0
|
|
98
|
+
self.__experiments = dict((e.name, e) for e in experiments)
|
|
99
|
+
|
|
100
|
+
def available_experiments(self):
|
|
101
|
+
return list(self.__experiments.keys())
|
|
102
|
+
|
|
103
|
+
def get_experiment(self, name):
|
|
104
|
+
return self.__experiments[name]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class Experiment:
|
|
108
|
+
def __init__(self, dataset_name, csv_path):
|
|
109
|
+
self.dataset = dataset_name
|
|
110
|
+
self.csv_path = csv_path
|
|
111
|
+
self.name = csv_path.stem
|
|
112
|
+
self.columns = pd.read_csv(self.csv_path, nrows=0).columns.tolist()
|
|
113
|
+
|
|
114
|
+
def as_pandas_dataframe(self):
|
|
115
|
+
"""Returns a pandas dataframe with the experiment data (excl. images)"""
|
|
116
|
+
return pd.read_csv(self.csv_path)
|
|
117
|
+
|
|
118
|
+
def as_image_array(self):
|
|
119
|
+
raise NotImplementedError("This is not an image dataset!")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ImageExperiment(Experiment):
|
|
123
|
+
def __init__(self, dataset_name, csv_path):
|
|
124
|
+
super().__init__(dataset_name, csv_path)
|
|
125
|
+
self.image_folders = {}
|
|
126
|
+
for path in [p for p in Path(csv_path.parents[0]).glob("images_*")]:
|
|
127
|
+
size = path.stem.split("_")[1]
|
|
128
|
+
self.image_folders[size] = path
|
|
129
|
+
|
|
130
|
+
def available_sizes(self):
|
|
131
|
+
return list(self.image_folders.keys())
|
|
132
|
+
|
|
133
|
+
def as_image_array(self, size):
|
|
134
|
+
"""Returns a numpy array with all the images along the first dimension (axis-0)"""
|
|
135
|
+
if size not in self.image_folders.keys():
|
|
136
|
+
raise ValueError(
|
|
137
|
+
f" Size {size} not available; available image sizes: {list(self.image_folders.keys())}."
|
|
138
|
+
)
|
|
139
|
+
image_filenames = pd.read_csv(self.csv_path).image_file
|
|
140
|
+
image_folder = self.image_folders[size]
|
|
141
|
+
image_paths = [Path(image_folder, f) for f in image_filenames]
|
|
142
|
+
return np.array([np.array(Image.open(f)) for f in image_paths])
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
latex_names = {
|
|
146
|
+
# Light tunnel variables
|
|
147
|
+
"red": r"R",
|
|
148
|
+
"green": r"G",
|
|
149
|
+
"blue": r"B",
|
|
150
|
+
"osr_c": r"O_C",
|
|
151
|
+
"v_c": r"R_C",
|
|
152
|
+
"current": r"\tilde{C}",
|
|
153
|
+
"pol_1": r"\theta_1",
|
|
154
|
+
"pol_2": r"\theta_2",
|
|
155
|
+
"osr_angle_1": r"O_1",
|
|
156
|
+
"osr_angle_2": r"O_2",
|
|
157
|
+
"v_angle_1": r"R_1",
|
|
158
|
+
"v_angle_2": r"R_2",
|
|
159
|
+
"angle_1": r"\tilde{\theta}_1",
|
|
160
|
+
"angle_2": r"\tilde{\theta}_2",
|
|
161
|
+
"ir_1": r"\tilde{I}_1",
|
|
162
|
+
"vis_1": r"\tilde{V}_1",
|
|
163
|
+
"ir_2": r"\tilde{I}_2",
|
|
164
|
+
"vis_2": r"\tilde{V}_2",
|
|
165
|
+
"ir_3": r"\tilde{I}_3",
|
|
166
|
+
"vis_3": r"\tilde{V}_3",
|
|
167
|
+
"l_11": r"L_{11}",
|
|
168
|
+
"l_12": r"L_{12}",
|
|
169
|
+
"l_21": r"L_{21}",
|
|
170
|
+
"l_22": r"L_{22}",
|
|
171
|
+
"l_31": r"L_{31}",
|
|
172
|
+
"l_32": r"L_{32}",
|
|
173
|
+
"diode_ir_1": r"D^I_1",
|
|
174
|
+
"diode_vis_1": r"D^V_1",
|
|
175
|
+
"diode_ir_2": r"D^I_2",
|
|
176
|
+
"diode_vis_2": r"D^V_2",
|
|
177
|
+
"diode_ir_3": r"D^I_3",
|
|
178
|
+
"diode_vis_3": r"D^V_3",
|
|
179
|
+
"t_ir_1": r"T^I_1",
|
|
180
|
+
"t_vis_1": r"T^V_1",
|
|
181
|
+
"t_ir_2": r"T^I_2",
|
|
182
|
+
"t_vis_2": r"T^V_2",
|
|
183
|
+
"t_ir_3": r"T^I_3",
|
|
184
|
+
"t_vis_3": r"T^V_3",
|
|
185
|
+
"im": r"\tilde{\text{I}}\text{m}",
|
|
186
|
+
"shutter_speed": r"T_\text{Im}",
|
|
187
|
+
"aperture": r"\text{Ap}",
|
|
188
|
+
"iso": r"\text{ISO}",
|
|
189
|
+
# Wind tunnel variables
|
|
190
|
+
"hatch": r"H",
|
|
191
|
+
"pot_1": r"A_1",
|
|
192
|
+
"pot_2": r"A_2",
|
|
193
|
+
"osr_1": r"O_1",
|
|
194
|
+
"osr_2": r"O_2",
|
|
195
|
+
"osr_mic": r"O_M",
|
|
196
|
+
"osr_in": r"O_\text{in}",
|
|
197
|
+
"osr_out": r"O_\text{out}",
|
|
198
|
+
"osr_upwind": r"O_\text{up}",
|
|
199
|
+
"osr_downwind": r"O_\text{dw}",
|
|
200
|
+
"osr_ambient": r"O_\text{amb}",
|
|
201
|
+
"osr_intake": r"O_\text{int}",
|
|
202
|
+
"v_1": r"R_1",
|
|
203
|
+
"v_2": r"R_2",
|
|
204
|
+
"v_mic": r"R_M",
|
|
205
|
+
"v_in": r"R_\text{in}",
|
|
206
|
+
"v_out": r"R_\text{out}",
|
|
207
|
+
"load_in": r"L_\text{in}",
|
|
208
|
+
"load_out": r"L_\text{out}",
|
|
209
|
+
"current_in": r"\tilde{C}_\text{in}",
|
|
210
|
+
"current_out": r"\tilde{C}_\text{out}",
|
|
211
|
+
"res_in": r"T_\text{in}",
|
|
212
|
+
"res_out": r"T_\text{out}",
|
|
213
|
+
"rpm_in": r"\tilde{\omega}_\text{in}",
|
|
214
|
+
"rpm_out": r"\tilde{\omega}_\text{out}",
|
|
215
|
+
"pressure_upwind": r"\tilde{P}_\text{up}",
|
|
216
|
+
"pressure_downwind": r"\tilde{P}_\text{dw}",
|
|
217
|
+
"pressure_ambient": r"\tilde{P}_\text{amb}",
|
|
218
|
+
"pressure_intake": r"\tilde{P}_\text{int}",
|
|
219
|
+
"mic": r"\tilde{M}",
|
|
220
|
+
"signal_1": r"\tilde{S}_1",
|
|
221
|
+
"signal_2": r"\tilde{S}_2",
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def latex_name(var, enclose=True):
|
|
226
|
+
"""Translate from machine variable name to latex name."""
|
|
227
|
+
if var not in latex_names:
|
|
228
|
+
return var
|
|
229
|
+
else:
|
|
230
|
+
name = latex_names[var]
|
|
231
|
+
return "$" + name + "$" if enclose else name
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
lt_standard_edges = [
|
|
235
|
+
("red", "ir_1"),
|
|
236
|
+
("green", "ir_1"),
|
|
237
|
+
("blue", "ir_1"),
|
|
238
|
+
("red", "ir_2"),
|
|
239
|
+
("green", "ir_2"),
|
|
240
|
+
("blue", "ir_2"),
|
|
241
|
+
("red", "ir_3"),
|
|
242
|
+
("green", "ir_3"),
|
|
243
|
+
("blue", "ir_3"),
|
|
244
|
+
("red", "vis_1"),
|
|
245
|
+
("green", "vis_1"),
|
|
246
|
+
("blue", "vis_1"),
|
|
247
|
+
("red", "vis_2"),
|
|
248
|
+
("green", "vis_2"),
|
|
249
|
+
("blue", "vis_2"),
|
|
250
|
+
("red", "vis_3"),
|
|
251
|
+
("green", "vis_3"),
|
|
252
|
+
("blue", "vis_3"),
|
|
253
|
+
("red", "current"),
|
|
254
|
+
("green", "current"),
|
|
255
|
+
("blue", "current"),
|
|
256
|
+
("pol_1", "ir_3"),
|
|
257
|
+
("pol_2", "ir_3"),
|
|
258
|
+
("pol_1", "vis_3"),
|
|
259
|
+
("pol_2", "vis_3"),
|
|
260
|
+
("pol_1", "angle_1"),
|
|
261
|
+
("pol_2", "angle_2"),
|
|
262
|
+
("v_angle_1", "angle_1"),
|
|
263
|
+
("osr_angle_1", "angle_1"),
|
|
264
|
+
("v_angle_2", "angle_2"),
|
|
265
|
+
("osr_angle_2", "angle_2"),
|
|
266
|
+
("v_c", "current"),
|
|
267
|
+
("osr_c", "current"),
|
|
268
|
+
("l_11", "ir_1"),
|
|
269
|
+
("l_12", "ir_1"),
|
|
270
|
+
("l_11", "vis_1"),
|
|
271
|
+
("l_12", "vis_1"),
|
|
272
|
+
("t_ir_1", "ir_1"),
|
|
273
|
+
("diode_ir_1", "ir_1"),
|
|
274
|
+
("t_vis_1", "vis_1"),
|
|
275
|
+
("diode_vis_1", "vis_1"),
|
|
276
|
+
("l_21", "ir_2"),
|
|
277
|
+
("l_22", "ir_2"),
|
|
278
|
+
("l_21", "vis_2"),
|
|
279
|
+
("l_22", "vis_2"),
|
|
280
|
+
("t_ir_2", "ir_2"),
|
|
281
|
+
("diode_ir_2", "ir_2"),
|
|
282
|
+
("t_vis_2", "vis_2"),
|
|
283
|
+
("diode_vis_2", "vis_2"),
|
|
284
|
+
("l_31", "ir_3"),
|
|
285
|
+
("l_32", "ir_3"),
|
|
286
|
+
("l_31", "vis_3"),
|
|
287
|
+
("l_32", "vis_3"),
|
|
288
|
+
("t_ir_3", "ir_3"),
|
|
289
|
+
("diode_ir_3", "ir_3"),
|
|
290
|
+
("t_vis_3", "vis_3"),
|
|
291
|
+
("diode_vis_3", "vis_3"),
|
|
292
|
+
("pol_1", "im"),
|
|
293
|
+
("pol_2", "im"),
|
|
294
|
+
("red", "im"),
|
|
295
|
+
("green", "im"),
|
|
296
|
+
("blue", "im"),
|
|
297
|
+
("shutter_speed", "im"),
|
|
298
|
+
("aperture", "im"),
|
|
299
|
+
("iso", "im"),
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
wt_standard_edges = [
|
|
303
|
+
("load_in", "rpm_in"),
|
|
304
|
+
("res_in", "rpm_in"),
|
|
305
|
+
("load_in", "rpm_out"),
|
|
306
|
+
("load_in", "current_in"),
|
|
307
|
+
("load_in", "current_out"),
|
|
308
|
+
("load_out", "rpm_in"),
|
|
309
|
+
("load_out", "rpm_out"),
|
|
310
|
+
("res_out", "rpm_out"),
|
|
311
|
+
("load_out", "current_out"),
|
|
312
|
+
("load_out", "current_in"),
|
|
313
|
+
("hatch", "rpm_in"),
|
|
314
|
+
("hatch", "rpm_out"),
|
|
315
|
+
("load_in", "pressure_intake"),
|
|
316
|
+
("hatch", "pressure_intake"),
|
|
317
|
+
("load_out", "pressure_intake"),
|
|
318
|
+
("osr_intake", "pressure_intake"),
|
|
319
|
+
("load_in", "pressure_upwind"),
|
|
320
|
+
("hatch", "pressure_upwind"),
|
|
321
|
+
("load_out", "pressure_upwind"),
|
|
322
|
+
("osr_up", "pressure_upwind"),
|
|
323
|
+
("load_in", "pressure_downwind"),
|
|
324
|
+
("hatch", "pressure_downwind"),
|
|
325
|
+
("load_out", "pressure_downwind"),
|
|
326
|
+
("osr_downwind", "pressure_downwind"),
|
|
327
|
+
("osr_ambient", "pressure_ambient"),
|
|
328
|
+
("osr_in", "current_in"),
|
|
329
|
+
("v_in", "current_in"),
|
|
330
|
+
("osr_out", "current_out"),
|
|
331
|
+
("v_out", "current_out"),
|
|
332
|
+
("pot_1", "signal_1"),
|
|
333
|
+
("osr_1", "signal_1"),
|
|
334
|
+
("v_1", "signal_1"),
|
|
335
|
+
("pot_1", "signal_2"),
|
|
336
|
+
("pot_2", "signal_2"),
|
|
337
|
+
("osr_2", "signal_2"),
|
|
338
|
+
("v_2", "signal_2"),
|
|
339
|
+
("pot_1", "mic"),
|
|
340
|
+
("load_in", "mic"),
|
|
341
|
+
("load_out", "mic"),
|
|
342
|
+
("hatch", "mic"),
|
|
343
|
+
("osr_mic", "mic"),
|
|
344
|
+
("v_mic", "mic"),
|
|
345
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2023 Juan L. Gamella
|
|
4
|
+
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
Functions for general sampling.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def random_walk(n, step_size=1, step_resolution=1, random_state=None):
|
|
31
|
+
rng = np.random.default_rng(random_state)
|
|
32
|
+
possible_transitions = np.arange(
|
|
33
|
+
-step_size, step_size + step_resolution, step_resolution
|
|
34
|
+
)
|
|
35
|
+
steps = rng.choice(possible_transitions, size=n, replace=True)
|
|
36
|
+
return np.cumsum(steps)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def random_walk_mirrored(
|
|
40
|
+
n, minn, maxx, step_size=1, step_resolution=1, random_state=None
|
|
41
|
+
):
|
|
42
|
+
rng = np.random.default_rng(random_state - 1)
|
|
43
|
+
init = rng.choice(np.arange(minn, maxx + step_resolution, step_resolution))
|
|
44
|
+
walk = init + random_walk(
|
|
45
|
+
n,
|
|
46
|
+
step_size=step_size,
|
|
47
|
+
step_resolution=step_resolution,
|
|
48
|
+
random_state=random_state,
|
|
49
|
+
)
|
|
50
|
+
# As long as walk is out of bounds
|
|
51
|
+
while (
|
|
52
|
+
maxx is not None
|
|
53
|
+
and (walk > maxx).any()
|
|
54
|
+
or minn is not None
|
|
55
|
+
and (walk < minn).any()
|
|
56
|
+
):
|
|
57
|
+
walk = np.minimum(walk, maxx) - np.maximum(walk, maxx) + maxx
|
|
58
|
+
walk = np.maximum(walk, minn) - np.minimum(walk, minn) + minn
|
|
59
|
+
return walk
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Deprecatedx
|
|
63
|
+
def discrete_rw(n, minn, maxx, step_sizes=[-1, 0, 1], p=None, seed=42):
|
|
64
|
+
rng = np.random.default_rng(seed)
|
|
65
|
+
walk = np.zeros(n)
|
|
66
|
+
walk[0] = rng.integers(minn, maxx + 1)
|
|
67
|
+
steps = rng.choice(step_sizes, p=p, size=n - 1, replace=True)
|
|
68
|
+
for i, s in enumerate(steps):
|
|
69
|
+
walk[i + 1] = min(max(walk[i] + s, minn), maxx)
|
|
70
|
+
return walk
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def smooth_discrete_rw(n, minn, maxx, step_sizes=[-1, 0, 1], p=None, lags=0, seed=42):
|
|
74
|
+
walk = discrete_rw(n + lags, minn, maxx, step_sizes, p, seed)
|
|
75
|
+
walk = pd.DataFrame(walk).rolling(lags).mean()[lags:]
|
|
76
|
+
return walk.values
|