Plotly - Plot 2 Y Axes With Time Series - r

I am trying to use r plotly to plot a chart that has following features:
Date objects as X-variable
2 line plots in one charts with 2 Y-axis: one on the left, the other on the right
Date Amount1 Amount2
2/1/2017 19251130 21698.94
2/2/2017 26429396 10687.37
2/5/2017 669252 0.00
2/6/2017 25944054 11885.10
2/7/2017 27895562 14570.39
2/8/2017 20842279 20080.56
2/9/2017 25485527 9570.51
2/10/2017 17008478 14847.49
2/11/2017 172562 0.00
2/12/2017 379397 900.00
2/13/2017 25362794 18390.80
2/14/2017 26740881 11490.94
2/15/2017 20539413 22358.26
2/16/2017 22589808 12450.45
2/17/2017 18290862 3023.45
2/19/2017 1047087 775.00
2/20/2017 4159070 4100.00
2/21/2017 28488401 22750.35
and the code I use is:
ay <- list(
#tickfont = list(color = "red"),
overlaying = "y",
side = "right"
)
p <- plot_ly() %>%
add_lines(x = df$Date, y = df$Amount1, name = "Amount1",type = "scatter", mode = "lines") %>%
add_lines(x = df$Date, y = df$Amount2, name = "Amount2", yaxis = "y2",type = "scatter", mode = "lines") %>%
layout(
title = "Chart Summary", yaxis2 = ay,
xaxis = list(title="Date")
)
The output chart looks fine but the date intervals on the X-axis is looking bad. I am wondering what is the solution to this, and if I want to have 2 histograms in one chart using the data above, what is the optimal way to do it?
Thank you for help!

Is your Date column a string or date?
If it is a string, convert it to date and let Plotly take care of it.
df$Date <- as.Date(df$Date , "%m/%d/%Y")
Full code
library('plotly')
txt <- "Date Amount1 Amount2
2/1/2017 19251130 21698.94
2/2/2017 26429396 10687.37
2/5/2017 669252 0
2/6/2017 25944054 11885.1
2/7/2017 27895562 14570.39
2/8/2017 20842279 20080.56
2/9/2017 25485527 9570.51
2/10/2017 17008478 14847.49
2/11/2017 172562 0
2/12/2017 379397 900
2/13/2017 25362794 18390.8
2/14/2017 26740881 11490.94
2/15/2017 20539413 22358.26
2/16/2017 22589808 12450.45
2/17/2017 18290862 3023.45
2/19/2017 1047087 775
2/20/2017 4159070 4100
2/21/2017 28488401 22750.35"
df$Date <- as.Date(df$Date , "%m/%d/%Y")
ay <- list(
#tickfont = list(color = "red"),
overlaying = "y",
side = "right"
)
p <- plot_ly() %>%
add_lines(x = df$Date, y = df$Amount1, name = "Amount1",type = "scatter", mode = "lines") %>%
add_lines(x = df$Date, y = df$Amount2, name = "Amount2", yaxis = "y2",type = "scatter", mode = "lines") %>%
layout(
title = "Chart Summary", yaxis2 = ay,
xaxis = list(title="Date", ticks=df$Date)
)
p

Related

Cannot display the text in plotly with R if there is only one data point

The code blow generates a plotly graph with one data point. I design this plot to be able to display some text information when the users move the mouse cursor to the data point, but as the plot shows, this does not work.
library(dplyr)
library(lubridate)
library(plotly)
a1 <- data.frame(
DateTime = ymd_hms("2020-01-01 08:00:00"),
Value = 1
)
a1 <- a1 %>%
mutate(DateTimeText = as.character(DateTime))
p1 <- plot_ly(a1, x = ~DateTime, y = ~Value, type = "scatter", mode = "markers",
text = ~DateTimeText,
hovertemplate = paste(
"<br>Date Time: %{text} </br>",
"<br>Value: %{y} </br>",
"<extra></extra>"))
However, if I provided two data points. The code works. Here is an example. This is strange to me as I think both cases should work. Please give some advice.
a2 <- data.frame(
DateTime = ymd_hms(c("2020-01-01 08:00:00", "2020-01-02 08:00:00")),
Value = c(1, 2)
)
a2 <- a2 %>%
mutate(DateTimeText = as.character(DateTime))
p2 <- plot_ly(a2, x = ~DateTime, y = ~Value, type = "scatter", mode = "markers",
text = ~DateTimeText,
hovertemplate = paste(
"<br>Date Time: %{text} </br>",
"<br>Value: %{y} </br>",
"<extra></extra>"))
The issue is that your length 1 vector in R is not properly converted to a JSON array of length 1. This a known pitfall as there is some ambiguity when converting R objects to JSON, see https://plotly-r.com/json.html. This ambiguity does not arise when you have a vector of length > 1. That's why you code works in such cases.
To solve this make use of the asIs function or I, i.e. use text = ~I(DateTimeText). Try this:
library(dplyr)
library(lubridate)
library(plotly)
a1 <- data.frame(
DateTime = ymd_hms("2020-01-01 08:00:00"),
Value = 1
)
a1 <- a1 %>%
mutate(DateTimeText = as.character(DateTime))
p1 <- plot_ly(a1, x = ~DateTime, y = ~Value, type = "scatter", mode = "markers",
text = ~I(DateTimeText),
hovertemplate = paste(
"<br>Date Time: %{text} </br>",
"<br>Value: %{y} </br>",
"<extra></extra>"))
p1

r plotly chart based on multiple columns [duplicate]

This question already has answers here:
Format axis tick labels to percentage in plotly
(2 answers)
Closed 2 years ago.
I have a df which can have 2 or more columns with the first one month always fixed.I am trying to plot them using plotly r. As of now it has three columns: month,apple,orange. Based on analysis it can have another column banana. Below is the code I am using right now but it even takes the column month for y-axis. How do I fix this:
> sample_test
month apple orange
2 Aug-17 2 1
3 Dec-17 2 1
4 Feb-18 2 1
5 Jan-18 2 1
6 Jul-17 2 1
7 Jun-17 2 1
8 May-17 2 1
9 Nov-17 2 1
10 Oct-17 2 1
11 Sep-17 2 1
p<- plot_ly(sample_test, x = sample_test$month, name = 'alpha', type = 'scatter', mode = 'lines',
line = list(color = 'rgb(24, 205, 12)', width = 4)) %>%
layout(#title = "abbb",
xaxis = list(title = "Time"),
yaxis = list (title = "Percentage"))
for(trace in colnames(sample_test)){
p <- p %>% plotly::add_trace(y = as.formula(paste0("~`", trace, "`")), name = trace)
}
p
The output looks like this :
Does this help?
sample_test <- read.table(
text = ' month apple orange
2 Aug-17 2 1
3 Dec-17 2 1
4 Feb-18 2 1
5 Jan-18 2 1
6 Jul-17 2 1
7 Jun-17 2 1
8 May-17 2 1
9 Nov-17 2 1
10 Oct-17 2 1
11 Sep-17 2 1'
)
sample_test$month <- as.Date(paste('01', sample_test$month, sep = '-'), format = '%d-%b-%y')
library(plotly)
p <- plot_ly(sample_test, type = 'scatter', mode = 'lines',
line = list(color = 'rgb(24, 205, 12)', width = 4)) %>%
layout(#title = "abbb",
xaxis = list(title = "Time"),
yaxis = list (title = "Percentage", tickformat = '%'))
for(trace in colnames(sample_test)[2:ncol(sample_test)]){
p <- p %>% plotly::add_trace(x = sample_test[['month']], y = sample_test[[trace]], name = trace)
}
p
There are couple of things to note here -
While dealing with dates, it's best to format them as dates. This can save a lot of headache later on. It is also useful as most if not all functions that require dealing with dates have methods built to handle them.
While adding traces in a for loop, always reference the vector to be plotted explicitly like data$vector or data[['vector']] and not like y = ~vector, because plotly for some reason ends up plotting just one trace over and over again.
You can specify a trace for the first y element, which will give you your raw counts. Next you can add a format for your y-axis using tickformat, which will convert to percentages.
sample_test <- data.frame(month = c("Aug-17", "Dec-17", "Feb-18"), apple = c(2,2,2), orange = c(1,1,1))
p <- plot_ly(sample_test, x = sample_test$month, y = ~apple, name = 'alpha', type = 'scatter', mode = 'lines',
line = list(color = 'rgb(24, 205, 12)', width = 4)) %>%
layout(xaxis = list(title = "Time")) %>%
layout(yaxis = list(tickformat = "%", title = "Percentage"))
Although for some reason this appears to just multiply by 100 and add a % label for some reason, rather than actually calculate a percentage. From this SO answer, looks like that's all it does. I don't really use plotly, but in ggplot you can do this if you reshape your data to long and map your categorical variable (in this case fruit) as a percent.
Edit: per OP's comment, removed month from being traced.
p <- plot_ly(type = 'scatter', mode = 'lines') %>%
layout(yaxis = list(tickformat = "%", title = "Percentage"))
colNames <- names(sample_test)
colNames <- colNames[-which(colNames == 'month')]
for(trace in colNames){
p <- p %>% plotly::add_trace(data = sample_test, x = ~ month, y = as.formula(paste0("~`", trace, "`")), name = trace)
print(paste0("~`", trace, "`"))
}
p

Plot inside of hoverinfo in Plotly

I've a plotly bar chart with a hover event which plots another plot alongside the first one
Is there a way to have the second plot show up inside of the hoverinfo box ?
The code I used is as under:
UI
library(shiny)
library(plotly)
library(shinythemes)
library(dplyr)
library(png)
ui <- fluidPage(
theme = shinytheme("spacelab"),
h2("Coupled hover-events in plotly charts using Shiny"),
tags$hr(),
fluidRow(
column(6, plotlyOutput(outputId = "ageplot", height = "600px")),
column(6, plotlyOutput(outputId = "raceplot", height = "600px"))),
tags$hr(),
tags$blockquote("Hover over age plot for race and gender information")
)
SERVER:
server <- function(input, output){
patdata <- read.csv("Sal.csv")
boston_race<-read.csv("bostonrace.csv")
patdata$Race<-ifelse(patdata$Race=="RACE_UNKNOWN", "Unknown",ifelse(patdata$Race=="BLACK_AFRICAN_AMERICAN", "African American",ifelse(patdata$Race=="RACE_LATINO_HISPANIC", "Latino Hispanic",
ifelse(patdata$Race=="WHITE", "White",ifelse(patdata$Race=="ASIAN", "Asian",
ifelse(patdata$Race=="RACE_OTHER", "Other","Unknown"))))))
patdata$Date<-as.Date(patdata$CreateDate, format = "%m/%d/%Y")
patdata$agegroup<- ifelse(patdata$Age>=0 &patdata$Age<=19,"<20",
ifelse(patdata$Age>=20 &patdata$Age<=29,"20-29",
ifelse(patdata$Age>=30 &patdata$Age<=39,"30-39",
ifelse(patdata$Age>=40 &patdata$Age<=49,"40-49",
ifelse(patdata$Age>=50,"50+","Invalid Age")))))
patdata$dp<- ifelse(patdata$Age>=0 &patdata$Age<=19,0,
ifelse(patdata$Age>=20 &patdata$Age<=29,1,
ifelse(patdata$Age>=30 &patdata$Age<=39,2,
ifelse(patdata$Age>=40 &patdata$Age<=49,3,
ifelse(patdata$Age>=50,4,NA)))))
patdata$dp<-as.numeric(patdata$dp)
patdata_age<- subset(patdata, select="agegroup")
patdata_age<-as.data.frame(table(patdata_age))
selection<-patdata_age
output$ageplot <- renderPlotly({
colnames(selection)<-c("agegroup","Freq")
selection$y<-round((patdata_age$Freq*100/sum(patdata_age$Freq)))
plot_ly(source = "source",selection, x = ~agegroup, y = selection$y, type = 'bar',
marker = list(color = 'rgb(255,140,0)',
# marker = list(color,alpha = d),
line = list(color = 'rgb(8,48,107)', width = 1.5))) %>%
layout(title = paste0("Age-group distribution of patients "),xaxis = list(title = 'age group'),
yaxis = list(title = paste0('Percentage of Patients')),titlefont=list(size=13),
annotations = list(x = ~agegroup, y = selection$y, text = paste0(selection$y, "%"),
xanchor = 'center', yanchor = 'bottom',
showarrow = FALSE))
})
output$raceplot <- renderPlotly({
eventdata <- event_data("plotly_hover", source = "source")
validate(need(!is.null(eventdata), "Hover over the age plot to populate this race plot"))
datapoint <- as.numeric(eventdata$pointNumber)[1]
sel<-patdata %>% filter(dp %in% datapoint)
raceselection<-subset(sel,select="Race")
raceselection<-as.data.frame(table(raceselection))
colnames(raceselection)<-c("Race","Freq")
raceselection$y<-round((raceselection$Freq*100/sum(raceselection$Freq)))
raceall<-merge(raceselection,boston_race)
raceall$Race<- as.character(raceall$Race)
raceall$Percent<-round(raceall$Percent,0)
plot_ly(raceall, x = ~Race, y = ~Percent, type = 'bar', name = 'Total Population',marker = list(color = 'rgb(255,140,0)',
line = list(color = 'rgb(8,48,107)', width = 1))
) %>%
add_trace(y = ~y, name = 'Patient Population',marker = list(color = 'rgb(49,130,189)',
line = list(color = 'rgb(8,48,107)', width = 1))) %>%
layout(yaxis = list(title = 'Population Percent'), barmode = 'group',
title = paste0("Patient Race comparison"))
})
}
boston_race dataset:
Race Percent
White 47
Unknown 0
Other 1.8
Latino Hispanic 17.5
Asian 8.9
African American 22.4
Sal data snippet:
CreateDate Age Race
1/6/1901 20 RACE_LATINO_HISPANIC
1/21/1901 37 BLACK_AFRICAN_AMERICAN
1/21/1901 51 WHITE
1/31/1901 58 WHITE
2/2/1901 24 ASIAN
2/4/1901 31 WHITE
2/7/1901 29 WHITE
2/7/1901 19 WHITE
2/11/1901 7 BLACK_AFRICAN_AMERICAN
2/12/1901 41 ASIAN
2/13/1901 22 WHITE
2/19/1901 3 RACE_LATINO_HISPANIC
2/24/1901 19 WHITE
3/7/1901 26 WHITE
3/12/1901 21 RACE_UNKNOWN
3/17/1901 39 RACE_LATINO_HISPANIC
3/18/1901 71 WHITE
3/20/1901 65 WHITE
4/10/1901 19 WHITE
4/18/1901 31 WHITE
4/23/1901 63 WHITE
4/24/1901 20 WHITE
4/29/1901 19 WHITE
4/30/1901 27 WHITE
5/2/1901 23 WHITE
5/12/1901 21 WHITE
5/16/1901 26 RACE_LATINO_HISPANIC
5/20/1901 54 BLACK_AFRICAN_AMERICAN
5/20/1901 2 WHITE
5/20/1901 9 RACE_LATINO_HISPANIC
5/21/1901 28 WHITE
5/29/1901 2 BLACK_AFRICAN_AMERICAN
5/30/1901 0 WHITE
6/3/1901 21 WHITE
6/9/1901 10 ASIAN
6/9/1901 37 WHITE
The current output:
I want the second plot to appear in a small hoverinfo box

Mult-Color Line Plot in R Plotly based on Y Value

I would like to create a Plotly graph in R that is colored green when it is positive and red when it is negative.
I attempted to do this using two separate traces producing the fist plot below which is discontinuous. I then attempted to create a colored trace using the color column which I created by the code below. These are the only implementations that I can think of using the current version of plotly.
> str(results)
'data.frame': 804 obs. of 7 variables:
$ date : Date, format: "2014-03-06" "2014-03-07" "2014-03-10" ...
$ 5yr : num 32.9 32.5 32.9 32.8 32.8 ...
$ 3y5 : num 32.4 32.1 32.5 32.4 32.4 ...
$ spread: num -0.488 -0.431 -0.438 -0.388 -0.452 ...
$ pos : num NA NA NA NA NA NA NA NA NA NA ...
$ neg : num -0.488 -0.431 -0.438 -0.388 -0.452 ...
$ color : chr "red" "red" "red" "red" ...
results$spread <- results[,3] - results[,2]
results$neg <- ifelse(results$spread < 0 , results$spread, NA)
results$pos <- ifelse(results$spread >= 0 , results$spread, NA)
plot_ly(results,
x = ~dates,
y = ~pos,
type = 'scatter',
mode = 'lines',
line = list(color = 'green')) %>%
add_trace(results,
x = ~dates,
y = ~neg,
type = 'scatter',
mode = 'lines',
line = list(color = 'red')) %>%
layout(xaxis = list(title = 'Date'),
yaxis = list(title = 'Price'))
plot_ly(results,
x = ~dates,
y = ~spread,
type = 'scatter',
mode = 'lines',
color = ~color) %>%
layout(xaxis = list(title = 'Date'),
yaxis = list(title = 'Price'))
This was an interesting one. But after a while I realized you can get what you want by inserting a zero value at every zero crossing of your plot:
I think the code is self-explanatory (with the comments)
Here is the code - (with some faked data):
library(plotly)
#fake up some data
set.seed(123)
n <- 100
sdate <- as.Date("2014-03-06")
dt <- seq.Date(sdate,by="days",length.out=n)
results <- data.frame(dates=dt,v1=rnorm(n,32.6,0.2),v2=rnorm(n,32.6,0.2))
results$spread <- results[,3] - results[,2]
# find all the zero crossings
spd <- results$spread
lagspd <- c(spd[1],spd[1:(length(spd)-1)])
crs <- sign(spd)!=sign(lagspd)
results$crs <- crs
# now insert a zero row where there is a crossing
insertZeroRow <- function(df,i){
n <- nrow(df)
ndf1 <- df[1:i,] # note these overlap by 1
ndf2 <- df[i:n,] # that is the row we insert
ndf1$spread[i] <- 0
ndf <- rbind(ndf1,ndf2)
}
i <- 1
while(i<nrow(results)){
if (results$crs[i]){
results <- insertZeroRow(results,i)
i <- i+1
}
i <- i+1
}
# plot it now
results$neg <- ifelse(results$spread <= 0 , results$spread, NA)
results$pos <- ifelse(results$spread >= 0 , results$spread, NA)
plot_ly(results,
x = ~dates,
y = ~pos,
type = 'scatter',
mode = 'lines',
line = list(color = 'green')) %>%
add_trace(results,
x = ~dates,
y = ~neg,
type = 'scatter',
mode = 'lines',
line = list(color = 'red')) %>%
layout(xaxis = list(title = 'Date'),
yaxis = list(title = 'Price'))
And here is the result:
Note you could make it better by interpolating the dates and spread value to get the correct x-axis crossing point, but I think it would not make a huge difference in most cases. If you did that you would need a date type that can represent hours of the day too (like as.POSIXct), in order to be able to specify the correct x-axis value.
Update:
Just to clear up any confusion, adding the zero rows are necessary. If you comment out the insertZeroRow call, you get this:
basically you can change your first implementation in this part of code:
results$spread <- results[,3] - results[,2]
results$neg <- ifelse(results$spread < 0 , results$spread, NA)
results$pos <- ifelse(results$spread >= 0 , results$spread, NA)
adding = in the second line of code:
results$spread <- results[,3] - results[,2]
results$neg <- ifelse(results$spread <= 0 , results$spread, NA)
results$pos <- ifelse(results$spread >= 0 , results$spread, NA)
try, it should work removing the discontinuities

Change format of y axis in plotly

I'm trying to format y axis numbers' format (in plotly) and - after hours of googling - I've decieded to ask here.
I've got a plot like here:
generated with this code:
library("plotly")
library("dplyr")
data.frame(miesiac_label = as.character(as.roman(c(1:12))),
ile = c(12000, 12100, 11100, 12000, 12000, 11900, 12200, 12100, 6000, 12100, 12100, 12100)) -> dane
dane$miesiac_label <- factor(dane$miesiac_label, levels = dane[["miesiac_label"]])
plot_ly(dane) %>%
add_trace(x = ~miesiac_label, y = ~ile,
type = 'bar', marker = list(color = '#99d3df'))
My goal is to change yaxis formatting from 12k, 10k, 8k, ... to more "polish one", so 12 tys, 10 tys, 8 tys, .... I know how to change it setting ticktext and ticktext, but I don't want to set values on my own, I prefer to have them more automatic.
I've read about thickformat but I didn't find there option, which would change k to tys.
Thanks for your help!
I think using layout() gives you what you want. First, you need to divide your y by 1000 (maybe there is another way) and then use ticksuffix to add tys.
plot_ly(dane) %>%
add_trace(x = ~miesiac_label, y = ~ile/1000, type = 'bar', marker = list(color = '#99d3df')) %>%
layout(yaxis = list(ticksuffix= "tys", title = "ile"))

Resources