knitr::opts_chunk$set(echo = TRUE, warning = F, message = F)
options(encoding = 'UTF-8')

Visualização de Dados - Observatório do Comércio Eletrônico

O Ministério do Desenvolvimento, Indústria, Comércio e Serviços (MDIC) mantém um Observatório do Comércio Eletrônico Nacional.

O produto final consiste em um dashboard que compila as informações do comércio realizado por intermédio de plataformas digitais.

Conforme delimitado pelo projeto, tratam-se de transações de B2C (CNPJ para CPF) (empresa para cliente) obtidas via Notas Fiscais Eletrônicas (NFe)

O dashboard pode ser acessado em https://app.powerbi.com/view?r=eyJrIjoiOTdmOTE4MmUtMGM5OC00Yzk3LTljYTctN2I4YTEwODhlMDlkIiwidCI6IjNlYzkyOTY5LTVhNTEtNGYxOC04YWM5LWVmOThmYmFmYTk3OCJ9

As bases de dados, por sua vez, estão disponíveis em arquivos .xlsx na página https://www.gov.br/mdic/pt-br/assuntos/observatorio-do-comercio-eletronico/perguntas-frequentes

O objetivo dessa análise é o de apresentar os dados em formato de gráficos do tipo Sankey para que sirva como tutorial ou para referência de pesquisa posterior.

Preparando os dados

As tabelas mais recentes disponíveis são do ano de 2023. São duas bases em .xlsx A primeira até o capítulo 69 e outra dos capítulos 70 em diante.

library(readxl)
library(dplyr)
library(tidyverse)
library(networkD3) #sankey
library(htmltools) #titulos dos gráficos
library(scales) #para valores em reais
dados1 = read_excel("C:/Users/raphael.rsl/Desktop/R/ecommerce_mdic/2023atcaptulo69.xlsx")
dados2 = read_excel("C:/Users/raphael.rsl/Desktop/R/ecommerce_mdic/2023captulos70a97.xlsx")

colnames(dados1)
## [1] "Ano"               "NCM"               "Descricao"        
## [4] "CFOP_1_Digito"     "UF_Emitente"       "UF_Destinatario"  
## [7] "quant_comercial"   "Valor_Total_Bruto"
colnames(dados2)
## [1] "Ano"               "NCM"               "Descricao"        
## [4] "CFOP_1_Digito"     "UF_Emitente"       "UF_Destinatario"  
## [7] "quant_comercial"   "Valor_Total_Bruto"
#Considerando nomes de colunas iguais, basta fazer um mescla de adição de linhas em um mesmo dataset
dados = rbind (dados1, dados2)
rm(dados1, dados2)
colnames(dados)
## [1] "Ano"               "NCM"               "Descricao"        
## [4] "CFOP_1_Digito"     "UF_Emitente"       "UF_Destinatario"  
## [7] "quant_comercial"   "Valor_Total_Bruto"
nrow(dados) #número de linhas
## [1] 874133
glimpse(dados)
## Rows: 874,133
## Columns: 8
## $ Ano               <dbl> 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023…
## $ NCM               <chr> "01011090", "01011090", "01011090", "01011100", "010…
## $ Descricao         <chr> "Animais da espécie asinina/muar, reprodutores, raça…
## $ CFOP_1_Digito     <dbl> 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5…
## $ UF_Emitente       <chr> "GO", "GO", "GO", "GO", "BA", "DF", "ES", "GO", "MA"…
## $ UF_Destinatario   <chr> "MT", "PA", "TO", "GO", "BA", "DF", "ES", "GO", "MA"…
## $ quant_comercial   <dbl> 292.00, 13.00, 157.00, 11.00, 405.00, 3.00, 44.00, 8…
## $ Valor_Total_Bruto <dbl> 1999.99, 306.00, 3007.00, 272.80, 1241.20, 20200.00,…
summary(dados)
##       Ano           NCM             Descricao         CFOP_1_Digito  
##  Min.   :2023   Length:874133      Length:874133      Min.   :5.000  
##  1st Qu.:2023   Class :character   Class :character   1st Qu.:6.000  
##  Median :2023   Mode  :character   Mode  :character   Median :6.000  
##  Mean   :2023                                         Mean   :5.813  
##  3rd Qu.:2023                                         3rd Qu.:6.000  
##  Max.   :2023                                         Max.   :6.000  
##  UF_Emitente        UF_Destinatario    quant_comercial     Valor_Total_Bruto  
##  Length:874133      Length:874133      Min.   :        0   Min.   :0.000e+00  
##  Class :character   Class :character   1st Qu.:        4   1st Qu.:3.240e+02  
##  Mode  :character   Mode  :character   Median :       25   Median :2.160e+03  
##                                        Mean   :     4325   Mean   :2.243e+05  
##                                        3rd Qu.:      218   3rd Qu.:1.746e+04  
##                                        Max.   :146123540   Max.   :1.969e+09

São 8 colunas com as seguintes informações:

  1. Ano
  2. NCM - Nomenclatura Comum do Mercosul (NCM) é uma Nomenclatura regional para categorização de mercadorias adotada pelo Brasil, Argentina, Paraguai e Uruguai desde 1995
  3. Descrição
  4. CFOP_1_Digito - Códigos Fiscais de Operações e Prestações. Ou seja, são as transações dentro do estado e interestaduais. Se 5, é dentro do Estado, Se 6 é interestadual
  5. UF_Emitente
  6. UF_Destinatario
  7. quant_comercial
  8. Valor_Total_Bruto

Vamos fazer alguns testes para comparar os resultados do dataframe importado e o dashboard

total_bruto_geral <- sum(dados$Valor_Total_Bruto, na.rm = TRUE)
valor_formatado <- format(total_bruto_geral, big.mark = ".", decimal.mark = ",", nsmall = 2)
valor_formatado <- paste0("R$ ", valor_formatado)
valor_formatado
## [1] "R$ 196.100.457.196,71"
#Há uma diferença de cerca de R$ 212 mil. Creio que tenha relação com o UF_Destinatario que contém uma entrada de EX. Vamos testar

total_bruto_geral2 <- dados %>% 
  filter(UF_Destinatario != "EX") %>%
  summarise(Valor = sum(Valor_Total_Bruto, na.rm = TRUE)) %>% 
  mutate(Valor_Formatado = paste0("R$ ",
                                  format(Valor, big.mark = ".", decimal.mark = ",", nsmall = 2)))

total_bruto_geral2
## # A tibble: 1 × 2
##           Valor Valor_Formatado      
##           <dbl> <chr>                
## 1 196100437409. R$ 196.100.437.408,74
#O valor veio próximo. Tendo em vista o valor baixo na comparação, podemos seguir.

#O valor veio exato ao somatório do arquivo em excel, mas um pouco diferente do valor do dashboard.

#A diferença é de cerca de 212 mil a menos no dashboard

Vamos fazer a checagem por UF

total_bruto_por_uf <- dados %>%
  group_by(UF_Emitente) %>%
  summarise(Valor_Total = sum(Valor_Total_Bruto, na.rm = TRUE)) %>%
  arrange(desc(Valor_Total)) %>%
  mutate(Valor_Formatado = paste0(
    "R$ ",
    format(Valor_Total, big.mark = ".", decimal.mark = ",", nsmall = 2)
  ))

total_bruto_por_uf
## # A tibble: 27 × 3
##    UF_Emitente  Valor_Total Valor_Formatado     
##    <chr>              <dbl> <chr>               
##  1 SP          95097186015. R$ 95.097.186.015,29
##  2 MG          24177864893. R$ 24.177.864.892,84
##  3 ES          14197989847. R$ 14.197.989.847,46
##  4 SC          13181370126. R$ 13.181.370.125,65
##  5 PR          10735256562. R$ 10.735.256.562,09
##  6 RJ          10661383456. R$ 10.661.383.455,77
##  7 RS           5894393942. R$  5.894.393.942,35
##  8 PE           4551112546. R$  4.551.112.546,32
##  9 PB           3068661436. R$  3.068.661.435,53
## 10 GO           2670341534. R$  2.670.341.533,93
## # ℹ 17 more rows
total_bruto_por_uf_dest <- dados %>%
  group_by(UF_Destinatario) %>%
  summarise(Valor_Total = sum(Valor_Total_Bruto, na.rm = TRUE)) %>%
  arrange(desc(Valor_Total)) %>%
  mutate(Valor_Formatado = paste0(
    "R$ ",
    format(Valor_Total, big.mark = ".", decimal.mark = ",", nsmall = 2)
  ))

total_bruto_por_uf_dest
## # A tibble: 28 × 3
##    UF_Destinatario  Valor_Total Valor_Formatado
##    <chr>                  <dbl> <chr>          
##  1 SP              64102683504  R$ 6,410268e+10
##  2 MG              22088791421. R$ 2,208879e+10
##  3 RJ              19047881882. R$ 1,904788e+10
##  4 PR              11879685840. R$ 1,187969e+10
##  5 RS              11530569563. R$ 1,153057e+10
##  6 SC               9527098516. R$ 9,527099e+09
##  7 BA               9178477020. R$ 9,178477e+09
##  8 GO               5990127840. R$ 5,990128e+09
##  9 PE               5668294099. R$ 5,668294e+09
## 10 CE               4493116598. R$ 4,493117e+09
## # ℹ 18 more rows
#As diferenças existem, mas não são significativas. Podemos seguir.

Valor Bruto Total - Gráfico Sankey

No primeiro momento, vamos olhar para os dados de Valor Bruto transacionados

fluxo <- dados %>%
  group_by(UF_Emitente, UF_Destinatario) %>%
  summarise(Valor = sum(Valor_Total_Bruto, na.rm = TRUE)) %>%
  ungroup()

glimpse(fluxo)
## Rows: 732
## Columns: 3
## $ UF_Emitente     <chr> "AC", "AC", "AC", "AC", "AC", "AC", "AC", "AC", "AC", …
## $ UF_Destinatario <chr> "AC", "AL", "AM", "AP", "BA", "CE", "DF", "ES", "GO", …
## $ Valor           <dbl> 13558716.53, 3826.55, 125296.11, 219.03, 1629.80, 8101…
nodes <- data.frame(name = unique(c(fluxo$UF_Emitente, fluxo$UF_Destinatario)))

fluxo$source <- match(fluxo$UF_Emitente, nodes$name) - 1  # -1 pq networkD3 usa index começando do zero
fluxo$target <- match(fluxo$UF_Destinatario, nodes$name) - 1

sankeyNetwork(Links = fluxo,
              Nodes = nodes,
              Source = "source",
              Target = "target",
              Value = "Valor",
              NodeID = "name",
              fontSize = 12,
              nodeWidth = 30)

A visualização fica prejudicada sem uma ordenação mais restrita dos nós, especialmente por conta do fluxo a partir de SP

Uma solução é fazer uma coluna de saída e outra de chegada, ou seja, duas colunas fixas.

Duas colunas fixas

unique(fluxo$UF_Destinatario)
##  [1] "AC" "AL" "AM" "AP" "BA" "CE" "DF" "ES" "GO" "MA" "MG" "MS" "MT" "PA" "PB"
## [16] "PE" "PI" "PR" "RJ" "RN" "RO" "RR" "RS" "SC" "SE" "SP" "TO" "EX"
unique(fluxo$UF_Emitente)
##  [1] "AC" "AL" "AM" "AP" "BA" "CE" "DF" "ES" "GO" "MA" "MG" "MS" "MT" "PA" "PB"
## [16] "PE" "PI" "PR" "RJ" "RN" "RO" "RR" "RS" "SC" "SE" "SP" "TO"
# Em UF_Destinatário há um indicador de EX (indicando exterior). Vamos retirar.

fluxo2 <- dados %>% 
  filter(UF_Destinatario != "EX") %>% 
  group_by(UF_Emitente, UF_Destinatario) %>%
  summarise(Valor = sum(Valor_Total_Bruto, na.rm = TRUE)) %>%
  ungroup()

# criando os indicadores de emitente e destinatário
nodes2 <- data.frame(name = c(paste0("E: ", unique(fluxo2$UF_Emitente)),
                             paste0("D: ", unique(fluxo2$UF_Destinatario))))


fluxo2$source <- match(paste0("E: ", fluxo2$UF_Emitente), nodes2$name) - 1
fluxo2$target <- match(paste0("D: ", fluxo2$UF_Destinatario), nodes2$name) - 1

sankey_2col <- sankeyNetwork(Links = fluxo2,
                             Nodes = nodes2,
                             Source = "source",
                             Target = "target",
                             Value = "Valor",
                             NodeID = "name",
                             fontSize = 12,
                             nodeWidth = 30,
                             height = 800, 
                             width = "100%")

#Para adicionar título em gráfico sankey é necessário tratar direto no HTML com o pacote htmltools

browsable(tagList(tags$h2("Fluxos de valor transacionado por e-commerce", 
                          style = "text-align:center; font-family:Arial; font-size:20px"),
                  tags$h4("por UFs - Unidades da Federação", 
                          style = "text-align:center; font-family:Arial; font-size:14px; font-weight:normal;
                          color:gray;"),
                  sankey_2col,
                  tags$p("Fonte: MDIC", 
                         style = "text-align:left; font-style:italic; font-size:12px; font-family:Arial;")))

Fluxos de valor transacionado por e-commerce

por UFs - Unidades da Federação

Fonte: MDIC

Colunas Fixas em Ordem decrescente

# descrescente por emitente
rank_emitente <- fluxo2 %>%
  group_by(UF_Emitente) %>%
  summarise(Total = sum(Valor)) %>%
  arrange(desc(Total)) %>%
  mutate(name = paste0("E: ", UF_Emitente))

destinatarios <- fluxo2 %>%
  distinct(UF_Destinatario) %>%
  mutate(name = paste0("D: ", UF_Destinatario))

nodes3 <- bind_rows(rank_emitente %>% select(name),
                   destinatarios %>% select(name)) %>%
  distinct() %>%
  mutate(group = "neutro")

fluxo3 <- fluxo2 %>%
  mutate(source = match(paste0("E: ", UF_Emitente), nodes3$name) - 1,
         target = match(paste0("D: ", UF_Destinatario), nodes3$name) - 1,
         group = UF_Emitente)

sankey_2col_desc <- sankeyNetwork(Links = fluxo3,
                                  Nodes = nodes3,
                                  Source = "source",
                                  Target = "target",
                                  Value = "Valor",
                                  NodeID = "name",
                                  fontSize = 12,
                                  nodeWidth = 30,
                                  LinkGroup = "group",
                                  NodeGroup = "group",
                                  height = 800, 
                                  width = "100%")

browsable(tagList(tags$h2("Fluxos de valor transacionado por e-commerce", 
                          style = "text-align:center; font-family:Arial; font-size:20px"),
                  tags$h4("por UFs - Unidades da Federação - Decrescente", 
                          style = "text-align:center; font-family:Arial; font-size:14px; font-weight:normal;
                          color:gray;"),
                  sankey_2col_desc,
                  tags$p("Fonte: MDIC", 
                         style = "text-align:left; font-size:12px; font-family:Arial;")))

Fluxos de valor transacionado por e-commerce

por UFs - Unidades da Federação - Decrescente

Fonte: MDIC

Nem sempre a visualização decrescente funciona, pois o pacote networdD3 prioriza as sobreposições. Ou seja, busca minimizar o número de sobreposições.

Tendo em vista que a visualização não fica tão interessante, vamos fazer por regiões político-administrativas

Uma primeira opção é a de fazer um filtro para as 3 UFs com maior valor transacionado. Desse modo a quantidade máxima de nós diminuirá de 729 para 81.

Três maiores destinos (UFs)

Vamos seguir com a primeira opção de verificar os 3 maiores destinos por UF emitente, reduzindo a quantidade de nós para 81.

fluxo_top3 <- dados %>%
  filter(UF_Destinatario != "EX") %>%
  group_by(UF_Emitente, UF_Destinatario) %>%
  summarise(Valor = sum(Valor_Total_Bruto, na.rm = TRUE), .groups = "drop") %>%
  group_by(UF_Emitente) %>%
  slice_max(order_by = Valor, n = 3, with_ties = FALSE) %>%
  ungroup()

nodes_top3 <- data.frame(name = c(paste0("E: ", unique(fluxo_top3$UF_Emitente)),
                                  paste0("D: ", unique(fluxo_top3$UF_Destinatario)))) %>% 
  distinct() %>%
  mutate(group = "neutro")

fluxo_top3 <- fluxo_top3 %>%
  mutate(source = match(paste0("E: ", UF_Emitente), nodes_top3$name) - 1,
         target = match(paste0("D: ", UF_Destinatario), nodes_top3$name) - 1,
         group = UF_Emitente)

# 4. Gráfico Sankey
sankey_top3 <- sankeyNetwork(Links = fluxo_top3,
                             Nodes = nodes_top3,
                             Source = "source",
                             Target = "target",
                             Value = "Valor",
                             NodeID = "name",
                             fontSize = 13,
                             nodeWidth = 30,
                             LinkGroup = "group",
                             NodeGroup = "group",
                             height = 800, 
                             width = "100%")

browsable(tagList(tags$h2("Fluxos de valor transacionado por e-commerce", 
                          style = "text-align:center; font-family:Arial; font-size:20px"),
                  tags$h4("3 maiores destinos de cada UF", 
                          style = "text-align:center; font-family:Arial; font-size:14px; font-weight:normal;
                          color:gray;"),
                  sankey_top3,
                  tags$p("Fonte: MDIC", 
                         style = "text-align:left; font-size:12px; font-family:Arial;")))

Fluxos de valor transacionado por e-commerce

3 maiores destinos de cada UF

Fonte: MDIC

#Tabela

library(reactable)

reactable(fluxo_top3 %>%
            select(UF_Emitente, UF_Destinatario, Valor) %>%
            arrange(desc(Valor)),
          columns = list(
            UF_Destinatario = colDef(name = "UF Emitente"),
            UF_Destinatario = colDef(name = "UF Destinatária"),
            Valor = colDef(
              name = "Valor Transacionado (R$)",
              format = colFormat(prefix = "R$ ", separators = TRUE, digits = 0))),
          defaultPageSize = 25,
          bordered = TRUE,
          highlight = TRUE,
          striped = TRUE,
          theme = reactableTheme(
            headerStyle = list(backgroundColor = "#f7f7f8", fontWeight = "bold"),
            rowSelectedStyle = list(backgroundColor = "#e6f2ff")),
          searchable = TRUE,
          sortable = TRUE,
          filterable = TRUE)

Conforme é possível visualizar do gráfico, os três princiais destinos de produtos emitidos por São Paulo são: o próprio estado de São Paulo, Rio de Janeiro e Minas Gerais.

Minas Gerais, por sua vez, transaciona com o próprio estado, São Paulo e Rio de Janeiro.

E o Rio de Janeiro fecha a mesma tríade com si mesmo, Minas Gerais e São Paulo.

Três maiores destinos sem SP

Para o gráfico há uma distorção por conta dos dados de SP. Talvez seja interessante gerar novamente o mesmo gráfico, agora retirando SP.

fluxo_top3_semSP <- dados %>%
  filter(UF_Destinatario != "EX",
         UF_Emitente != "SP",) %>%
  group_by(UF_Emitente, UF_Destinatario) %>%
  summarise(Valor = sum(Valor_Total_Bruto, na.rm = TRUE), .groups = "drop") %>%
  group_by(UF_Emitente) %>%
  slice_max(order_by = Valor, n = 3, with_ties = FALSE) %>%  # top‑3 por emitente
  ungroup()

nodes_top3_semSP <- data.frame(name = c(paste0("E: ", unique(fluxo_top3_semSP$UF_Emitente)),
                                        paste0("D: ", unique(fluxo_top3_semSP$UF_Destinatario)))) %>%
  distinct() %>% 
  mutate(group = "neutro")        

fluxo_top3_semSP <- fluxo_top3_semSP %>% 
  mutate(source = match(paste0("E: ", UF_Emitente), nodes_top3_semSP$name) - 1,
         target = match(paste0("D: ", UF_Destinatario), nodes_top3_semSP$name) - 1,
         group  = UF_Emitente)

sankey_top3_semSP <- sankeyNetwork(Links = fluxo_top3_semSP,
                                   Nodes = nodes_top3_semSP,
                                   Source = "source",
                                   Target = "target",
                                   Value = "Valor",
                                   NodeID = "name",
                                   LinkGroup = "group",           # cores nos fluxos
                                   NodeGroup = "group",           # nós neutros (todos "neutro")
                                   fontSize   = 12,
                                   nodeWidth  = 30,
                                   height = 800, 
                                   width = "100%")

browsable(tagList(tags$h2("Fluxos de valor transacionado por e-commerce", 
                          style = "text-align:center; font-family:Arial; font-size:20px"),
                  tags$h4("3 maiores destinos de cada UF - Sem SP", 
                          style = "text-align:center; font-family:Arial; font-size:14px; font-weight:normal;
                          color:gray;"),
                  sankey_top3_semSP,
                  tags$p("Fonte: MDIC", 
                         style = "text-align:left; font-size:12px; font-family:Arial;")))

Fluxos de valor transacionado por e-commerce

3 maiores destinos de cada UF - Sem SP

Fonte: MDIC

Separando por regiões

Uma segunda opção, para melhorar a visualização, é a divisão por paineis a partir das regiões

uf_regiao <- data.frame(UF_Emitente = c("AC","AL","AM","AP","BA","CE","DF","ES","GO","MA","MG",
                                        "MS","MT","PA","PB","PE","PI","PR","RJ","RN","RO","RR",
                                        "RS","SC","SE","SP","TO"),
                        Regiao = c("Norte","Nordeste","Norte","Norte","Nordeste","Nordeste",
                             "Centro-Oeste","Sudeste","Centro-Oeste","Nordeste","Sudeste",
                             "Centro-Oeste","Centro-Oeste","Norte","Nordeste","Nordeste",
                             "Nordeste","Sul","Sudeste","Nordeste","Norte","Norte",
                             "Sul","Sul","Nordeste","Sudeste","Norte"))

dados_regiao <- dados %>%
  filter(UF_Destinatario != "EX") %>%
  left_join(uf_regiao, by = "UF_Emitente")

# Top 3 fluxos por UF (por região)
fluxo_top3_regiao <- dados_regiao %>%
  group_by(Regiao, UF_Emitente, UF_Destinatario) %>%
  summarise(Valor = sum(Valor_Total_Bruto, na.rm = TRUE), .groups = "drop") %>%
  group_by(Regiao, UF_Emitente) %>%
  slice_max(Valor, n = 3, with_ties = FALSE) %>%
  ungroup()

#Função para gerar sankey
gerar_sankey_por_regiao <- function(regiao_nome) {
  fluxo_reg <- fluxo_top3_regiao %>% filter(Regiao == regiao_nome)

  nodes <- data.frame(
    name = c(paste0("E: ", unique(fluxo_reg$UF_Emitente)),
             paste0("D: ", unique(fluxo_reg$UF_Destinatario)))) %>% 
    distinct() %>%
    mutate(group = "neutro")

  fluxo <- fluxo_reg %>%
    mutate(source = match(paste0("E: ", UF_Emitente), nodes$name) - 1,
           target = match(paste0("D: ", UF_Destinatario), nodes$name) - 1,
           group = UF_Emitente)

  sankey_regiao_UF <- sankeyNetwork(Links = fluxo,
                Nodes = nodes,
                Source = "source",
                Target = "target",
                Value = "Valor",
                NodeID = "name",
                LinkGroup = "group",
                NodeGroup = "group",
                fontSize = 12,
                nodeWidth = 15,
                height = 800, 
                width = "100%")
  
    tagList(tags$h3(paste0("Fluxos de valor transacionado por e-commerce - Região: ", regiao_nome),
                    style = "text-align:center; font-family:Arial; margin-bottom:5px;"),
            tags$h4("3 maiores destinos de cada UF", 
                          style = "text-align:center; font-family:Arial; font-size:14px; font-weight:normal;
                          color:gray;"),
            sankey_regiao_UF,
            tags$p("Fonte: MDIC",
                   style = "text-align:left; font-size:12px; font-family:Arial; margin-bottom:40px;"))
}

gerar_sankey_por_regiao("Norte")

Fluxos de valor transacionado por e-commerce - Região: Norte

3 maiores destinos de cada UF

Fonte: MDIC

gerar_sankey_por_regiao("Nordeste")

Fluxos de valor transacionado por e-commerce - Região: Nordeste

3 maiores destinos de cada UF

Fonte: MDIC

gerar_sankey_por_regiao("Centro-Oeste")

Fluxos de valor transacionado por e-commerce - Região: Centro-Oeste

3 maiores destinos de cada UF

Fonte: MDIC

gerar_sankey_por_regiao("Sudeste")

Fluxos de valor transacionado por e-commerce - Região: Sudeste

3 maiores destinos de cada UF

Fonte: MDIC

gerar_sankey_por_regiao("Sul")

Fluxos de valor transacionado por e-commerce - Região: Sul

3 maiores destinos de cada UF

Fonte: MDIC

Agregando por regiões político-administrativas

regioes <- c(AC = "Norte", AL = "Nordeste", AM = "Norte", AP = "Norte", BA = "Nordeste", 
  CE = "Nordeste", DF = "Centro-Oeste", ES = "Sudeste", GO = "Centro-Oeste", 
  MA = "Nordeste", MG = "Sudeste", MS = "Centro-Oeste", MT = "Centro-Oeste", 
  PA = "Norte", PB = "Nordeste", PE = "Nordeste", PI = "Nordeste", PR = "Sul", 
  RJ = "Sudeste", RN = "Nordeste", RO = "Norte", RR = "Norte", RS = "Sul", 
  SC = "Sul", SE = "Nordeste", SP = "Sudeste", TO = "Norte")

# Agregando por região
fluxo_regiao <- dados %>%
  filter(UF_Destinatario != "EX") %>% 
  mutate(Regiao_Emitente = recode(UF_Emitente, !!!regioes), #!!! descompactação do list. Por isso não foi usado uf_região. Para testar a função.
         Regiao_Destinatario = recode(UF_Destinatario, !!!regioes)) %>%
  group_by(Regiao_Emitente, Regiao_Destinatario) %>%
  summarise(Valor = sum(Valor_Total_Bruto, na.rm = TRUE), .groups = "drop")

regioes_ordenadas <- fluxo_regiao %>%
  group_by(Regiao_Emitente) %>%
  summarise(Total = sum(Valor)) %>%
  arrange(desc(Total)) %>%
  mutate(name = paste0("E: ", Regiao_Emitente))

destinos_regiao <- fluxo_regiao %>%
  distinct(Regiao_Destinatario) %>%
  mutate(name = paste0("D: ", Regiao_Destinatario))

nodes_regiao <- bind_rows(regioes_ordenadas %>% select(name),
                          destinos_regiao %>% select(name)) %>%
  distinct() %>%
  mutate(group = "neutro")


fluxo_regiao <- fluxo_regiao %>%
  mutate(source = match(paste0("E: ", Regiao_Emitente), nodes_regiao$name) - 1,
         target = match(paste0("D: ", Regiao_Destinatario), nodes_regiao$name) - 1,
         group = Regiao_Emitente)

glimpse(fluxo_regiao)
## Rows: 25
## Columns: 6
## $ Regiao_Emitente     <chr> "Centro-Oeste", "Centro-Oeste", "Centro-Oeste", "C…
## $ Regiao_Destinatario <chr> "Centro-Oeste", "Nordeste", "Norte", "Sudeste", "S…
## $ Valor               <dbl> 4181133880, 310941085, 177111610, 932794741, 27445…
## $ source              <dbl> 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 0, 0,…
## $ target              <dbl> 5, 6, 7, 8, 9, 5, 6, 7, 8, 9, 5, 6, 7, 8, 9, 5, 6,…
## $ group               <chr> "Centro-Oeste", "Centro-Oeste", "Centro-Oeste", "C…
sankey_regiao <- sankeyNetwork(Links = fluxo_regiao,
                        Nodes = nodes_regiao,
                        Source = "source",
                        Target = "target",
                        Value = "Valor",
                        NodeID = "name",
                        fontSize = 12,
                        fontFamily = "Arial",
                        nodeWidth = 10,
                        NodeGroup = "group",
                        LinkGroup = "group",
                        colourScale = JS("d3.scaleOrdinal()
                     .domain(['Norte', 'Nordeste', 'Centro-Oeste', 'Sudeste', 'Sul'])
                     .range(['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd'])"))


browsable(tagList(tags$h2("Fluxos de valor transacionado por e-commerce", 
                          style = "text-align:center; font-family:Arial; font-size:20px"),
                  tags$h4("Agregador por Regiões", 
                          style = "text-align:center; font-family:Arial; font-size:14px; font-weight:normal;
                          color:gray;"),
                  sankey_regiao,
                  tags$p("Fonte: MDIC", 
                         style = "text-align:left; font-size:12px; font-family:Arial;")))

Fluxos de valor transacionado por e-commerce

Agregador por Regiões

Fonte: MDIC

Inserir uma tabela

library(reactable)
library(scales)

reactable(fluxo_regiao %>%
            select(Regiao_Emitente, Regiao_Destinatario, Valor) %>%
            arrange(desc(Valor)),
          columns = list(Regiao_Emitente = colDef(name = "Região Emitente"),
                         Regiao_Destinatario = colDef(name = "Região Destinatária"),
                         Valor = colDef(name = "Valor Transacionado (R$)",
                                        format = colFormat(prefix = "R$ ", separators = TRUE, digits = 0))),
          defaultPageSize = 25,
          bordered = TRUE,
          highlight = TRUE,
          striped = TRUE,
          theme = reactableTheme(headerStyle = list(backgroundColor = "#f7f7f8", fontWeight = "bold"),
                                 rowSelectedStyle = list(backgroundColor = "#e6f2ff")),
          searchable = TRUE,
          sortable = TRUE,
          filterable = TRUE)

Emitentes vs Destinatários

Um potencial indicador acerca das movimentações a partir do e-commerce pode ser realizada na razão entre valores transacionados de maneira intra e interestadual

Antes, vamos verificar os valores separados por interestadual e dentro do estado na comparação com o dashboard

Geral

resumo_geral <- dados %>%
  filter(UF_Destinatario != "EX") %>% 
  mutate(Tipo_Venda = ifelse(CFOP_1_Digito == 5, "Dentro do Estado",
                             ifelse(CFOP_1_Digito == 6, "Interestadual", "NA"))) %>%
  group_by(Tipo_Venda) %>%
  summarise(Valor_Total = sum(Valor_Total_Bruto, na.rm = TRUE)) %>%
  arrange(desc(Valor_Total))

resumo_geral
## # A tibble: 2 × 2
##   Tipo_Venda         Valor_Total
##   <chr>                    <dbl>
## 1 Interestadual    121497653560.
## 2 Dentro do Estado  74602783849.

Índice de Transação Interestadual

resumo_por_uf <- dados %>%
  filter(CFOP_1_Digito %in% c(5, 6)) %>%
  mutate(Tipo_Venda = ifelse(CFOP_1_Digito == 5, "Dentro", "Fora")) %>%
  group_by(UF_Emitente, Tipo_Venda) %>%
  summarise(Valor = sum(Valor_Total_Bruto, na.rm = TRUE), .groups = "drop") %>%
  tidyr::pivot_wider(names_from = Tipo_Venda, values_from = Valor, values_fill = 0) %>%
  mutate(Razao_Fora_Dentro = ifelse(Dentro == 0, NA, Fora / Dentro),
         Dentro_formatado = paste0("R$ ", format(Dentro, big.mark = ".", decimal.mark = ",", nsmall = 2, scientific = FALSE)),
         Fora_formatado   = paste0("R$ ", format(Fora,   big.mark = ".", decimal.mark = ",", nsmall = 2, scientific = FALSE)),
         Razao_formatado  = format(Razao_Fora_Dentro, big.mark = ".", decimal.mark = ",", nsmall = 2)) %>%
  mutate(Razao_num = Razao_Fora_Dentro) %>%  # manter para ordenação
  select(UF_Emitente, Dentro = Dentro_formatado, Fora = Fora_formatado, Razao = Razao_formatado, Razao_num) %>%
  arrange(desc(Razao_num))

reactable(
  resumo_por_uf,  
  columns = list(UF_Emitente = colDef(name = "UF"),
                 Dentro = colDef(name = "Venda Dentro do Estado"),
                 Fora = colDef(name = "Venda Fora do Estado"),
                 Razao = colDef(name = "Razão (Fora / Dentro)", sortNALast = TRUE, align = "right"),
                 Razao_num = colDef(show = FALSE)  # Oculta a coluna usada para ordenação
  ),
  defaultSorted = "Razao_num",  # Usa a coluna numérica oculta para ordenar
  defaultSortOrder = "asc",
  striped = TRUE,
  highlight = TRUE,
  defaultPageSize = 10)

A partir desse índice é possível observar que as UFs que menos comercializam para fora do estado, proporcionalmente aos valores internos, são Roraima, Rondônia e Amapá. Essa proporção pode apontar para desafios de logística, do tipo de produto transacionado, dentre outros possíveis aspectos.

Por outro lado, as UFs que mais mandam para fora do Estado são, respectivamente, o Espírito Santo, o Amazonas e Santa Catarina.

Isso nos leva a explorar os dados agora para verificar os produtos transacionados