I have come across a behaviour of R that I can't quite get my head around and after a while browsing older threads I am still not sure what it is that I am not getting right.
Here is a minimal example:
ls <- list(c(1,2), c(3,4))
names(ls) <- c("one", "two")
Creates a list with the the following structure:
$one
1 2
$two
3 4
I can access an element as:
ls$one
Which returns
1 2
But if I try with a loop e. g.:
for (i in names(ls)) {
print(ls$i)
}
It returns
NULL
NULL
Does someone know what is the problem? Is it some slight modification that is needed or R prefers a fundamentally different approach for solving such problems?
Related
All of the below is conducted in R.
I am trying to store the result of a for-loop result in different containers, but somehow I keep ending up with NA-warnings and the results are not not stored in my container. Even tried different containers for different for-loops within the function and then finally a matrix for the containers, but it seems it's not working.
Already trying different solutions for two full days, and it seems there should be such an easy solution. Maybe I just can't see it myself anymore...
data.ols<-data.frame(cbind(rep(1),holiday,weathersit,atemp,hum,windspeed))
y<-as.vector(cnt)
z=c(holiday, weathersit, atemp, hum, windspeed)
z.names=c("holiday","weathersit","atemp","hum","windspeed")
result.container<-data.frame(matrix(nrow=6,ncol=4))
colnames(result.container)<-c("beta","SE","t-statistic","p-value")
ols<-function(y,X2,x=0){
X<-matrix(z, ncol=5)
X2<-cbind(rep(1, nrow(X)), X)
XXinv <- solve(t(X2) %*% X2, diag(ncol(X2))) # Compute (X'X)^-1
beta<-XXinv%*%t(X2)%*%y
print(beta)
result.container[,1]<-beta
result.testdebug<-vector()
for (i in c("V1","holiday","weathersit","atemp","hum","windspeed")){
SE<-sd(i)
result.testdebug[i]<-sd(data.ols[,i])
return(result.testdebug)
result.container[,2]<-result.testdebug}
result.testtvalue<-vector()
for (i in c("V1","holiday","weathersit","atemp","hum","windspeed")){
nominator<-(mean(i)-x)
t.value <- nominator/sd(i)
return(t.value)
result.testtvalue<-t.value
result.container[,3]<-result.testtvalue}
df <- length(X)-1
p.value <- 2*pt(t.value, df, lower.tail=FALSE)
return(p.value)
result.container[,4]<-p.value
list(rbind(beta,result.testdebug,t.value,p.value))}
It seems you are having some trouble with functions in R. In R, functions have their own environment (i.e. their own set of objects). Even though they can read from their parent environment (the set of all objects), they cannot write on it. Let me demonstrate that with a simpler code.
teste2=matrix(,2,2)
teste=function(a,b) {teste2[,1]=c(a,b)}
teste(3,2)
teste2
[,1] [,2]
[1,] NA NA
[2,] NA NA
As you can see teste (the function) cannot change teste2 (the matrix).
In R ,the best way to make a function is to give it all the objects it needs as parameters and by the end of the function body, to give a single return() function that gives the final object.
You did something close to that, but used multiple return() functions. R only uses the first return() and ignores the rest. See below:
teste=function(a,b) {c=a;return(c);d=b;return(d)}
teste(3,2)
[1] 3
For your particular code, I reccomend excluding all result.container<- and put return() only on the end, around that last (list)
There is a list called "G" and i am trying to replace any numbers above 5 with smile ":)" symbol and any number below 5 with ":(". i wrote this code, and expecting to only have five smiles however, the result is totally different and smile appears for all numbers. Thanks if anyone tell me what's wrong with this very simple and basic code.
`G <- list(1,2,3,4,5,6,7,8,9,10)
B <- for (i in 1:length(G))
if (any(G> 5)) {
print(":)")
} else {
print(":(")
} `
The following points will help with the question and understanding of R for the future.
Lists vs. Vectors
For loops vs. Vectorization
print with assignment
Lists
In R, the list object is a special object that can hold other objects. They can hold almost anything, even other lists! Because they are so accepting of a mix of things, they are not equipped with all of the cool functions like vectors.
The vector is a simple R object with a common type. "Common type" means that the entire vector will either be numbers, or character values, or whatever other type we are using. If we created c(1,"a") we will have mixed a letter with a number. These two types will be forced to be only one type, character. The quantitative functions will not work with it anymore. But the list list(1,"a") will hold each as its own type.
In your case, you have a series of whole numbers. When the numbers are in a vector, we can apply many built-in functions to work with them that will not work with the generic list. Functions like sum, mean, and sd, are built to accept vectors. But they do not work with lists. We should change G to a vector:
G <- c(1,2,3,4,5,6,7,8,9,10)
#Or a bit shorter
G <- 1:10
For loops and Vectorization
In R, because vectors can only be of one type, many cool things are possible. I can do G + 1 and R will add one to each element of G. It completed the whole thing without a loop. For conditional statements like yours we can vectorize also:
ifelse(G > 5, ":)", ":(")
Print with assignment
The print function can be saved but it is better to simply capture the output itself as is:
#Don't do
x <- print("hello")
#Do
x <- "hello"
Both work, but the second is more in line with R programming. The reason B did not save the output in your case is because you attempted to save the for loop itself. If you would like to save the output of a loop, do it within the loop since the output will be dumped upon completion.
To summarize, we can simplify to:
G <- 1:10
B <- ifelse(G > 5, ":)", ":(")
#[1] ":(" ":(" ":(" ":(" ":(" ":)" ":)" ":)" ":)" ":)"
To follow up on what Pierre mentioned: lists in R are different from Python. What you're looking for is a vector, you can read more about them here. You can initialize a vector with c() like:
test <- c(1,2,3,4,5,6,7,8,9,10)
You can access a vector using it’s index by putting it in brackets []. For example, in the vector nouns <- c(“cat”, “dog”, “tree”) using nouns[2] will return ”dog”.
A functioning version of your code is:
G <- c(1,2,3,4,5,6,7,8,9,10)
for (i in 1:length(G))
{ #missed loop curly bracket on this line
if (G[i] > 5) {
print(":)") #if you want to assign the output of a loop do it here
} else {
print(":(")
}
}
Edit: Pierre beat me to it! Leaving up but Pierre has a more thorough answer.
I am just beginning to learn R and am having an issue that is leaving me fairly confused. My goal is to create an empty vector and append elements to it. Seems easy enough, but solutions that I have seen on stackoverflow don't seem to be working.
To wit,
> a <- numeric()
> append(a,1)
[1] 1
> a
numeric(0)
I can't quite figure out what I'm doing wrong. Anyone want to help a newbie?
append does something that is somewhat different from what you are thinking. See ?append.
In particular, note that append does not modify its argument. It returns the result.
You want the function c:
> a <- numeric()
> a <- c(a, 1)
> a
[1] 1
Your a vector is not being passed by reference, so when it is modified you have to store it back into a. You cannot access a and expect it to be updated.
You just need to assign the return value to your vector, just as Matt did:
> a <- numeric()
> a <- append(a, 1)
> a
[1] 1
Matt is right that c() is preferable (fewer keystrokes and more versatile) though your use of append() is fine.
sorry for the ugly code, but I'm not sure exactly what's going wrong
for (i in 1:1)
tab_sector[1:48,i] <-
tapply(get(paste("employee",1997-1+i, "[birth<=(1997-1+i)]",sep="")),
ordered(sic2digit[birth<=(1997-1+i)],levels=tab_sector_list))
# Error in get(paste("employee", 1997 - 1 + i,
# "[birth<=(1997-1+i))]", : object 'employee97[birth<=(1997-1+i)]' not found
but the variable is there:
head(employee97[birth<=(1997-1+i)])
# [1] 1 2 2 1 3 4
a simpler version where "employee" is not conditioned by "birth" works
It would help if you told us what you are trying to accomplish.
In your code the get function is looking for a variable whose name is "'employee97[birth<=(1997-1+i)]", the code that works is finding a variable whose name is "employee1997" then subsetting it, those are very different. The get function does not do subsetting.
Part of what you are trying to do is FAQ 7.21, the most important part of which is the end where it suggests storing your data in lists to make accessing easier.
You can't get an indexed element, e.g. get("x[i]") fails: you need get("x")[i].
Your code is almost too messy too see what's going on, but this is an attempt at a translation:
for (i in 1:1){
ind <- 1997-1+i
v1 <- get(paste0("employee",ind))
tab_sector[1:48,i] <- tapply(v1[birth<=ind],
ordered(sic2digit[birth<=ind],levels=tab_sector_list))
}
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")