8 Shiny

Todos os pacotes criados usando o htmlwidgets fornecem funções necessárias para criação de visualizações em aplicações shiny.

O pacote D3plusR fornece duas funções:

  • renderD3plus(): função usada no server da aplicação;
  • d3plusOutput(): função utilizada para a interface do usuário ui.

8.1 Exemplo de Aplicação.

O link para o arquivo completo da aplicação encontra-se aqui.

Nesta aplicação, vamos utilizar a API fornecida pelo Comtrade/UN. Também é forcido o código para criação de uma função em R, chamada de get.comtrade().

8.1.1 Preparação

library(shiny)
library(D3plusR)
library(dplyr)
library(tidyr)

regions <- read.csv("https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/all/all.csv",
                    stringsAsFactors = FALSE)
regions <- regions %>%
  select(alpha.3, region) %>%
  rename(Partner.ISO = alpha.3)

string <- "http://comtrade.un.org/data/cache/partnerAreas.json"
reporters <- jsonlite::fromJSON(string)$results
paises <- reporters$id
names(paises) <- reporters$text

get.Comtrade <- function(url="https://comtrade.un.org/api/get?"
                         ,maxrec=50000
                         ,type="C"
                         ,freq="A"
                         ,px="HS"
                         ,ps="now"
                         ,r
                         ,p
                         ,rg="all"
                         ,cc="TOTAL"
                         ,fmt="json"
)
{
  string<- paste(url
                 ,"max=",maxrec,"&" #maximum no. of records returned
                 ,"type=",type,"&" #type of trade (c=commodities)
                 ,"freq=",freq,"&" #frequency
                 ,"px=",px,"&" #classification
                 ,"ps=",ps,"&" #time period
                 ,"r=",r,"&" #reporting area
                 ,"p=",p,"&" #partner country
                 ,"rg=",rg,"&" #trade flow
                 ,"cc=",cc,"&" #classification code
                 ,"fmt=",fmt        #Format
                 ,sep = ""
  )

  if(fmt == "csv") {
    raw.data<- read.csv(string,header=TRUE)
    return(list(validation=NULL, data=raw.data))
  } else {
    if(fmt == "json" ) {
      raw.data<- rjson::fromJSON(file=string)
      data<- raw.data$dataset
      validation<- unlist(raw.data$validation, recursive=TRUE)
      ndata<- NULL
      if(length(data)> 0) {
        var.names<- names(data[[1]])
        data<- as.data.frame(t( sapply(data,rbind)))
        ndata<- NULL
        for(i in 1:ncol(data)){
          data[sapply(data[,i],is.null),i]<- NA
          ndata<- cbind(ndata, unlist(data[,i]))
        }
        ndata<- as.data.frame(ndata)
        colnames(ndata)<- var.names
      }
      return(list(validation=validation,data =ndata))
    }
  }
}

8.1.2 Server

No server, vamos apenas criar um objeto reativo (data_un) que faz a consulta na API e prepara os dados para a visualização. Com o objeto de daods pronto, adicionamos o treemap ou output (output$treemap) usando a função renderD3plus(). O código para visualização segue o padrão já tratado anteriormente. O único detalhe é que o conjunto de dados é data_un(). Ou seja, sempre que o input (seletor) for alterado, data_un() é atualizado e, consequentemente, output$treemap também é atualizado.

server <- function(input, output) {

   data_un <- reactive({
     existe_dados <- FALSE
     ano <- 2016
     dados <- data.frame()
     while(!existe_dados){
       dados <- get.Comtrade(r = input$country, p = "all", ps = ano, fmt = "csv")$data
       existe_dados <- nrow(dados) > 1
       Sys.sleep(1.1)
       ano <- ano - 1
     }

    dados <- dados %>%
      filter(Partner != "World", Trade.Flow %in% c("Import", "Export")) %>%
      select(Year, Trade.Flow, Reporter, Reporter.ISO, Partner, Partner.ISO,
             Trade.Value..US..)

    dados_exp <- dados %>% filter(Trade.Flow == "Export") %>%
      select(-Trade.Flow) %>%
      rename(Trade_Value_Exp = Trade.Value..US..)

    dados_imp <- dados %>% filter(Trade.Flow == "Import") %>%
      select(-Trade.Flow) %>%
      rename(Trade_Value_Imp = Trade.Value..US..)

    dados <- full_join(dados_exp, dados_imp) %>%
      replace_na(list(Trade_Value_Exp = 0,
                      Trade_Value_Imp = 0))

    dados <- dados %>%
      left_join(regions) %>%
      replace_na(list(region = "Not Specified")) %>%
      mutate(region = ifelse(region == "", "Not Specified", region))
   })


   output$treemap <- renderD3plus({

     toggle_button <- list(list(Exports = "Trade_Value_Exp"),
                           list(Imports = "Trade_Value_Imp"))

     ano <- max(data_un()$Year)

     title <- paste0("Trade by Partners - ",
                     reporters$text[reporters$id == input$country],
                     " - ",
                     ano)

     d3plus(data_un(), type = "tree_map",
            id = c("region", "Partner"),
            currency_var = c("Trade_Value_Exp",
                             "Trade_Value_Imp"),
            locale = "pt_BR") %>%
       d3plusSize("Trade_Value_Exp") %>%
       d3plusUi(list(method = "size", type = "toggle",
                     label = "Trade Flow: ",
                     value = toggle_button)) %>%
       d3plusColor("region") %>%
       d3plusDepth(1) %>%
       d3plusTitle(value = title, font = list(size = 22, weight = 900),
                   total = list(value = list(prefix = "Total: "),
                                font = list(size = 16, weight = 900))) %>%
       d3plusFooter(value = "Source: UN/Comtrade.",
                    font = list(align = "left"))
   })
}

8.1.3 UI

Para apresentação da visualização na aplicação, utiliza-se a função d3plusOutput com o nome do elemento que definimos no server. Ou seja, treemap.

ui <- fluidPage(

  fluidRow(
    column(2),
    column(8,
           h1("Exports and Imports by Country", align = "center")
    )
  ),
  fluidRow(
    column(2),
    column(8,
           selectInput("country",
                       label = "Select a reporter:",
                       choices = paises,
                       selected = "76",
                       multiple = FALSE)
    )
  ),
  fluidRow(
    column(2),
    column(8,
           d3plusOutput('treemap', height = 600)
           )
    )
)

8.1.4 Código completo

library(shiny)
library(D3plusR)
library(dplyr)
library(tidyr)

regions <- read.csv("https://raw.githubusercontent.com/lukes/ISO-3166-Countries-with-Regional-Codes/master/all/all.csv",
                    stringsAsFactors = FALSE)
regions <- regions %>%
  select(alpha.3, region) %>%
  rename(Partner.ISO = alpha.3)

string <- "http://comtrade.un.org/data/cache/partnerAreas.json"
reporters <- jsonlite::fromJSON(string)$results
paises <- reporters$id
names(paises) <- reporters$text

get.Comtrade <- function(url="https://comtrade.un.org/api/get?"
                         ,maxrec=50000
                         ,type="C"
                         ,freq="A"
                         ,px="HS"
                         ,ps="now"
                         ,r
                         ,p
                         ,rg="all"
                         ,cc="TOTAL"
                         ,fmt="json"
)
{
  string<- paste(url
                 ,"max=",maxrec,"&" #maximum no. of records returned
                 ,"type=",type,"&" #type of trade (c=commodities)
                 ,"freq=",freq,"&" #frequency
                 ,"px=",px,"&" #classification
                 ,"ps=",ps,"&" #time period
                 ,"r=",r,"&" #reporting area
                 ,"p=",p,"&" #partner country
                 ,"rg=",rg,"&" #trade flow
                 ,"cc=",cc,"&" #classification code
                 ,"fmt=",fmt        #Format
                 ,sep = ""
  )

  if(fmt == "csv") {
    raw.data<- read.csv(string,header=TRUE)
    return(list(validation=NULL, data=raw.data))
  } else {
    if(fmt == "json" ) {
      raw.data<- rjson::fromJSON(file=string)
      data<- raw.data$dataset
      validation<- unlist(raw.data$validation, recursive=TRUE)
      ndata<- NULL
      if(length(data)> 0) {
        var.names<- names(data[[1]])
        data<- as.data.frame(t( sapply(data,rbind)))
        ndata<- NULL
        for(i in 1:ncol(data)){
          data[sapply(data[,i],is.null),i]<- NA
          ndata<- cbind(ndata, unlist(data[,i]))
        }
        ndata<- as.data.frame(ndata)
        colnames(ndata)<- var.names
      }
      return(list(validation=validation,data =ndata))
    }
  }
}

# Define UI for application that draws a histogram
ui <- fluidPage(

  fluidRow(
    column(2),
    column(8,
           h1("Exports and Imports by Country", align = "center")
    )
  ),
  fluidRow(
    column(2),
    column(8,
           selectInput("country",
                       label = "Select a reporter:",
                       choices = paises,
                       selected = "76",
                       multiple = FALSE)
    )
  ),
  fluidRow(
    column(2),
    column(8,
           d3plusOutput('treemap', height = 600)
           )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {

   data_un <- reactive({
     existe_dados <- FALSE
     ano <- 2016
     dados <- data.frame()
     while(!existe_dados){
       dados <- get.Comtrade(r = input$country, p = "all", ps = ano, fmt = "csv")$data
       existe_dados <- nrow(dados) > 1
       Sys.sleep(1.1)
       ano <- ano - 1
     }

    dados <- dados %>%
      filter(Partner != "World", Trade.Flow %in% c("Import", "Export")) %>%
      select(Year, Trade.Flow, Reporter, Reporter.ISO, Partner, Partner.ISO,
             Trade.Value..US..)

    dados_exp <- dados %>% filter(Trade.Flow == "Export") %>%
      select(-Trade.Flow) %>%
      rename(Trade_Value_Exp = Trade.Value..US..)

    dados_imp <- dados %>% filter(Trade.Flow == "Import") %>%
      select(-Trade.Flow) %>%
      rename(Trade_Value_Imp = Trade.Value..US..)

    dados <- full_join(dados_exp, dados_imp) %>%
      replace_na(list(Trade_Value_Exp = 0,
                      Trade_Value_Imp = 0))

    dados <- dados %>%
      left_join(regions) %>%
      replace_na(list(region = "Not Specified")) %>%
      mutate(region = ifelse(region == "", "Not Specified", region))
   })


   output$treemap <- renderD3plus({

     toggle_button <- list(list(Exports = "Trade_Value_Exp"),
                           list(Imports = "Trade_Value_Imp"))

     ano <- max(data_un()$Year)

     title <- paste0("Trade by Partners - ",
                     reporters$text[reporters$id == input$country],
                     " - ",
                     ano)

     d3plus(data_un(), type = "tree_map",
            id = c("region", "Partner"),
            currency_var = c("Trade_Value_Exp",
                             "Trade_Value_Imp"),
            locale = "pt_BR") %>%
       d3plusSize("Trade_Value_Exp") %>%
       d3plusUi(list(method = "size", type = "toggle",
                     label = "Trade Flow: ",
                     value = toggle_button)) %>%
       d3plusColor("region") %>%
       d3plusDepth(1) %>%
       d3plusTitle(value = title, font = list(size = 22, weight = 900),
                   total = list(value = list(prefix = "Total: "),
                                font = list(size = 16, weight = 900))) %>%
       d3plusFooter(value = "Source: UN/Comtrade.",
                    font = list(align = "left"))
   })
}

# Run the application
shinyApp(ui = ui, server = server)