library(tidyverse)
library(modelr)
options(na.action = na.warn) # nos indicará si falta algun dato
Para este caso trataremos con el dataset “diamonds”. Información general:
Format: A data frame with 53940 rows and 10 variables. A dataset containing the prices and other attributes of almost 54,000 diamonds. The variables are as follows:
price: price in US dollars ($326–$18,823)
carat: weight of the diamond (0.2–5.01
cut: quality of the cut (Fair, Good, Very Good, Premium, Ideal)
color: diamond colour, from J (worst) to D (best)
clarity: a measurement of how clear the diamond is (I1 (worst), SI2, SI1, VS2, VS1, VVS2, VVS1, IF (best))
x: length in mm (0–10.74)
y: width in mm (0–58.9)
z: depth in mm (0–31.8)
depth: total depth percentage = z / mean(x, y) = 2 * z / (x + y) (43–79)
¿Por qué los diamantes de baja calidad son más caros?
# El precio de los diamantes de peor corte son de media más caros
diamonds %>%
ggplot(aes(cut, price)) +
geom_boxplot()
# Los diamantes con peor color tienen un precio muy elevado (color J es el peor)
diamonds %>%
ggplot(aes(color, price)) +
geom_boxplot()
# La peor claridad parece ser el más caro
diamonds %>%
ggplot(aes(clarity, price)) +
geom_boxplot()
Hipótesis: puede que el peso del diamante sea el que define el precio:
diamonds %>%
ggplot(aes(carat, price)) +
geom_hex(bins = 50) # separados por color según densidad de muestras
Este gráfico nos muestra que la variable carat parece tener una relación muy clara con el precio. Para desarrollar nuestra toería procesaremos los datos, fijándonos solo en los “normales”, y dejando de lado los que se salen de lo “normal”, los que estan más alla de 2’5. Por lo tando filtramos los outliers:
diamonds2 <- diamonds %>%
filter(carat <= 2.5) %>% # quitamos una parte muy pequeña de la información, menos del 3.5% del dataset
mutate(lprice = log2(price), lcarat = log2(carat))# como vemos esta tendencia de crecer de forma exponencial con el kilate, vamos a transformar los datos con el logaritmo en base 2 para buscar así la relación lineal
diamonds2 %>%
ggplot(aes(lcarat, lprice)) +
geom_hex(bins = 50)
Con esta tranformación hemos visto que hay que hay una relación lineal.
NOTA: la tranformación logarítmica es muy útil cuando vemos que la tendencia crece muy rápido tanto en X como en Y, porque este crecimiento acelerado tiende a tranformarse en un patrón lineal, ayudándonos a crear un modelo que sea mas fácil trabajar (un modelo lineal).
Ahora podemos empezar a ver si podemos ajustar un modelo con la función lm():
mod_diamonds <- lm(lprice ~ lcarat, data = diamonds2)
# Que nos dice el modelo?
grid <- diamonds2 %>%
data_grid(carat = seq_range(carat, 30)) %>% # hacmos 30 divisiones
mutate(lcarat = log2(carat)) %>%
add_predictions(mod_diamonds, "lprice") %>%
mutate(price = 2 ^ lprice)
grid
Entendamos la parrilla que hemos hecho:
Tenemos carat en su valor original
lcarat como la transformación del logaritmo de carat
lprice es el la predicción que ha hecho nuestro modelo
price es la predicción final después de elevar a la 2 el la predicción del precio para volver al precio original en dólares
diamonds2 %>%
ggplot(aes(carat, price)) +
geom_hex(bins = 50) +
geom_line(data = grid, color = "red", size = 1)
Este modelo nos dice que los diamantes que son más grandes, son mas baratos de los que realmente uno espera, dado que el máximo de precio no sobrepasa nunca los $20000.
# Añadimos una columna con los residuos
diamonds2 <- diamonds2 %>%
add_residuals(mod_diamonds, "lresid")
diamonds2 %>%
ggplot(aes(lcarat, lresid)) +
geom_hex(bins = 50)
Este modelo nos muestra que más o menos ha eliminado este patrón lineal. Todo está más o menos concentrado en torno al centro. Si que hay un patrón en la derecha del gráfico, pero el color es un azul fuerte, indicando que casi no hay observaciones.
Vamos a ver si el residuo se distribuye de igual forma entre todas las categorías:
diamonds2 %>%
ggplot(aes(cut, lresid)) +
geom_boxplot()
diamonds2 %>%
ggplot(aes(color, lresid)) +
geom_boxplot()
diamonds2 %>%
ggplot(aes(clarity, lresid)) +
geom_boxplot()
Todos estan más o menos alrededor de 0. Ahora podemos ver que la claridad del diamante si que afecta a precio.
Con el análisi de resiudos queremos explicar la parte del modelo que no queda explicada, y en que escala estan. En caso que sea negativo, podemos concluir que la clase por ejemplo, de claridad, es más barata que la que nos predice el modelo, y a la inversa.
Vamos a incluir color, corte y claridad para hacer explícita el efecto de estas tres variables catgóricas en relación al precio del diamante.
mod_diamonds2 <- lm(lprice ~ lcarat + color + cut + clarity,
data = diamonds2)
grid <- diamonds2 %>%
data_grid(cut, .model = mod_diamonds2) %>% # parrilla de usando la variable cut a partir del modelo de mod_diamonds2 para hacer más senzilla la injesta de datos
add_predictions(mod_diamonds2) # le añado la predicción
grid
ggplot(grid, aes(cut, pred)) +
geom_point() +
ggtitle("Modelo respecto el corte")
Tenemos un precio que va en función del corte del diamante, a mejor corte, más precio.
diamonds2 %>%
data_grid(color, .model = mod_diamonds2) %>%
add_predictions(mod_diamonds2) %>%
ggplot(aes(color, pred)) +
geom_point() +
ggtitle("Modelo respecto al color")
Se ve claramente como baja el precio respecto al color.
diamonds2 %>%
data_grid(clarity, .model = mod_diamonds2) %>%
add_predictions(mod_diamonds2) %>%
ggplot(aes(clarity, pred)) +
geom_point() +
ggtitle("Modelo respecto la claridad")
Sube el precio en relación a la claridad.
diamonds2 <- diamonds2 %>%
add_residuals(mod_diamonds2, "lresid2")
ggplot(diamonds2, aes(lcarat, lresid2)) +
geom_hex(bins = 50)
La gran mayoría de valores residules estan en torno a 0, excepto algunos que son muy elevados con una diferencia de 2 puntos. Es decir, que el precio debería ser 4 veces superior al predicho, y a la inversa con los valores negativos.
diamonds2 %>%
filter(abs(lresid2) >1) %>% # filtramos por los residuos mayores que 1 para evaluar los errores
add_predictions(mod_diamonds2) %>% # añadimos las predicciones
mutate(pred = round(2^pred)) %>% # tranformación para ver los precios reales
select(price, pred, cut, carat, cut, color, clarity, x:z) %>% # seleccionames las variables que nos interesan
arrange(price) # ordenamos por el precio
Solucionar la pregunta de si es un problema que el modelo no se adapte a este caso. En el caso del diamante “Fair” con una predicción de un precio de 2644, puede que fuera una buena opción comprarlo, dado que el precio esta por debajo de la predicción.
Con este modelo hemos explicado que no es verdad que los diamantes más malos, más mal cortados y más feos sean los mas baratos, sino que la variable que molesta en la interpretación de los datos es el tamaño de los diamantes, “carat”.
Algunas representaciones gráficas para entender los resultados:
ggplot(diamonds2, aes(carat, lresid2)) +
geom_hex() +
facet_grid( ~ color)
ggplot(diamonds2, aes(carat, lresid2)) +
geom_hex() +
facet_grid( ~ cut)
ggplot(diamonds2, aes(carat, lresid2)) +
geom_hex() +
facet_grid( ~ clarity)