Skip to content

Write your own corrections

Custom corrections can be registered with the correction factory when you need to test a site-specific or experimental correction without changing the built-in method list.

Built a correction that works well? Share it

The approach below registers a correction at runtime in your own analysis code. If your correction could be useful to others, consider contributing it to Neptoon so it can become part of the standard pipeline. See Develop with Neptoon.

Creating a custom correction

Extend the Correction base class, call super().__init__(), and implement apply():

import pandas as pd

from neptoon.corrections import Correction, CorrectionType


class NewIdeaForBiomass(Correction):
    """Example biomass correction that also uses relative humidity."""

    def __init__(
        self,
        correction_type: CorrectionType = CorrectionType.CUSTOM,
        correction_factor_column_name: str = "new_biomass_correction",
    ) -> None:
        super().__init__(
            correction_type=correction_type,
            correction_factor_column_name=correction_factor_column_name,
        )
        self.humidity_column_name = "air_relative_humidity"
        self.biomass_column_name = "site_biomass"

    @staticmethod
    def new_func(biomass: float, humidity: float) -> float:
        if biomass == 0:
            return 1.0
        return 1 - ((biomass / humidity) / 1000)

    def apply(
        self,
        data_frame: pd.DataFrame,
        invalid_data_policy: str | None = None,
    ) -> pd.DataFrame:
        data_frame[self.correction_factor_column_name] = data_frame.apply(
            lambda row: self.new_func(
                row[self.biomass_column_name],
                row[self.humidity_column_name],
            ),
            axis=1,
        )
        return data_frame

The older apply(self, data_frame) signature remains supported. New corrections should accept the optional invalid_data_policy argument so they can follow the same row-level error policy as built-in corrections.

Registration

Register the class, not an instance:

data_hub.correction_factory.register_custom_correction(
    correction_type=CorrectionType.CUSTOM,
    theory="my_new_idea",
    correction_class=NewIdeaForBiomass,
)

data_hub.select_correction(
    correction_type=CorrectionType.CUSTOM,
    correction_theory="my_new_idea",
)

data_hub.correct_neutrons()

Theory names are stable lookup keys and must use lowercase snake_case.

What is validated

Registration fails immediately with an actionable error and hint when:

  • correction_type is not a CorrectionType member;
  • theory is empty or is not lowercase snake_case;
  • the supplied value is not a Correction subclass;
  • the class is still abstract because apply() is missing;
  • the constructor cannot be called as MyCorrection(correction_type=...);
  • construction raises an exception;
  • the instance reports a different correction_type from the registered type;
  • correction_factor_column_name is missing, empty, non-string, or contains unsafe whitespace;
  • apply() cannot be called with a pandas DataFrame.

Some checks require real data and therefore happen when the correction is first applied. Neptoon then verifies that apply() returns a pandas DataFrame and that it creates the declared correction-factor column.

For example, returning None produces an error that explains that the input DataFrame must be returned. Declaring correction_factor_column_name = "my_factor" without creating data_frame["my_factor"] produces an error pointing to the missing assignment.

Replacing a registration

Accidental replacement is rejected by default. Explicitly opt in when replacing a previously registered class is intentional:

data_hub.correction_factory.register_custom_correction(
    correction_type=CorrectionType.CUSTOM,
    theory="my_new_idea",
    correction_class=RevisedBiomassCorrection,
    overwrite=True,
)