I have the need to build a function that will plot multiple pdfs, read them in, combine the results (different sized pdfs), save the combined file and delete the initial files. I'm getting hung up on the initial part of interactively plotting multiple plots to external pdfs. The problem is I need a way of pausing in the for loop, waiting for the plot and then moving on after receiving the plot. I thought readLines was the way to go (and it may be) but this did not work (i.e. no plot was produced).
How can I make R pause between pdf take the plot, move onto dev.off and reiterate through the process again? The desired outcome is to have three files in the wd called file1.pdf, file2.pdf and file3.pdf. Again, after running the loop/lapply, this process will be interactive.
This is a MWE of the problem:
widths <- c(10, 9, 8)
heights <- c(11, 9, 7)
file <- "foo.pdf"
lapply(1:3, function(i) { #will askfor/take 3 plots interactively
qo <- gsub(".pdf", paste0(i, ".pdf"), file, fixed = TRUE)
cat("plot now...")
pdf(file=qo, width = widths[i], height = heights[i])
#pause command here
dev.off()
})
#the interactive part
plot(1:10)
plot(1:13)
plot(1:15)
EDIT 1 Related question: Determine ghostscript version
EDIT 2 Here's a link to the package I used this information to create -click here-
Is it as simple as this?
for(i in 1:3){
cat(i, "\n")
cat("plot now...")
readLines(n=1)
}
This stops to read a single line from stdin, i.e. the console. Press Enter to carry on.
Are you looking for something like this?
widths <- c(10, 9, 8)
heights <- c(11, 9, 7)
file <- "foo.pdf"
lapply(1:3, function(i) {
qo <- gsub(".pdf", paste0(i, ".pdf"), file, fixed = TRUE)
pdf(file=qo, width = widths[i], height = heights[i])
# Reads string interactively
input <- scan("", what = "character", nmax=1, quiet=TRUE)
# Executes `input` as a command (possibly, needs extra check)
eval(parse(text=input))
dev.off()
})
This results in three files: foo1.pdf, foo2.pdf and foo3.pdf with plots produced using commands you typed interactively.
Related
I have a series of csv files like this:
dataframe_1 <- read.csv('C:filepath/data_1.csv', header = T, skip = 1)
Where each file is a year of records. The number varies from run to run, so one time might be only a few, other times dozens of files. What I've been doing is creating individual dataframes, stripping out the columns I want using:
cutout_1 <- dataframe_1[c(1:365), c(2, 4, 6, 8, 10)]
and then binding them with rbind() as follows:
total <- rbind(cutout_1, cutout_2, cutout_3, cutout_4)
as.data.frame(total)
However this is clunky and I need to re-write every time I change something about the model I am using, such as the number of years (and thus the number of files it produces), which wastes a lot of time.
I have tried indexing through the data file, but can't seem to find a way to extract only the files I want, nor find a way to skip the first row, which is essential because of the way the data is produced which I have no control over.
Assuming the working directory is the directory where the files can be found, the code below first gets the filenames, then reads them in a lapply loop and creates a cutout column with the file base name without directory path nor extension. Then rbinds them in one data.frame.
filenames <- list.files(pattern = "Day_Climate_.*\\.csv")
cols_to_keep <- c(2, 4, 6, 8, 10)
rows_to_keep <- 1:365
cutout_list <- lapply(filenames, \(x) {
dftmp <- read.csv(x, skip = 1L)
dftmp <- dftmp[rows_to_keep, cols_to_keep]
# these instructions create in each file
# a column telling where they came from
# (this might not be needed)
cutout <- basename(x)
cutout <- tools::file_path_sans_ext(cutout)
dftmp$cutout <- cutout
# need to return the anonymous function value
dftmp
})
total <- do.call(rbind, cutout_list)
To minimize 3rd party package dependencies & reserve the ability to parallelize the code; this reproduceable example below is intended to create png images for each row step of a plot using R's Base graphics (no Tidyverse or GGPlot).
It, however, produces the entire series for each image, & not the intended iterative build required:
#
setwd("///images")
data(mtcars) # load DF
frames = 50 # set image qty rate
for(i in 1:frames){
# creating a name for each plot file with leading zeros
if (i < 10) {name = paste('000',i,'plot.png',sep='')}
if (i < 100 && i >= 10) {name = paste('00',i,'plot.png', sep='')}
if (i >= 100) {name = paste('0', i,'plot.png', sep='')}
png(name)
# plot(mtcars$mpg,type="l")
plot(mtcars$mpg)
dev.off()
}
my_cmd <- 'convert *.png -delay 5 -loop 5 mpg.gif'
system(my_cmd)
#
My own attempts to unsuccessfully resolve the issue include:
1) Remove the frame iteration & used nrows (mtcars) as the loop controlling agent?
2) Reference the row index somehow for each plot call?
3) Insert a sleep() call inside the loop after each plot?
4) Use the apply() function instead of a loop?
Any pointers or alternative coding to be more R efficient to make this work as intended?
Thanks.
This code will create one .png file for series of plots where each successive plot has one additional point on it:
# load data
data(mtcars)
# specify number of files to create (one per row of mtcars)
frames <- nrow(mtcars)
# figure out how many leading zeros will be needed in filename
ndigits <- nchar(as.character(frames))
for(i in 1:frames){
# name each file
zeros <- ndigits - nchar(as.character(i))
ichar <- paste0(strrep('0',zeros), i)
name <- paste0(ichar, 'plot.png')
# plot as .png
png(filename = name)
plot(x=1:i, y=mtcars$mpg[1:i], pch=20, col="blue",
xlim=c(0,frames), ylim=range(mtcars$mpg))
dev.off()
}
While fine-tuning parameters for plots I want to save all the test runs in different files so that they will not be lost. So far, I managed to do it using the code below:
# Save the plot as WMF file - using random numbers to avoid overwriting
number <- sample(1:20,1)
filename <- paste("dummy", number, sep="-")
fullname <- paste(filename, ".wmf", sep="")
# Next line actually creates the file
dev.copy(win.metafile, fullname)
dev.off() # Turn off the device
This code works, generating files with name "dummy-XX.wmf", where XX is a random number between 1 and 20, but it looks cumbersome and not elegant at all.
Is there any more elegant method to accomplish the same? Or even, to keep a count of how many times the code has been run and generate nice progressive numbers for the files?
If you really want to increment (to avoid overwriting what files already exist) you can create a small function like this one:
createNewFileName = function(path = getwd(), pattern = "plot_of_something", extension=".png") {
myExistingFiles = list.files(path = path, pattern = pattern)
print(myExistingFiles)
completePattern = paste0("^(",pattern,")([0-9]*)(",extension,")$")
existingNumbers = gsub(pattern = completePattern, replacement = "\\2", x = myExistingFiles)
if (identical(existingNumbers, character(0)))
existingNumbers = 0
return(paste0(pattern,max(as.numeric(existingNumbers))+1,extension))
}
# will create the file myplot1.png
png(filename = createNewFileName(pattern="myplot"))
hist(rnorm(100))
dev.off()
# will create the file myplot2.png
png(filename = createNewFileName(pattern="myplot"))
hist(rnorm(100))
dev.off()
If you are printing many plots, you can do something like
png("plot-%02d.png")
plot(1)
plot(1)
plot(1)
dev.off()
This will create three files "plot-01.png", "plot-02.png", "plot-03.png"
The filename you specify can take an sprintf-like format where the index of the plot in passed in. Note that counting is reset when you open a new graphics device so all calls to plot() will need to be done before calling dev.off().
Note however with this method, it will not look to see which files already exist. It will always reset the counting at 1. Also, there is no way to change the first index to anything other than 1.
I have a dataframe data with information on tiffs, including one column txt describing the content of the tiff. Unfortunately, txt is not always correct and we need to correct them by hand. Therefore I want to loop over each row in data, show the tiff and ask for feedback, which is than put into data$txt.cor.
setwd(file.choose())
Some test tiffs (with nonsene inside, but to show the idea...):
txt <- sample(100:199, 5)
for (i in 1:length(txt)){
tiff(paste0(i, ".tif"))
plot(txt[i], ylim = c(100, 200))
dev.off()
}
and the dataframe:
pix.files <- list.files(getwd(), pattern = "*.tif", full.names = TRUE)
pix.file.info <- file.info(pix.files)
data <- cbind(txt, pix.file.info)
data$file <- row.names(pix.file.info)
data$txt.cor <- ""
data$txt[5] <- 200 # wrong one
My feedback function (error handling stripped):
read.number <- function(){
n <- readline(prompt = "Enter the value: ")
n <- as.character(n) #Yes, character. Sometimes we have alphanumerical data or leading zeros
}
Now the loop, for which help would be very much appreciated:
for (i in nrow(data)){
file.show(data[i, "file"]) # show the image file
data[i, "txt.cor"] <- read.number() # aks for the feedback and put it back into the dataframe
}
In my very first attempts I was thinking of the plot.lm idea, where you go through the diagnostic plots after pressing return. I suspect that plot and tiffs are not big friends. file.show turned out to be easier. But now I am having a hard time with that loop...
Your problem is that you don't loop over the data, you only evaluate the last row. Simply write 1:nrow(data)to iterate over all rows.
To display your tiff images in R you can use the package rtiff:
library(rtiff)
for (i in 1:nrow(data)){
tif <- readTiff(data[i,"file"]) # read in the tiff data
plot(tif) # plot the image
data[i, "txt.cor"] <- read.number() # aks for the feedback and put it back into the dataframe
}
I've made a loop to create multiple boxplots. The thing is, I want to save all the boxplots without overwriting each other. Any suggestions?
This is my current code:
boxplot <- list()
for (x in 1:nrow(checkresults)){
boxplots <- boxplot(PIM[,x], MYC [,x], OBX[,x], WDR[,x], EV[,x],
main=colnames(PIM)[x],
xlab="PIM, MYC, OBX, WDR, EV")
}
Do you want to save them in some files, or save them to be able to look at them in different windows ?
If it is the first case, you can use a png, pdf or whatever function call inside your for loop :
R> for (i in 1:5) {
R> png(file=paste("plot",i,".png",sep=""))
R> plot(rnorm(10))
R> dev.off()
R> }
If you want to display them in separate windows, just use dev.new :
R> for (i in 1:5) {
R> dev.new()
R> plot(rnorm(10));
R> }
Just to add to #juba's answer, if you want to save the plots to a multi-page pdf file, then you don't have to use the paste command that #juba suggested. This
pdf("myboxplots.pdf")
for (x in seq_along(boxplots)){
boxplot(PIM[,x], MYC [,x], OBX[,x], WDR[,x],EV[,x],
main = colnames(PIM)[x],
xlab = "PIM, MYC, OBX, WDR, EV")
}
dev.off()
creates a single multi-page pdf document, where each page is a boxplot. If you want to store the boxplots in separate pdf documents, then use the file=paste command.
First, create a list of the right length - it just makes things easier and is good practice to allocate storage before filling objects in via a loop:
boxplots <- vector(mode = "list", length = nrow(checkresults))
Then we can loop over the data you want, assigning to each component of the boxplots list as we go, using the [[x]] notation:
for (x in seq_along(boxplots)){
boxplots[[x]] <- boxplot(PIM[,x], MYC [,x], OBX[,x], WDR[,x],EV[,x],
main = colnames(PIM)[x],
xlab = "PIM, MYC, OBX, WDR, EV")
}
Before, your code was overwriting the previous boxplot info during subsequent iterations.