I need to convert a ggplot that has two x variables to mschart so that I can work with it in Word. I used to produce these in Excel, but we are moving to R for stats. Here's what it looks like in ggplot2:
In ggplot2, I just pass the x variable as interaction('Gender','JO_Type'). When I try to pass this to mschart, it returns this error message:
x %in% names(data) is not TRUE
I have tried converting the data to an array and vector, but no luck. I am not even sure mscharts can handle a two-dimensional x argument.
Data:
gender_category_JOType = structure(list(JO_Type = c("Standard" ,"Standard","Standard","Standard","Standard","Standard","Standard","Standard","Continuous","Continuous","Continuous","Continuous","Continuous","Continuous","Continuous","Continuous"),
Category = c("FS","FS","P1-P4","P1-P4","P5-P6","P5-P6","D1","D1","FS","FS","P1-P4","P1-P4","P5-P6","P5-P6","D1","D1"),
Gender = c("Female","Male","Female","Male","Female","Male","Female","Male","Female","Male","Female","Male","Female","Male","Female","Male"),
count = c(76,144,668,697,173,305,61,110,514,1214,264,504,46,130,18,41),
percentage=c("34.5%","65.5%","48.9%","51.1%","36.2%","63.8%","35.7%","64.3%","29.7%","70.3%","34.4%","65.6%","26.1%","73.9%","30.5%","69.5%")), row.names = c(1:16), class = "data.frame")
Code:
library(tidyverse)
library(magrittr)
library(mschart)
library(officer)
# the data for gender_category_JOType
print(gender_category_JOType)
# ggplot function
myBarChart_3 <- function(data,var1,var2,var3,count,title,xLabel,yLabel){
ggplot(data,
aes_string(x=var1, y=count, fill=var2)) +
ggtitle(title) +
geom_bar(stat = 'identity',width=1.15,
position = position_dodge2(padding=0.15, reverse=FALSE, preserve=c("single"))) +
geom_text(aes(label=count),
vjust=-.5, position=position_dodge2(width=1.15), size=2.5) +
scale_y_continuous(sec.axis=waiver(),
expand = expansion(mult = c(0,0.05))) +
facet_wrap(var3, nrow=1,strip.position="bottom",scales = "free_x") +
xlab(xLabel) +
ylab(yLabel)
}
myBarChart_3(gender_category_JOType, interaction('Gender','JO_Type'), 'Category',
c('JO_Type','Gender'), 'count', "Category, Gender, and JO Type",
"level", "total")# +
#geom_text(aes(label = percentage), vjust=-1.75, hjust='center',
# position=position_dodge2(width=1.15), size=2.5)
# the msbarchart version
# add the chart to an existing Word doc
gen_docx <- function(chart,file,file2){
doc <- read_docx()
doc <- body_add_par(doc, " ", style = "Normal", pos = "after")
doc <- body_add_chart(doc, chart = chart, style = "Normal", pos="after")
doc <- body_add_break(doc, pos="after")
doc <- body_add_par(doc, " ", style = "Normal", pos = "after")
doc <- body_add_par(doc, " ", style = "Normal", pos = "after")
doc <- body_add_docx(doc, src = file2, pos = "before")
doc <- print(doc, target = file)
}
# create the chart function
my_msbarchart_doc <- function(srcFile, docName, chartName, data, var, count,
grouping, title, xLabel, yLabel){
chartName <- ms_barchart(data, x = var, y = count, group = grouping) %>%
chart_labels(title = title, xlab = xLabel, ylab = yLabel)
chartName <- chart_data_labels(chartName, position="outEnd", show_val = T)
doc <- gen_docx(chartName, paste0(docName,".docx"), paste0(srcFile,".docx"))
}
# create the chart
lion <- my_msbarchart_doc("myDocument2", "myDocument3", lion, gender_category_JOType,
interaction('Gender','JO_Type'), "count", "Category",
"Category, JO Type, and Gender", "Gender and JO Type","count")
# error msg caused by the interaction that works in the ggplot but not in mschart: x %in% names(data) is not TRUE
Related
I don't quite understand why the legend disappeared when I converted a plot made by ggplot to plotly using ggplotly. The plotly help page did not have any information. I don't think their examples even worked properly on that page.
Any help is greatly appreciated!
Sample data
library(scales)
packageVersion("ggplot2")
#> [1] '3.4.0'
library(plotly)
packageVersion("plotly")
#> [1] '4.10.1'
data <- data.frame(
stringsAsFactors = FALSE,
Level = c("Fast","Fast","Fast","Fast",
"Fast","Fast","Slow","Slow","Slow",
"Slow","Slow","Slow"),
Period = c("1Year","3Month","1Year","3Month",
"1Year","3Month","1Year","3Month",
"1Year","3Month","1Year","3Month"),
X = c(0.002,0.002,0.1,0.1,0.9,0.9,
0.002,0.002,0.1,0.1,0.9,0.9),
Y = c(1.38,1.29,1.61,1.61,1.74,0.98,
1.14,0.97,1.09,1.1,0.94,0.58)
)
ggplot2
plt <- ggplot(data = data,
aes(x = X,
y = Y,
shape = Period,
color = Level)) +
geom_point(alpha = 0.6, size = 3) +
labs(x = " ",
y = "Value") +
scale_y_continuous(labels = number_format(accuracy = 0.1)) +
guides(color = guide_legend(title = "Level", order = 1),
shape = guide_legend(title = "Period", order = 2)) +
theme(axis.text.x = element_text(angle = 90))
plt
Convert to plotly, legend disappeared
ggplotly(plt, height = 500) %>%
layout(xaxis = list(autorange = "reversed"))
Edit
There was an issue with guides(). If I removed it, the legend in ggplotly showed up
plt2 <- ggplot(data = data,
aes(x = X,
y = Y,
shape = Period,
color = Level)) +
geom_point(alpha = 0.6, size = 3) +
labs(x = " ",
y = "Value") +
scale_y_continuous(labels = number_format(accuracy = 0.1)) +
theme(axis.text.x = element_text(angle = 90))
plt2
ggplotly(plt2, height = 500) %>%
layout(
xaxis = list(autorange = "reversed"),
legend = list(
title = list(text = '(Period, Level)'))
)
After OPs Edit:
Here is a workaround using basic R {plotly} to modify the legend according to #Tung's requirements:
library(scales)
library(ggplot2)
library(plotly)
library(data.table)
DT <- data.frame(
stringsAsFactors = FALSE,
Level = c("Fast","Fast","Fast","Fast",
"Fast","Fast","Slow","Slow","Slow",
"Slow","Slow","Slow"),
Period = c("1Year","3Month","1Year","3Month",
"1Year","3Month","1Year","3Month",
"1Year","3Month","1Year","3Month"),
X = c(0.002,0.002,0.1,0.1,0.9,0.9,
0.002,0.002,0.1,0.1,0.9,0.9),
Y = c(1.38,1.29,1.61,1.61,1.74,0.98,
1.14,0.97,1.09,1.1,0.94,0.58)
)
setDT(DT)
LevelDT <- unique(DT, by = "Level")
PeriodDT <- unique(DT, by = "Period")
LevelDT[, Y := min(DT$Y)-1]
PeriodDT[, Y := min(DT$Y)-1]
plt2 <- ggplot(data = DT,
aes(x = X,
y = Y,
shape = Period,
color = Level)) +
geom_point(alpha = 0.6, size = 3) +
labs(x = " ",
y = "Value") +
scale_y_continuous(labels = number_format(accuracy = 0.1)) +
theme(axis.text.x = element_text(angle = 90))
plt2
markercolors <- hue_pal()(2)
ggplotly(plt2, height = 500) |>
layout(
xaxis = list(autorange = "reversed"),
legend = list(
title = list(text = ''),
itemclick = FALSE,
itemdoubleclick = FALSE,
groupclick = FALSE
)
) |>
add_trace(
data = LevelDT,
x = ~ X,
y = ~ Y,
inherit = FALSE,
type = "scatter",
mode = "markers",
marker = list(
color = markercolors,
size = 14,
opacity = 0.6,
symbol = "circle"
),
name = ~ Level,
legendgroup = "Level",
legendgrouptitle = list(text = "Level")
) |>
add_trace(
data = PeriodDT,
x = ~ X,
y = ~ Y,
inherit = FALSE,
type = "scatter",
mode = "markers",
marker = list(
color = "darkgrey",
size = 14,
opacity = 0.6,
symbol = c("circle", "triangle-up")
),
name = ~Period,
legendgroup = "Period",
legendgrouptitle = list(text = "Period")
) |> style(showlegend = FALSE, traces = 1:4)
Original answer:
I'm not sure why they are set to FALSE in the first place, but setting showlegend = TRUE in layout() and style() (for the traces) brings back the legend:
library(scales)
library(ggplot2)
library(plotly)
data <- data.frame(
stringsAsFactors = FALSE,
Level = c("Fast","Fast","Fast","Fast",
"Fast","Fast","Slow","Slow","Slow",
"Slow","Slow","Slow"),
Period = c("1Year","3Month","1Year","3Month",
"1Year","3Month","1Year","3Month",
"1Year","3Month","1Year","3Month"),
X = c(0.002,0.002,0.1,0.1,0.9,0.9,
0.002,0.002,0.1,0.1,0.9,0.9),
Y = c(1.38,1.29,1.61,1.61,1.74,0.98,
1.14,0.97,1.09,1.1,0.94,0.58)
)
# ggplot2
plt <- ggplot(data = data,
aes(x = X,
y = Y,
shape = Period,
color = Level)) +
geom_point(alpha = 0.6, size = 3) +
labs(x = " ",
y = "Value") +
scale_y_continuous(labels = number_format(accuracy = 0.1)) +
guides(color = guide_legend(title = "Period", order = 1),
shape = guide_legend(title = "", order = 2)) +
theme(axis.text.x = element_text(angle = 90))
plt
# Convert to plotly, legend disappeared
fig <- ggplotly(plt, height = 500) %>%
layout(showlegend = TRUE, xaxis = list(autorange = "reversed")) %>%
style(showlegend = TRUE)
fig
This answer is for plotly 4.10.1. I have defined two functions:
set_legend_names() This edits the names of the htmlwidget created by ggplotly(), before it is passed to plotly.js.
set_legend_symbols(). This appends some js to the htmlwidget object to change the symbols after plotly.js has drawn them.
plt2 |>
ggplotly(height = 500) |>
layout(xaxis = list(autorange = "reversed")) |>
set_legend_names() |>
set_legend_symbols()
Function definitions:
1. set_legend_names()
set_legend_names <- function(p,
new_legend_names = c(
"Fast", "Slow", "One Year", "Three Month"
)) {
# Update legend names and put in one group
for (i in seq_along(p$x$data)) {
p$x$data[[i]]$name <- new_legend_names[i]
}
p$x$layout$legend$title <- ""
return(p)
}
2. set_legend_symbols()
set_legend_symbols <- function(p,
symbol_nums_change_color = c(3, 4),
new_color_string = "rgb(105, 105, 105)",
symbols_num_change_shape = 3,
symbols_nums_target_shape = 1) {
js_get_legend <- htmltools::HTML(
'let legend = document.querySelector(".scrollbox");
let symbols = legend.getElementsByClassName("legendsymbols");
const re = new RegExp("fill: rgb.+;", "i");\n
'
)
js_symbol_const <- paste0(
'const shape_re = new RegExp(\'d=".*?"\');\n',
"const correct_shape = symbols[",
symbols_nums_target_shape,
"].innerHTML.match(shape_re)[0];\n"
)
# subtract 1 for 0-indexed js
change_symbol_color_code <- lapply(
symbol_nums_change_color - 1,
\(i)
paste0(
"symbols[", i, "].innerHTML = ",
"symbols[", i, "].innerHTML.replace(re,",
' "fill: ', new_color_string, ';");'
)
) |>
paste(collapse = "\n")
# subtract 1 for 0-indexed js
change_symbols_shape_code <- lapply(
symbols_num_change_shape - 1,
\(i)
paste0(
"symbols[", i, "].innerHTML = symbols[",
symbols_nums_target_shape, "].innerHTML.replace(shape_re, correct_shape);"
)
) |>
paste(collapse = "\n")
all_js <- htmltools::HTML(
unlist(c(
js_get_legend,
js_symbol_const,
change_symbols_shape_code,
change_symbol_color_code
))
)
# Add it to the plot
p <- htmlwidgets::prependContent(
p,
htmlwidgets::onStaticRenderComplete(all_js)
)
return(p)
}
I've never posted a second answer before but it seems substantially different in plotly 4.10.1. I eagerly anticipate the release of plotly 4.10.2 so I can post a third answer.
Plotly generates a different legend from ggplot2 - this can be fixed with R and and a little javascript
The first thing to do is ensure that you have a reasonably current version of the packages:
packageVersion("ggplot2") # 3.4.0
packageVersion("plotly") # 4.10.0
With these versions, like #Quentin, I do get a legend, although it is different to the one generated by ggplot2.
ggplotly(plt, height = 500) %>%
layout(xaxis = list(autorange = "reversed"))
Steps to replicate the ggplot2 legend:
Change the legend text. This can be done by editing the R object before it is passed to plotly.js.
Remove the color from the shape guide. This can only be done with javascript after the plot has rendered.
Change the third circle into a triangle. This also needs to be done in javascript.
Changing the legend text
To do this manually, we could do p$x$data[[1]]$name <- "Fast", and replicate for each layer.
Fortunately, you have manually specified the legend order, making it easy to know where to access the correct legend names before passing to plotly. If we just do this step, it will create a legend which looks like this, i.e. still wrong (the first triangle should be a circle and neither should be have a color):
Changing the symbol shape and colors
We cannot do this in R. I have written an R helper function to generate some javascript to do this for us:
get_symbol_change_js <- function(symbol_nums,
new_color_string = "rgb(105, 105, 105)") {
js_get_legend <- htmltools::HTML(
'let legend = document.querySelector(".scrollbox");
let symbols = legend.getElementsByClassName("legendsymbols");
const re = new RegExp("fill: rgb.+;", "i");
'
)
change_symbol_color_code <- lapply(
symbol_nums,
\(i)
paste0(
"symbols[", i, "].innerHTML = ",
"symbols[", i, "].innerHTML.replace(re,",
' "fill: ', new_color_string, ';");'
)
) |>
paste(collapse = "\n")
# shape to change
shape_change_num <- symbol_nums[1]
# shape to replace with
shape_change_from <- shape_change_num - 1
change_symbols_shape_code <- paste0(
'const shape_re = new RegExp(\'d=".*?"\');\n',
"const correct_shape = symbols[", shape_change_from, "].innerHTML.match(shape_re)[0];\n",
"symbols[2].innerHTML = symbols[", shape_change_num, "].innerHTML.replace(shape_re, correct_shape);"
)
all_js <- htmltools::HTML(
unlist(c(
js_get_legend,
change_symbol_color_code,
change_symbols_shape_code
))
)
return(all_js)
}
We can put this all together to generate the plot as desired:
draw_plotly_with_legend(plt)
Final draw_plotly_with_legend() function
Note this function calls get_symbol_change_js(), as defined above. It also uses htmlwidgets::prependContent() to attach our custom html to the widget before rendering.
draw_plotly_with_legend <- function(gg = plt,
guide_types = c("colour", "shape")) {
# Period, Level
legend_categories <- lapply(
guide_types, \(x) rlang::quo_get_expr(plt$mapping[[x]])
)
new_legend_names <- lapply(legend_categories, \(category) {
unique(data[[category]])
}) |> setNames(guide_types)
# Work out which symbols need to have color removed
symbols_to_remove_color <- new_legend_names[
names(new_legend_names) != "colour"
] |> unlist()
new_legend_names <- unlist(new_legend_names)
symbol_num_remove_color <- which(
new_legend_names %in% symbols_to_remove_color
)
# Create plot
p <- ggplotly(gg, height = 500) %>%
layout(xaxis = list(autorange = "reversed"))
# Show legend
p$x$layout$showlegend <- TRUE
# Update legend names and put in one group
for (i in seq_along(p$x$data)) {
p$x$data[[i]]$name <- new_legend_names[i]
p$x$data[[1]]$legendgroup <- "Grouped legend"
}
# Get the js code to change legend color
# js is 0 indexed
js_symbol_nums <- symbol_num_remove_color - 1
js_code <- get_symbol_change_js(js_symbol_nums)
# Add it to the plot
p <- htmlwidgets::prependContent(
p,
htmlwidgets::onStaticRenderComplete(js_code)
)
return(p)
}
I have this data frame where I want to create multiple plots at the same time in a loop, but when I run the code it gives me an error. Can anyone please tell me what I am doing wrong!
Data:
structure(list(Date = structure(c(289094400, 297043200, 304992000,
312854400, 320716800, 328665600), tzone = "UTC", class = c("POSIXct",
"POSIXt")), NORTH = c(4.06976744186047, 5.51675977653633, 7.2799470549305,
4.75015422578655, 4.59363957597172, 3.15315315315317), YORKSANDTHEHUMBER = c(4.0121120363361,
5.45851528384282, 9.52380952380951, 6.04914933837431, 3.03030303030299,
5.42099192618225), NORTHWEST = c(6.57894736842105, 6.95256660168939,
6.50060753341436, 5.5904164289789, 4.59211237169096, 4.70041322314051
), EASTMIDS = c(4.98489425981872, 8.20143884892085, 6.91489361702127,
5.22388059701494, 5.61465721040189, 4.64465584778958), WESTMIDS = c(4.65838509316771,
4.74777448071216, 8.66855524079319, 6.56934306569344, 3.22896281800389,
3.17535545023698), EASTANGLIA = c(6.74525212835624, 8.58895705521476,
8.47457627118643, 10.7291666666667, 4.8447789275635, 4.84522207267835
), OUTERSEAST = c(6.7110371602884, 7.53638253638255, 9.47317544707589,
8.56512141280351, 3.82269215128102, 2.11515863689776), OUTERMET = c(4.54545454545458,
6.58505698607005, 7.36633663366336, 7.08225746956843, 4.3747847054771,
1.68316831683168), LONDON = c(8.11719500480309, 10.3065304309196,
6.32299637535239, 7.65151515151515, 1.30190007037299, 2.1535255296978
), SOUTHWEST = c(6.17577197149644, 7.71812080536912, 7.63239875389407,
9.45489628557649, 2.46804759806079, 2.19354838709679), WALES = c(6.09418282548476,
8.35509138381203, 7.40963855421687, 7.01065619742007, 1.15303983228513,
3.47150259067357), SCOTLAND = c(5.15222482435597, 4.12026726057908,
5.40106951871658, 8.67579908675796, -0.280112044817908, 2.94943820224719
), NIRELAND = c(4.54545454545454, 4.94752623688156, 4.42857142857145,
2.96397628818967, 6.06731620903454, 0.0835073068893502), UK = c(5.76890543055322,
7.20302836425676, 7.39543442582184, 7.22885986848197, 3.23472252213347,
2.95766398929048)), row.names = c(NA, -6L), class = c("tbl_df",
"tbl", "data.frame"))
Code:
for (i in 2:ncol(data2)) { # Printing ggplot within for-loop
print(ggplot(data2, aes(x = Date, y = data2[, i])) + # Basic ggplot2 plot of x & y's
geom_line() +
labs(title = "Uk States",
y = "",
x = "") +
theme_bw() +
geom_hline(yintercept = 0))
Sys.sleep(1)
}
Error:
Don't know how to automatically pick scale for object of type tbl_df/tbl/data.frame. Defaulting to continuous.
Error in is.finite(x) : default method not implemented for type 'list'
I would suggest to loop over the column names instead of value. You may then use .data to use as y-index.
library(tidyverse)
for(i in names(data2)[-1]) { # Printing ggplot within for-loop
# Basic ggplot2 plot of x & y's
print(ggplot(data2, aes(x = Date, y = .data[[i]])) +
geom_line()+ labs(title = "Uk States",
y = "",
x = "")+
theme_bw()+
geom_hline(yintercept = 0))
Sys.sleep(1)
}
You may also try facet_wrap to combine multiple plots together.
data2 %>%
pivot_longer(cols = -Date) %>%
ggplot(aes(Date, value)) +
geom_line() + facet_wrap(~name) +
labs(title = "Uk States", x = "", y = "") +
theme_bw() +
geom_hline(yintercept = 0)
Another way of generating ggplot in a loop is to use lapply, where we loop for colnames and use aes_string as the aesthetic mapping.
Here the results are saved to the list ggplot_list, where you can extract individual plot by indexing (e.g. plot for NORTH is stored in ggplot_list[[1]])
Note that I've changed labs(title = i) so that the plot title would be your column names.
library(ggplot2)
ggplot_list <- lapply(colnames(data2[-1]), \(i) {
ggplot(data2, aes_string("Date", x)) +
geom_line() +
labs(title = i, y = "", x = "") +
theme_bw() +
geom_hline(yintercept = 0)
})
[enter image description here][1]I am trying to create a lowry plot in R but am having difficulty debugging the errors returned. I am using the following code to create the plot:
library(ggplot2)
library(reshape)
m_xylene_data <- data.frame(
Parameter = c(
"BW", "CRE", "DS", "KM", "MPY", "Pba", "Pfaa",
"Plia", "Prpda", "Pspda", "QCC", "QfaC", "QliC",
"QPC", "QspdC", "Rurine", "Vfac", "VliC", "Vmax"),
"Main Effect" = c(
1.03E-01, 9.91E-02, 9.18E-07, 3.42E-02, 9.27E-3, 2.82E-2, 2.58E-05,
1.37E-05, 5.73E-4, 2.76E-3, 6.77E-3, 8.67E-05, 1.30E-02,
1.19E-01, 4.75E-04, 5.25E-01, 2.07E-04, 1.73E-03, 1.08E-03),
Interaction = c(
1.49E-02, 1.43E-02, 1.25E-04, 6.84E-03, 3.25E-03, 7.67E-03, 8.34E-05,
1.17E-04, 2.04E-04, 7.64E-04, 2.84E-03, 8.72E-05, 2.37E-03,
2.61E-02, 6.68E-04, 4.57E-02, 1.32E-04, 6.96E-04, 6.55E-04
)
)
fortify_lowry_data <- function(data,
param_var = "Parameter",
main_var = "Main.Effect",
inter_var = "Interaction")
{
#Convert wide to long format
mdata <- melt(data, id.vars = param_var)
#Order columns by main effect and reorder parameter levels
o <- order(data[, main_var], decreasing = TRUE)
data <- data[o, ]
data[, param_var] <- factor(
data[, param_var], levels = data[, param_var]
)
#Force main effect, interaction to be numeric
data[, main_var] <- as.numeric(data[, main_var])
data[, inter_var] <- as.numeric(data[, inter_var])
#total effect is main effect + interaction
data$.total.effect <- rowSums(data[, c(main_var, inter_var)])
#Get cumulative totals for the ribbon
data$.cumulative.main.effect <- cumsum(data[, main_var])
data$.cumulative.total.effect <- cumsum(data$.total.effect)
#A quirk of ggplot2 means we need x coords of bars
data$.numeric.param <- as.numeric(data[, param_var])
#The other upper bound
#.maximum = 1 - main effects not included
data$.maximum <- c(1 - rev(cumsum(rev(data[, main_var])))[-1], 1)
data$.valid.ymax <- with(data,
pmin(.maximum, .cumulative.total.effect)
)
mdata[, param_var] <- factor(
mdata[, param_var], levels = data[, param_var]
)
list(data = data, mdata = mdata)
}
lowry_plot <- function(data,
param_var = "Parameter",
main_var = "Main.Effect",
inter_var = "Interaction",
x_lab = "Parameters",
y_lab = "Total Effects (= Main Effects + Interactions)",
ribbon_alpha = 0.5,
x_text_angle = 25)
{
#Fortify data and dump contents into plot function environment
data_list <- fortify_lowry_data(data, param_var, main_var, inter_var)
list2env(data_list, envir = sys.frame(sys.nframe()))
p <- ggplot(data) +
geom_bar(aes_string(x = param_var, y = "value", fill = "variable"),
data = mdata) +
geom_ribbon(
aes(x = .numeric.param, ymin = .cumulative.main.effect, ymax =
.valid.ymax),
data = data,
alpha = ribbon_alpha) +
xlab(x_lab) +
ylab(y_lab) +
scale_y_continuous(labels = "percent") +
theme(axis.text.x = text(angle = x_text_angle, hjust = 1)) +
scale_fill_grey(end = 0.5) +
theme(legend.position = "top",
legend.title =blank(),
legend.direction = "horizontal"
)
p
}
m_xylene_lowry <- lowry_plot(m_xylene_data)
When I run the code, it is giving me the following error:
Error: argument "x" is missing, with no default
It is not specific enough for me to know what the issue is. What is causing the error to be displayed and how can I make error statements more verbose?
Lowry PLOT
It seems that you have more than one faulty element in your code than just the error it throws. In my experience it always helps to first check whether the code works as expected before putting it into a function. The plotting-part below should work:
p <- ggplot(data) + # no need to give data here, if you overwrite it anyway blow, but does not affect outcome...
# geom_bar does the counting but does not take y-value. Use geom_col:
geom_col(aes_string(x = param_var, y = "value", fill = "variable"),
data = mdata,
position = position_stack(reverse = TRUE)) +
geom_ribbon(
aes(x = .numeric.param, ymin = .cumulative.main.effect, ymax =
.valid.ymax),
data = data,
alpha = ribbon_alpha) +
xlab(x_lab) +
ylab(y_lab) +
# use scales::percent_format():
scale_y_continuous(labels = scales::percent_format()) +
# text is not an element you can use here, use element_text():
theme(axis.text.x = element_text(angle = x_text_angle, hjust = 1)) +
scale_fill_grey(end = 0.5) +
# use element_blank(), not just blank()
theme(legend.position = "top",
legend.title = element_blank(),
legend.direction = "horizontal"
)
This at least plots something, but I'm not sure whether it is what you expect it to do. It would help if you could show the desired output.
Edit:
Added position = position_stack(reverse = TRUE) to order according to sample plot.
I have written a function to load spatial data, extract data from an input dataset and merge this dataset with the spatial data. Then my function returns a map on which my cases get plotted.
My function works fine if I return my plot as the following:
(with fill = totalCases)
return ({
ggplot() +
geom_polygon(data = sl_adm2_Month, aes(x = long, y = lat, group = group,
fill = totalCases), colour = "white") +
geom_text(data = sl_adm2_months_names_DF, aes(label = NAME_2, x = long.1, y = lat.2, group = NAME_2), size = 3) +
# labs(title = paste("Ebola", str_sub(as.character(variable), 6, -1), "cases by district in Sierra Leone - until", format(as.Date(date), "%B %Y"))) +
xlab("") +
ylab("") +
theme_gray() +
theme(legend.position = "bottom")
})
However, my goal is to pass a parameter providing the value (= variable) for the fill parameter as you can see in my below code. But this throws the following error:
Error in eval(expr, envir, enclos) : object 'variable' not found
Here is my code:
plotMonths <- function(data, variable, date) {
# Reloading district polygons
sl_adm2_months <- readOGR("C:/Users/woba/Documents/Ordina/TFS-Projects/Ordina - Mail Analytics/Johnson/Wouter/03. GeoData map - R/Sierra Leone adm2", "SLE_adm2", verbose = TRUE, stringsAsFactors = FALSE)
sl_adm2_months_DF <- fortify(sl_adm2_months, region = "NAME_2")
# Getting the correct District names
colnames(sl_adm2_months_DF)[7] <- "District"
sl_adm2_months_DF$District <- ifelse(sl_adm2_months_DF$District == "Western Rural", "Western Area Rural", as.character(sl_adm2_months_DF$District))
sl_adm2_months_DF$District <- ifelse(sl_adm2_months_DF$District == "Western Urban", "Western Area Urban", as.character(sl_adm2_months_DF$District))
sl_adm2_months_DF$District <- as.factor(sl_adm2_months_DF$District)
#Extracting district names for plotting
sl_adm2_months_names_DF <- data.frame(long = coordinates(sl_adm2_months[, 1]), lat = coordinates(sl_adm2_months[, 2]))
sl_adm2_months_names_DF[, "ID_2"] <- sl_adm2_months#data[, "ID_2"]
sl_adm2_months_names_DF[, "NAME_2"] <- sl_adm2_months#data[, "NAME_2"]
# Subset May data
sl_Month <- data[data$Country == "Sierra Leone" & data$Date <= as.Date(date), ]
sl_Month <- droplevels(sl_Month)
sl_Month[is.na(sl_Month)] <- 0
confirmed <- ddply(sl_Month, .(Localite), function(x){max(x$cmlConfirmed.cases, na.rm = T)})
cases <- ddply(sl_Month, .(Localite), function(x){max(x$cmlCases, na.rm = T)})
deaths <- ddply(sl_Month, .(Localite), function(x){max(x$cmlDeaths, na.rm = T)})
sl_Month <- merge(cases, deaths, by = "Localite")
sl_Month <- merge(sl_Month, confirmed, by = "Localite")
sl_Month <- droplevels(sl_Month)
sl_Month <- droplevels(sl_Month)
colnames(sl_Month)<- c("District", "totalCases", "totalDeaths", "totalConfirmed")
sl_Month <- sl_Month[-which(sl_Month$District == "National"),]
# Merging Month data with District polygons
sl_adm2_Month <- merge(sl_adm2_months_DF, sl_Month, by = "District", all.x = TRUE)
sl_adm2_Month$totalCases <- as.numeric(sl_adm2_Month$totalCases)
sl_adm2_Month$totalDeaths <- as.numeric(sl_adm2_Month$totalDeaths)
sl_adm2_Month$totalConfirmed <- as.numeric(sl_adm2_Month$totalConfirmed)
#NA to 0 for values missing for districts
sl_adm2_Month[is.na(sl_adm2_Month)] <- 0
#Sorting
sl_adm2_Month <- sl_adm2_Month[order(sl_adm2_Month$District, sl_adm2_Month$order), ]
# Prints & Views
print(head(sl_Month))
View(sl_Month)
View(sl_adm2_Month)
Sys.setlocale("LC_TIME", "English")
# Plotting Cases
return ({
ggplot() +
geom_polygon(data = sl_adm2_Month, aes(x = long, y = lat, group = group,
fill = variable), colour = "white") +
geom_text(data = sl_adm2_months_names_DF, aes(label = NAME_2, x = long.1, y = lat.2, group = NAME_2), size = 3) +
# labs(title = paste("Ebola", str_sub(as.character(variable), 6, -1), "cases by district in Sierra Leone - until", format(as.Date(date), "%B %Y"))) +
xlab("") +
ylab("") +
theme_gray() +
theme(legend.position = "bottom")
})
}
# Plotting the months - variable = second input and must be IN c(totalDeaths, totalCases, totalConfirmed)
plotMonths(final_dataset, "totalCases", "2014-05-31")
I've read some similar questions on the forum but wasn't able to resolve my issue.
Any help on how to fix this is very welcome!
Using 'aes_string' instead of 'aes' solved my issue.
aes_string(x = "long", y = "lat", group = "group", fill = variable)
Explanation on the differences between aes & aes_string for the ggplot2 package can be found here:
What is the difference between aes and aes_string (ggplot2) in R
All credit goes to Axeman & Benjamin - their answers solved my issue!
Alright, this has got me stumped. I have this function:
tf <- function(formula = NULL, data = NULL) {
res <- as.character(formula[[2]])
fac2 <- as.character(formula[[3]][3])
fac1 <- as.character(formula[[3]][2])
# Aesthetic & Data 1
p <- ggplot(aes_string(x = fac1, y = res, color = fac1), data = data) +
facet_grid(paste(".~", fac2)) + geom_point() # OK if we only go this far
facCounts <- count(data, vars = c(fac2, fac1))
facCounts$label <- paste("n = ", facCounts$freq , sep = "")
facCounts$y <- min(data$res) - 0.1*diff(range(data$res))
facCounts <- facCounts[,-3]
names(facCounts) <- c("f2", "f1", "lab", "y") # data frame looks correct
# Aesthetic & Data 2
p <- p + geom_text(aes(x = f1, y = y, label = lab),
color = "black", size = 4.0, data = facCounts) + facet_grid(".~f2")
p
}
Which when run with this data and call:
set.seed(1234)
mydf <- data.frame(
resp = rnorm(40),
cat1 = sample(LETTERS[1:3], 40, replace = TRUE),
cat2 = sample(letters[1:2], 40, replace = TRUE))
p <- tf(formula = resp~cat1*cat2, data = mydf); print(p)
Produces this picture:
If you look carefully, you'll see that the data in the two facets are actually the same. The counts are correct for that data that should be displayed (and is stored in facCounts). If the call to geom_text is commented out, then the plot is correct. A variety of changes to the geom_text call leave me with either what you see above, or the correct data is present but the count texts overlap. I can't find the way out of this labyrinth! An attempt with + annotate("text", ...) doesn't work either. What change is needed to keep the data faceted and the counts correct? Thanks. This is ggplot 0.9.3 btw.
Now that I've convinced myself that this will work:
tf <- function(formula = NULL, data = NULL) {
res <- as.character(formula[[2]])
fac2 <- as.character(formula[[3]][3])
fac1 <- as.character(formula[[3]][2])
# Aesthetic & Data 1
p <- ggplot(aes_string(x = fac1, y = res, color = fac1), data = data) +
facet_grid(paste(".~", fac2)) + geom_point() # OK if we only go this far
facCounts <- count(data, vars = c(fac2, fac1))
facCounts$label <- paste("n = ", facCounts$freq , sep = "")
facCounts$y <- min(data$res) - 0.1*diff(range(data$res))
facCounts <- facCounts[,-3]
names(facCounts) <- c("cat2", "f1", "lab", "y") # data frame looks correct
# Aesthetic & Data 2
p <- p + geom_text(aes(x = f1, y = y, label = lab),
color = "black", size = 4.0, data = facCounts)
p
}
You were calling facet_grid a second time using a differently named faceting variable. Removing the second call and renaming f2 to `cat2 seems to work.