{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE)

Introducción

Este documento ejecuta un modelo de programación lineal (PL) para generar un plan de alimentación personalizado utilizando el Sistema Mexicano de Alimentos Equivalentes (SMAE).
El modelo considera requerimientos energéticos, distribución de macronutrientes y límites por categoría alimentaria.


Cargar librerías y datos

library(lpSolve)
## Warning: package 'lpSolve' was built under R version 4.3.3
library(readr)
library(tidyverse)
## Warning: package 'tidyr' was built under R version 4.3.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.2     ✔ purrr     1.0.2
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.5.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.2     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(stringr)
library(dplyr)

# --- Cargar archivos ---
smae <- read_csv("C:/Users/geova/Downloads/smae1.csv") 
## Rows: 2871 Columns: 27
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (20): Alimento, Categoria, Unidad, AG saturados (g), AG monoinsaturados ...
## dbl  (7): Cantidad, Peso bruto, g, Peso neto, g, Energia, Proteinas, Lipidos...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
smae_reducido <- read_csv("C:/Users/geova/OneDrive/Documentos/curso de titulacion/PROYECTO FINAL/smae_reducido_macronutrientes1.csv")
## Rows: 17 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): Grupo
## dbl (4): Energia, Proteina, Lipidos, Carbohidratos
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
df_principal <- smae
df_secundario <- smae_reducido

Calculo de calorías necesarias

genero <- 0 # 0 hombre, 1 mujer
edad <- 25
peso <- 85
altura <- 180
ejercicio <- 1 # 0 nada, 1 ligero, 2 moderado, 3 fuerte, 4 muy fuerte

altura_m <- altura/100
IMC <- peso/(altura_m*altura_m)

if(IMC>24.9){
  peso <- 24.9*(altura_m*altura_m)
}
if(IMC<18.5){
  peso <- 18.5*(altura_m*altura_m)
}

ifelse(genero == 0,
       TMB <- 66.4730 + (13.7516*peso) + (5.0033*altura) - (6.7550*edad),
       TMB <- 655.0955 + (9.5634*peso) + (1.8449*altura) - (4.6756*edad)
)
## [1] 1907.616
factor_act <- c(1.2, 1.375, 1.55, 1.725, 1.9)
calorias_necesarias <- TMB * factor_act[ejercicio]
calorias_necesarias
## [1] 2289.139

Requerimientos nutricionales

objetivo_calorias <- calorias_necesarias
objetivo_proteinas_min <- (calorias_necesarias * 0.1)/4
objetivo_proteinas_max <- (calorias_necesarias * 0.15)/4
objetivo_carbohidratos_min <- (calorias_necesarias * 0.55)/4
objetivo_carbohidratos_max <- (calorias_necesarias * 0.60)/4
objetivo_grasas_min <- (calorias_necesarias * 0.2)/9
objetivo_grasas_max <- (calorias_necesarias * 0.25)/9

Mapeo de categorías del SMAE

mapa_categorias <- list(
  "Verduras" = c("Verduras"),
  "Frutas" = c("Frutas"),
  "Cereales" = c("Cereales S/G", "Cereales C/G"),
  "Leguminosas" = c("Leguminosas"),
  "AOA" = c("AOA MBAG", "AOA BAG", "AOA MAG", "AOA AAG"),
  "Leche" = c("Leche Entera", "Leche Descremada", "Leche Semidescremada", "Leche con azucar"),
  "Grasas" = c("Grasas con proteina", "Grasas sin proteina"),
  "Azucares sin grasa"=c("Azucares sin grasa"),
  "Azucares con grasa"=c("Azucares con grasa")
)

df_secundario$CategoriaGeneral <- NA
for (cat in names(mapa_categorias)) {
  subcats <- mapa_categorias[[cat]]
  df_secundario$CategoriaGeneral[df_secundario$Grupo %in% subcats] <- cat
}
df_secundario$costo <- c(rep(1,17))

Variables del modelo

cal <- df_secundario$Energia
pro <- df_secundario$Proteina
carb <- df_secundario$Carbohidratos
gras <- df_secundario$Lipidos
costo <- df_secundario$costo

Límites por categoría

limites_categorias <- data.frame(
  Categoria = names(mapa_categorias),
  Min = c(2, 1, 2, 1, 2, 1, 1, 1, 1), #Verduras,Frutas,Cereales,Leguminosas,AOA,Leche,
  Max = c(5, 5, 5, 7, 5, 2, 5, 5, 5)  #Grasas,AzúcaresS/G,AzúcaresC/G
)

matriz_cat <- list()
dir_cat <- c()
rhs_cat <- c()

for (i in seq_len(nrow(limites_categorias))) {
  cat <- limites_categorias$Categoria[i]
  fila <- as.numeric(df_secundario$CategoriaGeneral == cat)

  if (!is.na(limites_categorias$Min[i]) && limites_categorias$Min[i] > 0) {
    matriz_cat <- append(matriz_cat, list(fila))
    dir_cat <- c(dir_cat, ">=")
    rhs_cat <- c(rhs_cat, limites_categorias$Min[i])
  }

  if (!is.na(limites_categorias$Max[i]) && limites_categorias$Max[i] > 0) {
    matriz_cat <- append(matriz_cat, list(fila))
    dir_cat <- c(dir_cat, "<=")
    rhs_cat <- c(rhs_cat, limites_categorias$Max[i])
  }
}

matriz_cat <- do.call(rbind, matriz_cat)

Construcción del modelo

matriz <- rbind(
  cal,  # min calorías
  cal, #max calorias
  pro,  # min proteínas
  pro, #max proteinas
  carb, # min carbohidrato
  carb, #max carbohidratos
  gras, # min grasas
  gras, #max carbohidratos
  matriz_cat
)

direcciones <- c(
  "<=", # min calorías
  ">=", # max calorias
  ">=", # min proteínas
  "<=", # max proteinas
  ">=", # min carbohidratos
  "<=", # max carbohidratos
  ">=", # min grasas
  "<=", #max grasas
  dir_cat
)

rhs <- c(
  1.05*objetivo_calorias,
  0.95*objetivo_calorias,
  objetivo_proteinas_min,
  objetivo_proteinas_max,
  objetivo_carbohidratos_min,
  objetivo_carbohidratos_max,
  objetivo_grasas_min,
  objetivo_grasas_max,
  rhs_cat
)

Solución del modelo

resultado <- lp(
  direction = "min",
  objective.in = costo,
  const.mat = matriz,
  const.dir = direcciones,
  const.rhs = rhs,
  all.int = TRUE
)

Resultados

if (resultado$status == 0) {
  cat("Solución óptima encontrada:\n\n")

  solucion <- resultado$solution

  seleccion <- data.frame(
    CategoriaGeneral = df_secundario$CategoriaGeneral,
    Porciones = solucion,
    Calorias = cal * solucion,
    Proteinas = pro * solucion,
    Carbohidratos = carb * solucion,
    Grasas = gras * solucion
  ) %>% filter(Porciones > 0)

  print(seleccion)

  cat("\n--- TOTALES NUTRICIONALES ---\n")
  cat(sprintf("Calorías: %.2f (Objetivo: %.2f)\n",
              sum(cal * solucion), objetivo_calorias))
  cat(sprintf("Proteínas: %.2fg (Objetivo: %.2fg - %.2fg)\n",
              sum(pro * solucion), objetivo_proteinas_min, objetivo_proteinas_max))
  cat(sprintf("Carbohidratos: %.2fg (Objetivo: %.2fg - %.2fg)\n",
              sum(carb * solucion), objetivo_carbohidratos_min, objetivo_carbohidratos_max))
  cat(sprintf("Grasas: %.2fg (Objetivo: %.2fg - %.2fg)\n",
              sum(gras * solucion), objetivo_grasas_min, objetivo_grasas_max))

} else {
  cat("No se encontró una solución óptima.\n")
}
## Solución óptima encontrada:
## 
##      CategoriaGeneral Porciones Calorias Proteinas Carbohidratos Grasas
## 1            Verduras         2       50         4             8      0
## 2              Frutas         4      240         0            60      0
## 3            Cereales         5      575        10            75     25
## 4         Leguminosas         5      600        40           100      5
## 5                 AOA         1       75         7             0      5
## 6                 AOA         1      100         7             0      8
## 7               Leche         2      400        16            60     10
## 8              Grasas         1       45         0             0      5
## 9  Azucares sin grasa         1       40         0            10      0
## 10 Azucares con grasa         1       85         0            10      5
## 
## --- TOTALES NUTRICIONALES ---
## Calorías: 2210.00 (Objetivo: 2289.14)
## Proteínas: 84.00g (Objetivo: 57.23g - 85.84g)
## Carbohidratos: 323.00g (Objetivo: 314.76g - 343.37g)
## Grasas: 63.00g (Objetivo: 50.87g - 63.59g)