Skip to main content

SPA Class

The main class for Structural Path Analysis.

Constructor

SPA(
A: ArrayLike,
intensities: Union[ArrayLike, Dict[str, ArrayLike], SatelliteWithConcordance],
sectors: Optional[Sequence[str]] = None,
mode: Literal["sector_specific", "system_wide"] = "sector_specific",
)

Parameters

ParameterTypeDescription
AArrayLikeTechnical coefficients matrix (n×n). Can be dense NumPy array or scipy sparse matrix.
intensitiesArrayLike or DictDirect intensities. Either a 1D array (single satellite) or dict mapping names to arrays.
sectorsSequence[str]Optional list of sector names for readable output.
modestrAnalysis mode: "sector_specific" (default) or "system_wide".

Example

from fastspa import SPA

spa = SPA(
A_matrix,
{"ghg": ghg_vector, "water": water_vector},
sectors=["Agriculture", "Mining", "Manufacturing"],
mode="system_wide",
)

Properties

PropertyTypeDescription
n_sectorsintNumber of sectors in the system
modestrAnalysis mode ("sector_specific" or "system_wide")
satellitesList[str]Names of available satellites
sectorsTuple[str, ...]Sector names (if provided)
ANDArrayTechnical coefficients matrix
LNDArrayLeontief inverse (computed lazily)

Methods

analyze()

Run structural path analysis for a target sector.

analyze(
sector: Union[int, str],
depth: int = 8,
*,
threshold: float = 0.001,
threshold_type: Literal["percentage", "absolute"] = "percentage",
satellite: Optional[str] = None,
max_paths: Optional[int] = None,
include_stage_0: bool = True,
) -> Union[PathCollection, SPAResult]

Parameters

ParameterTypeDefaultDescription
sectorint or str-Target sector (1-indexed integer or name)
depthint8Maximum supply chain depth to explore
thresholdfloat0.001Minimum contribution threshold
threshold_typestr"percentage"Type of threshold: "percentage" or "absolute"
satellitestrNoneSpecific satellite to analyze (None = all)
max_pathsintNoneMaximum number of paths to return
include_stage_0boolTrueInclude Stage 0 (direct requirements)

Returns

  • PathCollection if single satellite
  • SPAResult if multiple satellites

Example

# Basic analysis
paths = spa.analyze(sector=42, depth=8)

# By sector name
paths = spa.analyze(sector="Electricity", depth=8)

# With custom threshold
paths = spa.analyze(sector=42, threshold=0.0001, threshold_type="absolute")

# Limit results
paths = spa.analyze(sector=42, max_paths=100)

stream()

Stream paths as they are discovered (memory-efficient).

stream(
sector: Union[int, str],
depth: int = 8,
*,
threshold: float = 0.001,
threshold_type: Literal["percentage", "absolute"] = "percentage",
satellite: Optional[str] = None,
) -> Iterator[Path]

Returns paths in discovery order (not sorted by contribution).

analyze_many()

Analyze multiple sectors at once.

analyze_many(
sectors: Sequence[Union[int, str]],
depth: int = 8,
**kwargs,
) -> Dict[Union[int, str], Union[PathCollection, SPAResult]]

hotspots()

Identify emission hotspots (sectors contributing most to total).

hotspots(
sector: Union[int, str],
depth: int = 8,
*,
satellite: Optional[str] = None,
top_n: int = 10,
) -> List[Tuple[int, float, str]]

Returns list of (sector_index, contribution, sector_name) tuples.

compare_sectors()

Compare SPA results across multiple sectors.

compare_sectors(
sectors: Sequence[Union[int, str]],
depth: int = 8,
*,
satellite: Optional[str] = None,
) -> pd.DataFrame

Returns DataFrame with columns: sector, sector_name, total_intensity, num_paths, coverage, top_path_contribution.

total_intensity()

Get total intensities for a satellite.

total_intensity(satellite: Optional[str] = None) -> NDArray

Returns array of total intensities for each sector.

sensitivity()

Elasticities of a target sector's total intensity to each direct intensity input.

sensitivity(sector: Union[int, str], *, satellite: Optional[str] = None) -> Dict[int, float]

monte_carlo()

Monte Carlo (or Latin Hypercube) uncertainty propagation.

monte_carlo(
sector: Union[int, str],
depth: int = 8,
*,
n_samples: int = 200,
intensity_cv: Union[float, ArrayLike] = 0.1,
sampling: Literal["random", "lhs"] = "random",
distribution: Literal["lognormal", "normal"] = "lognormal",
seed: Optional[int] = None,
) -> MonteCarloResult

See: guides/uncertainty for interpretation and examples.

consequential()

Consequential (perturbation / policy scenario) analysis.

consequential(
sector: Union[int, str],
depth: int = 8,
*,
satellite: Optional[str] = None,
intensity_multiplier: Optional[ArrayLike] = None,
intensity_delta: Optional[ArrayLike] = None,
A_multiplier: Optional[ArrayLike] = None,
A_delta: Optional[ArrayLike] = None,
) -> ConsequentialResult

analyze_time_series()

Multi-year / dynamic SPA.

analyze_time_series(
series: Mapping[Any, Union[SPA, Tuple[ArrayLike, Any]]],
sector: Union[int, str],
depth: int = 8,
*,
satellite: Optional[str] = None,
) -> TemporalSPAResult