bw-processing 1.5__tar.gz → 1.6__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.5/src/bw_processing.egg-info → bw_processing-1.6}/PKG-INFO +34 -7
- {bw_processing-1.5 → bw_processing-1.6}/README.md +33 -6
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/__init__.py +1 -1
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/datapackage.py +101 -3
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/matrix_entry.py +18 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/merging.py +1 -1
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/utils.py +8 -1
- {bw_processing-1.5 → bw_processing-1.6/src/bw_processing.egg-info}/PKG-INFO +34 -7
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_datapackage.py +98 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_matrix_entry.py +95 -1
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_utils.py +17 -5
- {bw_processing-1.5 → bw_processing-1.6}/LICENSE +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/MANIFEST.in +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/pyproject.toml +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/setup.cfg +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/array_creation.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/constants.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/errors.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/__init__.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_1/another name.indices.parquet +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.data.parquet +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.distributions.parquet +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.flip.parquet +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_1/sa-data-vector-from-dict.indices.parquet +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_1/some name.data.parquet +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_1/some name.flip.parquet +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_1/some name.indices.parquet +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/datapackage_2.zip +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/interfaces.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/parquet_files.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/examples/simple.zip +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/filesystem.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/indexing.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/io_helpers.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/io_parquet_helpers.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/io_pyarrow_helpers.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/param_labels.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/proxies.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing/unique_fields.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing.egg-info/SOURCES.txt +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing.egg-info/dependency_links.txt +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing.egg-info/requires.txt +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/src/bw_processing.egg-info/top_level.txt +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_array_creation.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_filesystem.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_filtered_datapackage.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_indexing.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_integration.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_interfaces.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_io_parquet_helpers.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_io_pyarrow_helpers.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_loading.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_merging.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_params.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_proxies.py +0 -0
- {bw_processing-1.5 → bw_processing-1.6}/tests/test_unique_fields.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bw_processing
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6
|
|
4
4
|
Summary: Foo
|
|
5
5
|
Author-email: Chris Mutel <cmutel@gmail.com>
|
|
6
6
|
Maintainer-email: Chris Mutel <cmutel@gmail.com>
|
|
@@ -211,12 +211,12 @@ print(data_obj.url)
|
|
|
211
211
|
|
|
212
212
|
### Scale arrays
|
|
213
213
|
|
|
214
|
-
Any resource group (persistent or dynamic, vector or array) can carry an optional `
|
|
214
|
+
Any resource group (persistent or dynamic, vector or array) can carry an optional `rescale_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.
|
|
215
215
|
|
|
216
216
|
Typical use cases:
|
|
217
217
|
|
|
218
|
-
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `
|
|
219
|
-
* **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 `
|
|
218
|
+
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `rescale_array` keeps them alongside the data they modify without requiring a separate processing step.
|
|
219
|
+
* **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 `rescale_array` rather than baked into every data value.
|
|
220
220
|
|
|
221
221
|
```python
|
|
222
222
|
import numpy as np
|
|
@@ -226,18 +226,45 @@ from bw_processing.constants import INDICES_DTYPE
|
|
|
226
226
|
dp = create_datapackage()
|
|
227
227
|
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
228
228
|
data_array = np.array([100.0, 200.0, 300.0])
|
|
229
|
-
|
|
229
|
+
rescale_array = np.array([0.6, 1.0, 0.4]) # e.g. allocation factors
|
|
230
230
|
|
|
231
231
|
dp.add_persistent_vector(
|
|
232
232
|
matrix="technosphere",
|
|
233
233
|
name="my-process",
|
|
234
234
|
indices_array=indices_array,
|
|
235
235
|
data_array=data_array,
|
|
236
|
-
|
|
236
|
+
rescale_array=rescale_array,
|
|
237
237
|
)
|
|
238
238
|
```
|
|
239
239
|
|
|
240
|
-
The stored resource has `kind="
|
|
240
|
+
The stored resource has `kind="rescale"` and can be retrieved via `dp.get_resource("my-process.rescale")`. The `rescale_array` must be a float dtype (`float32` or `float64`); passing an integer array raises `WrongDatatype`.
|
|
241
|
+
|
|
242
|
+
### Reference (production) exchanges
|
|
243
|
+
|
|
244
|
+
Any resource group can also carry an optional `reference_array`: a one-dimensional boolean array of the same length as `indices_array`. Where an element is `True`, that exchange is the **reference (production) exchange** for its activity/column.
|
|
245
|
+
|
|
246
|
+
The five structural heuristics in `bw_graph_tools` (matching ids, single non-flipped entry, single positive, single negative, unique product) cannot always identify the reference exchange — whenever an activity has more than one same-sign exchange and the products also appear in other columns, the choice is genuinely ambiguous. Only the modeller knows the answer. `reference_array` records it directly so downstream tools can read it instead of guessing.
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
import numpy as np
|
|
250
|
+
from bw_processing import create_datapackage
|
|
251
|
+
from bw_processing.constants import INDICES_DTYPE
|
|
252
|
+
|
|
253
|
+
dp = create_datapackage()
|
|
254
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
255
|
+
data_array = np.array([1.0, 0.5, 2.0])
|
|
256
|
+
reference_array = np.array([True, False, False]) # first exchange is the reference
|
|
257
|
+
|
|
258
|
+
dp.add_persistent_vector(
|
|
259
|
+
matrix="technosphere",
|
|
260
|
+
name="my-process",
|
|
261
|
+
indices_array=indices_array,
|
|
262
|
+
data_array=data_array,
|
|
263
|
+
reference_array=reference_array,
|
|
264
|
+
)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The stored resource has `kind="reference"` and can be retrieved via `dp.get_resource("my-process.reference")`. It must be a boolean array; passing a non-boolean array raises `WrongDatatype`. To keep the common case cheap, the resource is written only when at least one entry is `True` — a group with no reference flags carries no `reference` resource. Using the high-level `MatrixEntry`/`ArrayEntry` API, set `reference=True` (or a boolean `reference` array) on the entries you want flagged.
|
|
241
268
|
|
|
242
269
|
### Parameter arrays for sensitivity analysis
|
|
243
270
|
|
|
@@ -168,12 +168,12 @@ print(data_obj.url)
|
|
|
168
168
|
|
|
169
169
|
### Scale arrays
|
|
170
170
|
|
|
171
|
-
Any resource group (persistent or dynamic, vector or array) can carry an optional `
|
|
171
|
+
Any resource group (persistent or dynamic, vector or array) can carry an optional `rescale_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.
|
|
172
172
|
|
|
173
173
|
Typical use cases:
|
|
174
174
|
|
|
175
|
-
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `
|
|
176
|
-
* **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 `
|
|
175
|
+
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `rescale_array` keeps them alongside the data they modify without requiring a separate processing step.
|
|
176
|
+
* **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 `rescale_array` rather than baked into every data value.
|
|
177
177
|
|
|
178
178
|
```python
|
|
179
179
|
import numpy as np
|
|
@@ -183,18 +183,45 @@ from bw_processing.constants import INDICES_DTYPE
|
|
|
183
183
|
dp = create_datapackage()
|
|
184
184
|
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
185
185
|
data_array = np.array([100.0, 200.0, 300.0])
|
|
186
|
-
|
|
186
|
+
rescale_array = np.array([0.6, 1.0, 0.4]) # e.g. allocation factors
|
|
187
187
|
|
|
188
188
|
dp.add_persistent_vector(
|
|
189
189
|
matrix="technosphere",
|
|
190
190
|
name="my-process",
|
|
191
191
|
indices_array=indices_array,
|
|
192
192
|
data_array=data_array,
|
|
193
|
-
|
|
193
|
+
rescale_array=rescale_array,
|
|
194
194
|
)
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
The stored resource has `kind="
|
|
197
|
+
The stored resource has `kind="rescale"` and can be retrieved via `dp.get_resource("my-process.rescale")`. The `rescale_array` must be a float dtype (`float32` or `float64`); passing an integer array raises `WrongDatatype`.
|
|
198
|
+
|
|
199
|
+
### Reference (production) exchanges
|
|
200
|
+
|
|
201
|
+
Any resource group can also carry an optional `reference_array`: a one-dimensional boolean array of the same length as `indices_array`. Where an element is `True`, that exchange is the **reference (production) exchange** for its activity/column.
|
|
202
|
+
|
|
203
|
+
The five structural heuristics in `bw_graph_tools` (matching ids, single non-flipped entry, single positive, single negative, unique product) cannot always identify the reference exchange — whenever an activity has more than one same-sign exchange and the products also appear in other columns, the choice is genuinely ambiguous. Only the modeller knows the answer. `reference_array` records it directly so downstream tools can read it instead of guessing.
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
import numpy as np
|
|
207
|
+
from bw_processing import create_datapackage
|
|
208
|
+
from bw_processing.constants import INDICES_DTYPE
|
|
209
|
+
|
|
210
|
+
dp = create_datapackage()
|
|
211
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
212
|
+
data_array = np.array([1.0, 0.5, 2.0])
|
|
213
|
+
reference_array = np.array([True, False, False]) # first exchange is the reference
|
|
214
|
+
|
|
215
|
+
dp.add_persistent_vector(
|
|
216
|
+
matrix="technosphere",
|
|
217
|
+
name="my-process",
|
|
218
|
+
indices_array=indices_array,
|
|
219
|
+
data_array=data_array,
|
|
220
|
+
reference_array=reference_array,
|
|
221
|
+
)
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
The stored resource has `kind="reference"` and can be retrieved via `dp.get_resource("my-process.reference")`. It must be a boolean array; passing a non-boolean array raises `WrongDatatype`. To keep the common case cheap, the resource is written only when at least one entry is `True` — a group with no reference flags carries no `reference` resource. Using the high-level `MatrixEntry`/`ArrayEntry` API, set `reference=True` (or a boolean `reference` array) on the entries you want flagged.
|
|
198
225
|
|
|
199
226
|
### Parameter arrays for sensitivity analysis
|
|
200
227
|
|
|
@@ -512,6 +512,7 @@ class Datapackage(DatapackageBase):
|
|
|
512
512
|
distributions_array,
|
|
513
513
|
flip_array,
|
|
514
514
|
rescale_array,
|
|
515
|
+
reference_array,
|
|
515
516
|
) = resolve_dict_iterator(dict_iterator, nrows)
|
|
516
517
|
self.add_persistent_vector(
|
|
517
518
|
matrix=matrix,
|
|
@@ -522,6 +523,7 @@ class Datapackage(DatapackageBase):
|
|
|
522
523
|
flip_array=flip_array,
|
|
523
524
|
distributions_array=distributions_array,
|
|
524
525
|
rescale_array=rescale_array,
|
|
526
|
+
reference_array=reference_array,
|
|
525
527
|
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
526
528
|
**kwargs,
|
|
527
529
|
)
|
|
@@ -538,6 +540,8 @@ class Datapackage(DatapackageBase):
|
|
|
538
540
|
High-level convenience method that does not require working directly
|
|
539
541
|
with NumPy arrays. If any entry has a ``rescale`` value other than
|
|
540
542
|
``1.0``, the rescale values are stored as a ``rescale_array`` resource.
|
|
543
|
+
If any entry has ``reference=True``, the reference flags are stored as
|
|
544
|
+
a ``reference_array`` resource (``kind="reference"``).
|
|
541
545
|
|
|
542
546
|
Args:
|
|
543
547
|
matrix: Name of the target matrix (e.g. ``"technosphere"``).
|
|
@@ -562,6 +566,8 @@ class Datapackage(DatapackageBase):
|
|
|
562
566
|
Each :class:`.ArrayEntry` becomes one persistent-array resource group.
|
|
563
567
|
Resource group names are auto-generated. If an entry has a ``rescale``
|
|
564
568
|
array it is stored as a ``rescale_array`` resource (``kind="rescale"``).
|
|
569
|
+
If an entry has a ``reference`` array with any ``True`` value it is
|
|
570
|
+
stored as a ``reference_array`` resource (``kind="reference"``).
|
|
565
571
|
|
|
566
572
|
Args:
|
|
567
573
|
matrix: Name of the target matrix (e.g. ``"technosphere"``).
|
|
@@ -577,6 +583,7 @@ class Datapackage(DatapackageBase):
|
|
|
577
583
|
data_array=entry.data,
|
|
578
584
|
flip_array=entry.flip,
|
|
579
585
|
rescale_array=entry.rescale,
|
|
586
|
+
reference_array=entry.reference,
|
|
580
587
|
)
|
|
581
588
|
|
|
582
589
|
def add_persistent_vector(
|
|
@@ -589,6 +596,7 @@ class Datapackage(DatapackageBase):
|
|
|
589
596
|
flip_array: Optional[np.ndarray] = None,
|
|
590
597
|
distributions_array: Optional[np.ndarray] = None,
|
|
591
598
|
rescale_array: Optional[np.ndarray] = None,
|
|
599
|
+
reference_array: Optional[np.ndarray] = None,
|
|
592
600
|
params_array: Optional[np.ndarray] = None,
|
|
593
601
|
param_labels: Optional[list] = None,
|
|
594
602
|
param_label_schema: Optional[AnyLabelSchema] = None,
|
|
@@ -605,6 +613,12 @@ class Datapackage(DatapackageBase):
|
|
|
605
613
|
factors and unit conversions. A value of ``1.0`` leaves the data
|
|
606
614
|
unchanged.
|
|
607
615
|
|
|
616
|
+
``reference_array`` is an optional 1-D boolean array of the same length
|
|
617
|
+
as ``indices_array``. Where ``True``, that entry is the reference
|
|
618
|
+
(production) exchange for its column. It is stored as a
|
|
619
|
+
``reference_array`` resource (``kind="reference"``) only when at least
|
|
620
|
+
one entry is flagged.
|
|
621
|
+
|
|
608
622
|
``params_array`` is an optional 1-D float array recording the values of
|
|
609
623
|
independent variables (e.g. model parameters) used to generate this
|
|
610
624
|
resource group. ``param_labels`` is an optional list of label objects
|
|
@@ -713,6 +727,15 @@ class Datapackage(DatapackageBase):
|
|
|
713
727
|
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
714
728
|
**kwargs,
|
|
715
729
|
)
|
|
730
|
+
if reference_array is not None:
|
|
731
|
+
self._add_reference_array_resource(
|
|
732
|
+
reference_array=reference_array,
|
|
733
|
+
indices_array=indices_array,
|
|
734
|
+
name=name,
|
|
735
|
+
keep_proxy=keep_proxy,
|
|
736
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
737
|
+
**kwargs,
|
|
738
|
+
)
|
|
716
739
|
if params_array is not None:
|
|
717
740
|
params_array = load_bytes(params_array)
|
|
718
741
|
if params_array.ndim != 1:
|
|
@@ -751,6 +774,7 @@ class Datapackage(DatapackageBase):
|
|
|
751
774
|
name: Optional[str] = None,
|
|
752
775
|
flip_array: Optional[np.ndarray] = None,
|
|
753
776
|
rescale_array: Optional[np.ndarray] = None,
|
|
777
|
+
reference_array: Optional[np.ndarray] = None,
|
|
754
778
|
params_array: Optional[np.ndarray] = None,
|
|
755
779
|
param_labels: Optional[list] = None,
|
|
756
780
|
param_label_schema: Optional[AnyLabelSchema] = None,
|
|
@@ -767,6 +791,12 @@ class Datapackage(DatapackageBase):
|
|
|
767
791
|
factors and unit conversions. A value of ``1.0`` leaves the data
|
|
768
792
|
unchanged.
|
|
769
793
|
|
|
794
|
+
``reference_array`` is an optional 1-D boolean array of the same length
|
|
795
|
+
as ``indices_array``. Where ``True``, that entry is the reference
|
|
796
|
+
(production) exchange for its column. It is stored as a
|
|
797
|
+
``reference_array`` resource (``kind="reference"``) only when at least
|
|
798
|
+
one entry is flagged.
|
|
799
|
+
|
|
770
800
|
``params_array`` is an optional 2-D float array of shape
|
|
771
801
|
``(n_params, n_scenarios)`` where ``n_scenarios`` must equal
|
|
772
802
|
``data_array.shape[1]``. It records the independent variable values
|
|
@@ -850,6 +880,15 @@ class Datapackage(DatapackageBase):
|
|
|
850
880
|
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
851
881
|
**kwargs,
|
|
852
882
|
)
|
|
883
|
+
if reference_array is not None:
|
|
884
|
+
self._add_reference_array_resource(
|
|
885
|
+
reference_array=reference_array,
|
|
886
|
+
indices_array=indices_array,
|
|
887
|
+
name=name,
|
|
888
|
+
keep_proxy=keep_proxy,
|
|
889
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
890
|
+
**kwargs,
|
|
891
|
+
)
|
|
853
892
|
if params_array is not None:
|
|
854
893
|
params_array = load_bytes(params_array)
|
|
855
894
|
if params_array.ndim != 2:
|
|
@@ -912,7 +951,7 @@ class Datapackage(DatapackageBase):
|
|
|
912
951
|
if kind == "indices":
|
|
913
952
|
meta_object = "vector"
|
|
914
953
|
meta_type = "indices"
|
|
915
|
-
elif kind in ("flip", "rescale", "params"):
|
|
954
|
+
elif kind in ("flip", "rescale", "reference", "params"):
|
|
916
955
|
meta_object = "vector"
|
|
917
956
|
meta_type = "generic"
|
|
918
957
|
elif kind == "distributions":
|
|
@@ -986,6 +1025,41 @@ class Datapackage(DatapackageBase):
|
|
|
986
1025
|
**kwargs,
|
|
987
1026
|
)
|
|
988
1027
|
|
|
1028
|
+
def _add_reference_array_resource(
|
|
1029
|
+
self,
|
|
1030
|
+
*,
|
|
1031
|
+
reference_array: np.ndarray,
|
|
1032
|
+
indices_array: np.ndarray,
|
|
1033
|
+
name: str,
|
|
1034
|
+
keep_proxy: bool,
|
|
1035
|
+
matrix_serialize_format_type: Optional[MatrixSerializeFormat],
|
|
1036
|
+
**kwargs,
|
|
1037
|
+
) -> None:
|
|
1038
|
+
reference_array = load_bytes(reference_array)
|
|
1039
|
+
if reference_array.dtype != bool:
|
|
1040
|
+
raise WrongDatatype(
|
|
1041
|
+
"`reference_array` dtype is {}, but must be `bool`".format(reference_array.dtype)
|
|
1042
|
+
)
|
|
1043
|
+
if reference_array.shape != indices_array.shape:
|
|
1044
|
+
raise ShapeMismatch(
|
|
1045
|
+
"`reference_array` shape ({}) doesn't match `indices_array` ({}).".format(
|
|
1046
|
+
reference_array.shape, indices_array.shape
|
|
1047
|
+
)
|
|
1048
|
+
)
|
|
1049
|
+
# If no references flagged, don't need to store it
|
|
1050
|
+
if reference_array.sum():
|
|
1051
|
+
self._add_numpy_array_resource(
|
|
1052
|
+
array=reference_array,
|
|
1053
|
+
group=name,
|
|
1054
|
+
name=name + ".reference",
|
|
1055
|
+
kind="reference",
|
|
1056
|
+
keep_proxy=keep_proxy,
|
|
1057
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
1058
|
+
meta_object="vector",
|
|
1059
|
+
meta_type="generic",
|
|
1060
|
+
**kwargs,
|
|
1061
|
+
)
|
|
1062
|
+
|
|
989
1063
|
@staticmethod
|
|
990
1064
|
def _check_params_args(
|
|
991
1065
|
params_array: Optional[np.ndarray],
|
|
@@ -1133,6 +1207,7 @@ class Datapackage(DatapackageBase):
|
|
|
1133
1207
|
name: Optional[str] = None,
|
|
1134
1208
|
flip_array: Optional[np.ndarray] = None, # Not interface
|
|
1135
1209
|
rescale_array: Optional[np.ndarray] = None, # Not interface
|
|
1210
|
+
reference_array: Optional[np.ndarray] = None, # Not interface
|
|
1136
1211
|
params_array: Optional[np.ndarray] = None, # Not interface
|
|
1137
1212
|
param_labels: Optional[list] = None,
|
|
1138
1213
|
param_label_schema: Optional[AnyLabelSchema] = None,
|
|
@@ -1147,7 +1222,7 @@ class Datapackage(DatapackageBase):
|
|
|
1147
1222
|
1-D numpy array of length ``len(indices_array)`` each time it is called.
|
|
1148
1223
|
|
|
1149
1224
|
The ``indices_array``, optional ``flip_array``, optional ``rescale_array``,
|
|
1150
|
-
and optional ``params_array`` are static and are stored as normal numpy
|
|
1225
|
+
optional ``reference_array``, and optional ``params_array`` are static and are stored as normal numpy
|
|
1151
1226
|
resources. See ``add_persistent_vector`` for documentation of the
|
|
1152
1227
|
``params_array``, ``param_labels``, and ``param_label_schema`` arguments.
|
|
1153
1228
|
|
|
@@ -1162,6 +1237,8 @@ class Datapackage(DatapackageBase):
|
|
|
1162
1237
|
multiplied by ``-1`` before insertion.
|
|
1163
1238
|
rescale_array: Optional 1-D float array of multiplicative factors
|
|
1164
1239
|
applied before matrix insertion.
|
|
1240
|
+
reference_array: Optional 1-D boolean array; where ``True`` the
|
|
1241
|
+
entry is the reference (production) exchange for its column.
|
|
1165
1242
|
keep_proxy: If ``True``, store a proxy rather than the raw array
|
|
1166
1243
|
for on-disk resources.
|
|
1167
1244
|
matrix_serialize_format_type: Override the instance-level
|
|
@@ -1219,6 +1296,15 @@ class Datapackage(DatapackageBase):
|
|
|
1219
1296
|
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
1220
1297
|
**kwargs,
|
|
1221
1298
|
)
|
|
1299
|
+
if reference_array is not None:
|
|
1300
|
+
self._add_reference_array_resource(
|
|
1301
|
+
reference_array=reference_array,
|
|
1302
|
+
indices_array=indices_array,
|
|
1303
|
+
name=name,
|
|
1304
|
+
keep_proxy=keep_proxy,
|
|
1305
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
1306
|
+
**kwargs,
|
|
1307
|
+
)
|
|
1222
1308
|
if params_array is not None:
|
|
1223
1309
|
params_array = load_bytes(params_array)
|
|
1224
1310
|
if params_array.ndim != 1:
|
|
@@ -1267,6 +1353,7 @@ class Datapackage(DatapackageBase):
|
|
|
1267
1353
|
name: Optional[str] = None,
|
|
1268
1354
|
flip_array: Optional[np.ndarray] = None,
|
|
1269
1355
|
rescale_array: Optional[np.ndarray] = None, # Not interface
|
|
1356
|
+
reference_array: Optional[np.ndarray] = None, # Not interface
|
|
1270
1357
|
params_array: Optional[np.ndarray] = None, # Not interface
|
|
1271
1358
|
param_labels: Optional[list] = None,
|
|
1272
1359
|
param_label_schema: Optional[AnyLabelSchema] = None,
|
|
@@ -1283,7 +1370,7 @@ class Datapackage(DatapackageBase):
|
|
|
1283
1370
|
interface.
|
|
1284
1371
|
|
|
1285
1372
|
The ``indices_array``, optional ``flip_array``, optional ``rescale_array``,
|
|
1286
|
-
and optional ``params_array`` are static and are stored as normal numpy
|
|
1373
|
+
optional ``reference_array``, and optional ``params_array`` are static and are stored as normal numpy
|
|
1287
1374
|
resources. For dynamic arrays the column count of ``params_array`` is
|
|
1288
1375
|
not validated against the interface (whose column count may be unknown at
|
|
1289
1376
|
write time). See ``add_persistent_vector`` for documentation of the
|
|
@@ -1300,6 +1387,8 @@ class Datapackage(DatapackageBase):
|
|
|
1300
1387
|
multiplied by ``-1`` before insertion.
|
|
1301
1388
|
rescale_array: Optional 1-D float array of multiplicative factors
|
|
1302
1389
|
applied before matrix insertion.
|
|
1390
|
+
reference_array: Optional 1-D boolean array; where ``True`` the
|
|
1391
|
+
entry is the reference (production) exchange for its column.
|
|
1303
1392
|
keep_proxy: If ``True``, store a proxy rather than the raw array
|
|
1304
1393
|
for on-disk resources.
|
|
1305
1394
|
matrix_serialize_format_type: Override the instance-level
|
|
@@ -1365,6 +1454,15 @@ class Datapackage(DatapackageBase):
|
|
|
1365
1454
|
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
1366
1455
|
**kwargs,
|
|
1367
1456
|
)
|
|
1457
|
+
if reference_array is not None:
|
|
1458
|
+
self._add_reference_array_resource(
|
|
1459
|
+
reference_array=reference_array,
|
|
1460
|
+
indices_array=indices_array,
|
|
1461
|
+
name=name,
|
|
1462
|
+
keep_proxy=keep_proxy,
|
|
1463
|
+
matrix_serialize_format_type=matrix_serialize_format_type,
|
|
1464
|
+
**kwargs,
|
|
1465
|
+
)
|
|
1368
1466
|
if params_array is not None:
|
|
1369
1467
|
params_array = load_bytes(params_array)
|
|
1370
1468
|
if params_array.ndim != 2:
|
|
@@ -63,6 +63,12 @@ class MatrixEntry:
|
|
|
63
63
|
Stored as a ``rescale_array`` resource (``kind="rescale"``). Note
|
|
64
64
|
that the Python ``float`` value is downcast to ``numpy.float32``
|
|
65
65
|
when written to the structured array.
|
|
66
|
+
reference: If True, this exchange is the reference (production) exchange
|
|
67
|
+
for its activity/column. Consumers such as bw_graph_tools use this
|
|
68
|
+
to identify production exchanges directly instead of guessing from
|
|
69
|
+
matrix structure. Stored as a ``reference_array`` resource
|
|
70
|
+
(``kind="reference"``) only when at least one entry is flagged;
|
|
71
|
+
defaults to False.
|
|
66
72
|
"""
|
|
67
73
|
|
|
68
74
|
row: int
|
|
@@ -77,6 +83,7 @@ class MatrixEntry:
|
|
|
77
83
|
maximum: float = math.nan
|
|
78
84
|
negative: bool = False
|
|
79
85
|
rescale: float = 1.0
|
|
86
|
+
reference: bool = False
|
|
80
87
|
|
|
81
88
|
def __post_init__(self):
|
|
82
89
|
if self.uncertainty_type in _NO_UNCERTAINTY_IDS:
|
|
@@ -109,6 +116,10 @@ class ArrayEntry:
|
|
|
109
116
|
rescale: Optional 1-D float array of per-entry multiplicative factors
|
|
110
117
|
(one per row). ``1.0`` leaves the value unchanged. Stored as a
|
|
111
118
|
``rescale_array`` resource (``kind="rescale"``).
|
|
119
|
+
reference: Optional 1-D boolean sequence of length ``n_entries``.
|
|
120
|
+
Where True, that entry is the reference (production) exchange for
|
|
121
|
+
its column. Stored as a ``reference_array`` resource
|
|
122
|
+
(``kind="reference"``) only when at least one entry is flagged.
|
|
112
123
|
"""
|
|
113
124
|
|
|
114
125
|
rows: np.ndarray
|
|
@@ -116,6 +127,7 @@ class ArrayEntry:
|
|
|
116
127
|
data: np.ndarray
|
|
117
128
|
flip: Optional[np.ndarray] = None
|
|
118
129
|
rescale: Optional[np.ndarray] = None
|
|
130
|
+
reference: Optional[np.ndarray] = None
|
|
119
131
|
|
|
120
132
|
def __post_init__(self):
|
|
121
133
|
self.rows = np.asarray(self.rows)
|
|
@@ -150,6 +162,12 @@ class ArrayEntry:
|
|
|
150
162
|
raise ValueError(
|
|
151
163
|
f"`rescale` shape {self.rescale.shape} doesn't match `rows` shape {self.rows.shape}"
|
|
152
164
|
)
|
|
165
|
+
if self.reference is not None:
|
|
166
|
+
self.reference = np.asarray(self.reference, dtype=bool)
|
|
167
|
+
if self.reference.shape != self.rows.shape:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"`reference` shape {self.reference.shape} doesn't match `rows` shape {self.rows.shape}"
|
|
170
|
+
)
|
|
153
171
|
|
|
154
172
|
|
|
155
173
|
def create_datapackage_from_entries(
|
|
@@ -53,7 +53,7 @@ def add_resource_suffix(metadata: dict, suffix: str) -> dict:
|
|
|
53
53
|
last = metadata["name"].split(".")[-1]
|
|
54
54
|
rest = metadata["name"][: -len(last) - 1]
|
|
55
55
|
|
|
56
|
-
if last not in {"indices", "data", "distributions", "flip"}:
|
|
56
|
+
if last not in {"indices", "data", "distributions", "flip", "rescale", "reference"}:
|
|
57
57
|
raise ValueError("Can't understand resource name suffix")
|
|
58
58
|
|
|
59
59
|
rest = metadata["name"][: -len(last) - 1]
|
|
@@ -65,6 +65,7 @@ def dictionary_formatter(row: dict) -> tuple:
|
|
|
65
65
|
row.get("negative", False),
|
|
66
66
|
row.get("flip", False),
|
|
67
67
|
row.get("rescale", 1.0),
|
|
68
|
+
row.get("reference", False),
|
|
68
69
|
)
|
|
69
70
|
|
|
70
71
|
|
|
@@ -74,13 +75,18 @@ def resolve_dict_iterator(iterator: Any, nrows: int = None) -> tuple:
|
|
|
74
75
|
data = (dictionary_formatter(row) for row in iterator)
|
|
75
76
|
array = create_structured_array(
|
|
76
77
|
data,
|
|
77
|
-
INDICES_DTYPE
|
|
78
|
+
INDICES_DTYPE
|
|
79
|
+
+ [("amount", np.float32)]
|
|
80
|
+
+ UNCERTAINTY_DTYPE
|
|
81
|
+
+ [("flip", bool), ("rescale", np.float32), ("reference", bool)],
|
|
78
82
|
nrows=nrows,
|
|
79
83
|
sort=True,
|
|
80
84
|
sort_fields=sort_fields,
|
|
81
85
|
)
|
|
82
86
|
rescale = array["rescale"]
|
|
83
87
|
rescale_array = rescale if (rescale != 1.0).any() else None
|
|
88
|
+
reference = array["reference"]
|
|
89
|
+
reference_array = reference if reference.any() else None
|
|
84
90
|
return (
|
|
85
91
|
array["amount"],
|
|
86
92
|
# Not repacking fields would cause this multi-field index to return a view
|
|
@@ -102,6 +108,7 @@ def resolve_dict_iterator(iterator: Any, nrows: int = None) -> tuple:
|
|
|
102
108
|
),
|
|
103
109
|
array["flip"],
|
|
104
110
|
rescale_array,
|
|
111
|
+
reference_array,
|
|
105
112
|
)
|
|
106
113
|
|
|
107
114
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bw_processing
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6
|
|
4
4
|
Summary: Foo
|
|
5
5
|
Author-email: Chris Mutel <cmutel@gmail.com>
|
|
6
6
|
Maintainer-email: Chris Mutel <cmutel@gmail.com>
|
|
@@ -211,12 +211,12 @@ print(data_obj.url)
|
|
|
211
211
|
|
|
212
212
|
### Scale arrays
|
|
213
213
|
|
|
214
|
-
Any resource group (persistent or dynamic, vector or array) can carry an optional `
|
|
214
|
+
Any resource group (persistent or dynamic, vector or array) can carry an optional `rescale_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.
|
|
215
215
|
|
|
216
216
|
Typical use cases:
|
|
217
217
|
|
|
218
|
-
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `
|
|
219
|
-
* **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 `
|
|
218
|
+
* **Allocation factors** — when a process produces multiple products, the exchange amounts must be partitioned between them. Storing the allocation coefficients as a `rescale_array` keeps them alongside the data they modify without requiring a separate processing step.
|
|
219
|
+
* **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 `rescale_array` rather than baked into every data value.
|
|
220
220
|
|
|
221
221
|
```python
|
|
222
222
|
import numpy as np
|
|
@@ -226,18 +226,45 @@ from bw_processing.constants import INDICES_DTYPE
|
|
|
226
226
|
dp = create_datapackage()
|
|
227
227
|
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
228
228
|
data_array = np.array([100.0, 200.0, 300.0])
|
|
229
|
-
|
|
229
|
+
rescale_array = np.array([0.6, 1.0, 0.4]) # e.g. allocation factors
|
|
230
230
|
|
|
231
231
|
dp.add_persistent_vector(
|
|
232
232
|
matrix="technosphere",
|
|
233
233
|
name="my-process",
|
|
234
234
|
indices_array=indices_array,
|
|
235
235
|
data_array=data_array,
|
|
236
|
-
|
|
236
|
+
rescale_array=rescale_array,
|
|
237
237
|
)
|
|
238
238
|
```
|
|
239
239
|
|
|
240
|
-
The stored resource has `kind="
|
|
240
|
+
The stored resource has `kind="rescale"` and can be retrieved via `dp.get_resource("my-process.rescale")`. The `rescale_array` must be a float dtype (`float32` or `float64`); passing an integer array raises `WrongDatatype`.
|
|
241
|
+
|
|
242
|
+
### Reference (production) exchanges
|
|
243
|
+
|
|
244
|
+
Any resource group can also carry an optional `reference_array`: a one-dimensional boolean array of the same length as `indices_array`. Where an element is `True`, that exchange is the **reference (production) exchange** for its activity/column.
|
|
245
|
+
|
|
246
|
+
The five structural heuristics in `bw_graph_tools` (matching ids, single non-flipped entry, single positive, single negative, unique product) cannot always identify the reference exchange — whenever an activity has more than one same-sign exchange and the products also appear in other columns, the choice is genuinely ambiguous. Only the modeller knows the answer. `reference_array` records it directly so downstream tools can read it instead of guessing.
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
import numpy as np
|
|
250
|
+
from bw_processing import create_datapackage
|
|
251
|
+
from bw_processing.constants import INDICES_DTYPE
|
|
252
|
+
|
|
253
|
+
dp = create_datapackage()
|
|
254
|
+
indices_array = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
255
|
+
data_array = np.array([1.0, 0.5, 2.0])
|
|
256
|
+
reference_array = np.array([True, False, False]) # first exchange is the reference
|
|
257
|
+
|
|
258
|
+
dp.add_persistent_vector(
|
|
259
|
+
matrix="technosphere",
|
|
260
|
+
name="my-process",
|
|
261
|
+
indices_array=indices_array,
|
|
262
|
+
data_array=data_array,
|
|
263
|
+
reference_array=reference_array,
|
|
264
|
+
)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The stored resource has `kind="reference"` and can be retrieved via `dp.get_resource("my-process.reference")`. It must be a boolean array; passing a non-boolean array raises `WrongDatatype`. To keep the common case cheap, the resource is written only when at least one entry is `True` — a group with no reference flags carries no `reference` resource. Using the high-level `MatrixEntry`/`ArrayEntry` API, set `reference=True` (or a boolean `reference` array) on the entries you want flagged.
|
|
241
268
|
|
|
242
269
|
### Parameter arrays for sensitivity analysis
|
|
243
270
|
|
|
@@ -947,3 +947,101 @@ def test_zipfile_compressed_roundtrip(tmp_path):
|
|
|
947
947
|
dp = load_datapackage(ZipFileSystem(str(zip_path)))
|
|
948
948
|
data, _ = dp.get_resource("sa-data-vector.data")
|
|
949
949
|
assert np.allclose(data, [2, 7, 12])
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
REFERENCE_INDICES = np.array([(1, 4), (2, 5), (3, 6)], dtype=INDICES_DTYPE)
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
def test_reference_array_stored():
|
|
956
|
+
dp = create_datapackage()
|
|
957
|
+
dp.add_persistent_vector(
|
|
958
|
+
matrix="sa_matrix",
|
|
959
|
+
data_array=np.array([2, 7, 12]),
|
|
960
|
+
name="sa-ref",
|
|
961
|
+
indices_array=REFERENCE_INDICES,
|
|
962
|
+
reference_array=np.array([True, False, False], dtype=bool),
|
|
963
|
+
)
|
|
964
|
+
data, _ = dp.get_resource("sa-ref.reference")
|
|
965
|
+
assert data.dtype == bool
|
|
966
|
+
assert list(data) == [True, False, False]
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
def test_reference_array_not_stored_when_all_false():
|
|
970
|
+
dp = create_datapackage()
|
|
971
|
+
dp.add_persistent_vector(
|
|
972
|
+
matrix="sa_matrix",
|
|
973
|
+
data_array=np.array([2, 7, 12]),
|
|
974
|
+
name="sa-ref",
|
|
975
|
+
indices_array=REFERENCE_INDICES,
|
|
976
|
+
reference_array=np.array([False, False, False], dtype=bool),
|
|
977
|
+
)
|
|
978
|
+
assert "sa-ref.reference" not in [r["name"] for r in dp.resources]
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
def test_reference_array_wrong_dtype():
|
|
982
|
+
dp = create_datapackage()
|
|
983
|
+
with pytest.raises(WrongDatatype):
|
|
984
|
+
dp.add_persistent_vector(
|
|
985
|
+
matrix="sa_matrix",
|
|
986
|
+
data_array=np.array([2, 7, 12]),
|
|
987
|
+
name="sa-ref",
|
|
988
|
+
indices_array=REFERENCE_INDICES,
|
|
989
|
+
reference_array=np.array([1, 0, 0], dtype=int),
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
def test_reference_array_shape_mismatch():
|
|
994
|
+
dp = create_datapackage()
|
|
995
|
+
with pytest.raises(ShapeMismatch):
|
|
996
|
+
dp.add_persistent_vector(
|
|
997
|
+
matrix="sa_matrix",
|
|
998
|
+
data_array=np.array([2, 7, 12]),
|
|
999
|
+
name="sa-ref",
|
|
1000
|
+
indices_array=REFERENCE_INDICES,
|
|
1001
|
+
reference_array=np.array([True, False], dtype=bool),
|
|
1002
|
+
)
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
def test_reference_array_persistent_array():
|
|
1006
|
+
dp = create_datapackage()
|
|
1007
|
+
dp.add_persistent_array(
|
|
1008
|
+
matrix="sa_matrix",
|
|
1009
|
+
data_array=np.arange(12).reshape((3, 4)),
|
|
1010
|
+
name="sa-ref-arr",
|
|
1011
|
+
indices_array=REFERENCE_INDICES,
|
|
1012
|
+
reference_array=np.array([False, True, False], dtype=bool),
|
|
1013
|
+
)
|
|
1014
|
+
data, _ = dp.get_resource("sa-ref-arr.reference")
|
|
1015
|
+
assert list(data) == [False, True, False]
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
def test_reference_array_dynamic_vector():
|
|
1019
|
+
dp = create_datapackage()
|
|
1020
|
+
dp.add_dynamic_vector(
|
|
1021
|
+
interface=Dummy(),
|
|
1022
|
+
matrix="sa_matrix",
|
|
1023
|
+
name="sa-ref-dyn",
|
|
1024
|
+
indices_array=REFERENCE_INDICES,
|
|
1025
|
+
reference_array=np.array([False, False, True], dtype=bool),
|
|
1026
|
+
)
|
|
1027
|
+
data, _ = dp.get_resource("sa-ref-dyn.reference")
|
|
1028
|
+
assert list(data) == [False, False, True]
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
def test_reference_array_roundtrip(tmp_path):
|
|
1032
|
+
fs = generic_zipfile_filesystem(dirpath=tmp_path, filename="ref.zip")
|
|
1033
|
+
dp = create_datapackage(fs=fs, name="test-reference")
|
|
1034
|
+
dp.add_persistent_vector(
|
|
1035
|
+
matrix="sa_matrix",
|
|
1036
|
+
data_array=np.array([2, 7, 12]),
|
|
1037
|
+
name="sa-ref",
|
|
1038
|
+
indices_array=REFERENCE_INDICES,
|
|
1039
|
+
reference_array=np.array([True, False, True], dtype=bool),
|
|
1040
|
+
)
|
|
1041
|
+
dp.finalize_serialization()
|
|
1042
|
+
|
|
1043
|
+
loaded = load_datapackage(ZipFileSystem(str(tmp_path / "ref.zip")))
|
|
1044
|
+
data, resource = loaded.get_resource("sa-ref.reference")
|
|
1045
|
+
assert resource["kind"] == "reference"
|
|
1046
|
+
assert data.dtype == bool
|
|
1047
|
+
assert list(data) == [True, False, True]
|
|
@@ -59,6 +59,18 @@ class TestMatrixEntry:
|
|
|
59
59
|
e = MatrixEntry(row=1, col=2, amount=3.0, rescale=2.0)
|
|
60
60
|
assert e.as_dict()["rescale"] == pytest.approx(2.0)
|
|
61
61
|
|
|
62
|
+
def test_reference_default_is_false(self):
|
|
63
|
+
e = MatrixEntry(row=1, col=2, amount=3.0)
|
|
64
|
+
assert e.reference is False
|
|
65
|
+
|
|
66
|
+
def test_reference_custom_value(self):
|
|
67
|
+
e = MatrixEntry(row=1, col=2, amount=3.0, reference=True)
|
|
68
|
+
assert e.reference is True
|
|
69
|
+
|
|
70
|
+
def test_reference_in_as_dict(self):
|
|
71
|
+
e = MatrixEntry(row=1, col=2, amount=3.0, reference=True)
|
|
72
|
+
assert e.as_dict()["reference"] is True
|
|
73
|
+
|
|
62
74
|
def test_loc_set_to_amount_for_no_uncertainty(self):
|
|
63
75
|
e = MatrixEntry(row=1, col=2, amount=5.0)
|
|
64
76
|
assert e.loc == pytest.approx(5.0)
|
|
@@ -90,7 +102,7 @@ class TestMatrixEntry:
|
|
|
90
102
|
assert set(d.keys()) == {
|
|
91
103
|
"row", "col", "amount", "flip", "uncertainty_type",
|
|
92
104
|
"loc", "scale", "shape", "minimum", "maximum", "negative",
|
|
93
|
-
"rescale",
|
|
105
|
+
"rescale", "reference",
|
|
94
106
|
}
|
|
95
107
|
|
|
96
108
|
def test_as_dict_values(self):
|
|
@@ -251,6 +263,19 @@ class TestArrayEntry:
|
|
|
251
263
|
with pytest.raises(ValueError, match="rescale.*rows"):
|
|
252
264
|
ArrayEntry(rows=[0, 1], cols=[2, 3], data=np.ones((2, 4)), rescale=[1.0, 2.0, 3.0])
|
|
253
265
|
|
|
266
|
+
def test_reference_default_is_none(self):
|
|
267
|
+
e = ArrayEntry(rows=[0, 1], cols=[2, 3], data=np.ones((2, 4)))
|
|
268
|
+
assert e.reference is None
|
|
269
|
+
|
|
270
|
+
def test_reference_coerced_to_bool(self):
|
|
271
|
+
e = ArrayEntry(rows=[0, 1], cols=[2, 3], data=np.ones((2, 4)), reference=[1, 0])
|
|
272
|
+
assert e.reference.dtype == bool
|
|
273
|
+
assert list(e.reference) == [True, False]
|
|
274
|
+
|
|
275
|
+
def test_reference_shape_mismatch(self):
|
|
276
|
+
with pytest.raises(ValueError, match="reference.*rows"):
|
|
277
|
+
ArrayEntry(rows=[0, 1], cols=[2, 3], data=np.ones((2, 4)), reference=[True, False, True])
|
|
278
|
+
|
|
254
279
|
|
|
255
280
|
class TestAddArrayEntries:
|
|
256
281
|
def test_single_entry(self):
|
|
@@ -325,6 +350,32 @@ class TestAddArrayEntries:
|
|
|
325
350
|
stored = dp.data[dp.resources.index(rescale_resource)]
|
|
326
351
|
np.testing.assert_array_almost_equal(stored, [2.0, 0.5])
|
|
327
352
|
|
|
353
|
+
def test_reference_stored_correctly(self):
|
|
354
|
+
dp = create_datapackage()
|
|
355
|
+
entry = ArrayEntry(rows=[0, 1], cols=[2, 3], data=np.ones((2, 3)), reference=[True, False])
|
|
356
|
+
dp.add_array_entries(matrix="technosphere_matrix", entries=[entry])
|
|
357
|
+
group = next(iter(dp.groups.values()))
|
|
358
|
+
reference_resource = next(r for r in group.resources if r["kind"] == "reference")
|
|
359
|
+
stored = dp.data[dp.resources.index(reference_resource)]
|
|
360
|
+
assert stored.dtype == bool
|
|
361
|
+
assert list(stored) == [True, False]
|
|
362
|
+
|
|
363
|
+
def test_no_reference_resource_when_reference_is_none(self):
|
|
364
|
+
dp = create_datapackage()
|
|
365
|
+
entry = ArrayEntry(rows=[0, 1], cols=[2, 3], data=np.ones((2, 3)))
|
|
366
|
+
dp.add_array_entries(matrix="technosphere_matrix", entries=[entry])
|
|
367
|
+
group = next(iter(dp.groups.values()))
|
|
368
|
+
kinds = [r["kind"] for r in group.resources]
|
|
369
|
+
assert "reference" not in kinds
|
|
370
|
+
|
|
371
|
+
def test_no_reference_resource_when_all_false(self):
|
|
372
|
+
dp = create_datapackage()
|
|
373
|
+
entry = ArrayEntry(rows=[0, 1], cols=[2, 3], data=np.ones((2, 3)), reference=[False, False])
|
|
374
|
+
dp.add_array_entries(matrix="technosphere_matrix", entries=[entry])
|
|
375
|
+
group = next(iter(dp.groups.values()))
|
|
376
|
+
kinds = [r["kind"] for r in group.resources]
|
|
377
|
+
assert "reference" not in kinds
|
|
378
|
+
|
|
328
379
|
|
|
329
380
|
class TestAddEntries:
|
|
330
381
|
def test_no_rescale_resource_when_all_rescale_one(self):
|
|
@@ -388,6 +439,49 @@ class TestAddEntries:
|
|
|
388
439
|
else:
|
|
389
440
|
assert rescales[i] == pytest.approx(2.0)
|
|
390
441
|
|
|
442
|
+
def test_no_reference_resource_when_no_references(self):
|
|
443
|
+
dp = create_datapackage()
|
|
444
|
+
entries = [
|
|
445
|
+
MatrixEntry(row=1, col=2, amount=1.0),
|
|
446
|
+
MatrixEntry(row=3, col=4, amount=2.0),
|
|
447
|
+
]
|
|
448
|
+
dp.add_entries(matrix="technosphere_matrix", entries=entries)
|
|
449
|
+
group = next(iter(dp.groups.values()))
|
|
450
|
+
kinds = [r["kind"] for r in group.resources]
|
|
451
|
+
assert "reference" not in kinds
|
|
452
|
+
|
|
453
|
+
def test_reference_resource_stored_when_set(self):
|
|
454
|
+
dp = create_datapackage()
|
|
455
|
+
entries = [
|
|
456
|
+
MatrixEntry(row=1, col=2, amount=1.0, reference=True),
|
|
457
|
+
MatrixEntry(row=3, col=4, amount=2.0, reference=True),
|
|
458
|
+
]
|
|
459
|
+
dp.add_entries(matrix="technosphere_matrix", entries=entries)
|
|
460
|
+
group = next(iter(dp.groups.values()))
|
|
461
|
+
reference_resource = next(r for r in group.resources if r["kind"] == "reference")
|
|
462
|
+
stored = dp.data[dp.resources.index(reference_resource)]
|
|
463
|
+
assert stored.dtype == bool
|
|
464
|
+
assert list(stored) == [True, True]
|
|
465
|
+
|
|
466
|
+
def test_reference_aligned_with_data_when_sorted(self):
|
|
467
|
+
# add_entries sorts by (row, col); reference flags must follow the same order
|
|
468
|
+
dp = create_datapackage()
|
|
469
|
+
entries = [
|
|
470
|
+
MatrixEntry(row=3, col=4, amount=2.0, reference=False),
|
|
471
|
+
MatrixEntry(row=1, col=2, amount=1.0, reference=True),
|
|
472
|
+
]
|
|
473
|
+
dp.add_entries(matrix="technosphere_matrix", entries=entries)
|
|
474
|
+
group = next(iter(dp.groups.values()))
|
|
475
|
+
idx_resource = next(r for r in group.resources if r["kind"] == "indices")
|
|
476
|
+
reference_resource = next(r for r in group.resources if r["kind"] == "reference")
|
|
477
|
+
indices = dp.data[dp.resources.index(idx_resource)]
|
|
478
|
+
references = dp.data[dp.resources.index(reference_resource)]
|
|
479
|
+
for i, idx in enumerate(indices):
|
|
480
|
+
if idx["row"] == 1:
|
|
481
|
+
assert references[i] == np.bool_(True)
|
|
482
|
+
else:
|
|
483
|
+
assert references[i] == np.bool_(False)
|
|
484
|
+
|
|
391
485
|
|
|
392
486
|
class TestSimpleGraphDeprecation:
|
|
393
487
|
def test_deprecation_warning(self):
|
|
@@ -37,7 +37,7 @@ def test_dictionary_formatter_sparse():
|
|
|
37
37
|
result = dictionary_formatter(given)
|
|
38
38
|
assert result[:5] == (1, 1, 4, 0, 4)
|
|
39
39
|
assert all(np.isnan(x) for x in result[5:9])
|
|
40
|
-
assert result[9:] == (False, False, 1.0)
|
|
40
|
+
assert result[9:] == (False, False, 1.0, False)
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def test_dictionary_formatter_uncertainty_type():
|
|
@@ -68,7 +68,7 @@ def test_dictionary_formatter_complete():
|
|
|
68
68
|
"negative": True,
|
|
69
69
|
"flip": False,
|
|
70
70
|
}
|
|
71
|
-
expected = (1, 2, 3, 4, 5, 6, 7, 8, 9, True, False, 1.0)
|
|
71
|
+
expected = (1, 2, 3, 4, 5, 6, 7, 8, 9, True, False, 1.0, False)
|
|
72
72
|
assert dictionary_formatter(given) == expected
|
|
73
73
|
|
|
74
74
|
|
|
@@ -85,20 +85,32 @@ def test_dictionary_formatter_one_dimensional():
|
|
|
85
85
|
"negative": True,
|
|
86
86
|
"flip": False,
|
|
87
87
|
}
|
|
88
|
-
expected = (1, 1, 3, 4, 5, 6, 7, 8, 9, True, False, 1.0)
|
|
88
|
+
expected = (1, 1, 3, 4, 5, 6, 7, 8, 9, True, False, 1.0, False)
|
|
89
89
|
assert dictionary_formatter(given) == expected
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def test_dictionary_formatter_rescale():
|
|
93
93
|
given = {"row": 1, "col": 2, "amount": 3.0, "rescale": 0.5}
|
|
94
94
|
result = dictionary_formatter(given)
|
|
95
|
-
assert result[-
|
|
95
|
+
assert result[-2] == 0.5
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def test_dictionary_formatter_rescale_default():
|
|
99
99
|
given = {"row": 1, "amount": 4}
|
|
100
100
|
result = dictionary_formatter(given)
|
|
101
|
-
assert result[-
|
|
101
|
+
assert result[-2] == 1.0
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_dictionary_formatter_reference():
|
|
105
|
+
given = {"row": 1, "col": 2, "amount": 3.0, "reference": True}
|
|
106
|
+
result = dictionary_formatter(given)
|
|
107
|
+
assert result[-1] is True
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_dictionary_formatter_reference_default():
|
|
111
|
+
given = {"row": 1, "amount": 4}
|
|
112
|
+
result = dictionary_formatter(given)
|
|
113
|
+
assert result[-1] is False
|
|
102
114
|
|
|
103
115
|
|
|
104
116
|
def test_check_suffix():
|
|
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
|
|
File without changes
|