Skip to content

View or edit on GitHub

This page is synchronized from trase/products/analysis/notebooks/engagement/brazil_beef/MARFRIG/2025_JAN_COUNCIL_REPORT.md. Last modified on 2026-02-03 10:30 CET by Jason J. Benedict. Please view or edit the original file there; changes should be reflected here after a midnight build (CET time), or manually triggering it with a GitHub action (link).

Marfrig 2025 Analysis

Erasmus 2025-01-16

Analysis of the supplier data shared by Marfrig in January 2025.

This is currently exploratory.

Load data

Load data from 2025.

# Load data shared in 2025
# [A] Direct suppliers
dir_suppliers <- s3read_using(
  object = "brazil/beef/norges_council/input/2024-Relação de Fazendas Diretas e Indiretas - Amz e Cerrado.xlsx",
  FUN = read_excel,
  bucket = ts,
  sheet = "Diretos", 
  skip = 1) %>% 
  mutate(POSITION = "DIRECT") %>%
  janitor::clean_names("screaming_snake")

# [B] Indirect suppliers
ind_suppliers <- s3read_using(
  object = "brazil/beef/norges_council/input/2024-Relação de Fazendas Diretas e Indiretas - Amz e Cerrado.xlsx",
  FUN = read_excel,
  bucket = ts,
  sheet = "Indiretos", 
  skip = 1) %>% 
  mutate(POSITION = "INDIRECT") %>%
  janitor::clean_names("screaming_snake")

Comments: * they don’t provide the level of risk of indirect suppliers

Load data from 2023.

# Load data shared in 2023 - volumes/risk per supplier
dir_suppliers_2023 <- s3read_using(
  object = "brazil/beef/norges_council/input/CAR - Fazendas Set 2021 a Ago 2022 (Detalhes volume e risco).xlsx", 
  FUN = read_excel,
  bucket = ts
) %>%
  mutate(POSITION = "DIRECT") %>%
  janitor::clean_names("screaming_snake") %>%
  rename(LEVEL_RISK = RISCO, 
         PRODUCTION_CYCLE = SISTEMA_DE_PRODUCAO, 
         CATTLE = NO_DE_ANIMAIS,
         WEIGHT_KG = PESO_KG)

# Load number of direct/indirect suppliers in 2023
# ... using file with deforestation on Marfrig supplier properties (with PRODES BIOMAS)
supplier_count_2023 <- s3read_using(
  object = "brazil/beef/norges_council/results/nrwcon_results_v3.xlsx", 
  FUN = read_excel,
  bucket = ts, 
  sheet = "results_prodes_biomes"
) %>%
  group_by(fornecedor) %>% 
  summarise(N = n()) %>% 
  ungroup() %>%
  rename(POSITION = fornecedor) %>%
  mutate(SOURCE = "2023 DISCLOSURE")

Direct sourcing summary

dir_summary <- dir_suppliers %>% 
  group_by(PRODUCTION_CYCLE) %>%
  summarise_at(.vars = c("CATTLE", "WEIGHT_KG"), .funs = sum) %>%
  ungroup() %>%
  mutate(TOTAL_CATTLE = sum(CATTLE), 
         TOTAL_KG = sum(WEIGHT_KG), 
         PERC_CATTLE = CATTLE / TOTAL_CATTLE * 100,
         PERC_KG = WEIGHT_KG / sum(WEIGHT_KG) * 100) 
dir_summary %>% 
  select(PRODUCTION_CYCLE, PERC_CATTLE, PERC_KG) %>%
  pivot_longer(cols = c(PERC_CATTLE, PERC_KG), names_to = "METRIC", values_to = "VALUE") %>%
  ggplot(aes("", VALUE, fill = PRODUCTION_CYCLE)) + 
  geom_bar(stat = "identity") + 
  facet_grid(~METRIC) + 
  theme_minimal() +
  coord_flip() +
  theme(legend.position = "bottom")

Suppliers per state

dir_state <- dir_suppliers %>% mutate(STATE = str_sub(CAR, start = 1, end = 2)) %>% count(POSITION, STATE)
ind_state <- ind_suppliers %>% mutate(STATE = str_sub(CAR, start = 1, end = 2)) %>% count(POSITION, STATE)
bind_rows(ind_state, dir_state)
## # A tibble: 6 × 3
##   POSITION STATE     n
##   <chr>    <chr> <int>
## 1 INDIRECT MS        1
## 2 INDIRECT MT      402
## 3 INDIRECT RO       46
## 4 INDIRECT SP        1
## 5 DIRECT   MT     1625
## 6 DIRECT   RO     1079

Need to download CAR data for 4 states… (of which only MT and RO consequential)

Note: the CAR code should be 43 characters long. There are also a handful of (direct supplier) CAR codes that are 13-14 characters, which may be registered in the MT state system, rather than the national one.

dir_suppliers %>% 
  mutate(STATE = str_sub(CAR, start = 1, end = 2)) %>%
  mutate(NCHAR = nchar(CAR)) %>%
  count(STATE, NCHAR)
## # A tibble: 4 × 3
##   STATE NCHAR     n
##   <chr> <int> <int>
## 1 MT       13     2
## 2 MT       14     1
## 3 MT       43  1622
## 4 RO       43  1079
ind_suppliers %>% 
  mutate(STATE = str_sub(CAR, start = 1, end = 2)) %>%
  mutate(NCHAR = nchar(CAR)) %>%
  count(STATE, NCHAR)
## # A tibble: 4 × 3
##   STATE NCHAR     n
##   <chr> <int> <int>
## 1 MS       43     1
## 2 MT       43   402
## 3 RO       43    46
## 4 SP       43     1

Compare with previous disclosures

We compare with previous disclosures the:

  • Number of suppliers (direct/indirect)
  • Number of cattle
  • Relative and total carcass volumes

First, the Number of suppliers (direct/indirect).

bind_rows(
  "2024 DISCLOSURE" = dir_suppliers,
  "2024 DISCLOSURE" = ind_suppliers,
  .id = "SOURCE"
) %>% 
  group_by(SOURCE, POSITION) %>%
  summarise(N = n()) %>%
  ungroup() %>%
  bind_rows(supplier_count_2023) %>%
  mutate(POSITION = case_when(POSITION == "direto" ~ "DIRECT", 
                              POSITION == "indireto" ~ "INDIRECT", 
                              TRUE ~ POSITION))
## `summarise()` has grouped output by 'SOURCE'. You can override using the
## `.groups` argument.

## # A tibble: 4 × 3
##   SOURCE          POSITION     N
##   <chr>           <chr>    <int>
## 1 2024 DISCLOSURE DIRECT    2704
## 2 2024 DISCLOSURE INDIRECT   450
## 3 2023 DISCLOSURE DIRECT    2215
## 4 2023 DISCLOSURE INDIRECT   267

Second, the number of cattle

bind_rows(
  "2024 DISCLOSURE" = dir_suppliers,
  "2023 DISCLOSURE" = dir_suppliers_2023, 
  .id = "SOURCE"
) %>%
  mutate(PRODUCTION_CYCLE = gsub(",", ";", str_to_upper(PRODUCTION_CYCLE))) %>%
  group_by(SOURCE, PRODUCTION_CYCLE) %>%
  summarise_at(.vars = c("CATTLE", "WEIGHT_KG"), .funs = sum) %>%
  ungroup() %>% 
  select(SOURCE, PRODUCTION_CYCLE, CATTLE, WEIGHT_KG) %>%
  pivot_longer(cols = c(CATTLE, WEIGHT_KG), names_to = "METRIC", values_to = "VALUE") %>%
  mutate(PRODUCTION_CYCLE = as_factor(PRODUCTION_CYCLE), 
         PRODUCTION_CYCLE = fct_relevel(PRODUCTION_CYCLE,  c("CRIA;RECRIA;ENGORDA", "RECRIA;ENGORDA", "ENGORDA"))) %>%
  ggplot(aes(SOURCE, VALUE, fill = PRODUCTION_CYCLE)) + 
  geom_bar(stat = "identity") + 
  facet_wrap(~METRIC, scales = "free") + 
  theme_minimal() +
  theme(legend.position = "bottom") 

Third the relative and total carcass volumes

bind_rows(
  "2024 DISCLOSURE" = dir_suppliers,
  "2023 DISCLOSURE" = dir_suppliers_2023, 
  .id = "SOURCE"
) %>%
  mutate(PRODUCTION_CYCLE = gsub(",", ";", str_to_upper(PRODUCTION_CYCLE))) %>%
  group_by(SOURCE, PRODUCTION_CYCLE) %>%
  summarise_at(.vars = c("CATTLE", "WEIGHT_KG"), .funs = sum) %>%
  group_by(SOURCE) %>%
  mutate(TOTAL_CATTLE = sum(CATTLE), 
         TOTAL_KG = sum(WEIGHT_KG), 
         PERC_CATTLE = CATTLE / TOTAL_CATTLE * 100,
         PERC_KG = WEIGHT_KG / sum(WEIGHT_KG) * 100) %>%
  ungroup() %>% 
  select(SOURCE, PRODUCTION_CYCLE, PERC_CATTLE, PERC_KG) %>%
  mutate(PRODUCTION_CYCLE = as_factor(PRODUCTION_CYCLE), 
         PRODUCTION_CYCLE = fct_relevel(PRODUCTION_CYCLE,  c("CRIA;RECRIA;ENGORDA", "RECRIA;ENGORDA", "ENGORDA"))) %>%
  pivot_longer(cols = c(PERC_CATTLE, PERC_KG), names_to = "METRIC", values_to = "VALUE") %>%
  ggplot(aes(SOURCE, VALUE, fill = PRODUCTION_CYCLE)) + 
  geom_bar(stat = "identity") + 
  facet_grid(~METRIC) + 
  theme_minimal() +
  coord_flip() +
  theme(legend.position = "bottom")

Weight/carcass

Out of curiousity, compare weights they get from different production systems and states.

bind_rows(
  "2024 DISCLOSURE" = dir_suppliers,
  "2023 DISCLOSURE" = dir_suppliers_2023, 
  .id = "SOURCE"
) %>%
  mutate(PRODUCTION_CYCLE = gsub(",", ";", str_to_upper(PRODUCTION_CYCLE))) %>%
  group_by(SOURCE, PRODUCTION_CYCLE) %>%
  summarise_at(.vars = c("CATTLE", "WEIGHT_KG"), .funs = sum) %>%
  ungroup() %>%
  mutate(CARCASS_KG = WEIGHT_KG / CATTLE) %>%
  mutate(PRODUCTION_CYCLE = as_factor(PRODUCTION_CYCLE), 
         PRODUCTION_CYCLE = fct_relevel(PRODUCTION_CYCLE,  c("CRIA;RECRIA;ENGORDA", "RECRIA;ENGORDA", "ENGORDA"))) %>%
  ggplot(aes(SOURCE, CARCASS_KG, col = PRODUCTION_CYCLE)) + 
  geom_point() + 
  theme_minimal() +
  theme(legend.position = "bottom")

As perhaps expected, weights: Engorda > Recria;Engorda > Ciclo Completo.