ggvis inside a function - r

I try to create a simple function which allow to draw a ggvis plot. I know that I have to use non-standard evaluation here that's why I use intercept function of lazyeval package:
test_fn <- function(data,xvar, yvar){
plot <-
data %>%
ggvis(lazyeval::interp(~x, x = as.name(xvar)),
lazyeval::interp(~y, y = as.name(yvar))) %>%
layer_points()
return(plot)
}
EDIT:
This function works fine:
test_fn(mtcars,'mpg', 'qsec')
But what should I do additionally in order to a given command works:
test_fn(mtcars,mpg, qsec)

One option is to use deparse(substitute(...)) for this sort of non-standard evaluation. It makes the function longer but can be convenient for the user.
Here's what it could look like using the lazyeval::interp method:
test_fn <- function(data, xvar, yvar){
x <- deparse(substitute(xvar))
y <- deparse(substitute(yvar))
plot <-
data %>%
ggvis(lazyeval::interp(~x, x = as.name(x)),
lazyeval::interp(~y, y = as.name(y))) %>%
layer_points()
return(plot)
}
And here's the version with prop:
test_fn <- function(data, xvar, yvar){
x <- deparse(substitute(xvar))
y <- deparse(substitute(yvar))
plot <-
data %>%
ggvis(prop("x", as.name(x)),
prop("y", as.name(y))) %>%
layer_points()
return(plot)
}
Both work using unquoted variable names:
test_fn(mtcars, mpg, wt)

Related

Plotting box-plots with for loop in Plotly

I made several plots with this lines of code:
dataset_numeric = dplyr::select_if(dataset, is.numeric)
par(mfrow=c(3,3))
for(i in 1:9) {
boxplot(dataset_numeric[,i], main=names(dataset_numeric)[i])
}
And output from this plot is pic below :
So I want to do same but now with library(Plotly) so can anybody help me how to do that ?
The following uses packages tidyr and ggplot2. First, the data are converted to a long table with pivot_longer, and then piped to ggplot. One issue to note in the example with one box only is that an explicit x aesthetic is needed, otherwise only the first box may be shown.
library("dplyr")
library("plotly")
library("ggplot2")
library("tidyr")
dataset <- as.data.frame(matrix(rnorm(99), ncol=9))
p <- pivot_longer(dataset, cols=everything()) %>%
ggplot(aes(x=0, y = value)) +
geom_boxplot() + facet_wrap( ~ name)
ggplotly(p)
Edit: a first had still an issue, that could be solved by adding x=0.
I you want to use plotly and put all variables in the same graph, you can use add_trace() in a for loop to do what you want.
library(plotly)
dataset_numeric = dplyr::select_if(iris, is.numeric)
fig <- plot_ly(data = dataset_numeric, type = "box")
for (i in 1:ncol(dataset_numeric)) {
fig <- fig %>% add_trace(y = dataset_numeric[,i])
}
fig
If you want to have separate plot for each variable, you can use subplot()
all_plot <- list()
for (i in 1:ncol(dataset_numeric)) {
fig <- plot_ly(data = dataset_numeric, type = "box") %>%
add_trace(y = dataset_numeric[,i])
all_plot <- append(all_plot, list(fig))
}
plt <- subplot(all_plot)
plt

How to change the lab name corresponding to function in ggplot

I have a few functions which are called by a final function to draw ggplot graph. I am using a list to pass to function call so that the graph could be iterated through each of them. However, the xlab is not showing correctly.
first_plot <- function(dta, vari) {
vari <- enquo(vari)
dta %>%
group_by(!!vari) %>%
summarize(a = mean(!!whatever) +
ggplot(aes(x=!!vari)) +
geom_point(aes(a))
}
plot_all <- function(dta, item) {
list_var <- list(name = item)
plot_list<- list()
for(i in 1:length(item)) {
vari <- sym(item[i])
plot_list[[i]] <- first_plot(dta, vari)
}
}
If I pass plot_all(data, c('a','b','c'), I would expect the xlab would show a, b, c correspondingly but it always show vari. Can you help me to troubleshoot this?
Thanks.
Looks like you need as.name instead of enquo in first_plot. Then the !! will work when creating the plot. A simplified example with iris data:
library(ggplot2)
library(cowplot)
first_plot <- function(dta, vari) {
vari <- as.name(vari)
ggplot(dta, aes(y = !!vari, x=Species)) +
geom_point()
}
first_plot(iris, "Sepal.Length")
plot_list <- lapply(names(iris)[1:4], first_plot, dta=iris)
plot_grid(plotlist=plot_list)
Should make this plot

Functional programming with dplyr

Looking for a more efficient / elegant way to pass multiple arguments to a group-by using non-standard evaluation in a function using dplyr. I don't want to use the ... operator, but to specify the functions individually.
My specific use case is a function which takes a data frame and creates a ggplot object with simpler syntax. Here is an example of the code I want to automate with my function:
# create data frame
my_df <- data.frame(month = sample(1:12, 1000, replace = T),
category = sample(head(letters, 3), 1000, replace = T),
approved = as.numeric(runif(1000) < 0.5))
my_df$converted <- my_df$approved * as.numeric(runif(1000) < 0.5)
my_df %>%
group_by(month, category) %>%
summarize(conversion_rate = sum(converted) / sum(approved)) %>%
ggplot + geom_line(aes(x = month, y = conversion_rate, group = category,
color = category))
I want to combine that group_by, summarize, ggplot, and geom_line into a simple function that I can feed an x, y, and group, and have it perform all the dirty work under the hood. Here's what I've gotten to work:
# create the function that does the grouping and plotting
plot_lines <- function(df, x, y, group) {
x <- enquo(x)
group <- enquo(group)
group_bys <- quos(!! x, !! group)
df %>%
group_by(!!! group_bys) %>%
my_smry %>%
ggplot + geom_line(aes_(x = substitute(x), y = substitute(y),
group = substitute(group), color = substitute(group)))
}
# create a function to do the summarization
my_smry <- function(x) {
x %>%
summarize(conversion_rate = sum(converted) / sum(approved))
}
# use my function
my_df %>%
plot_lines(x = month, y = conversion_rate, group = category)
I feel like the group_by handling is pretty inelegant: quoting x and group with enquo, then unquoting them with !! inside of another quoting function quos, only to re-unquote them with !!! on the next line, but it's the only thing I've been able to get to work. Is there a better way to do this?
Also, is there a way to get ggplot to take !! instead of substitute? What I'm doing feels inconsistent.
You could just do a straight eval.parent(substitute(...)) like this. Being base R it works consistently across R and is simple to do. One can even use an ordinary aes.
plot_lines <- function(df, x, y, group) eval.parent(substitute(
df %>%
group_by(x, group) %>%
my_smry %>%
ggplot + geom_line(aes(x = x, y = y, group = group, color = group))
))
plot_lines(my_df, month, conversion_rate, category)
The problem is that ggplot hasn't been updated to handle quosures yet, so you've got to pass it expressions, which you can create from quosures with rlang::quo_expr:
library(tidyverse)
set.seed(47)
my_df <- data_frame(month = sample(1:12, 1000, replace = TRUE),
category = sample(head(letters, 3), 1000, replace = TRUE),
approved = as.numeric(runif(1000) < 0.5),
converted = approved * as.numeric(runif(1000) < 0.5))
plot_lines <- function(df, x, y, group) {
x <- enquo(x)
y <- enquo(y)
group <- enquo(group)
df %>%
group_by(!! x, !! group) %>%
summarise(conversion_rate = sum(converted) / sum(approved)) %>%
ggplot(aes_(x = rlang::quo_expr(x),
y = rlang::quo_expr(y),
color = rlang::quo_expr(group))) +
geom_line()
}
my_df %>% plot_lines(month, conversion_rate, category)
However, keep in mind that ggplot will almost inevitably be updated from lazyeval to rlang, so while this interface will probably keep working, a simpler, more consistent one will probably be possible shortly.

Using purrr to plot multiple items against each other

I am trying to learn purrr from the tidyverse
I have set up a piece of code to attempt to plot all variables in the iris data-set against each other to see if they are linearly related. Unfortunately I don't seem to get anything back except blank plots. Below is my example. Can anyone help
library(tidyverse)
mydf <- iris %>%
as_tibble %>%
dplyr::select(everything(), -Species)
# Create a grid of names of columns
mynames <- names(mydf)
mygrid <- expand.grid(x=mynames, y =mynames)
# Define function
plot_my_data <- function(mydata, x, y){
ggplot(mydata, aes(x, y)) +
geom_smooth()}
map2(.x = mygrid$x,
.y = mygrid$y,
.f = ~ plot_my_data(mydf, .x,.y))
You have 2 issues in your code.
First one is that you use aes where you should use aes_string, and second is that you have factors and not characters in mygrid.
This works:
mygrid <- expand.grid(x=mynames, y =mynames,stringsAsFactors = F)
# Define function
plot_my_data <- function(mydata, x, y){
ggplot(mydata, aes_string(x, y)) +
geom_smooth()}
map2(.x = mygrid$x,
.y = mygrid$y,
.f = ~ plot_my_data(mydf, .x,.y))

Variable column names in the pipe

I have the following code:
install.packages('tidyverse')
library(tidyverse)
x <- 1:10
y <- x^2
df <- data.frame(first_column = x, second_column = y)
tibble <- as_tibble(df)
tibble %>%
filter(second_column != 16) %>%
ggplot(aes(x = first_column, y = second_column)) +
geom_line()
Now I would like to create the following function
test <- function(colname) {
tibble %>%
filter(colname != 16) %>%
ggplot(aes(x = first_column, y = colname)) +
geom_line()
}
test('second_column')
But running it creates a vertical line instead of the function. How can I make this function work?
Edit: My focus is on getting the pipe to work, not ggplot.
In order to pass character strings for variable names, you have to use the standard evaluation version of each function. It is aes_string for aes, and filter_ for filter. See the NSE vignette for more details.
Your function could look like:
test <- function(colname) {
tibble %>%
filter_(.dots= paste0(colname, "!= 16")) %>%
ggplot(aes_string(x = "first_column", y = colname)) +
geom_line()
}

Resources