I was wondering if anyone could help me use a variable name within a function.
I've put together a dot plot that sorts variables and then produces a bitmap, but I can't get R to pass the variable name to the plot title.
Example data
id<-c(1,2,3)
blood<-c(1,2,10)
weight<-c(1,2,13)
mydata<-as.data.frame(cbind(id,blood,weight))
mydata$blood
#######SORTED DOT PLOT####
Dplotter<-function (id,x,Title=""){
if (is.null(Title)) {Title=""} else {Title=Title}
DIR<-paste("C:/temp/WholePlots/New/",Title,".bmp",sep="")
D<-as.data.frame(cbind(id,x))
x1<-as.data.frame(D[order(x),])
bmp(DIR)
dotchart(x1$x,labels=id,main=Title,pch=16)
dev.off()
}
###############
Dplotter(mydata$id,mydata$blood,"Blood")
Dplotter(mydata$id,mydata$weight,"Weight")
In the second line of the function, I'd like to pass on the variable
name, something like
`if (is.null(Title)) {Title=varname(x)} else {Title=Title}`
so that I don't have to put "Blood" in the function Title field
(e.g. Dplotter(mydata$id,mydata$blood)
Basically, how does one paste in the variable name in a function? It
would be even better if one could take out the dataset name from the
Title (without attaching the dataset, which I've been told is bad
practice), so that instead of getting mydata$blood, you just get
"blood" in the title.
I've failed to find an easy solution to paste in a variable name in
a function. As you can guess, putting the variable name in a
paste() function returns the values of the variable (so that the
plot title is filled with values rather the variable name).
I'd also like to automate the function even further, so that I can
just put the dataset and the ID,and then have the function repeated
for each variable in the dataset. Obviously this requires solving
question 1 first, otherwise both title and filenames will encounter
problems.
The general answer is deparse(substitute(x)). E.g.
fooPlot <- function(x, main, ...) {
if(missing(main))
main <- deparse(substitute(x))
plot(x, main = main, ...)
}
Here it is in use:
set.seed(42)
dat <- data.frame(x = rnorm(1:10), y = rnorm(1:10))
fooPlot(dat, col = "red")
Which produces:
In your particular example though, this won't work because you don't want dat$x as the title, you want just x. We could do a little more manipulation however:
fooPlot <- function(x, main, ...) {
if(missing(main)) {
main <- deparse(substitute(x))
if(grepl("\\$", main)) {
main <- strsplit(main, "\\$")[[1]][2]
}
}
plot(x, main = main, ...)
}
Which for fooPlot(dat$x, col = "red") gives:
Note this code makes some assumptions, that main is not a vector, that there will only ever be one $ in the object passed to plot (i.e. you couldn't use a nested list for example with the above code).
You need to retrieve a set of strings, the variable names, and use them for the title of your plots and filenames as well.
I will use the longley dataset to illustrate the trick.
data(longley, package="datasets")
#return a vector with variable names
colnames(longley)
names(longley) #equivalent
#get the name of a specific variable (column number):
names(longley)[1]
To plot each variable, get two sets of strings: variable names and filenames:
var.names=names(longley)
file.names=paste(var.names, "bmp", sep=".")
#with an extra step to prefix a directory to those filenames
for (i in 1:ncol(longley) ) {
bmp(file=file.names[i])
plot(longley[[i]], main=var.names[i], ylab="")
dev.off()
}
ylab="", since otherwise it gives a silly "longley[[i]]" as y-label, and if I use var.name[i] as ylab, it would be redundant.
Related
I have a program that is supposed to create a pdf file of actograms given a csv of activity and time. I need to loop through multiple activity columns, one for each subject. The first activity column is column 3. Here is the relevant code:
pdf("All Actograms.pdf")
for(i in 3:(length(dat) - 1)) {
activity <- colnames(dat)[i]
# Plot the actogram
print(actogram(activity~datetime, dat=dat, col="black", main=colnames(dat)[i], strip.left.format="%m/%d", doublePlot = TRUE, scale=0.75))
}
dev.off()
When I call my actogram function, I get the error "non-numeric argument to binary operator." The problem is the formula "activity~datetime," because datetime is a column name and activity should be too. If I try it out of the loop, with the name of an activity column rather than a variable containing the name, it works fine. Upon debugging, I found the actogram function is receiving the string "activity," rather than the variable activity. I don't really understand formulas, but I want to know if there's any way to accomplish what I'm trying to do, which is loop through many columns, changing the column before the "~" each time I call the actogram function. I'm very new to R.
Thanks!
We do not have the data you are working on but I think the simplest thing you can do is the following:
pdf("All Actograms.pdf")
for(i in 3:(length(dat) - 1)) {
activity <- colnames(dat)[i]#save the name of the column I
colnames(dat)[i] <- "activity" # change the name of column I to activity
# Plot the actogram
print(actogram(activity~datetime, dat=dat, col="black", main=activity, strip.left.format="%m/%d", doublePlot = TRUE, scale=0.75))
colnames(dat)[i] <- activity # change back the name of the column I to its original name
}
dev.off()
Hopefully it works.
I have a function that looks like this:
removeRows <- function(dataframe, rows.remove){
dataframe <- dataframe[-rows.remove,]
print(paste("The", paste0(rows.remove, "th"), "row was removed from", "xxxxxxx"))
}
I can use the function like this to remove the 5th row from the dataframe:
removeRows(mtcars, 5)
The function output this message:
"The 5th row was removed from xxxxxxx"
How can I replace xxxxxxx with the name of the dataframe I have used, so in this case mtcars?
You need to access the variable name in an unevaluated context. We can use substitute for this:
removeRows <- function(dataframe, rows.remove) {
df.name <- deparse(substitute(dataframe))
dataframe <- dataframe[rows.remove,]
print(paste("The", paste0(rows.remove, "th"), "row was removed from", df.name))
}
In fact, that is its main use; as per the documentation,
The typical use of substitute is to create informative labels for data sets and plots.
I would like to point out that df.name <- deparse(substitute(dataframe)) should be used at the top of your function before any transformation is done. I used it right at the end of my function, just before doing ggsave, which does not return the name but somehow what is inside the dataframe, which is not what you want. This gave me a lot of headache.
So something like this :
function(df){
df.name <- deparse(substitute(dataframe))
ggplot()
ggsave()
}
I want to create a series of x-y scatter charts, where y is always the same variable and x are the variables I want to check if they are correlated with. As an example lets use the mtcars dataset.
I am relatively new to R but getting better.
The code below works, the list charts contains all the charts, except that the X axis shows as "x", and I want it to be the name of the variable. I tried numerous combinations of xlab= and I do not seem to get it
if I use names(data) I see the names I want to use. I guess I want to reference the first of names(data) the first iteration of apply, the second the second time, etc. How can I do that?
Th next step would be to print them in a lattice together, I assume an lapply or sapply will do the trick with the print function - I appreciate idea for this too, just pointers I do not need a solution.
load(mtcars)
mypanel <- function(x,y,...) {
panel.xyplot(x,data[,y],...)
panel.grid(x=-1,y=-1)
panel.lmline(x,y,col="red",lwd=1,lty=1)
}
data <- mtcars[,2:11]
charts <- apply(data,2,function(x) xyplot (mtcars[,1] ~ x, panel=mypanel,ylab="MPG"))
This all started because I was not able to use the panel function to cycle.
I did not find that this code "worked". Modified it to do so:
mypanel <- function(x,y,...) {
panel.xyplot(x, y, ...)
panel.grid(x=-1, y=-1)
panel.lmline(x,y,col="red",lwd=1,lty=1)
}
data <- mtcars[,2:11]
charts <- lapply(names(data), function(x) { xyplot (mtcars[,1] ~ mtcars[,x],
panel=mypanel,ylab="MPG", xlab=x)})
Needed to remove the 'data[,y]' from the panel function and pass names instead of column vectors so there was something to use for a x-label.
I have a function that computes some things and then assigns that to a matrix. This matrix receives its name from a paste statement (based on some other current values). I then want to assign the dimnames to the matrix, but don't know how to make the pasted name be understood.
Here is what is going on:
function <- someComputations(labs) {
### bunch of computations, leading to X, Y, and Z:
matName <- paste("rhoMat_", X, sep = "") # this yields rhoMat_15 if X equals 15
assign(matName, Y %*% Z)
assign(dimnames(matName), labs) # labs is a list of row labels and column labels
return(matName)
}
This works well, including the first assign statement, and then it breaks down.
I have tried all kinds of approaches, such as eval(parse(text = matNum)), as.name(matNum), substitute(matNum), but to no avail.
Since I don't know the actual name of the matrix (because matNum is not given), I can't hardcode the name into the function--so I am stuck with its character name matName. How can I make R understand I want to set the dimnames of the matrix rhoMat_15, rather than of matName?
Thanks, Peter
dimnames(get(matName)) <- labs
Yesterday, I asked a question regarding how to plot multiple (horizontal-ish) lines, each with a user-specified color, without using a loop.
I tried to use the suggested function matplot() to plot the short vertical lines shown in the plot below with the relevant code.
ci = matrix(1:30, nrow=3, byrow=T)
ci=list(rbind(ci[1,], ci[1,]+2),
rbind(ci[2,], ci[2,]+2),
rbind(ci[3,], ci[3,]+2))
x = rbind(1:10, 1:10)
plot(-5, xlim=c(1,10), ylim=c(1,32))
invisible(mapply(matlines, x=list(x), y=ci,
col=c("red","blue","black"),
lty = 1))
This is all good. However, I am trying to wrap this code inside a function, and I would like to be able to input a list of optional arguments, which can then be passed to mapply/matlines. I tried to use the argument MoreArgs in mapply() to no avail. It seems that arguments that MoreArgs takes are not treated the same as others. As you can see in the first code, each item of the list gets a different color, but when I put col inside the list args.ci, the 3 colors are recycled within each item of the list. I wonder if there is anyway to resolve this issue so that if I have multiple values for an argument, each value gets applied to one item of a list. Thanks!
args.ci = list(col=c("red","blue","black"), lty=1:3)
plot(-5, xlim=c(1,10), ylim=c(1,32))
invisible(mapply(matlines, x=list(x), y=ci,
MoreArgs = args.ci))
Here's a general approach to this sort of problem. You should be able to adapt it to do more exactly what you want:
myFun <- function(...) {
fixedArgs <- list(matlines, x=list(x), y=ci)
dots <- list(...)
allArgs <- c(fixedArgs, dots)
plot(-5, xlim=c(1,10), ylim=c(1,32))
invisible(do.call(mapply, allArgs))
}
myFun(col=c("red","blue","black"), lty=1)