bw-processing 1.2__tar.gz → 1.3__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.
- {bw_processing-1.2/src/bw_processing.egg-info → bw_processing-1.3}/PKG-INFO +45 -2
- {bw_processing-1.2 → bw_processing-1.3}/README.md +44 -1
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/__init__.py +1 -1
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/constants.py +7 -4
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/datapackage.py +161 -5
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/io_helpers.py +35 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/proxies.py +8 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/unique_fields.py +17 -0
- {bw_processing-1.2 → bw_processing-1.3/src/bw_processing.egg-info}/PKG-INFO +45 -2
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_datapackage.py +213 -1
- {bw_processing-1.2 → bw_processing-1.3}/LICENSE +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/MANIFEST.in +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/pyproject.toml +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/setup.cfg +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/array_creation.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/errors.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/__init__.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_1/another name.indices.parquet +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.data.parquet +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.distributions.parquet +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.flip.parquet +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.indices.parquet +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_1/some name.data.parquet +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_1/some name.flip.parquet +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_1/some name.indices.parquet +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/datapackage_2.zip +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/interfaces.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/parquet_files.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/examples/simple.zip +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/filesystem.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/indexing.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/io_parquet_helpers.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/io_pyarrow_helpers.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/matrix_entry.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/merging.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing/utils.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing.egg-info/SOURCES.txt +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing.egg-info/dependency_links.txt +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing.egg-info/requires.txt +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/src/bw_processing.egg-info/top_level.txt +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_array_creation.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_filesystem.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_filtered_datapackage.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_indexing.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_integration.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_interfaces.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_io_parquet_helpers.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_io_pyarrow_helpers.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_loading.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_matrix_entry.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_merging.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_proxies.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_unique_fields.py +0 -0
- {bw_processing-1.2 → bw_processing-1.3}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bw_processing
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3
|
|
4
4
|
Summary: Foo
|
|
5
5
|
Author-email: Chris Mutel <cmutel@gmail.com>
|
|
6
6
|
Maintainer-email: Chris Mutel <cmutel@gmail.com>
|
|
@@ -84,6 +84,7 @@ The [Brightway LCA framework](https://brightway.dev/) has stored data used in co
|
|
|
84
84
|
* **Dynamic data sources**. Instead of requiring that data for matrix construction be present and savedd on disk, it can now be generated dynamically, either through code running locally or on another computer system. This is a big step towards embeddding life cycle assessment in a web of environmental models.
|
|
85
85
|
* **Use [fsspec](https://filesystem-spec.readthedocs.io/en/latest/) for file IO**. The use of this library allows for data packages to be stored on your local computer, or on [many logical or virtual file systems](https://docs.pyfilesystem.org/en/latest/guide.html).
|
|
86
86
|
* **Simpler handling of numeric values whose sign should be flipped**. Sometimes it is more convenient to specify positive numbers in dataset definitions, even though such numbers should be negative when inserted into the resulting matrices. For example, in the technosphere matrix in life cycle assessment, products produced are positive and products consumed are negative, though both values are given as positive in datasets. Brightway used to use a type mapping dictionary to indicate which values in a matrix should have their sign flipped after insertion. Such mapping dictionaries are brittle and inelegant. `bw_processing` uses an optional boolean vector, called `flip`, to indicate if any values should be flipped.
|
|
87
|
+
* **Per-exchange multiplicative scaling**. An optional float vector, called `scale`, can be attached to any resource group. Each element is a multiplicative factor applied to the corresponding data value — whether static or sampled stochastically — before it is inserted into the matrix. Typical uses are allocation factors and unit conversions. A value of `1.0` leaves the data unchanged.
|
|
87
88
|
* **Separation of uncertainty distribution parameters from other data**. Fitting data to a [probability density function](https://en.wikipedia.org/wiki/Probability_density_function) (PDF), or an estimate of such a PDF, is only one approach to quantitative uncertainty analysis. We would like to support other approaches, including [direct sampling from real data](https://github.com/PascalLesage/presamples/). Therefore, uncertainty distribution parameters are stored separately, only loaded if needed, and are only one way to express quantitative uncertainty.
|
|
88
89
|
|
|
89
90
|
## Concepts
|
|
@@ -96,6 +97,18 @@ Data objects can be vectors or arrays. Vectors will always produce the same matr
|
|
|
96
97
|
|
|
97
98
|
### Vectors versus arrays
|
|
98
99
|
|
|
100
|
+
Vectors and arrays differ in how many possible values they provide per matrix cell.
|
|
101
|
+
|
|
102
|
+
A **vector** provides one value per cell. Every time a vector resource is used, it produces the same result. This is the standard case for deterministic LCA calculations.
|
|
103
|
+
|
|
104
|
+
An **array** provides multiple possible values per cell, stored as columns of a 2-D numpy array. Each time the data package is iterated, a different column is selected and inserted into the matrix. This is used for:
|
|
105
|
+
|
|
106
|
+
* **Monte Carlo analysis** — columns hold independently sampled values drawn from the uncertainty distributions.
|
|
107
|
+
* **Scenario analysis** — each column represents a predefined scenario, such as a different technology mix or policy assumption.
|
|
108
|
+
* **Presamples** — a generalization of the [presamples library](https://github.com/PascalLesage/presamples/), where pre-drawn samples are stored for reproducibility.
|
|
109
|
+
|
|
110
|
+
Which column is selected on each iteration is controlled by the `sequential` and `combinatorial` policies; see [Policies](#policies).
|
|
111
|
+
|
|
99
112
|
### Persistent versus dynamic
|
|
100
113
|
|
|
101
114
|
Persistent data is fixed, and can be completely loaded into memory and used directly or written to disk. Dynamic data is only resolved as the data is used, during matrix construction and iteration. Dynamic data is provided by *interfaces* - Python code that either generates the data, or wraps data coming from other software. There are many possible use cases for data interfaces, including:
|
|
@@ -187,6 +200,36 @@ print(data_obj.url)
|
|
|
187
200
|
>>> "example.com"
|
|
188
201
|
```
|
|
189
202
|
|
|
203
|
+
### Scale arrays
|
|
204
|
+
|
|
205
|
+
Any resource group (persistent or dynamic, vector or array) can carry an optional `scale_array`: a one-dimensional float array of the same length as `indices_array`. Each element is a multiplicative factor applied to the corresponding data value before it is inserted into the matrix. The factor is applied to both static and stochastically-sampled values. A value of `1.0` leaves the data unchanged.
|
|
206
|
+
|
|
207
|
+
Typical use cases:
|
|
208
|
+
|
|
209
|
+
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `scale_array` keeps them alongside the data they modify without requiring a separate processing step.
|
|
210
|
+
* **Unit conversions** — when source data is expressed in a unit that differs from the matrix convention, a constant conversion factor can be stored as a `scale_array` rather than baked into every data value.
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import numpy as np
|
|
214
|
+
from bw_processing import create_datapackage
|
|
215
|
+
from bw_processing.constants import INDICES_DTYPE
|
|
216
|
+
|
|
217
|
+
dp = create_datapackage()
|
|
218
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
219
|
+
data_array = np.array([100.0, 200.0, 300.0])
|
|
220
|
+
scale_array = np.array([0.6, 1.0, 0.4]) # e.g. allocation factors
|
|
221
|
+
|
|
222
|
+
dp.add_persistent_vector(
|
|
223
|
+
matrix="technosphere",
|
|
224
|
+
name="my-process",
|
|
225
|
+
indices_array=indices_array,
|
|
226
|
+
data_array=data_array,
|
|
227
|
+
scale_array=scale_array,
|
|
228
|
+
)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The stored resource has `kind="scale"` and can be retrieved via `dp.get_resource("my-process.scale")`. The `scale_array` must be a float dtype (`float32` or `float64`); passing an integer array raises `WrongDatatype`.
|
|
232
|
+
|
|
190
233
|
### Policies
|
|
191
234
|
|
|
192
235
|
Data package policies define how the data should be used. Policies apply to the entire data package; you may wish to adjust what is stored in which data packages to get the effect you desire.
|
|
@@ -219,7 +262,7 @@ Please make sure you understand how `combinatorial` and `sequential` interact! T
|
|
|
219
262
|
|
|
220
263
|
## Install
|
|
221
264
|
|
|
222
|
-
Install using pip or conda (channel `
|
|
265
|
+
Install using pip or conda (channel `conda-forge`). Depends on `numpy` and `pandas` (for reading and writing CSVs).
|
|
223
266
|
|
|
224
267
|
Has no explicit or implicit dependence on any other part of Brightway.
|
|
225
268
|
|
|
@@ -42,6 +42,7 @@ The [Brightway LCA framework](https://brightway.dev/) has stored data used in co
|
|
|
42
42
|
* **Dynamic data sources**. Instead of requiring that data for matrix construction be present and savedd on disk, it can now be generated dynamically, either through code running locally or on another computer system. This is a big step towards embeddding life cycle assessment in a web of environmental models.
|
|
43
43
|
* **Use [fsspec](https://filesystem-spec.readthedocs.io/en/latest/) for file IO**. The use of this library allows for data packages to be stored on your local computer, or on [many logical or virtual file systems](https://docs.pyfilesystem.org/en/latest/guide.html).
|
|
44
44
|
* **Simpler handling of numeric values whose sign should be flipped**. Sometimes it is more convenient to specify positive numbers in dataset definitions, even though such numbers should be negative when inserted into the resulting matrices. For example, in the technosphere matrix in life cycle assessment, products produced are positive and products consumed are negative, though both values are given as positive in datasets. Brightway used to use a type mapping dictionary to indicate which values in a matrix should have their sign flipped after insertion. Such mapping dictionaries are brittle and inelegant. `bw_processing` uses an optional boolean vector, called `flip`, to indicate if any values should be flipped.
|
|
45
|
+
* **Per-exchange multiplicative scaling**. An optional float vector, called `scale`, can be attached to any resource group. Each element is a multiplicative factor applied to the corresponding data value — whether static or sampled stochastically — before it is inserted into the matrix. Typical uses are allocation factors and unit conversions. A value of `1.0` leaves the data unchanged.
|
|
45
46
|
* **Separation of uncertainty distribution parameters from other data**. Fitting data to a [probability density function](https://en.wikipedia.org/wiki/Probability_density_function) (PDF), or an estimate of such a PDF, is only one approach to quantitative uncertainty analysis. We would like to support other approaches, including [direct sampling from real data](https://github.com/PascalLesage/presamples/). Therefore, uncertainty distribution parameters are stored separately, only loaded if needed, and are only one way to express quantitative uncertainty.
|
|
46
47
|
|
|
47
48
|
## Concepts
|
|
@@ -54,6 +55,18 @@ Data objects can be vectors or arrays. Vectors will always produce the same matr
|
|
|
54
55
|
|
|
55
56
|
### Vectors versus arrays
|
|
56
57
|
|
|
58
|
+
Vectors and arrays differ in how many possible values they provide per matrix cell.
|
|
59
|
+
|
|
60
|
+
A **vector** provides one value per cell. Every time a vector resource is used, it produces the same result. This is the standard case for deterministic LCA calculations.
|
|
61
|
+
|
|
62
|
+
An **array** provides multiple possible values per cell, stored as columns of a 2-D numpy array. Each time the data package is iterated, a different column is selected and inserted into the matrix. This is used for:
|
|
63
|
+
|
|
64
|
+
* **Monte Carlo analysis** — columns hold independently sampled values drawn from the uncertainty distributions.
|
|
65
|
+
* **Scenario analysis** — each column represents a predefined scenario, such as a different technology mix or policy assumption.
|
|
66
|
+
* **Presamples** — a generalization of the [presamples library](https://github.com/PascalLesage/presamples/), where pre-drawn samples are stored for reproducibility.
|
|
67
|
+
|
|
68
|
+
Which column is selected on each iteration is controlled by the `sequential` and `combinatorial` policies; see [Policies](#policies).
|
|
69
|
+
|
|
57
70
|
### Persistent versus dynamic
|
|
58
71
|
|
|
59
72
|
Persistent data is fixed, and can be completely loaded into memory and used directly or written to disk. Dynamic data is only resolved as the data is used, during matrix construction and iteration. Dynamic data is provided by *interfaces* - Python code that either generates the data, or wraps data coming from other software. There are many possible use cases for data interfaces, including:
|
|
@@ -145,6 +158,36 @@ print(data_obj.url)
|
|
|
145
158
|
>>> "example.com"
|
|
146
159
|
```
|
|
147
160
|
|
|
161
|
+
### Scale arrays
|
|
162
|
+
|
|
163
|
+
Any resource group (persistent or dynamic, vector or array) can carry an optional `scale_array`: a one-dimensional float array of the same length as `indices_array`. Each element is a multiplicative factor applied to the corresponding data value before it is inserted into the matrix. The factor is applied to both static and stochastically-sampled values. A value of `1.0` leaves the data unchanged.
|
|
164
|
+
|
|
165
|
+
Typical use cases:
|
|
166
|
+
|
|
167
|
+
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `scale_array` keeps them alongside the data they modify without requiring a separate processing step.
|
|
168
|
+
* **Unit conversions** — when source data is expressed in a unit that differs from the matrix convention, a constant conversion factor can be stored as a `scale_array` rather than baked into every data value.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import numpy as np
|
|
172
|
+
from bw_processing import create_datapackage
|
|
173
|
+
from bw_processing.constants import INDICES_DTYPE
|
|
174
|
+
|
|
175
|
+
dp = create_datapackage()
|
|
176
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
177
|
+
data_array = np.array([100.0, 200.0, 300.0])
|
|
178
|
+
scale_array = np.array([0.6, 1.0, 0.4]) # e.g. allocation factors
|
|
179
|
+
|
|
180
|
+
dp.add_persistent_vector(
|
|
181
|
+
matrix="technosphere",
|
|
182
|
+
name="my-process",
|
|
183
|
+
indices_array=indices_array,
|
|
184
|
+
data_array=data_array,
|
|
185
|
+
scale_array=scale_array,
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The stored resource has `kind="scale"` and can be retrieved via `dp.get_resource("my-process.scale")`. The `scale_array` must be a float dtype (`float32` or `float64`); passing an integer array raises `WrongDatatype`.
|
|
190
|
+
|
|
148
191
|
### Policies
|
|
149
192
|
|
|
150
193
|
Data package policies define how the data should be used. Policies apply to the entire data package; you may wish to adjust what is stored in which data packages to get the effect you desire.
|
|
@@ -177,7 +220,7 @@ Please make sure you understand how `combinatorial` and `sequential` interact! T
|
|
|
177
220
|
|
|
178
221
|
## Install
|
|
179
222
|
|
|
180
|
-
Install using pip or conda (channel `
|
|
223
|
+
Install using pip or conda (channel `conda-forge`). Depends on `numpy` and `pandas` (for reading and writing CSVs).
|
|
181
224
|
|
|
182
225
|
Has no explicit or implicit dependence on any other part of Brightway.
|
|
183
226
|
|
|
@@ -35,12 +35,15 @@ DEFAULT_LICENSES = [
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class MatrixSerializeFormat(str, Enum):
|
|
38
|
-
"""
|
|
39
|
-
|
|
38
|
+
"""Serialization format used when writing numpy arrays to disk.
|
|
39
|
+
|
|
40
|
+
Because this is a ``str`` enum, values can be compared directly to strings.
|
|
41
|
+
The default is ``NUMPY``. ``PARQUET`` requires the optional ``pyarrow``
|
|
42
|
+
dependency.
|
|
40
43
|
"""
|
|
41
44
|
|
|
42
|
-
NUMPY = "numpy"
|
|
43
|
-
PARQUET = "parquet"
|
|
45
|
+
NUMPY = "numpy"
|
|
46
|
+
PARQUET = "parquet"
|
|
44
47
|
|
|
45
48
|
|
|
46
49
|
# FILE EXTENSIONS
|
|
@@ -384,6 +384,17 @@ class Datapackage(DatapackageBase):
|
|
|
384
384
|
self.data = []
|
|
385
385
|
|
|
386
386
|
def finalize_serialization(self) -> None:
|
|
387
|
+
"""Write the metadata file and close the filesystem.
|
|
388
|
+
|
|
389
|
+
Must be called once after all resources have been added, before the
|
|
390
|
+
datapackage can be loaded again from disk. Dehydrates any interface
|
|
391
|
+
resources (replaces them with ``UndefinedInterface``) prior to writing.
|
|
392
|
+
|
|
393
|
+
Raises:
|
|
394
|
+
Closed: Datapackage has already been finalized.
|
|
395
|
+
ValueError: Datapackage uses an in-memory filesystem, which cannot
|
|
396
|
+
be serialized.
|
|
397
|
+
"""
|
|
387
398
|
if self._finalized:
|
|
388
399
|
raise Closed("Datapackage already finalized")
|
|
389
400
|
elif isinstance(self.fs, DictFS):
|
|
@@ -485,11 +496,20 @@ class Datapackage(DatapackageBase):
|
|
|
485
496
|
data_array: Optional[np.ndarray] = None,
|
|
486
497
|
flip_array: Optional[np.ndarray] = None,
|
|
487
498
|
distributions_array: Optional[np.ndarray] = None,
|
|
499
|
+
scale_array: Optional[np.ndarray] = None,
|
|
488
500
|
keep_proxy: bool = False,
|
|
489
501
|
matrix_serialize_format_type: Optional[MatrixSerializeFormat] = None,
|
|
490
502
|
**kwargs,
|
|
491
503
|
) -> None:
|
|
492
|
-
"""
|
|
504
|
+
"""Add a persistent vector resource group to the datapackage.
|
|
505
|
+
|
|
506
|
+
``scale_array`` is an optional 1-D float array of the same length as
|
|
507
|
+
``indices_array``. Each element is a multiplicative factor applied to
|
|
508
|
+
the corresponding data value — whether static or stochastic — before
|
|
509
|
+
the value is inserted into the matrix. Typical uses are allocation
|
|
510
|
+
factors and unit conversions. A value of ``1.0`` leaves the data
|
|
511
|
+
unchanged.
|
|
512
|
+
"""
|
|
493
513
|
self._prepare_modifications()
|
|
494
514
|
|
|
495
515
|
# Check lengths
|
|
@@ -580,6 +600,15 @@ class Datapackage(DatapackageBase):
|
|
|
580
600
|
meta_type="generic",
|
|
581
601
|
**kwargs,
|
|
582
602
|
)
|
|
603
|
+
if scale_array is not None:
|
|
604
|
+
self._add_scale_array_resource(
|
|
605
|
+
scale_array=scale_array,
|
|
606
|
+
indices_array=indices_array,
|
|
607
|
+
name=name,
|
|
608
|
+
keep_proxy=keep_proxy,
|
|
609
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
610
|
+
**kwargs,
|
|
611
|
+
)
|
|
583
612
|
|
|
584
613
|
def add_persistent_array(
|
|
585
614
|
self,
|
|
@@ -589,11 +618,20 @@ class Datapackage(DatapackageBase):
|
|
|
589
618
|
indices_array: np.ndarray,
|
|
590
619
|
name: Optional[str] = None,
|
|
591
620
|
flip_array: Optional[np.ndarray] = None,
|
|
621
|
+
scale_array: Optional[np.ndarray] = None,
|
|
592
622
|
keep_proxy: bool = False,
|
|
593
623
|
matrix_serialize_format_type: Optional[MatrixSerializeFormat] = None,
|
|
594
624
|
**kwargs,
|
|
595
625
|
) -> None:
|
|
596
|
-
"""
|
|
626
|
+
"""Add a persistent array resource group to the datapackage.
|
|
627
|
+
|
|
628
|
+
``scale_array`` is an optional 1-D float array of the same length as
|
|
629
|
+
``indices_array``. Each element is a multiplicative factor applied to
|
|
630
|
+
the corresponding data value — whether static or stochastic — before
|
|
631
|
+
the value is inserted into the matrix. Typical uses are allocation
|
|
632
|
+
factors and unit conversions. A value of ``1.0`` leaves the data
|
|
633
|
+
unchanged.
|
|
634
|
+
"""
|
|
597
635
|
self._prepare_modifications()
|
|
598
636
|
|
|
599
637
|
kwargs.update({"matrix": matrix, "category": "array", "nrows": len(indices_array)})
|
|
@@ -660,9 +698,23 @@ class Datapackage(DatapackageBase):
|
|
|
660
698
|
meta_type="generic",
|
|
661
699
|
**kwargs,
|
|
662
700
|
)
|
|
701
|
+
if scale_array is not None:
|
|
702
|
+
self._add_scale_array_resource(
|
|
703
|
+
scale_array=scale_array,
|
|
704
|
+
indices_array=indices_array,
|
|
705
|
+
name=name,
|
|
706
|
+
keep_proxy=keep_proxy,
|
|
707
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
708
|
+
**kwargs,
|
|
709
|
+
)
|
|
663
710
|
|
|
664
711
|
def write_modified(self):
|
|
665
|
-
"""
|
|
712
|
+
"""Flush modified resources back to the filesystem.
|
|
713
|
+
|
|
714
|
+
After directly editing data arrays in ``self.data``, call this method
|
|
715
|
+
to persist the changes. Clears the internal ``_modified`` set on
|
|
716
|
+
completion. Does nothing if no resources have been marked as modified.
|
|
717
|
+
"""
|
|
666
718
|
for index in self._modified:
|
|
667
719
|
# get resource
|
|
668
720
|
resource = self.resources[index]
|
|
@@ -683,7 +735,7 @@ class Datapackage(DatapackageBase):
|
|
|
683
735
|
if kind == "indices":
|
|
684
736
|
meta_object = "vector"
|
|
685
737
|
meta_type = "indices"
|
|
686
|
-
elif kind
|
|
738
|
+
elif kind in ("flip", "scale"):
|
|
687
739
|
meta_object = "vector"
|
|
688
740
|
meta_type = "generic"
|
|
689
741
|
elif kind == "distributions":
|
|
@@ -716,6 +768,39 @@ class Datapackage(DatapackageBase):
|
|
|
716
768
|
|
|
717
769
|
self._modified = set()
|
|
718
770
|
|
|
771
|
+
def _add_scale_array_resource(
|
|
772
|
+
self,
|
|
773
|
+
*,
|
|
774
|
+
scale_array: np.ndarray,
|
|
775
|
+
indices_array: np.ndarray,
|
|
776
|
+
name: str,
|
|
777
|
+
keep_proxy: bool,
|
|
778
|
+
matrix_serialize_format_type: Optional[MatrixSerializeFormat],
|
|
779
|
+
**kwargs,
|
|
780
|
+
) -> None:
|
|
781
|
+
scale_array = load_bytes(scale_array)
|
|
782
|
+
if not np.issubdtype(scale_array.dtype, np.floating):
|
|
783
|
+
raise WrongDatatype(
|
|
784
|
+
"`scale_array` dtype is {}, but must be a float dtype".format(scale_array.dtype)
|
|
785
|
+
)
|
|
786
|
+
elif scale_array.shape != indices_array.shape:
|
|
787
|
+
raise ShapeMismatch(
|
|
788
|
+
"`scale_array` shape ({}) doesn't match `indices_array` ({}).".format(
|
|
789
|
+
scale_array.shape, indices_array.shape
|
|
790
|
+
)
|
|
791
|
+
)
|
|
792
|
+
self._add_numpy_array_resource(
|
|
793
|
+
array=scale_array,
|
|
794
|
+
group=name,
|
|
795
|
+
name=name + ".scale",
|
|
796
|
+
kind="scale",
|
|
797
|
+
keep_proxy=keep_proxy,
|
|
798
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
799
|
+
meta_object="vector",
|
|
800
|
+
meta_type="generic",
|
|
801
|
+
**kwargs,
|
|
802
|
+
)
|
|
803
|
+
|
|
719
804
|
def _add_numpy_array_resource(
|
|
720
805
|
self,
|
|
721
806
|
*,
|
|
@@ -798,10 +883,36 @@ class Datapackage(DatapackageBase):
|
|
|
798
883
|
indices_array: np.ndarray, # Not interface
|
|
799
884
|
name: Optional[str] = None,
|
|
800
885
|
flip_array: Optional[np.ndarray] = None, # Not interface
|
|
886
|
+
scale_array: Optional[np.ndarray] = None, # Not interface
|
|
801
887
|
keep_proxy: bool = False,
|
|
802
888
|
matrix_serialize_format_type: Optional[MatrixSerializeFormat] = None,
|
|
803
889
|
**kwargs,
|
|
804
890
|
) -> None:
|
|
891
|
+
"""Add a dynamic vector resource group to the datapackage.
|
|
892
|
+
|
|
893
|
+
The matrix values are provided at runtime by ``interface`` rather than
|
|
894
|
+
stored on disk. ``interface`` must implement ``__next__()`` and return a
|
|
895
|
+
1-D numpy array of length ``len(indices_array)`` each time it is called.
|
|
896
|
+
|
|
897
|
+
The ``indices_array``, optional ``flip_array``, and optional
|
|
898
|
+
``scale_array`` are static and are stored as normal numpy resources.
|
|
899
|
+
|
|
900
|
+
Args:
|
|
901
|
+
matrix: Name of the target matrix.
|
|
902
|
+
interface: Object implementing the dynamic-vector interface
|
|
903
|
+
(``__next__()``).
|
|
904
|
+
indices_array: Structured numpy array with dtype ``INDICES_DTYPE``
|
|
905
|
+
mapping each data value to a matrix cell.
|
|
906
|
+
name: Optional resource group name; auto-generated if omitted.
|
|
907
|
+
flip_array: Optional boolean array; where ``True`` the value is
|
|
908
|
+
multiplied by ``-1`` before insertion.
|
|
909
|
+
scale_array: Optional 1-D float array of multiplicative factors
|
|
910
|
+
applied before matrix insertion.
|
|
911
|
+
keep_proxy: If ``True``, store a proxy rather than the raw array
|
|
912
|
+
for on-disk resources.
|
|
913
|
+
matrix_serialize_format_type: Override the instance-level
|
|
914
|
+
serialization format for static arrays in this group.
|
|
915
|
+
"""
|
|
805
916
|
self._prepare_modifications()
|
|
806
917
|
|
|
807
918
|
kwargs.update({"matrix": matrix, "category": "vector", "nrows": len(indices_array)})
|
|
@@ -843,6 +954,15 @@ class Datapackage(DatapackageBase):
|
|
|
843
954
|
meta_type="generic",
|
|
844
955
|
**kwargs,
|
|
845
956
|
)
|
|
957
|
+
if scale_array is not None:
|
|
958
|
+
self._add_scale_array_resource(
|
|
959
|
+
scale_array=scale_array,
|
|
960
|
+
indices_array=indices_array,
|
|
961
|
+
name=name,
|
|
962
|
+
keep_proxy=keep_proxy,
|
|
963
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
964
|
+
**kwargs,
|
|
965
|
+
)
|
|
846
966
|
|
|
847
967
|
self.data.append(interface)
|
|
848
968
|
resource = {
|
|
@@ -862,11 +982,38 @@ class Datapackage(DatapackageBase):
|
|
|
862
982
|
indices_array: np.ndarray, # Not interface
|
|
863
983
|
name: Optional[str] = None,
|
|
864
984
|
flip_array: Optional[np.ndarray] = None,
|
|
985
|
+
scale_array: Optional[np.ndarray] = None, # Not interface
|
|
865
986
|
keep_proxy: bool = False,
|
|
866
987
|
matrix_serialize_format_type: Optional[MatrixSerializeFormat] = None,
|
|
867
988
|
**kwargs,
|
|
868
989
|
) -> None:
|
|
869
|
-
"""
|
|
990
|
+
"""Add a dynamic array resource group to the datapackage.
|
|
991
|
+
|
|
992
|
+
The matrix values are provided at runtime by ``interface``, which must
|
|
993
|
+
implement the presamples array API: a ``.shape`` property returning
|
|
994
|
+
``(nrows, ncols)`` and ``.__getitem__(args)`` returning the 1-D column
|
|
995
|
+
array for ``args[1]``. ``ncols`` may be ``None`` for an infinite
|
|
996
|
+
interface.
|
|
997
|
+
|
|
998
|
+
The ``indices_array``, optional ``flip_array``, and optional
|
|
999
|
+
``scale_array`` are static and are stored as normal numpy resources.
|
|
1000
|
+
|
|
1001
|
+
Args:
|
|
1002
|
+
matrix: Name of the target matrix.
|
|
1003
|
+
interface: Object implementing the dynamic-array interface
|
|
1004
|
+
(``.shape`` and ``.__getitem__``).
|
|
1005
|
+
indices_array: Structured numpy array with dtype ``INDICES_DTYPE``
|
|
1006
|
+
mapping each data value to a matrix cell.
|
|
1007
|
+
name: Optional resource group name; auto-generated if omitted.
|
|
1008
|
+
flip_array: Optional boolean array; where ``True`` the value is
|
|
1009
|
+
multiplied by ``-1`` before insertion.
|
|
1010
|
+
scale_array: Optional 1-D float array of multiplicative factors
|
|
1011
|
+
applied before matrix insertion.
|
|
1012
|
+
keep_proxy: If ``True``, store a proxy rather than the raw array
|
|
1013
|
+
for on-disk resources.
|
|
1014
|
+
matrix_serialize_format_type: Override the instance-level
|
|
1015
|
+
serialization format for static arrays in this group.
|
|
1016
|
+
"""
|
|
870
1017
|
self._prepare_modifications()
|
|
871
1018
|
|
|
872
1019
|
if isinstance(flip_array, np.ndarray) and not flip_array.sum():
|
|
@@ -911,6 +1058,15 @@ class Datapackage(DatapackageBase):
|
|
|
911
1058
|
meta_type="generic",
|
|
912
1059
|
**kwargs,
|
|
913
1060
|
)
|
|
1061
|
+
if scale_array is not None:
|
|
1062
|
+
self._add_scale_array_resource(
|
|
1063
|
+
scale_array=scale_array,
|
|
1064
|
+
indices_array=indices_array,
|
|
1065
|
+
name=name,
|
|
1066
|
+
keep_proxy=keep_proxy,
|
|
1067
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
1068
|
+
**kwargs,
|
|
1069
|
+
)
|
|
914
1070
|
|
|
915
1071
|
self.data.append(interface)
|
|
916
1072
|
resource = {
|
|
@@ -27,6 +27,19 @@ except ImportError:
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def generic_directory_filesystem(*, dirpath: Path) -> DirFileSystem:
|
|
30
|
+
"""Return a ``DirFileSystem`` rooted at ``dirpath``, creating it if needed.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
dirpath: Path to the target directory. Created if it does not exist;
|
|
34
|
+
its parent must already exist.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
A ``fsspec`` ``DirFileSystem`` pointing at ``dirpath``.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
ValueError: Parent directory does not exist.
|
|
41
|
+
AssertionError: ``dirpath`` is not a ``pathlib.Path``.
|
|
42
|
+
"""
|
|
30
43
|
assert isinstance(dirpath, Path), "`dirpath` must be a `pathlib.Path` instance"
|
|
31
44
|
if not dirpath.is_dir():
|
|
32
45
|
if not dirpath.parent.is_dir():
|
|
@@ -43,6 +56,28 @@ def generic_zipfile_filesystem(
|
|
|
43
56
|
compression: int = zipfile.ZIP_DEFLATED,
|
|
44
57
|
compresslevel: Optional[int] = None,
|
|
45
58
|
) -> ZipFileSystem:
|
|
59
|
+
"""Return a ``ZipFileSystem`` for ``dirpath / filename``.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
dirpath: Directory that contains (or will contain) the zip file.
|
|
63
|
+
Must already exist.
|
|
64
|
+
filename: Name of the zip file (e.g. ``"my-dp.zip"``).
|
|
65
|
+
write: If ``True`` (default), open for writing; otherwise open for
|
|
66
|
+
reading.
|
|
67
|
+
compression: ``zipfile`` compression constant. Defaults to
|
|
68
|
+
``zipfile.ZIP_DEFLATED`` which gives good compression speed.
|
|
69
|
+
Pass ``zipfile.ZIP_STORED`` for no compression or
|
|
70
|
+
``zipfile.ZIP_LZMA`` for maximum compression.
|
|
71
|
+
compresslevel: Compression level passed to ``zipfile.ZipFile``;
|
|
72
|
+
``None`` uses the default for the chosen algorithm.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
A ``fsspec`` ``ZipFileSystem``.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
ValueError: ``dirpath`` does not exist.
|
|
79
|
+
AssertionError: ``dirpath`` is not a ``pathlib.Path``.
|
|
80
|
+
"""
|
|
46
81
|
assert isinstance(dirpath, Path), "`dirpath` must be a `pathlib.Path` instance"
|
|
47
82
|
if not dirpath.is_dir():
|
|
48
83
|
raise ValueError("Destination directory `{}` doesn't exist".format(dirpath))
|
|
@@ -5,6 +5,14 @@ class UndefinedInterface:
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class Proxy:
|
|
8
|
+
"""Deferred file-read wrapper returned when ``proxy=True`` is passed to ``file_reader``.
|
|
9
|
+
|
|
10
|
+
Stores the reader function and its arguments without executing them. The
|
|
11
|
+
actual data is loaded the first time the proxy is called (i.e. when
|
|
12
|
+
``get_resource`` resolves it). The file or buffer is rewound to position 0
|
|
13
|
+
before each call so that repeated calls return the same data.
|
|
14
|
+
"""
|
|
15
|
+
|
|
8
16
|
def __init__(self, func, label, kwargs):
|
|
9
17
|
self.func = func
|
|
10
18
|
self.label = label
|
|
@@ -60,6 +60,23 @@ def greedy_set_cover(data, exclude=None, raise_error=True):
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
def as_unique_attributes_dataframe(df, exclude=None, include=None, raise_error=False):
|
|
63
|
+
"""Return a copy of ``df`` keeping only the columns needed to uniquely identify each row.
|
|
64
|
+
|
|
65
|
+
Columns are selected using the greedy set-cover heuristic (see
|
|
66
|
+
:func:`greedy_set_cover`). Columns listed in ``include`` are always kept,
|
|
67
|
+
even if they are not required for uniqueness.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
df: Input pandas DataFrame.
|
|
71
|
+
exclude: Column names to skip during the uniqueness search.
|
|
72
|
+
include: Column names to always retain in the output.
|
|
73
|
+
raise_error: If ``True``, raise ``NonUnique`` when no unique set can be
|
|
74
|
+
found; otherwise, all columns are kept silently.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A DataFrame with only the uniqueness-covering (and always-included)
|
|
78
|
+
columns.
|
|
79
|
+
"""
|
|
63
80
|
assert isinstance(df, pd.DataFrame)
|
|
64
81
|
include = greedy_set_cover(
|
|
65
82
|
df.reset_index().to_dict("records"), exclude=exclude, raise_error=raise_error
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bw_processing
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3
|
|
4
4
|
Summary: Foo
|
|
5
5
|
Author-email: Chris Mutel <cmutel@gmail.com>
|
|
6
6
|
Maintainer-email: Chris Mutel <cmutel@gmail.com>
|
|
@@ -84,6 +84,7 @@ The [Brightway LCA framework](https://brightway.dev/) has stored data used in co
|
|
|
84
84
|
* **Dynamic data sources**. Instead of requiring that data for matrix construction be present and savedd on disk, it can now be generated dynamically, either through code running locally or on another computer system. This is a big step towards embeddding life cycle assessment in a web of environmental models.
|
|
85
85
|
* **Use [fsspec](https://filesystem-spec.readthedocs.io/en/latest/) for file IO**. The use of this library allows for data packages to be stored on your local computer, or on [many logical or virtual file systems](https://docs.pyfilesystem.org/en/latest/guide.html).
|
|
86
86
|
* **Simpler handling of numeric values whose sign should be flipped**. Sometimes it is more convenient to specify positive numbers in dataset definitions, even though such numbers should be negative when inserted into the resulting matrices. For example, in the technosphere matrix in life cycle assessment, products produced are positive and products consumed are negative, though both values are given as positive in datasets. Brightway used to use a type mapping dictionary to indicate which values in a matrix should have their sign flipped after insertion. Such mapping dictionaries are brittle and inelegant. `bw_processing` uses an optional boolean vector, called `flip`, to indicate if any values should be flipped.
|
|
87
|
+
* **Per-exchange multiplicative scaling**. An optional float vector, called `scale`, can be attached to any resource group. Each element is a multiplicative factor applied to the corresponding data value — whether static or sampled stochastically — before it is inserted into the matrix. Typical uses are allocation factors and unit conversions. A value of `1.0` leaves the data unchanged.
|
|
87
88
|
* **Separation of uncertainty distribution parameters from other data**. Fitting data to a [probability density function](https://en.wikipedia.org/wiki/Probability_density_function) (PDF), or an estimate of such a PDF, is only one approach to quantitative uncertainty analysis. We would like to support other approaches, including [direct sampling from real data](https://github.com/PascalLesage/presamples/). Therefore, uncertainty distribution parameters are stored separately, only loaded if needed, and are only one way to express quantitative uncertainty.
|
|
88
89
|
|
|
89
90
|
## Concepts
|
|
@@ -96,6 +97,18 @@ Data objects can be vectors or arrays. Vectors will always produce the same matr
|
|
|
96
97
|
|
|
97
98
|
### Vectors versus arrays
|
|
98
99
|
|
|
100
|
+
Vectors and arrays differ in how many possible values they provide per matrix cell.
|
|
101
|
+
|
|
102
|
+
A **vector** provides one value per cell. Every time a vector resource is used, it produces the same result. This is the standard case for deterministic LCA calculations.
|
|
103
|
+
|
|
104
|
+
An **array** provides multiple possible values per cell, stored as columns of a 2-D numpy array. Each time the data package is iterated, a different column is selected and inserted into the matrix. This is used for:
|
|
105
|
+
|
|
106
|
+
* **Monte Carlo analysis** — columns hold independently sampled values drawn from the uncertainty distributions.
|
|
107
|
+
* **Scenario analysis** — each column represents a predefined scenario, such as a different technology mix or policy assumption.
|
|
108
|
+
* **Presamples** — a generalization of the [presamples library](https://github.com/PascalLesage/presamples/), where pre-drawn samples are stored for reproducibility.
|
|
109
|
+
|
|
110
|
+
Which column is selected on each iteration is controlled by the `sequential` and `combinatorial` policies; see [Policies](#policies).
|
|
111
|
+
|
|
99
112
|
### Persistent versus dynamic
|
|
100
113
|
|
|
101
114
|
Persistent data is fixed, and can be completely loaded into memory and used directly or written to disk. Dynamic data is only resolved as the data is used, during matrix construction and iteration. Dynamic data is provided by *interfaces* - Python code that either generates the data, or wraps data coming from other software. There are many possible use cases for data interfaces, including:
|
|
@@ -187,6 +200,36 @@ print(data_obj.url)
|
|
|
187
200
|
>>> "example.com"
|
|
188
201
|
```
|
|
189
202
|
|
|
203
|
+
### Scale arrays
|
|
204
|
+
|
|
205
|
+
Any resource group (persistent or dynamic, vector or array) can carry an optional `scale_array`: a one-dimensional float array of the same length as `indices_array`. Each element is a multiplicative factor applied to the corresponding data value before it is inserted into the matrix. The factor is applied to both static and stochastically-sampled values. A value of `1.0` leaves the data unchanged.
|
|
206
|
+
|
|
207
|
+
Typical use cases:
|
|
208
|
+
|
|
209
|
+
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `scale_array` keeps them alongside the data they modify without requiring a separate processing step.
|
|
210
|
+
* **Unit conversions** — when source data is expressed in a unit that differs from the matrix convention, a constant conversion factor can be stored as a `scale_array` rather than baked into every data value.
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import numpy as np
|
|
214
|
+
from bw_processing import create_datapackage
|
|
215
|
+
from bw_processing.constants import INDICES_DTYPE
|
|
216
|
+
|
|
217
|
+
dp = create_datapackage()
|
|
218
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
219
|
+
data_array = np.array([100.0, 200.0, 300.0])
|
|
220
|
+
scale_array = np.array([0.6, 1.0, 0.4]) # e.g. allocation factors
|
|
221
|
+
|
|
222
|
+
dp.add_persistent_vector(
|
|
223
|
+
matrix="technosphere",
|
|
224
|
+
name="my-process",
|
|
225
|
+
indices_array=indices_array,
|
|
226
|
+
data_array=data_array,
|
|
227
|
+
scale_array=scale_array,
|
|
228
|
+
)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The stored resource has `kind="scale"` and can be retrieved via `dp.get_resource("my-process.scale")`. The `scale_array` must be a float dtype (`float32` or `float64`); passing an integer array raises `WrongDatatype`.
|
|
232
|
+
|
|
190
233
|
### Policies
|
|
191
234
|
|
|
192
235
|
Data package policies define how the data should be used. Policies apply to the entire data package; you may wish to adjust what is stored in which data packages to get the effect you desire.
|
|
@@ -219,7 +262,7 @@ Please make sure you understand how `combinatorial` and `sequential` interact! T
|
|
|
219
262
|
|
|
220
263
|
## Install
|
|
221
264
|
|
|
222
|
-
Install using pip or conda (channel `
|
|
265
|
+
Install using pip or conda (channel `conda-forge`). Depends on `numpy` and `pandas` (for reading and writing CSVs).
|
|
223
266
|
|
|
224
267
|
Has no explicit or implicit dependence on any other part of Brightway.
|
|
225
268
|
|
|
@@ -8,7 +8,7 @@ import pytest
|
|
|
8
8
|
from fsspec.implementations.zip import ZipFileSystem
|
|
9
9
|
from morefs.dict import DictFS
|
|
10
10
|
|
|
11
|
-
from bw_processing import create_datapackage, load_datapackage, simple_graph
|
|
11
|
+
from bw_processing import create_datapackage, load_datapackage, simple_graph, MatrixSerializeFormat
|
|
12
12
|
from bw_processing.constants import INDICES_DTYPE, UNCERTAINTY_DTYPE, MAX_SIGNED_64BIT_INT, MAX_SIGNED_32BIT_INT
|
|
13
13
|
from bw_processing.errors import NonUnique, PotentialInconsistency, ShapeMismatch, WrongDatatype
|
|
14
14
|
from bw_processing.io_helpers import generic_directory_filesystem, generic_zipfile_filesystem
|
|
@@ -457,6 +457,218 @@ def test_add_dynamic_vector_flip_shapemistmatch():
|
|
|
457
457
|
)
|
|
458
458
|
|
|
459
459
|
|
|
460
|
+
def test_add_persistent_vector_scale_array():
|
|
461
|
+
dp = create_datapackage()
|
|
462
|
+
data_array = np.array([2.0, 7.0, 12.0])
|
|
463
|
+
scale_array = np.array([0.5, 1.0, 2.0])
|
|
464
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
465
|
+
dp.add_persistent_vector(
|
|
466
|
+
matrix="sa_matrix",
|
|
467
|
+
data_array=data_array,
|
|
468
|
+
name="sa-data-vector",
|
|
469
|
+
indices_array=indices_array,
|
|
470
|
+
scale_array=scale_array,
|
|
471
|
+
)
|
|
472
|
+
assert "sa-data-vector.scale" in [o["name"] for o in dp.resources]
|
|
473
|
+
data, meta = dp.get_resource("sa-data-vector.scale")
|
|
474
|
+
assert meta["kind"] == "scale"
|
|
475
|
+
assert np.allclose(data, scale_array)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def test_add_persistent_vector_scale_dtype():
|
|
479
|
+
dp = create_datapackage()
|
|
480
|
+
data_array = np.array([2.0, 7.0, 12.0])
|
|
481
|
+
scale_array = np.array([1, 2, 3]) # integer dtype
|
|
482
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
483
|
+
with pytest.raises(WrongDatatype):
|
|
484
|
+
dp.add_persistent_vector(
|
|
485
|
+
matrix="sa_matrix",
|
|
486
|
+
data_array=data_array,
|
|
487
|
+
name="sa-data-vector",
|
|
488
|
+
indices_array=indices_array,
|
|
489
|
+
scale_array=scale_array,
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def test_add_persistent_vector_scale_shapemismatch():
|
|
494
|
+
dp = create_datapackage()
|
|
495
|
+
data_array = np.array([2.0, 7.0, 12.0])
|
|
496
|
+
scale_array = np.array([0.5, 1.0, 2.0, 3.0])
|
|
497
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
498
|
+
with pytest.raises(ShapeMismatch):
|
|
499
|
+
dp.add_persistent_vector(
|
|
500
|
+
matrix="sa_matrix",
|
|
501
|
+
data_array=data_array,
|
|
502
|
+
name="sa-data-vector",
|
|
503
|
+
indices_array=indices_array,
|
|
504
|
+
scale_array=scale_array,
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def test_add_persistent_array_scale_array():
|
|
509
|
+
dp = create_datapackage()
|
|
510
|
+
data_array = np.arange(12, dtype=float).reshape(3, 4)
|
|
511
|
+
scale_array = np.array([0.5, 1.0, 2.0])
|
|
512
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
513
|
+
dp.add_persistent_array(
|
|
514
|
+
matrix="sa_matrix",
|
|
515
|
+
data_array=data_array,
|
|
516
|
+
name="sa-data-vector",
|
|
517
|
+
indices_array=indices_array,
|
|
518
|
+
scale_array=scale_array,
|
|
519
|
+
)
|
|
520
|
+
assert "sa-data-vector.scale" in [o["name"] for o in dp.resources]
|
|
521
|
+
data, meta = dp.get_resource("sa-data-vector.scale")
|
|
522
|
+
assert meta["kind"] == "scale"
|
|
523
|
+
assert np.allclose(data, scale_array)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def test_add_persistent_array_scale_dtype():
|
|
527
|
+
dp = create_datapackage()
|
|
528
|
+
data_array = np.arange(12, dtype=float).reshape(3, 4)
|
|
529
|
+
scale_array = np.array([1, 2, 3])
|
|
530
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
531
|
+
with pytest.raises(WrongDatatype):
|
|
532
|
+
dp.add_persistent_array(
|
|
533
|
+
matrix="sa_matrix",
|
|
534
|
+
data_array=data_array,
|
|
535
|
+
name="sa-data-vector",
|
|
536
|
+
indices_array=indices_array,
|
|
537
|
+
scale_array=scale_array,
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def test_add_persistent_array_scale_shapemismatch():
|
|
542
|
+
dp = create_datapackage()
|
|
543
|
+
data_array = np.arange(12, dtype=float).reshape(3, 4)
|
|
544
|
+
scale_array = np.array([0.5, 1.0, 2.0, 3.0])
|
|
545
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
546
|
+
with pytest.raises(ShapeMismatch):
|
|
547
|
+
dp.add_persistent_array(
|
|
548
|
+
matrix="sa_matrix",
|
|
549
|
+
data_array=data_array,
|
|
550
|
+
name="sa-data-vector",
|
|
551
|
+
indices_array=indices_array,
|
|
552
|
+
scale_array=scale_array,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def test_add_dynamic_vector_scale_array():
|
|
557
|
+
dp = create_datapackage()
|
|
558
|
+
scale_array = np.array([0.5, 1.0, 2.0])
|
|
559
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
560
|
+
dp.add_dynamic_vector(
|
|
561
|
+
matrix="sa_matrix",
|
|
562
|
+
interface=Dummy(),
|
|
563
|
+
name="sa-data-vector",
|
|
564
|
+
indices_array=indices_array,
|
|
565
|
+
scale_array=scale_array,
|
|
566
|
+
)
|
|
567
|
+
assert "sa-data-vector.scale" in [o["name"] for o in dp.resources]
|
|
568
|
+
data, meta = dp.get_resource("sa-data-vector.scale")
|
|
569
|
+
assert meta["kind"] == "scale"
|
|
570
|
+
assert np.allclose(data, scale_array)
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def test_add_dynamic_vector_scale_dtype():
|
|
574
|
+
dp = create_datapackage()
|
|
575
|
+
scale_array = np.array([1, 2, 3])
|
|
576
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
577
|
+
with pytest.raises(WrongDatatype):
|
|
578
|
+
dp.add_dynamic_vector(
|
|
579
|
+
matrix="sa_matrix",
|
|
580
|
+
interface=Dummy(),
|
|
581
|
+
name="sa-data-vector",
|
|
582
|
+
indices_array=indices_array,
|
|
583
|
+
scale_array=scale_array,
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def test_add_dynamic_vector_scale_shapemismatch():
|
|
588
|
+
dp = create_datapackage()
|
|
589
|
+
scale_array = np.array([0.5, 1.0, 2.0, 3.0])
|
|
590
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
591
|
+
with pytest.raises(ShapeMismatch):
|
|
592
|
+
dp.add_dynamic_vector(
|
|
593
|
+
matrix="sa_matrix",
|
|
594
|
+
interface=Dummy(),
|
|
595
|
+
name="sa-data-vector",
|
|
596
|
+
indices_array=indices_array,
|
|
597
|
+
scale_array=scale_array,
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def test_add_dynamic_array_scale_array():
|
|
602
|
+
dp = create_datapackage()
|
|
603
|
+
scale_array = np.array([0.5, 1.0, 2.0])
|
|
604
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
605
|
+
dp.add_dynamic_array(
|
|
606
|
+
matrix="sa_matrix",
|
|
607
|
+
interface=Dummy(),
|
|
608
|
+
name="sa-data-vector",
|
|
609
|
+
indices_array=indices_array,
|
|
610
|
+
scale_array=scale_array,
|
|
611
|
+
)
|
|
612
|
+
assert "sa-data-vector.scale" in [o["name"] for o in dp.resources]
|
|
613
|
+
data, meta = dp.get_resource("sa-data-vector.scale")
|
|
614
|
+
assert meta["kind"] == "scale"
|
|
615
|
+
assert np.allclose(data, scale_array)
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def test_add_dynamic_array_scale_dtype():
|
|
619
|
+
dp = create_datapackage()
|
|
620
|
+
scale_array = np.array([1, 2, 3])
|
|
621
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
622
|
+
with pytest.raises(WrongDatatype):
|
|
623
|
+
dp.add_dynamic_array(
|
|
624
|
+
matrix="sa_matrix",
|
|
625
|
+
interface=Dummy(),
|
|
626
|
+
name="sa-data-vector",
|
|
627
|
+
indices_array=indices_array,
|
|
628
|
+
scale_array=scale_array,
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def test_add_dynamic_array_scale_shapemismatch():
|
|
633
|
+
dp = create_datapackage()
|
|
634
|
+
scale_array = np.array([0.5, 1.0, 2.0, 3.0])
|
|
635
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
636
|
+
with pytest.raises(ShapeMismatch):
|
|
637
|
+
dp.add_dynamic_array(
|
|
638
|
+
matrix="sa_matrix",
|
|
639
|
+
interface=Dummy(),
|
|
640
|
+
name="sa-data-vector",
|
|
641
|
+
indices_array=indices_array,
|
|
642
|
+
scale_array=scale_array,
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def test_scale_array_parquet_roundtrip(tmp_path):
|
|
647
|
+
scale_array = np.array([0.5, 1.0, 2.0])
|
|
648
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
649
|
+
data_array = np.array([100.0, 200.0, 300.0])
|
|
650
|
+
|
|
651
|
+
dp = create_datapackage(
|
|
652
|
+
fs=generic_directory_filesystem(dirpath=tmp_path),
|
|
653
|
+
name="scale-parquet-test",
|
|
654
|
+
)
|
|
655
|
+
dp.add_persistent_vector(
|
|
656
|
+
matrix="sa_matrix",
|
|
657
|
+
data_array=data_array,
|
|
658
|
+
name="sa-data-vector",
|
|
659
|
+
indices_array=indices_array,
|
|
660
|
+
scale_array=scale_array,
|
|
661
|
+
matrix_serialize_format_type=MatrixSerializeFormat.PARQUET,
|
|
662
|
+
)
|
|
663
|
+
dp.finalize_serialization()
|
|
664
|
+
|
|
665
|
+
dp2 = load_datapackage(generic_directory_filesystem(dirpath=tmp_path))
|
|
666
|
+
assert "sa-data-vector.scale" in [o["name"] for o in dp2.resources]
|
|
667
|
+
loaded, meta = dp2.get_resource("sa-data-vector.scale")
|
|
668
|
+
assert meta["kind"] == "scale"
|
|
669
|
+
assert np.allclose(loaded, scale_array)
|
|
670
|
+
|
|
671
|
+
|
|
460
672
|
def test_simple_graph():
|
|
461
673
|
data = {
|
|
462
674
|
"some_matrix": [(1, 2, 3.14), (4, 5, 17, True), (8, 9, 11.11, False)],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|