Índices nutricionales por productos, por países y por continentes Enunciado Del url http://world.openfoodfacts.org/data se puede descargar la tabla de datos FoodFacts.csv que contiene información sobre más de 65,000 productos alimentarios: en concreto, 156 variables, de las que 87 corresponden a contenidos nutricionales. La tabla completa ocupa más de 160 Mb (y además va aumentando casi cada día), así que para este ejercicio (en MiriadaX no nos dejan subir tablas de más de 5 Mb), a partir de esta tabla hemos construido una tabla con sólo algunos productos y las siguientes variables: • product_name: nombre del producto • country: país donde se adquirió (cuando un producto ha sido adquirido en varios países, aparece una fila para cada país) • continent: continente del país • nutrition_grade_fr: escala nutricional francesa • additives_n: número de aditivos • main_category: categoría • energy: calorías por 100 g del producto • fat: grasa (en g) por 100 g del producto • sugars: azúcares (en g) por 100 g del producto • fiber: fibra (en g) por 100 g del producto • proteins: proteínas (en g) por 100 g del producto • sodium: sal (en g) por 100 g del producto • alcohol: alcohol (en g) por 100 g del producto • vitamin_b6 : vitamina B6 (en g) por 100 g del producto El resultado final es la tabla FoodFactsMooc.csv que encontraréis en el repositorio del curso (url https:// miriadax.net/documents/28098821/74010125/FoodFactsMooc.csv/c1b38463-6006-4a3b-b94a-a72d31d54831) A partir de esta tabla, vamos a estudiar algunos índices nutricionales por productos, por países y por continentes. a) Cargad la tabla de datos en un data frame “global” llamado DF_G; a continuación, cread un data frame para cada continente, y llamadlos DF_Europa, DF_Africa, DF_Asia, DF_AmericaN, DF_AmericaS, DF_Oceanía, respectivamente En algunos apartados necesitaremos eliminar repeticiones de productos de estos data frames. Un mismo producto puede aparecer varias veces en la tabla de partida, porque haya sido adquirido en diferentes países; si varias filas corresponden exactamente al mismo producto, sólo se diferencian en el país y, si corresponde, en el continente. Así que también tenéis que construir un data frame “global y sin repeticiones” DFU_G, que no contenga las variables correspondientes al país y el continente y donde cada producto aparezca una sola vez. ¿Cuántas repeticiones había en la tabla de datos original? Asimismo, para cada continente, cread un data frame sin repeticiones (DFU_Europa, DFU_Africa, etc., que no contenga la variable correspondiente al país (pero dejad el continente, os va a ser ser útil dentro de un rato), donde cada producto aparezca una sola vez. Finalmente, concatenad por filas los 6 data frames anteriores de la forma DFU_Continente en un único data frame DFU_Continentes. Este data frame contendrá productos repetidos, pero nunca dentro de un mismo continente. (Indicación: Si aplicáis la función unique a un dataframe, construís un nuevo dataframe eliminando las repeticiones de filas.) 1
b) ¿Qué porcentajes de valores NA hay en las diferentes variables del data frame DFU_G? Sería conveniente que presentaseis esta información en forma de tabla. ¿Qué variables presentan un mayor porcentaje de valores NA? ¿Tenéis alguna explicación para ello? c) Calculad las correlaciones entre las variables numéricas del dataframe DFU_G (sus últimas 8 variables). Escoged la opción use que produzca menos NA. Opcionalmente, representad los valores absolutos de estas correlaciones mediante un diagrama de calor. A continuación: 1. Si lo habéis hecho bien, aparecerá una sola pareja de variables con correlación NA. ¿A qué se debe este valor? 2. Determinad el par de variables numéricas diferentes con mayor correlación en valor absoluto, y dibujad su diagrama de dispersión incluyendo su recta de regresión. d) El Actimel de Danone “ayuda al funcionamiento normal del sistema inmunitario” porque contiene 0.21 mg de vitamina B6 por cada 100 g. ¿Qué porcentaje de los productos de los que en la tabla DFU_G se indica su cantidad de vitamina B6, tienen como mínimo tanta vitamina B6 por cada 100 g como el Actimel? ¿Qué 5 productos de esta tabla tienen la mayor cantidad de vitamina B6 por cada 100 g? e) Producid un gráfico que muestre, para cada continente, los diagramas de caja de las cantidades de azúcar en 100g en los productos adquiridos en el continente; y lo mismo para las cantidades de sal, de grasa y el número de aditivos. Usad el dataframe sin repeticiones dentro de continentes DFU_Continentes. ¿Se observan diferencias entre los consumos en los continentes? Comentadlas. f) Vamos a fijarnos ahora en las bebidas alcohólicas (aquellas que tengan valor “Beverage” en la variable main_category y contenido de alcohol mayor que 0). Usando los dataframes sin repeticiones DFU_G y DFU_Continentes, dibujad histogramas del contenido de alcohol en estas bebidas: uno global, y uno para cada continente. Procurad que los grupos sean los mismos en cada histograma (aunque algunos queden vacíos), para poder compararlos mejor. Poned nombres adecuados a los histogramas. ¿Se observan diferencias entre los continentes? ¿Qué continente presenta una distribución del contenido de alcohol en sus bebidas alcohólicas más parecido al global? ¿Se os ocurre por qué? g) La variable nutrition_grade_fr es un factor con niveles a,b,c,d,e. Hay otro nivel, vacío, para los productos sin categoría (correspondería al NA). Usando el data frame DFU_Continente, dibujad un diagrama de barras que muestre, para cada continente, el porcentaje de productos en cada categoría. No incluyáis los productos sin categoría asignada. h) A partir del dataframe DFU_G, dibujad un gráfico que contenga los diagramas de caja de los contenidos de azúcar por 100 g de los alimentos de cada nivel de la variable nutrition_grade_fr. Repetid este gráfico para los contenidos de sal, grasa, fibra y proteínas. A partir de estos gráficos, ¿podéis interpretar cómo clasifica esta variable los alimentos? Si queréis, podéis confirmar si vuestra interpretación va en la dirección correcta consultando el informe oficial (en francés) sobre la clasificación nutricional francesa.
Respuestas a)
# DF_G = read.csv("https://miriadax.net/documents/28098821/74010125/FoodFactsMooc.csv/c1b38463-6006-4a3b DF_G = read.csv("FoodFactsMooc.csv") DF_Europa = DF_G[DF_G$continent == "Europe",] DF_Africa = DF_G[DF_G$continent == "Africa",] DF_Asia = DF_G[DF_G$continent == "Asia",] DF_AméricaN = DF_G[DF_G$continent == "North America",] DF_AméricaS = DF_G[DF_G$continent == "South America",] DF_Oceania = DF_G[DF_G$continent == "Oceania",]
2
DF_sinpais = subset(DF_G, select = c(-2, -3)) DFU_G = unique(DF_sinpais) DFU_Europa = unique(subset(DF_Europa, select = -2)) DFU_Asia = unique(subset(DF_Asia, select = -2)) DFU_Africa = unique(subset(DF_Africa, select = -2)) DFU_AméricaN = unique(subset(DF_AméricaN, select = -2)) DFU_AméricaS = unique(subset(DF_AméricaS, select = -2)) DFU_Oceania = unique(subset(DF_Oceania, select = -2)) DFU_Continente = rbind(DFU_Europa, DFU_Africa, DFU_Asia, DFU_AméricaN, DFU_AméricaS, DFU_Oceania) diff.unicos = nrow(DF_G) - nrow(DFU_G) En la tabla global original hay 1529 productos repetidos repetidos. b) library(printr) variables = colnames(DFU_G) # Esta función en realidad no me vale porque no se puede comprar con NA prop.var = function (df, var, value) { v = df[,var] length(v[v==value]) / nrow(df) } prop.na = function (df, var) { v = df[,var] length(v[is.na(v)]) / nrow(df) } prop.var.df.na = function (var) { prop.na(DFU_G, var); } proporciones = sapply(variables, FUN=prop.var.df.na ) varpropna = data.frame(variables, paste(round(100*proporciones,2),"%")) colnames(varpropna) = c("Variable", "Proporción de NA") varpropna
Variable
Proporción de NA
product_name nutrition_grade_fr additives_n main_category energy fat sugars fiber proteins
0% 0% 6.99 % 0% 5.66 % 5.69 % 15.36 % 41.39 % 6.8 % 3
Variable
Proporción de NA
sodium alcohol vitamin_b6
14.68 % 94.02 % 98.19 %
var.maxna = variables [which(proporciones==max(proporciones))] La variable con más NA es vitamin_b6. Observando la variable, veo que no existen valores 0. Así que mi explicación es que cuando un alimento no contiene vitamin_b6, en vez de ponerse a 0, se pone a NA. Por tanto, pienso que vitamin_b6 es el nutriente menos común entre los alimentos de la tabla. c) DFU_G.num = subset(DFU_G, select=5:12) cor.DFU_G = cor(DFU_G.num, use="pairwise.complete.obs" ) cor.DFU_G
energy fat sugars fiber proteins sodium alcohol vitamin_b6
energy
fat
sugars
fiber
proteins
sodium
alcohol
vitamin_b6
1.0000000 0.7865891 0.3499862 0.2813207 0.2339195 -0.0208229 -0.0469585 -0.0134926
0.7865891 1.0000000 0.0214386 0.0901281 0.1521627 0.0064701 -0.0861346 -0.0293793
0.3499862 0.0214386 1.0000000 0.0616294 -0.2317457 -0.0945043 -0.0274759 -0.0456298
0.2813207 0.0901281 0.0616294 1.0000000 0.2256614 -0.0243331 -0.0683830 -0.0332359
0.2339195 0.1521627 -0.2317457 0.2256614 1.0000000 0.0836769 -0.1160096 0.0553701
-0.0208229 0.0064701 -0.0945043 -0.0243331 0.0836769 1.0000000 0.0301064 -0.0199524
-0.0469585 -0.0861346 -0.0274759 -0.0683830 -0.1160096 0.0301064 1.0000000 NA
-0.0134926 -0.0293793 -0.0456298 -0.0332359 0.0553701 -0.0199524 NA 1.0000000
heatmap(abs(cor.DFU_G), Rowv=NA, Colv=NA, revC=TRUE)
4
energy fat sugars fiber proteins sodium alcohol
vitamin_b6
alcohol
sodium
proteins
fiber
sugars
fat
energy
vitamin_b6
1. El alcohol y la vitamina B6 tienen correlación NA. Las únicas observaciones en común tienen 0 como valor de alcohol, haciendo imposible hacer ninguna correlación. 2. # A ojo parece que la energía y la grasa, pero me aseguraré which(cor.DFU_G ==max(abs(cor.DFU_G[cor.DFU_G<1]), na.rm=TRUE)) ## [1] 2 9 Me da los elementos 2 y 9, que en dos dimensiones se corresponden con el (1,2) y el (2,1). Es decir, efectivamente la grasa y la energía. Curiosamente, el azúcar y la energía no tienen una correlación tan fuerte como yo pensaba, ya que según lo que tenía entendido pensé que la correlación sería casi lineal. ## Parece más lógico representar la energía en función de la grasa que al revés plot(DFU_G$fat, DFU_G$energy, xlab="Grasa", ylab="Energía", main="Correlación entre grasa y energía", col="darkorange") abline( lm(DFU_G$energy~DFU_G$fat), col="purple", lwd="2" )
5
2000 0
1000
Energía
3000
4000
Correlación entre grasa y energía
0
20
40
60
80
100
Grasa d) # Hay que quitar los NA, que según mi interpretación anterior equivale a 0 proporcion.mejoresb6 = nrow ( DFU_G[DFU_G$vitamin_b6 >= 0.00021 & !is.na(DFU_G$vitamin_b6),] ) / nrow(DFU_G) proporcion.mejoresb6.nona= nrow ( DFU_G[DFU_G$vitamin_b6 >= 0.00021 & !is.na(DFU_G$vitamin_b6),] ) / nrow(DFU_G[!is.na(DFU_G$vitamin_b6),]) Hay un 1.6410531% de alimentos al menos tan ricos en vitamina B6 como el Actimel. Ahora bien, de entre los alimentos que sabemos que contienen vitamina B6, un 90.4270987% contienen al menos lo mismo que el Actimel. Los productos con mayor cantidad de vitamina B6 son: head(DFU_G[with(DFU_G, order(-vitamin_b6)), c("product_name", "vitamin_b6")], n=5) product_name 26883 55658 3299 47915 24641
vitamin_b6
Levure de bière Vitalité Beauté Zanahorias “VegaTajo” Pop Tarts Hot Fudge Sundae bcaa caps Levure de bière en paillettes
e)
6
0.2150 0.2000 0.0208 0.0140 0.0069
boxplot( DFU_Continente[DFU_Continente$continent=="Europe" ,]$sugars, DFU_Continente[DFU_Continente$continent=="Africa" ,]$sugars, DFU_Continente[DFU_Continente$continent=="Asia" ,]$sugars, DFU_Continente[DFU_Continente$continent=="North America" ,]$sugars, DFU_Continente[DFU_Continente$continent=="South America" ,]$sugars, DFU_Continente[DFU_Continente$continent=="Oceania" ,]$sugars, main="Productos con azúcar", names=c("Europa", "África", "Asia", "N. Amér.", "S. Amér", "Oceanía"), col=(c("Blue", "Brown", "Green", "Yellow", "Red", "Purple")), outline=FALSE )
0
10
20
30
40
50
60
Productos con azúcar
Europa
África
Asia
N. Amér.
S. Amér
Oceanía
boxplot( DFU_Continente[DFU_Continente$continent=="Europe" ,]$sodium, DFU_Continente[DFU_Continente$continent=="Africa" ,]$sodium, DFU_Continente[DFU_Continente$continent=="Asia" ,]$sodium, DFU_Continente[DFU_Continente$continent=="North America" ,]$sodium, DFU_Continente[DFU_Continente$continent=="South America" ,]$sodium, DFU_Continente[DFU_Continente$continent=="Oceania" ,]$sodium, main="Productos con sal", names=c("Europa", "África", "Asia", "N. Amér.", "S. Amér", "Oceanía"), col=(c("Blue", "Brown", "Green", "Yellow", "Red", "Purple")), outline=FALSE)
7
0.0
0.4
0.8
1.2
Productos con sal
Europa
África
Asia
N. Amér.
S. Amér
Oceanía
boxplot( DFU_Continente[DFU_Continente$continent=="Europe" ,]$fat, DFU_Continente[DFU_Continente$continent=="Africa" ,]$fat, DFU_Continente[DFU_Continente$continent=="Asia" ,]$fat, DFU_Continente[DFU_Continente$continent=="North America" ,]$fat, DFU_Continente[DFU_Continente$continent=="South America" ,]$fat, DFU_Continente[DFU_Continente$continent=="Oceania" ,]$fat, main="Productos con grasa", names=c("Europa", "África", "Asia", "N. Amér.", "S. Amér", "Oceanía"), col=(c("Blue", "Brown", "Green", "Yellow", "Red", "Purple")), outline=FALSE )
8
0
10 20 30 40 50 60 70
Productos con grasa
Europa
África
Asia
N. Amér.
S. Amér
Oceanía
boxplot( DFU_Continente[DFU_Continente$continent=="Europe" ,]$additives_n, DFU_Continente[DFU_Continente$continent=="Africa" ,]$additives_n, DFU_Continente[DFU_Continente$continent=="Asia" ,]$additives_n, DFU_Continente[DFU_Continente$continent=="North America" ,]$additives_n, DFU_Continente[DFU_Continente$continent=="South America" ,]$additives_n, DFU_Continente[DFU_Continente$continent=="Oceania" ,]$additives_n, main="Productos con aditivos", names=c("Europa", "África", "Asia", "N. Amér.", "S. Amér", "Oceanía"), col=(c("Blue", "Brown", "Green", "Yellow", "Red", "Purple")), outline=FALSE )
9
0
2
4
6
8
Productos con aditivos
Europa
África
Asia
N. Amér.
S. Amér
Oceanía
Para empezar, me salen gráficos de cajas con muchos puntos atípicos por encima. Pienso que es porque algunos productos tienen mucha más cantidad de esos nutrientes que los valores normales. Por otro lado, se aprecia que los continentes donde se toman alimentos más calóricos son África y América del Norte. Este último se corresponde con la percepción común que se tiene en España sobre los EEUU (no sé si será cierto, pero los diagramas lo confirmarían). En cuanto a África no sé por qué será. f) DFU_G_Bev = DFU_G[DFU_G$main_category=="Beverages" & !is.na(DFU_G$alcohol) & DFU_G$alcohol!=0,] DFU_Continente_Bev = DFU_Continente[DFU_Continente$main_category=="Beverages" & !is.na(DFU_Continente$alcohol)& DFU_Continente$alcohol!=0,] precision = 0.1 # Deducido de la observación de los datos calcula.amplitud = function (vector, nintervalos, precision) { (max(vector) -min(vector) + precision)/nintervalos } calcula.nintervalos = function(vector) { nclass.FD(vector) } calcula.intervalo = function (min, max, amplitud, intervalo, nintervalos, precision) { inicio.vector = min - 0.5 * precision inicio.intervalo = inicio.vector + (intervalo-1) * amplitud fin.intervalo = inicio.vector + (intervalo) * amplitud c( inicio.intervalo, fin.intervalo) } calcula.intervalos = function (vector, nintervalos, amplitud, precision) { 10
min(vector)-0.5*precision + amplitud*(0:nintervalos) } calcula.marca = function (min, max, intervalo, nintervalos, precision, amplitud) { inicio.vector = min - 0.5 * precision inicio.intervalo = inicio.vector + (intervalo-1) * amplitud fin.intervalo = inicio.vector + (intervalo) * amplitud (inicio.intervalo + fin.intervalo) / 2 } calcula.marcas = function (min, max, nintervalos, precision, amplitud) { calcula.marca(min, max, 1:nintervalos, nintervalos, precision, amplitud) } histograma = function (vector, nintervalos, amplitud, titulo) { if (nintervalos<0) { nintervalos = calcula.nintervalos(vector) } if (amplitud<0) { amplitud = calcula.amplitud(vector, nintervalos, precision) } intervalos = calcula.intervalos(vector, nintervalos, amplitud, precision) hist(vector, breaks=intervalos, right=FALSE, xlab="Alcohol", ylab="Frecuencia", main=titulo, col="darkblue") } histograma(DFU_G_Bev$alcohol, -1, -1, "Histograma general")
200 100 50 0
Frecuencia
300
Histograma general
0
10
20
30
40
Alcohol
11
50
60
70
# Calcular los valores generales para reutilizarlos nintervalos = calcula.nintervalos (DFU_G_Bev$alcohol) amplitud = calcula.amplitud (DFU_G_Bev$alcohol, nintervalos, precision) histograma(DFU_Continente_Bev[DFU_Continente_Bev$continent=="Europe",]$alcohol, nintervalos, amplitud, "Histograma de Europa")
100 150 200 250 300 0
50
Frecuencia
Histograma de Europa
0
10
20
30
40
50
60
Alcohol histograma(DFU_Continente_Bev[DFU_Continente_Bev$continent=="Africa",]$alcohol, nintervalos, amplitud, "Histograma de Ă frica")
12
70
2.0 1.5 1.0 0.0
0.5
Frecuencia
2.5
3.0
Histograma de Ă frica
10
20
30
40
50
60
70
Alcohol histograma(DFU_Continente_Bev[DFU_Continente_Bev$continent=="Asia",]$alcohol, nintervalos, amplitud, "Histograma de Asia")
3 2 1 0
Frecuencia
4
5
Histograma de Asia
10
20
30
40 Alcohol
13
50
60
70
histograma(DFU_Continente_Bev[DFU_Continente_Bev$continent=="North America",]$alcohol, nintervalos, amplitud, "Histograma de Norteamérica")
8 6 0
2
4
Frecuencia
10
12
Histograma de Norteamérica
10
20
30
40
50
60
70
Alcohol histograma(DFU_Continente_Bev[DFU_Continente_Bev$continent=="South America",]$alcohol, nintervalos, amplitud, "Histograma de Sudamérica")
14
0.6 0.4 0.0
0.2
Frecuencia
0.8
1.0
Histograma de Sudamérica
10
20
30
40
50
60
70
Alcohol histograma(DFU_Continente_Bev[DFU_Continente_Bev$continent=="Oceania",]$alcohol, nintervalos, amplitud, "Histograma de Oceanía")
0.6 0.4 0.0
0.2
Frecuencia
0.8
1.0
Histograma de Oceanía
10
20
30
40
50
60
70
Alcohol El histograma más parecido es el de Europa. ¿Por qué? Pues porque es el que contiene más datos. Mientras Europa tiene 1352 observaciones con alcohol que no sean NA, Norteamérica tiene 42, Africa 3 y Oceanía sólo 15
1. g) DFU_Continente.nona = DFU_Continente[!is.na(DFU_Continente$nutrition_grade_fr) & DFU_Continente$nutrition_grade_fr != "",] # Quitamos el nivel "", no importa a cuál lo asignemos porque no tiene valores levels(DFU_Continente.nona$nutrition_grade_fr) = c("a", "a", "b", "c", "d", "e") barplot( prop.table(table(DFU_Continente.nona$nutrition_grade_fr, DFU_Continente.nona$continent), margin=2), beside=TRUE, legend.text=TRUE, names=c("África", "Ásia", "Europa", "N. Amér.", "Oceanía", "S. Amér."), col=c("Red", "Green", "Blue", "Yellow", "Magenta"))
0.00
0.10
0.20
0.30
a b c d e
África
Ásia
Europa
N. Amér.
h) boxplot( DFU_G[DFU_G$nutrition_grade_fr=="a" ,]$sugars, DFU_G[DFU_G$nutrition_grade_fr=="b" ,]$sugars, DFU_G[DFU_G$nutrition_grade_fr=="c" ,]$sugars, DFU_G[DFU_G$nutrition_grade_fr=="d" ,]$sugars, DFU_G[DFU_G$nutrition_grade_fr=="e" ,]$sugars, main="Productos con azúcar", names=c("a", "b", "c", "d", "e"), col=c("Red", "Green", "Blue", "Yellow", "Magenta"), outline=FALSE)
16
Oceanía
S. Amér.
0
20
40
60
80
Productos con azĂşcar
a
b
c
d
boxplot( DFU_G[DFU_G$nutrition_grade_fr=="a" ,]$sodium, DFU_G[DFU_G$nutrition_grade_fr=="b" ,]$sodium, DFU_G[DFU_G$nutrition_grade_fr=="c" ,]$sodium, DFU_G[DFU_G$nutrition_grade_fr=="d" ,]$sodium, DFU_G[DFU_G$nutrition_grade_fr=="e" ,]$sodium, main="Productos con sal", col=c("Red", "Green", "Blue", "Yellow", "Magenta"), outline=FALSE)
0.0
0.5
1.0
1.5
Productos con sal
17
e
boxplot( DFU_G[DFU_G$nutrition_grade_fr=="a" ,]$fiber, DFU_G[DFU_G$nutrition_grade_fr=="b" ,]$fiber, DFU_G[DFU_G$nutrition_grade_fr=="c" ,]$fiber, DFU_G[DFU_G$nutrition_grade_fr=="d" ,]$fiber, DFU_G[DFU_G$nutrition_grade_fr=="e" ,]$fiber, main="Productos con fibra", col=c("Red", "Green", "Blue", "Yellow", "Magenta"), outline=FALSE)
0
2
4
6
8
Productos con fibra
boxplot( DFU_G[DFU_G$nutrition_grade_fr=="a" ,]$fat, DFU_G[DFU_G$nutrition_grade_fr=="b" ,]$fat, DFU_G[DFU_G$nutrition_grade_fr=="c" ,]$fat, DFU_G[DFU_G$nutrition_grade_fr=="d" ,]$fat, DFU_G[DFU_G$nutrition_grade_fr=="e" ,]$fat, main="Productos con grasa", col=c("Red", "Green", "Blue", "Yellow", "Magenta"), outline=FALSE)
18
0
10
20
30
40
50
60
Productos con grasa
Mi suposición fue que las categorías representan el valor energético del alimento, ya que las categorías más altas tienen mayores valores de azúcares y grasas. No obstante, según el artículo enlazado, parece que se trata de una clasificación que estimaría lo saludable del alimento.
19