Skip to content

Removals Estimation

Estimate average annual timber removals.

Overview

The removals() function calculates annual removals estimates (harvested timber).

import pyfia

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

# Total removals
result = pyfia.removals(db, measure="volume")

# Removals by species
by_species = pyfia.removals(db, measure="volume", grp_by="SPCD")

Function Reference

removals

removals(db: Union[str, FIA], grp_by: Optional[Union[str, List[str]]] = None, by_species: bool = False, by_size_class: bool = False, land_type: str = 'forest', tree_type: str = 'gs', measure: str = 'volume', tree_domain: Optional[str] = None, area_domain: Optional[str] = None, totals: bool = True, variance: bool = False, most_recent: bool = False, remeasure_period: float = 5.0) -> DataFrame

Estimate average annual removals from FIA data.

Calculates average annual removals of merchantable bole wood volume of growing-stock trees (at least 5 inches d.b.h.) on forest land.

PARAMETER DESCRIPTION
db

Database connection or path

TYPE: Union[str, FIA]

grp_by

Columns to group by (e.g., "STATECD", "FORTYPCD")

TYPE: Optional[Union[str, List[str]]] DEFAULT: None

by_species

Group by species code

TYPE: bool DEFAULT: False

by_size_class

Group by diameter size classes

TYPE: bool DEFAULT: False

land_type

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

TYPE: str DEFAULT: 'forest'

tree_type

Tree type: "gs" (growing stock), "all"

TYPE: str DEFAULT: 'gs'

measure

What to measure: "volume", "biomass", or "count"

TYPE: str DEFAULT: 'volume'

tree_domain

SQL-like filter for trees

TYPE: Optional[str] DEFAULT: None

area_domain

SQL-like filter for area

TYPE: Optional[str] DEFAULT: None

totals

Include population totals

TYPE: bool DEFAULT: True

variance

Return variance instead of SE

TYPE: bool DEFAULT: False

most_recent

Use most recent evaluation

TYPE: bool DEFAULT: False

remeasure_period

Remeasurement period in years for annualization

TYPE: float DEFAULT: 5.0

RETURNS DESCRIPTION
DataFrame

Removals estimates with columns: - REMOVALS_PER_ACRE: Annual removals per acre - REMOVALS_TOTAL: Total annual removals - REMOVALS_PER_ACRE_SE: Standard error of per-acre estimate - REMOVALS_TOTAL_SE: Standard error of total estimate - Additional grouping columns if specified

Examples:

>>> # Basic volume removals on forestland
>>> results = removals(db, measure="volume")
>>> # Removals by species (tree count)
>>> results = removals(db, by_species=True, measure="count")
>>> # Biomass removals by forest type
>>> results = removals(
...     db,
...     grp_by="FORTYPCD",
...     measure="biomass"
... )
>>> # Removals on timberland only
>>> results = removals(
...     db,
...     land_type="timber",
...     area_domain="SITECLCD >= 225"  # Productive sites
... )
Notes

Removals include trees cut or otherwise removed from the inventory, including those diverted to non-forest use. The calculation uses TREE_GRM_COMPONENT table with CUT and DIVERSION components.

The estimate is annualized by dividing by the remeasurement period (default 5 years).

Source code in src/pyfia/estimation/estimators/removals.py
def removals(
    db: Union[str, FIA],
    grp_by: Optional[Union[str, List[str]]] = None,
    by_species: bool = False,
    by_size_class: bool = False,
    land_type: str = "forest",
    tree_type: str = "gs",
    measure: str = "volume",
    tree_domain: Optional[str] = None,
    area_domain: Optional[str] = None,
    totals: bool = True,
    variance: bool = False,
    most_recent: bool = False,
    remeasure_period: float = 5.0,
) -> pl.DataFrame:
    """
    Estimate average annual removals from FIA data.

    Calculates average annual removals of merchantable bole wood volume of
    growing-stock trees (at least 5 inches d.b.h.) on forest land.

    Parameters
    ----------
    db : Union[str, FIA]
        Database connection or path
    grp_by : Optional[Union[str, List[str]]]
        Columns to group by (e.g., "STATECD", "FORTYPCD")
    by_species : bool
        Group by species code
    by_size_class : bool
        Group by diameter size classes
    land_type : str
        Land type: "forest", "timber", or "all"
    tree_type : str
        Tree type: "gs" (growing stock), "all"
    measure : str
        What to measure: "volume", "biomass", or "count"
    tree_domain : Optional[str]
        SQL-like filter for trees
    area_domain : Optional[str]
        SQL-like filter for area
    totals : bool
        Include population totals
    variance : bool
        Return variance instead of SE
    most_recent : bool
        Use most recent evaluation
    remeasure_period : float
        Remeasurement period in years for annualization

    Returns
    -------
    pl.DataFrame
        Removals estimates with columns:
        - REMOVALS_PER_ACRE: Annual removals per acre
        - REMOVALS_TOTAL: Total annual removals
        - REMOVALS_PER_ACRE_SE: Standard error of per-acre estimate
        - REMOVALS_TOTAL_SE: Standard error of total estimate
        - Additional grouping columns if specified

    Examples
    --------
    >>> # Basic volume removals on forestland
    >>> results = removals(db, measure="volume")

    >>> # Removals by species (tree count)
    >>> results = removals(db, by_species=True, measure="count")

    >>> # Biomass removals by forest type
    >>> results = removals(
    ...     db,
    ...     grp_by="FORTYPCD",
    ...     measure="biomass"
    ... )

    >>> # Removals on timberland only
    >>> results = removals(
    ...     db,
    ...     land_type="timber",
    ...     area_domain="SITECLCD >= 225"  # Productive sites
    ... )

    Notes
    -----
    Removals include trees cut or otherwise removed from the inventory,
    including those diverted to non-forest use. The calculation uses
    TREE_GRM_COMPONENT table with CUT and DIVERSION components.

    The estimate is annualized by dividing by the remeasurement period
    (default 5 years).
    """
    from ...validation import (
        validate_boolean,
        validate_domain_expression,
        validate_grp_by,
        validate_land_type,
        validate_mortality_measure,
        validate_positive_number,
        validate_tree_type,
    )

    land_type = validate_land_type(land_type)
    tree_type = validate_tree_type(tree_type)
    measure = validate_mortality_measure(measure)
    grp_by = validate_grp_by(grp_by)
    tree_domain = validate_domain_expression(tree_domain, "tree_domain")
    area_domain = validate_domain_expression(area_domain, "area_domain")
    by_species = validate_boolean(by_species, "by_species")
    by_size_class = validate_boolean(by_size_class, "by_size_class")
    totals = validate_boolean(totals, "totals")
    variance = validate_boolean(variance, "variance")
    most_recent = validate_boolean(most_recent, "most_recent")
    remeasure_period = validate_positive_number(remeasure_period, "remeasure_period")

    config = {
        "grp_by": grp_by,
        "by_species": by_species,
        "by_size_class": by_size_class,
        "land_type": land_type,
        "tree_type": tree_type,
        "measure": measure,
        "tree_domain": tree_domain,
        "area_domain": area_domain,
        "totals": totals,
        "variance": variance,
        "most_recent": most_recent,
        "remeasure_period": remeasure_period,
    }

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

Measurement Types

Measure Description
"volume" Net cubic-foot volume removals
"biomass" Above-ground biomass removals

Technical Notes

Removals estimation uses:

  • TREE_GRM_COMPONENT table for removal attributes
  • TREE_GRM_MIDPT table for annualized values
  • Trees with TPAREMV_UNADJ > 0 are removal trees
  • Calculated as: TPAREMV_UNADJ × VOLCFNET × ADJ × EXPNS

Note

PyFIA calculates removals from raw components rather than using pre-calculated REMVCFGS columns for consistency with EVALIDator methodology.

Examples

Total Removals Volume

result = pyfia.removals(
    db,
    measure="volume",
    land_type="forest"
)
print(f"Annual Removals: {result['estimate'][0]:,.0f} cu ft/year")

Removals by Species

result = pyfia.removals(
    db,
    measure="volume",
    grp_by="SPCD"
)
result = pyfia.join_species_names(result, db)
print(result.sort("estimate", descending=True).head(10))

Growing Stock Removals

result = pyfia.removals(
    db,
    measure="volume",
    land_type="timber",
    tree_type="gs"
)

Biomass Removals

result = pyfia.removals(
    db,
    measure="biomass",
    land_type="forest"
)
print(f"Annual Biomass Removals: {result['estimate'][0]:,.0f} tons/year")

Growth-Drain Analysis

# Compare growth to removals
growth = pyfia.growth(db, measure="volume")
removals = pyfia.removals(db, measure="volume")
mortality = pyfia.mortality(db, measure="volume")

print(f"Growth: {growth['estimate'][0]:,.0f} cu ft/year")
print(f"Removals: {removals['estimate'][0]:,.0f} cu ft/year")
print(f"Mortality: {mortality['estimate'][0]:,.0f} cu ft/year")

net_change = growth['estimate'][0] - removals['estimate'][0] - mortality['estimate'][0]
print(f"Net Change: {net_change:,.0f} cu ft/year")