The scale does not change the way I want and the way I wrote in the code. Why is that? Can someone help? My goal is to have a tick and label for every year on the x axis and a scale from -6 to 3 on the y axis, also including ticks and labels.
Periode <- as.Date(website$Datum)
Index <- website$KonSens
datebreaks <- seq(as.Date("1998-03-31"), as.Date("2022-06-30"), by = "1 month")
plot.Website <-
ggplot(data = website, aes(x = Periode, y = Index)) +
scale_x_date(breaks = datebreaks) +
scale_y_continuous(breaks = seq(-6,3,1), labels = c("-6", "-5", "-4", "-3", "-2", "-1", "0", "1", "2", "3"), limits = c(-6,4), n.breaks = 10)+
theme(axis.text = element_text(color = "black", face = "bold", size = 10),
axis.line.y = element_line(color = "black", size = 2),
axis.line.x = element_blank(),
axis.title = element_text(color = "black", face = "bold"),
axis.title.y = element_text(angle = 180)) +
geom_line(aes(y = Index), color = "#7AA489",size = 1.5) +
geom_point(shape = 21, color = "#003478", fill = "#7AA489", size = 2.5, stroke = 1)
ggplotly(plot.Website, dynamicTicks = TRUE) %>%
rangeslider(borderwidth = 1)
Plotly may ignore you regardless of what you do because it looks like you are asking for almost 300 tick labels on the x-axis. However, when you use ggplotly, date fields become character fields. (I don't know if this is from ggplot or if it's a Plotly-ism.)
After rendering the ggplotly object, you can replace the x in each trace and replace the majority of the arguments for layout.xaxis. Alternatively, you may find it to be a lot easier if you create this in Plotly, to begin with.
I've added the data I used because your question is not reproducible. It looks like you're new to SO; welcome to the community! If you want great answers quickly, it's best to make all questions reproducible. For example, sample data from the output of dput() or reprex::reprex(). Check it out: making R reproducible questions.
In the following code, the only parts you need to create the plot without the aesthetics are the first two piped: plot_ly() and add_lines().
I added comments to explain what different aspects of this code is doing. Additionally, I've broken down some of this a bit further after the code and plot image.
library(tidyverse)
library(plotly)
# Periode <- as.Date(website$Datum) # data not provided
# Index <- website$KonSens
set.seed(353)
website <- data.frame(
Periode = sample(seq(as.Date("1998-03-31"), as.Date("2022-06-30"), by = "week"), 50),
Index = sample(runif(100, -4.5, 2.5), 50)) %>%
arrange(Periode)
plot_ly(type = "scatter", mode = "markers",
marker = list(color = "#7AA489", size = 8,
line = list(width = 2, color = "#003478")),
data = website, x = ~Periode, y = ~Index) %>%
add_lines(line = list(width = 5, color = "#7AA489")) %>%
layout(xaxis = list(dtick = "M18", # every 18 months
tickformat = "%b %Y"), # Abbrev Month, 4-digit year
yaxis = list(range = c(-6, 3), # show this range
zeroline = F, # remove horizontal line at 0
showline = T), # vertical line along y-axis
showlegend = F) %>% # no legend
htmlwidgets::onRender("function(el, x){. # make the axis titles BOLD
gimmeX = document.querySelector('g.infolayer g.g-xtitle text');
gimmeY = document.querySelector('g.infolayer g.g-ytitle text');
gimmeX.style.fontWeight = 'bold';
gimmeY.style.fontWeight = 'bold';
}") %>%
rangeslider(borderwidth = 1)
Specifying the Range on the y-axis
In Plotly, you won't need to set breaks and labels. You just need to define the range. If that's all you designated in the layout, it would like something like this.
layout(yaxis = list(range = c(-6, 3))
Non-Dynamic Date x-axis Tick Text Formatting
Labels on the x-axis can be specified to be labeled by month. However, like a commenter mentioned, you loose the dynamic date scaling done when you zoom. You can actually set the scaling date format for zooming ranges, as well. (I won't go into that in this answer, though.)
To specify the labels on the x-axis as every month by month, you would use layout.xaxis.dtick = "M1", every 18 months is M18, and so on. If you wanted to specify the appearance as the month and year, you can use the same formatting as used in as.Date, where %b is an abbreviated month name, and %Y is a four-digit year. If this was all you needed to set in the layout, it would look like this.
layout(xaxis = list(dtick = "M18", # every 18 months
tickformat = "%b %Y")) # Abbrev Month, 4-digit year
Bold Axis Labels
Additionally, there isn't a parameter you can set to make the axis labels bold. It doesn't really matter if it's ggplotly or plot_ly.
However, you can use htmlwidgets::onRender to make this happen.
htmlwidgets::onRender("function(el, x){
gimmeX = document.querySelector('g.infolayer g.g-xtitle text');
gimmeY = document.querySelector('g.infolayer g.g-ytitle text');
gimmeX.style.fontWeight = 'bold';
gimmeY.style.fontWeight = 'bold';
}")
Related
I am trying to make my beautiful ggplot map interactive with a tooltip using ggplotly. But the map rendered with ggploty is not beautiful.
Here is a picture of my map with only ggplot:
Here is a picture of my map when using ggplotly. It removes the legend and make the map ugly:
Is there another way of making my ggplot map interactive with a tooltip? And also ggplotly takes some time to render the interactive map:
Here is my sample code for my ggplot:
ggplot(data = sdpf_f, aes( fill = n,x = long, y = lat, group = group, text = tooltip)) +
geom_polygon(color = "white") +
theme_void() +
scale_fill_continuous(low="#c3ffff", high="#0291da",
guide = guide_legend(title.position = "top", label.position = "bottom", keywidth = 2,
keyheight = 0.5,
title = "Number of agreements"),na.value="lightgrey"
) +
theme(legend.position="bottom") +
coord_map()
Thanks & kind regards,
Akshay
I don't have your data and this isn't exactly the same, but it's fairly close to what I think you're expecting.
The libraries:
I called tidyverse for the plotting and piping. I called maps for the data I used and plotly for the Plotly graph.
I used a function that is derived from one of the ways ggplot sets the aspect ratio. I know I found this function on SO, but I don't remember who wrote it.
library(tidyverse)
library(maps)
library(plotly)
map_aspect = function(x, y) {
x.center <- sum(range(x)) / 2
y.center <- sum(range(y)) / 2
x.dist <- ggplot2:::dist_central_angle(x.center + c(-0.5, 0.5), rep(y.center, 2))
y.dist <- ggplot2:::dist_central_angle(rep(x.center, 2), y.center + c(-0.5, 0.5))
y.dist / x.dist
}
I had to create data as your question is not reproducible. I figured I would include it, so that my answer was reproducible.
ms <- map_data("state") %>%
mutate(n = ifelse(str_detect(region, "^a"), 1.0,
ifelse(str_detect(region, "^o"), 1.5,
ifelse(str_detect(region, "^t"), 2.0,
ifelse(str_detect(region, "^s"), 2.5,
ifelse(str_detect(region, "^w"),
3.0,
NA))))))
I modified your ggplot call. The only change is coord_fixed instead of coord_map. I did this so that Plotly could interpret the aspect ratio correctly. Within coord_fixed, I used the function map_aspect.
gp <- ggplot(data = ms, aes(fill = n, x = long, y = lat,
group = group, text = tooltip)) +
geom_polygon(color = "white") +
theme_void() +
scale_fill_continuous(low="#c3ffff", high="#0291da",
guide = guide_legend(title.position = "top",
label.position = "bottom",
keywidth = 2,
keyheight = 0.5,
title = "Number of agreements"),
na.value="lightgrey"
) +
theme(legend.position="bottom") +
coord_fixed(with(ms, map_aspect(long, lat)))
Then I created a Plotly object. I set some requirements for the layout, as well (horizontal legend at the bottom, with the legend title above the elements—similar to the legend in your ggplot call).
pp <- ggplotly(gp) %>%
layout(legend = list(orientation = "h", valign = "bottom",
title = list(side = "top"),
x = .02),
xaxis = list(showgrid = F), yaxis = list(showgrid = F),
showlegend = T)
Next, I needed to add the information for the legend. I chose to name the traces (which are split by color). I started by creating a vector of the names of the traces (which is what you see in the legend). I added a "" at the end, so that the NA-colored states wouldn't have a named trace (so they won't show in the legend).
This is likely something you'll need to change for your data.**
# the color values and the last one, which is for NAs, no legend
nm <- seq(1, 3, by = .5) %>% sprintf(fmt = "%.1f") %>% append(., "")
Then I wanted to ensure that showlegend was true for all but the NA (light gray) states. I created a vector of T and F.
legs <- c(rep(TRUE, 5), FALSE)
Then I added these to the plot.
invisible(lapply(1:length(pp$x$data),
function(i){
pp$x$data[[i]]$name <<- nm[i]
pp$x$data[[i]]$showlegend <<- legs[i]
}))
Context & problem:
I am trying to show the evolution of a value over time and some events that occured during the same period. One x-axis shows dates and I would like to get another x-axis, on top of the plot, that shows alternative tick-labels for dates i.e. events.
Here is a ggplot version of this plot:
library(ggplot2)
# here is a dummy dataset
df <- data.frame(
timeaxis = seq.Date(from = as.Date.character("2020-01-01"),
to = as.Date.character("2020-02-01"),
by = "days"),
avalue = runif(32)
)
# now I want to add a secondary axis to show events that occured during this time period
df_event <- data.frame(
eventdate = as.Date.character(c("2020-01-05", "2020-01-17", "2020-01-20", "2020-01-25")),
eventlabel = c("diner", "exam", "meeting", "payday")
)
# and the basic plot related to it
p <- ggplot(data = df, aes(x = timeaxis, y = avalue)) +
geom_line()
# I can add a new axis like so:
p <- p + scale_x_date(sec.axis = sec_axis(
~ .,
breaks = df_event$eventdate,
labels = df_event$eventlabel
))
print(p)
Created on 2022-02-11 by the reprex package (v2.0.1)
I need to use plotly for the awesome rangeslider that adds a very nice to use way to zoom in specific time periods.
Demo:
library(dplyr)
library(plotly)
plotly::ggplotly(p) %>%
plotly::layout(
# as you can see the rangeslider is linked to xaxis, is it possible to link it to xaxis2?
xaxis = list(rangeslider = list(visible = TRUE))
)
# note that using rangeslider() does not show the plot right...
As you can see, the second x-axis is not showing. Problem is that plotly does not have, yet, a good handling of secondary axis. First, if you use ggplot2::scale_x_date(sec.axis = ggplot2::sec_axis() to get a secondary x-axis, it is not transfered to the plotly plot by using plotly::ggplotly(). Second, if you manually define a secondary x-axis using plotly functions (as proposed here or there, this axis does not change according to the rangeslider (I guess because the rangeslider is actually bound to xaxis and should de defined for xaxis2 as well). Not to mention, I was not able to change the labels of the breaks to get the "events" displayed (even the example on
Demo:
plotly::ggplotly(p) %>%
# making an invisible trace
plotly::add_lines(
data = df_event,
x = ~ eventdate,
y = 0,
color = I("transparent"),
hoverinfo = "skip",
showlegend = FALSE,
xaxis = "x2"
) %>%
plotly::layout(
xaxis = list(rangeslider = list(visible = TRUE)),
xaxis2 = list(overlaying = "x", side = "top")
)
And with the actual event labels:
plotly::ggplotly(p) %>%
# making an invisible trace
plotly::add_lines(
data = df_event,
x = ~ eventdate,
y = 0,
color = I("transparent"),
hoverinfo = "skip",
showlegend = FALSE,
xaxis = "x2"
) %>%
plotly::layout(
xaxis = list(rangeslider = list(visible = TRUE)),
xaxis2 = list(overlaying = "x",
side = "top",
tickvals = df_event$eventdate,
ticktext = df_event$eventlabel)
)
I tried to add matches = "x", anchor = "x", scaleanchor = "x" to xaxis2 list but nothing changed.
Question:
How to make a second axis in a plotly plot that reacts to "zoom" using rangeslider?
If you think there is a better way of achieving this, please go ahead! Any idea is very welcome, I am quite new to plotly and I certainly have overlooked its functionalities.
I am using the R package GGPubr to make Boxplots. I really like the nice visuals that it provides but am having problems. Does anyone know how to increase the font size of the numbers on the axes, and the axis labels, and class labels? Also how do I set the mean values so that they only display 2 decimal places?
Here is the code that I'm using:
library("ggpubr")
mydata <- read.csv("C:\\temp\\ndvi.csv")
ggboxplot(mydata, x = "class", y = "NDVI",
color = "class",
order = c("Conifer", "Deciduous", "Grasslands"), ggtheme=theme_gray(),
ylab = "NDVI Value", xlab = "Land Cover Class",
add="mean",
font.label = list(size = 30, face = "bold"))+ stat_summary(fun.data
= function(x) data.frame(y=1, label = paste("Mean=",mean(x))), geom="text")
+theme(legend.position="none")
And the csv:
NDVI,class
0.25,Conifer
0.27,Conifer
0.29,Conifer
0.403,Deciduous
0.38,Deciduous
0.365,Deciduous
0.31983489,Grasslands
0.32005,Grasslands
0.328887766,Grasslands
I would prefer to achieve the desired effects above with GGPubr rather than boxplot() or ggplot/ggplot 2. Thanks.
Here is one option where we use round() to take care of the two decimal places and add another theme() to change the text size.
ggboxplot(mydata, x = "class", y = "NDVI",
color = "class",
order = c("Conifer", "Deciduous", "Grasslands"), ggtheme=theme_gray(),
ylab = "NDVI Value", xlab = "Land Cover Class",
add="mean",
font.label = list(size = 30, face = "bold")) +
# use round() and set y = .45
stat_summary(fun.data = function(x) data.frame(y=1, label = paste("Mean=", round(mean(x), 2))), geom="text") +
theme(legend.position="none") +
theme(text = element_text(size = 16)) # change text size of theme components
I have just started using plotly in R and it is working great but I cannot figure out how to do two things.
1) I need to pick the colors of my split. Currently, I am splitting by Territory and plotly doesn't allow me to code what colors I want each territory to be.
2) I also need to scale the points so that some markers are very large. I tried creating a size for each row and setting size = ~size and sizes = c(2, 100) but this did not work.
Any advice on how to do this? I've tried reading the plotly R reference guide but cannot figure out how to do this with plotly_mapbox. I've pasted my code without the size or color attempt because I could never get it to work.
p <- df %>%
plot_mapbox(lat = ~lat, lon = ~lon,
split = ~Territory,
mode = 'scattermapbox',
text = df$text,
hoverinfo = "text"
) %>%
layout(title = 'Ship to Zip Codes',
font = list(color='white'),
plot_bgcolor = '#191A1A',
paper_bgcolor = '#191A1A',
mapbox = list(style = 'dark'),
legend = list(orientation = 'h',
font = list(size = 8)),
margin = list(l = 25, r = 25,
b = 25, t = 25,
pad = 2))
You can set the marker size via marker = list(size = 2).
Setting the colors is more tricky and cannot be done directly with plot_mapbox as far as I know.
But we can assign a new column to our data frame
df$colors <- factor(df$class, levels = unique(df$class))
then define our own color list
cols <- c("red", "blue", "black", "green", "orange", "cyan", "gray50")
and finally plot everything via plot_geo
plot_geo(df) %>%
add_markers(
x = ~reclong, y = ~reclat, color = ~colors, colors = cols, marker = list(size = 2))
The whole code to get the custom colors in a scatter map in Plotly.
library(plotly)
df = read.csv('https://raw.githubusercontent.com/bcdunbar/datasets/master/meteorites_subset.csv')
df$colors <- factor(df$class, levels = unique(df$class))
cols <- c("red", "blue", "black", "green", "orange", "cyan", "gray50")
plot_geo(df) %>%
add_markers(x = ~reclong,
y = ~reclat,
color = ~df$colors,
colors = cols,
marker = list(size = 2))
Is there a way to change the legend size in plotly for R? I have not come across this option. I have looked at the docs on legends, https://plot.ly/r/legend/, but it does not mention this.
Use layout(legend = list(font = list(size(30)))):
plot_ly(data = mtcars, x = as.character(mtcars$cyl),
y = mtcars$mpg, type = "box", color = as.character(mtcars$cyl)) %>%
layout(showlegend = TRUE, legend = list(font = list(size = 30)))
While working with legends, there are two pieces to configure.
Legend title (legend_title parameter)
Actual legend (legend parameter)
Let's say you already have a fig(it can be mostly any plot), to that add below line with parameters as you wish, in separate dictionaries.
fig.update_layout(legend = dict(font = dict(family = "Courier", size = 50, color = "black")),
legend_title = dict(font = dict(family = "Courier", size = 30, color = "blue")))
This way you will have more control over two thing.