The column-to-style feature allows binding data columns to individual
item properties like style,label,emphasis,etc. See series.data
for the full list.
There are two conditions to set this up:
series$encode$data with a value and properties
pointing to column namesHere is an example on how to use it. Some explanations are in the code.
comments <- "
value 'pos' is the coordinate of each item in singleAxis
'symbol' and 'symbolSize' are data properties and data columns with the same name are automatically bound to them, see https://echarts.apache.org/en/option.html#series-scatter.data.symbolSize
emphasis$label$fontSize is explicitly bound to column 'lftSize'
'country','tw','c2' data columns are also copied to each item's data, to use in tooltip,etc.
"
data <- data.frame(
country= c('China','USA','India','Russia','Japan'),
c2= c('cn','us','in','ru','jp'),
tw= c(8990,4145,1532,1024,913)
) |> mutate(
symbolSize= 2*sqrt(tw/pi) *1.5, # area of circle * scale
pos= cumsum(symbolSize),
symbol= paste0('image://https://hatscripts.github.io/circle-flags/flags/',c2,'.svg'),
lftSize= c(14,12,10,10,10)
)
ec.init( data,
grid=list(show=T, backgroundColor=
list(type="linear", x=0, y=0, x2=0, y2=1,
colorStops= list(
list(offset=0, color="#EDDD53aa"),
list(offset=0.5, color='#57C785aa'),
list(offset=1, color="#2A7B9Baa"))
)),
title= list(text='⚡️ Electricity consumption in 2024, \nleading countries (in terawatt-hours)',
subtext='source Statista',
sublink='https://www.statista.com/statistics/267081/electricity-consumption-in-selected-countries-worldwide/'),
singleAxis= list(type='value' ,
left='-2%', width='99%', splitLine= list(show=F),
axisLine= list(show=F), axisLabel= list(show=F), axisTick= list(show=F) ),
series.param= list(type='scatter', coordinateSystem= 'singleAxis',
label= list(show=T, position='bottom', formatter= ec.clmn('%L@', 'tw'), fontWeight='bold'),
encode= list(data= list(
value= c('pos'), # value is required, defines which columns correspond to the chart axes
emphasis= list(label= list(color= 'purple', fontSize='lftSize')))
)),
tooltip= list(formatter= ec.clmn('%@<br> %L@ TWh', 'country','tw'))
)
ECharts v.6 was released in July 2025 introducing some new chart
types.
Here are a few examples made with echarty.
Correlation heatmap in table format
mtx <- cor(swiss)
cols <- colnames(mtx)
mtx[upper.tri(mtx)] <- NA
datam <- as.data.frame(mtx)
#datam <- tibble::rownames_to_column(datam, 'x')
datam <- datam |> mutate(x= rownames(datam)); rownames(datam) <- NULL
# Convert to long format
long_data_base <- reshape( datam, direction= "long",
idvar = "x",
varying = list(colnames(mtx)),
v.names = "value",
timevar = "y",
times = cols # Custom labels for timevar
)
datam <- na.omit(long_data_base)
row.names(datam) <- NULL
vals <- lapply(cols, \(x) { list(value=x) })
ec.init(
title= list(text= 'demo: new matrix chart from ECharts v.6.0'),
matrix= list(x= list(data=vals), y= list(data=vals)),
visualMap= list(type='continuous', min=-1,max=1, dimension=3,
calculable=TRUE, orient='horizontal', bottom=0, left='center'),
series= list(list(type= 'heatmap', coordinateSystem= 'matrix',
data= ec.data(datam),
label= list(show=TRUE, formatter= ec.clmn('%R2@', 3))
))
)
Matrix is a coordinate system. It could be used to position charts in flexible layouts. Rewritten in R from the original. See also a more advanced example
jscode <- "function makeRenderItem() {
return function (params, api) {
xDim = params.encode.x[0];
yDim = params.encode.y[0];
valDim = xDim +1;
const dataExtent = [0, 10000];
const xval = api.value(xDim);
const yval = api.value(yDim);
const labelVal = api.value(valDim);
const rect = api.layout([xval, yval]).rect;
if (!rect) {
return;
}
const height = rect.height * 0.2;
const barY = rect.y + ((rect.height - height) / 4) * 3;
const barX = rect.x + rect.width * 0.15;
const widthMax = rect.width * 0.8;
const width = linearMap(labelVal, dataExtent, [0, widthMax]);
//console.log(rect, labelVal, width);
return {
type: 'group',
children: [
{
type: 'rect',
shape: { x: barX, y: barY, width, height },
style: api.style({
fill: '#0ca8df'
})
},
{
type: 'text',
x: barX,
y: rect.y + (rect.height / 4) * 1.5,
style: {
text: labelVal + '',
fill: '#333',
align: 'left',
verticalAlign: 'middle'
}
}
]
};
};
}
function linearMap(val, domain, range) {
const d0 = domain[0];
const d1 = domain[1];
const r0 = range[0];
const r1 = range[1];
const subDomain = d1 - d0;
const subRange = r1 - r0;
return subDomain === 0
? subRange === 0
? r0
: (r0 + r1) / 2
: val === d0
? r0
: val === d1
? r1
: ((val - d0) / subDomain) * subRange + r0;
}
"
ec.init(
dataset= list(source= list(
list("2021-02-01", "amount", 1212, "file", 2321, "Q", 1412),
list("2021-02-02", "amount", 7181, "file", 2114, "Q", 1402),
list("2021-02-03", "amount", 2763, "file", 4212, "Q", 8172),
list("2021-02-04", "amount", 6122, "file", 2942, "Q", 6121),
list("2021-02-05", "amount", 4221, "file", 3411, "Q", 1987) )),
matrix = list(x= list(levelSize= 50, itemStyle= list(color= "#f0f8ff"), label= list(fontWeight = "bold"))),
series = list(
list(type= "custom", coordinateSystem= "matrix", encode= list(x= 2, y= 1), renderItem= JS("makeRenderItem()")),
list(type= "custom", coordinateSystem= "matrix", encode= list(x= 4, y= 1), renderItem= JS("makeRenderItem()")),
list(type= "custom", coordinateSystem= "matrix", encode= list(x= 6, y= 1), renderItem= JS("makeRenderItem()"))
)
) |>
prependContent(HTML(paste("<script>",jscode,"</script>")))
ec.init(
load= 'https://cdn.jsdelivr.net/gh/apache/echarts-custom-series@main/custom-series/segmentedDoughnut/dist/index.auto.js', # custom chart source code
ask= 'loadRemote',
series.param= list(
type= 'custom', renderItem= 'segmentedDoughnut',
coordinateSystem= 'none',
itemPayload= list(
radius= list('50%','65%'), segmentCount= 8,
label= list(show=T, formatter= '{c}/{b}', fontSize=35, color= '#555')
),
data= list(5) )
)
Local zoom-in functionality for large-data charts. Tested with line and scatter charts. Uses the new “broken axis” feature from v.6. Rewritten in R from the original example
#---- fisheye v.6 -----
# https://echarts.apache.org/examples/en/editor.html?c=line-fisheye-lens&edit=1&reset=1
jscode <- "function initAxisBreakInteraction() {
GRID_TOP = 120;
GRID_BOTTOM = 80
GRID_LEFT = 60;
GRID_RIGHT = 60;
function roundXYValue(val) {
return +(+val).toFixed(0);
}
var _brushingEl = null;
myChart = ec_chart(echwid);
myChart.getZr().on('mousedown', function (params) {
_brushingEl = new echarts.graphic.Rect({
shape: { x: params.offsetX, y: params.offsetY },
style: { stroke: 'none', fill: '#ccc' },
ignore: true
});
myChart.getZr().add(_brushingEl);
});
myChart.getZr().on('mousemove', function (params) { //debugger;
if (!_brushingEl) {
return;
}
var initX = _brushingEl.shape.x;
var initY = _brushingEl.shape.y;
var currPoint = [params.offsetX, params.offsetY];
_brushingEl.setShape('width', currPoint[0] - initX);
_brushingEl.setShape('height', currPoint[1] - initY);
_brushingEl.ignore = false;
});
document.addEventListener('mouseup', function (params) {
if (!_brushingEl) {
return;
}
var initX = _brushingEl.shape.x;
var initY = _brushingEl.shape.y;
var currPoint = [params.offsetX, params.offsetY];
var xPixelSpan = Math.abs(currPoint[0] - initX);
var yPixelSpan = Math.abs(currPoint[1] - initY);
if (xPixelSpan > 2 && yPixelSpan > 2) {
updateAxisBreak(
myChart,
[initX, initY],
currPoint,
xPixelSpan,
yPixelSpan
);
}
myChart.getZr().remove(_brushingEl);
_brushingEl = null;
});
function updateAxisBreak(myChart, initXY, currPoint, xPixelSpan, yPixelSpan) {
var dataXY0 = myChart.convertFromPixel({ gridIndex: 0 }, initXY);
var dataXY1 = myChart.convertFromPixel({ gridIndex: 0 }, currPoint);
function makeDataRange(v0, v1) {
var dataRange = [roundXYValue(v0), roundXYValue(v1)];
if (dataRange[0] > dataRange[1]) {
dataRange.reverse();
}
return dataRange;
}
var xDataRange = makeDataRange(dataXY0[0], dataXY1[0]);
var yDataRange = makeDataRange(dataXY0[1], dataXY1[1]);
var xySpan = getXYAxisPixelSpan(myChart);
var xGapPercentStr = (xPixelSpan / xySpan[0]) * 100 + '%';
var yGapPercentStr = (yPixelSpan / xySpan[1]) * 100 + '%';
function makeOption(xGapPercentStr, yGapPercentStr) {
return {
xAxis: {
breaks: [
{
start: xDataRange[0],
end: xDataRange[1],
gap: xGapPercentStr
}
]
},
yAxis: {
breaks: [
{
start: yDataRange[0],
end: yDataRange[1],
gap: yGapPercentStr
}
]
}
};
}
// This is to make a transition animation effect - firstly create axis break
// on the brushed area, then collapse it to a small gap.
myChart.setOption(makeOption(xGapPercentStr, yGapPercentStr));
setTimeout(() => {
var option = makeOption('80%', '80%');
addClearButtonUpdateOption(option, true);
myChart.setOption(option);
}, 0);
}
function getXYAxisPixelSpan(myChart) {
return [
myChart.getWidth() - GRID_LEFT - GRID_RIGHT,
myChart.getHeight() - GRID_BOTTOM - GRID_TOP
];
}
} // End of initAxisBreakInteraction
setTimeout(initAxisBreakInteraction, 111);
function addClearButtonUpdateOption(option, show) {
option.graphic = [
{
elements: [
{
type: 'rect',
ignore: !show,
name: 'clearAxisBreakBtn',
top: 5,
left: 5,
shape: { r: 3, width: 70, height: 30 },
style: { fill: '#eee', stroke: '#999', lineWidth: 1 },
textContent: {
type: 'text',
style: {
text: 'Reset',
fontSize: 15,
fontWeight: 'bold'
}
},
textConfig: { position: 'inside' }
}
]
}
];
}"
# rewritten from JS to R by AI
generateSeriesData <- function() {
seriesData <- list()
DATA_COUNT <- 1000
reset1 <- TRUE
reset2 <- TRUE
yVal <- 0
for (idx in 0:(DATA_COUNT - 1)) {
if (idx < DATA_COUNT / 4) {
yVal <- makeRandom(yVal, c(100, 10000), 50000)
} else if (idx < (2 * DATA_COUNT) / 3) {
if (reset1) {
yVal <- 110010
reset1 <- FALSE
}
yVal <- makeRandom(yVal, c(100000, 105000), 50000)
} else {
if (reset2) {
yVal <- 300100
reset2 <- FALSE
}
yVal <- makeRandom(yVal, c(300000, 305000), 20000)
}
seriesData[[idx + 1]] <- c(idx, yVal)
}
return(seriesData)
}
makeRandom <- function(lastYVal, range, factor) {
lastYVal <- lastYVal - range[1]
delta <- (runif(1) - 0.5 * sin(lastYVal / factor)) *
(range[2] - range[1]) * 0.8
return(roundXYValue(lastYVal + delta + range[1]))
}
roundXYValue <- function(value, digits=0) {
return(round(value, digits))
}
library(htmlwidgets); library(htmltools)
GRID_TOP <- 120
GRID_BOTTOM <- 80
GRID_LEFT <- GRID_RIGHT <- 60
breakAreaStyle <- list(expandOnClick=F, zigzagZ= 200, zigzagAmplitude= 0,
itemStyle= list(borderColor= '#777', opacity= 0)
)
ec.init(title= list(text= 'Fisheye Lens on Line Chart',
subtext= 'Brush to magnify the details', left='center',
textStyle= list(fontSize=20),subtextStyle=list(color='#175ce5',fontSize=15,fontWeight='bold')),
tooltip= list(trigger='axis'),
legend= list(show=T), grid=list(top=GRID_TOP,bottom=GRID_BOTTOM,left=GRID_LEFT,right=GRID_RIGHT),
xAxis= list(splitLine=list(show=FALSE), breakArea= breakAreaStyle),
yAxis= list(axisTick=list(show=TRUE), breakArea= breakAreaStyle),
series.param= list(type='line',name='Data A',symbol='circle',showSymbol=FALSE,symbolSize=5, data= generateSeriesData()),
on= list(list(event='click', handler= JS("function (params) {
if (params.name === 'clearAxisBreakBtn') {
var option = {
xAxis: { breaks: [] },
yAxis: { breaks: [] }
};
addClearButtonUpdateOption(option, false);
this.setOption(option);
}
}") ))
) |>
appendContent(HTML(paste("<script>",jscode,"</script>")))
ec.init(
tooltip = list(show=T), legend= list(show=T),
series.param= list(type = "chord", name = "test",
startAngle = 90, endAngle = -270, clockwise = FALSE,
lineStyle = list(color = "target"),
data = list(
list(name= "A"), list(name= "B"), list(name= "C"), list(name= "D")),
links = list(list(source = "A", target = "B", value = 40),
list(source = "A", target = "C", value = 20, lineStyle= list(color = "source")),
list(source = "A", target = "D", value = 10) ))
)
You can read about the new v.6 features here.