I am somewhat new to plotly and I am trying to make an before-after dot plot in which you can switch variables by dropdown menu. I actually achieved this, but I want to have a color legend feature that categorizes the direction of the before-after differences into "Before > After", "Before < After" etc. In the example I named this variable dir_y. The updatemenus updates the variables (in my example y and z) but I dont know how to update dir_y and dir_z while maintaining only the 2 dropdown options ("Var y" and "Var z"). Needless to say, I need to update dir_y and dir_z in oder to select only one category ("Before > After", "Before < After" etc) from the legend and that category needs to correspond to either y or z depending on which one is selected from the dropdown. I added 2 comments where I thought dir_y and dir_z updating should go, but nothing I tried worked.
Thank you. Any help is greatly appreciated.
Here is my code:
library(plotly)
library(tidyverse)
set.seed(81)
df <- data.frame(id = rep(1:100, 2),
x = c(rep("pre", 100), rep("post", 100)),
y = runif(200),
z = rnorm(200, mean = 50, sd = 10))
df <- df[-sample(1:nrow(df), size = 20) , ] # delete some rows at random to simulate missing values
df_plotly <-
df %>%
mutate(x = forcats::fct_relevel(x, "pre", "post")) %>% # relevel Pre Post for plot
mutate(jit_x = jitter(as.numeric(x))) %>% # add jitter to x discrete var before piping to plotly
mutate(y = round(y, 2),
z = round(z, 2)) %>% # round y & z
group_by(id) %>% # group by id
mutate(dif_y = coalesce(lag(y) - y, y - lead(y)), # do Pre - Post by id for y
dif_z = coalesce(lag(z) - z, z - lead(z))) %>% # do Pre - Post by id for z
mutate(dir_y = case_when(dif_y != 0 && dif_y > 0 ~ "Pre > Post",
dif_y != 0 && dif_y < 0 ~ "Pre < Post",
dif_y == 0 ~ "Pre = Post",
TRUE ~ "Unpaired"),
dir_z = case_when(dif_z != 0 && dif_z > 0 ~ "Pre > Post",
dif_z != 0 && dif_z < 0 ~ "Pre < Post",
dif_z == 0 ~ "Pre = Post",
TRUE ~ "Unpaired"))
p1 <-
df_plotly %>%
plot_ly(x = ~jit_x, y = ~y) %>%
add_trace(x = ~jit_x, y = ~y, color = ~dir_y, colors = c("red", "lightgrey", "green", "black"),
mode = 'markers+lines', type = 'scatter', hoverinfo = 'text+y',
text = ~paste("ID: ", id, "<br>")
) %>%
layout(
title = "",
xaxis = list(title = "",
tickvals = list(1, 2), # jitter(1:2) from 2 levels factor produces values around 1 & 2, should be fine
ticktext = list("Pre", "Post") ),
yaxis = list(title = "",
hoverformat = '.2f',
zeroline = F),
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list("y", list(df_plotly$y) # , "dir_y", list(df_plotly$dir_y)
),
label = "Var Y"),
list(method = "restyle",
args = list("y", list(df_plotly$z) # , "dir_y", list(df_plotly$dir_z)
),
label = "Var Z")))
))
p1
Related
I'm trying to create a scatter plot using highchart, but the color in the graph and in the labels are different. The color should be defined by the column named "Check_color" beacuse i'm using into Rshiny app, and sometimes i don't have all the options in the graph and the color should be align with the column "Rank". I mean, if only "Yes" is selected all points should be green, if only "No", should be red, etc...
Ando Righ now in the graph "Yes" in green, but in the labels are blue, and the same for the rest. How can i math the colors in the labels? Thanks !!
This is my current code
data = data.table(
CJ(x = seq(as.Date("2019-01-01"), as.Date("2019-01-10"), by = "day"),
group = seq(1,20))
)
data[, value := round(runif(n=200, 0,5),4)]
data = data.table(data %>% mutate(cat=cut(value, breaks=quantile(data[value!=0]$value, seq(0,1,0.1)), labels=seq(1,10))))
colf = colorRampPalette(colors = c("red","yellow", "green"))
cols = colf(10)
data[, color := as.factor(cols[cat])]
data$x = datetime_to_timestamp(data$x)
data = data.table(data %>% group_by(x) %>% mutate(y = (order(order(value))-sum(value<0,na.rm=T))))
data[, name := group]
data$x <- runif(200, 100, 1000) / 10
data$y <- runif(200, 100, 1000) / 10
data$gp_ <- round(runif(200,1,5), digits = 0)
data$Index <- seq(1,200,1)
data$Rank <- ifelse(data$gp_ == 1 , "Yes", ifelse(data$gp_ == 2 , "No",ifelse(data$gp_ == 3 , "Minor Deficiency",ifelse(data$gp_ == 4 , "Major Deficiency",ifelse(data$gp_ == 5 , "Not Applicable","")))))
data <- data[1:71,]
data$Check_color <- ifelse(data$Rank == "Yes" , "#14E632", ifelse(data$Rank == "No" , "#FA0101",ifelse(data$Rank == "Minor Deficiency" , "#FF99FF",ifelse(data$Rank == "Major Deficiency" , "#FF9933",ifelse(data$Rank == "Not Applicable" , "#CACECE","")))))
hc_1 <- data %>%
hchart('scatter', hcaes(x = x, y = y , group = Rank, color = Check_color )) %>%
hc_title(text = "<b>PUBLIC COMPANY D&O COVERAGE HEAT MAP </b>") %>%
hc_chart(
borderColor = "#999999",
borderRadius = 20,
borderWidth = 3) %>%
hc_tooltip(pointFormat = 'Provision ID: {point.Index} <br/>
Provision: {point.Check_color} <br/>
Severity: {point.y:.2f} <br/>
Frequency: {point.x:.2f} ')
hc_1
The problem with the two color scales came from using group = Rank and color = Check_color in the hcaes. Remove color = Check_color, and you get matching color. However, I do not know how to specify the color from there... I tried hc_color() which didn't work. Maybe someone else can complete this answer!
hc_1 <- data %>%
hchart('scatter', hcaes(x = x, y = y , group = Rank )) %>%
hc_title(text = "<b>PUBLIC COMPANY D&O COVERAGE HEAT MAP </b>") %>%
hc_chart(
borderColor = "#999999",
borderRadius = 20,
borderWidth = 3) %>%
hc_tooltip(pointFormat = 'Provision ID: {point.Index} <br/>
Provision: {point.Check_color} <br/>
Severity: {point.y:.2f} <br/>
Frequency: {point.x:.2f} ')
hc_1
I have a data frame with 2 columns, one that I want to use as a toggle (so display grp1 or grp2) and another where I want to split the data into different lines. I can't seem to figure out how to get it to work properly with plotly, I think there should be a simple straightforward way to do it but for the life of me I can't get it to stop mixing up the groups.
library(tidyverse)
library(plotly)
library(ggplot2)
# example data my group 1 would be social, group 2 would be grp
df1 = data.frame(grp = "A", social = "Facebook",
days = c("2020-01-01","2020-01-02","2020-01-03","2020-01-04"),
yval = c(0.1, 0.2, 0.3, 0.4))
df2 = df1
df2$grp = "B"
df2$yval = df2$yval + 0.2
df3 = df1
df3$grp = "C"
df3$yval = df3$yval + 0.4
df = rbind(df1, df2, df3)
aux = df
aux$social = "Twitter"
aux$yval = aux$yval + 0.1
df = rbind(aux, df)
rm(aux, df1, df2, df3)
df$days = as.Date(df$days)
df$social_group = paste(df$social, df$grp)
ggplot(data = df, mapping = aes(x = days, y = yval, color = social)) + geom_point() + geom_line() + facet_wrap(facets = ~social)
So what I'm trying to do is to create a plotly that lets me switch between the ggplot facets, by toggling a Facebook or Twitter button.
This is what I currently got, which starts well, but as soon as I toggle the buttons the groups seem to mix, which shouldn't be happening when I consider I'm filtering on another column...
facebook_annotations <- list(
data=df %>% filter(social=="Facebook"),
x=~days,
y=~yval,
color = ~grp,
hovertemplate = paste('%{x}', '<br>Hover text: %{text}<br>'),
text=~days
)
twitter_annotations <- list(
data=df %>% filter(social=="Twitter"),
x=~days,
y=~yval,
color = ~grp,
hovertemplate = paste('%{x}', '<br>Hover text: %{text}<br>'),
text=~days
)
# updatemenus component
updatemenus <- list(
list(
active = 0,
type = "buttons",
buttons = list(
list(
label = "Facebook",
method = "update",
args = list(list(visible = c(TRUE, FALSE)),
list(title = "Facebook",
annotations = list(facebook_annotations, c())))),
list(
label = "Twitter",
method = "update",
args = list(list(visible = c(FALSE, TRUE)),
list(title = "Twitter",
annotations = list(c(), twitter_annotations)))))
)
)
fig <- df %>% plot_ly(type="scatter", mode="lines")
fig <- fig %>% add_lines(
data=df %>% filter(social=="Facebook"),
x=~days,
y=~yval,
color = ~grp,
hovertemplate = paste('%{x}', '<br>Hover text: %{text}<br>'),
text=~days
)
fig <- fig %>% add_lines(
data=df %>% filter(social=="Twitter"),
x=~days,
y=~yval,
color = ~grp,
hovertemplate = paste('%{x}', '<br>Hover text: %{text}<br>'),
text=~days,
visible=FALSE
)
fig <- fig %>% layout(title="Facebook",
xaxis=list(title=""),
yaxis = list(range = c(0, 1), title = "My Title"),
updatemenus=updatemenus)
fig
What am I missing? It's driving me crazy, I'm even considering just adding each group as an individual trace, but that's not really practical when my actual case study has 8 groups...
Is there a way to add an arrow image onto the side of a barchart, based on some underlying information?
I have the following script:
library(tidyverse)
library(plotly)
data <- tibble(url = c("google.com","yahoo.com","yandex.com"), values = c(500,400,300), change = c(0.5,-0.9,0.1))
data
data %>%
plot_ly(x = data$values,
y = data$url,
type = "bar")
This produces a simple bar chart in plotly, I would like to add some arrows next to each bar chart to show that the values have either decreased or increased based on the data$change column. so if the number is positive a arrow turned up and green, if negative, then an arrow that is red and pointed down
Is this possible?
If none of this possible - is there a way to overlay just the text of the percentage change instead next to the bar charts?
Hopefully this would go onto a shiny app, so even if there is a way of embedding or overlaying a html element would be useful!
If there is an alternative in ggplot, I would also be interested.
Hopefully something that looks like this:
JUST TO UPDATE IN REGARDS TO BELOWS ANSWER THE CODE WOULD BE:
`library(tidyverse)
library(plotly)
data <- tibble(url = c("google.com","yahoo.com","yandex.com"), values = c(500,400,300), change = c(0.5,-0.9,0.1))
data
data %>%
plot_ly(x = data$values,
y = data$url,
type = "bar")
library(dplyr)
data <- data %>%
mutate(x.start = values + 50,
y.end = seq(0,2,1),
y.start = y.end + 0.5) %>%
mutate(y.start.new = case_when(sign(change) == -1 ~ y.end,
TRUE ~ y.start),
y.end.new = case_when(sign(change) == -1 ~ y.start,
TRUE ~ y.end)
)
data %>%
plot_ly(x = data$values,
y = data$url,
type = "bar") %>%
add_markers(~values, ~url) %>%
add_annotations(x = ~x.start[change == "up"],
y = ~y.start.new[change == "up"],
xref = "x", yref = "y",
axref = "x", ayref = "y",
text = "",
showarrow = T,
ax = ~x.start[change == "up"],
ay = ~y.end.new[change == "up"],
arrowcolor = "green") %>%
add_annotations(x = ~x.start[change == "down"],
y = ~y.start.new[change == "down"],
xref = "x", yref = "y",
axref = "x", ayref = "y",
text = "",
showarrow = T,
ax = ~x.start[change == "down"],
ay = ~y.end.new[change == "down"],
arrowcolor = "red")
`
But you do not produce the same output - only one arrow appears?
You can add annotations. First specify arrow start and end positions: Please note that in the data you provide you have yahoo decreasing, not yandex like in your plot.
library(dplyr)
data <- data %>%
mutate(x.start = values + 50,
y.end = seq(0,2,1),
y.start = y.end + 0.5) %>%
mutate(y.start.new = case_when(sign(change) == -1 ~ y.end,
TRUE ~ y.start),
y.end.new = case_when(sign(change) == -1 ~ y.start,
TRUE ~ y.end)
) %>%
mutate(change_dir = case_when(sign(change) == -1 ~ "down",
sign(change) == 1 ~ "up"))
Then plot using add_annotations
data %>%
plot_ly(x = data$values,
y = data$url,
type = "bar") %>%
add_markers(~values, ~url) %>%
add_annotations(x = ~x.start[change_dir == "up"],
y = ~y.start.new[change_dir == "up"],
xref = "x", yref = "y",
axref = "x", ayref = "y",
text = "",
showarrow = T,
ax = ~x.start[change_dir == "up"],
ay = ~y.end.new[change_dir == "up"],
arrowcolor = "green") %>%
add_annotations(x = ~x.start[change_dir == "down"],
y = ~y.start.new[change_dir == "down"],
xref = "x", yref = "y",
axref = "x", ayref = "y",
text = "",
showarrow = T,
ax = ~x.start[change_dir == "down"],
ay = ~y.end.new[change_dir == "down"],
arrowcolor = "red")
I am trying to animate this test data.frame but the plotly plot doesn't even show up! The same code works for original plotly data though. I have doublechecked column's class and they are the same as plotly example. I am now puzzled why this fails.
This also works in marker mode but not in lines mode as you see.
total <- data.frame(replicate(4,sample(0:1, 100, rep=TRUE)))
names(total) <- c("date", "frame", "P1.10", "year")
total$date <- as.numeric(as.character(t(rbind(runif(100, min=2000, max=2010)))))
f.rank <- order(total$date)
total$frame[f.rank] <- 1:nrow(total)
total$P1.10 <- as.numeric(as.character(t(rbind(runif(100, min=1, max=10)))))
total$year <- 2000
p <- total %>%
plot_ly(
x = ~date,
y = ~P1.10,
frame = ~frame,
type = 'scatter',
mode = 'lines',
line = list(simplyfy = F)
) %>%
layout(
xaxis = list(
title = "Date",
zeroline = F
),
yaxis = list(
title = "P1.10",
zeroline = F
)
) %>%
animation_opts(
frame = 100,
transition = 0,
redraw = FALSE
) %>%
animation_slider(
hide = T
) %>%
animation_button(
x = 1, xanchor = "right", y = 0, yanchor = "bottom"
)
You have ignored accumulate_by in the example. You also need an ID field. This is the same but using ggplot in combination.
set.seed(123)
library(plotly)
total <- data.frame(replicate(4,sample(0:1, 100, rep=TRUE)))
names(total) <- c("date", "frame", "P1.10", "year")
total$date <- as.numeric(as.character(t(rbind(runif(100, min=2000, max=2010)))))
f.rank <- order(total$date)
total$frame[f.rank] <- 1:nrow(total)
total$ID[f.rank] <- 1:nrow(total)
total$P1.10 <- as.numeric(as.character(t(rbind(runif(100, min=1, max=10)))))
total$year <- 2000
accumulate_by <- function(dat, var) {
var <- lazyeval::f_eval(var, dat)
lvls <- plotly:::getLevels(var)
dats <- lapply(seq_along(lvls), function(x) {
cbind(dat[var %in% lvls[seq(1, x)], ], frame = lvls[[x]])
})
dplyr::bind_rows(dats)
}
total <- total %>%
accumulate_by(~ID)
p <- ggplot(total,aes(ID, P1.10, frame = frame)) +
geom_line()
p <- ggplotly(p) %>%
layout(
title = "",
yaxis = list(
title = "P1.10",
zeroline = F,
tickprefix = "$"
),
xaxis = list(
title = "Date",
zeroline = F,
showgrid = F
)
) %>%
animation_opts(
frame = 100,
transition = 0,
redraw = FALSE
) %>%
animation_slider(
currentvalue = list(
prefix = "Day "
)
)
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