Stocurile mondiale de petrol brut de @ellis2013nz

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

Cât țiței are la dispoziție lumea? Mințile întrebătoare vor să știe, dar este surprinzător de dificil să obții un singur număr. Auzim mai multe despre niveluri relative „mai puțin decât în ​​orice moment de la XXXX” și schimbările „au scăzut cu X milioane de barili în ultima lună” decât estimările nivelului absolut. Agenția Internațională pentru Energie (IEA) publică o mulțime de informații despre nivelurile și modificările stocurilor, dar estimările consolidate ale stocurilor globale, din câte văd, nu sunt disponibile publicului.

Cu toate acestea, Joint Organizations Data Initiative (JODI) publică estimări lunare la nivel de țară, raportate în mod voluntar de către țări. Partenerii JODI sunt APEC Energy Working Group, Eurostat, GECF, IEA, Organizația pentru Energie din America Latină și Caraibe (OLACDE), OPEC și Divizia de Statistică a ONU. Este un set de parteneri foarte reputați! Și datele JODI sunt disponibile pentru descărcare.

Doar 38 de țări au date pentru fiecare lună JODI colectează date, dar acestea includ SUA, Japonia, Marea Britanie, țările europene cheie și alți câțiva mari deținători precum Arabia Saudită, Coreea de Sud, Taiwan și Nigeria. Omisiunile esențiale includ, din păcate, dar deloc surprinzător, China, Rusia și Venezuela. Dar e mult mai bine decât nimic.

Scopul postării de astăzi este să-mi aduc graficul de mai jos, care arată cea mai bună imagine posibilă a stocurilor de țiței ale țărilor care raportează aceste stocuri, în comparație cu o estimare aproximativă de 85 de milioane de barili pe zi de producție globală a rafinăriilor de țiței:

Acea diagramă face acum parte din pagina mea de monitorizare a crizei de combustibil, care este actualizată cel puțin săptămânal.

Povestea generală este că avem într-adevăr mai puțin țiței în depozit decât oricând de când aceste înregistrări au început la începutul anilor 2000; dar mai sunt în jur de 20 de zile de acoperire a rafinăriei. Și în ritmul în care scade, va dura aproape doi ani pentru a ajunge la doar 10 zile de acoperire a rafinăriei. De fapt, m-aș aștepta ca panica pieței să se instaleze la aproximativ 15 zile de acoperire, dar sunt conservator aici, deoarece sunt interesat de punctul în care lumea a epuizat cu adevărat orice tampon împotriva întreruperilor de aprovizionare.

OK, restul blogului se referă în principal despre cum am obținut acele date, am făcut câteva alegeri cu privire la excluderea unor țări și am desenat graficul final.

În primul rând, descărcarea datelor. Există un CSV separat pentru fiecare an, începând cu 2002. Iată cum le descarc pe toate și le import în R

library(tidyverse)
library(glue)
library(janitor)
library(countrycode)

# Set to FALSE if you don't want to download the latest 2026 data. Will need to
# update all this workflow once we get to 2027 data.
update_2026 <- TRUE

#-----------------Downloads----------------

if (update_2026) {
  dir.create("fuel-crisis", showWarnings = FALSE)
  # Download current year. This will get more complete month by month.
  df <- here("fuel-crisis/jodi-2026.csv")
  url <- "https://www.jodidata.org/_resources/files/downloads/oil-data/annual-csv/primary/primaryyear2026.csv"
  download.file(url, destfile = df)
}


# Download historical data; only needed to be done once
if (!file.exists(here("fuel-crisis/jodi-2025.csv"))) {
  for (y in 2002:2025) {
    df <- here(glue("fuel-crisis/jodi-{y}.csv"))
    url <- glue(
      "https://www.jodidata.org/_resources/files/downloads/oil-data/annual-csv/primary/{y}.csv"
    )
    download.file(url, destfile = df)
  }
}

#----------------Import data------------------

# Import all the data to R:
jodi_hist_l <- list()
for (i in 1:25) {
  df <- here(glue("fuel-crisis/jodi-{i+2001}.csv"))
  jodi_hist_l((i)) <- read.csv(df) |> as_tibble()
}

jodi_hist <- bind_rows(jodi_hist_l) |>
  clean_names()

# see https://www.jodidata.org/_resources/files/downloads/oil-data/jodi-oil-wdb-item-names-ver2017.pdf
# for guide on what everything is
# CLOSTLV is closing stocks
# CRUDEOIL is just crude
# TOTCRUDE also includes NGL and refinery feedstocks, additives and other hydrocarbons and has a lot more missing data.

Acum că am datele, vreau să înțeleg lipsa. La un moment dat, m-am gândit la imputarea valorilor lipsă din diferite țări și, prin urmare, am făcut o versiune mare a datelor cu ideea că aș putea modela stocurile de țiței pe baza mișcării unora dintre numeroasele variabile din date. În cele din urmă, am abandonat acest lucru când mi-am dat seama cât de puține informații semnificative existau, de exemplu, despre China și Rusia, pentru a face acest lucru în mod semnificativ. Dar încă pot folosi jodi_wide set de date creat mai jos pentru majoritatea parcelelor mele:

#-----------explore which countries missing---------------------
jodi_wide <- jodi_hist |>
  #  filter(energy_product == "CRUDEOIL") |>
  mutate(
    dimensions = paste(energy_product, flow_breakdown, unit_measure, sep = "|")
  ) |>
  mutate(obs_value = as.numeric(obs_value)) |>
  select(ref_area, time_period, dimensions, obs_value) |>
  drop_na() |>
  spread(dimensions, obs_value, fill = NA) |>
  rename(crude = `CRUDEOIL|CLOSTLV|KBBL`) |>
  complete(ref_area, time_period, fill = list(crude = NA)) |>
  mutate(
    country = countrycode(
      ref_area,
      origin = "iso2c",
      destination = "country.name.en"
    )
  ) |>
  mutate(
    ref_area = fct_relevel(ref_area, "US"),
    date = ym(time_period),
    date_n = as.numeric(date)
  )

last_date <- jodi_wide |>
  filter(!is.na(crude)) |>
  summarise(ld = max(date)) |>
  mutate(ld = format(ld, "%B %Y")) |>
  pull(ld)

the_caption <- glue(
  "Source: JODI-OIL World Database. Most recent data is for {last_date}."
)


jodi_wide |>
  ggplot(aes(x = date, y = crude / 1000, colour = ref_area)) +
  geom_line() +
  theme(legend.position = "none") +
  scale_y_continuous(label = comma) +
  labs(
    x = "",
    y = "Millions of barrels",
    caption = the_caption,
    title = "Stocks of crude oil",
    subtitle = "Coloured by country; legend not shown."
  )

Iată primul complot explorator, o diagramă cu linii a tuturor țărilor. Cel mare aici este SUA; Nu am arătat legenda pentru că ar ocupa prea mult spațiu.

În continuare, vreau să identific care țări au date lipsă și, în special, cele care pot contribui semnificativ la stocurile mondiale de țiței, dar care lipsesc doar câteva observații pe care poate le-aș putea imputa. Încep prin a face o country_sum set de date rezumat:

possible_obs <- length(unique(jodi_wide$time_period))

country_sum <- jodi_wide |>
  group_by(ref_area, country) |>
  summarise(
    crude_total = mean(crude, na.rm = TRUE),
    missing_obs = sum(is.na(crude))
  ) |>
  ungroup() |>
  # particular problem if a country has any missing observations and averages
  # 10 million barrels or more of stock:
  mutate(
    all_there = as.logical(missing_obs == 0),
    some_missing = as.logical(
      missing_obs > 0 & missing_obs < possible_obs & !is.na(crude_total)
    ),
    problem = as.logical(crude_total > 10000 & missing_obs > 0)
  ) |>
  arrange(desc(crude_total))

good_countries <- country_sum |>
  filter(all_there) |>
  pull(country)

good_countries

Asta îmi oferă această listă a țărilor care au un set complet de observații (cele mai mari stocuri de țiței sunt enumerate mai întâi):

> good_countries
 (1) "United States"  "Japan"          "Saudi Arabia"  
 (4) "Germany"        "South Korea"    "Canada"        
 (7) "France"         "Turkey"         "Italy"         
(10) "Poland"         "Spain"          "United Kingdom"
(13) "Netherlands"    "Taiwan"         "Thailand"      
(16) "Norway"         "Nigeria"        "Australia"     
(19) "Sweden"         "Finland"        "Czechia"       
(22) "Hungary"        "Portugal"       "Austria"       
(25) "Slovakia"       "Belgium"        "Denmark"       
(28) "Chile"          "New Zealand"    "Azerbaijan"    
(31) "Brunei"         "Ireland"        "Switzerland"   
(34) "Estonia"        "Iceland"        "Luxembourg"    
(37) "Latvia"         "Slovenia"   

De asemenea, îmi permite să găsesc câteva țări care nu au deloc observații:

# 20 countries that never have any observations for Crude:
never_crude <- filter(country_sum, is.na(problem))
print(never_crude$country)
> print(never_crude$country)
 (1) "Albania"             "Bangladesh"         
 (3) "Bermuda"             "Belarus"            
 (5) "China"               "Egypt"              
 (7) "Georgia"             "Hong Kong SAR China"
 (9) "Moldova"             "Malta"              
(11) "Malaysia"            "Niger"              
(13) "Nepal"               "Sudan"              
(15) "Singapore"           "Syria"              
(17) "Eswatini"            "Tajikistan"         
(19) "Vietnam"             "Yemen" 

Descoperirea că erau 20 dintre acestea, inclusiv China și Singapore, este ceea ce m-a făcut să decid că nu are rost să încerc să imput valorile lipsă. Dar am explorat un pic mai mult ce țări aveau unele observații, dar nu un set complet. În primul rând, o diagramă de dispersie a tuturor acestor țări:

…și apoi o serie temporală fațetă a celor mai mari 9 țări cu probleme.

Am considerat că poate aș putea atribui valori pentru India, Mexic, Venezuela și Brazilia pe baza nivelurilor lor care cresc sau scad în medie la aproximativ ritmul mondial, dar am decis că acest lucru pur și simplu nu va merita. Și în mod clar Rusia le depășește pe toate și pur și simplu nu merită încercată, având în vedere circumstanțele speciale de acolo.

Iată codul pentru cele două parcele:

country_sum |>
  ggplot(aes(
    x = crude_total,
    y = missing_obs,
    label = ref_area,
    colour = problem
  )) +
  geom_text() +
  scale_x_log10(label = comma) +
  labs(
    x = "Average crude stocks (thousands of barrels)",
    y = "Number of months missing an observation",
    caption = the_caption,
    title = "Size of crude stocks by number of missing observations"
  ) +
  theme(legend.position = "none")

# The nine biggest problem countries in terms of partly missing data
jodi_wide |>
  #  filter(ref_area %in% filter(country_sum, some_missing)$ref_area) |>
  filter(ref_area %in% filter(country_sum, problem)$ref_area) |>
  mutate(country = fct_reorder(country, crude)) |>
  ggplot(aes(x = date, y = crude / 1000)) +
  facet_wrap(~country, scales = "free_y") +
  geom_line(colour = "steelblue") +
  expand_limits(y = 0) +
  scale_y_continuous(label = comma) +
  labs(
    x = "",
    y = "Millions of barrels",
    title = "Crude oil stocks of countries missing at least one data point",
    caption = the_caption
  )

Un ultim complot exploratoriu pentru a încerca să vedem impactul includerii sau renunțării acelor țări cu date parțiale, iată o diagramă cu zone:

Gândindu-mă în special la situația Rusiei, am decis cel mai bine să exclud cu totul acele țări parțial dispărute.

Iată codul pentru acea diagramă (și care, de asemenea, face ca crude_stocks obiect de date pe care îl voi folosi în curând pentru complotul meu real)

#----------------Summarise and draw chart------------

# Crude oil stocks (excludes NGL etc because often missing data):
crude_stocks <- jodi_wide |>
  left_join(select(country_sum, ref_area, some_missing), by = "ref_area") |>
  group_by(date, some_missing) |>
  summarise(
    total_crude_mbbl = sum(crude, na.rm = TRUE) / 1000,
    reporting_countries = length(unique(ref_area))
  ) |>
  ungroup()

crude_stocks |>
  ggplot(aes(x = date, y = total_crude_mbbl, fill = some_missing)) +
  geom_area() +
  labs(
    caption = the_caption,
    y = "Total crude oil (millions of barrels)",
    x = "",
    title = "Total crude oil by missingness status of countries",
    fill = "Countries missing any data:"
  )

În cele din urmă, codul pentru a desena diagrama reală de prezentare. Un număr magic-cheie aici este utilizarea țițeiului în rafinărie la nivel mondial la 85 de milioane de barili pe zi; aceasta este valoarea aproximativă a „produselor brute” dintr-un raport recent IEA privind piața petrolului. Rețineți că aceasta este mai mică decât valorile citate în mod obișnuit de aproximativ 105 milioane de barili pe zi, care includ LGN, biocombustibili și alți combustibili lichizi. Valoarea de 85 m pe zi este cea potrivită pentru mine să o folosesc când mă uit la seria CRUDEOIL în datele mele originale JODI; menționând că au și o serie TOTCRUDE care s-ar referi la cifra de 105 m pe zi, dar care are mult mai multe date lipsă și, fără îndoială, nu este chiar ceea ce mă interesează cel mai mult.

# note the commonly used figure of 105 is for TOTCRUDE, not just CRUDEOIl (which is more like 85)
world_use_per_day <- 85

# Summary for calculation of how long until only 10 days of cover:
war_summary <- crude_stocks |>
  # restrict it to countries that have observations for all months
  filter(!some_missing) |>
  summarise(
    prewar = total_crude_mbbl(date == "2026-03-01"),
    latest = total_crude_mbbl(date == max(date)),
    days = max(date) - as.Date("2026-03-01")
  ) |>
  mutate(
    rate = (prewar - latest) / as.numeric(days),
    ten_days_left = (latest - world_use_per_day * 10) / rate
  )

# draw chart:
crude_stocks |>
  filter(!some_missing) |>
  ggplot(aes(x = date, y = total_crude_mbbl)) +
  geom_line(colour = "blue") +
  geom_hline(yintercept = world_use_per_day * 10, colour = "darkred") + #expand_limits(y = world_use_per_day * 10) +
  expand_limits(y = 0) +
  scale_y_continuous(
    label = comma_format(suffix = "m"),
    sec.axis = sec_axis(
      ~ . / world_use_per_day,
      name = glue(
        "Days of crude inventory cover,nat {world_use_per_day} million barrels per day"
      )
    )
  ) +
  labs(
    x = glue(
      "{length(good_countries)} countries in total have data for all months in this period."
    ),
    title = "Crude oil stocks of consistently reporting countries worldwide",
    y = "Millions of barrels",
    subtitle = glue(
      "Excluding countries with any missing data (eg China, Russia, India, Venezuela).
If the rate of decline since March 2026 continued, crude inventories would fall to 10 days of cover in {round(war_summary$ten_days_left / 7)} weeks."
    ),
    caption = the_caption
  )

Asta îmi aduce această grafică, care este cea pe care o voi ține la curent de acum înainte:

Asta e tot pentru azi. Ai grijă acolo și cumpără-ți un vehicul electric și solar pe acoperiș, dacă poți!

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.