# El dataset diamonds viene incluido en ggplot2 (parte de tidyverse)
data(diamonds)
head(diamonds, 10) %>%
kable(caption = "Primeras 10 observaciones del dataset diamonds") %>%
estilo_tabla()
| carat | cut | color | clarity | depth | table | price | x | y | z |
|---|---|---|---|---|---|---|---|---|---|
| 0.23 | Ideal | E | SI2 | 61.5 | 55 | 326 | 3.95 | 3.98 | 2.43 |
| 0.21 | Premium | E | SI1 | 59.8 | 61 | 326 | 3.89 | 3.84 | 2.31 |
| 0.23 | Good | E | VS1 | 56.9 | 65 | 327 | 4.05 | 4.07 | 2.31 |
| 0.29 | Premium | I | VS2 | 62.4 | 58 | 334 | 4.20 | 4.23 | 2.63 |
| 0.31 | Good | J | SI2 | 63.3 | 58 | 335 | 4.34 | 4.35 | 2.75 |
| 0.24 | Very Good | J | VVS2 | 62.8 | 57 | 336 | 3.94 | 3.96 | 2.48 |
| 0.24 | Very Good | I | VVS1 | 62.3 | 57 | 336 | 3.95 | 3.98 | 2.47 |
| 0.26 | Very Good | H | SI1 | 61.9 | 55 | 337 | 4.07 | 4.11 | 2.53 |
| 0.22 | Fair | E | VS2 | 65.1 | 61 | 337 | 3.87 | 3.78 | 2.49 |
| 0.23 | Very Good | H | VS1 | 59.4 | 61 | 338 | 4.00 | 4.05 | 2.39 |
glimpse(diamonds)
## Rows: 53,940
## Columns: 10
## $ carat <dbl> 0.23, 0.21, 0.23, 0.29, 0.31, 0.24, 0.24, 0.26, 0.22, 0.23, 0.…
## $ cut <ord> Ideal, Premium, Good, Premium, Good, Very Good, Very Good, Ver…
## $ color <ord> E, E, E, I, J, J, I, H, E, H, J, J, F, J, E, E, I, J, J, J, I,…
## $ clarity <ord> SI2, SI1, VS1, VS2, SI2, VVS2, VVS1, SI1, VS2, VS1, SI1, VS1, …
## $ depth <dbl> 61.5, 59.8, 56.9, 62.4, 63.3, 62.8, 62.3, 61.9, 65.1, 59.4, 64…
## $ table <dbl> 55, 61, 65, 58, 58, 57, 57, 55, 61, 61, 55, 56, 61, 54, 62, 58…
## $ price <int> 326, 326, 327, 334, 335, 336, 336, 337, 337, 338, 339, 340, 34…
## $ x <dbl> 3.95, 3.89, 4.05, 4.20, 4.34, 3.94, 3.95, 4.07, 3.87, 4.00, 4.…
## $ y <dbl> 3.98, 3.84, 4.07, 4.23, 4.35, 3.96, 3.98, 4.11, 3.78, 4.05, 4.…
## $ z <dbl> 2.43, 2.31, 2.31, 2.63, 2.75, 2.48, 2.47, 2.53, 2.49, 2.39, 2.…
El dataset contiene 53,940 observaciones y 10 variables.
Selección de variables:
| Variable | Tipo | Descripción |
|---|---|---|
price |
Numérica discreta | Precio del diamante (USD) |
carat |
Numérica continua | Peso del diamante (quilates) |
cut |
Categórica ordinal | Calidad del corte (Fair → Ideal) |
color |
Categórica ordinal | Color (J peor → D mejor) |
clarity |
Categórica ordinal | Pureza (I1 peor → IF mejor) |
diamonds %>%
summarise(
minimo = min(price),
Q1 = quantile(price, 0.25),
mediana = median(price),
media = mean(price),
Q3 = quantile(price, 0.75),
maximo = max(price)
) %>%
kable(digits = 0, caption = "Estadísticos del precio (USD)") %>%
estilo_tabla()
| minimo | Q1 | mediana | media | Q3 | maximo |
|---|---|---|---|---|---|
| 326 | 950 | 2401 | 3933 | 5324 | 18823 |
El precio de los diamantes va desde $326 hasta $18,823 USD. La media ($3,933) es notablemente mayor que la mediana ($2,401), lo que adelanta una distribución asimétrica con cola larga hacia precios altos — característica del mercado de bienes de lujo.
diamonds %>%
summarise(across(everything(), ~ sum(is.na(.)))) %>%
pivot_longer(everything(), names_to = "variable", values_to = "n_NA") %>%
kable(caption = "Conteo de valores faltantes por variable") %>%
estilo_tabla()
| variable | n_NA |
|---|---|
| carat | 0 |
| cut | 0 |
| color | 0 |
| clarity | 0 |
| depth | 0 |
| table | 0 |
| price | 0 |
| x | 0 |
| y | 0 |
| z | 0 |
El dataset no presenta valores faltantes en ninguna variable. No es necesario aplicar imputación ni eliminación de registros por NAs.
En diamantes reales, ni el peso ni el precio pueden ser negativos o
cero, y las dimensiones físicas (x, y,
z) no pueden ser cero (un diamante con dimensión nula no
existe).
diamonds %>%
summarise(
carat_no_positivo = sum(carat <= 0),
price_no_positivo = sum(price <= 0),
x_cero = sum(x == 0),
y_cero = sum(y == 0),
z_cero = sum(z == 0)
) %>%
pivot_longer(everything(), names_to = "verificacion", values_to = "n_registros") %>%
kable(caption = "Verificación de valores no válidos") %>%
estilo_tabla()
| verificacion | n_registros |
|---|---|
| carat_no_positivo | 0 |
| price_no_positivo | 0 |
| x_cero | 8 |
| y_cero | 7 |
| z_cero | 20 |
Hallazgo: carat y price no
presentan valores no válidos, pero las dimensiones físicas sí: existen
registros con x = 0, y = 0 y
z = 0. Aunque estas variables no se analizan en esta
actividad, su presencia sugiere errores de medición en el origen de los
datos.
n_duplicados <- sum(duplicated(diamonds))
tibble(
filas_totales = nrow(diamonds),
filas_unicas = nrow(diamonds) - n_duplicados,
filas_duplicadas = n_duplicados,
pct_duplicados = round(100 * n_duplicados / nrow(diamonds), 2)
) %>%
kable(caption = "Análisis de duplicados") %>%
estilo_tabla()
| filas_totales | filas_unicas | filas_duplicadas | pct_duplicados |
|---|---|---|---|
| 53940 | 53794 | 146 | 0.27 |
Se detectan 146 filas duplicadas. Como el dataset no incluye un identificador único por pieza, no es posible distinguir si corresponden a diamantes distintos con características idénticas (plausible, dado el redondeo de las variables) o a errores reales de duplicación.
price y carat)diamonds %>%
select(carat, price) %>%
pivot_longer(everything(), names_to = "variable", values_to = "valor") %>%
ggplot(aes(x = valor)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.5,
outlier.alpha = 0.4) +
facet_wrap(~ variable, scales = "free", ncol = 2) +
labs(title = "Boxplots de las variables numéricas analizadas",
x = NULL, y = NULL) +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
Ambas variables presentan outliers en el extremo superior, pero no parecen errores: son valores plausibles de un mercado donde coexisten diamantes pequeños y baratos (mayoría) con piezas excepcionales grandes y caras (minoría).
Conforme al enunciado, no se limpian los datos. Sin embargo, se documentan los hallazgos a tener en cuenta en futuros análisis:
x, y,
z — afectan análisis dimensionales pero no las
cinco variables que se analizan en esta actividad.price y carat son
valores reales del mercado y deben conservarse para no perder
información sobre piezas premium.price
(precio)ggplot(diamonds, aes(x = price)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.6,
outlier.alpha = 0.4) +
labs(title = "Distribución del precio (USD)", x = "Precio (USD)", y = NULL) +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
axis.text.y = element_blank(), axis.ticks.y = element_blank())
Reflexión — price
carat
(peso)ggplot(diamonds, aes(x = carat)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.6,
outlier.alpha = 0.4) +
labs(title = "Distribución del peso (carat)", x = "Peso (carat)", y = NULL) +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
axis.text.y = element_blank(), axis.ticks.y = element_blank())
Reflexión — carat
cut
(calidad del corte)diamonds %>%
count(cut) %>%
mutate(pct = round(100 * n / sum(n), 1)) %>%
ggplot(aes(x = cut, y = n)) +
geom_col(fill = "#2C3E50", width = 0.7) +
geom_text(aes(label = paste0(format(n, big.mark = ","), " (", pct, "%)")),
vjust = -0.3, size = 3.3, color = "#2C3E50") +
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
labs(title = "Distribución de la calidad del corte (cut)",
x = NULL, y = "Frecuencia") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
panel.grid.major.x = element_blank())
Reflexión — cut
Ideal (~40%), el
corte de mayor calidad.Fair (~3%), el
corte de menor calidad.Ideal y Premium.color
(color)diamonds %>%
mutate(color = factor(color, levels = c("D","E","F","G","H","I","J"))) %>%
count(color) %>%
mutate(pct = round(100 * n / sum(n), 1)) %>%
ggplot(aes(x = color, y = n)) +
geom_col(fill = "#2C3E50", width = 0.7) +
geom_text(aes(label = paste0(format(n, big.mark = ","), " (", pct, "%)")),
vjust = -0.3, size = 3.3, color = "#2C3E50") +
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
labs(title = "Distribución del color (D = mejor → J = peor)",
x = NULL, y = "Frecuencia") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
panel.grid.major.x = element_blank())
Reflexión — color
G (~21%), color
intermedio casi incoloro.J (~5%), el
color más amarillento.clarity (claridad)diamonds %>%
mutate(clarity = factor(clarity,
levels = c("I1","SI2","SI1","VS2","VS1","VVS2","VVS1","IF"))) %>%
count(clarity) %>%
mutate(pct = round(100 * n / sum(n), 1)) %>%
ggplot(aes(x = clarity, y = n)) +
geom_col(fill = "#2C3E50", width = 0.7) +
geom_text(aes(label = paste0(format(n, big.mark = ","), " (", pct, "%)")),
vjust = -0.3, size = 3.3, color = "#2C3E50") +
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
labs(title = "Distribución de la claridad (I1 = peor → IF = mejor)",
x = NULL, y = "Frecuencia") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
panel.grid.major.x = element_blank())
Reflexión — clarity
SI1 (~24%),
claridad media con pequeñas inclusiones visibles solo con lupa.I1 (~1.4%),
claridad pobre con inclusiones visibles a simple vista.IF) son geológicamente raros, mientras
que los muy incluidos (I1) se descartan o se reservan para
uso industrial. La muestra refleja el segmento de joyería formal.carat vs priceggplot(diamonds, aes(x = carat, y = price)) +
geom_point(alpha = 0.15, color = "#2C3E50", size = 0.6) +
geom_smooth(method = "loess", color = "coral", se = FALSE, linewidth = 1) +
labs(title = "Relación entre peso (carat) y precio (USD)",
subtitle = "53,940 observaciones · Curva suavizada en coral",
x = "Peso (carat)", y = "Precio (USD)") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"))
¿Cómo se relacionan?
Existe una relación positiva clara: a mayor peso, mayor precio.
¿Los diamantes más grandes son siempre más caros?
No. Para un mismo peso, los precios pueden variar enormemente: dos diamantes de ~1 ct pueden costar entre $2,000 y $18,000 dependiendo de las otras características (cut, color, clarity).
¿La relación es lineal o muestra crecimiento acelerado?
La curva suavizada muestra crecimiento acelerado, no lineal: el precio crece más rápido que el peso. Doblar el peso multiplica el precio por más del doble.
¿Hay umbrales psicológicos donde el precio se dispara?
Sí. Se observan bandas verticales densas en los pesos comerciales clásicos: 0.30, 0.50, 0.70, 1.00, 1.50 y 2.00 quilates. Los cortadores sacrifican peso real para alcanzar estos umbrales redondos porque el mercado los premia con saltos de precio. Por ejemplo, un diamante de 0.99 ct vale mucho menos que uno de 1.00 ct, aunque la diferencia visual sea imperceptible.
¿Por qué dos diamantes del mismo peso pueden tener precios muy distintos?
Porque el precio depende de las 4 C’s (carat, cut, color, clarity), no solo del peso. La dispersión vertical observada para cada valor de carat anticipa el análisis de los Pasos 5 y 6.
cut)ggplot(diamonds, aes(x = cut, y = price)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.4,
outlier.alpha = 0.3) +
stat_summary(fun = mean, geom = "point",
shape = 23, size = 3, fill = "coral", color = "black") +
labs(title = "Precio según calidad del corte (cut)",
subtitle = "Diamante coral: precio medio del grupo",
x = NULL, y = "Precio (USD)") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"))
diamonds %>%
group_by(cut) %>%
summarise(
n = n(),
precio_medio = round(mean(price), 0),
carat_medio = round(mean(carat), 2)
) %>%
kable(caption = "Precio y peso medio por cut") %>%
estilo_tabla()
| cut | n | precio_medio | carat_medio |
|---|---|---|---|
| Fair | 1610 | 4359 | 1.05 |
| Good | 4906 | 3929 | 0.85 |
| Very Good | 12082 | 3982 | 0.81 |
| Premium | 13791 | 4584 | 0.89 |
| Ideal | 21551 | 3458 | 0.70 |
Reflexión — cut
Resultado contraintuitivo: el corte
Ideal (mejor calidad) tiene el precio medio más
bajo (~$3,458), mientras que el corte Premium
tiene el más alto. Si el corte determinara el precio, esperaríamos lo
contrario.
¿Por qué ocurre esto? Observando la columna
carat_medio, los diamantes de corte Fair y
Premium son considerablemente más pesados
en promedio que los Ideal. Como el peso domina el precio,
este efecto oculta la verdadera influencia del corte.
clarity)diamonds %>%
mutate(clarity = factor(clarity,
levels = c("I1","SI2","SI1","VS2","VS1","VVS2","VVS1","IF"))) %>%
ggplot(aes(x = clarity, y = price)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.4,
outlier.alpha = 0.3) +
stat_summary(fun = mean, geom = "point",
shape = 23, size = 3, fill = "coral", color = "black") +
labs(title = "Precio según claridad",
subtitle = "I1 = peor · IF = mejor · Diamante coral: precio medio",
x = NULL, y = "Precio (USD)") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"))
diamonds %>%
mutate(clarity = factor(clarity,
levels = c("I1","SI2","SI1","VS2","VS1","VVS2","VVS1","IF"))) %>%
group_by(clarity) %>%
summarise(
n = n(),
precio_medio = round(mean(price), 0),
carat_medio = round(mean(carat), 2)
) %>%
kable(caption = "Precio y peso medio por clarity") %>%
estilo_tabla()
| clarity | n | precio_medio | carat_medio |
|---|---|---|---|
| I1 | 741 | 3924 | 1.28 |
| SI2 | 9194 | 5063 | 1.08 |
| SI1 | 13065 | 3996 | 0.85 |
| VS2 | 12258 | 3925 | 0.76 |
| VS1 | 8171 | 3839 | 0.73 |
| VVS2 | 5066 | 3284 | 0.60 |
| VVS1 | 3655 | 2523 | 0.50 |
| IF | 1790 | 2865 | 0.51 |
Reflexión — clarity
Se repite el patrón paradójico: las claridades inferiores (I1, SI2) tienen precios medios superiores a las claridades excelentes (IF, VVS1). Si la claridad fuera el determinante principal del precio, los diamantes IF deberían ser los más caros.
¿Por qué? Los diamantes de baja claridad tienden a ser considerablemente más grandes (I1 promedia 1.28 ct, IF apenas 0.50 ct). De nuevo, el peso enmascara el efecto de la claridad.
diamonds %>%
mutate(color = factor(color, levels = c("D","E","F","G","H","I","J"))) %>%
ggplot(aes(x = color, y = price)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.4,
outlier.alpha = 0.3) +
stat_summary(fun = mean, geom = "point",
shape = 23, size = 3, fill = "coral", color = "black") +
labs(title = "Precio según color",
subtitle = "D = mejor → J = peor · Diamante coral: precio medio",
x = NULL, y = "Precio (USD)") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"))
diamonds %>%
group_by(color) %>%
summarise(
n = n(),
precio_medio = round(mean(price), 0),
carat_medio = round(mean(carat), 2)
) %>%
kable(caption = "Precio y peso medio por color") %>%
estilo_tabla()
| color | n | precio_medio | carat_medio |
|---|---|---|---|
| D | 6775 | 3170 | 0.66 |
| E | 9797 | 3077 | 0.66 |
| F | 9542 | 3725 | 0.74 |
| G | 11292 | 3999 | 0.77 |
| H | 8304 | 4487 | 0.91 |
| I | 5422 | 5092 | 1.03 |
| J | 2808 | 5324 | 1.16 |
Reflexión — color
Patrón idéntico: los diamantes color J (peor color, más amarillento) tienen precio medio mayor que los color D (mejor color, perfectamente incoloro). Los diamantes J pesan en promedio casi el doble que los D.
¿Por qué los mejores en calidad no son siempre los más caros?
Porque existe una correlación inversa entre tamaño y calidad en la oferta del mercado: los diamantes grandes tienden a tener peor cut, peor clarity y peor color. Como el peso domina el precio, este patrón superpuesto produce la paradoja observada — los diamantes de peor calidad aparente son los más grandes y, por tanto, los más caros en promedio.
Para evaluar el efecto real de cut, clarity y color hay que controlar por el peso (Paso 6).
¿Qué variable parece tener mayor influencia?
A nivel agregado, carat (peso) domina
ampliamente el precio. El efecto de cut, clarity y color queda
oculto al no controlar por el peso. En el Paso 6 se desentraña su
impacto real.
carat por categoría de cutggplot(diamonds, aes(x = cut, y = carat)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.4,
outlier.alpha = 0.3) +
stat_summary(fun = mean, geom = "point",
shape = 23, size = 3, fill = "coral", color = "black") +
labs(title = "Peso (carat) según calidad del corte",
x = NULL, y = "Peso (carat)") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"))
carat vs price por categoría de
cutggplot(diamonds, aes(x = carat, y = price, color = cut)) +
geom_point(alpha = 0.2, size = 0.5) +
geom_smooth(method = "loess", se = FALSE, linewidth = 0.9) +
scale_color_brewer(palette = "Set1") +
labs(title = "Carat vs price segmentado por cut",
x = "Peso (carat)", y = "Precio (USD)", color = "Cut") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
legend.position = "bottom")
Reflexión — cut
Fair (1.05 ct) es ~50% mayor que el de los
Ideal (0.70 ct). Los cortadores sacrifican calidad
del corte para conservar peso comercial en piezas grandes.Ideal vs un
Fair del mismo peso? En el scatter segmentado se
observa que, para un mismo carat, los Ideal están
consistentemente por encima de los Fair. La prima por mejor
corte ronda el 15-25%.carat por categoría de claritydiamonds %>%
mutate(clarity = factor(clarity,
levels = c("I1","SI2","SI1","VS2","VS1","VVS2","VVS1","IF"))) %>%
ggplot(aes(x = clarity, y = carat)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.4,
outlier.alpha = 0.3) +
stat_summary(fun = mean, geom = "point",
shape = 23, size = 3, fill = "coral", color = "black") +
labs(title = "Peso (carat) según claridad",
subtitle = "I1 = peor → IF = mejor",
x = NULL, y = "Peso (carat)") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"))
carat vs price por categoría de
claritydiamonds %>%
mutate(clarity = factor(clarity,
levels = c("I1","SI2","SI1","VS2","VS1","VVS2","VVS1","IF"))) %>%
ggplot(aes(x = carat, y = price, color = clarity)) +
geom_point(alpha = 0.15, size = 0.5) +
geom_smooth(method = "loess", se = FALSE, linewidth = 0.9) +
scale_color_brewer(palette = "RdYlGn") +
labs(title = "Carat vs price segmentado por clarity",
subtitle = "Verde = mejor claridad · Rojo = peor",
x = "Peso (carat)", y = "Precio (USD)", color = "Clarity") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
legend.position = "bottom")
Reflexión — clarity
I1 (1.28 ct) es
más del doble que el de los IF (0.50 ct).
La razón es geológica: a mayor tamaño, mayor
probabilidad de que el cristal capture inclusiones durante su
formación.IF puede valer hasta 3-4 veces
más que un I1.carat por categoría de colordiamonds %>%
mutate(color = factor(color, levels = c("D","E","F","G","H","I","J"))) %>%
ggplot(aes(x = color, y = carat)) +
geom_boxplot(fill = "#2C3E50", color = "black",
outlier.color = "coral", outlier.size = 0.4,
outlier.alpha = 0.3) +
stat_summary(fun = mean, geom = "point",
shape = 23, size = 3, fill = "coral", color = "black") +
labs(title = "Peso (carat) según color",
subtitle = "D = mejor → J = peor",
x = NULL, y = "Peso (carat)") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"))
carat vs price por categoría de
colordiamonds %>%
mutate(color = factor(color, levels = c("D","E","F","G","H","I","J"))) %>%
ggplot(aes(x = carat, y = price, color = color)) +
geom_point(alpha = 0.15, size = 0.5) +
geom_smooth(method = "loess", se = FALSE, linewidth = 0.9) +
scale_color_brewer(palette = "RdYlBu") +
labs(title = "Carat vs price segmentado por color",
subtitle = "Azul = mejor color · Rojo = peor",
x = "Peso (carat)", y = "Precio (USD)", color = "Color") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold"),
legend.position = "bottom")
Reflexión — color
¿Por qué los diamantes de mejor calidad no siempre son los más caros?
El análisis exploratorio muestra que la respuesta está en la interacción entre tamaño y calidad dentro de la oferta del mercado:
carat) es el principal determinante
del precio, con un crecimiento acelerado y no lineal.Implicaciones para futuros análisis: el dataset diamonds es un caso didáctico ideal para introducir el concepto de variable confusora (el peso enmascara el efecto de las otras variables) y para motivar el uso de modelos multivariados. La relación carat-price exhibe propiedades multiplicativas que justifican transformaciones logarítmicas en análisis predictivos posteriores.