import torch
from importlib.resources import files, as_file
from .model_def import AmmoniaRNN
from .utils import generate_tensors_predictors
from .utils import check_allowed_cols
from .utils import check_needed_cols
from .utils import check_nan
from .utils import check_unique_values_for_plot_level_variables
from .utils import check_correct_values_for_categorical_variables
from .utils import check_t_incorp_col
from .utils import check_t_incorp_values
from .utils import complete_incorp
from .utils import complete_other
from .utils import add_dt
from .utils import convert_cat_variables
from .utils import reorder_variables
DEVICE = "cpu"
model = AmmoniaRNN().to(DEVICE)
resource = files(__package__).joinpath("data").joinpath("final_model.pth")
with as_file(resource) as path:
model.load_state_dict(torch.load(path, weights_only = True, map_location=torch.device('cpu')))
[docs]
def predict (df):
"""
Prediction of ammonia emissions following field fertilization.
Parameters
----------
df: pandas.DataFrame
Input DataFrame containing the environmental conditions for which predictions are made.
This DataFrame must include some mandatory columns and may include optional columns.
Mandatory columns:
- pmid: identifier for plots
- ct: time since fertilizer application (h)
- tan_app: total ammoniacal nitrogen applied (kgN/ha)
Optional columns:
Dynamic variables:
- air_temp: air temperature (°C)
- wind_2m: wind speed (m/s)
- rain_rate: rainfall rate (mm/h)
Plot-level variables:
- app_rate: application rate (t/ha)
- man_dm: manure dry matter content (%)
- man_ph: manure pH
- app_mthd: application method (must belong to {bc, bsth, ts, os, cs} (1))
- man_source: manure source (must belong to {pig, cat})
- incorp: incorporation (must belong to {none, shallow, deep})
- t_incorp: time of incorporation (h) (2)
(1) bc = broadacst, bsth = band spreading trailing hose, ts = trailing shoe, os = open slot, cs = closed slot.
(2) When incorp = none, t_incorp must be set to NaN.
Default values for optional columns: air_temp = 13.89, wind_2m = 3.11, rain_rate = 0, app_rate = 29.38, man_dm = 6.25, man_ph = 7.38, app_mthd = bsth, man_source = cat, incorp = none.
Returns
-------
pandas.DataFrame
The DataFrame completed with the column 'prediction_ecum' (cumulative prediction, kgN/ha) as well as the optional columns not initially provided, filled with their default values.
Example
--------
>>> import pandas as pd
>>> from nh3pred import predict
>>> # Prediction for two plots, identified with pmid = 1 and pmid = 2
>>> df = pd.DataFrame ({
... "pmid": [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2],
... "ct": [3, 6, 10, 24, 48, 72, 1, 3, 6, 10, 21, 46],
... "tan_app": [42, 42, 42, 42, 42, 42, 120, 120, 120, 120, 120, 120],
... "air_temp": [18, 23, 24, 15, 21, 20, 9, 10, 11, 6, 7, 10],
... "wind_2m": [2, 2, 1, 1, 2, 2, 4, 5, 5, 6, 3, 4],
... "rain_rate": 0,
... "app_rate": [20, 20, 20, 20, 20, 20, 28, 28, 28, 28, 28, 28],
... "man_dm": [8.3, 8.3, 8.3, 8.3, 8.3, 8.3, 13, 13, 13, 13, 13, 13],
... "man_ph": [7.1, 7.1, 7.1, 7.1, 7.1, 7.1, 7.7, 7.7, 7.7, 7.7, 7.7, 7.7],
... "app_mthd": ["ts", "ts", "ts", "ts", "ts", "ts", "bc", "bc", "bc", "bc", "bc", "bc"],
... "man_source": ["cat", "cat", "cat", "cat", "cat", "cat", "pig", "pig", "pig", "pig", "pig", "pig"]
... })
>>> predict (df)
pmid ct air_temp wind_2m rain_rate app_mthd incorp man_source tan_app app_rate man_dm man_ph t_incorp prediction_ecum
0 1 3 18 2 0 ts none cat 42 20 8.3 7.1 NaN 3.090000
1 1 6 23 2 0 ts none cat 42 20 8.3 7.1 NaN 5.620000
2 1 10 24 1 0 ts none cat 42 20 8.3 7.1 NaN 7.460000
3 1 24 15 1 0 ts none cat 42 20 8.3 7.1 NaN 8.030000
4 1 48 21 2 0 ts none cat 42 20 8.3 7.1 NaN 9.760000
5 1 72 20 2 0 ts none cat 42 20 8.3 7.1 NaN 10.480000
6 2 1 9 4 0 bc none pig 120 28 13.0 7.7 NaN 8.550000
7 2 3 10 5 0 bc none pig 120 28 13.0 7.7 NaN 27.150000
8 2 6 11 5 0 bc none pig 120 28 13.0 7.7 NaN 41.639999
9 2 10 6 6 0 bc none pig 120 28 13.0 7.7 NaN 48.549999
10 2 21 7 3 0 bc none pig 120 28 13.0 7.7 NaN 52.610001
11 2 46 10 4 0 bc none pig 120 28 13.0 7.7 NaN 61.220001
"""
check_allowed_cols (df)
check_needed_cols (df)
check_nan (df)
check_unique_values_for_plot_level_variables (df)
check_correct_values_for_categorical_variables (df)
check_t_incorp_col (df)
check_t_incorp_values (df)
data_predictions = df.copy()
data_predictions = complete_incorp (data_predictions)
data_predictions = complete_other (data_predictions)
data_predictions = add_dt (data_predictions)
data_predictions = convert_cat_variables (data_predictions)
data_predictions = reorder_variables (data_predictions)
pmids = data_predictions['pmid'].unique()
data_predictions['prediction_ecum'] = None
data_predictions['prediction_delta_ecum'] = None
with torch.no_grad():
all_predictions = torch.empty(0).to(DEVICE)
for i in pmids:
x = generate_tensors_predictors (data_predictions, i, device = DEVICE)
y = model(x)
all_predictions = torch.cat ((all_predictions, y.squeeze()), 0)
data_predictions['prediction_delta_ecum'] = all_predictions.to("cpu").detach()
data_predictions['prediction_ecum'] = data_predictions.groupby('pmid')['prediction_delta_ecum'].cumsum()
data_predictions["prediction_ecum"] = data_predictions["prediction_ecum"].round(2)
data_predictions = data_predictions.drop (['dt', 'prediction_delta_ecum'], axis = 1)
data_predictions["app_mthd"] = data_predictions["app_mthd"].map({0: "bc", 1: "bsth", 2: "os", 3: "ts", 4:"cs"})
data_predictions["man_source"] = data_predictions["man_source"].map({0: "pig", 1: "cat"})
data_predictions["incorp"] = data_predictions["incorp"].map({0: "none", 1: "shallow", 2: "deep"})
data_predictions["t_incorp"] = data_predictions["t_incorp"].replace(1000, float("nan"))
return data_predictions