matrix-utils 0.7.1__tar.gz → 0.9__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.
- {matrix_utils-0.7.1 → matrix_utils-0.9}/CHANGELOG.md +13 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/PKG-INFO +15 -6
- {matrix_utils-0.7.1 → matrix_utils-0.9}/README.md +13 -4
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/__init__.py +1 -1
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/mapped_matrix.py +129 -1
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/resource_group.py +99 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils.egg-info/PKG-INFO +15 -6
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils.egg-info/requires.txt +1 -1
- {matrix_utils-0.7.1 → matrix_utils-0.9}/pyproject.toml +1 -1
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/mapped_matrix.py +531 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/monte_carlo.py +2 -2
- {matrix_utils-0.7.1 → matrix_utils-0.9}/.gitignore +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/.pre-commit-config.yaml +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/CODE_OF_CONDUCT.md +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/LICENSE +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/MANIFEST.in +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/binder/Brightway 2.5 demonstration.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/binder/environment.yml +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Brightway 2.5 - Linking with IAMs.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Brightway 2.5 demonstration.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Graph traversal with modification.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Indexing speed tests.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Interfaces which produce results for multiple matrices.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Mapping speed tests.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Multifunctionality with Brightway 2.5.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Real-time modification of hybrid databases.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Supply chain modification.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Untitled.ipynb +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/bw2.prof +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/bw2.svg +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/bw25.prof +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/bw25.svg +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/bw25_profile.txt +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/bw25_profiling.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/bw2_profile.txt +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/bw2_profiling.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/profiling/profiling-versus-bw2.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/speed_test_unique_indices.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/dev/speed_tests.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/docs/Makefile +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/docs/conf.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/docs/index.rst +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/docs/loading.rst +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/docs/make.bat +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/docs/saving.rst +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/docs/utilities.rst +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/aggregation.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/array_mapper.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/errors.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/indexers.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/mapped_matrix_dict.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils/utils.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils.egg-info/SOURCES.txt +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils.egg-info/dependency_links.txt +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/matrix_utils.egg-info/top_level.txt +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/setup.cfg +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/array_mapper.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/fixtures/__init__.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/fixtures/a-first.zip +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/fixtures/b-second.zip +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/fixtures/basic.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/fixtures/create_static_fixtures.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/fixtures/sa-1.zip +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/fixtures/sa-2.zip +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/indexers.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/integration.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/interfaces.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/masking.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/ordering.py +0 -0
- {matrix_utils-0.7.1 → matrix_utils-0.9}/tests/utils.py +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9] - 2026-07-12
|
|
4
|
+
|
|
5
|
+
* Merge [#45](https://github.com/brightway-lca/matrix_utils/pull/45): Expose reference (production) exchange arrays from [bw_processing #108](https://github.com/brightway-lca/bw_processing/pull/108). New `ResourceGroup` properties `has_reference`, `reference`, and `reference_current`. Requires `bw_processing>=1.6`.
|
|
6
|
+
|
|
7
|
+
## [0.8] - 2026-06-04
|
|
8
|
+
|
|
9
|
+
* Merge [#42](https://github.com/brightway-lca/matrix_utils/pull/42): Rename `scale_array` to `rescale_array`, tracking [bw_processing #104](https://github.com/brightway-lca/bw_processing/pull/104). Adds `ResourceGroup.rescale_current` and `MappedMatrix.input_rescale_vector()`.
|
|
10
|
+
* Merge [#41](https://github.com/brightway-lca/matrix_utils/pull/41): Add `params_array` support from [bw_processing #93](https://github.com/brightway-lca/bw_processing/pull/93). New `ResourceGroup` properties `has_params`, `params_current`, `has_param_labels`, and `param_labels`; new `MappedMatrix.input_params()` method.
|
|
11
|
+
* Merge [#40](https://github.com/brightway-lca/matrix_utils/pull/40): Fix `input_uncertainties` crashing when an array group has no flip array (closes [#8](https://github.com/brightway-lca/matrix_utils/issues/8))
|
|
12
|
+
* Merge [#38](https://github.com/brightway-lca/matrix_utils/pull/38): `NaN` values in data vectors/arrays are skipped during matrix insertion, preserving earlier package values
|
|
13
|
+
* Merge [#37](https://github.com/brightway-lca/matrix_utils/pull/37): Add data access convenience methods (`input_raw_indices`, `input_flip_vector`, `input_provenance`, `n_elements_dropped`, `group`)
|
|
14
|
+
* Merge [#36](https://github.com/brightway-lca/matrix_utils/pull/36): Improve access to indexer values (`indexers`, `local_indexers`, `indexers_by_type`, `indexers_are_unique`)
|
|
15
|
+
|
|
3
16
|
### [0.7.1] - 2026-03-20
|
|
4
17
|
|
|
5
18
|
* Fix all elements being masked return sparse matrix instead of array when indexing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matrix_utils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9
|
|
4
4
|
Summary: Tools to create matrices from data packages
|
|
5
5
|
Author-email: Chris Mutel <cmutel@gmail.com>
|
|
6
6
|
Maintainer-email: Chris Mutel <cmutel@gmail.com>
|
|
@@ -28,7 +28,7 @@ License-File: LICENSE
|
|
|
28
28
|
Requires-Dist: numpy
|
|
29
29
|
Requires-Dist: scipy>=1.10
|
|
30
30
|
Requires-Dist: pandas
|
|
31
|
-
Requires-Dist: bw_processing>=1.
|
|
31
|
+
Requires-Dist: bw_processing>=1.6
|
|
32
32
|
Requires-Dist: stats_arrays
|
|
33
33
|
Provides-Extra: testing
|
|
34
34
|
Requires-Dist: matrix_utils; extra == "testing"
|
|
@@ -106,21 +106,30 @@ Out[3]:
|
|
|
106
106
|
|
|
107
107
|
`MappedMatrix` is iterable; calling `next()` will draw new samples from all included stochastic resources, and rebuild the matrix.
|
|
108
108
|
|
|
109
|
+
#### Indexers
|
|
110
|
+
|
|
111
|
+
Each datapackage is assigned exactly one indexer, shared across all its resource groups. This indexer determines which column of an array resource is used on each iteration. The design assumption is one indexer per datapackage — `MappedMatrix.indexers` returns a `{name: indexer}` dict at that level. `MappedMatrix.local_indexers` returns a `{group_label: indexer}` dict for what each resource group is actually using, which will normally be the same object (or a `Proxy` wrapping it for combinatorial packages). You may replace `group.indexer` on any individual group after construction, but you are then responsible for the consequences — in particular, `next()` only advances the package-level indexers, so any custom group-level indexer must be advanced manually.
|
|
112
|
+
|
|
113
|
+
#### NaN as a sentinel value
|
|
114
|
+
|
|
115
|
+
A `NaN` value in a data vector or array is treated as **"no data insertion"** — that element is skipped when the matrix is built or rebuilt, leaving the matrix cell at its current value. This is useful for scenario or override packages: place `NaN` at positions you want to inherit from an earlier package, and a real number where you want to override. Non-NaN elements behave normally. `ResourceGroup.data_current` still carries the raw `NaN` so `input_data_vector()` reflects the actual datapackage contents.
|
|
116
|
+
|
|
109
117
|
You may also find it useful to iterate through `MappedMatrix.groups`, which are instances of `ResourceGroup`, documented below.
|
|
110
118
|
|
|
111
119
|
### `ResourceGroup` class
|
|
112
120
|
|
|
113
|
-
A `bw_processing` data package is essentially a metadata file and a bag of data resources. These resources are *grouped*, for multiple resources are needed to build one matrix, or one component of one matrix. For example, one needs not only the data vector, but also the row and column indices to build a simple matrix. One could also have a `flip` vector
|
|
121
|
+
A `bw_processing` data package is essentially a metadata file and a bag of data resources. These resources are *grouped*, for multiple resources are needed to build one matrix, or one component of one matrix. For example, one needs not only the data vector, but also the row and column indices to build a simple matrix. One could also have a `flip` vector (to negate signs before insertion) or a `scale` vector (to apply a per-exchange multiplicative factor before insertion, e.g. for allocation or unit conversion).
|
|
114
122
|
|
|
115
123
|
The `ResourceGroup` class provides a single interface to these data files and their metadata. `ResourceGroup` instances are created automatically by `MappedMatrix`, and available via `MappedMatrix.groups`. The [source code](https://github.com/brightway-lca/matrix_utils/) is pretty readable, and in general you probably don't need to worry about this low-level class, but the following could be useful:
|
|
116
124
|
|
|
117
125
|
* `ResourceGroup.data_original`: The data as it is present in the datapackage, before masking (i.e. the Numpy data vector or array, or the data interface). This is the raw input data, duplicate elements are not aggregated (if applicable).
|
|
118
|
-
* `ResourceGroup.data_current`: The data sample used (before aggregation) to build the matrix. It is
|
|
126
|
+
* `ResourceGroup.data_current`: The data sample used (before aggregation) to build the matrix. It is masked, flipped (if a flip array is present), and scaled (if a scale array is present).
|
|
127
|
+
* `ResourceGroup.scale`: The scale array with all masks applied. Raises `KeyError` if no scale array is present.
|
|
119
128
|
* `ResourceGroup.row|col_mapped`: Mapped row and column indices. Has the same length as the datapackage resource, but uses `-1` for values which weren't mapped.
|
|
120
129
|
* `ResourceGroup.row|col_masked`: The data after the custom filter and mapping mask have been applied.
|
|
121
|
-
* `
|
|
130
|
+
* `ResourceGroup.row|col_matrix`: Row and column indices (but not data) for insertion into the matrix. These indices are after aggregation within the resource group (if any).
|
|
122
131
|
* `ResourceGroup.calculate(vector=None)`: Function to recalculate matrix row, column, and data vectors. Uses the current state of the indexers, but re-draws values from data iterators. If `vector` is given, use this instead of the given data source.
|
|
123
|
-
* `ResourceGroup.indexer`: The instance of the `Indexer` class applicable for this `ResourceGroup`. Only used for data arrays.
|
|
132
|
+
* `ResourceGroup.indexer`: The instance of the `Indexer` class applicable for this `ResourceGroup`. Only used for data arrays. By design each datapackage has one indexer shared across all its resource groups; this is set up automatically by `MappedMatrix`. You may assign a different indexer to individual groups after construction, but you are then responsible for keeping them consistent (e.g. ensuring they advance together when `next()` is called).
|
|
124
133
|
* `ResourceGroup.ncols`: The integer number of columns in a data array. Returns `None` if a data vector is present.
|
|
125
134
|
|
|
126
135
|
## Contributing
|
|
@@ -59,21 +59,30 @@ Out[3]:
|
|
|
59
59
|
|
|
60
60
|
`MappedMatrix` is iterable; calling `next()` will draw new samples from all included stochastic resources, and rebuild the matrix.
|
|
61
61
|
|
|
62
|
+
#### Indexers
|
|
63
|
+
|
|
64
|
+
Each datapackage is assigned exactly one indexer, shared across all its resource groups. This indexer determines which column of an array resource is used on each iteration. The design assumption is one indexer per datapackage — `MappedMatrix.indexers` returns a `{name: indexer}` dict at that level. `MappedMatrix.local_indexers` returns a `{group_label: indexer}` dict for what each resource group is actually using, which will normally be the same object (or a `Proxy` wrapping it for combinatorial packages). You may replace `group.indexer` on any individual group after construction, but you are then responsible for the consequences — in particular, `next()` only advances the package-level indexers, so any custom group-level indexer must be advanced manually.
|
|
65
|
+
|
|
66
|
+
#### NaN as a sentinel value
|
|
67
|
+
|
|
68
|
+
A `NaN` value in a data vector or array is treated as **"no data insertion"** — that element is skipped when the matrix is built or rebuilt, leaving the matrix cell at its current value. This is useful for scenario or override packages: place `NaN` at positions you want to inherit from an earlier package, and a real number where you want to override. Non-NaN elements behave normally. `ResourceGroup.data_current` still carries the raw `NaN` so `input_data_vector()` reflects the actual datapackage contents.
|
|
69
|
+
|
|
62
70
|
You may also find it useful to iterate through `MappedMatrix.groups`, which are instances of `ResourceGroup`, documented below.
|
|
63
71
|
|
|
64
72
|
### `ResourceGroup` class
|
|
65
73
|
|
|
66
|
-
A `bw_processing` data package is essentially a metadata file and a bag of data resources. These resources are *grouped*, for multiple resources are needed to build one matrix, or one component of one matrix. For example, one needs not only the data vector, but also the row and column indices to build a simple matrix. One could also have a `flip` vector
|
|
74
|
+
A `bw_processing` data package is essentially a metadata file and a bag of data resources. These resources are *grouped*, for multiple resources are needed to build one matrix, or one component of one matrix. For example, one needs not only the data vector, but also the row and column indices to build a simple matrix. One could also have a `flip` vector (to negate signs before insertion) or a `scale` vector (to apply a per-exchange multiplicative factor before insertion, e.g. for allocation or unit conversion).
|
|
67
75
|
|
|
68
76
|
The `ResourceGroup` class provides a single interface to these data files and their metadata. `ResourceGroup` instances are created automatically by `MappedMatrix`, and available via `MappedMatrix.groups`. The [source code](https://github.com/brightway-lca/matrix_utils/) is pretty readable, and in general you probably don't need to worry about this low-level class, but the following could be useful:
|
|
69
77
|
|
|
70
78
|
* `ResourceGroup.data_original`: The data as it is present in the datapackage, before masking (i.e. the Numpy data vector or array, or the data interface). This is the raw input data, duplicate elements are not aggregated (if applicable).
|
|
71
|
-
* `ResourceGroup.data_current`: The data sample used (before aggregation) to build the matrix. It is
|
|
79
|
+
* `ResourceGroup.data_current`: The data sample used (before aggregation) to build the matrix. It is masked, flipped (if a flip array is present), and scaled (if a scale array is present).
|
|
80
|
+
* `ResourceGroup.scale`: The scale array with all masks applied. Raises `KeyError` if no scale array is present.
|
|
72
81
|
* `ResourceGroup.row|col_mapped`: Mapped row and column indices. Has the same length as the datapackage resource, but uses `-1` for values which weren't mapped.
|
|
73
82
|
* `ResourceGroup.row|col_masked`: The data after the custom filter and mapping mask have been applied.
|
|
74
|
-
* `
|
|
83
|
+
* `ResourceGroup.row|col_matrix`: Row and column indices (but not data) for insertion into the matrix. These indices are after aggregation within the resource group (if any).
|
|
75
84
|
* `ResourceGroup.calculate(vector=None)`: Function to recalculate matrix row, column, and data vectors. Uses the current state of the indexers, but re-draws values from data iterators. If `vector` is given, use this instead of the given data source.
|
|
76
|
-
* `ResourceGroup.indexer`: The instance of the `Indexer` class applicable for this `ResourceGroup`. Only used for data arrays.
|
|
85
|
+
* `ResourceGroup.indexer`: The instance of the `Indexer` class applicable for this `ResourceGroup`. Only used for data arrays. By design each datapackage has one indexer shared across all its resource groups; this is set up automatically by `MappedMatrix`. You may assign a different indexer to individual groups after construction, but you are then responsible for keeping them consistent (e.g. ensuring they advance together when `next()` is called).
|
|
77
86
|
* `ResourceGroup.ncols`: The integer number of columns in a data array. Returns `None` if a data vector is present.
|
|
78
87
|
|
|
79
88
|
## Contributing
|
|
@@ -219,6 +219,10 @@ class MappedMatrix:
|
|
|
219
219
|
self.matrix.data *= 0
|
|
220
220
|
for group in self.groups:
|
|
221
221
|
row, col, data = group.calculate()
|
|
222
|
+
# NaN means "no data insertion" — skip those elements so earlier
|
|
223
|
+
# packages' values are preserved rather than overwritten with NaN.
|
|
224
|
+
nan_mask = ~np.isnan(data)
|
|
225
|
+
row, col, data = row[nan_mask], col[nan_mask], data[nan_mask]
|
|
222
226
|
if group.package.metadata["sum_inter_duplicates"]:
|
|
223
227
|
self.matrix[row, col] += data
|
|
224
228
|
else:
|
|
@@ -256,6 +260,60 @@ class MappedMatrix:
|
|
|
256
260
|
for obj in resources:
|
|
257
261
|
obj.add_indexer(indexer=package.indexer)
|
|
258
262
|
|
|
263
|
+
@property
|
|
264
|
+
def indexers(self) -> dict:
|
|
265
|
+
"""Return package-level indexers keyed by datapackage name.
|
|
266
|
+
|
|
267
|
+
By design each datapackage has one indexer shared across all its resource groups,
|
|
268
|
+
but this constraint is not enforced — individual groups can be given different
|
|
269
|
+
indexers after construction. Use ``local_indexers`` to inspect what each group
|
|
270
|
+
is actually using.
|
|
271
|
+
"""
|
|
272
|
+
return {
|
|
273
|
+
package.metadata["name"]: package.indexer
|
|
274
|
+
for package in self.packages
|
|
275
|
+
if hasattr(package, "indexer")
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def local_indexers(self) -> dict:
|
|
280
|
+
"""Return the indexer actually used by each resource group, keyed by group label.
|
|
281
|
+
|
|
282
|
+
Normally all groups within a package share the same indexer (or a ``Proxy``
|
|
283
|
+
wrapping it for combinatorial packages), but users can assign a different indexer
|
|
284
|
+
to any group after construction, so this may differ from ``indexers``.
|
|
285
|
+
"""
|
|
286
|
+
return {group.label: group.indexer for group in self.groups if hasattr(group, "indexer")}
|
|
287
|
+
|
|
288
|
+
def indexers_by_type(self, indexer_type: type) -> list:
|
|
289
|
+
"""Return all package-level indexers that are instances of ``indexer_type``."""
|
|
290
|
+
return [indexer for indexer in self.indexers.values() if isinstance(indexer, indexer_type)]
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def indexers_are_unique(self) -> bool:
|
|
294
|
+
"""True if no two packages share the same indexer instance."""
|
|
295
|
+
indexers = list(self.indexers.values())
|
|
296
|
+
return len({id(i) for i in indexers}) == len(indexers)
|
|
297
|
+
|
|
298
|
+
def group(self, label: str) -> "ResourceGroup":
|
|
299
|
+
"""Return the ResourceGroup with the given label.
|
|
300
|
+
|
|
301
|
+
Raises ``KeyError`` if no group with that label exists.
|
|
302
|
+
"""
|
|
303
|
+
for grp in self.groups:
|
|
304
|
+
if grp.label == label:
|
|
305
|
+
return grp
|
|
306
|
+
raise KeyError(label)
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def n_elements_dropped(self) -> int:
|
|
310
|
+
"""Total number of datapackage elements dropped across all resource groups.
|
|
311
|
+
|
|
312
|
+
An element is dropped when it is excluded by the custom filter or when its
|
|
313
|
+
row/column id could not be mapped to a matrix index.
|
|
314
|
+
"""
|
|
315
|
+
return sum(grp.n_elements_dropped for grp in self.groups)
|
|
316
|
+
|
|
259
317
|
def input_data_vector(self) -> np.ndarray:
|
|
260
318
|
return np.hstack([group.data_current for group in self.groups])
|
|
261
319
|
|
|
@@ -273,6 +331,52 @@ class MappedMatrix:
|
|
|
273
331
|
array["col"] = cols
|
|
274
332
|
return array
|
|
275
333
|
|
|
334
|
+
def input_raw_indices(self) -> np.ndarray:
|
|
335
|
+
"""Return original datapackage indices (before mapping), with masks applied.
|
|
336
|
+
|
|
337
|
+
The result has the same length and order as ``input_data_vector`` and
|
|
338
|
+
``input_row_col_indices``, but contains the raw source ids from the datapackage
|
|
339
|
+
rather than the translated matrix row/column positions. Useful for tracing which
|
|
340
|
+
original ids contributed to each matrix element.
|
|
341
|
+
"""
|
|
342
|
+
arrays = []
|
|
343
|
+
for grp in self.groups:
|
|
344
|
+
if grp.empty:
|
|
345
|
+
arrays.append(np.array([], dtype=INDICES_DTYPE))
|
|
346
|
+
else:
|
|
347
|
+
arrays.append(grp.apply_masks(grp.get_indices_data()))
|
|
348
|
+
return np.concatenate(arrays)
|
|
349
|
+
|
|
350
|
+
def input_rescale_vector(self) -> np.ndarray:
|
|
351
|
+
"""Return rescale factors for each element, aligned with ``input_data_vector``.
|
|
352
|
+
|
|
353
|
+
Groups without a rescale array contribute all-ones values.
|
|
354
|
+
"""
|
|
355
|
+
arrays = []
|
|
356
|
+
for grp in self.groups:
|
|
357
|
+
if grp.empty:
|
|
358
|
+
arrays.append(np.array([]))
|
|
359
|
+
else:
|
|
360
|
+
rc = grp.rescale_current
|
|
361
|
+
arrays.append(rc if rc is not None else np.ones(len(grp.data_current)))
|
|
362
|
+
return np.hstack(arrays)
|
|
363
|
+
|
|
364
|
+
def input_flip_vector(self) -> np.ndarray:
|
|
365
|
+
"""Return a boolean array indicating which elements were negated before matrix insertion.
|
|
366
|
+
|
|
367
|
+
Groups without a flip array contribute all-``False`` values. The result aligns
|
|
368
|
+
element-wise with ``input_data_vector`` and ``input_row_col_indices``.
|
|
369
|
+
"""
|
|
370
|
+
arrays = []
|
|
371
|
+
for grp in self.groups:
|
|
372
|
+
if grp.empty:
|
|
373
|
+
arrays.append(np.array([], dtype=bool))
|
|
374
|
+
elif grp.has_flip:
|
|
375
|
+
arrays.append(grp.flip)
|
|
376
|
+
else:
|
|
377
|
+
arrays.append(np.zeros(len(grp.data_current), dtype=bool))
|
|
378
|
+
return np.hstack(arrays)
|
|
379
|
+
|
|
276
380
|
def input_provenance(self) -> List[tuple]:
|
|
277
381
|
"""Describe where the data in the other ``input_X`` comes from. Returns a list
|
|
278
382
|
of ``(datapackage, group_label, (start_index, end_index))`` tuples.
|
|
@@ -293,6 +397,29 @@ class MappedMatrix:
|
|
|
293
397
|
position += num_elements
|
|
294
398
|
return result
|
|
295
399
|
|
|
400
|
+
def input_params(self) -> dict:
|
|
401
|
+
"""Return current parameter values keyed by ``(package, group_label)``.
|
|
402
|
+
|
|
403
|
+
Only groups that carry a params array are included; groups without
|
|
404
|
+
params are omitted entirely (not present vs. ``None``-valued).
|
|
405
|
+
Keying by ``(package, group_label)`` avoids silent collisions when
|
|
406
|
+
two packages contribute groups with the same label.
|
|
407
|
+
|
|
408
|
+
For vector groups the full 1-D params array is returned (parameters
|
|
409
|
+
are fixed and do not vary by iteration). For array groups the column
|
|
410
|
+
matching the current indexer position is returned, so the result
|
|
411
|
+
stays in sync with ``input_data_vector`` across Monte Carlo
|
|
412
|
+
iterations.
|
|
413
|
+
"""
|
|
414
|
+
result = {}
|
|
415
|
+
for package, groups in self.packages.items():
|
|
416
|
+
for group in groups:
|
|
417
|
+
try:
|
|
418
|
+
result[(package, group.label)] = group.params_current
|
|
419
|
+
except KeyError:
|
|
420
|
+
pass
|
|
421
|
+
return result
|
|
422
|
+
|
|
296
423
|
def input_indexer_vector(self) -> np.ndarray:
|
|
297
424
|
index_values = []
|
|
298
425
|
|
|
@@ -363,7 +490,8 @@ class MappedMatrix:
|
|
|
363
490
|
|
|
364
491
|
array = self._construct_distributions_array(group.data_current, uncertainty_type=98)
|
|
365
492
|
array["loc"] = np.mean(data, axis=1)
|
|
366
|
-
|
|
493
|
+
if group.has_flip:
|
|
494
|
+
array["loc"][group.flip] *= -1
|
|
367
495
|
array["scale"] = np.std(data, axis=1)
|
|
368
496
|
arrays.append(array)
|
|
369
497
|
elif group.is_interface():
|
|
@@ -119,6 +119,76 @@ class ResourceGroup:
|
|
|
119
119
|
except KeyError:
|
|
120
120
|
return False
|
|
121
121
|
|
|
122
|
+
@property
|
|
123
|
+
def has_flip(self) -> bool:
|
|
124
|
+
try:
|
|
125
|
+
self.get_resource_by_suffix("flip")
|
|
126
|
+
return True
|
|
127
|
+
except KeyError:
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def has_rescale(self) -> bool:
|
|
132
|
+
try:
|
|
133
|
+
self.get_resource_by_suffix("rescale")
|
|
134
|
+
return True
|
|
135
|
+
except KeyError:
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def has_reference(self) -> bool:
|
|
140
|
+
try:
|
|
141
|
+
self.get_resource_by_suffix("reference")
|
|
142
|
+
return True
|
|
143
|
+
except KeyError:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def has_params(self) -> bool:
|
|
148
|
+
try:
|
|
149
|
+
self.get_resource_by_suffix("params")
|
|
150
|
+
return True
|
|
151
|
+
except KeyError:
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def has_param_labels(self) -> bool:
|
|
156
|
+
try:
|
|
157
|
+
self.get_resource_by_suffix("param_labels")
|
|
158
|
+
return True
|
|
159
|
+
except KeyError:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def params_current(self) -> np.ndarray:
|
|
164
|
+
"""Current parameter values.
|
|
165
|
+
|
|
166
|
+
For vectors: returns the full 1-D params array (parameters are fixed
|
|
167
|
+
and do not vary by indexer position).
|
|
168
|
+
For arrays: returns the column at the current indexer position,
|
|
169
|
+
mirroring how ``calculate()`` picks the active data column.
|
|
170
|
+
|
|
171
|
+
Raises ``KeyError`` if no params array exists for this group.
|
|
172
|
+
"""
|
|
173
|
+
data = self.get_resource_by_suffix("params")
|
|
174
|
+
if self.vector:
|
|
175
|
+
return data
|
|
176
|
+
return data[:, self.indexer.index % data.shape[1]]
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def param_labels(self) -> dict:
|
|
180
|
+
"""Raw param-labels dict. Always has ``"values"``;
|
|
181
|
+
also has ``"schema"`` when a label schema was provided at write time.
|
|
182
|
+
|
|
183
|
+
Raises ``KeyError`` if no param_labels resource exists.
|
|
184
|
+
"""
|
|
185
|
+
return self.get_resource_by_suffix("param_labels")
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def n_elements_dropped(self) -> int:
|
|
189
|
+
"""Number of original datapackage elements dropped by the custom filter and mapping mask."""
|
|
190
|
+
return len(self.get_indices_data()) - len(self.row_masked)
|
|
191
|
+
|
|
122
192
|
@property
|
|
123
193
|
def data_original(self):
|
|
124
194
|
if self.use_distributions and self.has_distributions:
|
|
@@ -131,6 +201,30 @@ class ResourceGroup:
|
|
|
131
201
|
"""The flip array, with all masks applied (if necessary)."""
|
|
132
202
|
return self.apply_masks(self.get_resource_by_suffix("flip"))
|
|
133
203
|
|
|
204
|
+
@property
|
|
205
|
+
def rescale(self):
|
|
206
|
+
"""The rescale array, with all masks applied (if necessary)."""
|
|
207
|
+
return self.apply_masks(self.get_resource_by_suffix("rescale"))
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def rescale_current(self) -> Optional[np.ndarray]:
|
|
211
|
+
"""Current rescale values with masks applied, or ``None`` if no rescale array exists."""
|
|
212
|
+
if self.has_rescale:
|
|
213
|
+
return self.rescale
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def reference(self):
|
|
218
|
+
"""The reference (production) exchange boolean array, with masks applied (if necessary)."""
|
|
219
|
+
return self.apply_masks(self.get_resource_by_suffix("reference"))
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def reference_current(self) -> Optional[np.ndarray]:
|
|
223
|
+
"""Current reference flags with masks applied, or ``None`` if no reference array exists."""
|
|
224
|
+
if self.has_reference:
|
|
225
|
+
return self.reference
|
|
226
|
+
return None
|
|
227
|
+
|
|
134
228
|
def get_indices_data(self):
|
|
135
229
|
"""The source data for the indices array."""
|
|
136
230
|
indices = self.get_resource_by_suffix("indices")
|
|
@@ -299,6 +393,11 @@ class ResourceGroup:
|
|
|
299
393
|
# No flip array
|
|
300
394
|
pass
|
|
301
395
|
|
|
396
|
+
try:
|
|
397
|
+
data *= self.rescale
|
|
398
|
+
except KeyError:
|
|
399
|
+
pass
|
|
400
|
+
|
|
302
401
|
# Second copy because we want to store the data before aggregation
|
|
303
402
|
self.data_current = data.copy()
|
|
304
403
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matrix_utils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9
|
|
4
4
|
Summary: Tools to create matrices from data packages
|
|
5
5
|
Author-email: Chris Mutel <cmutel@gmail.com>
|
|
6
6
|
Maintainer-email: Chris Mutel <cmutel@gmail.com>
|
|
@@ -28,7 +28,7 @@ License-File: LICENSE
|
|
|
28
28
|
Requires-Dist: numpy
|
|
29
29
|
Requires-Dist: scipy>=1.10
|
|
30
30
|
Requires-Dist: pandas
|
|
31
|
-
Requires-Dist: bw_processing>=1.
|
|
31
|
+
Requires-Dist: bw_processing>=1.6
|
|
32
32
|
Requires-Dist: stats_arrays
|
|
33
33
|
Provides-Extra: testing
|
|
34
34
|
Requires-Dist: matrix_utils; extra == "testing"
|
|
@@ -106,21 +106,30 @@ Out[3]:
|
|
|
106
106
|
|
|
107
107
|
`MappedMatrix` is iterable; calling `next()` will draw new samples from all included stochastic resources, and rebuild the matrix.
|
|
108
108
|
|
|
109
|
+
#### Indexers
|
|
110
|
+
|
|
111
|
+
Each datapackage is assigned exactly one indexer, shared across all its resource groups. This indexer determines which column of an array resource is used on each iteration. The design assumption is one indexer per datapackage — `MappedMatrix.indexers` returns a `{name: indexer}` dict at that level. `MappedMatrix.local_indexers` returns a `{group_label: indexer}` dict for what each resource group is actually using, which will normally be the same object (or a `Proxy` wrapping it for combinatorial packages). You may replace `group.indexer` on any individual group after construction, but you are then responsible for the consequences — in particular, `next()` only advances the package-level indexers, so any custom group-level indexer must be advanced manually.
|
|
112
|
+
|
|
113
|
+
#### NaN as a sentinel value
|
|
114
|
+
|
|
115
|
+
A `NaN` value in a data vector or array is treated as **"no data insertion"** — that element is skipped when the matrix is built or rebuilt, leaving the matrix cell at its current value. This is useful for scenario or override packages: place `NaN` at positions you want to inherit from an earlier package, and a real number where you want to override. Non-NaN elements behave normally. `ResourceGroup.data_current` still carries the raw `NaN` so `input_data_vector()` reflects the actual datapackage contents.
|
|
116
|
+
|
|
109
117
|
You may also find it useful to iterate through `MappedMatrix.groups`, which are instances of `ResourceGroup`, documented below.
|
|
110
118
|
|
|
111
119
|
### `ResourceGroup` class
|
|
112
120
|
|
|
113
|
-
A `bw_processing` data package is essentially a metadata file and a bag of data resources. These resources are *grouped*, for multiple resources are needed to build one matrix, or one component of one matrix. For example, one needs not only the data vector, but also the row and column indices to build a simple matrix. One could also have a `flip` vector
|
|
121
|
+
A `bw_processing` data package is essentially a metadata file and a bag of data resources. These resources are *grouped*, for multiple resources are needed to build one matrix, or one component of one matrix. For example, one needs not only the data vector, but also the row and column indices to build a simple matrix. One could also have a `flip` vector (to negate signs before insertion) or a `scale` vector (to apply a per-exchange multiplicative factor before insertion, e.g. for allocation or unit conversion).
|
|
114
122
|
|
|
115
123
|
The `ResourceGroup` class provides a single interface to these data files and their metadata. `ResourceGroup` instances are created automatically by `MappedMatrix`, and available via `MappedMatrix.groups`. The [source code](https://github.com/brightway-lca/matrix_utils/) is pretty readable, and in general you probably don't need to worry about this low-level class, but the following could be useful:
|
|
116
124
|
|
|
117
125
|
* `ResourceGroup.data_original`: The data as it is present in the datapackage, before masking (i.e. the Numpy data vector or array, or the data interface). This is the raw input data, duplicate elements are not aggregated (if applicable).
|
|
118
|
-
* `ResourceGroup.data_current`: The data sample used (before aggregation) to build the matrix. It is
|
|
126
|
+
* `ResourceGroup.data_current`: The data sample used (before aggregation) to build the matrix. It is masked, flipped (if a flip array is present), and scaled (if a scale array is present).
|
|
127
|
+
* `ResourceGroup.scale`: The scale array with all masks applied. Raises `KeyError` if no scale array is present.
|
|
119
128
|
* `ResourceGroup.row|col_mapped`: Mapped row and column indices. Has the same length as the datapackage resource, but uses `-1` for values which weren't mapped.
|
|
120
129
|
* `ResourceGroup.row|col_masked`: The data after the custom filter and mapping mask have been applied.
|
|
121
|
-
* `
|
|
130
|
+
* `ResourceGroup.row|col_matrix`: Row and column indices (but not data) for insertion into the matrix. These indices are after aggregation within the resource group (if any).
|
|
122
131
|
* `ResourceGroup.calculate(vector=None)`: Function to recalculate matrix row, column, and data vectors. Uses the current state of the indexers, but re-draws values from data iterators. If `vector` is given, use this instead of the given data source.
|
|
123
|
-
* `ResourceGroup.indexer`: The instance of the `Indexer` class applicable for this `ResourceGroup`. Only used for data arrays.
|
|
132
|
+
* `ResourceGroup.indexer`: The instance of the `Indexer` class applicable for this `ResourceGroup`. Only used for data arrays. By design each datapackage has one indexer shared across all its resource groups; this is set up automatically by `MappedMatrix`. You may assign a different indexer to individual groups after construction, but you are then responsible for keeping them consistent (e.g. ensuring they advance together when `next()` is called).
|
|
124
133
|
* `ResourceGroup.ncols`: The integer number of columns in a data array. Returns `None` if a data vector is present.
|
|
125
134
|
|
|
126
135
|
## Contributing
|
|
@@ -9,6 +9,108 @@ from fsspec.implementations.zip import ZipFileSystem
|
|
|
9
9
|
from matrix_utils import ArrayMapper, MappedMatrix
|
|
10
10
|
from matrix_utils.errors import AllArraysEmpty, EmptyArray
|
|
11
11
|
|
|
12
|
+
# --- input_params ---
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _mm_with_params(**kwargs):
|
|
16
|
+
"""Build a MappedMatrix from the given datapackages."""
|
|
17
|
+
return MappedMatrix(
|
|
18
|
+
matrix="foo",
|
|
19
|
+
use_vectors=True,
|
|
20
|
+
use_arrays=True,
|
|
21
|
+
use_distributions=False,
|
|
22
|
+
**kwargs,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_input_params_empty_when_no_params():
|
|
27
|
+
mm = _mm_with_params(packages=[basic_mm()])
|
|
28
|
+
assert mm.input_params() == {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_input_params_vector():
|
|
32
|
+
dp = bwp.create_datapackage()
|
|
33
|
+
params = np.array([0.1, 0.9])
|
|
34
|
+
dp.add_persistent_vector(
|
|
35
|
+
matrix="foo",
|
|
36
|
+
name="v",
|
|
37
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
38
|
+
data_array=np.array([5.0, 7.0]),
|
|
39
|
+
params_array=params,
|
|
40
|
+
)
|
|
41
|
+
mm = _mm_with_params(packages=[dp])
|
|
42
|
+
result = mm.input_params()
|
|
43
|
+
pkg = list(mm.packages.keys())[0]
|
|
44
|
+
assert list(result.keys()) == [(pkg, "v")]
|
|
45
|
+
assert np.allclose(result[(pkg, "v")], params)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_input_params_only_groups_with_params_included():
|
|
49
|
+
dp = bwp.create_datapackage()
|
|
50
|
+
dp.add_persistent_vector(
|
|
51
|
+
matrix="foo",
|
|
52
|
+
name="with_params",
|
|
53
|
+
indices_array=np.array([(0, 0)], dtype=bwp.INDICES_DTYPE),
|
|
54
|
+
data_array=np.array([1.0]),
|
|
55
|
+
params_array=np.array([42.0]),
|
|
56
|
+
)
|
|
57
|
+
dp.add_persistent_vector(
|
|
58
|
+
matrix="foo",
|
|
59
|
+
name="no_params",
|
|
60
|
+
indices_array=np.array([(1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
61
|
+
data_array=np.array([2.0]),
|
|
62
|
+
)
|
|
63
|
+
mm = _mm_with_params(packages=[dp])
|
|
64
|
+
result = mm.input_params()
|
|
65
|
+
labels = {label for (_, label) in result}
|
|
66
|
+
assert labels == {"with_params"}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_input_params_array_tracks_indexer():
|
|
70
|
+
dp = bwp.create_datapackage(sequential=True)
|
|
71
|
+
data = np.array([[10.0, 11.0, 12.0], [20.0, 21.0, 22.0]])
|
|
72
|
+
params = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
|
|
73
|
+
dp.add_persistent_array(
|
|
74
|
+
matrix="foo",
|
|
75
|
+
name="a",
|
|
76
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
77
|
+
data_array=data,
|
|
78
|
+
params_array=params,
|
|
79
|
+
)
|
|
80
|
+
mm = _mm_with_params(packages=[dp])
|
|
81
|
+
pkg = list(mm.packages.keys())[0]
|
|
82
|
+
assert np.allclose(mm.input_params()[(pkg, "a")], [1.0, 4.0])
|
|
83
|
+
next(mm)
|
|
84
|
+
assert np.allclose(mm.input_params()[(pkg, "a")], [2.0, 5.0])
|
|
85
|
+
next(mm)
|
|
86
|
+
assert np.allclose(mm.input_params()[(pkg, "a")], [3.0, 6.0])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_input_params_duplicate_labels_across_packages():
|
|
90
|
+
dp1 = bwp.create_datapackage()
|
|
91
|
+
dp1.add_persistent_vector(
|
|
92
|
+
matrix="foo",
|
|
93
|
+
name="v",
|
|
94
|
+
indices_array=np.array([(0, 0)], dtype=bwp.INDICES_DTYPE),
|
|
95
|
+
data_array=np.array([1.0]),
|
|
96
|
+
params_array=np.array([0.1]),
|
|
97
|
+
)
|
|
98
|
+
dp2 = bwp.create_datapackage()
|
|
99
|
+
dp2.add_persistent_vector(
|
|
100
|
+
matrix="foo",
|
|
101
|
+
name="v",
|
|
102
|
+
indices_array=np.array([(1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
103
|
+
data_array=np.array([2.0]),
|
|
104
|
+
params_array=np.array([0.9]),
|
|
105
|
+
)
|
|
106
|
+
mm = _mm_with_params(packages=[dp1, dp2])
|
|
107
|
+
result = mm.input_params()
|
|
108
|
+
assert len(result) == 2
|
|
109
|
+
values = list(result.values())
|
|
110
|
+
assert any(np.allclose(v, [0.1]) for v in values)
|
|
111
|
+
assert any(np.allclose(v, [0.9]) for v in values)
|
|
112
|
+
|
|
113
|
+
|
|
12
114
|
dirpath = Path(__file__).parent.resolve() / "fixtures"
|
|
13
115
|
|
|
14
116
|
|
|
@@ -708,6 +810,29 @@ def test_input_uncertainties_no_distributions(sensitivity_dps):
|
|
|
708
810
|
assert np.allclose(ua[field], expected[field], equal_nan=True)
|
|
709
811
|
|
|
710
812
|
|
|
813
|
+
def test_input_uncertainties_array_group_without_flip():
|
|
814
|
+
"""Regression test for issue #8: array group without flip should not crash."""
|
|
815
|
+
dp = bwp.create_datapackage()
|
|
816
|
+
indices_array = np.array([(10, 10), (11, 11)], dtype=bwp.INDICES_DTYPE)
|
|
817
|
+
data_array = np.array([[7, 8], [8, 9], [9, 10]]).T
|
|
818
|
+
dp.add_persistent_array(
|
|
819
|
+
matrix="matrix",
|
|
820
|
+
data_array=data_array,
|
|
821
|
+
name="no_flip",
|
|
822
|
+
indices_array=indices_array,
|
|
823
|
+
)
|
|
824
|
+
mm = MappedMatrix(
|
|
825
|
+
packages=[dp],
|
|
826
|
+
matrix="matrix",
|
|
827
|
+
use_vectors=True,
|
|
828
|
+
use_arrays=True,
|
|
829
|
+
use_distributions=False,
|
|
830
|
+
)
|
|
831
|
+
ua = mm.input_uncertainties()
|
|
832
|
+
assert ua.shape == (2,)
|
|
833
|
+
assert np.allclose(ua["loc"], [8.0, 9.0])
|
|
834
|
+
|
|
835
|
+
|
|
711
836
|
def test_input_index_vector(sensitivity_dps):
|
|
712
837
|
dp = bwp.create_datapackage(combinatorial=True)
|
|
713
838
|
|
|
@@ -868,3 +993,409 @@ def test_input_indexer_vector_raises_on_unsupported_type():
|
|
|
868
993
|
|
|
869
994
|
with pytest.raises(ValueError, match="Can't understand indexer value"):
|
|
870
995
|
mm.input_indexer_vector()
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
def test_indexers_single_package():
|
|
999
|
+
from matrix_utils.indexers import RandomIndexer
|
|
1000
|
+
|
|
1001
|
+
dp = basic_mm(name="alpha")
|
|
1002
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1003
|
+
gi = mm.indexers
|
|
1004
|
+
assert list(gi.keys()) == ["alpha"]
|
|
1005
|
+
assert isinstance(gi["alpha"], RandomIndexer)
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
def test_indexers_multiple_packages():
|
|
1009
|
+
from matrix_utils.indexers import RandomIndexer
|
|
1010
|
+
|
|
1011
|
+
dp1 = basic_mm(name="pkg-one")
|
|
1012
|
+
dp2 = basic_mm(name="pkg-two")
|
|
1013
|
+
mm = MappedMatrix(packages=[dp1, dp2], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1014
|
+
gi = mm.indexers
|
|
1015
|
+
assert set(gi.keys()) == {"pkg-one", "pkg-two"}
|
|
1016
|
+
assert isinstance(gi["pkg-one"], RandomIndexer)
|
|
1017
|
+
assert isinstance(gi["pkg-two"], RandomIndexer)
|
|
1018
|
+
# each package gets its own indexer instance
|
|
1019
|
+
assert gi["pkg-one"] is not gi["pkg-two"]
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
def test_local_indexers_keyed_by_group_label():
|
|
1023
|
+
from matrix_utils.indexers import RandomIndexer
|
|
1024
|
+
|
|
1025
|
+
dp = basic_mm(name="alpha")
|
|
1026
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1027
|
+
li = mm.local_indexers
|
|
1028
|
+
# basic_mm adds two vector groups: "vector" and "vector2"
|
|
1029
|
+
assert set(li.keys()) == {"vector", "vector2"}
|
|
1030
|
+
# both groups share the same package-level indexer instance
|
|
1031
|
+
assert isinstance(li["vector"], RandomIndexer)
|
|
1032
|
+
assert li["vector"] is li["vector2"]
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
def test_local_indexers_combinatorial_are_proxies():
|
|
1036
|
+
from matrix_utils.indexers import CombinatorialIndexer, Proxy
|
|
1037
|
+
|
|
1038
|
+
dp = bwp.create_datapackage(combinatorial=True, name="combo")
|
|
1039
|
+
indices = np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE)
|
|
1040
|
+
dp.add_persistent_array(
|
|
1041
|
+
matrix="foo", name="g", indices_array=indices, data_array=np.array([[1, 2], [3, 4]]).T
|
|
1042
|
+
)
|
|
1043
|
+
dp.add_persistent_array(
|
|
1044
|
+
matrix="foo", name="h", indices_array=indices, data_array=np.array([[5, 6], [7, 8]]).T
|
|
1045
|
+
)
|
|
1046
|
+
|
|
1047
|
+
mm = MappedMatrix(packages=[dp], matrix="foo")
|
|
1048
|
+
li = mm.local_indexers
|
|
1049
|
+
assert set(li.keys()) == {"g", "h"}
|
|
1050
|
+
assert all(isinstance(v, Proxy) for v in li.values())
|
|
1051
|
+
# global indexer is the underlying CombinatorialIndexer
|
|
1052
|
+
assert isinstance(mm.indexers["combo"], CombinatorialIndexer)
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
def test_indexers_by_type():
|
|
1056
|
+
from matrix_utils.indexers import RandomIndexer, SequentialIndexer
|
|
1057
|
+
|
|
1058
|
+
dp_rand = basic_mm(name="rand")
|
|
1059
|
+
dp_seq = bwp.create_datapackage(sequential=True, name="seq")
|
|
1060
|
+
dp_seq.add_persistent_vector(
|
|
1061
|
+
matrix="foo",
|
|
1062
|
+
name="v",
|
|
1063
|
+
indices_array=np.array([(0, 0)], dtype=bwp.INDICES_DTYPE),
|
|
1064
|
+
data_array=np.array([1.0]),
|
|
1065
|
+
)
|
|
1066
|
+
mm = MappedMatrix(
|
|
1067
|
+
packages=[dp_rand, dp_seq], matrix="foo", use_arrays=False, use_distributions=False
|
|
1068
|
+
)
|
|
1069
|
+
|
|
1070
|
+
rand_indexers = mm.indexers_by_type(RandomIndexer)
|
|
1071
|
+
seq_indexers = mm.indexers_by_type(SequentialIndexer)
|
|
1072
|
+
assert len(rand_indexers) == 1
|
|
1073
|
+
assert isinstance(rand_indexers[0], RandomIndexer)
|
|
1074
|
+
assert len(seq_indexers) == 1
|
|
1075
|
+
assert isinstance(seq_indexers[0], SequentialIndexer)
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
def test_indexers_are_unique_true():
|
|
1079
|
+
dp1 = basic_mm(name="a")
|
|
1080
|
+
dp2 = basic_mm(name="b")
|
|
1081
|
+
mm = MappedMatrix(packages=[dp1, dp2], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1082
|
+
assert mm.indexers_are_unique is True
|
|
1083
|
+
|
|
1084
|
+
|
|
1085
|
+
def test_indexers_are_unique_false_with_override():
|
|
1086
|
+
from matrix_utils.indexers import SequentialIndexer
|
|
1087
|
+
|
|
1088
|
+
shared = SequentialIndexer()
|
|
1089
|
+
dp1 = basic_mm(name="a")
|
|
1090
|
+
dp2 = basic_mm(name="b")
|
|
1091
|
+
mm = MappedMatrix(
|
|
1092
|
+
packages=[dp1, dp2],
|
|
1093
|
+
matrix="foo",
|
|
1094
|
+
use_arrays=False,
|
|
1095
|
+
use_distributions=False,
|
|
1096
|
+
indexer_override=shared,
|
|
1097
|
+
)
|
|
1098
|
+
assert mm.indexers_are_unique is False
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
# ── group() ───────────────────────────────────────────────────────────────────
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
def test_group_lookup_found():
|
|
1105
|
+
mm = MappedMatrix(
|
|
1106
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1107
|
+
)
|
|
1108
|
+
grp = mm.group("vector")
|
|
1109
|
+
assert grp.label == "vector"
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
def test_group_lookup_not_found():
|
|
1113
|
+
mm = MappedMatrix(
|
|
1114
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1115
|
+
)
|
|
1116
|
+
with pytest.raises(KeyError):
|
|
1117
|
+
mm.group("nonexistent")
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
# ── has_flip / has_rescale ────────────────────────────────────────────────────
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
def test_has_flip_true():
|
|
1124
|
+
mm = MappedMatrix(
|
|
1125
|
+
packages=[diagonal()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1126
|
+
)
|
|
1127
|
+
assert mm.group("vector").has_flip is True
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
def test_has_flip_false():
|
|
1131
|
+
mm = MappedMatrix(
|
|
1132
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1133
|
+
)
|
|
1134
|
+
assert mm.group("vector").has_flip is False
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
def test_has_rescale_false():
|
|
1138
|
+
mm = MappedMatrix(
|
|
1139
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1140
|
+
)
|
|
1141
|
+
assert mm.group("vector").has_rescale is False
|
|
1142
|
+
|
|
1143
|
+
|
|
1144
|
+
def test_has_rescale_true():
|
|
1145
|
+
dp = bwp.create_datapackage()
|
|
1146
|
+
dp.add_persistent_vector(
|
|
1147
|
+
matrix="foo",
|
|
1148
|
+
name="sv",
|
|
1149
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
1150
|
+
data_array=np.array([1.0, 2.0]),
|
|
1151
|
+
rescale_array=np.array([0.5, 2.0]),
|
|
1152
|
+
)
|
|
1153
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1154
|
+
assert mm.group("sv").has_rescale is True
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
# ── has_reference / reference ──────────────────────────────────────────────────
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
def test_has_reference_false():
|
|
1161
|
+
mm = MappedMatrix(
|
|
1162
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1163
|
+
)
|
|
1164
|
+
assert mm.group("vector").has_reference is False
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
def test_has_reference_true():
|
|
1168
|
+
dp = bwp.create_datapackage()
|
|
1169
|
+
dp.add_persistent_vector(
|
|
1170
|
+
matrix="foo",
|
|
1171
|
+
name="sv",
|
|
1172
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
1173
|
+
data_array=np.array([1.0, 2.0]),
|
|
1174
|
+
reference_array=np.array([True, False], dtype=bool),
|
|
1175
|
+
)
|
|
1176
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1177
|
+
assert mm.group("sv").has_reference is True
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
def test_reference_values():
|
|
1181
|
+
dp = bwp.create_datapackage()
|
|
1182
|
+
dp.add_persistent_vector(
|
|
1183
|
+
matrix="foo",
|
|
1184
|
+
name="sv",
|
|
1185
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
1186
|
+
data_array=np.array([1.0, 2.0]),
|
|
1187
|
+
reference_array=np.array([True, False], dtype=bool),
|
|
1188
|
+
)
|
|
1189
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1190
|
+
group = mm.group("sv")
|
|
1191
|
+
assert group.reference.dtype == bool
|
|
1192
|
+
assert list(group.reference) == [True, False]
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
def test_reference_current_none_when_absent():
|
|
1196
|
+
mm = MappedMatrix(
|
|
1197
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1198
|
+
)
|
|
1199
|
+
assert mm.group("vector").reference_current is None
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
def test_reference_current_returns_masked_values():
|
|
1203
|
+
dp = bwp.create_datapackage()
|
|
1204
|
+
dp.add_persistent_vector(
|
|
1205
|
+
matrix="foo",
|
|
1206
|
+
name="sv",
|
|
1207
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
1208
|
+
data_array=np.array([1.0, 2.0]),
|
|
1209
|
+
reference_array=np.array([False, True], dtype=bool),
|
|
1210
|
+
)
|
|
1211
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1212
|
+
assert list(mm.group("sv").reference_current) == [False, True]
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
# ── n_elements_dropped ─────────────────────────────────────────────────────────────────
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
def test_n_elements_dropped_none():
|
|
1219
|
+
mm = MappedMatrix(
|
|
1220
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1221
|
+
)
|
|
1222
|
+
assert mm.n_elements_dropped == 0
|
|
1223
|
+
assert mm.group("vector").n_elements_dropped == 0
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
def test_n_elements_dropped_custom_filter():
|
|
1227
|
+
mm = MappedMatrix(
|
|
1228
|
+
packages=[basic_mm()],
|
|
1229
|
+
matrix="foo",
|
|
1230
|
+
use_arrays=False,
|
|
1231
|
+
use_distributions=False,
|
|
1232
|
+
custom_filter=lambda x: x["row"] < 5,
|
|
1233
|
+
)
|
|
1234
|
+
# basic_mm vector has rows [0, 2, 4, 8]; filter keeps [0, 2, 4], drops 8
|
|
1235
|
+
assert mm.group("vector").n_elements_dropped == 1
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
def test_n_elements_dropped_unmapped():
|
|
1239
|
+
# Build a matrix with a pre-existing row_mapper that excludes some ids
|
|
1240
|
+
dp = bwp.create_datapackage()
|
|
1241
|
+
dp.add_persistent_vector(
|
|
1242
|
+
matrix="foo",
|
|
1243
|
+
name="v",
|
|
1244
|
+
indices_array=np.array([(0, 0), (99, 0)], dtype=bwp.INDICES_DTYPE),
|
|
1245
|
+
data_array=np.array([1.0, 2.0]),
|
|
1246
|
+
)
|
|
1247
|
+
# row_mapper that only knows about id 0, not 99
|
|
1248
|
+
am = ArrayMapper(array=np.array([0]))
|
|
1249
|
+
mm = MappedMatrix(
|
|
1250
|
+
packages=[dp], matrix="foo", use_arrays=False, use_distributions=False, row_mapper=am
|
|
1251
|
+
)
|
|
1252
|
+
assert mm.group("v").n_elements_dropped == 1
|
|
1253
|
+
assert mm.n_elements_dropped == 1
|
|
1254
|
+
|
|
1255
|
+
|
|
1256
|
+
def test_n_elements_dropped_total():
|
|
1257
|
+
dp1 = bwp.create_datapackage()
|
|
1258
|
+
dp1.add_persistent_vector(
|
|
1259
|
+
matrix="foo",
|
|
1260
|
+
name="a",
|
|
1261
|
+
indices_array=np.array([(0, 0), (99, 0)], dtype=bwp.INDICES_DTYPE),
|
|
1262
|
+
data_array=np.array([1.0, 2.0]),
|
|
1263
|
+
)
|
|
1264
|
+
dp2 = bwp.create_datapackage()
|
|
1265
|
+
dp2.add_persistent_vector(
|
|
1266
|
+
matrix="foo",
|
|
1267
|
+
name="b",
|
|
1268
|
+
indices_array=np.array([(0, 0), (98, 0)], dtype=bwp.INDICES_DTYPE),
|
|
1269
|
+
data_array=np.array([3.0, 4.0]),
|
|
1270
|
+
)
|
|
1271
|
+
am = ArrayMapper(array=np.array([0]))
|
|
1272
|
+
mm = MappedMatrix(
|
|
1273
|
+
packages=[dp1, dp2], matrix="foo", use_arrays=False, use_distributions=False, row_mapper=am
|
|
1274
|
+
)
|
|
1275
|
+
assert mm.n_elements_dropped == 2
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
# ── input_raw_indices() ───────────────────────────────────────────────────────
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
def test_input_raw_indices_aligns_with_data_vector():
|
|
1282
|
+
mm = MappedMatrix(
|
|
1283
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1284
|
+
)
|
|
1285
|
+
raw = mm.input_raw_indices()
|
|
1286
|
+
data = mm.input_data_vector()
|
|
1287
|
+
assert len(raw) == len(data)
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
def test_input_raw_indices_contains_original_ids():
|
|
1291
|
+
dp = bwp.create_datapackage()
|
|
1292
|
+
dp.add_persistent_vector(
|
|
1293
|
+
matrix="foo",
|
|
1294
|
+
name="v",
|
|
1295
|
+
indices_array=np.array([(10, 20), (30, 40)], dtype=bwp.INDICES_DTYPE),
|
|
1296
|
+
data_array=np.array([1.0, 2.0]),
|
|
1297
|
+
)
|
|
1298
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1299
|
+
raw = mm.input_raw_indices()
|
|
1300
|
+
assert list(raw["row"]) == [10, 30]
|
|
1301
|
+
assert list(raw["col"]) == [20, 40]
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
def test_input_raw_indices_excludes_dropped():
|
|
1305
|
+
dp = bwp.create_datapackage()
|
|
1306
|
+
dp.add_persistent_vector(
|
|
1307
|
+
matrix="foo",
|
|
1308
|
+
name="v",
|
|
1309
|
+
indices_array=np.array([(0, 0), (99, 0)], dtype=bwp.INDICES_DTYPE),
|
|
1310
|
+
data_array=np.array([1.0, 2.0]),
|
|
1311
|
+
)
|
|
1312
|
+
am = ArrayMapper(array=np.array([0]))
|
|
1313
|
+
mm = MappedMatrix(
|
|
1314
|
+
packages=[dp], matrix="foo", use_arrays=False, use_distributions=False, row_mapper=am
|
|
1315
|
+
)
|
|
1316
|
+
raw = mm.input_raw_indices()
|
|
1317
|
+
# id 99 was unmapped, so only id 0 survives
|
|
1318
|
+
assert len(raw) == 1
|
|
1319
|
+
assert raw["row"][0] == 0
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
# ── input_flip_vector() ───────────────────────────────────────────────────────
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
def test_input_flip_vector_no_flip():
|
|
1326
|
+
mm = MappedMatrix(
|
|
1327
|
+
packages=[basic_mm()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1328
|
+
)
|
|
1329
|
+
flip = mm.input_flip_vector()
|
|
1330
|
+
assert flip.dtype == bool
|
|
1331
|
+
assert not flip.any()
|
|
1332
|
+
assert len(flip) == len(mm.input_data_vector())
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
def test_input_flip_vector_with_flip():
|
|
1336
|
+
mm = MappedMatrix(
|
|
1337
|
+
packages=[diagonal()], matrix="foo", use_arrays=False, use_distributions=False
|
|
1338
|
+
)
|
|
1339
|
+
flip = mm.input_flip_vector()
|
|
1340
|
+
# diagonal fixture: flip_array = [False, True, False, False]
|
|
1341
|
+
assert flip.dtype == bool
|
|
1342
|
+
assert flip.sum() == 1
|
|
1343
|
+
assert len(flip) == len(mm.input_data_vector())
|
|
1344
|
+
|
|
1345
|
+
|
|
1346
|
+
def test_nan_in_vector_skips_insertion():
|
|
1347
|
+
dp = bwp.create_datapackage()
|
|
1348
|
+
dp.add_persistent_vector(
|
|
1349
|
+
matrix="foo",
|
|
1350
|
+
name="v",
|
|
1351
|
+
indices_array=np.array([(0, 0), (1, 1), (2, 2)], dtype=bwp.INDICES_DTYPE),
|
|
1352
|
+
data_array=np.array([1.0, np.nan, 3.0]),
|
|
1353
|
+
)
|
|
1354
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_arrays=False, use_distributions=False)
|
|
1355
|
+
assert mm.matrix[0, 0] == 1.0
|
|
1356
|
+
assert mm.matrix[1, 1] == 0.0 # NaN skipped, stays zero
|
|
1357
|
+
assert mm.matrix[2, 2] == 3.0
|
|
1358
|
+
assert not np.isnan(mm.matrix.data).any()
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
def test_nan_in_array_skips_insertion():
|
|
1362
|
+
dp = bwp.create_datapackage(sequential=True)
|
|
1363
|
+
dp.add_persistent_array(
|
|
1364
|
+
matrix="foo",
|
|
1365
|
+
name="a",
|
|
1366
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
1367
|
+
data_array=np.array([[1.0, np.nan], [3.0, 4.0]]),
|
|
1368
|
+
)
|
|
1369
|
+
mm = MappedMatrix(packages=[dp], matrix="foo", use_distributions=False)
|
|
1370
|
+
# column 0: data = [1.0, 3.0] — both inserted
|
|
1371
|
+
assert mm.matrix[0, 0] == 1.0
|
|
1372
|
+
assert mm.matrix[1, 1] == 3.0
|
|
1373
|
+
next(mm)
|
|
1374
|
+
# column 1: data = [nan, 4.0] — first element skipped, stays zero
|
|
1375
|
+
assert mm.matrix[0, 0] == 0.0
|
|
1376
|
+
assert mm.matrix[1, 1] == 4.0
|
|
1377
|
+
assert not np.isnan(mm.matrix.data).any()
|
|
1378
|
+
|
|
1379
|
+
|
|
1380
|
+
def test_nan_preserves_earlier_package_value():
|
|
1381
|
+
# Package A sets values; package B uses NaN to leave them untouched.
|
|
1382
|
+
dp_base = bwp.create_datapackage()
|
|
1383
|
+
dp_base.add_persistent_vector(
|
|
1384
|
+
matrix="foo",
|
|
1385
|
+
name="base",
|
|
1386
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
1387
|
+
data_array=np.array([5.0, 7.0]),
|
|
1388
|
+
)
|
|
1389
|
+
dp_scenario = bwp.create_datapackage()
|
|
1390
|
+
dp_scenario.add_persistent_vector(
|
|
1391
|
+
matrix="foo",
|
|
1392
|
+
name="scenario",
|
|
1393
|
+
indices_array=np.array([(0, 0), (1, 1)], dtype=bwp.INDICES_DTYPE),
|
|
1394
|
+
data_array=np.array([np.nan, 99.0]),
|
|
1395
|
+
)
|
|
1396
|
+
mm = MappedMatrix(
|
|
1397
|
+
packages=[dp_base, dp_scenario], matrix="foo", use_arrays=False, use_distributions=False
|
|
1398
|
+
)
|
|
1399
|
+
assert mm.matrix[0, 0] == 5.0 # NaN in scenario → base value preserved
|
|
1400
|
+
assert mm.matrix[1, 1] == 99.0 # non-NaN in scenario → overrides base
|
|
1401
|
+
assert not np.isnan(mm.matrix.data).any()
|
|
@@ -111,8 +111,8 @@ def test_distributions_without_uncertainties():
|
|
|
111
111
|
row = mm.row_mapper.to_dict()
|
|
112
112
|
col = mm.col_mapper.to_dict()
|
|
113
113
|
|
|
114
|
-
assert
|
|
115
|
-
assert
|
|
114
|
+
assert 20 <= np.mean(results[row[10], col[20], :]) <= 180
|
|
115
|
+
assert 5 <= np.mean(results[row[11], col[21], :]) <= 18
|
|
116
116
|
|
|
117
117
|
assert np.allclose(results[row[10], col[10], :], 11)
|
|
118
118
|
assert np.allclose(results[row[18], col[7], :], 125)
|
|
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
|
{matrix_utils-0.7.1 → matrix_utils-0.9}/dev/Real-time modification of hybrid databases.ipynb
RENAMED
|
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
|