Skip to content

Tree Metrics

Compute TPA-weighted descriptive statistics at the condition or group level.

Overview

The tree_metrics() function calculates sample-level tree metrics such as quadratic mean diameter (QMD), mean height, and species composition. Unlike population-level estimators (volume(), tpa(), etc.), these are descriptive statistics that do not use expansion factors or variance estimation.

This is useful for characterizing stand structure, linking plot-level attributes to external models, or computing derived metrics not available in the standard FIA tables.

import pyfia

db = pyfia.FIA("georgia.duckdb")
db.clip_by_state("GA")

# QMD and mean height by forest type
result = pyfia.tree_metrics(db, metrics=["qmd", "mean_height"], grp_by="FORTYPCD")

Function Reference

tree_metrics

tree_metrics(db: 'FIA', metrics: list[str], grp_by: str | list[str] | None = None, land_type: str = 'forest', tree_type: str = 'live', tree_domain: str | None = None, area_domain: str | None = None, sawtimber_threshold: float = 9.0, include_cond_attrs: list[str] | None = None) -> DataFrame

Compute TPA-weighted tree metrics from FIA data.

Calculates derived per-condition or per-group tree metrics such as quadratic mean diameter (QMD), mean height, and species composition. These are sample-level descriptive statistics, not population-level estimates -- they do not use expansion factors or variance estimation.

PARAMETER DESCRIPTION
db

FIA database connection with EVALID set.

TYPE: FIA

metrics

Metrics to compute. Valid options:

  • "qmd": Quadratic mean diameter
  • "mean_dia": Arithmetic mean diameter (TPA-weighted)
  • "mean_height": Mean tree height (TPA-weighted)
  • "softwood_prop": Softwood proportion of biomass (SPCD < 300)
  • "sawtimber_prop": Proportion of TPA above sawtimber threshold
  • "max_dia": Maximum tree diameter
  • "stocking": Rough stocking index

TYPE: list of str

grp_by

Grouping columns. Supports standard FIA columns (FORTYPCD, STDAGE, etc.) and plot-condition level grouping (PLT_CN, CONDID).

TYPE: str or list of str DEFAULT: None

land_type

Land type filter: "forest", "timber", or "all".

TYPE: str DEFAULT: "forest"

tree_type

Tree status filter: "live", "dead", or "gs" (growing stock).

TYPE: str DEFAULT: "live"

tree_domain

SQL-like tree filter (e.g., "DIA >= 5.0").

TYPE: str DEFAULT: None

area_domain

SQL-like condition filter (e.g., "FORTYPCD IN (161, 162)").

TYPE: str DEFAULT: None

sawtimber_threshold

Diameter threshold for sawtimber_prop metric.

TYPE: float DEFAULT: 9.0

include_cond_attrs

COND table columns to pass through in the output (e.g., ["SLOPE", "SICOND", "ASPECT"]). Only useful when grouping by PLT_CN + CONDID.

TYPE: list of str DEFAULT: None

RETURNS DESCRIPTION
DataFrame

Metrics with one row per group. Columns include the requested metrics plus N_PLOTS and N_TREES counts.

Examples:

QMD and mean height by forest type:

>>> result = tree_metrics(db, metrics=["qmd", "mean_height"], grp_by="FORTYPCD")

Condition-level metrics for timber valuation:

>>> result = tree_metrics(
...     db,
...     metrics=["qmd", "mean_height", "softwood_prop", "sawtimber_prop"],
...     grp_by=["PLT_CN", "CONDID", "STDAGE", "FORTYPCD"],
...     land_type="timber",
...     tree_domain="DIA >= 1.0",
...     include_cond_attrs=["SLOPE", "SICOND"],
... )
Source code in src/pyfia/estimation/estimators/tree_metrics.py
def tree_metrics(
    db: "FIA",
    metrics: list[str],
    grp_by: str | list[str] | None = None,
    land_type: str = "forest",
    tree_type: str = "live",
    tree_domain: str | None = None,
    area_domain: str | None = None,
    sawtimber_threshold: float = 9.0,
    include_cond_attrs: list[str] | None = None,
) -> pl.DataFrame:
    """Compute TPA-weighted tree metrics from FIA data.

    Calculates derived per-condition or per-group tree metrics such as
    quadratic mean diameter (QMD), mean height, and species composition.
    These are sample-level descriptive statistics, not population-level
    estimates -- they do not use expansion factors or variance estimation.

    Parameters
    ----------
    db : FIA
        FIA database connection with EVALID set.
    metrics : list of str
        Metrics to compute. Valid options:

        - ``"qmd"``: Quadratic mean diameter
        - ``"mean_dia"``: Arithmetic mean diameter (TPA-weighted)
        - ``"mean_height"``: Mean tree height (TPA-weighted)
        - ``"softwood_prop"``: Softwood proportion of biomass (SPCD < 300)
        - ``"sawtimber_prop"``: Proportion of TPA above sawtimber threshold
        - ``"max_dia"``: Maximum tree diameter
        - ``"stocking"``: Rough stocking index
    grp_by : str or list of str, optional
        Grouping columns. Supports standard FIA columns (FORTYPCD, STDAGE,
        etc.) and plot-condition level grouping (PLT_CN, CONDID).
    land_type : str, default "forest"
        Land type filter: "forest", "timber", or "all".
    tree_type : str, default "live"
        Tree status filter: "live", "dead", or "gs" (growing stock).
    tree_domain : str, optional
        SQL-like tree filter (e.g., ``"DIA >= 5.0"``).
    area_domain : str, optional
        SQL-like condition filter (e.g., ``"FORTYPCD IN (161, 162)"``).
    sawtimber_threshold : float, default 9.0
        Diameter threshold for sawtimber_prop metric.
    include_cond_attrs : list of str, optional
        COND table columns to pass through in the output (e.g.,
        ``["SLOPE", "SICOND", "ASPECT"]``). Only useful when grouping
        by PLT_CN + CONDID.

    Returns
    -------
    pl.DataFrame
        Metrics with one row per group. Columns include the requested
        metrics plus N_PLOTS and N_TREES counts.

    Examples
    --------
    QMD and mean height by forest type:

    >>> result = tree_metrics(db, metrics=["qmd", "mean_height"], grp_by="FORTYPCD")

    Condition-level metrics for timber valuation:

    >>> result = tree_metrics(
    ...     db,
    ...     metrics=["qmd", "mean_height", "softwood_prop", "sawtimber_prop"],
    ...     grp_by=["PLT_CN", "CONDID", "STDAGE", "FORTYPCD"],
    ...     land_type="timber",
    ...     tree_domain="DIA >= 1.0",
    ...     include_cond_attrs=["SLOPE", "SICOND"],
    ... )
    """
    # Validate inputs
    invalid_metrics = set(metrics) - VALID_METRICS
    if invalid_metrics:
        raise ValueError(
            f"Invalid metric(s): {invalid_metrics}. "
            f"Valid metrics: {sorted(VALID_METRICS)}"
        )
    if not metrics:
        raise ValueError("At least one metric must be specified.")

    land_type = validate_land_type(land_type)
    if grp_by is not None:
        grp_by = validate_grp_by(grp_by)
    if tree_domain is not None:
        tree_domain = validate_domain_expression(tree_domain, "tree_domain")
    if area_domain is not None:
        area_domain = validate_domain_expression(area_domain, "area_domain")

    db, _owns_db = ensure_fia_instance(db)
    ensure_evalid_set(db, eval_type="VOL", estimator_name="tree_metrics")

    config = {
        "metrics": metrics,
        "grp_by": grp_by,
        "land_type": land_type,
        "tree_type": tree_type,
        "tree_domain": tree_domain,
        "area_domain": area_domain,
        "sawtimber_threshold": sawtimber_threshold,
        "include_cond_attrs": include_cond_attrs,
    }

    estimator = TreeMetricsEstimator(db, config)
    return estimator.estimate()

Available Metrics

Metric Output Column Description
"qmd" QMD Quadratic mean diameter (TPA-weighted)
"mean_dia" MEAN_DIA Arithmetic mean diameter (TPA-weighted)
"mean_height" MEAN_HT Mean tree height (TPA-weighted, nulls excluded)
"softwood_prop" SOFTWOOD_PROP Softwood proportion of bole biomass (SPCD < 300)
"sawtimber_prop" SAWTIMBER_PROP Proportion of TPA above sawtimber diameter threshold
"max_dia" MAX_DIA Maximum tree diameter in group
"stocking" STOCKING Rough stocking index

All results also include N_PLOTS and N_TREES diagnostic counts.

Examples

QMD by Forest Type

result = pyfia.tree_metrics(db, metrics=["qmd"], grp_by="FORTYPCD")
result = pyfia.join_forest_type_names(result, db)
print(result.sort("QMD", descending=True).head(10))

Stand Structure Profile

Compute multiple metrics at once for a comprehensive stand description:

result = pyfia.tree_metrics(
    db,
    metrics=["qmd", "mean_height", "softwood_prop", "sawtimber_prop", "stocking"],
    grp_by="FORTYPCD",
)

Condition-Level Metrics for External Models

Get per-plot-condition metrics with additional COND attributes passed through:

result = pyfia.tree_metrics(
    db,
    metrics=["qmd", "mean_height", "softwood_prop", "sawtimber_prop"],
    grp_by=["PLT_CN", "CONDID", "STDAGE", "FORTYPCD"],
    land_type="timber",
    tree_domain="DIA >= 1.0",
    include_cond_attrs=["SLOPE", "SICOND"],
)

Each row represents a single plot-condition, useful for linking to harvest probability models or growth simulators.

Large Trees Only

result = pyfia.tree_metrics(
    db,
    metrics=["qmd", "mean_dia", "max_dia"],
    tree_domain="DIA >= 12.0",
    grp_by="FORTYPCD",
)

Sawtimber with Custom Threshold

The default sawtimber threshold is 9.0 inches. Override it for hardwood-specific analysis:

result = pyfia.tree_metrics(
    db,
    metrics=["sawtimber_prop"],
    sawtimber_threshold=11.0,
    grp_by="FORTYPCD",
)

Comparison with Other Estimators

Feature tree_metrics() tpa(), volume(), etc.
Estimate type Sample-level descriptive Population-level statistical
Expansion factors No Yes
Variance / SE No Yes
Confidence intervals No Yes
Use case Stand characterization, model inputs Area/volume/biomass totals and per-acre rates