── 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.4.4 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ 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 (readxl)
library (here)
here() starts at /Users/lexi/Desktop/L7/Study/SAIS/Sustainable Finance - Application and Methods/Assignment/Critical Minerals
path_to_sheet <- here ("data-raw" , "CM_Data_Explorer.xlsx" )
read_ev_sheet <- partial (
.f = read_excel,
path = path_to_sheet,
sheet = "2.3 EV" ,
col_names = FALSE
)
sheet_header <- read_ev_sheet (range = "A4:W5" )
New names:
• `` -> `...1`
• `` -> `...2`
• `` -> `...3`
• `` -> `...4`
• `` -> `...5`
• `` -> `...6`
• `` -> `...7`
• `` -> `...8`
• `` -> `...9`
• `` -> `...10`
• `` -> `...11`
• `` -> `...12`
• `` -> `...13`
• `` -> `...14`
• `` -> `...15`
• `` -> `...16`
• `` -> `...17`
• `` -> `...18`
• `` -> `...19`
• `` -> `...20`
• `` -> `...21`
• `` -> `...22`
• `` -> `...23`
sheet_header_processed <- sheet_header |>
t () |>
as_tibble () |>
rename (scenario = V1, year = V2) |>
fill (scenario) |>
replace_na (list (scenario = "Current Year" ))
Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if
`.name_repair` is omitted as of tibble 2.0.0.
ℹ Using compatibility `.name_repair`.
ev_type <- read_ev_sheet (range = "A7" ) |>
pull ()
New names:
• `` -> `...1`
mineral_info <- read_ev_sheet (range = "A8:W19" )
New names:
• `` -> `...1`
• `` -> `...2`
• `` -> `...3`
• `` -> `...4`
• `` -> `...5`
• `` -> `...6`
• `` -> `...7`
• `` -> `...8`
• `` -> `...9`
• `` -> `...10`
• `` -> `...11`
• `` -> `...12`
• `` -> `...13`
• `` -> `...14`
• `` -> `...15`
• `` -> `...16`
• `` -> `...17`
• `` -> `...18`
• `` -> `...19`
• `` -> `...20`
• `` -> `...21`
• `` -> `...22`
• `` -> `...23`
mineral_info_col_names <- names (mineral_info)
sheet_headers_and_col_names <- sheet_header_processed |>
add_column (mineral_info_col_names = mineral_info_col_names)
mineral_info_long <- mineral_info |>
rename (mineral = ` ...1 ` ) |>
pivot_longer (cols = - mineral,
names_to = "mineral_info_col_names" ) |>
add_column (ev_type)
combined_data <- mineral_info_long |>
left_join (sheet_headers_and_col_names, by = join_by (mineral_info_col_names)) |>
filter (! is.na (year)) |>
mutate (
year = as.integer (year)
) |>
select (ev_type, mineral, scenario, year, value)
read_iea_ev_table <-
function (ev_type_range, mineral_info_range) {
ev_type <-
read_ev_sheet (range = ev_type_range) |>
pull ()
mineral_info <- read_ev_sheet (range = mineral_info_range)
mineral_info_col_names <- names (mineral_info)
mineral_info_long <- mineral_info |>
rename (mineral = ` ...1 ` ) |>
pivot_longer (cols = - mineral,
names_to = "mineral_info_col_names" ) |>
add_column (ev_type)
combined_data <- mineral_info_long |>
left_join (sheet_headers_and_col_names, by = join_by (mineral_info_col_names)) |>
filter (! is.na (year)) |>
mutate (
year = as.integer (year)
) |>
select (ev_type, mineral, scenario, year, value)
combined_data
}
constrained_nickel_table <- read_iea_ev_table (
ev_type_range = "A7" ,
mineral_info_range = "A8:W19"
)
New names:
New names:
• `` -> `...1`
silicon_rich_anodes_table <- read_iea_ev_table (
ev_type_range = "A22" ,
mineral_info_range = "A23:W34"
)
New names:
New names:
• `` -> `...1`
solid_state_batteries_table <- read_iea_ev_table (
ev_type_range = "A37" ,
mineral_info_range = "A38:W49"
)
New names:
New names:
• `` -> `...1`
lower_battery_sizes_table <- read_iea_ev_table (
ev_type_range = "A52" ,
mineral_info_range = "A53:W64"
)
New names:
New names:
• `` -> `...1`
limited_battery_size_reduction_table <- read_iea_ev_table (
ev_type_range = "A67" ,
mineral_info_range = "A68:W79"
)
New names:
New names:
• `` -> `...1`
base_table <- read_iea_ev_table (
ev_type_range = "A82" ,
mineral_info_range = "A83:W94"
)
New names:
New names:
• `` -> `...1`
final_iea_ev_table <- constrained_nickel_table |>
bind_rows (silicon_rich_anodes_table) |>
bind_rows (solid_state_batteries_table) |>
bind_rows (lower_battery_sizes_table) |>
bind_rows (limited_battery_size_reduction_table) |>
bind_rows (base_table)
final_iea_ev_table
# A tibble: 1,368 × 5
ev_type mineral scenario year value
<chr> <chr> <chr> <int> <dbl>
1 Constrained nickel supply Copper Current Year 2022 381.
2 Constrained nickel supply Copper Stated policies scenario 2025 617.
3 Constrained nickel supply Copper Stated policies scenario 2030 1389.
4 Constrained nickel supply Copper Stated policies scenario 2035 1736.
5 Constrained nickel supply Copper Stated policies scenario 2040 2233.
6 Constrained nickel supply Copper Stated policies scenario 2045 2418.
7 Constrained nickel supply Copper Stated policies scenario 2050 2313.
8 Constrained nickel supply Copper Announced pledges scenario 2025 732.
9 Constrained nickel supply Copper Announced pledges scenario 2030 2113.
10 Constrained nickel supply Copper Announced pledges scenario 2035 3587.
# ℹ 1,358 more rows
write_csv (final_iea_ev_table,here ("data" , "iea_total_demand_for_critical_minerals.csv" ))
copper_cobalt_iea_ev_table <- final_iea_ev_table |>
filter (mineral %in% c ("Copper" , "Cobalt" ))
write_csv (copper_cobalt_iea_ev_table,here ("data" , "iea_total_demand_for_critical_minerals.csv" ))
1
Under Net Zero Emissions by 2050 scenario:
Copper demand increases when nickel supply decreases.
Copper demand decreases when battery size decreases.
Copper demand is not affected by the development of solid state battery and silicon-rich anode.
Copper demand flattens after 2035.
copper_cobalt_iea_ev_table %>%
filter (mineral %in% "Copper" ) %>%
filter (scenario %in% "Net Zero Emissions by 2050 scenario" ) %>%
ggplot () +
aes (x = year, y = value, colour = ev_type) +
geom_line () +
scale_color_brewer (palette = "Set3" , direction = 1 ) +
labs (
x = "Year" ,
y = "Kiloton (kt)" ,
title = "Copper Demand by EV Development under Net Zero Scenario" ,
subtitle = "Only affected by nickel supply and battery size" ,
caption = "Source: IEA | Insight: Lexi" ,
color = "EV Development"
) +
theme_minimal ()
2
Under Net Zero Emissions by 2050 scenario:
Cobalt demand keeps increasing unless nickel supply decreases.
Cobalt demand changes as battery size decreases.
Cobalt demand is not affected by the development of solid state battery and silicon-rich anode.
copper_cobalt_iea_ev_table %>%
filter (mineral %in% "Cobalt" ) %>%
filter (scenario %in% "Net Zero Emissions by 2050 scenario" ) %>%
ggplot () +
aes (x = year, y = value, colour = ev_type) +
geom_line () +
scale_color_brewer (palette = "Set3" , direction = 1 ) +
labs (
x = "Year" ,
y = "Kiloton (kt)" ,
title = "Cobalt Demand by EV Development under Net Zero Scenario" ,
subtitle = "Cobalt demand keeps increasing unless nickel supply decreases" ,
caption = "Source: IEA | Insight: Lexi" ,
color = "EV Development"
) +
theme_minimal ()
3
With constrained nickel supply:
Copper demand increases significantly compared to base case, especially under Net Zero Emissions by 2050 and announced pledges scenarios.
copper_cobalt_iea_ev_table %>%
filter (ev_type %in% c ("Constrained nickel supply" , "STEPS - Base case"
)) %>%
filter (mineral %in% "Copper" ) %>%
filter (! (scenario %in% "Current Year" )) %>%
ggplot () +
aes (x = year, y = value, colour = scenario) +
geom_line () +
scale_color_brewer (palette = "Accent" , direction = 1 ) +
labs (
x = "Year" ,
y = "Kiloton (kt)" ,
title = "Copper Demand under Constrained Nickel Supply" ,
subtitle = "Copper demand increases significantly compared to base case" ,
caption = "Source: IEA | Insight: Lexi" ,
color = "Policy Scenario"
) +
theme_minimal () +
theme (legend.position = "top" ) +
facet_wrap (vars (ev_type))
4
With constrained nickel supply:
Cobalt demand decreases significantly compared to base case.
Cobalt demand declines overall.
Cobalt demand stops declining between 2030-2040.
copper_cobalt_iea_ev_table %>%
filter (ev_type %in% c ("Constrained nickel supply" , "STEPS - Base case"
)) %>%
filter (mineral %in% "Cobalt" ) %>%
filter (! (scenario %in% "Current Year" )) %>%
ggplot () +
aes (x = year, y = value, colour = scenario) +
geom_line () +
scale_color_brewer (palette = "Accent" , direction = 1 ) +
labs (
x = "Year" ,
y = "Kiloton (kt)" ,
title = "Cobalt Demand under Constrained Nickel Supply" ,
subtitle = "Cobalt demand declines overall" ,
caption = "Source: IEA | Insight: Lexi" ,
color = "Policy Scenario"
) +
theme_minimal () +
theme (legend.position = "top" ) +
facet_wrap (vars (ev_type))
5
Under Announced Pledges Scenario:
Copper demand is significantly larger than cobalt demand.
Copper demand continues to grow larger as EV evolves, while cobalt demand flattens or declines.
copper_cobalt_iea_ev_table %>%
filter (! (ev_type %in% "Limited battery size reduction" )) %>%
filter (scenario %in%
"Announced pledges scenario" ) %>%
mutate (ev_type = factor (ev_type, levels = c (
"Wider use of silicon-rich anodes" ,
"Faster uptake of solid state batteries" ,
"Lower battery sizes" ,
"Constrained nickel supply" ,
"STEPS - Base case" ,
"Limited battery size reduction" ))) %>%
ggplot () +
aes (x = year, y = value, colour = mineral) +
geom_line () +
scale_color_hue (direction = 1 ) +
labs (
x = "Year" ,
y = "Kiloton (kt)" ,
title = "Copper and Cobalt Demand under Announced Pledges Scenario" ,
subtitle = "Copper demand grows larger as EV evolves" ,
caption = "Source: IEA | Insights: Lexi" ,
color = "Mineral"
) +
theme_minimal () +
facet_wrap (vars (ev_type))