2026-05-07

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.1.6
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.2
## ✔ purrr     1.2.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(ggridges)
library(scales)
## 
## Attaching package: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
library(viridis)
## Loading required package: viridisLite
## 
## Attaching package: 'viridis'
## 
## The following object is masked from 'package:scales':
## 
##     viridis_pal
df <- read_csv("/Users/sangdao/Downloads/laptop_prices.csv")
## Rows: 1275 Columns: 23
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (14): Company, Product, TypeName, OS, Screen, Touchscreen, IPSpanel, Ret...
## dbl  (9): Inches, Ram, Weight, Price_euros, ScreenW, ScreenH, CPU_freq, Prim...
## 
## ℹ 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.

The RAM Effect

ggplot(df, aes(x = factor(Ram), y = Price_euros, fill = factor(Ram))) +
  geom_jitter(width = 0.2, alpha = 0.3, color = "darkgray") +
  geom_boxplot(alpha = 0.8, outlier.shape = NA) +
  scale_fill_viridis_d(option = "mako") +
  scale_y_continuous(labels = scales::dollar_format(prefix = "€")) +
  labs(title = "The RAM Effect", 
       subtitle = "Price Distribution by RAM Size with Individual Data Points",
       x = "RAM (GB)", 
       y = "Price") +
  theme_minimal() +
  theme(legend.position = "none", 
        plot.title = element_text(size = rel(1.5), face = "bold"))

The “Gaming” Label

ggplot(df, aes(x = Price_euros, 
                          y = fct_reorder(TypeName, Price_euros, .fun = median), 
                          fill = TypeName)) +
  geom_density_ridges(alpha = 0.8, color = "white", scale = 1.2) +
  scale_fill_viridis_d() +
  scale_x_continuous(labels = scales::dollar_format(prefix = "€")) +
  labs(title = "The 'Gaming' Label", 
       subtitle = "Density Ridge Plot of Prices by Laptop Category",
       x = "Price", 
       y = "Laptop Type") +
  theme_minimal() +
  theme(legend.position = "none",
        plot.title = element_text(size = rel(1.5), face = "bold"))
## Picking joint bandwidth of 197

Brand Strategies

ggplot(df, aes(x = fct_rev(fct_infreq(Company)), fill = TypeName)) +
  geom_bar() +
  coord_flip() + 
  scale_fill_viridis_d(option = "plasma") +
  labs(title = "Brand Strategies", 
       subtitle = "Laptop Types Produced by Company (Ordered by Volume)",
       x = "Brand", 
       y = "Number of Models", 
       fill = "Laptop Type") +
  theme_minimal() +
  theme(panel.grid.major.y = element_blank(),
        plot.title = element_text(size = rel(1.5), face = "bold"))

The Screen Myth

ggplot(df, aes(x = Inches, y = Price_euros, color = Touchscreen)) +
  geom_jitter(alpha = 0.6, size = 2, width = 0.15) +
  geom_smooth(method = "loess", se = FALSE, linewidth = 1.2) +
  scale_color_manual(values = c("No" = "#2c3e50", "Yes" = "#e74c3c")) +
  scale_y_continuous(labels = scales::dollar_format(prefix = "€")) +
  labs(title = "The Screen Myth", 
       subtitle = "Screen Size vs. Price, Separated by Touchscreen Capability",
       x = "Screen Size (Inches)", 
       y = "Price") +
  theme_light() +
  theme(plot.title = element_text(size = rel(1.5), face = "bold"))
## `geom_smooth()` using formula = 'y ~ x'

The Smart Choice

ggplot(df, aes(x = CPU_freq, y = Price_euros, color = factor(Ram), size = ScreenW)) +
  geom_jitter(alpha = 0.6, width = 0.05) + 
  scale_color_viridis_d(option = "turbo") +
  scale_y_continuous(labels = scales::dollar_format(prefix = "€")) +
  labs(title = "The Smart Choice: Identifying the P/P Sweet Spot", 
       subtitle = "Price driven by CPU Frequency, RAM (Color), and Resolution Width (Size)",
       x = "CPU Frequency (GHz)", 
       y = "Price", 
       color = "RAM (GB)", 
       size = "Screen Width") +
  theme_minimal() +
  theme(plot.title = element_text(size = rel(1.5), face = "bold"))

Strategic Buying

ggplot(df, aes(x = CPU_freq, y = Price_euros, color = factor(Ram), size = PrimaryStorage)) +
  geom_jitter(alpha = 0.6, width = 0.05) +
  facet_wrap(~ TypeName) +
  scale_color_viridis_d(option = "turbo") +
  scale_y_continuous(labels = scales::dollar_format(prefix = "€")) +
  labs(title = "Strategic Buying: Finding the True Deals", 
       subtitle = "Price vs. CPU Frequency, RAM (Color), Storage (Size), and Laptop Type (Facet)",
       x = "CPU Frequency (GHz)", 
       y = "Price", 
       color = "RAM (GB)", 
       size = "Storage (GB)") +
  theme_minimal() +
  theme(plot.title = element_text(size = rel(1.5), face = "bold"),
        strip.text = element_text(size = rel(1.2), face = "bold"))