saving_ggplot <- function(name = 'default', plotname = last_plot()) {
image_name = paste(name, ".png", sep="")
ggsave(image_name, plot = plotname,
scale = 1,
dpi = 300, limitsize = TRUE)
}
This is my function which saves a ggplot. However, I for the life of me cannot figure out how to take the name argument as a string.
for example if someone runes saving_ggplot(FILENAME, PLOTNAME)
it will just say no object FILENAME. In python I can just capture it and use it as str(), but using as.character or toString in R still doesn't work.
Error:
saving_ggplot(weightvsageTEST, weightvsageplot)
Error in paste(name, ".png", sep = "") :
object 'weightvsageTEST' not found
Successful call using ggsave:
ggsave('weightvsage.png', plot = last_plot(),
scale = 1,
dpi = 300, limitsize = TRUE)
You can use substitute():
saving_ggplot <- function(name, plotname) {
image_name = paste0(substitute(name), ".png") # paste0 removes need for sep arg
ggsave(image_name, plot = plotname,
scale = 1,
dpi = 300, limitsize = TRUE)
}
saving_ggplot(foo, p) # saves foo.png
Alternately, if you want to stay within tidyverse quasiquotation syntax, use enexpr() instead:
enexpr(name) # instead of substitute(name)
Data:
N <- 100
df <- data.frame(x=rnorm(n=N), y=rnorm(n=N))
p <- ggplot(df, aes(x,y)) + geom_smooth()
Related
I am trying to modify an existing function by copy and pasting it to an R script, and assigning it to a new function object in my local environment. However the new function cannot find functions that are called to within the original function. How can I fix this without looking up and finding each function individually? I am guessing that the original function is somehow linked to the package or its dependencies and 'knows where to look' for the missing function, but I cannot figure out how to do this with my new copy-and-pasted function.
library("camtrapR")
Print the function name
activityDensity
The output here is the code for this function. I have omitted it here because it is long (and I have pasted it below), but I copy and paste the output of the function code exactly (see below where I assign this exact code to a new function), except for the last two lines of output, which I think are important:
<bytecode: 0x000000002a2d1e20>
<environment: namespace:camtrapR>
So now I assign the copy and pasted code from the output above to a new function with New <-
New <- function (recordTable, species, allSpecies = FALSE, speciesCol = "Species",
recordDateTimeCol = "DateTimeOriginal", recordDateTimeFormat = "%Y-%m-%d %H:%M:%S",
plotR = TRUE, writePNG = FALSE, plotDirectory, createDir = FALSE,
pngMaxPix = 1000, add.rug = TRUE, ...)
{
wd0 <- getwd()
mar0 <- par()$mar
on.exit(setwd(wd0))
on.exit(par(mar = mar0), add = TRUE)
recordTable <- dataFrameTibbleCheck(df = recordTable)
timeZone <- "UTC"
checkForSpacesInColumnNames(speciesCol = speciesCol, recordDateTimeCol = recordDateTimeCol)
if (!is.data.frame(recordTable))
stop("recordTable must be a data frame", call. = FALSE)
if (!speciesCol %in% colnames(recordTable))
stop(paste("speciesCol = \"", speciesCol, "\" is not a column name in recordTable",
sep = ""), call. = FALSE)
if (!recordDateTimeCol %in% colnames(recordTable))
stop(paste("recordDateTimeCol = \"", recordDateTimeCol,
"\" is not a column name in recordTable", sep = ""),
call. = FALSE)
stopifnot(is.logical(c(allSpecies, writePNG, plotR, createDir)))
if (allSpecies == FALSE) {
stopifnot(species %in% recordTable[, speciesCol])
stopifnot(hasArg(species))
}
recordTable$DateTime2 <- parseDateTimeObject(inputColumn = recordTable[,
recordDateTimeCol], dateTimeFormat = recordDateTimeFormat,
timeZone = timeZone)
recordTable$Time2 <- format(recordTable$DateTime2, format = "%H:%M:%S",
usetz = FALSE)
recordTable$Time.rad <- (as.numeric(as.POSIXct(strptime(recordTable$Time2,
format = "%H:%M:%S", tz = timeZone))) - as.numeric(as.POSIXct(strptime("0",
format = "%S", tz = timeZone))))/3600 * (pi/12)
if (isTRUE(writePNG)) {
if (hasArg(plotDirectory)) {
if (isTRUE(createDir)) {
dir.create(plotDirectory, recursive = TRUE, showWarnings = FALSE)
setwd(plotDirectory)
}
else {
stopifnot(file.exists(plotDirectory))
setwd(plotDirectory)
}
}
else {
stop("writePNG is TRUE. Please set plotDirectory",
call. = FALSE)
}
}
pngWidth <- pngMaxPix
pngHeight <- round(pngMaxPix * 0.8)
if (allSpecies == FALSE) {
subset_species <- subset(recordTable, recordTable[, speciesCol] ==
species)
if (nrow(subset_species) == 1)
stop(paste(species, "had only 1 record. Cannot estimate density."),
call. = FALSE)
try_error_tmp <- try({
if (isTRUE(writePNG))
png(filename = paste("activity_density_",
species, "_", Sys.Date(), ".png",
sep = ""), width = pngWidth, height = pngHeight,
units = "px", res = 96, type = "cairo")
if (isTRUE(writePNG) | isTRUE(plotR)) {
densityPlot(subset_species$Time.rad, main = paste("Activity of",
species), rug = add.rug, ...)
mtext(paste("number of records:", nrow(subset_species)),
side = 3, line = 0)
}
if (isTRUE(writePNG))
dev.off()
}, silent = TRUE)
if (class(try_error_tmp) == "try-error")
warning(paste(toupper(species), ": ", try_error_tmp[1],
" - SKIPPED", sep = ""), call. = FALSE)
}
else {
subset_species_list <- list()
for (i in 1:length(unique(recordTable[, speciesCol]))) {
spec.tmp <- unique(recordTable[, speciesCol])[i]
subset_species <- subset(recordTable, recordTable[,
speciesCol] == spec.tmp)
plot_main_title <- paste("Activity of", spec.tmp)
if (nrow(subset_species) == 1) {
warning(paste(toupper(spec.tmp), ": It had only 1 record. Cannot estimate density. - SKIPPED",
sep = ""), call. = FALSE)
next
}
else {
try_error_tmp <- try({
if (isTRUE(writePNG))
png(filename = paste("activity_density_",
spec.tmp, "_", Sys.Date(), ".png",
sep = ""), width = pngWidth, height = pngHeight,
units = "px", res = 96, type = "cairo")
if (isTRUE(writePNG) | isTRUE(plotR)) {
densityPlot(subset_species$Time.rad, main = plot_main_title,
rug = add.rug, ...)
mtext(paste("number of records:", nrow(subset_species)),
side = 3, line = 0)
}
if (isTRUE(writePNG))
dev.off()
}, silent = TRUE)
if (class(try_error_tmp) == "try-error")
warning(paste(toupper(spec.tmp), ": ",
try_error_tmp[1], " - SKIPPED",
sep = ""), call. = FALSE)
}
subset_species_list[[i]] <- subset_species$Time.rad
names(subset_species_list)[i] <- spec.tmp
}
}
if (allSpecies == FALSE) {
return(invisible(subset_species$Time.rad))
}
else {
return(invisible(subset_species_list))
}
}
Yet, when I try to run this new function (arguments omitted here for clarity), it can't find a function embedded within.
How can I somehow assign this function to look within the original package camtrapR for any dependencies, etc.? and why does the code output from the function not already do this?
New()
Error in dataFrameTibbleCheck(df = recordTable) :
could not find function "dataFrameTibbleCheck"
This answer here: https://stackoverflow.com/a/49277036/9096420 allows one to manually edit and save a function's code for each R session, but it is non-reproducible (not code) that can be shared or re-used.
If New is the new function copied from camtrapR then use
environment(New) <- asNamespace("camtrapR")
to ensure that the function calls in its body are looked up in the correct places.
I have generated several Utilisation Distributions (UD) with AdehabitatHR and stored them as Geotiffs. I am now using the same UDs with the Lattice package to generate some maps and saving them to a high-res tiff image with LZW compression. Problem is that I have literally hundreds of maps to make, save and name. Is there a way automatically do this once i have loaded all the necessary files from a directory? Each one of my UDs has the following structure of the filename "UD_resolution_species_area_year_season. tif" and in the final name I give to my map I would like to keep the same structure (or entire filename) but add the prefix "blablabla_" e.g. "blablabla_UD_resolution_species_area_year_season.tiff". The image also include a main name, a capital letter, which should also change.
At the moment I am using the following:
rlist = list.files(getwd(), pattern = "tif$", full.names = FALSE)
for (i in rlist) {
assign(unlist(strsplit(i, "[.]"))[1], raster(i))
}
shplist = list.files(getwd(), pattern = "shp$", full.names = FALSE)
for (i in shplist) {
assign(unlist(strsplit(i, "[.]"))[1], readOGR(i))
}
UD <- 'UD_resolution_species_area_year_season'
ext <- extent(UD) + 0.3 # set the extent for the plot
aa <-
quantile(UD,
probs = c(0.25, 0.75),
type = 8,
names = TRUE)
my.at <- c(aa[1], aa[2])
my.at <- round(my.at, 3)
maxval <- maxValue(UD)
tiff(
"C:/myworkingdirectory/maps/blablabla_UD_resolution_species_area_year_season.tiff",
res = 600,
compression = "lzw",
width = 15,
height = 15,
units = "cm"
)
levelplot(
UD,
xlab = "",
ylab = "",
xlim = c(ext[1], ext[2]),
ylim = c(ext[3], ext[4]),
margin = FALSE,
contour = FALSE,
col.regions = viridis(1000),
colorkey = list(at = seq(0, maxval)),
main = "A",
maxpixels = 2e5
) + latticeExtra::layer(sp.polygons(Land, fill = "grey50", col = NA)) + contourplot(
`UD`,
at = my.at[1],
labels = FALSE,
margin = FALSE,
lty = 2,
col = "orange",
pretty = TRUE
) + contourplot(
UD,
at = my.at[2],
labels = FALSE,
margin = FALSE,
lty = 2,
col = "red",
pretty = TRUE,
)
dev.off()
It is a common beginners mistake to use assign. Do not use it, it creates the type of trouble you are now facing. In stead, you can make lists and/or use a loop.
Also what you are asking is basic R stuff, but you are complicating the question with adding lots of irrelevant detail about setting the extent, and levelplot. It is better to learn about doing these basic things by removing the clutter and focus on a simple case first. That is also how you should write questions for this forum.
In essence you have a bunch of files you want to process. Below I show how you can loop over a vector of the names and then loop and do what you need to do in that loop.
library(raster)
rastfiles <- list.files(pattern = "tif$", full.names=TRUE)
outputfiles <- file.path("output/path", paste0("prefix_", basename(rastfiles)))
for (i in 1:length(rastfiles))
r <- raster(rastfiles[i])
png(outputfiles[i])
plot(r)
dev.off()
}
You can also first read all the files into a list
rastfiles <- list.files(pattern = "tif$", full.names=TRUE)
rlist <- lapply(rastfiles, raster)
names(rlist) <- gsub(".tif$", "", basename(rastfiles))
rastfiles <- list.files(pattern = "shp$", full.names=TRUE)
slist <- lapply(shpfiles, readOGR)
names(slist) <- gsub(".shp$", "", basename(shpfiles))
And perhaps create a vector of output filenames
outputtif <- file.path("output/dir", basename(rastfiles))
And then loop over the items in the list, or the output filenames
I have list of data and list of plots which I want two write to xlsx file (each element to separate sheet). Example data:
require(ggplot2)
require(data.table)
n <- 10
N <- 100
dtList <- lapply(1:n, function(x) data.table(sample(1e6, N), 1:N))
names(dtList) <- 1:n
plots <- lapply(dtList, function(x) ggplot(x, aes(y = V1, x = V2)) + geom_line())
Currently I use openxlsx, but it is quite slow for multiple plots:
require(openxlsx)
wb <- createWorkbook()
modifyBaseFont(wb, fontSize = 10)
writeXlsx <- function(x, sName) {
addWorksheet(wb, sName, gridLines = FALSE)
writeData(wb, sName, x = x, xy = c(1, 1))
print(plots[[sName]])
insertPlot(wb, sName, width = 19, height = 9, dpi = 200, units = "cm",
startRow = 2, startCol = 5)
}
system.time(
sapply(seq_along(dtList), function(x) {
writeXlsx(dtList[[x]], names(dtList)[[x]])
})
) # ~ 17.00 sek
openXL(wb)
How could I increase speed of this? Is there a better package to accomplish this?
One options is to use simpler graphics. For example, changing plots to base graphics, like:
plots <- lapply(dtList, function(x) plot(x$V2, x$V1, type = 'l'))
reduces the xlsx creation time to ~0.72 seconds vs ~7.78 seconds (original code works now faster than before), that is around 10 times faster.
When ggplot graphics are needed, I modified insertPlot function to accept this type of object and save it to file without needing to print in R session (using ggsave):
insertggPlot <- function(wb, sheet, width = 6, height = 4, xy = NULL,
startRow = 1, startCol = 1, fileType = "png",
units = "in", dpi = 300, PLOT) {
od <- getOption("OutDec")
options(OutDec = ".")
on.exit(expr = options(OutDec = od), add = TRUE)
if (!"Workbook" %in% class(wb)) stop("First argument must be a Workbook.")
if (!is.null(xy)) {
startCol <- xy[[1]]
startRow <- xy[[2]]
}
fileType <- tolower(fileType)
units <- tolower(units)
if (fileType == "jpg") fileType = "jpeg"
if (!fileType %in% c("png", "jpeg", "tiff", "bmp"))
stop("Invalid file type.\nfileType must be one of: png, jpeg, tiff, bmp")
if (!units %in% c("cm", "in", "px"))
stop("Invalid units.\nunits must be one of: cm, in, px")
fileName <- tempfile(pattern = "figureImage",
fileext = paste0(".", fileType))
ggsave(plot = PLOT, filename = fileName, width = width, height = height,
units = units, dpi = dpi)
insertImage(wb = wb, sheet = sheet, file = fileName, width = width,
height = height, startRow = startRow, startCol = startCol,
units = units, dpi = dpi)
}
Using this, reduces time to ~2 sek.
I want to save 300 plots with a for loop but somehow the code works in the console but no plots are saved. I always get the following error:
Error in plot_list[[i]] : subscript out of bounds
If I plot histograms everything works just fine.
Here's my code:
plot_list = list()
for (i in 1:300) {
p <-plot(matrix(1:15000, nrow = 15000, ncol = 50), datamatrix[1:15000,var_list[i,1]:var_list[i,2]], main = layer_list[[1]][i], xlab = "r [micrometer]")
plot_list[[i]] = p
}
for (i in 1:300) {
png(paste("plot", i, ".png", sep = ""), width = 1200, height = 750)
plot(plot_list[[i]], main = substitute(paste('Layer ', a), list(a=layer_list[[1]][i])), xlab = "r [micrometer]", ylab = " Frequency")
dev.off()
}
If I look at the plot_list, I get:
plot_list
list()
Can anyone help? Thank you!
Please try to provide datamatrix, or at least head(datamatrix).
As in the comments, you can do this is one loop and don't need to save objects to an intermediate list.
for (i in 1:300) {
png(paste("plot", i, ".png", sep = ""), width = 1200, height = 750)
plot(matrix(1:15000, nrow = 15000, ncol = 50),
datamatrix[1:15000, var_list[i, 1]:var_list[i, 2]],
main = sprintf("Layer %s", layer_list[[1]][i]),
xlab = "r [micrometer]",
ylab = "Frequency")
dev.off()
}
I'm trying to save several xyplots created with a "for" loop in R and I'm not able to get complete pdf files (all files have the same size and I can't open them) if I execute the following loop:
for (i in 1:length(gases.names)) {
# Set ylim;
r_y <- round(range(ratio.cal[,i][ratio.cal[,i]<999], na.rm = T), digits = 1);
r_y <- c(r_y[1]-0.1, r_y[2]+0.1);
outputfile <- paste (path, "/cal_ratio_",gases.names[i], ".pdf", sep="");
dev.new();
xyplot(ratio.cal[,i] ~ data.GC.all$data.time, groups = data.vial, panel =
panel.superpose, xlab = "Date", ylab = gases.names[i], xaxt="n", ylim = r_y);
savePlot(filename = outputfile, type = 'pdf', device = dev.cur());
dev.off();
}
(a previous version was using trellis.device() instead of dev.new() + savePlot())
Do you know why I can't get good pdf files? If I do it "manually" it works... Any idea?
use pdf directly
for (i in seq_along(gases.names)) {
# Set ylim
r_y <- round(range(ratio.cal[,i][ratio.cal[,i]<999], na.rm = T), digits = 1)
r_y <- c(r_y[1]-0.1, r_y[2]+0.1)
outputfile <- paste (path, "/cal_ratio_",gases.names[i], ".pdf", sep="")
pdf(file = outputfile, width = 7, height = 7)
print(xyplot(ratio.cal[,i] ~ data.GC.all$data.time, groups = data.vial,
panel = panel.superpose, xlab = "Date", ylab = gases.names[i],
xaxt="n", ylim = r_y))
dev.off()
}