JohnCoene / echarts4r

🐳 ECharts 5 for R
http://echarts4r.john-coene.com/
Other
595 stars 81 forks source link

how to give the right color to the right serie in case of different data sets in one echarts object? #577

Closed rdatasculptor closed 11 months ago

rdatasculptor commented 11 months ago

I am experimenting with different chart types in one chart and I may have found another puzzle (@munoztd0 :)).

Take a look at this one:

df1 <- data.frame(
  x1 = LETTERS[1:5],
  y1 = runif(5, 1, 5),
  size = c(10, 20, 10, 20, 10)
)

df2 <- data.frame(
  x2 = LETTERS[1:5],
  y2 = runif(5, 1, 5),
  color = c("blue", "red", "yellow", "green", "purple")
)

df1 %>%
  e_charts(x1) %>%
  e_scatter(y1) %>%
  e_add_unnested("symbolSize", size) %>%
  e_data(df2, x2) %>%
  e_bar(y2) %>%
  e_add_nested("itemStyle", color = color)

The result is this:

image

As you can see the line e_add_nested("itemStyle", color = color) affects the scatter plot instead of the bars. How can I make it affect the bar chart instead? I experimented with the .serie parameter but no luck.

Thanks!

rdatasculptor commented 11 months ago

This is related to this issue I guess: https://github.com/JohnCoene/echarts4r/issues/574#issue-1909870962

munoztd0 commented 11 months ago

Yeah I am struggling with this weird bug, but I am on it.

JohnCoene commented 11 months ago

Should use the .serie argument but it's not working. Let me.know if you need help @munoztd0

rdatasculptor commented 11 months ago

yes, somehow .serie doesn't do its job. I haven't figured out yet why.

rdatasculptor commented 11 months ago

@munoztd0 @JohnCoene

Somewhere in the e_add_unnested() function the data sets are retrieved by ds <- e$x$data. If I check what is in there:

plot <-df1 %>%
  e_charts(x1) %>%
  e_scatter(y1) %>%
  e_add_unnested("symbolSize", size) %>%
  e_data(df2, x2) %>%
  e_bar(y2) %>%
  e_add_nested("itemStyle", color = color)

plot$x$data

then I get this object back. Only the second dataset:

[[1]]
  x2       y2  color
1  A 2.577452   blue
2  B 1.449567    red
3  C 2.701676 yellow
4  D 3.797036  green
5  E 4.549628 purple

No dataset for each serie. So the .serieargument doesn't make sense here anyway.

also these lines doen't seem to do their job

     if(!is.null(.serie) && .serie != j) 
        next

If I set .serie = 2 it should skip serie 1, but the colors are assigned to serie 1 anyway.

Maybe these findings help fixing this bug?

munoztd0 commented 11 months ago

Yeah I know 😂

rdatasculptor commented 11 months ago

I guess e_data() should add the new dataset to the existing one instead of overwriting it (but that thought won't be new to you either). I did some experiments, but haven't made it work yet. :-(

rdatasculptor commented 11 months ago

It turns out that I was on the wrong track with e_data() because I have a potential fix in e_add_nested() itself. What I did was checking on .serie on another spot in the function. It works for the two examples below. I am not sure I it tackles all thinkable cases, but maybe you @munoztd0 (@JohnCoene) can use this as inspiration for a final fix?

e_add_nested2 <- function(e, param, ..., .serie = NULL, .data = NULL) {
  if (missing(e) || missing(param)) {
    stop("missing e or param", call. = FALSE)
  }

  if(is.null(.data))
    ds <- e$x$data
  else
    ds <- map_grps_(.data, e$x$tl)
  dfcheck <<- ds
  for (i in seq_along(ds)) {
    data <- ds[[i]] |>
      dplyr::select(...)

    data <- apply(data, 1, as.list)

    for (j in seq_along(data)) {

      if (!e$x$tl) {
        for(k in seq_along(e$x$opts$series)){
          if(!is.null(.serie) && .serie != k)
            next
        e$x$opts$series[[k]]$data[[j]][[param]] <- data[[j]]
        }
      } else {
        for(k in seq_along(e$x$opts$options[[i]]$series)){
          if(!is.null(.serie) && .serie != k) 
            next
          e$x$opts$options[[i]]$series[[k]]$data[[j]][[param]] <- data[[j]]
        }
      }
    }
  }
  e
}

here an example without timeline:

df1 <- data.frame(
  x1 = LETTERS[1:5],
  y1 = runif(5, 1, 5),
  size = c(10, 20, 10, 20, 10)
)

df2 <- data.frame(
  x2 = LETTERS[1:5],
  y2 = runif(5, 1, 5),
  color = c("blue", "red", "yellow", "green", "purple")
)

df1 %>%
  e_charts(x1) %>%
  e_scatter(y1) %>%
  e_add_unnested("symbolSize", size) %>%
  e_data(df2, x2) %>%
  e_bar(y2) %>%
  e_add_nested2("itemStyle", color = color, .serie = 2)

gives:

image

here an example with timeline:

df1_tl <- data.frame(
  x1 = rep(LETTERS[1:5], 2),
  y1 = runif(10, 1, 5),
  size = rep(c(10, 20, 10, 20, 10), 2),
  group = rep(c(1,2), 5)
) %>% group_by(group)

df2_tl <- data.frame(
  x2 = rep(LETTERS[1:5], 2),
  y2 = runif(10, 1, 5),
  color = rep(c("blue", "red", "yellow", "green", "purple"), 2),
  group = rep(c(1,2), 5)
) %>% group_by(group)

df1_tl %>%
  e_charts(x1, timeline = TRUE) %>%
  e_scatter(y1) %>%
  e_add_unnested("symbolSize", size) %>%
  e_data(df2_tl, x2) %>%
  e_bar(y2) %>%
  e_add_nested2("itemStyle", color = color, .serie = 2)

gives

image

rdatasculptor commented 11 months ago

Also I tested my potential fix against the example of @stuvet mentioned here

.data <- tibble(x = 2 * pi * (1:100)/100) |>
  mutate(
    y1_bar = 0.5 * (sin(x)) + 1,
    y2_bar = 0.5 * (sin(x)) - 1,
    deltaY1 = rnorm(100, sd = 0.1),
    deltaY2 = rnorm(100, sd = 0.1),
    y1 = y1_bar + deltaY1,
    y2 = y2_bar + deltaY2,
    sizeY1 = abs(deltaY1),
    sizeY2 = abs(deltaY2),
    # Top series should be teal & black
    colorY1 = case_when(y1_bar < y1 ~ "#008080", TRUE ~ "black"), 
    # Bottom series should be magenta & black
    colorY2 = case_when(y2_bar < y2 ~ "#AA00AA", TRUE ~ "black")) 

e <- .data |>
  echarts4r::e_chart(x = x) |>
  echarts4r::e_scatter(serie = y1, size = sizeY1) |>
  # Teal (& black) colors correctly applied to y1 (top)
  e_add_nested2("itemStyle", color = colorY1) |>
  echarts4r::e_scatter(serie = y2, size = sizeY2)

e |>
  # NOTE: itemStyle skipped when using the `.serie` parameter, leaving graph unchanged.
  # according to the code `.serie` appears to target the individual datapoint? Without this parameter the y1 series turns magenta.
  e_add_nested2("itemStyle", color = colorY2, .serie = 2)

I guess it works on this example as well, (exept for the legenda colors) this is the result:

image

JohnCoene commented 11 months ago

Thanks! Can you make a PR?

rdatasculptor commented 11 months ago

I did. Please feel free to alter if you think it can be better coded. Thanks!