fips.Operator.from_blocks#

classmethod Operator.from_blocks(blocks, rows=None, cols=None, name=None)[source]#

Build an operator from its blocks, each named by its row and its column block.

A pair of blocks that is not given is zero. A labeled block is placed by its labels: it may cover part of its blocks’ labels (the rest is zero), as a transport Jacobian covers the cells a footprint touched. A Covariance may not (see Raises): its variances are the diagonal of the blocks’ own pairs, so each of those must cover its block’s labels.

Parameters:
  • blocks (Mapping[tuple[str, str], Any]) – Each (row_block, col_block) pair mapped to its matrix, labeled or not. Labeled: a DataFrame (dense or pandas-sparse) whose index and columns are the blocks’ own labels (no block level); a tuple (matrix, row_labels, col_labels) of an array or a SciPy sparse matrix and two pandas Indexes; or an Operator on a row space of the one block row_block and a column space of the one block col_block. Unlabeled: an array, a SciPy sparse matrix or a form of fips.linalg, whose rows and columns are in the order of the blocks’ labels in rows and cols. An array or a sparse matrix is copied, as for Operator.

  • rows (Space | None) – The labels of the rows and columns. By default each block’s labels are those of the labeled matrices in its row or column, which must then have the same labels (in any order); a matrix that covers part of a block is placed by giving the block’s labels here. An unlabeled matrix needs its blocks’ labels from here or from a labeled matrix in the same row or column block.

  • cols (Space | None) – The labels of the rows and columns. By default each block’s labels are those of the labeled matrices in its row or column, which must then have the same labels (in any order); a matrix that covers part of a block is placed by giving the block’s labels here. An unlabeled matrix needs its blocks’ labels from here or from a labeled matrix in the same row or column block.

  • name (str | None) – The operator’s name.

Returns:

The assembled matrix: sparse if every block is sparse (or diagonal), dense otherwise. A labeled block keeps its form when it covers its blocks’ labels (a pandas-sparse frame is sparse); a single block that covers both spaces keeps its form.

Return type:

Self

Raises:

ValueError – If a pair is given twice, a block’s labels are found nowhere, two labeled matrices of a block have different labels and rows or cols does not give them, a labeled matrix has a label its block does not have (the message names it), a labeled matrix is on other blocks than its key names, or an unlabeled matrix has the wrong shape. For a Covariance also: a block has no pair of its own (("flux", "flux")), or that pair lacks a label of the block on its rows or its columns. That label would get a variance of 0, so the message names it; give the pair a row and a column for every label.

Examples

>>> import numpy as np
>>> import pandas as pd
>>> from fips import Operator, Space
>>> obs = pd.Index(["o1", "o2"], name="obs_id")
>>> H = Operator.from_blocks(
...     {
...         ("co2", "flux"): pd.DataFrame(
...             [[1.0, 0.5], [0.0, 2.0]],
...             index=obs,
...             columns=pd.Index(["a", "b"], name="cell"),
...         ),
...         ("co2", "bias"): pd.DataFrame(
...             1.0, index=obs, columns=pd.Index(["A"], name="site")
...         ),
...     }
... )
>>> H.cols
Space(flux: 2 x ['cell'], bias: 1 x ['site'])
>>> H.dense()
array([[1. , 0.5, 1. ],
       [0. , 2. , 1. ]])

A Jacobian of the cells a footprint touched, placed on every cell:

>>> cells = Space({"flux": pd.Index(["a", "b", "c"], name="cell")})
>>> J = pd.DataFrame(
...     [[0.5], [0.2]], index=obs, columns=pd.Index(["c"], name="cell")
... )
>>> Operator.from_blocks({("co2", "flux"): J}, cols=cells).dense()
array([[0. , 0. , 0.5],
       [0. , 0. , 0.2]])