nutripy/diet.py
from .nutripy import Nutripy, Goal, Gender, Activity
from .config import *
import numpy as np
from enum import Enum
from scipy.ndimage.filters import uniform_filter1d
def is_close(a, b, close=150):
if abs(a - b) < close:
return True
return False
class Phase(Enum):
LOSS = 0
STOP = 1
MAINTAINANCE = 2
GAIN = 3
def gain(x):
if x > 0.5:
return 0
if x <= 0.5:
return CALORIES_STEP_SIZE
def maintain(x):
if x > -0.5 and x < 0.5:
return 0
if x > 0.5:
return -CALORIES_STEP_SIZE
if x < 0.5:
return CALORIES_STEP_SIZE
def loss(x):
if x < -0.5:
return 0
if x > -0.5:
return -CALORIES_STEP_SIZE
def get_new_state(age, height, gender, activity, goal, weight_history, phases_history, tdci, tdee):
assert len(weight_history) == len(phases_history), \
"weight history should have the same length as phase history"
nut = Nutripy()
delta_cal = 0
new_weight = weight_history[-1]
current_phase = phases_history[-1]
duration = 1 # counts the number of weeks within the phase
for i in range(1, len(phases_history)):
if phases_history[-i-1] == current_phase:
duration += 1
else:
break
if goal == Goal.GAIN:
if current_phase == Phase.GAIN and duration <= 16:
new_phase = Phase.GAIN
if current_phase == Phase.GAIN and duration > 16:
new_phase = Phase.STOP
duration = 1
elif goal == Goal.LOSS:
if current_phase == Phase.LOSS and duration <= 16:
new_phase = Phase.LOSS
if current_phase == Phase.LOSS and duration > 16:
new_phase = Phase.STOP
duration = 1
elif goal == Goal.MAINTAIN:
pass # should be pass since everything is handled below for this trivial goal
else:
raise NotImplementedError
if current_phase == Phase.STOP:
if is_close(tdee, tdci, close=200):
new_phase = Phase.MAINTAINANCE
else:
new_phase = Phase.STOP
#phases_history.append(new_phase)
if current_phase == Phase.MAINTAINANCE:
new_phase = Phase.MAINTAINANCE # todo: renew main Phase or do another goal
print(current_phase)
if new_phase == Phase.MAINTAINANCE:
if len(weight_history) > 3:
weight_derivative = np.gradient(weight_history)
y = uniform_filter1d(weight_derivative, size=3)
delta_cal = maintain(y[-1])
if goal == Goal.GAIN:
if new_phase == Phase.GAIN:
if len(weight_history) > 3:
weight_derivative = np.gradient(weight_history)
y = uniform_filter1d(weight_derivative, size=3)
delta_cal = gain(y[-1])
if new_phase == Phase.STOP:
if duration == 1:
tdee = nut.get_daily_needs(age, new_weight, height, gender, activity, goal)
print(tdee)
if not is_close(tdee, tdci, close=200):
delta_cal = -CALORIES_STEP_SIZE
elif goal == Goal.LOSS:
if new_phase == Phase.LOSS:
if len(weight_history) > 3:
weight_derivative = np.gradient(weight_history)
y = uniform_filter1d(weight_derivative, size=3)
delta_cal = loss(y[-1])
if new_phase == Phase.STOP:
if duration == 1:
tdee = nut.get_daily_needs(age, new_weight, height, gender, activity, goal)
if not is_close(tdee, tdci, close=200):
delta_cal = CALORIES_STEP_SIZE
elif goal == Goal.MAINTAIN:
pass # should be pass since everything is handled above for this trivial goal
else:
raise NotImplementedError
return {
"phase": new_phase,
"tdci": tdci + delta_cal,
"tdee": tdee
}
if __name__ == "__main__":
def delta_weight(tdci, tdee, sigma=.6):
return np.random.normal(0, sigma) + ((tdci - tdee) * 7) / 7000
nut = Nutripy()
params = {
"age": 30,
"height": 180,
"gender": Gender.MALE,
"activity": Activity.SEDENTARY,
"goal": Goal.GAIN,
"weight_history": [80, 80, 80.5, 80],
"phases_history":[Phase.GAIN, Phase.GAIN, Phase.GAIN, Phase.GAIN],
"tdci": nut.get_daily_needs(30, 80, 180, Gender.MALE, Activity.SEDENTARY, Goal.GAIN),
"tdee": nut.get_daily_needs(30, 80, 180, Gender.MALE, Activity.SEDENTARY, Goal.GAIN),
}
for i in range(20):
state = get_new_state(**params)
new_phase = state["phase"]
new_tdci = state["tdci"]
new_tdee = state["tdee"]
tdci = params["tdci"]
tdee = params["tdee"]
delta_w = delta_weight(tdci, tdee)
new_weight = params["weight_history"][-1] + delta_w
params["phases_history"].append(new_phase)
params["weight_history"].append(new_weight)
params["tdci"] = new_tdci
params["tdee"] = new_tdee