nh3pred 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nh3pred/__init__.py +8 -0
- nh3pred/api.py +172 -0
- nh3pred/data/final_model.pth +0 -0
- nh3pred/model_def.py +52 -0
- nh3pred/utils.py +154 -0
- nh3pred-0.1.1.dist-info/METADATA +68 -0
- nh3pred-0.1.1.dist-info/RECORD +9 -0
- nh3pred-0.1.1.dist-info/WHEEL +5 -0
- nh3pred-0.1.1.dist-info/top_level.txt +1 -0
nh3pred/__init__.py
ADDED
nh3pred/api.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from importlib.resources import files, as_file
|
|
3
|
+
|
|
4
|
+
from .model_def import AmmoniaRNN
|
|
5
|
+
|
|
6
|
+
from .utils import generate_tensors_predictors
|
|
7
|
+
|
|
8
|
+
from .utils import check_allowed_cols
|
|
9
|
+
from .utils import check_needed_cols
|
|
10
|
+
from .utils import check_nan
|
|
11
|
+
from .utils import check_unique_values_for_plot_level_variables
|
|
12
|
+
from .utils import check_correct_values_for_categorical_variables
|
|
13
|
+
from .utils import check_t_incorp_col
|
|
14
|
+
from .utils import check_t_incorp_values
|
|
15
|
+
|
|
16
|
+
from .utils import complete_incorp
|
|
17
|
+
from .utils import complete_other
|
|
18
|
+
from .utils import add_dt
|
|
19
|
+
from .utils import convert_cat_variables
|
|
20
|
+
from .utils import reorder_variables
|
|
21
|
+
|
|
22
|
+
DEVICE = "cpu"
|
|
23
|
+
num_layers = 1
|
|
24
|
+
nonlinearity = "relu"
|
|
25
|
+
bidirectional = True
|
|
26
|
+
hidden_size = 512
|
|
27
|
+
|
|
28
|
+
cat_dims = [5, 3, 2]
|
|
29
|
+
embedding_dims = [10, 9, 8]
|
|
30
|
+
input_size = 13
|
|
31
|
+
output_size = 1
|
|
32
|
+
|
|
33
|
+
model = AmmoniaRNN(input_size = input_size,
|
|
34
|
+
output_size = output_size,
|
|
35
|
+
hidden_size = hidden_size,
|
|
36
|
+
nonlinearity = nonlinearity,
|
|
37
|
+
num_layers = num_layers,
|
|
38
|
+
bidirectional = bidirectional,
|
|
39
|
+
cat_dims = cat_dims,
|
|
40
|
+
embedding_dims = embedding_dims).to(DEVICE)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
resource = files(__package__).joinpath("data").joinpath("final_model.pth")
|
|
44
|
+
with as_file(resource) as path:
|
|
45
|
+
model.load_state_dict(torch.load(path, weights_only = True, map_location=torch.device('cpu')))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def predict (df):
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
Prediction of ammonia emissions following field fertilization.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
df: pandas.DataFrame
|
|
57
|
+
Input DataFrame containing the environmental conditions for which predictions are made.
|
|
58
|
+
This DataFrame must include some mandatory columns and may include optional columns.
|
|
59
|
+
|
|
60
|
+
Mandatory columns:
|
|
61
|
+
- pmid: identifier for plots
|
|
62
|
+
- ct: time since fertilizer application (h)
|
|
63
|
+
- tan_app: total ammoniacal nitrogen applied (kgN/ha)
|
|
64
|
+
|
|
65
|
+
Optional columns:
|
|
66
|
+
Dynamic variables:
|
|
67
|
+
- air_temp: air temperature (°C)
|
|
68
|
+
- wind_2m: wind speed (m/s)
|
|
69
|
+
- rain_rate: rainfall rate (mm/h)
|
|
70
|
+
Plot-level variables:
|
|
71
|
+
- app_rate: application rate (t/ha)
|
|
72
|
+
- man_dm: manure dry matter content (%)
|
|
73
|
+
- man_ph: manure pH
|
|
74
|
+
- app_mthd: application method (must belong to {bc, bsth, ts, os, cs} (1))
|
|
75
|
+
- man_source: manure source (must belong to {pig, cat})
|
|
76
|
+
- incorp: incorporation (must belong to {none, shallow, deep})
|
|
77
|
+
- t_incorp: time of incorporation (h) (2)
|
|
78
|
+
|
|
79
|
+
(1) bc = broadacst, bsth = band spreading trailing hose, ts = trailing shoe, os = open slot, cs = closed slot.
|
|
80
|
+
|
|
81
|
+
(2) When incorp = none, t_incorp must be set to NaN.
|
|
82
|
+
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
pandas.DataFrame
|
|
88
|
+
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.
|
|
89
|
+
|
|
90
|
+
Example
|
|
91
|
+
--------
|
|
92
|
+
>>> # Prediction for two plots, identified with pmid = 1 and pmid = 2
|
|
93
|
+
>>> import pandas as pd
|
|
94
|
+
>>> from nh3pred import predict
|
|
95
|
+
|
|
96
|
+
>>> df = pd.DataFrame ({
|
|
97
|
+
... "pmid": [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2],
|
|
98
|
+
... "ct": [3, 6, 10, 24, 48, 72, 1, 3, 6, 10, 21, 46],
|
|
99
|
+
... "tan_app": [42, 42, 42, 42, 42, 42, 120, 120, 120, 120, 120, 120],
|
|
100
|
+
... "air_temp": [18, 23, 24, 15, 21, 20, 9, 10, 11, 6, 7, 10],
|
|
101
|
+
... "wind_2m": [2, 2, 1, 1, 2, 2, 4, 5, 5, 6, 3, 4],
|
|
102
|
+
... "rain_rate": 0,
|
|
103
|
+
... "app_rate": [20, 20, 20, 20, 20, 20, 28, 28, 28, 28, 28, 28],
|
|
104
|
+
... "man_dm": [8.3, 8.3, 8.3, 8.3, 8.3, 8.3, 13, 13, 13, 13, 13, 13],
|
|
105
|
+
... "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],
|
|
106
|
+
... "app_mthd": ["ts", "ts", "ts", "ts", "ts", "ts", "bc", "bc", "bc", "bc", "bc", "bc"],
|
|
107
|
+
... "man_source": ["cat", "cat", "cat", "cat", "cat", "cat", "pig", "pig", "pig", "pig", "pig", "pig"]
|
|
108
|
+
... })
|
|
109
|
+
|
|
110
|
+
>>> predict (df)
|
|
111
|
+
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
|
|
112
|
+
0 1 3 18 2 0 ts none cat 42 20 8.3 7.1 NaN 3.090000
|
|
113
|
+
1 1 6 23 2 0 ts none cat 42 20 8.3 7.1 NaN 5.620000
|
|
114
|
+
2 1 10 24 1 0 ts none cat 42 20 8.3 7.1 NaN 7.460000
|
|
115
|
+
3 1 24 15 1 0 ts none cat 42 20 8.3 7.1 NaN 8.030000
|
|
116
|
+
4 1 48 21 2 0 ts none cat 42 20 8.3 7.1 NaN 9.760000
|
|
117
|
+
5 1 72 20 2 0 ts none cat 42 20 8.3 7.1 NaN 10.480000
|
|
118
|
+
6 2 1 9 4 0 bc none pig 120 28 13.0 7.7 NaN 8.550000
|
|
119
|
+
7 2 3 10 5 0 bc none pig 120 28 13.0 7.7 NaN 27.150000
|
|
120
|
+
8 2 6 11 5 0 bc none pig 120 28 13.0 7.7 NaN 41.639999
|
|
121
|
+
9 2 10 6 6 0 bc none pig 120 28 13.0 7.7 NaN 48.549999
|
|
122
|
+
10 2 21 7 3 0 bc none pig 120 28 13.0 7.7 NaN 52.610001
|
|
123
|
+
11 2 46 10 4 0 bc none pig 120 28 13.0 7.7 NaN 61.220001
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
check_allowed_cols (df)
|
|
127
|
+
check_needed_cols (df)
|
|
128
|
+
check_nan (df)
|
|
129
|
+
check_unique_values_for_plot_level_variables (df)
|
|
130
|
+
check_correct_values_for_categorical_variables (df)
|
|
131
|
+
check_t_incorp_col (df)
|
|
132
|
+
check_t_incorp_values (df)
|
|
133
|
+
|
|
134
|
+
data_predictions = df.copy()
|
|
135
|
+
|
|
136
|
+
data_predictions = complete_incorp (data_predictions)
|
|
137
|
+
data_predictions = complete_other (data_predictions)
|
|
138
|
+
data_predictions = add_dt (data_predictions)
|
|
139
|
+
data_predictions = convert_cat_variables (data_predictions)
|
|
140
|
+
data_predictions = reorder_variables (data_predictions)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
pmids = data_predictions['pmid'].unique()
|
|
144
|
+
|
|
145
|
+
data_predictions['prediction_ecum'] = None
|
|
146
|
+
data_predictions['prediction_delta_ecum'] = None
|
|
147
|
+
|
|
148
|
+
with torch.no_grad():
|
|
149
|
+
|
|
150
|
+
all_predictions = torch.empty(0).to(DEVICE)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
for i in pmids:
|
|
154
|
+
|
|
155
|
+
x = generate_tensors_predictors (data_predictions, i, device = DEVICE)
|
|
156
|
+
y = model(x)
|
|
157
|
+
all_predictions = torch.cat ((all_predictions, y.squeeze()), 0)
|
|
158
|
+
|
|
159
|
+
data_predictions['prediction_delta_ecum'] = all_predictions.to("cpu").detach()
|
|
160
|
+
|
|
161
|
+
data_predictions['prediction_ecum'] = data_predictions.groupby('pmid')['prediction_delta_ecum'].cumsum()
|
|
162
|
+
data_predictions["prediction_ecum"] = data_predictions["prediction_ecum"].round(2)
|
|
163
|
+
|
|
164
|
+
data_predictions = data_predictions.drop (['dt', 'prediction_delta_ecum'], axis = 1)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
data_predictions["app_mthd"] = data_predictions["app_mthd"].map({0: "bc", 1: "bsth", 2: "os", 3: "ts", 4:"cs"})
|
|
168
|
+
data_predictions["man_source"] = data_predictions["man_source"].map({0: "pig", 1: "cat"})
|
|
169
|
+
data_predictions["incorp"] = data_predictions["incorp"].map({0: "none", 1: "shallow", 2: "deep"})
|
|
170
|
+
data_predictions["t_incorp"] = data_predictions["t_incorp"].replace(1000, float("nan"))
|
|
171
|
+
|
|
172
|
+
return data_predictions
|
|
Binary file
|
nh3pred/model_def.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
|
|
4
|
+
class AmmoniaRNN(nn.Module):
|
|
5
|
+
|
|
6
|
+
def __init__(self,
|
|
7
|
+
input_size,
|
|
8
|
+
output_size,
|
|
9
|
+
hidden_size,
|
|
10
|
+
num_layers,
|
|
11
|
+
nonlinearity,
|
|
12
|
+
bidirectional,
|
|
13
|
+
cat_dims = None, embedding_dims = None):
|
|
14
|
+
|
|
15
|
+
super(AmmoniaRNN, self).__init__()
|
|
16
|
+
|
|
17
|
+
D = 1 + 1 * bidirectional
|
|
18
|
+
|
|
19
|
+
self.embeddings = nn.ModuleList([
|
|
20
|
+
nn.Embedding(num_embeddings = cat_dim, embedding_dim = embed_dim)
|
|
21
|
+
for cat_dim, embed_dim in zip(cat_dims, embedding_dims)
|
|
22
|
+
])
|
|
23
|
+
|
|
24
|
+
input_size = input_size - len(cat_dims) + sum(embedding_dims)
|
|
25
|
+
|
|
26
|
+
self.rnn = nn.RNN(input_size,
|
|
27
|
+
hidden_size,
|
|
28
|
+
num_layers = num_layers,
|
|
29
|
+
nonlinearity = nonlinearity,
|
|
30
|
+
bidirectional = bidirectional)
|
|
31
|
+
|
|
32
|
+
self.fc1 = nn.Linear(hidden_size * D, 6)
|
|
33
|
+
self.relu = nn.ReLU()
|
|
34
|
+
self.fc2 = nn.Linear(6, output_size)
|
|
35
|
+
|
|
36
|
+
def forward(self, x):
|
|
37
|
+
|
|
38
|
+
x_continuous = x[0]
|
|
39
|
+
x_categoricals = x[1]
|
|
40
|
+
|
|
41
|
+
x_embeds = [embed(x_cat) for embed, x_cat in zip(self.embeddings, x_categoricals)]
|
|
42
|
+
|
|
43
|
+
x = torch.cat([x_continuous] + x_embeds, dim = -1)
|
|
44
|
+
|
|
45
|
+
h, _ = self.rnn(x)
|
|
46
|
+
|
|
47
|
+
out = self.fc1(h)
|
|
48
|
+
out = self.relu(out)
|
|
49
|
+
out = self.fc2(out)
|
|
50
|
+
|
|
51
|
+
return out
|
|
52
|
+
|
nh3pred/utils.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
def generate_tensors_predictors(df, pmid, device):
|
|
5
|
+
|
|
6
|
+
data_filtered = df[df['pmid'] == pmid]
|
|
7
|
+
|
|
8
|
+
x_cont = data_filtered[['ct', 'dt', 'air_temp', 'wind_2m', 'rain_rate', 'tan_app', 'app_rate', 'man_dm', 'man_ph', 't_incorp']]
|
|
9
|
+
|
|
10
|
+
x_cont_tensor = torch.tensor(x_cont.values, dtype=torch.float32).view(len(x_cont), len(x_cont.columns))
|
|
11
|
+
x_cont_tensor = x_cont_tensor.to(device)
|
|
12
|
+
|
|
13
|
+
x_cat = data_filtered[['app_mthd', 'incorp', 'man_source']]
|
|
14
|
+
|
|
15
|
+
x_cat_tensor = torch.tensor(x_cat.values, dtype=torch.long).view(len(x_cat), len(x_cat.columns))
|
|
16
|
+
x_cat_tensor = x_cat_tensor.to(device)
|
|
17
|
+
x_cat_tensor = torch.unbind (x_cat_tensor, dim = 1)
|
|
18
|
+
|
|
19
|
+
output = [x_cont_tensor, x_cat_tensor]
|
|
20
|
+
|
|
21
|
+
return output
|
|
22
|
+
|
|
23
|
+
def check_allowed_cols (_df):
|
|
24
|
+
|
|
25
|
+
allowed_cols = ['pmid', 'ct', 'air_temp', 'wind_2m', 'rain_rate', 'tan_app', 'app_rate', 'man_dm', 'man_ph', 'app_mthd', 'man_source', 'incorp', 't_incorp']
|
|
26
|
+
|
|
27
|
+
invalid_cols = list(set(_df.columns) - set(allowed_cols))
|
|
28
|
+
|
|
29
|
+
if invalid_cols:
|
|
30
|
+
raise ValueError(f"The following columns are not allowed: {invalid_cols}")
|
|
31
|
+
|
|
32
|
+
def check_needed_cols (_df):
|
|
33
|
+
|
|
34
|
+
needed_cols = ['pmid', 'ct', 'tan_app']
|
|
35
|
+
|
|
36
|
+
missing_cols = list (set(needed_cols) - set(_df.columns))
|
|
37
|
+
|
|
38
|
+
if missing_cols:
|
|
39
|
+
raise ValueError (f"The following column(s) is(are) missing: {missing_cols}")
|
|
40
|
+
|
|
41
|
+
def check_nan (_df):
|
|
42
|
+
|
|
43
|
+
for col in list (set (_df.columns) - set (['t_incorp'])):
|
|
44
|
+
|
|
45
|
+
if _df[col].isna().any():
|
|
46
|
+
raise ValueError(f"NaN values in {col}.")
|
|
47
|
+
|
|
48
|
+
def check_unique_values_for_plot_level_variables (_df):
|
|
49
|
+
|
|
50
|
+
plot_level_variables = ["app_mthd", "man_source", "incorp", "man_ph", "man_dm"]
|
|
51
|
+
|
|
52
|
+
pmids = _df['pmid'].unique()
|
|
53
|
+
variables_to_check = list (set (plot_level_variables) & set (_df.columns))
|
|
54
|
+
|
|
55
|
+
for var in variables_to_check:
|
|
56
|
+
df_tmp = _df.groupby('pmid')[var].unique()
|
|
57
|
+
for i in pmids:
|
|
58
|
+
if len (df_tmp[i]) != 1:
|
|
59
|
+
raise ValueError (f"Not unique value for {var} in pmid {i}")
|
|
60
|
+
|
|
61
|
+
def check_correct_values_for_categorical_variables (_df):
|
|
62
|
+
|
|
63
|
+
categorical_variables = ['app_mthd', 'incorp', 'man_source']
|
|
64
|
+
|
|
65
|
+
expected_values = {'app_mthd': ['bc', 'bsth', 'ts', 'os', 'cs'],
|
|
66
|
+
'incorp': ['none', 'deep', 'shallow'],
|
|
67
|
+
'man_source': ['pig', 'cat']}
|
|
68
|
+
|
|
69
|
+
variables_to_check = list (set (categorical_variables) & set (_df.columns))
|
|
70
|
+
|
|
71
|
+
for var in variables_to_check:
|
|
72
|
+
unvalid_values = list (set (_df[var].unique()) - set (expected_values[var]))
|
|
73
|
+
if unvalid_values:
|
|
74
|
+
raise ValueError(f"The following values are not allowed in {var}: {unvalid_values}")
|
|
75
|
+
|
|
76
|
+
def check_t_incorp_col (_df):
|
|
77
|
+
if 'incorp' in _df.columns:
|
|
78
|
+
if list (set (_df['incorp'].unique()) - set (["none"])):
|
|
79
|
+
if 't_incorp' not in _df.columns:
|
|
80
|
+
raise ValueError ("Column t_incorp is missing.")
|
|
81
|
+
else:
|
|
82
|
+
if 't_incorp' in _df.columns:
|
|
83
|
+
raise ValueError ("Column t_incorp is not allowed since there is no incorp column.")
|
|
84
|
+
|
|
85
|
+
def check_t_incorp_values (_df):
|
|
86
|
+
|
|
87
|
+
if 't_incorp' in _df.columns:
|
|
88
|
+
|
|
89
|
+
df_tmp = _df [(_df['incorp'] == "none") & (_df ['t_incorp'].notna())]
|
|
90
|
+
if df_tmp.shape[0] > 0:
|
|
91
|
+
raise ValueError ("t_incorp should be set to NaN when incorp = none.")
|
|
92
|
+
|
|
93
|
+
df_tmp = _df [(_df['incorp'] != "none") & (_df ['t_incorp'].isna())]
|
|
94
|
+
if df_tmp.shape[0] > 0:
|
|
95
|
+
raise ValueError ("NaN values in t_incorp for incorp != none.")
|
|
96
|
+
|
|
97
|
+
def complete_incorp (_df):
|
|
98
|
+
|
|
99
|
+
if 'incorp' in _df.columns:
|
|
100
|
+
if 't_incorp' in _df.columns:
|
|
101
|
+
_df['t_incorp'] = _df['t_incorp'].fillna (1000)
|
|
102
|
+
|
|
103
|
+
elif 't_incorp' not in _df.columns:
|
|
104
|
+
_df["t_incorp"] = [1000 if val == "none" else 0 for val in _df["incorp"]]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
elif 'incorp' not in _df.columns:
|
|
108
|
+
_df.insert (len (_df.columns), 'incorp', "none")
|
|
109
|
+
_df.insert (len (_df.columns), 't_incorp', 1000)
|
|
110
|
+
|
|
111
|
+
return _df
|
|
112
|
+
|
|
113
|
+
def complete_other (_df):
|
|
114
|
+
all_columns = ['pmid', 'ct', 'air_temp', 'wind_2m', 'rain_rate', 'app_mthd', 'man_source', 'tan_app',
|
|
115
|
+
'app_rate', 'man_dm', 'man_ph']
|
|
116
|
+
|
|
117
|
+
default_values = pd.Series({
|
|
118
|
+
'air_temp': 13.89,
|
|
119
|
+
'wind_2m': 3.11,
|
|
120
|
+
'rain_rate': 0,
|
|
121
|
+
'app_mthd': "bsth",
|
|
122
|
+
'man_source' : 'cat',
|
|
123
|
+
'tan_app' : 62.07,
|
|
124
|
+
'app_rate': 29.38,
|
|
125
|
+
'man_dm': 6.25,
|
|
126
|
+
'man_ph': 7.38,
|
|
127
|
+
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
missing_col = list (set (all_columns) - set (_df.columns))
|
|
131
|
+
|
|
132
|
+
for col in missing_col:
|
|
133
|
+
_df.insert (len (_df.columns), col, default_values[col])
|
|
134
|
+
|
|
135
|
+
return _df
|
|
136
|
+
|
|
137
|
+
def add_dt (_df):
|
|
138
|
+
_df.insert (1, 'ct_shift', _df.groupby("pmid")["ct"].shift(1, fill_value=0))
|
|
139
|
+
_df['dt'] = _df['ct'] - _df['ct_shift']
|
|
140
|
+
_df = _df.drop (['ct_shift'], axis = 1)
|
|
141
|
+
return _df
|
|
142
|
+
|
|
143
|
+
def convert_cat_variables (_df):
|
|
144
|
+
_df["app_mthd"] = _df["app_mthd"].map({"bc": 0, "bsth": 1, "os": 2, "ts": 3, "cs": 4})
|
|
145
|
+
_df["man_source"] = _df["man_source"].map({"pig": 0, "cat": 1})
|
|
146
|
+
_df["incorp"] = _df["incorp"].map({"none": 0, "shallow": 1, "deep": 2})
|
|
147
|
+
|
|
148
|
+
return _df
|
|
149
|
+
|
|
150
|
+
def reorder_variables (_df):
|
|
151
|
+
return _df[['pmid', 'ct', 'dt', 'air_temp', 'wind_2m', 'rain_rate', 'app_mthd', 'incorp', 'man_source', 'tan_app',
|
|
152
|
+
'app_rate', 'man_dm', 'man_ph', 't_incorp']]
|
|
153
|
+
|
|
154
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nh3pred
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Predict ammonia emissions with a recurrent neural network model
|
|
5
|
+
Author-email: Armand Favrot <armand.favrot@inrae.fr>
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Requires-Python: >=3.12.3
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: pandas>=2.2.3
|
|
12
|
+
Requires-Dist: torch>=2.5.0
|
|
13
|
+
Provides-Extra: build
|
|
14
|
+
Requires-Dist: build; extra == "build"
|
|
15
|
+
Requires-Dist: twine; extra == "build"
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
|
|
19
|
+
# nh3pred
|
|
20
|
+
|
|
21
|
+
This package provides a single function, `predict`, which estimates ammonia emissions following field fertilization under given environmental conditions.
|
|
22
|
+
The underlying model is a recurrent neural network described in [ref] under the name *"rnn 9 – data a."*.
|
|
23
|
+
The `predict` function works similarly to the `alfam2` function from the R package [ALFAM2](https://cran.r-project.org/web/packages/ALFAM2/index.html).
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install nh3pred
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Documentation
|
|
32
|
+
|
|
33
|
+
A complete documentation for the `predict` function is available [here](https://nh3pred.readthedocs.io/en/latest/api.html#nh3pred.api.predict).
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
You can use the package in Python as follows:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import pandas as pd
|
|
41
|
+
from ammonia_predict_3 import predict
|
|
42
|
+
|
|
43
|
+
df = pd.DataFrame ({
|
|
44
|
+
"pmid": [1, 1, 1, 1, 1, 1],
|
|
45
|
+
"ct": [3, 6, 10, 24, 48, 72],
|
|
46
|
+
"tan_app": [42, 42, 42, 42, 42, 42],
|
|
47
|
+
"air_temp": [18, 23, 24, 15, 21, 20],
|
|
48
|
+
"wind_2m": [2, 2, 1, 1, 2, 2],
|
|
49
|
+
"rain_rate": 0,
|
|
50
|
+
"app_rate": [20, 20, 20, 20, 20, 20],
|
|
51
|
+
"man_dm": [8.3, 8.3, 8.3, 8.3, 8.3, 8.3],
|
|
52
|
+
"man_ph": [7.1, 7.1, 7.1, 7.1, 7.1, 7.1],
|
|
53
|
+
"app_mthd": ["ts", "ts", "ts", "ts", "ts", "ts"],
|
|
54
|
+
"man_source": ["cat", "cat", "cat", "cat", "cat", "cat"]
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
pred = predict(df)
|
|
58
|
+
print(pred)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## Notes
|
|
63
|
+
|
|
64
|
+
- The trained weights are included in the package under `ammonia_predict/data/final_model.pth`.
|
|
65
|
+
- The package requires **Python ≥3.12**, **PyTorch ≥2.5.0**, and **pandas ≥2.2.3**.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
nh3pred/__init__.py,sha256=gR8afgOvwZBjb2w-AONXJZCHqINKQydV5wYNObFcXtM,125
|
|
2
|
+
nh3pred/api.py,sha256=IqT9YZQNdz9Llz3HBw5TVQHn6UPrKOGCGBM5pTxd1w4,7902
|
|
3
|
+
nh3pred/model_def.py,sha256=aue_GP7nnJGFwp5645ScluyDHdt0kBi9g8WxcFs17mI,1528
|
|
4
|
+
nh3pred/utils.py,sha256=z2H6dxebdvVJrN96_Qx4XWYVt8czMj7fFo7xzQocxgE,5352
|
|
5
|
+
nh3pred/data/final_model.pth,sha256=RSAaLGytjB9uQPlFW_EAKyrwGjaDmlQKhLgDozJfB18,2285808
|
|
6
|
+
nh3pred-0.1.1.dist-info/METADATA,sha256=sgEKMbzf5Y1VGVrzYYN1zL328I2-dpJO6nXi4uU-6LY,2077
|
|
7
|
+
nh3pred-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
nh3pred-0.1.1.dist-info/top_level.txt,sha256=pCAF7nBmtSkoPGy2HebVx00kF1r6gascwX4NOmfLFVw,8
|
|
9
|
+
nh3pred-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nh3pred
|