RDataImport

Author

Yuhao Shen

Loading Required Libraries

library(tidyverse) # pretty much always...
── 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) # for reading excel files
#install.packages("here") # install the package if you haven't yet
library(here) # for using relative file paths
here() starts at /Users/shenyuhao/Desktop/JHU SAIS/Spring/Sustainable Finance/Coding Assignment 5

Data Importing

Creating a function to read into the dataset

path_to_sheet <- here("data-raw", "CM_Data_Explorer.xlsx")

path_to_sheet
[1] "/Users/shenyuhao/Desktop/JHU SAIS/Spring/Sustainable Finance/Coding Assignment 5/data-raw/CM_Data_Explorer.xlsx"
read_key_minerals_sheet <- partial(
  # provide the function, in this case readxl::read_excel()
  .f = read_excel,
  # provide any arguments you want filled in
  path = path_to_sheet,
  sheet = "1 Total demand for key minerals",
  col_names = FALSE
)

# now all we have to do is provide the range
sheet_header <- read_key_minerals_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
# A tibble: 2 × 23
  ...1   ...2 ...3  ...4    ...5  ...6  ...7  ...8  ...9 ...10 ...11 ...12 ...13
  <lgl> <dbl> <lgl> <chr>  <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> <chr> <dbl> <dbl>
1 NA       NA NA    State…    NA    NA    NA    NA    NA NA    Anno…    NA    NA
2 NA     2022 NA    2025    2030  2035  2040  2045  2050 NA    2025   2030  2035
# ℹ 10 more variables: ...14 <dbl>, ...15 <dbl>, ...16 <dbl>, ...17 <lgl>,
#   ...18 <chr>, ...19 <dbl>, ...20 <dbl>, ...21 <dbl>, ...22 <dbl>,
#   ...23 <dbl>

Tidying the headers

sheet_header_processed <- sheet_header |> 
  # transpose the data
  t() |>
  # turn it back into a tibble
  as_tibble() |>
  # make them meaningful
  rename(scenario = V1, year = V2) |>
  # fill scenario down
  fill(scenario) |>
  #insert "Current" at top
  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`.
sheet_header_processed
# A tibble: 23 × 2
   scenario                 year 
   <chr>                    <chr>
 1 Current Year             <NA> 
 2 Current Year             2022 
 3 Current Year             <NA> 
 4 Stated policies scenario 2025 
 5 Stated policies scenario 2030 
 6 Stated policies scenario 2035 
 7 Stated policies scenario 2040 
 8 Stated policies scenario 2045 
 9 Stated policies scenario 2050 
10 Stated policies scenario <NA> 
# ℹ 13 more rows

Get mineral name

mineral_name <- read_key_minerals_sheet(range = "A7") |> 
  pull()
New names:
• `` -> `...1`
mineral_name
[1] "Copper"
# get the table 
mineral_info <- read_key_minerals_sheet(range = "A8:W18")
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
# A tibble: 11 × 23
   ...1         ...2 ...3     ...4    ...5    ...6    ...7    ...8    ...9 ...10
   <chr>       <dbl> <lgl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl> <lgl>
 1 Solar PV  6.82e+2 NA    7.79e+2 9.07e+2 9.25e+2 9.59e+2 1.12e+3 1.26e+3 NA   
 2 Wind      3.94e+2 NA    4.28e+2 6.46e+2 5.95e+2 5.32e+2 6.39e+2 7.21e+2 NA   
 3 Other lo… 8.50e+1 NA    6.90e+1 1.08e+2 8.69e+1 8.47e+1 1.10e+2 1.17e+2 NA   
 4 Electric… 3.73e+2 NA    5.80e+2 1.04e+3 1.22e+3 1.42e+3 1.46e+3 1.35e+3 NA   
 5 Grid bat… 2.04e+1 NA    3.84e+1 8.27e+1 1.54e+2 2.25e+2 2.38e+2 2.20e+2 NA   
 6 Electric… 4.18e+3 NA    4.58e+3 6.51e+3 6.51e+3 6.58e+3 7.11e+3 6.98e+3 NA   
 7 Hydrogen… 3.20e-3 NA    3.88e-3 6.76e-3 6.53e-3 6.99e-3 8.90e-3 1.45e-2 NA   
 8 Total cl… 5.74e+3 NA    6.47e+3 9.30e+3 9.49e+3 9.80e+3 1.07e+4 1.06e+4 NA   
 9 Other us… 1.98e+4 NA    2.03e+4 2.15e+4 2.20e+4 2.34e+4 2.51e+4 2.57e+4 NA   
10 Total de… 2.55e+4 NA    2.68e+4 3.08e+4 3.15e+4 3.32e+4 3.58e+4 3.64e+4 NA   
11 Share of… 2.25e-1 NA    2.42e-1 3.01e-1 3.01e-1 2.96e-1 2.98e-1 2.93e-1 NA   
# ℹ 13 more variables: ...11 <dbl>, ...12 <dbl>, ...13 <dbl>, ...14 <dbl>,
#   ...15 <dbl>, ...16 <dbl>, ...17 <lgl>, ...18 <dbl>, ...19 <dbl>,
#   ...20 <dbl>, ...21 <dbl>, ...22 <dbl>, ...23 <dbl>

Putting everything together

# Get the column names
mineral_info_col_names <- names(mineral_info)

mineral_info_col_names
 [1] "...1"  "...2"  "...3"  "...4"  "...5"  "...6"  "...7"  "...8"  "...9" 
[10] "...10" "...11" "...12" "...13" "...14" "...15" "...16" "...17" "...18"
[19] "...19" "...20" "...21" "...22" "...23"
#add the names as a column with the sheet header data we tidied above
sheet_headers_and_col_names <- sheet_header_processed |> 
  add_column(mineral_info_col_names = mineral_info_col_names)

sheet_headers_and_col_names
# A tibble: 23 × 3
   scenario                 year  mineral_info_col_names
   <chr>                    <chr> <chr>                 
 1 Current Year             <NA>  ...1                  
 2 Current Year             2022  ...2                  
 3 Current Year             <NA>  ...3                  
 4 Stated policies scenario 2025  ...4                  
 5 Stated policies scenario 2030  ...5                  
 6 Stated policies scenario 2035  ...6                  
 7 Stated policies scenario 2040  ...7                  
 8 Stated policies scenario 2045  ...8                  
 9 Stated policies scenario 2050  ...9                  
10 Stated policies scenario <NA>  ...10                 
# ℹ 13 more rows
# pivot the mineral info into a tidy long-format, and we’ll add the mineral name
mineral_info_long <- mineral_info |> 
  rename(indicator = `...1`) |> 
  pivot_longer(cols = -indicator,
               names_to = "mineral_info_col_names") |> 
  add_column(mineral_name)

mineral_info_long
# A tibble: 242 × 4
   indicator mineral_info_col_names value mineral_name
   <chr>     <chr>                  <dbl> <chr>       
 1 Solar PV  ...2                    682. Copper      
 2 Solar PV  ...3                     NA  Copper      
 3 Solar PV  ...4                    779. Copper      
 4 Solar PV  ...5                    907. Copper      
 5 Solar PV  ...6                    925. Copper      
 6 Solar PV  ...7                    959. Copper      
 7 Solar PV  ...8                   1122. Copper      
 8 Solar PV  ...9                   1262. Copper      
 9 Solar PV  ...10                    NA  Copper      
10 Solar PV  ...11                   923. Copper      
# ℹ 232 more rows
# combine the tidied header with the tidied table.

Keep putting everything together

# combine the tidied header with the tidied table. We do this by joining the data by the column names
combined_data <- mineral_info_long |> 
  left_join(sheet_headers_and_col_names, by = join_by(mineral_info_col_names)) |> 
  # filter out what were empty columns (where years are NA) 
  filter(!is.na(year)) |> 
  # case_when is supercharged if else (like if else except it can take multiple conditions)
  mutate(unit = case_when(
    indicator == "Share of clean technologies in total demand" ~ "Percent",
    .default = "kiloton"
    ),
    # convert the year column from character to numeric
    year = as.integer(year)
  ) |> 
  select(mineral_name, indicator, scenario, unit, year, value)

combined_data
# A tibble: 209 × 6
   mineral_name indicator scenario                   unit     year value
   <chr>        <chr>     <chr>                      <chr>   <int> <dbl>
 1 Copper       Solar PV  Current Year               kiloton  2022  682.
 2 Copper       Solar PV  Stated policies scenario   kiloton  2025  779.
 3 Copper       Solar PV  Stated policies scenario   kiloton  2030  907.
 4 Copper       Solar PV  Stated policies scenario   kiloton  2035  925.
 5 Copper       Solar PV  Stated policies scenario   kiloton  2040  959.
 6 Copper       Solar PV  Stated policies scenario   kiloton  2045 1122.
 7 Copper       Solar PV  Stated policies scenario   kiloton  2050 1262.
 8 Copper       Solar PV  Announced pledges scenario kiloton  2025  923.
 9 Copper       Solar PV  Announced pledges scenario kiloton  2030 1177.
10 Copper       Solar PV  Announced pledges scenario kiloton  2035 1369.
# ℹ 199 more rows

Function to read the data

read_iea_mineral_table <-
  function(mineral_name_range, mineral_info_range) {
    mineral_name <-
      read_key_minerals_sheet(range = mineral_name_range) |>
      pull()
    
    mineral_info <- read_key_minerals_sheet(range = mineral_info_range)
    
    mineral_info_col_names <- names(mineral_info)
    
    mineral_info_long <- mineral_info |>
      rename(indicator = `...1`) |>
      pivot_longer(cols = -indicator,
                   names_to = "mineral_info_col_names") |>
      add_column(mineral_name)
    
    combined_data <- mineral_info_long |>
      left_join(sheet_headers_and_col_names, by = join_by(mineral_info_col_names)) |>
      # filter out what were empty columns (where years are NA)
      filter(!is.na(year)) |>
      # case_when is supercharged if else
      mutate(
        unit = case_when(
          indicator == "Share of clean technologies in total demand" ~ "Percent",
          .default = "kiloton"
        ),
        # convert the year column from character to numeric
        year = as.integer(year)
      ) |>
      select(mineral_name, indicator, scenario, unit, year, value)
    
    combined_data
  }

# bind all data on one file
copper_table <- read_iea_mineral_table(
  mineral_name_range = "A7",
  mineral_info_range = "A8:W18"
)
New names:
New names:
• `` -> `...1`
cobalt_table <- read_iea_mineral_table(
  mineral_name_range = "A21",
  mineral_info_range = "A22:W29"
)
New names:
New names:
• `` -> `...1`
lithium_table <- read_iea_mineral_table(
  mineral_name_range = "A32",
  mineral_info_range = "A33:W38"
)
New names:
New names:
• `` -> `...1`
nickel_table <- read_iea_mineral_table(
  mineral_name_range = "A41",
  mineral_info_range = "A42:W51"
)
New names:
New names:
• `` -> `...1`
neodymium_table <- read_iea_mineral_table(
  mineral_name_range = "A54",
  mineral_info_range = "A55:W60"
)
New names:
New names:
• `` -> `...1`
final_iea_minerals_table <- copper_table |> 
  bind_rows(cobalt_table) |> 
  bind_rows(lithium_table) |>
  bind_rows(nickel_table) |>
  bind_rows(neodymium_table)

final_iea_minerals_table
# A tibble: 779 × 6
   mineral_name indicator scenario                   unit     year value
   <chr>        <chr>     <chr>                      <chr>   <int> <dbl>
 1 Copper       Solar PV  Current Year               kiloton  2022  682.
 2 Copper       Solar PV  Stated policies scenario   kiloton  2025  779.
 3 Copper       Solar PV  Stated policies scenario   kiloton  2030  907.
 4 Copper       Solar PV  Stated policies scenario   kiloton  2035  925.
 5 Copper       Solar PV  Stated policies scenario   kiloton  2040  959.
 6 Copper       Solar PV  Stated policies scenario   kiloton  2045 1122.
 7 Copper       Solar PV  Stated policies scenario   kiloton  2050 1262.
 8 Copper       Solar PV  Announced pledges scenario kiloton  2025  923.
 9 Copper       Solar PV  Announced pledges scenario kiloton  2030 1177.
10 Copper       Solar PV  Announced pledges scenario kiloton  2035 1369.
# ℹ 769 more rows

Writing data into folder

write_csv(final_iea_minerals_table,here("data", "iea_total_demand_for_critical_minerals.csv"))

# To use cleaned data
our_cleaned_data <- here("data", "iea_total_demand_for_critical_minerals.csv") |> 
  read_csv()
Rows: 779 Columns: 6
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (4): mineral_name, indicator, scenario, unit
dbl (2): year, value

ℹ 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.
our_cleaned_data
# A tibble: 779 × 6
   mineral_name indicator scenario                   unit     year value
   <chr>        <chr>     <chr>                      <chr>   <dbl> <dbl>
 1 Copper       Solar PV  Current Year               kiloton  2022  682.
 2 Copper       Solar PV  Stated policies scenario   kiloton  2025  779.
 3 Copper       Solar PV  Stated policies scenario   kiloton  2030  907.
 4 Copper       Solar PV  Stated policies scenario   kiloton  2035  925.
 5 Copper       Solar PV  Stated policies scenario   kiloton  2040  959.
 6 Copper       Solar PV  Stated policies scenario   kiloton  2045 1122.
 7 Copper       Solar PV  Stated policies scenario   kiloton  2050 1262.
 8 Copper       Solar PV  Announced pledges scenario kiloton  2025  923.
 9 Copper       Solar PV  Announced pledges scenario kiloton  2030 1177.
10 Copper       Solar PV  Announced pledges scenario kiloton  2035 1369.
# ℹ 769 more rows

HW Question

Read in 2.3 EV

read_ev_sheet <- partial(
  # provide the function, in this case readxl::read_excel()
  .f = read_excel,
  # provide any arguments you want filled in
  path = path_to_sheet,
  sheet = "2.3 EV",
  col_names = FALSE
)

# now all we have to do is provide the range
sheet_header <- read_key_minerals_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
# A tibble: 2 × 23
  ...1   ...2 ...3  ...4    ...5  ...6  ...7  ...8  ...9 ...10 ...11 ...12 ...13
  <lgl> <dbl> <lgl> <chr>  <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> <chr> <dbl> <dbl>
1 NA       NA NA    State…    NA    NA    NA    NA    NA NA    Anno…    NA    NA
2 NA     2022 NA    2025    2030  2035  2040  2045  2050 NA    2025   2030  2035
# ℹ 10 more variables: ...14 <dbl>, ...15 <dbl>, ...16 <dbl>, ...17 <lgl>,
#   ...18 <chr>, ...19 <dbl>, ...20 <dbl>, ...21 <dbl>, ...22 <dbl>,
#   ...23 <dbl>
read_iea_ev_table <-
  function(mineral_name_range, mineral_info_range) {
    case_mineral_needs <-
      read_ev_sheet(range = mineral_name_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_name = `...1`) |>
      pivot_longer(cols = -mineral_name,
                   names_to = "mineral_info_col_names") |>
      add_column(case_mineral_needs)
    
    combined_data <- mineral_info_long |>
      left_join(sheet_headers_and_col_names, by = join_by(mineral_info_col_names)) |>
      # filter out what were empty columns (where years are NA)
      filter(!is.na(year)) |>
      # case_when is supercharged if else
      mutate(
        unit = case_when(
          mineral_name == "Total EV" ~ "kiloton",
          .default = "kiloton"
        ),
        # convert the year column from character to numeric
        year = as.integer(year)
      ) |>
      select(case_mineral_needs, mineral_name, scenario, unit, year, value)
    
    combined_data
  }


# bind all data on one file
constrained_nickel_supply <- read_iea_ev_table(
  mineral_name_range = "A7",
  mineral_info_range = "A8:W19"
)
New names:
New names:
• `` -> `...1`
wider_use_of_silicon_rich_anodes <- read_iea_ev_table(
  mineral_name_range = "A22",
  mineral_info_range = "A23:W34"
)
New names:
New names:
• `` -> `...1`
faster_uptake_of_solid_state_batteries <- read_iea_ev_table(
  mineral_name_range = "A37",
  mineral_info_range = "A38:W49"
)
New names:
New names:
• `` -> `...1`
lower_battery_sizes <- read_iea_ev_table(
  mineral_name_range = "A52",
  mineral_info_range = "A53:W64"
)
New names:
New names:
• `` -> `...1`
limited_battery_size_reduction <- read_iea_ev_table(
  mineral_name_range = "A67",
  mineral_info_range = "A68:W79"
)
New names:
New names:
• `` -> `...1`
steps_base_case <- read_iea_ev_table(
  mineral_name_range = "A82",
  mineral_info_range = "A83:W94"
)
New names:
New names:
• `` -> `...1`
final_iea_ev_table <- steps_base_case |> 
  bind_rows(constrained_nickel_supply) |> 
  bind_rows(wider_use_of_silicon_rich_anodes) |>
  bind_rows(faster_uptake_of_solid_state_batteries) |>
  bind_rows(lower_battery_sizes) |> 
  bind_rows(limited_battery_size_reduction) |> 
  bind_rows(steps_base_case) 

final_iea_ev_table
# A tibble: 1,596 × 6
   case_mineral_needs mineral_name scenario                   unit    year value
   <chr>              <chr>        <chr>                      <chr>  <int> <dbl>
 1 STEPS - Base case  Copper       Current Year               kilot…  2022  373.
 2 STEPS - Base case  Copper       Stated policies scenario   kilot…  2025  580.
 3 STEPS - Base case  Copper       Stated policies scenario   kilot…  2030 1044.
 4 STEPS - Base case  Copper       Stated policies scenario   kilot…  2035 1219.
 5 STEPS - Base case  Copper       Stated policies scenario   kilot…  2040 1421.
 6 STEPS - Base case  Copper       Stated policies scenario   kilot…  2045 1458.
 7 STEPS - Base case  Copper       Stated policies scenario   kilot…  2050 1346.
 8 STEPS - Base case  Copper       Announced pledges scenario kilot…  2025  689.
 9 STEPS - Base case  Copper       Announced pledges scenario kilot…  2030 1592.
10 STEPS - Base case  Copper       Announced pledges scenario kilot…  2035 2570.
# ℹ 1,586 more rows

Write in cleaned Data

write_csv(final_iea_ev_table,here("data", "iea_ev_demand_for_minerals.csv"))

To use our cleaned data

# To use cleaned data
our_cleaned_data_ev <- here("data", "iea_ev_demand_for_minerals.csv") |> 
  read_csv()
Rows: 1596 Columns: 6
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (4): case_mineral_needs, mineral_name, scenario, unit
dbl (2): year, value

ℹ 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.
our_cleaned_data_ev
# A tibble: 1,596 × 6
   case_mineral_needs mineral_name scenario                   unit    year value
   <chr>              <chr>        <chr>                      <chr>  <dbl> <dbl>
 1 STEPS - Base case  Copper       Current Year               kilot…  2022  373.
 2 STEPS - Base case  Copper       Stated policies scenario   kilot…  2025  580.
 3 STEPS - Base case  Copper       Stated policies scenario   kilot…  2030 1044.
 4 STEPS - Base case  Copper       Stated policies scenario   kilot…  2035 1219.
 5 STEPS - Base case  Copper       Stated policies scenario   kilot…  2040 1421.
 6 STEPS - Base case  Copper       Stated policies scenario   kilot…  2045 1458.
 7 STEPS - Base case  Copper       Stated policies scenario   kilot…  2050 1346.
 8 STEPS - Base case  Copper       Announced pledges scenario kilot…  2025  689.
 9 STEPS - Base case  Copper       Announced pledges scenario kilot…  2030 1592.
10 STEPS - Base case  Copper       Announced pledges scenario kilot…  2035 2570.
# ℹ 1,586 more rows

Producing 5 visualizations

Use the tidied dataset to come up with 5 compelling data visualizations that illustrate key actionable insights about how policy scenarios, and technological scenarios will impact demand for copper and cobalt.

# Setting up for graphing

library(ggplot2)
library(dplyr)
library(readr)

# Load the dataset
our_cleaned_data_ev <- here("data", "iea_ev_demand_for_minerals.csv") |> 
  read_csv()
Rows: 1596 Columns: 6
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (4): case_mineral_needs, mineral_name, scenario, unit
dbl (2): year, value

ℹ 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.
library(esquisse)

our_cleaned_data_copper <- our_cleaned_data_ev %>%
  filter(mineral_name %in% c("Copper", "Total EV")) %>%
  group_by(year) 


our_cleaned_data_copper
# A tibble: 266 × 6
# Groups:   year [7]
   case_mineral_needs mineral_name scenario                   unit    year value
   <chr>              <chr>        <chr>                      <chr>  <dbl> <dbl>
 1 STEPS - Base case  Copper       Current Year               kilot…  2022  373.
 2 STEPS - Base case  Copper       Stated policies scenario   kilot…  2025  580.
 3 STEPS - Base case  Copper       Stated policies scenario   kilot…  2030 1044.
 4 STEPS - Base case  Copper       Stated policies scenario   kilot…  2035 1219.
 5 STEPS - Base case  Copper       Stated policies scenario   kilot…  2040 1421.
 6 STEPS - Base case  Copper       Stated policies scenario   kilot…  2045 1458.
 7 STEPS - Base case  Copper       Stated policies scenario   kilot…  2050 1346.
 8 STEPS - Base case  Copper       Announced pledges scenario kilot…  2025  689.
 9 STEPS - Base case  Copper       Announced pledges scenario kilot…  2030 1592.
10 STEPS - Base case  Copper       Announced pledges scenario kilot…  2035 2570.
# ℹ 256 more rows
our_cleaned_data_cobalt <- our_cleaned_data_ev %>%
  filter(mineral_name %in% c("Cobalt", "Total EV")) %>%
  group_by(year) %>%
  mutate(total_KT_needed = sum(value))

our_cleaned_data_cobalt
# A tibble: 266 × 7
# Groups:   year [7]
   case_mineral_needs mineral_name scenario    unit   year value total_KT_needed
   <chr>              <chr>        <chr>       <chr> <dbl> <dbl>           <dbl>
 1 STEPS - Base case  Cobalt       Current Ye… kilo…  2022  64.6          10802.
 2 STEPS - Base case  Cobalt       Stated pol… kilo…  2025  66.4             NA 
 3 STEPS - Base case  Cobalt       Stated pol… kilo…  2030  73.9             NA 
 4 STEPS - Base case  Cobalt       Stated pol… kilo…  2035  77.7             NA 
 5 STEPS - Base case  Cobalt       Stated pol… kilo…  2040 105.              NA 
 6 STEPS - Base case  Cobalt       Stated pol… kilo…  2045 134.              NA 
 7 STEPS - Base case  Cobalt       Stated pol… kilo…  2050 146.              NA 
 8 STEPS - Base case  Cobalt       Announced … kilo…  2025  77.5             NA 
 9 STEPS - Base case  Cobalt       Announced … kilo…  2030 113.              NA 
10 STEPS - Base case  Cobalt       Announced … kilo…  2035 159.              NA 
# ℹ 256 more rows
#esquisser(our_cleaned_data)

# Data Visualization 1: Copper demand across different policy scenarios

Visualization 1

ggplot(our_cleaned_data_copper) +
  aes(
    x = year,
    y = value,
    fill = case_mineral_needs,
    colour = case_mineral_needs
  ) +
  geom_line() +
  scale_fill_manual(
    values = c(`Constrained nickel supply` = "#F8766D",
    `Faster uptake of solid state batteries` = "#ACA000",
    `Limited battery size reduction` = "#00BB4C",
    `Lower battery sizes` = "#00BAD5",
    `STEPS - Base case` = "#918BFD",
    `Wider use of silicon-rich anodes` = "#FF61C3")
  ) +
  scale_color_manual(
    values = c(`Constrained nickel supply` = "#F8766D",
    `Faster uptake of solid state batteries` = "#ACA000",
    `Limited battery size reduction` = "#00BB4C",
    `Lower battery sizes` = "#00BAD5",
    `STEPS - Base case` = "#918BFD",
    `Wider use of silicon-rich anodes` = "#FF61C3")
  ) +
  labs(
    x = "Year",
    y = "Kilotons",
    title = "Copper Needs by Year",
    subtitle = "by Different Case Specific Needs",
    caption = "Made by Yuhao Shen",
    color = "Cases"
  ) +
  theme_minimal() +
  facet_wrap(vars(case_mineral_needs))

Visualization 2

# Sorting out copper to total EV demand percentages
# Assuming 'Total EV' demand is consistent across the same 'scenario' within 'case_mineral_needs'
# and that 'case_mineral_needs' and 'scenario' combinations are unique for 'Copper' and 'Total EV'
our_cleaned_data_copper_only <- our_cleaned_data_ev %>%
  filter(mineral_name == "Copper")

our_cleaned_data_copper_only
# A tibble: 133 × 6
   case_mineral_needs mineral_name scenario                   unit    year value
   <chr>              <chr>        <chr>                      <chr>  <dbl> <dbl>
 1 STEPS - Base case  Copper       Current Year               kilot…  2022  373.
 2 STEPS - Base case  Copper       Stated policies scenario   kilot…  2025  580.
 3 STEPS - Base case  Copper       Stated policies scenario   kilot…  2030 1044.
 4 STEPS - Base case  Copper       Stated policies scenario   kilot…  2035 1219.
 5 STEPS - Base case  Copper       Stated policies scenario   kilot…  2040 1421.
 6 STEPS - Base case  Copper       Stated policies scenario   kilot…  2045 1458.
 7 STEPS - Base case  Copper       Stated policies scenario   kilot…  2050 1346.
 8 STEPS - Base case  Copper       Announced pledges scenario kilot…  2025  689.
 9 STEPS - Base case  Copper       Announced pledges scenario kilot…  2030 1592.
10 STEPS - Base case  Copper       Announced pledges scenario kilot…  2035 2570.
# ℹ 123 more rows
# First, isolate the total EV demand for each scenario
total_ev_demand <- our_cleaned_data_copper %>%
  filter(mineral_name == "Total EV") %>%
  select(case_mineral_needs, scenario, year, total_EV = value)

# Now merge this data back with the original dataset to get a corresponding total EV value for each row
data_with_ev <- our_cleaned_data_copper_only %>%
  left_join(total_ev_demand, by = c("case_mineral_needs", "scenario", "year")) %>%
  distinct()
Warning in left_join(., total_ev_demand, by = c("case_mineral_needs", "scenario", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 1 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
# Calculate the copper to total EV demand ratio
data_with_ev <- data_with_ev %>%
  mutate(copper_to_ev_ratio = ifelse(mineral_name == "Copper", value / total_EV * 100, NA))

# View the modified dataframe
print(data_with_ev)
# A tibble: 114 × 8
   case_mineral_needs mineral_name scenario           unit   year value total_EV
   <chr>              <chr>        <chr>              <chr> <dbl> <dbl>    <dbl>
 1 STEPS - Base case  Copper       Current Year       kilo…  2022  373.    1480.
 2 STEPS - Base case  Copper       Stated policies s… kilo…  2025  580.    2426.
 3 STEPS - Base case  Copper       Stated policies s… kilo…  2030 1044.    4231.
 4 STEPS - Base case  Copper       Stated policies s… kilo…  2035 1219.    5160.
 5 STEPS - Base case  Copper       Stated policies s… kilo…  2040 1421.    6063.
 6 STEPS - Base case  Copper       Stated policies s… kilo…  2045 1458.    6393.
 7 STEPS - Base case  Copper       Stated policies s… kilo…  2050 1346.    6011.
 8 STEPS - Base case  Copper       Announced pledges… kilo…  2025  689.    2847.
 9 STEPS - Base case  Copper       Announced pledges… kilo…  2030 1592.    6451.
10 STEPS - Base case  Copper       Announced pledges… kilo…  2035 2570.   10621.
# ℹ 104 more rows
# ℹ 1 more variable: copper_to_ev_ratio <dbl>
ggplot(data_with_ev) +
  aes(x = year, y = copper_to_ev_ratio, colour = scenario) +
  geom_point(shape = "circle", size = 1.5) +
  scale_color_hue(direction = 1) +
  labs(
    y = "Percentage",
    title = "Copper Demanded As Percentage in EV Market",
    subtitle = "case specific and by demand",
    caption = "Made by Yuhao Shen",
    color = "Scenarios"
  ) +
  theme_minimal() +
  facet_wrap(vars(case_mineral_needs))
Warning: Removed 12 rows containing missing values (`geom_point()`).

Visualization 3

ggplot(our_cleaned_data_cobalt) +
  aes(
    x = year,
    y = value,
    fill = case_mineral_needs,
    colour = case_mineral_needs
  ) +
  geom_line() +
  scale_fill_manual(
    values = c(`Constrained nickel supply` = "#F8766D",
    `Faster uptake of solid state batteries` = "#ACA000",
    `Limited battery size reduction` = "#00BB4C",
    `Lower battery sizes` = "#00BAD5",
    `STEPS - Base case` = "#918BFD",
    `Wider use of silicon-rich anodes` = "#FF61C3")
  ) +
  scale_color_manual(
    values = c(`Constrained nickel supply` = "#F8766D",
    `Faster uptake of solid state batteries` = "#ACA000",
    `Limited battery size reduction` = "#00BB4C",
    `Lower battery sizes` = "#00BAD5",
    `STEPS - Base case` = "#918BFD",
    `Wider use of silicon-rich anodes` = "#FF61C3")
  ) +
  labs(
    x = "Year",
    y = "Kilotons",
    title = "Cobalt Needs by Year",
    subtitle = "by Different Case Specific Needs",
    caption = "Made by Yuhao Shen",
    color = "Cases"
  ) +
  theme_minimal() +
  facet_wrap(vars(case_mineral_needs))

Visualization 4

our_cleaned_data_cobalt_only <- our_cleaned_data_ev %>%
  filter(mineral_name == "Cobalt")

our_cleaned_data_cobalt_only
# A tibble: 133 × 6
   case_mineral_needs mineral_name scenario                   unit    year value
   <chr>              <chr>        <chr>                      <chr>  <dbl> <dbl>
 1 STEPS - Base case  Cobalt       Current Year               kilot…  2022  64.6
 2 STEPS - Base case  Cobalt       Stated policies scenario   kilot…  2025  66.4
 3 STEPS - Base case  Cobalt       Stated policies scenario   kilot…  2030  73.9
 4 STEPS - Base case  Cobalt       Stated policies scenario   kilot…  2035  77.7
 5 STEPS - Base case  Cobalt       Stated policies scenario   kilot…  2040 105. 
 6 STEPS - Base case  Cobalt       Stated policies scenario   kilot…  2045 134. 
 7 STEPS - Base case  Cobalt       Stated policies scenario   kilot…  2050 146. 
 8 STEPS - Base case  Cobalt       Announced pledges scenario kilot…  2025  77.5
 9 STEPS - Base case  Cobalt       Announced pledges scenario kilot…  2030 113. 
10 STEPS - Base case  Cobalt       Announced pledges scenario kilot…  2035 159. 
# ℹ 123 more rows
# First, isolate the total EV demand for each scenario
total_ev_demand_cobalt <- our_cleaned_data_cobalt %>%
  filter(mineral_name == "Total EV") %>%
  select(case_mineral_needs, scenario, year, total_EV = value)

# Now merge this data back with the original dataset to get a corresponding total EV value for each row
data_with_ev_co <- our_cleaned_data_cobalt_only %>%
  left_join(total_ev_demand_cobalt, by = c("case_mineral_needs", "scenario", "year")) 
Warning in left_join(., total_ev_demand_cobalt, by = c("case_mineral_needs", : Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 1 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
# Calculate the copper to total EV demand ratio
data_with_ev_co <- data_with_ev_co %>%
  distinct() %>%
  mutate(cobalt_to_ev_ratio = ifelse(mineral_name == "Cobalt", value / total_EV * 100, NA)) 

# View the modified dataframe
print(data_with_ev_co)
# A tibble: 114 × 8
   case_mineral_needs mineral_name scenario           unit   year value total_EV
   <chr>              <chr>        <chr>              <chr> <dbl> <dbl>    <dbl>
 1 STEPS - Base case  Cobalt       Current Year       kilo…  2022  64.6    1480.
 2 STEPS - Base case  Cobalt       Stated policies s… kilo…  2025  66.4    2426.
 3 STEPS - Base case  Cobalt       Stated policies s… kilo…  2030  73.9    4231.
 4 STEPS - Base case  Cobalt       Stated policies s… kilo…  2035  77.7    5160.
 5 STEPS - Base case  Cobalt       Stated policies s… kilo…  2040 105.     6063.
 6 STEPS - Base case  Cobalt       Stated policies s… kilo…  2045 134.     6393.
 7 STEPS - Base case  Cobalt       Stated policies s… kilo…  2050 146.     6011.
 8 STEPS - Base case  Cobalt       Announced pledges… kilo…  2025  77.5    2847.
 9 STEPS - Base case  Cobalt       Announced pledges… kilo…  2030 113.     6451.
10 STEPS - Base case  Cobalt       Announced pledges… kilo…  2035 159.    10621.
# ℹ 104 more rows
# ℹ 1 more variable: cobalt_to_ev_ratio <dbl>
ggplot(data_with_ev_co) +
  aes(x = year, y = cobalt_to_ev_ratio, colour = scenario) +
  geom_point(shape = "circle", size = 1.5) +
  scale_color_hue(direction = 1) +
  labs(
    y = "Percentage",
    title = "Cobalt Demanded As Percentage in EV Market",
    subtitle = "case specific and by demand",
    caption = "Made by Yuhao Shen",
    color = "Scenarios"
  ) +
  theme_minimal() +
  facet_wrap(vars(case_mineral_needs))
Warning: Removed 12 rows containing missing values (`geom_point()`).

Visualization 5

our_cleaned_data_coco <- our_cleaned_data_ev %>%
  filter(mineral_name %in% c("Copper", "Cobalt")) %>%
  group_by(year)

ggplot(our_cleaned_data_coco) +
  aes(x = year, y = value, colour = mineral_name) +
  geom_point(shape = "circle", size = 1.5) +
  scale_color_hue(direction = 1) +
  scale_y_continuous(trans = "log10") +
  labs(
    x = "Year",
    y = "Demand in Kilotons",
    title = "Copper and Cobalt Demanded ",
    caption = "Made by Yuhao Shen",
    color = "Mineral Name"
  ) +
  theme_minimal() +
  facet_wrap(vars(case_mineral_needs))
Warning: Removed 24 rows containing missing values (`geom_point()`).