ipeadata-lab / ipeaplot

ipeaplot: criando graficos no padrão editorial do Ipea
https://ipeadata-lab.github.io/ipeaplot/
Other
1 stars 0 forks source link

ggplot(d,aes(x,y,color=as.factor(z)) + geom_line() + scale_color_ipea() .Error: Discrete value supplied to continuous scale #35

Closed lucasmation closed 5 months ago

lucasmation commented 7 months ago

Segue exemplo reproduzivel:


iris %>% ggplot(aes(Sepal.Length,Sepal.Width,color=as.factor(Petal.Width))) + geom_line() +
+   scale_color_ipea() +
+   theme_ipea()
Error: Discrete value supplied to continuous scale
rafapereirabr commented 7 months ago

A chamada scale_color_ipea() usa cores continuas como default discrete = F.

se voce usar discrete = TRUE vai funcionar.

  iris |> ggplot(aes(Sepal.Length,Sepal.Width,color=as.factor(Petal.Width))) + geom_line() +
   scale_color_ipea(discrete = TRUE) +
     theme_ipea()
lucasmation commented 7 months ago

valeu. Mas este ajuste nao é uma coisa que o proprio "scale_color_ipea" nao deveria detectar automaticamente e adotar o padrao correto?

Me parece que nao tem como esperar que o usuario faça este grau de customizacao, etc... nao é o objetivo do pacote.

rafapereirabr commented 7 months ago

Você quer que o pacote detecte automaticamente se a variável da pessoa é discreta ou continua? @PedroJorge7 , é possivel implementar isso?

Dos pacotes que costumo usar, em geral eles pedem para o usuário informar se a variavel é continua ou discreta.

lucasmation commented 7 months ago

exato. Quando voce coloca: ggplot()+ geom_line() + theme_classic, por exemplo, ele vai la e muda as fontes, eixos, cores. Nao fica te perguntando que tipo de variaveis voce esta usando, etc... O ipeaplot deveria ser algo assim. Ja nao gosto de ter dois comandos suplementares (scale_color_ipea() + theme_ipea() ), mas tudo bem. Agora pelo menos que o usuario nao precise ficar especificando detalhes como o tipo de variavel...

rafapereirabr commented 7 months ago

No uso corrente do ggplot, as pessoas ja tem que passar chamadas diferentes, uma para cor outro para temas.

No theme_ipea() funciona como um theme, tipo theme_classic(), theme_bw(). E veja que esses themes são utilizados para mudar a estetica da 'estrutura' do gráfico, mas não das cores dos dados representados nos graficos. Por isso se usa o scale_color_ipea() seperado, seguindo o padrao do ggplot

PedroJorge7 commented 7 months ago

Em relação ao pacote detectar automaticamente se a variável é discreta ou contínua nós passamos um bom tempo buscando uma solução como essa, mas não encontramos nenhum pacote que fizesse isso e nem uma solução.

O que eu tinha pensado inicialmente era tentar vincular pela classe da coluna (se for caractere, usaria discreto e se for numérico utiliza contínua), mas parece que há uma certa dificuldade de vincular os argumentos aes(color = ...) com o scale_color_ipea.

Enfim, inicialmente me pareceu que era uma coisa bem interessante de se implementar, mas após várias tentativas acabamos deixando como argumento mesmo

PedroJorge7 commented 7 months ago

Em relação a ideia de contemplar tudo dentro de um argumento só, o que tínhamos pensado foi o que o Rafa falou de manter o pacote dentro do padrão dos demais pacotes do R para evitar muita confusão.

Umas das primeiras versões do pacote até lembro que tinha essa opção de trabalhar com tudo junto, mas pensamos que fugir da usabilidade padrão talvez possa causar mais confusão do que ajudar.

lucasmation commented 6 months ago

@PedroJorge7 , @cavalcanti1985 ,

sobre esta dificuldade de automatizar a detecção do tipo de variavel Voces fizeram alguma pergunta no Stack Overflow sobre isso? Se ainda nao por favor façam a pergunta (bem feita ,reproduzível, mostrando a versão mais simplificada possível do código que reproduz o problema). E coloquem aqui para que possamos editar e upvote. Se mesmo assim ninguem no SO achar uma solucao eu aceito fechar o issue.

PedroJorge7 commented 6 months ago

Certo. Seguindo sua sugestão, criamos essa pergunta no Stack Overflow

https://stackoverflow.com/questions/77682155/automatically-detect-discrete-or-continuous-palette-in-custom-ggplot2

rafapereirabr commented 6 months ago

Muito bom. O problema da solucao do 'Allan Cameron' é que ela só funciona para plots basicos com uma unica camada, e os dados e aes tem q ser passados no comando ggplot e não no geom_ . Então seguindo a sugestão dele, por exemplo, isso aqui nao funciona:

ggplot() +
  geom_point(data = mtcars, aes(x = mpg, y = hp, color = gear)) +
  scale_color_ipea()

Provoquei ele no SO para ver se ele teria uma sugestao que cobre esses casos.

PedroJorge7 commented 6 months ago

Pessoal. A partir da solução sugerida pelo Allan Cameron eu fiz novas modificações no nosso scale_color_ipea. Antes de subir no github gostaria que vocês fizessem alguns testes para ver possíveis falhas da nova implementação. Caso alguma estrutura da função ou algum argumento não esteja fazendo sentido fiquem a vontade de sugerir mudanças.

# Definition of the scale_color_ipea function
scale_color_ipea <- function(discrete = F,
                             palette = c('Blue','Green','Orange','Pink','Green-Blue',
                                         'Red-Blue','Green-Blue','Orange-Blue', 'Viridis',
                                         'Inferno','Magma','Plasma','Cividis'),
                             palette_direction = 1,
                             decimal.mark = ",",
                             barheight = NULL, barwidth = NULL,
                             title.hjust = NULL, label.hjust = NULL,
                             ...) {

  structure(list(discrete = discrete, palette = palette, palette_direction = palette_direction,
                 decimal.mark = decimal.mark, barheight = barheight, barwidth = barwidth,
                 title.hjust = title.hjust, label.hjust = label.hjust,...), class = "scale_ipea")
}
# Definition of the ggplot_add.scale_ipea method
ggplot_add.scale_ipea <- function(object, plot, name, ...){

  args <- object
  discrete <- args$discrete
  palette <- args$palette
  palette_direction <- args$palette_direction
  decimal.mark <- args$decimal.mark
  barheight <- args$barheight
  barwidth <- args$barwidth
  title.hjust <- args$title.hjust
  label.hjust <- args$label.hjust

  # Set palette to 'ipea1' if it is not provided, otherwise use the provided value
  palette <-  ifelse(missing(palette),'Blue',palette)

  if (decimal.mark == ",") {
    # Use comma as decimal mark and dot as thousand separator for labels (Brazilian Portuguese)
    labels = scales::label_comma(decimal.mark = ",", big.mark = ".")
  } else if (decimal.mark == "."){
    # Use dot as decimal mark and comma as thousand separator for labels (default)
    labels = scales::label_comma(decimal.mark = ".", big.mark = ",")
  } else {
    stop("Decimal.mark argument must be '.' or ','.")
  }

  label.hjust = ifelse(is.null(label.hjust),0.5,label.hjust)
  title.hjust = ifelse(is.null(title.hjust),0.5,title.hjust)

  if(is.null(barheight)){
    barheight = NULL
  } else{
    barheight = unit(barheight, units = "mm")
  }

  if(is.null(barwidth)){
    barwidth = NULL
  } else{
    barwidth = unit(barwidth, units = "mm")
  }

  # Find the first layer with the defined 'color' aesthetic
  for (i in seq_along(plot$layers)) {
    if ("colour" %in% names(plot$layers[[i]]$mapping)) {
      colour_var <- plot$layers[[i]]$mapping$colour
      break
    } else {
      colour_var <- NULL
    }
  }

  # If 'colour_var' was not found in any layer, check in the base ggplot call
  if (is.null(colour_var) && !is.null(plot$mapping$colour)) {
    colour_var <- plot$mapping$colour
  }

  # If 'colour_var' is not yet found, returns the graphic without changes
  if (is.null(colour_var)) {
    return(plot)
  }

  # Evaluates whether the variable is numeric.
  # Based on this criterion, we will select whether the palette will be discrete or continuous.
  var_evaluated <- rlang::eval_tidy(colour_var, plot$data)
  auto_discrete_choose <- !is.numeric(var_evaluated)

  !!!!!!!
  # Acredito que em alguns casos (não vem nenhum em mente no momento) a seleção automática pode falhar.
  # A ideia do tryCatch seria testar um 'plano B' que seria a estrutura já estabelecido no scale_color_ipea
  # Caso vocês achem que não precisa do tryCatch, podemos remover o argumento 'discrete'

  tryCatch({
  # Choose the correct scale
  if (auto_discrete_choose) {

    return(plot +
             list(ggplot2::discrete_scale(aesthetics = "color", scale_name = "ipea", ipea_pal(palette = palette, palette_direction = palette_direction), ...),
                  ggplot2::guides(colour = guide_legend(...))))
  } else {
    scale_manual_pal <- ipea_palette(palette = palette, n = 10, palette_direction = palette_direction)
    return(
      plot +
             list(ggplot2::scale_color_gradientn(
               labels = labels,  # Set the labels for the gradient scale
               colours = scale_manual_pal,  # Set the scale_manual_pal for the gradient scale
               ...),
               guides(color =   guide_coloursteps(
                 label.hjust = label.hjust,
                 title.hjust = title.hjust,
                 barheight = barheight,
                 barwidth = barwidth,
                 title.position = 'top',
                 even.steps = F,...))))
  }
  }, error = function(e) {

    !!!!!!!!!!!!!!
    # Essa etapa daqui é uma sugestão, pois não tenho certeza se realmente seria necessário
    # Plano B em caso de erro
    if(isFALSE(discrete)){
      # Set palette option
      scale_manual_pal <- ipeaplot:::ipea_palette(palette = palette, n = 10, palette_direction = palette_direction)

      # Graph
      return(
      plot +
        list(ggplot2::scale_color_gradientn(
        labels = labels,  # Set the labels for the gradient scale
        colours = scale_manual_pal,  # Set the scale_manual_pal for the gradient scale
        ...),
        guides(color =   guide_coloursteps(
          label.hjust = label.hjust,
          title.hjust = title.hjust,
          barheight = barheight,
          barwidth = barwidth,
          title.position = 'top',
          even.steps = F,...)))
      )
    }
    if(isTRUE(discrete)){
      # Create a discrete color scale with the specified palette
      return(
        plot +
        list(ggplot2::discrete_scale("color", "ipea", ipea_pal(palette = palette, palette_direction = palette_direction), ...),
                    ggplot2::guides(colour = guide_legend(...)))
      )
    }
  })
}

Exemplos de uso

library(ggplot2)
library(ipeaplot)

ggplot(data = mtcars, aes(x = mpg, y = hp, color = as.character(gear))) +
  geom_point() +
  scale_color_ipea()

ggplot(data = mtcars, aes(x = mpg, y = hp, color = gear)) +
  geom_point() +
  scale_color_ipea()

ggplot(data = mtcars, aes(x = mpg, y = hp, color = as.character(gear))) +
  geom_point() +
  scale_color_ipea(palette = 'Red-Blue')

ggplot(data = mtcars, aes(x = mpg, y = hp, color = gear)) +
  geom_point() +
  scale_color_ipea(palette = 'Red-Blue')

ggplot(data = mtcars, aes(x = mpg, y = hp)) +
  geom_point(aes(color = gear)) +
  scale_color_ipea()

ggplot(data = mtcars, aes(x = mpg, y = hp)) +
  geom_point(aes(color = as.character(gear))) +
  scale_color_ipea()

ggplot(data = mtcars, aes(x = mpg, y = hp)) +
  geom_point(aes(color = gear)) +
  scale_color_ipea(palette = 'Magma')

ggplot(data = mtcars, aes(x = mpg, y = hp)) +
  geom_point(aes(color = as.character(gear))) +
  scale_color_ipea(palette = 'Magma')

ggplot(data = mtcars, aes(x = mpg, y = hp)) +
  geom_point(aes(color = gear)) +
  scale_color_ipea(palette = 'Viridis',barheight = 3, barwidth = 50) +
  theme_ipea(legend.position = 'bottom')
PedroJorge7 commented 6 months ago

Pessoal, boa noite! Conversei com o Pedro e como ele não encontrou nenhum problema na função então eu fiz a implementação da função no pacote. Quando puder gostaria que testassem para que assim a gente possa fechar o issue.

iris %>% ggplot(aes(Sepal.Length,Sepal.Width,color=as.factor(Petal.Width))) + geom_line() +
  scale_color_ipea() +
  theme_ipea()

image

cavalcanti1985 commented 6 months ago

@PedroJorge7 Testei por aqui e não encontrei nenhum problema até o momento. Vou deixar o issue aberto até o final da semana, se até lá não identificarmos nada, fecho, tudo bem?