I want to use ggvis for data exploration (because I am familiar with ggplot2), and it would be of great help, due to many groups in my data sets, to be able to select and unselect groups to make different specific (data) comparisons.
set.seed(10)
dat <- data.frame(x=c(1:3,1:3),y=rnorm(6),groups=factor(rep(1:2,each=3)))
library(ggvis)
dat %>% ggvis(~x, ~y) %>% layer_points(fill=~groups)
# i know this example does not work - but is that possible somehow?
dat %>% ggvis(~x, ~y) %>% layer_points(fill=input_checkbox(~groups))
What I want at the end, is a graphic with which I can select a subset of groups by using radio buttons (for example).
Is there any way to do that?
Thank you!
From the documentation
Limitations
Currently, interactive inputs can only be used in two places:
as arguments to transforms: layer_smooths(span = input_slider(0, 1))
as properties: props(size = input_slider(10, 1000))
This means that interactive inputs can only modify the data, not the
underlying plot specification. In other words, with only basic
interactivity there’s no way to add or remove layers, or switch
between different datasets. This is a reasonable limitation because if
you’re doing exploration you can always create a new ggvis with R
code, or if you’re polishing a plot for presentation, you can embed it
in a Shiny app and gain full control over the plot.
For simple data exploration, one idea could be to use filter():
set.seed(10)
dat <- data.frame(x = c(1:5,1:5,1:5),
y = rnorm(15), groups = factor(rep(1:5,each=3)))
Note: I edited your initial dataset to illustrate the concept with more groups
library(dplyr)
library(ggvis)
dat %>% ggvis(~x, ~y) %>% layer_points(fill = ~groups)
You could pass the arguments (either manually directly in your R code or with your radio buttons in a Shiny app) to filter() to isolate specific groups:
dat %>%
filter(groups == 1 | groups == 3) %>%
ggvis(~x, ~y) %>% layer_points(fill = ~groups)
Related
In summary, I would like to split a list of plots created using nest and ggplot2 to two columns. The problem I have had in my approaches is that elements in different subplots end up having more or less inconsistent dimensions because subplots have different heights (different number of elements in each groups, possibly exclusion of x-axis labels etc.).
Following example code uses the main tidyverse packages. I first generate some dummy data using mtcars; cars are split to random groups and each car is assigned an in-group position.
dummy <- mtcars %>%
mutate(group = sample(1:10, n(), replace = TRUE)) %>%
filter(group < 6) %>%
group_by(group) %>%
mutate(position = 1:n())
The actual code produces a list of subplots (plots) and information about number of elements in each group (heights).
## install patchwork via:
## devtools::install_github("thomasp85/patchwork")
plots <- dummy %>%
nest(-group, .key = "data") %>%
mutate(plots = map(data, ~ggplot(data = .x, aes(x = position, y = hp)) +
geom_bar(stat="identity") +
coord_flip()),
heights = purrr::map(data, ~ nrow(.)) %>% unlist())
g.plot <- patchwork::wrap_plots(plots$plots, ncol = 1, heights = plots$heights)
Function wrap_plots is able to produce a nice one-panel image using information about heights (included image panel A). When there is a large number of images to the plot, the one-column plot is not practical. I would, therefore, kindly ask help on how to turn the output of the above code (panel A) to the hoped output (panel B). Panel C exaggerates the problematic effect I have had using different image concatenation approaches.
Current output and hoped output
you could try setting the panel size to fixed dimensions and then arranging the gtables together,
library(egg)
library(gridExtra)
lg <- purrr::map2(plots$plots, plots$heights,
function(p,h) gtable_frame(ggplotGrob(p),
height =unit(h/10,'npc'), #tweak
width =unit(0.7,'npc'))) #tweak
grid.arrange(gtable_rbind(lg[[1]],lg[[2]], egg::.dummy_gtable),
gtable_rbind(lg[[3]],lg[[4]], egg::.dummy_gtable), ncol=2)
(tested with set.seed(12); I don't know what sample() OP had)
I'm trying to draw a barplot using ggvis, for some data where for each variable I have both a negative and a positive value. It would be similar to this example from ggplot2.
However, when I try something similar in ggvis, I end up with basically no plot at all, just some weird lines.
Example data:
df <- data.frame(
direction=rep(c("up", "down"), each=3),
value=c(1:3, -c(1:3)),
x=rep(c("A", "B", "C"), 2))
This works, for all positive values:
df %>%
mutate(value.pos=abs(value)) %>%
ggvis(x=~x, y=~value.pos) %>%
group_by(direction) %>%
layer_bars(stack=TRUE)
This gives me nothing:
df %>%
ggvis(x=~x, y=~value) %>%
group_by(direction) %>%
layer_bars(stack=TRUE)
I've also tried various combinations of plotting them one by one, e.g.:
df %>%
spread(key=direction, value=value) %>%
ggvis(x=~x, y=~up) %>%
layer_bars() %>%
layer_bars(x=~x, y=~down)
So far, no luck. I suspect I'm missing some simple solution...
I don't ggvis lets you produce stacked bar plots with negative values within the same groups as positive data.
This is because if an x value appear more than once in the data, then ggvis will sum up the y values at each x. I had thought that since you plotted the vector 1:3, they canceled out, but that's not the case.
As of now, I do not believe that dodged bar plots exist for this. It also messes with the labels.
You can produce the plot non-stacked, while filling in the position.
df %>%
group_by(direction) %>%
ggvis(x=~x, y=~value, fill = ~direction) %>%
layer_bars(stack = FALSE)
Anyways, you might consider avoiding ggvis for any production work since it is under development, and hasn't been updated in a couple of months.
#shayaa
Thanks, this does seem to be working, although it will probably require some tweaking, and may not look as nicely as if I was using ggplot2. Actually, the reason I am using ggvis, is because I would like to combine it with shiny, to make a small interactive web version. For example:
df <- data.frame(
direction=rep(c("up", "down"), each=3),
value=c(1:3, -c(1:3)),
x=rep(c("A", "B", "C"), 2))
plot_fct <- function(letter) {
df %>%
filter(x==letter) %>%
ggvis(x=~x, y=~value, fill = ~direction) %>%
layer_bars(stack = FALSE) %>%
scale_numeric("y", domain=c(NA,NA))
}
ui <- fluidPage(
sidebarPanel(
selectInput("letter", "Choose letter", c("A", "B", "C"), selected="A")
),
mainPanel(
ggvisOutput("letter_barplot")
)
)
server <- function(input, output) {
plot_fct(letter=reactive(input$letter)) %>% bind_shiny("letter_barplot")
}
runApp(shinyApp(ui, server))
However, it does not seem to work for me anyway, since there is some issue with the reactive being of class character. I keep getting the error:
Error in eval(substitute(expr), envir, enclos) :
comparison (1) is possible only for atomic and list types
Guess I'll have to keep trying.
I would like to create an interactive histogram which gives information about the bins by hover. This thread gives answer to how to add tooltip with count numbers.
library("ggvis")
cocaine %>%
ggvis(x = ~weight) %>%
layer_histograms() %>%
add_tooltip(function(df) (paste("count:", df$stack_upr_ - df$stack_lwr_)))
How can I add the share of each bin as well? I should somehow add nrow(cocaine) to ggvis and create the shares from count but I did not succeed on how to achieve that (tried to take the suggestions of this post but apparently that solves a different problem).
You could do something like this I imagine:
cocaine %>%
ggvis(x = ~weight) %>%
layer_histograms() %>%
add_tooltip(function(df) paste("count:", df$stack_upr_, 'share:',
format(df$stack_upr_/nrow(cocaine), digits=2)))
This will show both the share and the bin number.
Also, as a side note you do not need df$stack_upr_ - df$stack_lwr_ because df$stack_lwr_ will be zero. Just df$stack_upr_ will do.
I am trying to layer on tooltips to a map of the US, but wherever I hover... it displays the same data. In addition, the data is wrong. I'm thinking that it is passing through the factor values and not the character value. I tried taking tips from the movie explorer example - http://shiny.rstudio.com/gallery/movie-explorer.html - but, it's not working as I hoped. Any hints or clues I should look into?
Update: I've determined that you can only pass through arguments that are being called into the ggvis function. So, if my tooltip function included region, long, & lat, all of them would appear in the tooltip. Since Population and Income do not appear anywhere in the function, it is not passing them through. I'm still lost on how to proceed, but any ideas would be awesome! :)
library(ggplot2)
library(shiny)
library(ggvis)
library(dplyr)
shinyApp(
ui = fluidPage(
#numericInput("n", "n", 1),
ggvisOutput("map")
),
server = function(input, output) {
statesData <- reactive({
states <- data.frame(state.x77)
states$region <- row.names(state.x77) %>% tolower
row.names(states) <- NULL
all_states <- map_data("state") %>%
mutate(region = tolower(region)) %>%
left_join(states)
all_states_unique <- all_states %>%
select(region, Population, Income, Illiteracy, Life.Exp, Murder, HS.Grad, Frost, Area) %>%
unique
states_tooltip <- function(x) {
if (is.null(x)) return(NULL)
if (is.null(x$region)) return(NULL)
# Pick out the movie with this ID
allStates <- isolate(all_states_unique)
state <- allStates[allStates$region == x$region, ]
paste0("<b>", state$region, "</b><br>",
state$Population, "<br>",
state$Income
)
}
all_states %>%
arrange(group, order) %>%
ggvis(x = ~long, y = ~lat) %>%
layer_paths(fill = ~region, stroke := .2) %>%
add_tooltip(states_tooltip, "hover")
})
statesData %>% bind_shiny('map')
}
)
Add an index to the dataframe you want to pull the tooltip data from:
state$id <- 1:nrow(state)
ggvis takes a "key" argument to facilitate this kind of tooltip:
ggvis(x = ~long, y = ~lat, key := ~id) %>%
I tried figuring out that movie example and didn't find it very helpful. This always works for me:
add_tooltip(function(x) {
row <- state[state$id == x$key,]
paste0("<b>", row[,"region"], "</b><br>",
row[,"Population"], "<br>",
row[,"Income"]
)})
As for the issue w/ the tooltip always coming up the same, I don't know for sure but think it's due to the order of your layers in the ggvis command. Had a similar problem where I had some polygons layered on top of a scatterplot. It kept trying to draw the tooltip for the polygons (which covered the whole chart) when what I wanted was the individual points to display the tooltip. By reversing their order in the ggvis command (ie layer_points() %>% layer_shapes()) I got it to work.
I realise this is quite late but for future reference and others that stumble across this page. If your dataframe has been converted using fortify and has the group variable then this may be equivalent to the State level. Group can then be used to filter for the tooltip as it is in the ggvis command. That then allowed me to gain access to other variables I wanted.
In my problem I couldn't use the key solution because I was creating the plot to react to numerous years. So to change what you have above states_tooltip would become:
states_tooltip <- function(x){
row <- allstates[allstates$group==x$group,] %>%
select(region, Population, Income) %>% unique
paste0("<b>", row[,"region"], "</b><br>",
row[,"Population"], "<br>",
row[,"Income"]
)})
Hi I'm a little confused with the scales in ggvis.
I'm trying to do two things: one is have a log scale (the equivalent of log="x" in plot()). I'm also looking for the equivalent of xlim=c(). In both cases, the code below is not giving the expected results.
# install.packages("ggvis", dependencies = TRUE)
library(ggvis)
df <- data.frame(a=c(1, 2, 3, 1000, 10000), b=c(0.1069, 0.0278, 0.0860, 15.5640, 30.1745))
df %>% ggvis(~a, ~b)
df %>% ggvis(~a, ~b) %>% scale_numeric("x", trans="log")
Notice that with trans="log", all dots are on the left of the plot and the scale disappears.
Next, I want to restrict the plot to certain values. I could subset the data frame but I'm looking to have the equivalent of xlim from plot().
df %>% ggvis(~a, ~b) %>% scale_numeric("x", trans="linear", domain=c(10, 40))
This is giving even weirder results, so I'm guessing I might be misinterpreting what domain does.
Thanks for your help!
I've encountered the same problem that you've mentioned.
Apparently, the developer of ggvis noticed this bug as well. This is currently marked as an issue. You can find the issue here: https://github.com/rstudio/ggvis/issues/230
In the context of your question:
# install.packages("ggvis", dependencies = TRUE)
library(ggvis)
df <- data.frame(a=c(1, 2, 3, 1000, 10000), b=c(0.1069, 0.0278, 0.0860, 15.5640, 30.1745))
df %>% ggvis(~a, ~b)
# Points will disapper
df %>% ggvis(~a, ~b) %>% scale_numeric("x", trans="log")
# Should work
df %>% ggvis(~a, ~b) %>% scale_numeric("x", trans="log", expand=0)
However, you may notice that after the transformation, the spacing between ticks don't appear to be uniform. But at least the dots are rendered correctly.
I ran into the same issue and noticed that as soon as I removed the 0 data points from my data (of which log() cannot be computed) everything started to work fine.
This is strange, I've now tried a lot of things with your data, but can't find the problem.
The I tested it on the Violent Crime Rates by US State (see help(USArrests)) and it worked like a charm.
data(USArrests)
# str(USArrests)
p <- USArrests %>% ggvis(~ Murder, ~ Rape) %>% layer_points()
p %>% scale_numeric("y", trans = "log")
This is not an answer, simply to share this with you.