Analysis

Load Packages

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── 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(dplyr)
library(ggplot2)
library(plotly)

Attaching package: 'plotly'

The following object is masked from 'package:ggplot2':

    last_plot

The following object is masked from 'package:stats':

    filter

The following object is masked from 'package:graphics':

    layout
library(ggthemes)
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(gghighlight)
library(forcats)

Load Data

load("df.RData")
load("cleaned_data.RData")
ev_share_clean <- read.csv("ev_share_clean.csv")

Data analysis

Q1. EV Sales

sales_df <- df |>
  filter(parameter == "EV sales", 
         category == "Historical", 
         year >= 2010, year <= 2023,
         region %in% c("China", "USA", "Europe", "World")) |>
  group_by(region, year) |>
  summarise(total_sales = sum(value))
`summarise()` has grouped output by 'region'. You can override using the
`.groups` argument.
# Data visualisation
econ_colors <- c("#E3120B", "#174B97", "#00A4DB", "#FFB400", "#6DCD59", "#A652BB", "#FF6E1A", "#8B4513","#7F7F7F", "#C4C4C4", "#333333", "#FFFFFF")


sales_df |>
  ggplot(aes(x = year, 
             y = total_sales/1e6, 
             color = region, fill = region)) +
  geom_area(position = "identity", alpha = 0.15, color = NA) +
  geom_line(linewidth = 1) + 
  geom_point(size = 1.5, shape = 21, fill = "white", stroke = 1.2) +
  theme_economist() +
  theme(
    plot.title = element_text(
      margin = margin(b = 10)
    ),
    legend.position = "right",
    legend.direction = "vertical",
    legend.text = element_text(size = 10),
    legend.title = element_text(size = 11, face = "bold"),
    legend.key.height = unit(0.8, "cm"),
    legend.spacing.y = unit(0.3, "cm")
  ) +
  labs(title = "Global EV Sales Growth by Major Markets",
       subtitle = "Millions of vehicles, 2010-2023",
       x = "",
       y = "",
       caption = "Source:IEA") +
  scale_y_continuous(labels = scales::comma) +
  scale_color_manual(values = econ_colors) +
  scale_fill_manual(values = econ_colors) +
  scale_x_continuous(breaks = seq(2010, 2023, 1)) +
  guides(
    color = guide_legend(
      keywidth = unit(1, "cm"),
    ),
    fill = "none"
  )

ggsave("out/sales.png", width = 8, height = 6, dpi = 300)

Q2. EV Sales Share

ev_share_2023 <- ev_share_clean |>
  filter(year == 2023) |>
  group_by(region) |>
  arrange(desc(share)) |>
  head(10) |>
  ggplot(aes(x = fct_reorder(region, share), y = share, fill = region)) +
  geom_col(width = 0.8) +
  gghighlight(region == "China", label_params = list(fill = "#E3120B"),
              unhighlighted_params = list(fill = "#FFB400")) +
  coord_flip() +
  theme_economist() +
  theme(plot.title = element_text(
      margin = margin(b = 10))) +
  geom_text(aes(label = paste0(round(share,1), "%")), 
            hjust = 1.3, size = 3.5) +
  labs(title = "Share of new vehicles sold that are electric (2023)",
       x = "",
       y = "",
       caption = "Source:IEA") +
  scale_y_continuous(labels = function(x) paste0(x, "%")) +
  theme(legend.position = "none")

ev_share_2023

ggsave("out/share.png", width = 8, height = 6, dpi = 300)

Q3. EV Market Share Trend

# List of 9 major countries and regions
country_list <- c("World", "Norway", "Europe", "Germany", "United States", "China", "Japan", "India", "South Africa")
ev_share_trend <- ev_share_clean |>
  filter(year >= 2010,
         year <= 2023,
         region %in% country_list) |>
  group_by(region, year) |>
  arrange(desc(share))
ev_share_trend |>
  ggplot(aes(x = year, y = share, colour = region)) +
  geom_line() +
  geom_point(size = 1) +
  facet_wrap(~ factor(region, levels = country_list), 
             scales = "free_y") +
  theme_economist() +
  theme(
    legend.position = "none",
    strip.text = element_text(face = "bold", size = 10),
    axis.text.x = element_text(),
    panel.spacing = unit(1, "lines"),
    plot.title = element_text(
      margin = margin(b = 8)
    )
  )+
  scale_color_manual(values = econ_colors) +
  labs(
    title = "Regional EV Adoption Trends (2010-2023)",
    x = "",
    y = "Share of New Car Sales (%)",
    caption = "Source: IEA | Note: Y-axis scales vary by region"
  ) +
  scale_x_continuous(breaks = seq(2010, 2023, 4))

ggsave("out/trend.png", width = 8, height = 6, dpi = 300)

Q4. EV Stocks

# Data Analysis
ev_stock_2023 <- df |>
  filter(parameter == "EV stock",
         category == "Historical",
         year == 2023,
         !region %in% c("World", "EU27", "Europe")) |>
  group_by(region) |>
  summarise(stock = sum(value)) |>
  arrange(-stock) |>
  head(10)
# Data Visualisation
p1 <- plot_ly(
  data = ev_stock_2023,
  type = "treemap",
  labels = ~region,
  parents = NA,
  values = ~stock,
  marker = list(colorscale = "Blues") +
    theme_economist()
) |>
  layout(
    title = list(
      text = "<b>Top 10 Electric Vehicle Stocks (2023)</b><br><sup>Excluding regional aggregates</sup>",
      x = 0.09
    )
  )

p1
library(htmlwidgets) 
htmlwidgets::saveWidget(p1, "out/interactive_plot.html", selfcontained = TRUE)
Back to top