With vs. lapply - why does with not work here? - r

I am trying to learn R and can't really figure out when to use with appropriately. I was thinking about this example:
The goal is to convert "dstr" and "died" in the whole dataframe "stroke" (in the ISwR database) to date format in several ways (just for practice). I've managed to do it like this:
#applying a function to the whole data frame - use the fact that data frames are lists actually
rawstroke=read.csv2(system.file("rawdata","stroke.csv",package="ISwR"),na.strings=".")
names(rawstroke)=tolower(names(rawstroke))
ix=c("dstr","died")
rawstroke[ix]=lapply(rawstroke[ix],as.Date,format="%d.%m.%Y")
head(rawstroke)
However, when I try using with function it does not give data frame as output, but only writes the definition of the function myfun. Here is the code I tried.
myfun=function(x)
{y=as.Date(x,format="%d.%m.%Y")
return(y)}
rawstroke=read.csv2(system.file("rawdata","stroke.csv",package="ISwR"),na.strings=".")
names(rawstroke)=tolower(names(rawstroke))
ix=c("dstr","died")
bla=with(rawstroke[ix],myfun)
head(bla)
If somebody could help me with this, it would be great.

Yeah, this doesn't seem like a job for with. To use your function here, you'd just replace as.Date in your first code with myfun and remove the format parameter, like
rawstroke[ix]=lapply(rawstroke[ix], myfun)
with is used to more cleanly access variables in data frames and environments. For example, instead of
t.test(dat$x, dat$y)
you could do
with(dat, t.test(x, y))

Related

paste input name between words for save it using write.table

im super newbie on R and i have been learning for myself for a few weeks already due my work degree.
Im almost done with the statistical analysis that i need, but it is through an ugly and messy code, that is, repeating lot of codes for several data frames, to apply different statistical tests, save results, etc.
Well now, for personal interest, want to write this better, but im totally trapped in my ignorance and really need a push to get the idea, please.
For example, i want to create a function that measure the correlation on all the data tables im using and save those results as a tables using the input name as part of the output name.
I mean, if we had the iris data but measured on different seasons, e.g. iris_fall, iris_winter, iris_spring and iris_summer, after apply cor(X) method to each one, i want to save those results as tables called like "mCoriris_fall.txt", "mCoriris_winter.txt", "mCoriris_spring.txt" and "mCoriris_summer.txt" respectively.
My useless code for now say:
cor_PQ<-function(X) {
cor_PQ<-cor(X, use="pairwise.complete.obs")
return(cor_PQ)
}
savecor<-function(t) {
outputname<-(paste0("mCor",t)) #HOW DO I CALL THE NAME OF THE INPUT? t is cor_PQ result matrix.
savecor<-write.table(t, file=paste0(outputname,".txt"))
return(savecor)
}
cor_PQ(Iris_fall)
I expect to get cor result and save it as a table in my workspace, using the input name as part of the output name.
Im aware this are 2 separates functions and the one to write table should be inside the function for cor(x), but i cant understand how.
I have been reading a lot but i just cant fit all in my head.
Thanks to anyone who can help me.
Regards.
UNTIL HERE IT HAS BEEN SOLVED...
But after making a list with my 14 data frames to apply cor and other methods, the write.table function overwrite the 14 cor results on 1 single doc. This is my code.
PQ_files<-list.files(path="C:/Users/Sol/Documents/ProyectoTítulo/CalidadAgua/Matrices/Regs",pattern="\\_PQ.txt")
PQ_data<-lapply(PQ_files, read.table)
names(PQ_data)<-gsub("\\_PQ.txt","", PQ_files)
PQ_data
cor_PQ<-function(X) {
cor_PQ<-cor(X, use="pairwise.complete.obs")
outputname.txt<-paste0("mCor",deparse(substitute(X)),".txt")
write.table(cor_PQ, file=outputname.txt)
outputname.pdf<-paste0("Cor",deparse(substitute(X)),".pdf")
pdf(outputname.pdf)
plot(X)
dev.off()
return(cor_PQ)
}
for (i in seq_along(PQ_data)){
Correlaciones<-lapply(PQ_data,cor_PQ)
}
Correlaciones
On SUM: seems to work almost good, until the write.table and plot(x) overwrite the outputs from the 14 dataframes on my PQ_data withe the name mCor[[i]] and CorX[[i]], respectively.
Should i define [i] somehow to have each results with the right name?
Also, when i run Correlaciones at the end, i can see the cor result for the 14 dataframes in one single dataframe, but i dont know how to split them correctly.
I guess almost there.
THANKS AGAIN!
You can combine the two functions and use deparse substitute to get input names as string
cor_PQ <- function(X) {
cor_PQ<-cor(X, use="pairwise.complete.obs")
outputname<- paste0("mCor",deparse(substitute(X)), ".txt")
write.table(t, file=outputname)
return(cor_PQ)
}
and then call
cor_PQ(Iris_fall)

Looping in R to create transformed variables

I have a dataset of 80 variables, and I want to loop though a subset of 50 of them and construct returns. I have a list of the names of the variables for which I want to construct returns, and am attempting to use the dplyr command mutate to construct the variables in a loop. Specifically my code is:
for (i in returnvars) {
alldta <- mutate(alldta,paste("r",i,sep="") = (i - lag(i,1))/lag(i,1))}
where returnvars is my list, and alldta is my dataset. When I run this code outside the loop with just one of the `i' values, it works fine. The code for that looks like this:
alldta <- mutate(alldta,rVar = (Var- lag(Var,1))/lag(Var,1))
However, when I run it in the loop (e.g., attempting to do the previous line of code 50 times for 50 different variables), I get the following error:
Error: unexpected '=' in:
"for (i in returnvars) {
alldta <- mutate(alldta,paste("r",i,sep="") ="
I am unsure why this issue is coming up. I have looked into a number of ways to try and do this, and have attempted solutions that use lapply as well, without success.
Any help would be much appreciated! If there is an easy way to do this with one of the apply commands as well, that would be great. I did not provide a dataset because my question is not data specific, I'm simply trying to understand, as a relative R beginner, how to construct many transformed variables at once and add them to my data frame.
EDIT: As per Frank's comment, I updated the code to the following:
for (i in returnvars) {
varname <- paste("r",i,sep="")
alldta <- mutate(alldta,varname = (i - lag(i,1))/lag(i,1))}
This fixes the previous error, but I am still not referencing the variable correctly, so I get the error
Error in "Var" - lag("Var", 1) :
non-numeric argument to binary operator
Which I assume is because R sees my variable name Var as a string, rather than as a variable. How would I correctly reference the variable in my dataset alldta? I tried get(i) and alldta$get(i), both without success.
I'm also still open to (and actively curious about), more R-style ways to do this entire process, as opposed to using a loop.
Using mutate inside a loop might not be a good idea either. I am not sure if mutate makes a copy of the data frame but its generally not a good practice to grow a data frame inside a loop. Instead create a separate data frame with the output and then name the columns based on your logic.
result = do.call(rbind,lapply(returnvars,function(i) {...})
names(result) = paste("r",returnvars,sep="")
After playing around with this more, I discovered (thanks to Frank's suggestion), that the following works:
extended <- alldta # Make a copy of my dataset
for (i in returnvars) {
varname <- paste("r",i,sep="")
extended[[varname]] = (extended[[i]] - lag(extended[[i]],1))/lag(extended[[i]],1)}
This is still not very R-styled in that I am using a loop, but for a task that is only repeating about 50 times, this shouldn't be a large issue.

How to properly loop without eval, parse, text=paste("... in R

So I had a friend help me with some R code and I feel bad asking because the code works but I have a hard time understanding and changing it and I have this feeling that it's not correct or proper code.
I am loading files into separate R dataframes, labeled x1, x2... xN etc.
I want to combine the dataframes and this is the code we got to work:
assign("x",eval(parse(text=paste("rbind(",paste("x",rep(1:length(toAppend)),sep="",collapse=", "),")",sep=""))))
"toAppend" is a list of the files that were loaded into the x1, x2 etc. dataframes.
Without all the text to code tricks it should be something like:
x <- rbind(##x1 through xN or some loop for 1:length(toAppend)#)
Why can't R take the code without the evaluate text trick? Is this good code? Will I get fired if I use this IRL? Do you know a proper way to write this out as a loop instead? Is there a way to do it without a loop? Once I combine these files/dataframes I have a data set over 30 million lines long which is very slow to work with using loops. It takes more than 24 hours to run my example line of code to get the 30M line data set from ~400 files.
If these dataframes all have the same structure, you will save considerable time by using the 'colClasses' argument to the read.table or read.csv steps. The lapply function can pass this to read.* functions and if you used Dason's guess at what you were really doing, it would be:
x <- do.call(rbind, lapply(file_names, read.csv,
colClasses=c("numeric", "Date", "character")
)) # whatever the ordered sequence of classes might be
The reason that rbind cannot take your character vector is that the names of objects are 'language' objects and a character vector is ... just not a language type. Pushing character vectors through the semi-permeable membrane separating 'language' from 'data' in R requires using assign, or do.call eval(parse()) or environments or Reference Classes or perhaps other methods I have forgotten.

returning different data frames in a function - R

Is it possible to return 4 different data frames from one function?
Scenario:
I am trying to read a file, parse it, and return some parts of the file.
My function looks something like this:
parseFile <- function(file){
carFile <- read.table(file, header=TRUE, sep="\t")
carNames <- carFile[1,]
carYear <- colnames(carFile)
return(list(carFile,carNames,carYear))
}
I don't want to have to use list(carFile,carNames,carYear). Is there a way return the 3 data frames without returning them in a list first?
R does not support multiple return values. You want to do something like:
foo = function(x,y){return(x+y,x-y)}
plus,minus = foo(10,4)
yeah? Well, you can't. You get an error that R cannot return multiple values.
You've already found the solution - put them in a list and then get the data frames from the list. This is efficient - there is no conversion or copying of the data frames from one block of memory to another.
This is also logical, the return from a function should conceptually be a single entity with some meaning that is transferred to whatever function is calling it. This meaning is also better conveyed if you name the returned values of the list.
You could use a technique to create multiple objects in the calling environment, but when you do that, kittens die.
Note in your example carYear isn't a data frame - its a character vector of column names.
There are other ways you could do that, if you really really want, in R.
assign('carFile',carFile,envir=parent.frame())
If you use that, then carFile will be created in the calling environment. As Spacedman indicated you can only return one thing from your function and the clean solution is to go for the list.
In addition, my personal opinion is that if you find yourself in such a situation, where you feel like you need to return multiple dataframes with one function, or do something that no one has ever done before, you should really revisit your approach. In most cases you could find a cleaner solution with an additional function perhaps, or with the recommended (i.e. list).
In other words the
envir=parent.frame()
will do the job, but as SpacedMan mentioned
when you do that, kittens die
The zeallot package does what you need in a similar that Python can unpack variables from a function. Reproducible example below.
parseFile <- function(){
carMPG <- mtcars$mpg
carName <- rownames(mtcars)
carCYL <- mtcars$cyl
return(list(carMPG,carName,carCYL))
}
library(zeallot)
c(myFile, myName, myYear) %<-% parseFile()

How to use a value that is specified in a function call as a "variable"

I am wondering if it is possible in R to use a value that is declared in a function call as a "variable" part of the function itself, similar to the functionality that is available in SAS IML.
Given something like this:
put.together <- function(suffix, numbers) {
new.suffix <<- as.data.frame(numbers)
return(new.suffix)
}
x <- c(seq(1000,1012, 1))
put.together(part.a, x)
new.part.a ##### does not exist!!
new.suffix ##### does exist
As it is written, the function returns a dataframe called new.suffix, as it should because that is what I'm asking it to do.
I would like to get a dataframe returned that is called new.part.a.
EDIT: Additional information was requested regarding the purpose of the analysis
The purpose of the question is to produce dataframes that will be sent to another function for analysis.
There exists a data bank where elements are organized into groups by number, and other people organize the groups
into a meaningful set.
Each group has an id number. I use the information supplied by others to put the groups together as they are specified.
For example, I would be given a set of id numbers like: part-1 = 102263, 102338, 202236, 302342, 902273, 102337, 402233.
So, part-1 has seven groups, each group having several elements.
I use the id numbers in a merge so that only the groups of interest are extracted from the large data bank.
The following is what I have for one set:
### all.possible.elements.bank <- .csv file from large database ###
id.part.1 <- as.data.frame(c(102263, 102338, 202236, 302342, 902273, 102337, 402233))
bank.names <- c("bank.id")
colnames(id.part.1) <- bank.names
part.sort <- matrix(seq(1,nrow(id.part.1),1))
sort.part.1 <- cbind(id.part.1, part.sort)
final.part.1 <- as.data.frame(merge(sort.part.1, all.possible.elements.bank,
by="bank.id", all.x=TRUE))
The process above is repeated many, many times.
I know that I could do this for all of the collections that I would pull together, but I thought I would be able to wrap the selection process into a function. The only things that would change would be the part numbers (part-1, part-2, etc..) and the groups that are selected out.
It is possible using the assign function (and possibly deparse and substitute), but it is strongly discouraged to do things like this. Why can't you just return the data frame and call the function like:
new.part.a <- put.together(x)
Which is the generally better approach.
If you really want to change things in the global environment then you may want a macro, see the defmacro function in the gtools package and most importantly read the document in the refrences section on the help page.
This is rarely something you should want to do... assigning to things out of the function environment can get you into all sorts of trouble.
However, you can do it using assign:
put.together <- function(suffix, numbers) {
assign(paste('new',
deparse(substitute(suffix)),
sep='.'),
as.data.frame(numbers),
envir=parent.env(environment()))
}
put.together(part.a, 1:20)
But like Greg said, its usually not necessary, and always dangerous if used incorrectly.

Resources