Is it possible to put logos on the y-axis - r

I have a very simple plotly graph for which I would like to put the company logos instead (or both) of their names on the Y axis
library(plotly)
plot_ly(
y = c("company_1", "company_2", "company_3", "company_4", "company_5"),
x = c(20, 14, 23, 6, 28),
name = "companies",
type = "bar"
)
For example, we can imagine that the companies are Facebook, Twitter, Apple, Microsoft and Tesla
I have images (logo) in png format.
Is there a way to do this?

This isn't something you'll be able to do strictly in R. However, if you use the function onRender from htmlwidgets, you can do it.
I added margin so the images would fit. However, the plot is exactly as you provided it. Make sure that if/when you make changes you respect the whitespace shown here. HTML and Javascript won't understand if the attributes are smashed together.
The first code & plot uses an image that is on the web. The next code & plot uses a local file. That way you can use whichever method applies in your situation.
Images via Hyperlink
plot_ly(
y = c("company_1", "company_2", "company_3", "company_4", "company_5"),
x = c(20, 14, 23, 6, 28),
name = "companies",
type = "bar") %>%
layout(margin = list(l = 120, r = 20, t = 20, b = 50)) %>%
htmlwidgets::onRender("function(){
gimme = document.querySelectorAll('g.ytick'); /* get the text positions on y-axis */
imTag1 = '<image width=\"100\" height=\"100\" ' /* pre-build the HTML */
imTag2 = ' href=\"https://www.rstudio.com/wp-content/uploads/2018/10/RStudio-Logo-Flat.png\" />'
for(i = 0; i < gimme.length; i++) {
zz = gimme[i].firstChild.getAttribute('transform'); /* get position on page*/
yy = gimme[i].firstChild.getAttribute('y');
yy = yy * -10;
img = imTag1 + 'x=\"10\" y=\"' + yy + '\" transform=\"' + zz + '\"' + imTag2;
gimme[i].innerHTML = img; /* replace the text tag */
}
}")
Images via Local File
The image I used here is from https://www.hiclipart.com/free-transparent-background-png-clipart-qzdon/download
In MY files it's saved at ./_images/orangeIcon.png (where '.' represents the current working directory).
Because this file is a png, I need to make something better for web portability, so I used base64 encoding using the package base64enc.
img <- base64enc::dataURI(file = "./_images/orangeIcon.png")
I'm going to assign this to customdata in the call for plotly. If you had five images, you can make a list of the 5 encoded images. if you list one, plotly will replicate it. (That's what it did here.)
plot_ly(
y = c("company_1", "company_2", "company_3", "company_4", "company_5"),
x = c(20, 14, 23, 6, 28),
name = "companies", customdata = img, # <---- custom data!!
type = "bar") %>%
layout(margin = list(l = 120, r = 20, t = 20, b = 50)) %>%
htmlwidgets::onRender("function(el, x){ /* changed here, as well */
gimme = document.querySelectorAll('g.ytick'); /* get the text positions on y-axis */
imgs = x.data[0].customdata; /* collect the images */
imTag1 = '<image width=\"100\" height=\"100\" href=\"' /* pre-build the HTML */
imTag2 = ' \" />'
for(i = 0; i < gimme.length; i++) {
zz = gimme[i].firstChild.getAttribute('transform'); /* get position on page*/
yy = gimme[i].firstChild.getAttribute('y');
yy = yy * -10;
img = imTag1 + imgs[i] + '\" x=\"10\" y=\"' + yy + '\" transform=\"' + zz + '\"' + imTag2;
gimme[i].innerHTML = img; /* replace the text tag */
}
}") # note the differences in imgTag1 and imgTag2
# to accommodate the new info for href

Related

Use hc_tooltip in R Highchart to give format to different lines

I'm trying to format two series of my graph in highchart. The first graph is a serie and the another is a %change. So I want to format each serie using "hc_tooltip" argument. A simplified version of my code to show my problem is the next:
a <- c(30, 40, 10, 40, 80)
b <- c(3, 4, -1, -4, -8)
d<-cbind(a,b)
dt <- seq(as.Date("2018-01-01"), as.Date("2018-01-05"), by = "days")
ts <- xts(d, dt )
highchart(type="stock") %>%
hc_add_series(ts$a,
type = "line",
color="black") %>%
hc_add_series(ts$b,
type = "lollipop",
color="red") %>%
hc_tooltip(pointFormat = '<b>{point.a.name}</b>
{point.y.a:.4f}')%>%
hc_tooltip(pointFormat = '<b>{point.b.name}</b>
{point.y.b:.4f}%')
Like I hoped, It's not working. I want I can see the data from the first serie like integer and the second like % in the graph when I put the mouse in the serie. How can I achieve that?
To achieve that, you need to use the tooltip.formatter with the relevant function
Example:
hc_tooltip(formatter = JS("function () {
if (this.series.name === "A") {
return `<b>${this.series.name}</b></br>${this.y}`
} else if (this.series.name === "B") {
return `<b>${this.series.name}</b></br>${this.y}%`
}}")
JS Demo:
https://jsfiddle.net/BlackLabel/zqyp85ag/
API Reference:
https://api.highcharts.com/highcharts/tooltip.formatter

Preserve custom node placement of a sankeyNetwork (networkD3) in R

I wish to customise the horizontal and vertical position of nodes in a sankeyNetwork (networkD3) in R, and export the network as a high-resolution image.
I credit and use the reproducible example of CJ Yetman (enable horizontal movement of nodes in networkD3's sankeyNetwork plots) which allows the user to move the nodes in any horizontal and vertical position:
library(networkD3)
library(htmlwidgets)
ID <- 0:24
NodeGroup <- c(rep(1,8),2,5,rep(4,7),3,5,6,6,7,7,8,9)
name <- c("29581k","5279k","4218k","1917k","1356k","Ventas diversas: 116",
"Venta diversa: 97","Venta de: 141","Venta totales: 42705",
"Contribucion marginal: 18183", "17531k","1744k","1326k","1208k",
"526k","459k","14k","IIBB: 1714","Costo: 22808",
"Gastos directos: 6734", "Gastos distribudos: 2958","Resultado: 8851",
"Total Gastos: 9332","Imp. Gcias: 3098","Resultado Netto: 5.753")
nodes <- data.frame(ID, name, NodeGroup)
nodes$NodeGroup <- as.character(nodes$NodeGroup)
source <- c(0:7, rep(8,9), 10:16, rep(9,3), 19, 20, 21, 21)
target <- c(rep(8,8), 17, 10:16, 9, rep(18,7), 19:21, rep(22, 2), 23, 24)
value <- c(29581,5279,4218,1917,1356,116,97,141,1714,17531,1744,1326,1208,526,
459,14,18138,17531,1744,1326,1208,526,459,14,6374,2958,8851,6374,
2958,3098,5753)
group <- c(1:8, rep(9,8), 10, rep(19,7), rep(18,3), rep(23,2), rep(22,2))
links <- data.frame(source, target, value, group)
links$group <- as.character(links$group)
sn <- sankeyNetwork(Links=links, Nodes=nodes, Source='source', Target='target',
Value='value', NodeID='name', fontSize=18,
NodeGroup = "NodeGroup",
sinksRight = FALSE,
LinkGroup = "group",
#nodeWidth = 40,
#width=1500, height=500,
#margin = list("right"=250),
iterations = FALSE)
onRender(sn,
'
function(el, x) {
var sankey = this.sankey;
var path = sankey.link();
var nodes = d3.selectAll(".node");
var link = d3.selectAll(".link")
var width = el.getBoundingClientRect().width - 40;
var height = el.getBoundingClientRect().height - 40;
window.dragmove = function(d) {
d3.select(this).attr("transform",
"translate(" + (
d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
) + "," + (
d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
) + ")");
sankey.relayout();
link.attr("d", path);
};
nodes.call(d3.drag()
.subject(function(d) { return d; })
.on("start", function() { this.parentNode.appendChild(this); })
.on("drag", dragmove));
}
'
)
Node positions can be moved in the Viewer window on RStudio, however the resolution is low when using the Export > Save as Image option, or when using a screenshot:
I have also tried exporting the sankey plot to html file, in order to customise and export as an image using the webshot package. However, changing the horizontal position of nodes is disabled and the new node positions are not preserved in the image export:
saveWidget(sn, "SankeyNetwork.html")
library(webshot)
webshot("SankeyNetwork.html", "sn.png", delay = 0.2)
I would be grateful if anyone can suggest a way to export the customised node positions as a high-resolution image and html file. Thank you in advance for your help.

Removing decimals on yAxis in rCharts shiny

I am plotting an nPlot using rCharts in shiny dashboard.. on the yAxis, i have big numbers (9 digits) plus 1 decimal (zero), please see this screenshot yAxis labels
I want to get rid of the zero (highlighted yellow), I have tried several solutions i found on stackoverflow, but nothing has worked till now
I tried to use format(round()) for the variable that is plotted in the yAxis
ct$Market = as.character(ct$Market)
output$top10markets <-renderChart({
topmarkets <-
arrange(ct %>%
group_by(as.character(Market)) %>%
summarise(
CTo = format(round(sum(`Net turnover`)), digits = 0)
), desc(CTo))
colnames(topmarkets)[colnames(topmarkets)=="as.character(Market)"] <- "Market"
topmarkets <- subset(topmarkets[1:10,], select = c(Market, CTo))
topmarkets$CTo <- format(round(topmarkets$CTo, digits = 0))
p <- nPlot(CTo~Market, data = topmarkets, type = "discreteBarChart", dom = "top10markets")
p$params$width <- 1000
p$params$height <- 200
p$xAxis(staggerLabels = TRUE)
p$yAxis(staggerLabels = TRUE, width = 10)
return(p)
})
and got this Error:non-numeric argument to mathematical function
I tried to use the TickFormat inside rCharts
p$yAxis(staggerLabels = TRUE, width = 50, tickFormat = "#! function(d) {return '€' + d} !#")
and got his result yAxis with tickFormat all commas are removed and still it overlaps the yAxis line
i tried also to add some CSS:
.nv-discreteBarWithAxes .nvd3 > g > g > text,
.nv-axisMaxMin text {
transform: translateX(13px);
width: 150px;
height: 80px;
-ms-transform: rotate(20deg);
-webkit-transform: rotate(20deg);
transform: rotate(20deg);
}
.nv-axisMaxMin text {
word-break: break-word;
}
Result: in this screenshot output with CSS
also not good as numbers are exceeding the box borders!
I have tried also to change the box border sizes but it didn't help
Please any help?
thanks a lot
You can set the left margin with p$chart(margin = list(left = 100)), and you can set a padding in p$yAxis by doing tickPadding = 15.
The number formatter you want is tickFormat = "#! function(d) {return d3.format('c')(8364) + d3.format(',.1')(d)} !#" (8364 is the decimal code of the euro sign).
So:
library(rCharts)
dat <- data.frame(
Market = c("A", "B", "C"),
CTo = c(1000000, 5000000, 10000000)
)
p <- nPlot(CTo~Market, data = dat, type = "discreteBarChart")
p$yAxis(tickPadding = 15, tickFormat = "#! function(d) {return d3.format('c')(8364) + d3.format(',.1')(d)} !#")
p$chart(margin = list(left = 100))
p

enable horizontal movement of nodes in networkD3's sankeyNetwork plots

I found this image from the Internet (link) and I think it was draw in R. I tried to reproduce this Figure and make it more or less similar with one from the above link. The code I used is as following:
ID <- 0:24
NodeGroup <- c(rep(1,8),2,5,rep(4,7),3,5,6,6,7,7,8,9)
name <- c("29581k","5279k","4218k","1917k","1356k","Ventas diversas: 116","Venta diversa: 97","Venta de: 141","Venta totales: 42705","Contribucion marginal: 18183", "17531k","1744k","1326k","1208k","526k","459k","14k","IIBB: 1714","Costo: 22808","Gastos directos: 6734","Gastos distribudos: 2958","Resultado: 8851","Total Gastos: 9332","Imp. Gcias: 3098","Resultado Netto: 5.753")
nodes <- data.frame(ID, name, NodeGroup)
nodes$NodeGroup <- as.character(nodes$NodeGroup)
source <- c(0:7, rep(8,9), 10:16, rep(9,3), 19, 20, 21, 21)
target <- c(rep(8,8), 17, 10:16, 9, rep(18,7), 19:21, rep(22, 2), 23, 24)
value <- c(29581,5279,4218,1917,1356,116,97,141,1714,17531,1744,1326,1208,526,459,14,18138,17531,1744,1326,1208,526,459,14,6374,2958,8851,6374,2958,3098,5753)
group <- c(1:8, rep(9,8), 10, rep(19,7), rep(18,3), rep(23,2), rep(22,2))
links <- data.frame(source, target, value, group)
links$group <- as.character(links$group)
sn <- sankeyNetwork(Links=links, Nodes=nodes, Source='source', Target='target',
Value='value', NodeID='name', fontSize=18,
NodeGroup = "NodeGroup",
sinksRight = FALSE,
LinkGroup = "group",
#nodeWidth = 40,
#width=1500, height=500,
#margin = list("right"=250),
iterations = FALSE)
sn
From this links it is possible to change the position of a node not only vertically, but also horizontally. Can we implement it in R?
Update 1: I can solve issue in question 2 by changing the source code of sankeyNetwork.js by using the code provide at this links. I do not know how to implement it through htmlwidgets (I am not familiar with JS; hence, just do trial and error!). I just need to copy the following code to the end of sankeyNetwork.js.
function dragmove(d) {
d3.select(this).attr("transform",
"translate(" + (
d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
) + "," + (
d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
) + ")");
sankey.relayout();
link.attr("d", path);
}
To enable horizontal movement of the nodes, along with the vertical movement, you could adapt d3noob's code to work, but it's not as easy as dropping in just their dragmove function declaration.
It was written using D3v3, and networkD3 uses D3v4... and they're not entirely compatible.
That function refers to a bunch of objects that are defined elsewhere, so the function on its own cannot work without knowing what these are: width, height, sankey, link, and path.
Here is one way of adapting it to work...
library(networkD3)
library(htmlwidgets)
ID <- 0:24
NodeGroup <- c(rep(1,8),2,5,rep(4,7),3,5,6,6,7,7,8,9)
name <- c("29581k","5279k","4218k","1917k","1356k","Ventas diversas: 116",
"Venta diversa: 97","Venta de: 141","Venta totales: 42705",
"Contribucion marginal: 18183", "17531k","1744k","1326k","1208k",
"526k","459k","14k","IIBB: 1714","Costo: 22808",
"Gastos directos: 6734", "Gastos distribudos: 2958","Resultado: 8851",
"Total Gastos: 9332","Imp. Gcias: 3098","Resultado Netto: 5.753")
nodes <- data.frame(ID, name, NodeGroup)
nodes$NodeGroup <- as.character(nodes$NodeGroup)
source <- c(0:7, rep(8,9), 10:16, rep(9,3), 19, 20, 21, 21)
target <- c(rep(8,8), 17, 10:16, 9, rep(18,7), 19:21, rep(22, 2), 23, 24)
value <- c(29581,5279,4218,1917,1356,116,97,141,1714,17531,1744,1326,1208,526,
459,14,18138,17531,1744,1326,1208,526,459,14,6374,2958,8851,6374,
2958,3098,5753)
group <- c(1:8, rep(9,8), 10, rep(19,7), rep(18,3), rep(23,2), rep(22,2))
links <- data.frame(source, target, value, group)
links$group <- as.character(links$group)
sn <- sankeyNetwork(Links=links, Nodes=nodes, Source='source', Target='target',
Value='value', NodeID='name', fontSize=18,
NodeGroup = "NodeGroup",
sinksRight = FALSE,
LinkGroup = "group",
#nodeWidth = 40,
#width=1500, height=500,
#margin = list("right"=250),
iterations = FALSE)
onRender(sn,
'
function(el, x) {
var sankey = this.sankey;
var path = sankey.link();
var nodes = d3.selectAll(".node");
var link = d3.selectAll(".link")
var width = el.getBoundingClientRect().width - 40;
var height = el.getBoundingClientRect().height - 40;
window.dragmove = function(d) {
d3.select(this).attr("transform",
"translate(" + (
d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
) + "," + (
d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
) + ")");
sankey.relayout();
link.attr("d", path);
};
nodes.call(d3.drag()
.subject(function(d) { return d; })
.on("start", function() { this.parentNode.appendChild(this); })
.on("drag", dragmove));
}
'
)

Highlight/find data points in plotly scatter from the browser

I generated a scatterplot in HTML format using plotly and a generic dataframe. I am aware that it is possible to highlight (with a different color for example) certain data points before generating the plot HTML. However, I wonder if it is possible to add an element to the HTML file that would enable a user to find/highlight a certain data point based on its text label after the plot has been produced.
The code I used to produce the dataframe and scatter:
tab <- data.frame(sample.id = pca$sample.id,
EV1 = pca$eigenvect[, 1],
EV2 = pca$eigenvect[, 2],
stringsAsFactors=F)
p <- plot_ly(tab, x=tab$EV1, y=tab$EV2, text=tab$sample.id)
p <- layout(p, title="PCA", xaxis=list(title="PC 1"),
yaxis=list(title="PC 2"))
htmlwidgets::saveWidget(as.widget(p), paste(output_name, ".html", sep=""))
As far as I know there is not builtin functionality in Plotly but you just need a few lines of Javascript code to get the functionality.
Plotly stores the data in a application/json object in the HTML file. You can get the data via
var data = JSON.parse(document.querySelectorAll("script[type='application/json']")[0].innerHTML);
The text elements are stored in
data.x.data[i].text[j]
where i is the trace number and j is point number.
Now we need a text field and a button, we can use htmltools for that purpose
p <- htmlwidgets::appendContent(p, htmltools::tags$input(id='inputText', value='Merc', ''), htmltools::tags$button(id='buttonSearch', 'Search'))
Let's add a eventlister to the button which triggers a hover event of the first point of the first trace.
p <- htmlwidgets::appendContent(p, htmltools::tags$script(HTML(
'document.getElementById("buttonSearch").addEventListener("click", function()
{
var myDiv = document.getElementsByClassName("js-plotly-plot")[0]
Plotly.Fx.hover(myDiv, [{curveNumber: 0, pointNumber: 0}]);
}
)
')))
And the whole code which searches for through all text labels and triggers a hover event when the entered text is found in the label.
library(plotly)
library(htmlwidgets)
library(htmltools)
pcaCars <- princomp(mtcars, cor = TRUE)
carsHC <- hclust(dist(pcaCars$scores), method = "ward.D2")
carsDf <- data.frame(pcaCars$scores, "cluster" = factor(carsClusters))
carsClusters <- cutree(carsHC, k = 3)
carsDf <- transform(carsDf, cluster_name = paste("Cluster", carsClusters))
p <- plot_ly(carsDf, x = ~Comp.1 , y = ~Comp.2, text = rownames(carsDf),
mode = "markers", color = ~cluster_name, marker = list(size = 11), type = 'scatter', mode = 'markers')
p <- htmlwidgets::appendContent(p, htmltools::tags$input(id='inputText', value='Merc', ''), htmltools::tags$button(id='buttonSearch', 'Search'))
p <- htmlwidgets::appendContent(p, htmltools::tags$script(HTML(
'document.getElementById("buttonSearch").addEventListener("click", function()
{
var i = 0;
var j = 0;
var found = [];
var myDiv = document.getElementsByClassName("js-plotly-plot")[0]
var data = JSON.parse(document.querySelectorAll("script[type=\'application/json\']")[0].innerHTML);
for (i = 0 ;i < data.x.data.length; i += 1) {
for (j = 0; j < data.x.data[i].text.length; j += 1) {
if (data.x.data[i].text[j].indexOf(document.getElementById("inputText").value) !== -1) {
found.push({curveNumber: i, pointNumber: j});
}
}
}
Plotly.Fx.hover(myDiv, found);
}
);')))
htmlwidgets::saveWidget(p, paste('pca', ".html", sep=""))
p
The PCA implementation was modified from here.

Resources