I have build a Gantt chart in plotly as per example.
I want to use trace filtering with plotly legend, but the code creates an enormous amount of traces which results in hundreds of traces in legend as well. To avoid that - I first use add_lines() on one row per each group with parameter showlegend = T and by specifying legendgroup. After that, I add the remaining lines to the figure with showlegend = F and by specifying the same legendgroup. This works great for the problem, but it messes the text flow inside hoverinfo. Namely, it groups and orders the text by legendgroup instead of allowing them to be ordered by xaxis value.
Data (an excerpt from Soderberg's Sex, Lies & Videotape :)):
library(plotly)
library(dplyr)
df_plotly = structure(list(tt = 1:9, speaker = c("A", "T", "A", "T", "A",
"T", "A", "T", "A"), min_time = c(42.328, 50.67, 53.297, 79.073,
87.54, 116.569, 120.948, 122.45, 131.959), max_time = c(50.5,
55.67, 81.573, 86.203, 112.938, 121.569, 125.948, 130.413, 136.959
), line = c("</br> Garbage.</br>All I've been thinking about all week is garbage.</br>I mean, I can't stop thinking about it.",
"</br> What kind of thoughts about garbage?", "</br> I just</br>I've gotten real concerned over what's gonna happen with all the garbage.</br>I mean, we've got so much of it.</br>You know? I mean, we have to run out of places to put this stuff eventually.</br>The last time I .</br>I started feelin' this way is when that barge was stranded</br>and, you know, it was going around the island and nobody would claim it.</br>Do you remember that?",
"</br> Yes, I remember.</br>Do you have any idea what may have triggered this concern?",
"</br> Yeah. Yeah.</br>You see, the other night, John was taking out the garbage,</br>and he kept spilling things out of the container,</br>and that made me</br>I started imagining, like,</br>a garbage can that produces garbage, and it doesn't stop.</br>It just keeps producing garbage, and it just keeps overflowing.</br>And, yy-you know, what would you do, you know, to try to stop something like that?",
"</br> Ann, do you see any pattern here?", "</br> What do you mean?",
"</br> Well, last week we were talking about your obsession</br>with the families of airline fatalities.</br>Now we're talking about your concern over the garbage problem.",
"</br> Yeah? So?"), color = structure(c(1L, 2L, 1L, 2L, 1L, 2L,
1L, 2L, 1L), .Label = c("#66C2A5", "#FC8D62"), class = "factor")), row.names = c(NA,
-9L), class = c("tbl_df", "tbl", "data.frame"))
Gantt without legend with correct order inside hoverinfo.
fig = plot_ly() %>% layout(hovermode = "x unified")
for(i in 1:(nrow(df_plotly) - 1)){
df =
df_plotly
fig <- add_lines(fig,
x = c(df$min_time[i], df$max_time[i]), # x0, x1
y = c(df$speaker[i], df$speaker[i]), # y0, y1
name = df$speaker[i],
mode = "lines",
line = list(color = df$color[i], width = 20),
hoverinfo = "text",
text = df$line[i],
evaluate = T # needed to avoid lazy loading
)
}
fig
Gantt with legend but with incorrect order inside hoverinfo:
fig = plot_ly() %>% layout(hovermode = "x unified")
for(i in 1:2){
df =
df_plotly %>%
group_by(speaker) %>%
filter(min_time == min(min_time)) %>%
ungroup()
fig <- add_lines(fig,
x = c(df$min_time[i], df$max_time[i]), # x0, x1
y = c(df$speaker[i], df$speaker[i]), # y0, y1
name = df$speaker[i],
mode = "lines",
line = list(color = df$color[i], width = 20),
legendgroup = df$speaker[i],
showlegend = T,
hoverinfo = "text",
text = df$line[i],
evaluate = T # needed to avoid lazy loading
)
}
for(i in 1:(nrow(df_plotly) - 1)){
df =
df_plotly %>%
group_by(speaker) %>%
filter(min_time != min(min_time)) %>%
ungroup()
fig <- add_lines(fig,
x = c(df$min_time[i], df$max_time[i]), # x0, x1
y = c(df$speaker[i], df$speaker[i]), # y0, y1
name = df$speaker[i],
mode = "lines",
line = list(color = df$color[i], width = 20),
legendgroup = df$speaker[i],
showlegend = F,
hoverinfo = "text",
text = df$line[i],
evaluate = T # needed to avoid lazy loading
)
}
fig
Thanks!
Related
mydat2 <- data.frame(subject = c("math", "english", "chemistry"), score = c(80, 50, 65), class = c("A", "B", "A"), count = c(50, 60, 70))
library(plotly)
plot_ly(data = mydat2,
x = ~score,
y = ~count,
color = ~class,
customdata= ~class,
hoverinfo = 'text',
text = ~subject,
hovertemplate = paste(
"<b>%{text}</b><br><br>",
"%{yaxis.title.text}: %{y:,.0f}<br>",
"%{xaxis.title.text}: %{x:,.0f}<br>",
"Class: %{customdata}",
"<extra></extra>"
))
I'm confused as to why the hover for the left most point shows up as %{text} instead of english. The hover labels for the other 2 points on the plot are perfectly fine.
Here is your graph, with the hover template that you're looking for. The explanation follows.
plot_ly(data = mydat2,
x = ~score,
y = ~count,
color = ~class,
type = "scatter",
mode = "markers",
customdata= ~class,
hoverinfo = 'text',
text = ~I(subject), # <---- I changed!!
hovertemplate = paste(
"<b>%{text}</b><br><br>",
"%{yaxis.title.text}: %{y:,.0f}<br>",
"%{xaxis.title.text}: %{x:,.0f}<br>",
"Class: %{customdata}",
"<extra></extra>"
))
I've seen this problem before. I've usually come up with a workaround, but never really figured out what went wrong until now. I found a closed ticket on GitHub for this issue (closed in 2019). (Ah, ya... so not fixed.) Apparently, it has to do with using color.
However, the bright spot...for a hot minute someone actually believed it was a problem and came up with a fix. #cpsievert wrote a bit of code that includes an lapply call (towards the middle of the page, if you visit the site). When I investigated what the code did, I realized it could be a lot simpler (and it was pretty simple, to begin with).
That ticket on GitHub is here if you wanted to check that out.
This is the code the maintainer provided (that you don't actually need).
l <- plotly_build(p)$x
l$data <- lapply(l$data, function(tr) { tr[["text"]] <- I(tr[["text"]]); tr })
as_widget(l)
The fix is the function I(). Instead of hovertext = or text = ~subject, you need hovertext = or text = ~I(subject). <-- note the I()
I want to plot a barchart timeseries with timestamps on the x-axis. My problem is the daylight-saving in Octobre, as there are 2 timestamps at 02:00, one with CEST timezone and one with CET.
The plotly-barchart superimposes these values, which is hard to spot and looks like an error when you hover over the barchart as you will find that the value does not correspond to the y-axis.
How can I display these 2 bars side by side, without changing the tickmode to "array" and defining tickvals/ticktext?
library(plotly)
df <- structure(list(value = round(runif(46, 20, 26), 3),
timestamp = structure(c(1603576800, 1603584000, 1603587600, 1603591200, 1603594800, 1603598400,
1603602000, 1603605600, 1603609200, 1603612800, 1603616400, 1603620000,
1603623600, 1603627200, 1603630800, 1603634400, 1603638000, 1603641600,
1603645200, 1603648800, 1603652400, 1603656000, 1603659600, 1603576800,
1603584000, 1603587600, 1603591200, 1603594800, 1603598400, 1603602000,
1603605600, 1603609200, 1603612800, 1603616400, 1603620000, 1603623600,
1603627200, 1603630800, 1603634400, 1603638000, 1603641600, 1603645200,
1603648800, 1603652400, 1603656000, 1603659600),
class = c("POSIXct", "POSIXt"), tzone = "Europe/Berlin"),
col = c(rep("#FFBA00", 23), rep("#B9CC2E", 23)),
name = c(rep("Grp1", 23), rep("Grp2", 23))),
row.names = 1:46, class = "data.frame")
plot_ly(data = df, text = "text") %>%
add_trace(x = ~timestamp, y = ~value, type = "bar",
marker = list(color = ~col),
text = ~sprintf("Time: %s<br>Value: %s", timestamp, value),
hoverinfo = "text",
name = ~name) %>%
plotly::layout(xaxis = list(title = 'Time', type = "date"),
barmode = 'group')
Wraping up my comments to an answer for future readers:
plotly currently doesn't support displaying daylight saving time natively:
Changes our internal date linearization to use UTC rather than local
milliseconds. Every day on a Plotly graph will now be 24 hours long,
with no daylight shifts.
source: https://github.com/plotly/plotly.js/pull/1194#issue-95087563
Accordingly, the only possibility I'm aware of, to work around this issue is using the tickvals and ticktext arguments in plotly's layout function (not desired by #SeGa and this is reasonable as we'll have duplicated tick labels. Nevertheless, maybe the following helps others).
library(plotly)
DF <- structure(list(value = round(runif(46, 20, 26), 3),
timestamp = structure(c(1603576800, 1603584000, 1603587600, 1603591200, 1603594800, 1603598400,
1603602000, 1603605600, 1603609200, 1603612800, 1603616400, 1603620000,
1603623600, 1603627200, 1603630800, 1603634400, 1603638000, 1603641600,
1603645200, 1603648800, 1603652400, 1603656000, 1603659600, 1603576800,
1603584000, 1603587600, 1603591200, 1603594800, 1603598400, 1603602000,
1603605600, 1603609200, 1603612800, 1603616400, 1603620000, 1603623600,
1603627200, 1603630800, 1603634400, 1603638000, 1603641600, 1603645200,
1603648800, 1603652400, 1603656000, 1603659600),
class = c("POSIXct", "POSIXt"), tzone = "Europe/Berlin"),
col = c(rep("#FFBA00", 23), rep("#B9CC2E", 23)),
name = c(rep("Grp1", 23), rep("Grp2", 23))),
row.names = 1:46, class = "data.frame")
# DF$timestamp_utc <- DF$timestamp
# attr(DF$timestamp_utc, "tzone") <- "UTC"
plot_ly(data = DF, text = "text") %>%
add_trace(x = ~as.numeric(timestamp), y = ~value, type = "bar",
marker = list(color = ~col),
text = ~sprintf("Time: %s<br>Value: %s", timestamp, value),
hoverinfo = "text",
name = ~name) %>%
plotly::layout(xaxis = list(title = 'Time',
ticktext = ~timestamp,
tickvals = ~as.numeric(timestamp),
tickmode = "array"),
barmode = 'group')
Minor edit: If we want to reduce the amount of ticklabels without a lot of effort we can use pretty in ticktext and tickvals:
ticktext = ~pretty(timestamp),
tickvals = ~as.numeric(pretty(timestamp))
Related docs: Formatting Ticks in R
I would like to plot different rows as different lines in the same plot to illustrate the movements of the average development of 3 groups: All, Men and Women. However, I'm not getting one of the lines printed and the legend is not being filled with the rownames.
I'l be glad for a solution, either in matplot or in ggplot.
Thank you!
Code:
matplot(t(Market_Work), type = 'l', xaxt = 'n', xlab = "Time Period", ylab = "Average", main ="Market Work")
legend("right", legend = seq_len(nrow(Market_Work)), fill=seq_len(nrow(Market_Work)))
axis(1, at = 1:6, colnames(Market_Work))
Data:
2003-2005 2006-2008 2009-2010 2011-2013 2014-2016 2017-2018
All 31.48489 32.53664 30.41938 30.53870 31.15550 31.77960
Men 37.38654 38.16698 35.10247 35.65543 36.54855 36.72496
Women 31.48489 32.53664 30.41938 30.53870 31.15550 31.77960
> dput(Market_Work)
structure(list(`2003-2005` = c(31.4848853173555, 37.3865421137,
31.4848853173555), `2006-2008` = c(32.5366433161048, 38.1669798351148,
32.5366433161048), `2009-2010` = c(30.4193794808191, 35.1024661973137,
30.4193794808191), `2011-2013` = c(30.5387012166381, 35.6554329405739,
30.5387012166381), `2014-2016` = c(31.1555032381292, 36.5485451138792,
31.1555032381292), `2017-2018` = c(31.7795953402235, 36.7249638612854,
31.7795953402235)), row.names = c("All", "Men", "Women"), class = "data.frame")
Here is an example with ggplot2. I changed some of your data, as two rows were same in your originial data.
library(tidyverse)
df <- structure(list(`2003-2005` = c(31.4848853173555, 37.3865421137,
30.4848853173555), `2006-2008` = c(32.5366433161048, 38.1669798351148,
30.5366433161048), `2009-2010` = c(30.4193794808191, 35.1024661973137,
33.4193794808191), `2011-2013` = c(30.5387012166381, 35.6554329405739,
33.5387012166381), `2014-2016` = c(31.1555032381292, 36.5485451138792,
30.1555032381292), `2017-2018` = c(31.7795953402235, 36.7249638612854,
30.7795953402235)), row.names = c("All", "Men", "Women"), class = "data.frame")
df2 <- as.data.frame(t(df))
df2$Year <- rownames(df2)
df2%>% pivot_longer( c(All,Men,Women), names_to = "Category") %>%
ggplot(aes(x = Year, y = value)) + geom_line(aes(group = Category, color = Category))
I am creating bar charts in plotly with y-axis representing percentages or shares within 0-1. The y-axis displays as 0.05 instead of 5.0%.
Is there a way to display y-axis tick labels as %##?
I have tried using tickformat = "%" but that doesn't seem to be working.
You can do this in plotly using layout:
p <- p %>%
layout(yaxis = list(tickformat = "%"))
Or if you want to only add % and do not reformat the numbers then:
p <- p %>%
layout(yaxis = list(ticksuffix = "%"))
Example:
This is an example that shows how to edit the y axis ticks as you wish (find the data used in the graph below).
I have this code and the following graph:
plot_ly(z = eegmean$value, x = eegmean$xproj, y= eegmean$yproj,
type = "contour") %>%
layout(yaxis = list(range = c(0, 1)))
Then I modify as below which gives me desired output:
plot_ly(z = eegmean$value, x = eegmean$xproj, y= eegmean$yproj*100,
type = "contour") %>%
layout(yaxis = list(ticksuffix = "%", range = c(0, 100)))
Data:
eegmean <-
structure(list(xproj = c(-4.36892281283989, 4.35956894475236,
-3.66712823067503, 3.66912002532953, -6.74087785458615, 6.7287326256584,
-3.06883681930631, 3.0727815538517, -3.05334720378955, 3.0570879596344,
-3.79278629306119, 3.79086730312228, -7.07653326595358, 7.06235689946147,
-7.90472265899708, 7.886291820964), yproj = c(0.0590663494057822,
0.0624572214558794, 4.86096691858553, 4.85057791325599, 5.19791938823655,
5.18984777332146, 9.40308855517187, 9.39510236056629, -9.35605694441838,
-9.34632728162916, -4.81178659276704, -4.80386416586077, -5.3889955653921,
-5.37981449730605, -0.00583969391994209, -0.00704057111565196
), value = c(0.0606980290462218, 0.0608382874925463, 0.0517195368020531,
0.0531772440361526, 0.0204264049886253, 0.0177325467223879, 0.0392064861131087,
0.0425640060844722, 0.0788962178010734, 0.0740093285228833, 0.0749098131481143,
0.0759725415557911, 0.0688015959610801, 0.0762816652838652, 0.0548817124454006,
0.0646901969995537)), .Names = c("xproj", "yproj", "value"), row.names = c("C3",
"C4", "F3", "F4", "F7", "F8", "FP1", "FP2", "O1", "O2", "P3",
"P4", "P7", "P8", "T7", "T8"), class = "data.frame")
The plotly documentation directs us to this page which has a comprehensive list of available formatting options.
In this case, formatting percentages like 5%, tickformat='%' should suffice. If you want to display decimal points too, then something like '.n%' would do the trick (replace n with desired number of digits after decimal point).
The github page referenced above conatins many more formatting options. Cheers!
I have a data frame like this.
ID read1 read2 read3 read4 class
1 5820350 0.3791915 0.3747022 0.3729779 0.3724259 1
2 5820364 0.3758676 0.3711775 0.3695976 0.3693112 2
3 5820378 0.3885081 0.3823900 0.3804273 0.3797707 2
4 5820392 0.3779945 0.3729582 0.3714910 0.3709072 1
5 5820425 0.2954782 0.2971604 0.2973882 0.2973216 3
6 5820426 0.3376101 0.3368173 0.3360203 0.3359517 3
Each row represents one sample with four values,and the last column is the classification of this sample. I want to visualize each sample curve and set the class as the color.
I tried to reshape the data frame, but I then lost the class feature which I need.
Could you please give me some hint or show me how to do that in R?
Thanks in advance.
You are going to want to tidy your data first (shown below with tidyr::gather). Then, when you plot, you will want to set your group = ID and color = factor(class) (for discrete colors):
library(tidyr)
library(ggplot2)
df <- structure(list(ID = c(5820350L, 5820364L, 5820378L, 5820392L, 5820425L, 5820426L),
read1 = c(0.3791915, 0.3758676, 0.3885081, 0.3779945, 0.2954782, 0.3376101),
read2 = c(0.3747022, 0.3711775, 0.38239, 0.3729582, 0.2971604, 0.3368173),
read3 = c(0.3729779, 0.3695976, 0.3804273, 0.371491, 0.2973882, 0.3360203),
read4 = c(0.3724259, 0.3693112, 0.3797707, 0.3709072, 0.2973216, 0.3359517),
class = c(1L, 2L, 2L, 1L, 3L, 3L)),
.Names = c("ID", "read1", "read2", "read3", "read4", "class"),
class = "data.frame", row.names = c("1", "2", "3", "4", "5", "6"))
df <- gather(df, reading, value, -c(ID, class))
ggplot(df, aes(x = reading, y = value, color = factor(class))) +
geom_line(aes(group = ID))
Here's a function that may do what you want:
PlotMultiCurve = function(x, classes, cols = NULL, colSet = "Set1", ...) {
if(!is.factor(classes)) classes = as.factor(classes)
nClasses = length(levels(classes))
if(is.null(cols)) cols = brewer.pal(nClasses, colSet)
plot(1:ncol(x), x[1,], col = cols[classes[1]], type = "l",
ylim = range(x), xaxt = "n", ...)
axis(1, 1:ncol(x), 1:ncol(x))
for(i in 2:nrow(x)) {
par(new = T)
plot(1:ncol(x), x[i,], col = cols[classes[i]], type = "l",
ylim = range(x), axes = F, xlab = "", ylab = "")
}
}
It uses chooses colors automatically from the RColorBrewer package unless you provide the colors. I copied your data directly into a text file and then ran the following:
# Prepare data
require(RColorBrewer)
myData = read.table("Data.2016-05-03.txt")
x = myData[,2:5]
classes = as.factor(myData$class)
# Plot into PNG file[![enter image description here][1]][1]
png("Plot.2016-05-03.png", width = 1000, height = 1000, res = 300)
par(cex = 0.8)
PlotMultiCurve(x = x, classes = classes, xlab = "Read", ylab = "Response")
dev.off()