SAR (Suspicious Activity Report): a report of a suspicious operation filed by banks with FinCEN (the U.S. financial intelligence unit) and leaked by the ICIJ in the FinCEN Files (2020).
Each row = a flow that a bank judged suspicious between an originating bank and a beneficiary bank, with dates and amounts.
⚠️ Suspicion is not crime: a bank’s presence in this data means it reported (or appeared in) movements that triggered its anti-money-laundering controls, not that criminal participation was proven. The network shows where suspicious activity was detected, with the reporting bias that implies.
2. Vocabulary
SAR: suspicious activity report. A single SAR (a single form) can contain several rows of transactions.
Originator: bank that sends the money. Beneficiary: bank that receives it.
Chronnet: the working network of the whole workshop (module 2). Node = bank; directed edge = co-activity on consecutive days (weight = product of event counts). Money does not live in the edges: it is computed from the reports and attached as an attribute (monto_total per bank, amount circulated per pair).
3. EDA — how much data and of what type?
Basic counts. Mind the difference between rows and SARs:
The top originators are where the reported-as-suspicious money leaves.
Fractionation pattern: many reports of small amounts (movements below reporting thresholds).
Consolidation pattern: few reports, very large amounts (money is pooled before moving on).
Neither pattern alone implies illegality — both also occur in legitimate commerce.
What an investigation prioritizes: the combination of both extremes in the same network.
5. EDA — Top countries in the flows
Each country sums the amount where it appears as originator or beneficiary.
Labels show amount | number of transactions:
Code
paises <-bind_rows( tx %>%transmute(pais = originator_bank_country, monto = amount_transactions, n = number_transactions), tx %>%transmute(pais = beneficiary_bank_country, monto = amount_transactions, n = number_transactions)) %>%group_by(pais) %>%summarise(monto =sum(monto, na.rm =TRUE),n_transacciones =sum(n, na.rm =TRUE),.groups ="drop") %>%mutate(pais =ifelse(is.na(pais), "(No country recorded)", pais)) %>%slice_max(monto, n =15)ggplot(paises, aes(x =reorder(pais, monto), y = monto /1e9)) +geom_col(fill ="#0D9488") +geom_text(aes(label =paste0(round(monto /1e9, 1), " B | ", scales::comma(n_transacciones), " tx")),hjust =-0.05, size =3, color ="#334155") +coord_flip(clip ="off") +scale_y_continuous(expand =expansion(mult =c(0, 0.25))) +labs(x ="", y ="Reported amount (billions USD)") +theme_minimal()
5.1 The country NAs: what they are and how we treat them
Code
n_sin <-sum(is.na(tx$originator_bank_country) |is.na(tx$beneficiary_bank_country))n_bancos_sin <- tx %>%filter(is.na(tx$originator_bank_country) |is.na(tx$beneficiary_bank_country)) %>%summarise(n =n_distinct(c(originator_bank_id, beneficiary_bank_id), na.rm =TRUE)) %>%pull(n)cat("Rows with at least one end lacking a country:", n_sin, "(", round(100*n_sin/nrow(tx),1), "%)\n")
Rows with at least one end lacking a country: 0 ( 0 %)