helgasoft / echarty

Minimal R/Shiny Interface to ECharts.js
https://helgasoft.github.io/echarty/
82 stars 3 forks source link

Code gists to the rescue #3

Open helgasoft opened 2 years ago

helgasoft commented 2 years ago

Several programming questions have been addressed already in our Code Gists. Gists are searchable by keyword. Example: to search for scatter in all gists - do this.

Since search is easier in Issues however, newer code snippets will mostly show up in this thread as comments.

helgasoft commented 2 years ago

endLabel formatting, inquiry by @Tom-Jenkins

tmp <- read.csv('https://github.com/JohnCoene/echarts4r/files/9375371/life_continent.txt', sep='\t')
tmp |> mutate(life_expectancy= round(life_expectancy,2), year= as.factor(year)) |>
    select(year, life_expectancy, country) |>   # order x,y,grp - will be redundant in echarty 1.4.7
    group_by(country) |> 
ec.init(
    ctype='line', 
    yAxis= list(scale= TRUE), 
    grid= list(right= '28%'),
    animation= list(duration= 5000)) |> 
ec.upd({
    series <- lapply(series, function(ss) {
        ss$showSymbol <- FALSE
        ss$emphasis <- list(disable=FALSE, lineStyle= list(width= 3), focus= "self")
        ss$endLabel <- list(show= TRUE, valueAnimation= TRUE, color= 'inherit',
                offset= c(-50,11), 
                formatter= ec.clmn('%@ %L@',3,2))
    ss })
})

image if you like this solution, please consider granting a Github star ⭐ to echarty.

helgasoft commented 2 years ago

Layout with two pie charts, inquiry by @DavZim.

library(dplyr)
df <- tibble(
    name = rep(c("v1", "v2", "v3"), 2), # pie native 'axes' are name/value
    value = c(20, 40, 40, 50, 30, 20),
    group = rep(c("A", "B"), each = 3)  # echarty currently requires group column to be last
)

library(echarty)   # v.1.4.7.01+
p <- lapply(df |> group_by(group) |> group_split(), function(x) { 
    ec.init(preset= FALSE, height= 250,
              title= list(text= unique(x$group)), 
              series= list(list(type= 'pie', data= ec.data(x, 'names'))),
              legend= list(show=TRUE))
})
ec.util(p, cmd='layout', cols=2)

# try also new tabset command
htmltools::browsable(
    ec.util(cmd='tabset', tab1= p[[1]], tab2= p[[2]])
)

image

df2 <- tibble(
    group = rep(c("A", "B"), each = 100),
    time = rep(1:100, 2)
) %>% mutate(value = time + rnorm(100) + ifelse(group == "A", 10, 0)) %>% 
    select(time,value,group)

library(echarty)
lapply(df2 |> group_by(group) |> group_split(), function(gd) { 
    tmp <- lm(value ~ time, gd)
    ec.init( 
        height= 300, title= list(text= unique(gd$group)), 
        xAxis= list(show=TRUE), yAxis=list(show=TRUE),
        series= list(
            list(type= 'scatter', data= ec.data(gd[,1:2]), symbolSize=3, name='data'),
            list(type='line', data= ec.data(data.frame(gd$time, tmp$fitted.values)),
                  showSymbol= FALSE, lineStyle= list(width=1, color='red'), name='lm')
        ),
        legend= list(show=TRUE))
}) |>
ec.util(cmd='layout', cols= 2)

image

if you like this solution, please consider granting a Github star ⭐ to echarty.

helgasoft commented 1 year ago

Answer question by @kesselhwvan about duplicating a ggplot chart

#  set.seed(?)
data = tibble(time = factor(sort(rep(c(4,8,24), 30)), levels = c(4,8,24)),
    dose = factor(rep(c(1,2,3), 30), levels = c(1,2,3)),
    id = rep(sort(rep(LETTERS[1:10], 3)),3),
    y = rnorm(n = 90, mean = 5, sd = 3))

# This is the plot i am aiming to recreate:
# ggplot(data = data, mapping = aes(x = time, y = y, group = id)) + geom_point() +
#      geom_line() + facet_wrap(~dose)

remotes::install_github('helgasoft/echarty')      # get latest 1.4.7.02
library(echarty)

df <- data |> select(time,y,id,dose)    # default order for X,Y,group,etc 
lapply(unique(df$dose), function(i) 
    df |> filter(dose==i) |> group_by(id) |> 
    ec.init(ctype='line', title= list(text= i), legend= list(show=FALSE)
               ,yAxis= list(min= floor(min(df$y)), max= ceiling(max(df$y)))
               ,grid= list(containLabel= TRUE)) 
) |>
ec.util(cmd='layout', cols=3)

image if you like this solution, please consider granting a Github star ⭐ to echarty.

helgasoft commented 1 year ago

@XiangyunHuang, hope that's what you are looking for. Parameter visualMap.dimension works with ECharts dataset, which is set by default in echarty.

library(echarty)
# enhance symbol size related to magnitude
quakes |> mutate(mage= ifelse(mag<5, 4, ifelse(mag<6, 10, 15))) |> 
ec.init(load='3D',
  xAxis3D= list(name= "Lat", scale=TRUE),
  yAxis3D= list(name = "Long", scale=TRUE),
  zAxis3D= list(name = "Depth"),
  series= list(list(type= 'scatter3D',
    encode= list(x= 'lat', y= 'long', z= 'depth'),
    symbolSize = ec.clmn(6),  # = mage
    name = "Fiji"
  )),
  visualMap= list(
    type = "continuous",
    # inRange = list(color = c('#4B0055', '#009B95', '#FDE333')),
    inRange = list(color = c('green', 'yellow', 'red')),
    dimension = 3, # third dimension x = 0, y = 1, z = 2, mag = 3, station = 4
    max= max(quakes$mag), min= min(quakes$mag), 
    text= c(paste('mag\n',max(quakes$mag)), min(quakes$mag)),
    top = 20, calculable= TRUE, precision= 1,
    textStyle= list(color= '#bbb')
  )
) |> ec.theme('dark-mushroom')

image

helgasoft commented 1 year ago

Numbers locale-based formatting, asked by @oobd Done with ec.clmn() in echarty. Format %L@ stands for 'localized'.

library(dplyr); library(echarty)
iris %>%
  mutate(Petal.Length = Petal.Length * 1000) %>% 
  group_by(Species) %>% 
  summarise(Petal.Length = sum(Petal.Length)) %>% 
  ec.init(
    grid= list(containLabel= TRUE),
    series= list(list( 
      type= 'bar',
      encode= list(x= 'Petal.Length', y= 'Species'),
      label= list(show= TRUE, position= 'inside', fontSize= 10, offset= c(0, 0.5),
                  formatter= ec.clmn('%L@', 'Petal.Length'))
    ))
  )

image

helgasoft commented 1 year ago

Simple heatmap of weekly hours, inquiry by @avenn98

set.seed(2022)
df <- data.frame(day_of_week= sample(c('Mon','Tue','Wed','Thu','Fri','Sat','Sun'),100, replace=TRUE), 
        hour_of_day= sample(5:23, 100, replace=TRUE), 
        jobs= sample(1:100, 100)) |> 
    dplyr::distinct(hour_of_day, day_of_week, .keep_all= TRUE)
df |> echarty::ec.init(
    title= list(text= "Heatmap of Successful API Requests by Day and Hour"),
    series= list(list(type= 'heatmap')),
    xAxis= list(type= 'category', data= c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')),
    yAxis= list(min= min(df$hour_of_day)-1, name= 'hours', interval= 1),
    visualMap= list(calculable= TRUE, max= 100, 
        orient= 'horizontal', bottom= 'bottom', left='middle', align= 'right', 
        inRange= list(color= c('yellow','red'))),
    tooltip= list(trigger= "item")
)

image if you like this solution, please consider granting a Github star ⭐ to echarty.

avenn98 commented 1 year ago

That worked perfectly, thank you so much!

helgasoft commented 1 year ago

Stacked bar chart with factor categories + colors, sample by @mmfc

library(dplyr)
ccv <- "project_id  sum count   status
100 35  20  Accepted
100 35  14  Proposal
100 35  1   QA
107 33  8   Accepted
107 33  9   In Progress
107 33  12  Proposal
107 33  4   QA
83  20  10  Accepted
83  20  3   In Progress
83  20  7   Proposal
11  18  6   Accepted
11  18  3   In Progress
11  18  5   Proposal
11  18  4   QA
91  17  11  Accepted
91  17  4   In Progress
91  17  2   Proposal
8   15  7   Accepted
8   15  3   Аbandoned
8   15  1   Blocked"
df <- read.csv(text=ccv, sep='\t', header=T) 
df$status <- factor(df$status, levels= c('Accepted','Proposal','QA','In Progress','Blocked','Аbandoned','Reference'))

library(echarty)
df |> mutate(project_id= as.character(project_id)) |> arrange(desc(sum)) |> 
  relocate(sum, .after=last_col()) |> group_by(status) |> 
ec.init(ctype= 'bar', 
  series.param= list(stack= "stak"), tooltip= list(show=T),
  xAxis= list(type= 'category', 
              data= unique(unname(unlist(df$project_id)))),
  # 'status' colors in same (factor) order
  color= c("#FEF2C0","#BFE5BF","#D4C5F9","#a10b0b","#000000","#7F8C8D","green")
) |> ec.theme('dark-mushroom')

image

helgasoft commented 1 year ago

A solution for boxplot + data chart using helper function echarty::ec.data(format='boxplot',...) for @lhabegger and @varunrd

library(echarty)      # use v.1.5.1+
# prepare data
ds <- iris |> dplyr::relocate(Species) |>
    ec.data(format= 'boxplot', jitter= 0.1, layout= 'v', 
            symbolSize= 6, tooltip= list(formatter= ec.clmn('%@',2)))
# display
ec.init(
  dataset= ds$dataset, series= ds$series,xAxis= ds$xAxis, yAxis= ds$yAxis,
  legend= list(show= T), tooltip= list(show= T)
)

image if you like this solution, please consider granting a Github star ⭐ to echarty.

helgasoft commented 1 year ago

Make data.tree acme dataset show as sunburst chart, for @GillesSanMartin. See also various hierarchical structures for echarty explained.

library(data.tree)
tmp <- acme
tmp$Do(function(x) x$value <- x$cost)  # add 'value' for sunburst
lst <- tmp |> ToListExplicit(unname = TRUE)

library(echarty)
p1 <- ec.init(
    series= list(list(
           # data=list(lst) is ok too, but children are not colored
           #type= 'treemap', data= lst$children) 
            type= 'sunburst', data= lst$children, 
               radius= list(0, '90%'), label= list(rotate='radial')))
)
p2 <- ec.init(width=300, series= list(list(type= 'tree', data= list(lst))))
ec.util(list(p1,p2), cmd='layout', cols=2)

image

helgasoft commented 11 months ago

New in version 1.6.0 - save complete chart to file, show local or remote charts. @rdatasculptor will like it

remotes::install_github('helgasoft/echarty')    # get latest v.1.6.0
library(echarty)

# build a chart with custom Javascript handlers
js = "chart.getZr().on('contextmenu', x => {chart.setOption({title:{text:'right-clicked'}}) })"
p <- cars |> ec.init(js= js, dataZoom= list(type='inside'))
p$x$on = list(
    list(event= 'datazoom', 
         handler= ec.clmn("function() {this.setOption({title:{text:'zoomed'}});}") )
)

# save chart to local file
p |> ec.inspect(target='full', file='c:/temp/tcar.txt')
# file could now be uploaded to net

# read local file and show chart
con <- file('c:/temp/tcar.txt','rb')
ec.fromJson(con); close(con)

# show a remote chart
echarty::ec.fromJson('https://helgasoft.github.io/echarty/test/pfull.json')
helgasoft commented 8 months ago

@pizifan, an complex example indeed from ECharts. But echarty can handle it. Currently tl.series supports a single serie, but more can be added thru ec.upd(). Notice that list attributes are straight from ECharts API.

remotes::install_github('helgasoft/echarty')  # get latest!
library(echarty)

tmp <- as.data.frame(jsonlite::fromJSON('https://echarts.apache.org/examples/data/asset/data/life-expectancy-table.json'))
colnames(tmp) <- tmp[1,]
df <- tmp[-1,]
df <- df |> mutate(across(.cols= -c(Country), as.numeric)) |> 
  filter(Year %in% c(1985,2000,2015), Country %in% c('Canada','France','Turkey'))
clr <- ec.clmn("(p) => { return p.dataIndex==0 ? 'violet' : p.dataIndex==1 ? 'orange' : 'brown' }")

df |> group_by(Year) |> 
ec.init(
  yAxis= list(list(name='income', max=45000), 
              list(name='life', max=85)),
  tl.series= list(type='bar', name='income',
                  encode= list(x='Country', y='Income')),
  tooltip= list(show=T), 
  legend= list(data= c('income','life','pop.'))
) |> 
ec.upd({
  options <- lapply(options, \(oo) {
    dix <- oo$series[[1]]$datasetIndex  # from tl.series (bar)
    oo$series <- append(oo$series, 
      list(
        list(type='bar', name='life', yAxisIndex=1, 
             datasetIndex= dix,
             encode= list(x='Country', y='Life Expectancy')),
        list(type='pie', name='pop.', itemStyle= list(color=clr),
             datasetIndex= dix,
             encode= list(value='Population', itemName='Country'), 
             center= c('75%', '25%'), radius= '20%', 
             label= list(show=T), labelLine= list(length=5, length2=0))
    ))
    oo
  })
})

image

helgasoft commented 8 months ago

some less used chart types, translated to echarty from official source, for @msgoussi

waterfall

# original https://echarts.apache.org/examples/en/editor.html?c=bar-waterfall2
xdat <- paste('Nov',1:11)
jscode <- htmlwidgets::JS("function(params) {
  let tar;
  if (params[1] && params[1].value !== '-') {
    tar = params[1];
  } else {
    tar = params[2];
  }
  return tar && tar.name + '<br/>' + tar.seriesName + ' : ' + tar.value;
}")

ec.init(
  title=list(text='Accumulated Waterfall Chart'),
  legend= list(show=T),
  grid= list(left='3%',right='4%',bottom='3%',containLabel=TRUE),
  xAxis=list(type='category', data=xdat),
  yAxis=list(type='value'),
  series=list(
    list(type='bar',stack='Total',silent=TRUE,  #Placeholder
         itemStyle=list(borderColor='transparent',color='transparent'),
         emphasis=list(itemStyle=list(borderColor='transparent',color='transparent')),
         data=list(0, 900, 1245, 1530, 1376, 1376, 1511, 1689, 1856, 1495, 1292)),
    list(name='Income',type='bar',stack='Total',label=list(show=TRUE,position='top'),
         data=list(900, 345, 393, '-', '-', 135, 178, 286, '-', '-', '-')),
    list(name='Expenses',type='bar',stack='Total',label=list(show=TRUE,position='bottom'),
         data=list('-', '-', '-', 108, 154, '-', '-', '-', 119, 361, 203))),
  tooltip=list(trigger='axis', axisPointer=list(type='shadow'), 
               formatter= jscode )
)

image

bar race

# original https://echarts.apache.org/examples/en/editor.html?c=bar-race
data <- round(runif(5,1, 200))
js <- paste0("
const data=[",paste(data,collapse=","),"];
function run() {
  for (var i = 0; i < data.length; ++i) {
    if (Math.random() > 0.9) {
      data[i] += Math.round(Math.random() * 2000);
    } else {
      data[i] += Math.round(Math.random() * 200);
    }
  }
  chart.setOption({ series: [{type: 'bar', data}] });
}
setTimeout(function () { run(); }, 0);
setInterval(function () { run();}, 3000);"
)

ec.init( js= js,
  xAxis = list(max = "dataMax"), 
  yAxis = list(type = "category", data = list("A","B","C","D","E"), inverse = TRUE, 
               animationDuration = 300, animationDurationUpdate = 300), 
  series = list(
    list(realtimeSort= TRUE, type= "bar", 
         data= data, colorBy= 'data',
         label= list(show= TRUE, position= "right", valueAnimation= TRUE))), 
  animationDuration= 0, animationDurationUpdate= 2000, 
  animationEasing= "linear", animationEasingUpdate= "linear"
) 

image

helgasoft commented 7 months ago

New extras function 💲 to build a two-level axis, with responsive auto-resize.
Currently coded in Javascript, but usable with ec.init. R-code version planned also. Often used to present category groups, see https://github.com/apache/echarts/issues/18923. image