Flexible inputs#

One function, whatever data you happen to have.

Every calculation takes your data in whichever of the common forms you already have it (a Polars or Pandas column, a NumPy array) and hands the result back in the same form. You don’t have to convert your data first, and there aren’t separate functions for different input types.

This page sets out exactly what each function accepts and returns.

Column arguments#

The measured quantities a function takes (e.g. temperature, radiation, neutron counts… etc.) are its column arguments. Each one accepts:

You pass

You get back

Notes

pl.Expr

pl.Expr

Use the returned expression inside Polars Dataframe’s methods such as select / with_columns.

str

pl.Expr

Shorthand for pl.col("name").

pl.Series

pl.Series

Evaluated immediately.

pd.Series

pd.Series

Evaluated immediately. Requires the pandas extra.

np.ndarray

np.ndarray

Evaluated immediately. Requires the numpy extra.

The Quick start shows each of those forms side by side.

Attribute arguments#

Some parameters describe features of where the measurement was taken, for example: a sensor height, a latitude, a wilting point, a calibration coefficient, etc. These are termed as attributes.

There are two ways that attributes can be provided:

  1. When the attribute is static, simply pass in a number

  2. When the attribute may vary across your data rows, pass a column-type (i.e. str, pl.Expr, pl.Series, pd.Series or np.ndarray)

from hydrometlib import cosmos

# 1. Pass through a number for each attribute: the attributes
#    are static values from a single site
cosmos.volumetric_water_content(
    "cts_mod_corr",  # measured column
    ref_soc=0.01,  # site attributes from here on
    ref_bulkdensity=1.35,
    ref_latticewater=0.02,
    n0_mod=2000,
    n_min=400,
    n_max=4000,
)

# 2. Pass through columns for an attribute: for example, your data
#    may have several sites in one frame so the attributes can vary across rows
cosmos.volumetric_water_content(
    "cts_mod_corr",
    ref_soc="ref_soc",
    ref_bulkdensity="ref_bulkdensity",
    ref_latticewater="ref_latticewater",
    n0_mod="n0_mod",
    n_min=400,  # still a single number if it is the same everywhere
    n_max=4000,
)

A number given for a site attribute takes no part in the “same kind” or “same length” rules below, and it does not affect what you get back: the return type is still decided by the measured columns.

If a column-type is provided for an attribute, it must match the kind and length of all the other column inputs.

Rules#

All column arguments in one call must be the same kind. Mixing kinds gives a TypeError explaining which argument was which. A site attribute given as a number is exempt - it pairs with any kind - but given as a column it must match like any other.

# This works fine - every column argument is a column name
meteorology.net_radiation("swin", "swout", "lwin", "lwout")

# a column name mixed with an expression
try:
    meteorology.net_radiation("swin", pl.col("swout") * 1.0, "lwin", "lwout")
except TypeError as err:
    print(err)

# a pl.Series mixed with a pd.Series
try:
    meteorology.net_radiation(
        pl.Series([22.9]),
        pd.Series([4.9]),
        pl.Series([24.1]),
        pl.Series([31.2]),
    )
except TypeError as err:
    print(err)
net_radiation(): all column arguments must be the same type - got pl.Expr (swout); column name (swin, lwin, lwout).
net_radiation(): all column arguments must be the same type - got pl.Series (swin, lwin, lwout); pd.Series (swout).

Series and array inputs must be the same length. Passing Series or arrays of different lengths raises a ValueError:

try:
    meteorology.net_radiation(
        pl.Series([22.9, 19.3]),
        pl.Series([4.9]),
        pl.Series([24.1, 26.0]),
        pl.Series([31.2, 31.9]),
    )
except ValueError as err:
    print(err)
Series arguments must all be the same length, got {'swin': 2, 'swout': 1, 'lwin': 2, 'lwout': 2}.

Note

These checks are about the kind of thing you pass, not the data inside it. A Polars Series holding non-numeric values is an accepted container, so the error for it comes from Polars when the calculation runs, not from hydrometlib.

Calling with only numbers#

Give every argument as a number and there is no column to build against, so the calculation is evaluated there and then and hands back a plain value instead:

# a value comes straight back, rather than a column to add to a frame
print(meteorology.net_radiation(100.0, 20.0, 300.0, 350.0))
30.0

This is handy for checking a value by hand.

Which form should I use?#

  • Building up a table of derived columns in Polars: pass column names. It reads cleanly inside with_columns.

  • You already hold a Series or array and just want the numbers: pass it directly and use the result.

  • Working with time-stream: pass column names or expressions and add the result with with_columns.

  • Checking a single set of values by hand: pass numbers for everything and read the result.