ggplot plotly API mess width stack bar graph - ggplot2

I am using plotly library to get me HTML interactive graph, which i already generating from ggplot2, but with stacked graph, plotly doesnt work properly.
Here is my ggplot code :
if(file.exists(filename)) {
data = read.table(filename,sep=",",header=T)
} else {
g <- paste0("=== [E] Error : Couldn't Found File : ",filename)
print (g)
}
ReadChData <- data[data$Channel %in% c("R"),]
#head(ReadChData,10)
# calculate midpoints of bars (simplified using comment by #DWin)
Data <- ddply(ReadChData, .(qos_level),
transform, pos = cumsum(AvgBandwidth) - (0.5 *AvgBandwidth)
)
# library(dplyr) ## If using dplyr...
# Data <- group_by(Data,Year) %>%
# mutate(pos = cumsum(Frequency) - (0.5 * Frequency))
# plot bars and add text
g <- ggplot(Data, aes(x = qos_level, y = AvgBandwidth)) +
scale_x_continuous(breaks = x_axis_break) +
geom_bar(aes(fill = MasterID), stat="identity", width=0.2) +
scale_colour_gradientn(colours = rainbow(7)) +
geom_text(aes(label = AvgBandwidth, y = pos), size = 3) +
theme_set(theme_bw()) +
ylab("Bandwidth (GB/s)") +
xlab("QoS Level") +
ggtitle("Qos Compting Stream")
png(paste0(opt$out,"/",GraphName,".png"),width=6*ppi, height=6*ppi, res=ppi)
print (g)
library(plotly)
p <- ggplotly(g)
#libdir arugumet will be use to point to commin lib
htmlwidgets::saveWidget(as.widget(p), selfcontained=FALSE, paste0(opt$out,"/qos_competing_stream.html"))
and here is HTML output form plotly lib
http://pasteboard.co/2fHQfJwFu.jpg
Please help.

This is perhaps quite a bit late to answer. But for someone who might have the issue in future...
The geom_bar's width parameter is not recognized by ggplotly function.
Work Around :
A work around (not very good one) by using parameters colour="white", size = 1. This basically adds a white line around the bars, making an effect like white space.
You could try the following:
stat_summary(aes(fill = MasterID), geom="bar", colour="white", size = 1, fun.y = "sum", position = "stack")
Better solution :
Use bargap parameter from layout function. The code should be:
ggplotly(type='bar', ...) %>% layout(bargap = 3, autosize=T)
P.S. the code in question code is not executable, throws an error due to missing filename.

Related

Creating graphs in for-loop stores in environment only last graph

I know this question has been asked before, but I found none of the answers where clear enough for me to understand how to solve my problem.
I am looping over several variables to create graphs. The thing is, I want to save each of the graphs in the environment and combine them using ggsave. The problem I have is that all the graphs stored in the R environment are the same and correspond to the last graph in the loop.
Here is the data I am using.
My code is the following:
# DEFINING VARIABLES
varnames <- c("firstborn", "gradet0", "p_educ")
#LOOPING OVER THEM:
for (var in varnames) {
var_m <- data[[paste(var,"m", sep="_")]]
var_se <- data[[paste(var,"se", sep="_")]]
varf <- data[[var]]
#CREATING GRAPHS:
plot <- ggplot(data, aes(x=var1_, y=var_m, fill=factor(varf))) +
geom_bar(stat = "identity", color="black",
position=position_dodge()) +
geom_errorbar(aes(ymin=var_m-1.96*var_se, ymax=var_m+1.96*var_se), width=.2,
position=position_dodge(.9)) +
theme_classic() +
labs(x =var, y = "Predicted values") +
scale_x_discrete(limits = c("noteen_female", "noteen_male", "teen_female",
"teen_male"),
labels = c("Female", "Male",
"Teen Mother", "Teen Father")) +
scale_fill_brewer(palette="Oranges") +
theme(legend.title = element_blank())
# TRYING TO SAVE TO THE R ENVIRONMENT
plot_var_name <- str_c(c("plot", var), collapse = "_")
assign(plot_var_name, plot)
# SAVING TO THE PC:
ggsave(paste("graph_",var,".png",sep=""), width = 200,
height = 100,
units = c("mm"),)
}
# COMBINING GRAPHS:
ggarrange(plot_firstborn, plot_gradet0, plot_p_educ,
ncol = 2, nrow = 2)
Whilst ggsave works well, the stored graphics in R environment are all the same. I am still an R newbie, so kind help/explanation would be greatly appreciated.

What is the easiest way to add a tooltip to a boxplot(ggplot) generated through Shiny?

I need the y-value displayed in a tooltip-style text box when the cursor hovers over a bar. I have to imagine this is a simple function through Shiny, but I haven't been able to figure it out.
I'll include the relevant UI and server code below:
UI :
#TAB 3 (QUOTA/SALES)
tabPanel(title = "Quota/Sales",
fluidRow(column(9,
wellPanel(height=600, width ="100%",
plotOutput("quota", height=550, width ="100%"
))),
column(3,
wellPanel(height=100, width ="100%",
selectInput("countyquota", "County:", choices=countychoices, selected = "Statewide"))))
)))
Server:
hdata <- reactive({
if(input$sexage == "All" & input$countyharv == "Statewide") {
harvdata}
else if(input$sexage == "All" & input$countyharv != "Statewide")
{filter(harvdata, NAME == input$countyharv)}
else if (input$sexage != "All" & input$countyharv != "Statewide")
{filter(harvdata, sexage == input$sexage, NAME == input$countyharv)}
else if (input$countyharv == "Statewide" & input$sexage != "All"){
filter(harvdata, sexage == input$sexage)
}
})
output$harv <- renderPlot({
ggplot(hdata(), aes(fill=sexage, y=harvest, x=year, label = harvest)) +
geom_bar(position="dodge", stat="identity") +
xlab("Year") +
ylab("Harvest") +
labs(fill = NULL)+
theme_bw()
})`
Welcome to StackOverflow. The first thing is that it is always a good idea to include a minimal reproducible example of your base code. That way you help us to help you. In your case, the code you provide does not run (is not reporducible) you have to include the data (or a sample).
Going into the answer. ggplot2 output by default does not show tooltip, you need to use a JavaScript base library for that. The more common are plotly and highcharter.
Lets create a shiny app using the mtcars dataset to show you how to take ggplot2 plots to plotly using the plotly::ggplotly() function.
Note the important comments within the code.
library(shiny)
library(plotly) # you need this packages
ui <- fluidPage(
selectInput('x', 'X axis', choices = names(mtcars), selected = 'wt'),
selectInput('y', 'Y axis', choices = names(mtcars), selected = 'mpg'),
fluidRow(
column(
width = 6,
plotOutput('static'),
),
column(
width = 6,
# New function to render plotly outputs
plotlyOutput('dynamic')
)
)
)
server <- function(input, output, session) {
# This a regular ggplot2 object.
plot <- reactive({
ggplot(
data = mtcars,
mapping = aes(x = .data[[input$x]], y = .data[[input$y]])
) +
geom_point() +
theme_bw()
})
output$static <- renderPlot({ plot() })
# 1- New render frunction to handel plotly outputs
# 2- Place a ggplot2 object within the ggplotly() function
output$dynamic <- renderPlotly({ ggplotly(plot()) })
}
shinyApp(ui, server)

Why is there a space between the bars and the axis in ggplot2 bar graphs, and how do I get rid of it?

I've been building a bar graph in R, and I noticed a problem. whenever the graph is made, it has a very small gap between the bars and the axis that causes a line of the background image to appear (Link). How can I get rid of this?
Code:
album_cover <- image_read("https://i.scdn.co/image/ab67616d0000b273922a12ba0b5a66f034dc9959")
ggplot(data=album_df, aes(x=rev(factor(track_names, track_names)), y=-1 * track_length)) +
ggtitle("Songs vs length")+
annotation_custom(rasterGrob(album_cover,
width = unit(1,"npc"),
height = unit(1,"npc")),
-Inf, Inf, -Inf, Inf)+
#geom_image(image = "https://i.scdn.co/image/ab67616d0000b273922a12ba0b5a66f034dc9959", size = Inf) +
geom_bar(stat="identity", position = "identity", color = 'NA', alpha = 0.9, width = 1, fill = 'white') +
scale_y_continuous(expand = c(0, 0), limits = c(-1 * max_track, 0)) +
scale_x_discrete(expand = c(0, 0)) +
theme(axis.title.x=element_blank(),
axis.title.y=element_blank(),
axis.text.x=element_blank(),
axis.ticks.x=element_blank()
) +
coord_flip()
Interesting issue. I've tried many things, including modification of many of the theme elements. It works with theme_void(), but then the issue resurfaces as you add back in the plot elements (namely the song titles on the axis, for some reason).
What finally did work is just squishing your image to be ever so slightly less than 1. In this case, just changing from 1 to 0.999 fixes the issue and you no longer have the strip of the image hanging out on the right. For this, I made up my own data, but I'm using the same image:
df <- data.frame(
track_names=paste0('Song',1:8),
track_length=c(3.5,7.5,5,3,7,10,6,7.4)
)
album_cover <- image_read2("https://i.scdn.co/image/ab67616d0000b273922a12ba0b5a66f034dc9959")
ggplot(data=df, aes(x=track_names, y=-1*track_length)) +
annotation_custom(rasterGrob(album_cover,
width=unit(0.999,'npc'), height=unit(1,'npc')),
xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
geom_col(alpha=0.9, width=1, fill='white', color=NA) +
scale_y_continuous(expand=c(0,0)) +
scale_x_discrete(expand=c(0,0)) +
ggtitle('Songs vs Length') +
coord_flip()
Note, the same code above gives the following image below when width=unit(1, 'npc'),... in the rasterGrob() function (note the line at the right side of the image):

Issue when trying to plot geom_tile using ggplotly

I would like to plot a ggplot2 image using ggplotly
What I am trying to do is to initially plot rectangles of grey fill without any aesthetic mapping, and then in a second step to plot tiles and change colors based on aesthetics. My code is working when I use ggplot but crashes when I try to use ggplotly to transform my graph into interactive
Here is a sample code
library(ggplot2)
library(data.table)
library(plotly)
library(dplyr)
x = rep(c("1", "2", "3"), 3)
y = rep(c("K", "B","A"), each=3)
z = sample(c(NA,"A","L"), 9,replace = TRUE)
df <- data.table(x,y,z)
p<-ggplot(df)+
geom_tile(aes(x=x,y=y),width=0.9,height=0.9,fill="grey")
p<-p+geom_tile(data=filter(df,z=="A"),aes(x=x,y=y,fill=z),width=0.9,height=0.9)
p
But when I type this
ggplotly(p)
I get the following error
Error in [.data.frame(g, , c("fill_plotlyDomain", "fill")) :
undefined columns selected
The versions I use are
> packageVersion("plotly")
1 ‘4.7.1
packageVersion("ggplot2")
1 ‘2.2.1.9000’
##########Edited example for Arthur
p<-ggplot(df)+
geom_tile(aes(x=x,y=y,fill="G"),width=0.9,height=0.9)
p<- p+geom_tile(data=filter(df,z=="A"),aes(x=x,y=y,fill=z),width=0.9,height=0.9)
p<-p+ scale_fill_manual(
guide = guide_legend(title = "test",
override.aes = list(
fill =c("red","white") )
),
values = c("red","grey"),
labels=c("A",""))
p
This works
but ggplotly(p) adds the grey bar labeled G in the legend
The output of the ggplotly function is a list with the plotly class. It gets printed as Plotly graph but you can still work with it as a list. Moreover, the documentation indicates that modifying the list makes it possible to clear all or part of the legend. One only has to understand how the data is structured.
p<-ggplot(df)+
geom_tile(aes(x=x,y=y,fill=z),width=0.9,height=0.9)+
scale_fill_manual(values = c(L='grey', A='red'), na.value='grey')
p2 <- ggplotly(p)
str(p2)
The global legend is here in p2$x$layout$showlegend and setting this to false displays no legend at all.
The group-specific legend appears at each of the 9 p2$x$data elements each time in an other showlegend attribute. Only 3 of them are set to TRUE, corresponding to the 3 keys in the legend. The following loop thus clears all the undesired labels:
for(i in seq_along(p2$x$data)){
if(p2$x$data[[i]]$legendgroup!='A'){
p2$x$data[[i]]$showlegend <- FALSE
}
}
Voilà!
This works here:
ggplot(df)+
geom_tile(aes(x=x,y=y,fill=z),width=0.9,height=0.9)+
scale_fill_manual(values = c(L='grey', A='red'), na.value='grey')
ggplotly(p)
I guess your problem comes from the use of 2 different data sources, df and filter(df,z=="A"), with columns with the same name.
[Note this is not an Answer Yet]
(Putting for reference, as it is beyond the limits for comments.)
The problem is rather complicated.
I just finished debugging the code of plotly. It seems like it's occurring here.
I have opened an issue in GitHub
Here is the minimal code for the reproduction of the problem.
library(ggplot2)
set.seed(1503)
df <- data.frame(x = rep(1:3, 3),
y = rep(1:3, 3),
z = sample(c("A","B"), 9,replace = TRUE),
stringsAsFactors = F)
p1 <- ggplot(df)+
geom_tile(aes(x=x,y=y, fill="grey"), color = "black")
p2 <- ggplot(df)+
geom_tile(aes(x=x,y=y),fill="grey", color = "black")
class(plotly::ggplotly(p1))
#> [1] "plotly" "htmlwidget"
class(plotly::ggplotly(p2))
#> Error in `[.data.frame`(g, , c("fill_plotlyDomain", "fill")): undefined columns selected

Storing plot objects in a list

I asked this question yesterday about storing a plot within an object. I tried implementing the first approach (aware that I did not specify that I was using qplot() in my original question) and noticed that it did not work as expected.
library(ggplot2) # add ggplot2
string = "C:/example.pdf" # Setup pdf
pdf(string,height=6,width=9)
x_range <- range(1,50) # Specify Range
# Create a list to hold the plot objects.
pltList <- list()
pltList[]
for(i in 1 : 16){
# Organise data
y = (1:50) * i * 1000 # Get y col
x = (1:50) # get x col
y = log(y) # Use natural log
# Regression
lm.0 = lm(formula = y ~ x) # make linear model
inter = summary(lm.0)$coefficients[1,1] # Get intercept
slop = summary(lm.0)$coefficients[2,1] # Get slope
# Make plot name
pltName <- paste( 'a', i, sep = '' )
# make plot object
p <- qplot(
x, y,
xlab = "Radius [km]",
ylab = "Services [log]",
xlim = x_range,
main = paste("Sample",i)
) + geom_abline(intercept = inter, slope = slop, colour = "red", size = 1)
print(p)
pltList[[pltName]] = p
}
# close the PDF file
dev.off()
I have used sample numbers in this case so the code runs if it is just copied. I did spend a few hours puzzling over this but I cannot figure out what is going wrong. It writes the first set of pdfs without problem, so I have 16 pdfs with the correct plots.
Then when I use this piece of code:
string = "C:/test_tabloid.pdf"
pdf(string, height = 11, width = 17)
grid.newpage()
pushViewport( viewport( layout = grid.layout(3, 3) ) )
vplayout <- function(x, y){viewport(layout.pos.row = x, layout.pos.col = y)}
counter = 1
# Page 1
for (i in 1:3){
for (j in 1:3){
pltName <- paste( 'a', counter, sep = '' )
print( pltList[[pltName]], vp = vplayout(i,j) )
counter = counter + 1
}
}
dev.off()
the result I get is the last linear model line (abline) on every graph, but the data does not change. When I check my list of plots, it seems that all of them become overwritten by the most recent plot (with the exception of the abline object).
A less important secondary question was how to generate a muli-page pdf with several plots on each page, but the main goal of my code was to store the plots in a list that I could access at a later date.
Ok, so if your plot command is changed to
p <- qplot(data = data.frame(x = x, y = y),
x, y,
xlab = "Radius [km]",
ylab = "Services [log]",
xlim = x_range,
ylim = c(0,10),
main = paste("Sample",i)
) + geom_abline(intercept = inter, slope = slop, colour = "red", size = 1)
then everything works as expected. Here's what I suspect is happening (although Hadley could probably clarify things). When ggplot2 "saves" the data, what it actually does is save a data frame, and the names of the parameters. So for the command as I have given it, you get
> summary(pltList[["a1"]])
data: x, y [50x2]
mapping: x = x, y = y
scales: x, y
faceting: facet_grid(. ~ ., FALSE)
-----------------------------------
geom_point:
stat_identity:
position_identity: (width = NULL, height = NULL)
mapping: group = 1
geom_abline: colour = red, size = 1
stat_abline: intercept = 2.55595281266726, slope = 0.05543539319091
position_identity: (width = NULL, height = NULL)
However, if you don't specify a data parameter in qplot, all the variables get evaluated in the current scope, because there is no attached (read: saved) data frame.
data: [0x0]
mapping: x = x, y = y
scales: x, y
faceting: facet_grid(. ~ ., FALSE)
-----------------------------------
geom_point:
stat_identity:
position_identity: (width = NULL, height = NULL)
mapping: group = 1
geom_abline: colour = red, size = 1
stat_abline: intercept = 2.55595281266726, slope = 0.05543539319091
position_identity: (width = NULL, height = NULL)
So when the plot is generated the second time around, rather than using the original values, it uses the current values of x and y.
I think you should use the data argument in qplot, i.e., store your vectors in a data frame.
See Hadley's book, Section 4.4:
The restriction on the data is simple: it must be a data frame. This is restrictive, and unlike other graphics packages in R. Lattice functions can take an optional data frame or use vectors directly from the global environment. ...
The data is stored in the plot object as a copy, not a reference. This has two
important consequences: if your data changes, the plot will not; and ggplot2 objects are entirely self-contained so that they can be save()d to disk and later load()ed and plotted without needing anything else from that session.
There is a bug in your code concerning list subscripting. It should be
pltList[[pltName]]
not
pltList[pltName]
Note:
class(pltList[1])
[1] "list"
pltList[1] is a list containing the first element of pltList.
class(pltList[[1]])
[1] "ggplot"
pltList[[1]] is the first element of pltList.
For your second question: Multi-page pdfs are easy -- see help(pdf):
onefile: logical: if true (the default) allow multiple figures in one
file. If false, generate a file with name containing the
page number for each page. Defaults to ‘TRUE’.
For your main question, I don't understand if you want to store the plot inputs in a list for later processing, or the plot outputs. If it is the latter, I am not sure that plot() returns an object you can store and retrieve.
Another suggestion regarding your second question would be to use either Sweave or Brew as they will give you complete control over how you display your multi-page pdf.
Have a look at this related question.