pyforestry.base.imputation package#
Submodules#
pyforestry.base.imputation.height module#
Height imputation from a fitted height-diameter curve.
A thin adapter over the existing height machinery in
pyforestry.base.helpers.height_models: the curve, its fit and its
linearising transform all live there and are unchanged. This module only teaches
them the Imputer contract, so an
imputed height records that Naslund’s curve produced it.
- Source:
Naslund, M. (1936). Skogsforsoksanstaltens gallringsforsok i tallskog. Meddelanden fran Statens skogsforsoksanstalt 29(1), 1-169.
- class pyforestry.base.imputation.height.NaslundHeightImputer(spec: str | Callable[[float], float | None] | HeightSource | NaslundHeightCurve = 'naslund', naslund_exponent: int | float | str = 2, _source: HeightSource | None = None)[source]#
Bases:
objectImpute
height_mfrom a height-diameter curve fitted to the stand.The curve is fitted from the measured
(diameter_cm, height_m)pairs of the trees it is given, so this imputer must befit()before use –impute()does that for you.- Variables:
spec (str | Callable[[float], float | None] | pyforestry.base.helpers.height_models.HeightSource | pyforestry.base.helpers.height_models.NaslundHeightCurve) – What to fit.
"naslund"fits from the stand’s own pairs; a callablef(diameter_cm) -> height_mor an already-fittedNaslundHeightCurveis used directly.naslund_exponent (int | float | str) – Exponent for the fit, or
"auto"to fit it too.
- attribute: str = 'height_m'#
- property component_id: str#
Stable identifier for this imputer.
- fit(trees: Sequence[Any], context: Mapping[str, Any]) NaslundHeightImputer | None[source]#
Fit the curve from
trees.- Parameters:
trees – Trees whose measured height-diameter pairs the curve is fitted from. Ignored when
specis already a curve or a callable.context – Unused; present for the
Imputercontract.
- Returns:
A bound copy ready to impute, or
Noneif the curve could not be fitted – too few usable pairs, a single diameter, or a fit implying a non-monotone curve.- Raises:
ValueError – If
specresolves to measured heights, which cannot impute anything.
- impute(tree: Any, context: Mapping[str, Any]) float | None[source]#
Return the curve height for
tree, orNonewithout a diameter.- Parameters:
tree – The tree to impute for; needs
diameter_cm.context – Unused; present for the
Imputercontract.
- Returns:
The modelled height in metres, or
None.- Raises:
RuntimeError – If the imputer has not been fitted.
- naslund_exponent: int | float | str = 2#
- property source: SourceReference#
Näslund (1936), the curve this imputer evaluates.
- spec: str | Callable[[float], float | None] | HeightSource | NaslundHeightCurve = 'naslund'#
pyforestry.base.imputation.imputer module#
The imputer contract, and a wrapper for ad-hoc callables.
An imputer produces one tree attribute from whatever the tree already carries.
Like every other model in this package it is Describable: it declares a
component_id and a SourceReference, so the value it produces can be
traced back to a publication.
Some imputers are fitted from the stand they are applied to – the Naslund
height curve is fitted from the stand’s own measured height-diameter pairs –
which is what Imputer.fit() is for. Stateless imputers return themselves.
- class pyforestry.base.imputation.imputer.CallableImputer(attribute: str, function: Callable[[Any], float | None], label: str = 'caller-supplied callable')[source]#
Bases:
objectWrap a plain callable so ad-hoc imputers carry provenance too.
The value it produces is recorded as uncited, which is the honest label: a lambda has no publication. That keeps caller-supplied values distinguishable from ones a published model produced.
- Variables:
attribute (str) – The attribute produced, e.g.
"crown_radius_m".function (Callable[[Any], float | None]) –
f(tree) -> value | None.label (str) – Short description used as the citation title and, slugified, as the
component_id.
- property component_id: str#
Identifier derived from the attribute and label.
- fit(trees: Sequence[Any], context: Mapping[str, Any]) CallableImputer[source]#
Return
self; a callable needs no fitting.
- label: str = 'caller-supplied callable'#
- property source: SourceReference#
The not-applicable citation; a callable has no publication.
- class pyforestry.base.imputation.imputer.Imputer(*args, **kwargs)[source]#
Bases:
ProtocolProduces one tree attribute from the rest of the tree record.
- property attribute: str#
Name of the tree attribute produced, e.g.
"height_m".
- property component_id: str#
Stable identifier for this imputer.
- fit(trees: Sequence[Any], context: Mapping[str, Any]) Imputer | None[source]#
Bind to a set of trees, returning an imputer ready to use.
- Parameters:
trees – Every tree the imputer may be applied to.
context – Stand-level values an imputer may need.
- Returns:
An imputer bound to
trees– oftenselffor a stateless model – orNoneif it could not be fitted from what is available.
- impute(tree: Any, context: Mapping[str, Any]) float | None[source]#
Return a value for
tree, orNoneif it cannot be produced.
- property source: SourceReference#
Bibliographic provenance for whatever this imputer computes.
- pyforestry.base.imputation.imputer.UNCITED = '(none)'#
Author string marking a value that traces to no publication. Matches the sentinel used by
retained_trees, the Swedish mortality engine andbase.competition.
- pyforestry.base.imputation.imputer.uncited_source(label: str) SourceReference[source]#
Build the not-applicable citation for an imputer with no publication.
- Parameters:
label – Short description of what the callable does.
- Returns:
A
SourceReferencewith author"(none)"and year 0 – the sentinel for “not applicable”, not a citation date.
pyforestry.base.imputation.registry module#
Which imputers are available for which attribute.
Keeps the mapping from an attribute name to the imputers that can produce it, so
stand.impute("height_m") finds one without the caller naming a class. New
imputers register themselves here; a caller can always bypass the registry by
passing an imputer instance or a plain callable.
- pyforestry.base.imputation.registry.available_attributes() List[str][source]#
Return every attribute with at least one registered imputer.
- pyforestry.base.imputation.registry.imputers_for(attribute: str) List[str][source]#
Return the registered imputer names for
attribute.
- pyforestry.base.imputation.registry.register_imputer(attribute: str, name: str, factory: Callable[[], Imputer], *, default: bool = False, rejects: Dict[str, str] | None = None) None[source]#
Register an imputer factory under an attribute.
- Parameters:
attribute – The attribute produced, e.g.
"height_m".name – Short name callers pass to
stand.impute, e.g."naslund".factory – Zero-argument callable returning a fresh imputer.
default – Whether this becomes the attribute’s default, used when the caller asks for the attribute without naming an imputer. The first imputer registered for an attribute becomes its default whether or not this is set, so a lone registration never leaves the attribute without one.
rejects – Names that are recognised but invalid for imputation, mapped to the reason. Requesting one raises
ValueErrorwith that reason rather than an unhelpful “unknown name”.
- pyforestry.base.imputation.registry.resolve_imputer(attribute: str, spec: str | Imputer | Callable[[Any], float | None] | None = None) Imputer[source]#
Turn a user-facing spec into an imputer.
- Parameters:
attribute – The attribute to impute.
spec – A registered name, an imputer instance, a bare callable, or
None/"auto"for the attribute’s default.
- Returns:
An imputer for
attribute.- Raises:
KeyError – If no imputer is registered for the attribute, or the named one is unknown.
ValueError – If an imputer instance produces a different attribute, or the name is recognised but cannot impute (e.g.
"measured").
Module contents#
Filling in tree attributes a record does not carry, with provenance.
Models keep needing attributes a Tree was
not measured for: the influence-zone competition indices need
crown_radius_m, Marklund (1988) T12/T15/T16 need crown_base_height_m,
and height is missing on most inventory trees. This package supplies one
mechanism for all of them.
The rule is that plain attributes are measurements. A modelled value never
overwrites one; it goes in Tree.imputed as an
ImputedValue carrying the imputer
that produced it and that imputer’s citation, and
value_of() resolves the two:
>>> stand.impute("height_m") # Naslund curve, fitted to the stand
>>> tree.height_m # None -- never measured
>>> tree.value_of("height_m") # 13.28
>>> tree.provenance("height_m") # 'imputed'
>>> tree.imputed_source("height_m").author # 'Näslund, M.'
Attributes with no published model are supplied by the caller, and are recorded as uncited rather than silently unattributed:
>>> stand.impute("crown_radius_m", lambda t: 0.15 * t.diameter_cm)
>>> tree.imputed["crown_radius_m"].is_cited # False
This package holds no science of its own: it is plumbing plus one adapter over
the Näslund curve that already lives in
pyforestry.base.helpers.height_models.