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.