# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: 2022-2023 Tanguy Fardet
# SPDX-License-Identifier: GPL-3.0-or-later
# orgmatt/metabolism/nutrient_flows.py
import re
from typing import Literal, Optional
import numpy as np
from .._utils import auto_format, return_values
from .._utils.tools import _filter_dataframe, _group_from_age, _regex_age
from ..data import body_compo, nutrients_flow_fractions
from ..typing import NumericArrayLike, NumericOrArray
r20_24 = re.compile(_regex_age("20-24"))
r25_29 = re.compile(_regex_age("25-29"))
r20_29 = re.compile(_regex_age("20-29"))
[docs]
@auto_format
def body_composition(
nutrient: str,
ci: int = 0,
q: Optional[NumericArrayLike] = None,
**kwargs
) -> NumericOrArray:
'''
Fraction of the body mass represented by `nutrient`.
Parameters
----------
nutrient : str
Name of the nutrient (among "N", "P", "K", "Ca").
ci : int (optional, default: 0)
Confidence interval (CI). If non-zero, the function will also return
the low and high expected values corresponding to that CI.
q : array-like of floats (optional: default: None)
Percentiles to compute (supersedes `ci`).
kwargs : dict, optional
Additional arguments to constrain the result, e.g. "age", "group",
"sex", or "region".
Returns
-------
A float in [0, 1) giving the fraction of body mass made of `nutrient`.
'''
filters = kwargs.copy()
filters["compound"] = nutrient
df = _filter_dataframe(body_compo, filters)
return 0.01*return_values(df, "content", 1, ci_value=ci, q=q)
[docs]
@auto_format
def fraction_nutrient_retention(
nutrient: str,
ci: int = 0,
q: Optional[NumericArrayLike] = None,
**kwargs
) -> NumericOrArray:
'''
Fraction of a nutrient retained in the body during growth.
Parameters
----------
nutrient : str
The nutrient of interest.
ci : int (optional, default: 0)
Confidence interval (CI). If non-zero, the function will also return
the low and high expected values corresponding to that CI.
q : array-like of floats (optional: default: None)
Percentiles to compute (supersedes `ci`).
**kwargs : arguments to use a subset of the database
Additional arguments can be use to restrict the results to a subset of
the full database. E.g. one can specify an `age` or a `group`.
Returns
-------
A float in [0, 1) giving the fraction of the daily nutrient intake
retained in the body.
'''
group = kwargs.get("group", "")
age = kwargs.get("age")
if not group and age:
group = _group_from_age(age)
if group == "senior":
return 0.
elif "adult" in group:
if age and nutrient == "P":
# check for phosphorus in young adults
if r20_24.match(age):
return 0.01
elif r25_29.match(age):
return 0.005
elif r20_29.match(age):
return 0.0075
if "young" not in group:
return 0.
if "young" not in group:
return 0.
# return proper values for kids and young adults
filters = kwargs.copy()
filters["compound"] = nutrient
filters["flow"] = "retention"
df = _filter_dataframe(nutrients_flow_fractions, filters)
frac = 0.01*return_values(df, "amount", 1, ci_value=ci, q=q)
if np.isnan(frac).any() and "age" in filters:
del filters["age"]
filters["group"] = group
df = _filter_dataframe(nutrients_flow_fractions, filters)
frac = 0.01*return_values(df, "amount", 1, ci_value=ci, q=q)
return frac
[docs]
@auto_format
def fraction_nutrient_excreta(
nutrient: str,
excreta: Literal["urine", "feces", "all"] = "all",
ci: int = 0,
q: Optional[NumericArrayLike] = None,
**kwargs
) -> NumericOrArray:
r'''
Fraction of the ingested nutrients that is excreted in urine and feces.
Other pathways notably include sweat, breathing, tegumen losses, and
retention in bones and muscles before adulthood.
.. math::
f_{\text{excreta}}^{(n)} = 1 - f_{\text{other losses}}^{(n)} -
f_{\text{retention}}^{(n)}
Parameters
----------
nutrient : str
The nutrient of interest.
ci : int (optional, default: 0)
Confidence interval (CI). If non-zero, the function will also return
the low and high expected values corresponding to that CI.
q : array-like of floats (optional: default: None)
Percentiles to compute (supersedes `ci`).
**kwargs : arguments to use a subset of the database
Additional arguments can be use to restrict the results to a subset of
the full database. E.g. one can specify an `age` or a `group`.
Returns
-------
A float in [0, 1) giving the fraction of the daily nutrient intake that
ends up in excreta (urine and feces combined).
'''
if excreta == "all":
frac: NumericOrArray = 1.
if nutrient == "K":
# approximately 20% loss in sweat
frac = 0.8
elif nutrient == "N":
# tegumen, sweat, and breathing losses
frac = 0.95
# subtract retention from bone and muscle accumulation
frac -= fraction_nutrient_retention(nutrient, ci=ci, q=q, **kwargs)
return frac
group = kwargs.get("group", "")
age = kwargs.get("age")
if not group and age:
group = _group_from_age(age)
# return proper values for kids and young adults
filters = kwargs.copy()
filters["compound"] = nutrient
filters["flow"] = excreta
df = _filter_dataframe(nutrients_flow_fractions, filters)
frac = 0.01*return_values(df, "amount", 1, ci, q)
if np.isnan(frac).any() and "age" in filters:
del filters["age"]
filters["group"] = group
df = _filter_dataframe(nutrients_flow_fractions, filters)
frac = 0.01*return_values(df, "amount", 1, ci, q)
return frac