Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Is there a way?
NB: the question is not whether it is right, good or sensible to do such a thing.
The question is if there is a way, so if your answer would be
"why would you want to do that?" "R uses functions what you want was once called procedure and good R usage/style does not ...", "could you explain better... provide some code" do NOT answer.
I did a quick try, that did not work eventually worked, using environments, more or less:
function(mydf) {
varName <- deparse(substitute(mydf))
...
assign(varName,mydf,envir=parent.frame(n = 1))
}
1) Wrap the function body in eval.parent(substitute({...})) like this:
f <- function(x) eval.parent(substitute({
x <- x + 1
}))
mydf <- data.frame(z = 1)
f(mydf)
mydf
## z
## 1 2
Also see the defmacro function in gtools and the wrapr package.
2) An alternative might be to use a replacement function:
"incr<-" <- function(x, value) {
x + value
}
mydf <- data.frame(z = 1)
incr(mydf) <- 1
mydf
## z
## 1 2
3) or just overwrite the input:
f2 <- function(x) x + 1
mydf <- data.frame(z = 1)
mydf <- f2(mydf)
mydf
## z
## 1 2
If the problem is that there are multiple outputs then use list in the gsubfn package. This is used on the left hand side of an assignment with square brackets as shown. See help(list, gsubfn)
library(gsubfn)
f3 <- function(x, y) list(x + 1, y + 2)
mydf <- mydf2 <- data.frame(z = 1)
list[mydf, mydf2] <- f3(mydf, mydf2)
mydf
## z
## 1 2
mydf2
## z
## 1 3
At least for my specific/limited needs I found a solution
myVar = 11
myF <- function(x) {
varName <- deparse(substitute(x))
# print(paste("var name is", varName))
x = 99
assign(varName,x,envir=parent.frame(n = 1))
NA # sorry this is not a function
# in real life sometimes you also need procedures
}
myF(myVar)
print(myVar)
# [1] 99
I think there is no way to emulate call-by-reference. However, several tricks can be used from case to case:
globals: It is, of course, possible to have a global variable instead of the parameter. This can be written from within a function using <<- instead of = or <-. In this way, many cases of needing call-by-reference vanish.
However, this is not compatible with parallelization and also not compatible with recursion.
When you need recursion, you can do very much the same and have a global stack. Before the recursive call, you have to append to this stack and as the first line of your function, you can get the index (similar to a stack pointer in CPUs) in order to write to the global stack.
Both approaches are not encouraged and should be used as a last resort or for education. If you really can't avoid call-by-reference, go to C++ with Rcpp and write a C++-function that does your heavy loading. If needed, it can actually call R functions. Look at some Rcpp tutorials, most of them cover this case...
Related
(1,2,2,3,3,3,4,4,4,4,...,n,...,n)
I want to make the above vector by for loop, but not using rep function or the other functions. This may not be good question to ask in stackoverflow, but since I am a newbie for R, I dare ask here to be helped.
(You can suppose the length of the vector is 10)
With a for loop, it can be done with
n <- 10
out <- c()
for(i in seq_len(n)){
for(j in seq_len(i)) {
out <- c(out, i)
}
}
In R, otherwise, this can be done as
rep(seq_len(n), seq_len(n))
I have been beaten by #akrun by seconds, even so I'd like to give you a few hints if using rep would have been possible which may help you with R in general. (Without rep usage, just look at #akrun)
Short answer using rep
rep(1:n, 1:n)
Long Answer using rep
Before posting a question you should try to develop your own solutions and share them.
Trying googling a bit and sharing what you already found is usually good as well. Please, have a look at "help/how-to-ask"
Let's try to do it together.
First of all, we should try to have a look at official sources:
R-project "getting help", here you can see the standard way to get a function's documentation is just typing ?func_name in your R console
R-project "official manuals" offer a good introduction to R. Try looking at the first topic, "An Introduction to R"
From the previous two (and other sources as well) you will find two interesting functions:
: operator: it can be used to generate a sequence of integers from a to b like a:b. Typing 1:3, for instance, gives you the 1, 2, 3 vector
rep(x, t) is a function which can be used to replicate the item(s) x t times.
You also need to know R is "vector-oriented", that is it applies functions over vectors without you typing explicits loops.
For instance, if you call repl(1:3, 2), it's (almost) equivalent to running:
for(i in 1:3)
rep(i, 2)
By combining the previous two functions and the notion R is "vector-oriented", you get the rep(1:n, 1:n) solution.
I am not sure why you don't want to use rep, but here is a method of not using it or any functions similar to rep within the loop.
`for (i in 1:10){
a<-NA
a[1:i] <- i
if (i==1){b<-a}
else if (i >1){b <- c(b,a)}
assign("OutputVector",b,envir = .GlobalEnv)
}`
`OutputVector`
Going for an n of ten seemed subjective so I just did the loop for numbers 1 through 10 and you can take the first 10 numbers in the vector if you want. OutputVector[1:10]
You can do this with a single loop, though it's a while rather than a for
n <- 10
x <- 1;
i <- 2;
while(i <= n)
{
x <- c(x, 1/i);
if(sum(x) %% 1 == 0) i = i + 1;
}
1/x
I want to get the number in order of the column in a dataframe.
df <- data.frame(item = rep(c('a','b','c'), 3),
year = rep(c('2010','2011','2012'), each=3),
count = c(1,4,6,3,8,3,5,7,9))
Lets say the function i am looking for is columnorder. I want to have this result
x <- columnorder(df$count)
x
> 3
x <- columnorder(df$item)
x
> 1
It seems like a basic task but I couldn't find the answer until now. I will appreciate your help. Thank you
You said,
It seems like a basic task but I couldn't find the answer until now.
In the general sense what you are trying to do -- translate a column name into a column index -- is basic, and a pretty common question. However, the particular scenario you describe above, where your input is of the form object_name$column_name, is atypical WRT what you are trying to achieve, which is most likely why you haven't found an existing solution.
In short, the problem is that when you pass an argument as df$count, you may as well just have used c(1,4,6,3,8,3,5,7,9) instead, because df$count will be evaluated as c(1,4,6,3,8,3,5,7,9). Of course, R does allow for a fair bit of metaprogramming, so with a little extra work, this could be implemented as, for example
column_order <- function(expr) {
x <- strsplit(deparse(substitute(expr)), "$", TRUE)[[1]]
match(x[2], names(get(x[1])))
}
column_order(df$item)
#[1] 1
column_order(df$year)
#[1] 2
column_order(df$count)
#[1] 3
But as I said above, this is an atypical interface for what you are ultimately trying to do. A much more standard approach would be for this function to accept the column name (typically as a string) and the target object as arguments, in which case the solution is much simpler:
column_order2 <- function(col, obj) match(col, names(obj))
column_order2("item", df)
#[1] 1
column_order2("year", df)
#[1] 2
column_order2("count", df)
#[1] 3
As proposed in the comments by #mtoto, here is one solution:
x <- which(colnames(df) == "count")
I have gotten in the habit of accessing data.table columns in j even when I do not need to:
require(data.table)
set.seed(1); n = 10
DT <- data.table(x=rnorm(n),y=rnorm(n))
frm <- formula(x~y)
DT[,lm(x~y)] # 1 works
DT[,lm(frm)] # 2 fails
lm(frm,data=DT) # 3 what I'll do instead
I expected # 2 to work, since lm should search for variables in DT and then in the global environment... Is there an elegant way to get something like # 2 to work?
In this case, I'm using lm, which takes a "data" argument, so # 3 works just fine.
EDIT. Note that this works:
x1 <- DT$x
y1 <- DT$y
frm1 <- formula(x1~y1)
lm(frm1)
and this, too:
rm(x1,y1)
bah <- function(){
x1 <- DT$x
y1 <- DT$y
frm1 <- formula(x1~y1)
lm(frm1)
}
bah()
EDIT2. However, this fails, illustrating #eddi's answer
frm1 <- formula(x1~y1)
bah1 <- function(){
x1 <- DT$x
y1 <- DT$y
lm(frm1)
}
bah1()
The way lm works it looks for the variables used in the environment of the formula supplied. Since you create your formula in the global environment, it's not going to look in the j-expression environment, so the only way to make the exact expression lm(frm) work would be to add the appropriate variables to the correct environment:
DT[, {assign('x', x, environment(frm));
assign('y', y, environment(frm));
lm(frm)}]
Now obviously this is not a very good solution, and both Arun's and Josh's suggestions are much better and I'm just putting it here for the understanding of the problem at hand.
edit Another (possibly more perverted, and quite fragile) way would be to change the environment of the formula at hand (I do it permanently here, but you could revert it back, or copy it and then do it):
DT[, {setattr(frm, '.Environment', get('SDenv', parent.frame(2))); lm(frm)}]
Btw a funny thing is happening here - whenever you use get in j-expression, all of the variables get constructed (so don't use it if you can avoid it), and this is why I don't need to also use x and y in some way for data.table to know that those variables are needed.
I'm working with a bunch of SAS datasets and I prefer the variable names to all be lowercase, using read.sas7bdat, but I do it so often I wanted to write a function. This method works fine,
df <- data.frame(ALLIGATOR=1:4, BLUEBIRD=rnorm(4))
names(file1) <- tolower(names(file1))
but when I try to put it into a function it doesn't assign.
lower <- function (df) {names(df) <- tolower(names(df))}
lower(file1)
I know that there is some larger concept that I'm missing, that is blocking me. It doesn't seem to do anything.
Arguments in R are passed by copy. You have to do:
lower <- function (df) {
names(df) <- tolower(names(df))
df
}
file1 <- lower(file1)
Although I don't see why you would do this rather than simply : names(df) <- tolower(names(df)), I think you should do:
lower <- function (x) {tolower(names(x))}
names(df) <- lower(df)
Here is an answer that I don't recommend using anywhere other than the globalenvironment but it does provide you some convenient shorthand. Basically we take care of the assignment inside the function, overwriting the object passed to it. Short-hand for you, but please be careful about how you use it:
tl <- function(x){
ass <- all.names(match.call()[-1])
assign( ass , setNames( x , tolower(names(x))) , env = sys.frame(sys.parent()) )
}
# This will 'overwrite' the names of df...
tl(df)
# Check what df now looks like...
df
alligator bluebird
1 1 0.2850386
2 2 -0.9570909
3 3 -1.3048907
4 4 -0.9077282
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
function with multiple outputs
This seems like an easy question, but I can't figure it out and I haven't had luck in the R manuals I've looked at. I want to find dim(x), but I want to assign dim(x)[1] to a and dim(x)[2] to b in a single line.
I've tried [a b] <- dim(x) and c(a, b) <- dim(x), but neither has worked. Is there a one-line way to do this? It seems like a very basic thing that should be easy to handle.
This may not be as simple of a solution as you had wanted, but this gets the job done. It's also a very handy tool in the future, should you need to assign multiple variables at once (and you don't know how many values you have).
Output <- SomeFunction(x)
VariablesList <- letters[1:length(Output)]
for (i in seq(1, length(Output), by = 1)) {
assign(VariablesList[i], Output[i])
}
Loops aren't the most efficient things in R, but I've used this multiple times. I personally find it especially useful when gathering information from a folder with an unknown number of entries.
EDIT: And in this case, Output could be any length (as long as VariablesList is longer).
EDIT #2: Changed up the VariablesList vector to allow for more values, as Liz suggested.
You can also write your own function that will always make a global a and b. But this isn't advisable:
mydim <- function(x) {
out <- dim(x)
a <<- out[1]
b <<- out[2]
}
The "R" way to do this is to output the results as a list or vector just like the built in function does and access them as needed:
out <- dim(x)
out[1]
out[2]
R has excellent list and vector comprehension that many other languages lack and thus doesn't have this multiple assignment feature. Instead it has a rich set of functions to reach into complex data structures without looping constructs.
Doesn't look like there is a way to do this. Really the only way to deal with it is to add a couple of extra lines:
temp <- dim(x)
a <- temp[1]
b <- temp[2]
It depends what is in a and b. If they are just numbers try to return a vector like this:
dim <- function(x,y)
return(c(x,y))
dim(1,2)[1]
# [1] 1
dim(1,2)[2]
# [1] 2
If a and b are something else, you might want to return a list
dim <- function(x,y)
return(list(item1=x:y,item2=(2*x):(2*y)))
dim(1,2)[[1]]
[1] 1 2
dim(1,2)[[2]]
[1] 2 3 4
EDIT:
try this: x <- c(1,2); names(x) <- c("a","b")