diff --git a/docs/requirements-examples.txt b/docs/requirements-examples.txt index 9818ab0..fe7a804 100644 --- a/docs/requirements-examples.txt +++ b/docs/requirements-examples.txt @@ -3,6 +3,7 @@ ipykernel>=6.15 jupytext>=1.14 matplotlib>=3.5 nbconvert>=6.5 +openpyxl>=3.0 scikit-learn>=1.0 seaborn==0.13.2 gurobipy>=10.0 diff --git a/docs/requirements.txt b/docs/requirements.txt index 616c7e1..8dfe914 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -10,6 +10,7 @@ myst-parser==0.19.2 nbconvert==7.2.5 nbsphinx==0.8.9 numpydoc==1.6.0 +openpyxl==3.1.5 scikit-learn==1.5.0 seaborn==0.13.2 sphinx-copybutton==0.5.2 diff --git a/docs/source/examples.rst b/docs/source/examples.rst index 834d415..ce693ba 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -14,6 +14,7 @@ the notebooks. .. toctree:: :maxdepth: 1 + examples/diet examples/projects examples/regression examples/unit-commitment diff --git a/docs/source/examples/data/diet.xlsx b/docs/source/examples/data/diet.xlsx new file mode 100644 index 0000000..eb0add4 Binary files /dev/null and b/docs/source/examples/data/diet.xlsx differ diff --git a/docs/source/examples/diet.md b/docs/source/examples/diet.md new file mode 100644 index 0000000..54280d5 --- /dev/null +++ b/docs/source/examples/diet.md @@ -0,0 +1,86 @@ +--- +jupytext: + formats: ipynb,md:myst + text_representation: + extension: .md + format_name: myst + format_version: 0.13 + jupytext_version: 1.14.1 +--- + +# The Stigler Diet Problem + +```{code-cell} +import pandas as pd +import gurobipy as gp +import gurobipy_pandas as gppd +from gurobipy import GRB + +gppd.set_interactive() +``` + +```{code-cell} +foods = pd.read_excel("data/diet.xlsx", sheet_name="Foods") +foods.head() +``` + +```{code-cell} +categories = pd.read_excel("data/diet.xlsx", sheet_name="Categories") +categories.head() +``` + +```{code-cell} +nutrition = pd.melt( + pd.read_excel("data/diet.xlsx", sheet_name="Nutrition"), + id_vars=["food"], + var_name="nutrient", +) +nutrition.head() +``` + +```{code-cell} +env = gp.Env() +model = gp.Model(env=env) +``` + +```{code-cell} +buy = gppd.add_vars(model, foods.set_index("food"), name="buy") +buy.head() +``` + +```{code-cell} +amounts = ( + nutrition.join(buy, on="food") + .set_index(["food", "nutrient"]) + .pipe(lambda df: df["value"] * df["buy"]) + .groupby("nutrient").sum() +) + +limits = categories.set_index("category") + +gppd.add_constrs( + model, + amounts, + GRB.GREATER_EQUAL, + limits["minimum"], +) + +gppd.add_constrs( + model, + amounts[limits["maximum"].notnull()], + GRB.LESS_EQUAL, + limits["maximum"].dropna(), +) +``` + +```{code-cell} +model.setObjective((buy * foods.set_index("food")["cost"]).sum()) +``` + +```{code-cell} +model.optimize() +``` + +```{code-cell} +buy.gppd.X.pipe(lambda s: s[s >0]) +```