R: formatting plotly hover text - r

I am using the R programming language. I trying to learn how to customize hover text in 3d plotly objects as seen here: https://rstudio-pubs-static.s3.amazonaws.com/441420_9a7c15988f3c4f59b2d828eb87ba1634.html
Recently, I have learned how to create a 3d plotly object for some data that I simulated :
library(Rtsne)
library(dplyr)
library(ggplot2)
library(plotly)
library(caret)
library(randomForest)
#data
a = iris
a <- unique(a)
#create two species just to make things easier
s <- c("a","b")
species<- sample(s , 149, replace=TRUE, prob=c(0.3, 0.7))
a$species = species
a$species = as.factor(a$species)
#split data into train/test, and then random forest
index = createDataPartition(a$species, p=0.7, list = FALSE)
train = a[index,]
test = a[-index,]
rf = randomForest(species ~ ., data=train, ntree=50, mtry=2)
#have the model predict the test set
pred = predict(rf, test, type = "prob")
labels = as.factor(ifelse(pred[,2]>0.5, "a", "b"))
confusionMatrix(labels, test$species)
#tsne algorithm
tsne_obj_3 <- Rtsne(test[,-5], perplexity=1, dims=3)
df_m2 <- as.data.frame(tsne_obj_3$Y)
df_m2$labels = test$species
df_m2$color = ifelse(df_m2$labels == "a", "red","blue")
df_m2$petal_length = test$Petal.Length
axis_1 = df_m2$V1
axis_2 = df_m2$V2
axis_3 = df_m2$V3
plot_ly(x=as.vector(axis_1),
y=as.vector(axis_2),
z=axis_3,
type="scatter3d",
mode="markers",
name = "Obs",
marker = list(size = 3)) %>%
add_mesh(x=as.vector(axis_1),
y=as.vector(axis_2),
z=df_m2$pred,
type = "mesh3d",
name = "Preds")
Now, I am trying to customize this plotly object so that different labels appear when you move the mouse over each point, and points corresponding to a given class all have the same color:
p <- plot_ly(type = 'scatter3d', mode = 'markers', colors = "Accent", color = df_m2$color) %>%
add_trace(
x = df_m2$V1,
y = df_m2$V2,
z = df_m2$V3,
marker = list(
size = 3),
name = df_m2$labels,
text = paste("Species: ", df_m2$labels ; "Width: ", df_m2$petal.width ; "color: ", df_m2$color" ),
showlegend = T
) %>%
add_mesh(x=as.vector(axis_1),
y=as.vector(axis_2),
z=df_m2$pred,
type = "mesh3d",
name = "Preds")
%>%
layout(
title = "none",
titlefont = list(
size = 10
),
paper_bgcolor = "#fffff8",
font = t,
xaxis = list(
zeroline = F
),
yaxis = list(
hoverformat = '.2f',
zeroline = F
)
)
p
However, there is an error here. Can someone please show me what I am doing wrong?
Thanks

Not sure what exactly you want to do with the predicted classes, but maybe something like this? (Color corresponds to real species, mouseover also shows prediction).
library(reprex)
reprex({
suppressPackageStartupMessages(invisible(
lapply(c("Rtsne", "dplyr", "ggplot2", "plotly", "caret", "randomForest"),
require, character.only = TRUE)))
#data
a <- unique(iris)
#create two species just to make things easier
set.seed(123)
a$species <- factor(sample(c("a", "b"), 149, replace=TRUE, prob=c(0.3, 0.7)))
#split data into train/test, and then random forest
index = createDataPartition(a$species, p=0.7, list = FALSE)
train = a[index,]
test = a[-index,]
rf <- randomForest(species ~ ., data=train, mtry=2)
#have the model predict the test set
pred <- predict(rf, test, type = "prob")
labels <- predict(rf, test)
confusionMatrix(labels, test$species)
#tsne algorithm
tsne_obj_3 <- Rtsne(test[,-5], perplexity=1, dims=3)
df_m2 <- as.data.frame(tsne_obj_3$Y)
df_m2$labels = toupper(test$species)
df_m2$pred <- labels # you did not define but call pred in plot_ly call
df_m2$color = ifelse(df_m2$labels == "A", "red", "blue")
df_m2$petal_length = test$Petal.Length
axis_1 <- df_m2$V1
axis_2 <- df_m2$V2
axis_3 <- df_m2$V3
plot_ly(type = 'scatter3d', mode = 'markers', colors = c("blue", "red"),
color = df_m2$color) %>%
add_trace(
x = df_m2$V1,
y = df_m2$V2,
z = df_m2$V3,
marker = list(size = 3),
name = df_m2$pred,
text = paste0("Species: ", df_m2$labels, "; Length: ",df_m2$petal_length, "; color: ", df_m2$color),
showlegend = TRUE) %>%
add_mesh(x=as.vector(axis_1),
y=as.vector(axis_2),
z=axis_3, # not sure what z you want here
type = "mesh3d",
name = "Preds") %>%
layout(
title = "none",
titlefont = list(size = 10),
paper_bgcolor = "#fffff8",
font = "Open Sans",
xaxis = list(zeroline = FALSE),
yaxis = list(hoverformat = '.2f', zeroline = FALSE)
)

Related

R plot_ly function plotly package

parameterslist <- cbind(v =c(40, 50, 60))
for (i in 1:nrow(parameterslist))
outlist[[i]] <- ode(func = model, y = yini, times = times, parms = c(parameterslist[i,], parameters))
outlist <- as.data.frame(outlist)
plot_vdisp_3 <- plot_ly(outlist, x = ~times, y = ~power1, name = 'v=30m/s', type = 'scatter', mode = 'lines') %>%
add_trace(y = ~outlist$y1, name = 'v=40m/s', mode = 'lines+markers') %>%
add_trace(y = ~outlist$y1.1, name = 'v=50m/s', mode = 'lines+markers') %>%
add_trace(y = ~outlist$y1.2, name = 'v=60m/s', mode = 'lines+markers') %>%
layout( title = 'Drag force according to different wind speeds',
xaxis = list(title = 'Time (hours)'),
yaxis = list(title = 'Drag Force'))
plot_vdisp_3
I solved a differential equation and created a dataframe but could not plot properly. Could you please explain what should I change? what does outlist$y1 and outlist$y1.1 mean? I created power1 data frame before. but is it ok? during plotting sometimes I could not see the orange line what is the reason for this?
Thanks.
Here is the part of data frame creation part.
times <- seq(from = 0, to = 25, by = 1)
out <- ode(func = model, y = yini, times = times, parms = parameters, method = "rk4")
model_out <- data.frame(times, out)
power1 <- model_out$y1
force1 <- model_out$y2
temp1 <- model_out$y3

Cannot set all violins to the same width in R Plotly

I'm trying to reproduce in R Plotly a 2 categorical variables violin plot that works just fine in ggplot2. But when I set the widths of the individual violins to be the same, using scalemode = "width", as described in the reference (https://plotly.com/r/reference/violin/), it simply wont work. Instead, it shows the widths (violin maximum) proportional to the counts in each category.
Here is an example:
# Paths:
path_data = "data/"
path_lib = "renv/library/R-4.1/x86_64-pc-linux-gnu/"
# Packages:
require(dplyr, lib = path_lib)
require(readr, lib = path_lib)
require(RColorBrewer, lib = path_lib)
require(plotly, lib = path_lib)
# Dataset:
df = readr::read_csv(paste0(path_data, "nasa_exoplanets.csv")) %>%
as.data.frame()
attr(df, "spec") = NULL
df_varnames = readr::read_csv(paste0(path_data, "nasa_exoplanets_var_names.csv")) %>%
as.data.frame()
attr(df_varnames, "spec") = NULL
# Variables:
cat_var1 = "st_metratio"
cat_var2 = "disc_locale"
cat_var_name1 = (df_varnames %>%
dplyr::filter(var == cat_var1))$var_name
cat_var_name2 = (df_varnames %>%
dplyr::filter(var == cat_var2))$var_name
num_var = "sy_dist"
num_var_name = (df_varnames %>%
dplyr::filter(var == num_var))$var_name
# Adapt the data:
df_plot = df %>%
dplyr::select(cat_var1,
cat_var2,
num_var)
# Deal with NA:
df_plot[which(is.na(df_plot[, cat_var1])), cat_var1] = "NA"
df_plot[which(is.na(df_plot[, cat_var2])), cat_var2] = "NA"
df_plot = df_plot[which(!is.na(df_plot[, num_var])), ]
# Levels order:
sorted_levels1 = sort(unique(df_plot[, cat_var1]))
df_plot[, cat_var1] = factor(x = df_plot[, cat_var1],
levels = sorted_levels1)
sorted_levels2 = sort(unique(df_plot[, cat_var2]))
df_plot[, cat_var2] = factor(x = df_plot[, cat_var2],
levels = sorted_levels2)
# Plot:
my_palette = colorRampPalette(c("#111539", "#97A1D9"))
n_levels2 = length(unique(df_plot[, cat_var2]))
p = plot_ly(
data = df_plot,
type = "violin",
x = ~eval(parse(text = cat_var1)),
y = ~eval(parse(text = num_var)),
color = ~eval(parse(text = cat_var2)),
colors = my_palette(n_levels2),
spanmode = "hard",
alpha = 1,
box = list(visible = FALSE),
meanline = list(visible = FALSE),
points = FALSE,
scalemode = "width" ### this doesn't work ###
) %>%
layout(
xaxis = list(
title = paste0("<b>", cat_var_name1, "</b>"),
titlefont = list(size = 20),
tickfont = list(size = 18),
categoryorder = "array"
),
yaxis = list(
title = paste0("<b>", num_var_name, "</b>"),
titlefont = list(size = 20),
tickfont = list(size = 18),
type = "log"
),
margin = list(
l = 10,
r = 10,
t = 10,
b = 10
),
legend = list(
title = list(
text = paste0("<br><b>", cat_var_name2, "</b>"),
font = list(size = 18)
)
),
hoverlabel = list(font = list(size = 16)),
showlegend = TRUE,
violinmode = "group"
)
p
data file: https://github.com/rafael747cardoso/Data_Visualization_Gallery/blob/main/data/nasa_exoplanets.csv
How it should be, plotted in ggplot2:
How it is with R Plotly:

R plotly Update Title When Using Transform Filter

I've created a graph that lets you pick which group's data to plot. I'd like to change the title when you pick the group, but I'm not sure how or if its possible. I'm having trouble learning which way to structure lists for certain plotly parameters. Even if I could add custom text to graph would probably work.
#Working Example so Far
library(plotly)
x <- c(1:100)
random_y <- rnorm(100, mean = 0)
random_y_prim <- rnorm(100, mean = 50)
mydata <- data.frame(x, random_y, random_y_prim, group = rep(letters[1:4], 25))
# Make Group List Button
groupList <- unique(mydata$group)
groupLoop <- list()
for (iter in 1:length(groupList)) {
groupLoop[[iter]] <- list(method = "restyle",
args = list("transforms[0].value", groupList[iter]),
label = groupList[iter])
}
# Set up Axis labeling
f <- list(
family = "Verdana",
size = 18,
color = "#7f7f7f"
)
xLab <- list(
title = "x Axis",
titlefont = f
)
yLab <- list(
title = "y Axis",
titlefont = f
)
fig <- plot_ly(mydata, x = ~x, y = ~random_y
, type = 'scatter', mode = 'lines',
transforms = list(
list(
type = 'filter',
target = ~mydata$group,
operation = '=',
value = groupList[1]
)
)
)
fig <- fig %>%
layout(
title = "Updating Practice",
xaxis = xLab,
yaxis = yLab,
updatemenus = list(
list(
type = 'dropdown',xanchor = 'center',
yanchor = "top",
active = 1,
buttons = groupLoop
)
)
)
fig

Multiple lines/traces for each button in a Plotly drop down menu in R

I am trying to generate multiple graphs in Plotly for 30 different sales offices. Each graph would have 3 lines: sales, COGS, and inventory. I would like to keep this on one graph with 30 buttons for the different offices. This is the closest solution I could find on SO:
## Create random data. cols holds the parameter that should be switched
l <- lapply(1:100, function(i) rnorm(100))
df <- as.data.frame(l)
cols <- paste0(letters, 1:100)
colnames(df) <- cols
df[["c"]] <- 1:100
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly(df,
type = "scatter",
mode = "lines",
x = ~c,
y= ~df[[cols[[1]]]],
name = cols[[1]])
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[-1]) {
p <- p %>% add_lines(x = ~c, y = df[[col]], name = col, visible = FALSE)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(cols, function(col) {
list(method="restyle",
args = list("visible", cols == col),
label = col)
})
)
)
)
print(p)
It works but only on graphs with single lines/traces. How can I modify this code to do the same thing but with graphs with 2 or more traces? or is there a better solution? Any help would be appreciated!
### EXAMPLE 2
#create fake time series data
library(plotly)
set.seed(1)
df <- data.frame(replicate(31,sample(200:500,24,rep=TRUE)))
cols <- paste0(letters, 1:31)
colnames(df) <- cols
#create time series
timeseries <- ts(df[[1]], start = c(2018,1), end = c(2019,12), frequency = 12)
fit <- auto.arima(timeseries, d=1, D=1, stepwise =FALSE, approximation = FALSE)
fore <- forecast(fit, h = 12, level = c(80, 95))
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly() %>%
add_lines(x = time(timeseries), y = timeseries,
color = I("black"), name = "observed") %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 2], ymax = fore$upper[, 2],
color = I("gray95"), name = "95% confidence") %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 1], ymax = fore$upper[, 1],
color = I("gray80"), name = "80% confidence") %>%
add_lines(x = time(fore$mean), y = fore$mean, color = I("blue"), name = "prediction")
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[2:31]) {
timeseries <- ts(df[[col]], start = c(2018,1), end = c(2019,12), frequency = 12)
fit <- auto.arima(timeseries, d=1, D=1, stepwise =FALSE, approximation = FALSE)
fore <- forecast(fit, h = 12, level = c(80, 95))
p <- p %>%
add_lines(x = time(timeseries), y = timeseries,
color = I("black"), name = "observed", visible = FALSE) %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 2], ymax = fore$upper[, 2],
color = I("gray95"), name = "95% confidence", visible = FALSE) %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 1], ymax = fore$upper[, 1],
color = I("gray80"), name = "80% confidence", visible = FALSE) %>%
add_lines(x = time(fore$mean), y = fore$mean, color = I("blue"), name = "prediction", visible = FALSE)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(cols, function(col) {
list(method="restyle",
args = list("visible", cols == col),
label = col)
})
)
)
)
p
You were very close!
If for example you want graphs with 3 traces,
You only need to tweak two things:
Set visible the three first traces,
Modify buttons to show traces in groups of three.
My code:
## Create random data. cols holds the parameter that should be switched
library(plotly)
l <- lapply(1:99, function(i) rnorm(100))
df <- as.data.frame(l)
cols <- paste0(letters, 1:99)
colnames(df) <- cols
df[["c"]] <- 1:100
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly(df,
type = "scatter",
mode = "lines",
x = ~c,
y= ~df[[cols[[1]]]],
name = cols[[1]])
p <- p %>% add_lines(x = ~c, y = df[[2]], name = cols[[2]], visible = T)
p <- p %>% add_lines(x = ~c, y = df[[3]], name = cols[[3]], visible = T)
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[4:99]) {
print(col)
p <- p %>% add_lines(x = ~c, y = df[[col]], name = col, visible = F)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(0:32, function(col) {
list(method="restyle",
args = list("visible", cols == c(cols[col*3+1],cols[col*3+2],cols[col*3+3])),
label = paste0(cols[col*3+1], " ",cols[col*3+2], " ",cols[col*3+3] ))
})
)
)
)
print(p)
PD: I only use 99 cols because I want 33 groups of 3 graphs

Plotly plot not showing in viewer

I tried to run this code and it seems to produce no errors but at the end I don't get the plot for some reason. I had some issues with the variables for the plot but i think that should be fixed now. I can't get the plot in my viewer. Is there an issue with the code or should I reinstall plotly?
library(PortfolioAnalytics)
library(quantmod)
library(PerformanceAnalytics)
library(zoo)
library(plotly)
library(foreach)
library(DEoptim)
library(iterators)
library(fGarch)
library(Rglpk)
library(quadprog)
library(ROI)
library(ROI.plugin.glpk)
library(ROI.plugin.quadprog)
library(ROI.plugin.symphony)
library(pso)
library(GenSA)
library(corpcor)
library(testthat)
library(nloptr)
library(MASS)
library(robustbase)
# Get data
getSymbols(c("MSFT", "SBUX", "IBM", "AAPL", "^GSPC", "AMZN"))
# Assign to dataframe
# Get adjusted prices
prices.data <- merge.zoo(MSFT[,6], SBUX[,6], IBM[,6], AAPL[,6], GSPC[,6], AMZN[,6])
# Calculate returns
returns.data <- CalculateReturns(prices.data)
returns.data <- na.omit(returns.data)
# Set names
colnames(returns.data) <- c("MSFT", "SBUX", "IBM", "AAPL", "^GSPC", "AMZN")
# Save mean return vector and sample covariance matrix
meanReturns <- colMeans(returns.data)
covMat <- cov(returns.data)
# Start with the names of the assets
port <- portfolio.spec(assets = c("MSFT", "SBUX", "IBM", "AAPL", "^GSPC", "AMZN"))
# Box
port <- add.constraint(port, type = "box", min = 0.05, max = 0.8)
# Leverage
port <- add.constraint(portfolio = port, type = "full_investment")
# Generate random portfolios
rportfolios <- random_portfolios(port, permutations = 5000, rp_method = "sample")
# Get minimum variance portfolio
minvar.port <- add.objective(port, type = "Risk", name = "var")
# Optimize
minvar.opt <- optimize.portfolio(returns.data, minvar.port, optimize_method = "random",
rp = rportfolios)
# Generate maximum return portfolio
maxret.port <- add.objective(port, type = "Return", name = "mean")
# Optimize
maxret.opt <- optimize.portfolio(returns.data, maxret.port, optimize_method = "random",
rp = rportfolios)
# Generate vector of returns
minret <- 0.06/100
maxret <- maxret.opt$weights %*% meanReturns
vec <- seq(minret, maxret, length.out = 100)
eff.frontier <- data.frame(Risk = rep(NA, length(vec)),
Return = rep(NA, length(vec)),
SharpeRatio = rep(NA, length(vec)))
frontier.weights <- mat.or.vec(nr = length(vec), nc = ncol(returns.data))
colnames(frontier.weights) <- colnames(returns.data)
for(i in 1:length(vec)){
eff.port <- add.constraint(port, type = "Return", name = "mean", return_target = vec[i])
eff.port <- add.objective(eff.port, type = "Risk", name = "var")
# eff.port <- add.objective(eff.port, type = "weight_concentration", name = "HHI",
# conc_aversion = 0.001)
eff.port <- optimize.portfolio(returns.data, eff.port, optimize_method = "ROI")
eff.frontier$Risk[i] <- sqrt(t(eff.port$weights) %*% covMat %*% eff.port$weights)
eff.frontier$Return[i] <- eff.port$weights %*% meanReturns
eff.frontier$Sharperatio[i] <- eff.port$Return[i] / eff.port$Risk[i]
frontier.weights[i,] = eff.port$weights
print(paste(round(i/length(vec) * 100, 0), "% done..."))
}
feasible.sd <- apply(rportfolios, 1, function(x){
return(sqrt(matrix(x, nrow = 1) %*% covMat %*% matrix(x, ncol = 1)))
})
feasible.means <- apply(rportfolios, 1, function(x){
return(x %*% meanReturns)
})
feasible.sr <- feasible.means / feasible.sd
p <- plot_ly(x = feasible.sd, y = feasible.means, color = feasible.sr,
mode = "markers", type = "scattergl", showlegend = F,
marker = list(size = 3, opacity = 0.5,
colorbar = list(title = "Sharpe Ratio"))) %>%
add_trace(data = eff.frontier, x = 'Risk', y = 'Return', mode = "markers",
type = "scattergl", showlegend = F,
marker = list(color = "#F7C873", size = 5)) %>%
layout(title = "Random Portfolios with Plotly",
yaxis = list(title = "Mean Returns", tickformat = ".2%"),
xaxis = list(title = "Standard Deviation", tickformat = ".2%"),
plot_bgcolor = "#434343",
paper_bgcolor = "#F8F8F8",
annotations = list(
list(x = 0.4, y = 0.75,
ax = -30, ay = -30,
text = "Efficient frontier",
font = list(color = "#F6E7C1", size = 15),
arrowcolor = "white")
))
You have a problem with add_trace() function syntax. If you want markers on the plot you will need to make dimensions of eff.frontier table corresponding to your feasible.sd and feasible.means dimensions, which you set as the first layer of your plot.
Simply, eff.frontier columns length should be the same as for the feasible.sd and feasible.means vectors.
So, if we create an example eff.frontier table with right dimensions we could construct plotly object without any problem:
# create eff.frontier example object
eff.frontier_example <- data.frame(Risk = seq(0.01373, 0.01557, length.out = length(feasible.sd)),
Return = seq(0.0006444, 0.0008915, length.out = length(feasible.sd)))
# create plotly object
p <- plot_ly(x = feasible.sd, y = feasible.means, color = feasible.sr,
mode = "markers", type = "scattergl", showlegend = F,
marker = list(size = 3, opacity = 0.5,
colorbar = list(title = "Sharpe Ratio"))) %>%
add_trace(x = eff.frontier_example$Risk, y = eff.frontier_example$Return, mode = "markers",
type = "scattergl", showlegend = F,
marker = list(color = "#F7C873", size = 5)) %>%
layout(title = "Random Portfolios with Plotly",
yaxis = list(title = "Mean Returns", tickformat = ".2%"),
xaxis = list(title = "Standard Deviation", tickformat = ".2%"),
plot_bgcolor = "#434343",
paper_bgcolor = "#F8F8F8",
annotations = list(
list(x = 0.4, y = 0.75,
ax = -30, ay = -30,
text = "Efficient frontier",
font = list(color = "#F6E7C1", size = 15),
arrowcolor = "white")
))
# show plotly object
p
I'll assume you ran the code exactly as posted. Your last code block assigns the plotly plot to p. Just add the line p to call the plot.
p <- plotly_ly(...)
p

Resources