R not printing the right thing in loop - r

I am trying to print the "result" of using table function, but when I tried to use the code here, I got something very strange:
for (i in 1:4){
print (table(paste("group",i,"$", "BMI_obese",sep=""), paste("group",i,"$","A1.1", sep="")))
}
This is the result in R output:
group1$A1.1
group1$BMI_obese 1
group2$A1.1
group2$BMI_obese 1
group3$A1.1
group3$BMI_obese 1
group4$A1.1
group4$BMI_obese 1
But when I type out the statement without typing inside the loop:
table(group2$BMI_obese, group2$A1.1)
I got what I want:
1 2 3 4 5
0 51 20 9 8 0
1 37 20 15 6 4
Does anyone know which part of my for loop code is not correct or can be modified to fit my purpose of printing the loop table result?
Hi, all but now I have another problem. I am trying to add an inner loop which will take the column name as an argument, because I would like to loop through mulitiple column for each of the group data (i.e. for group1, I would like to have table of BMI_obese vs A1.1, BMI_obese vs A1.2 ... BMI_obese vs A1.15. This is my code, but somehow it is not working, I think it is because it is not recognizing the A1.1, A1.2,... as an column taking from the data group1, group2, group3, group4. But instead it is treated as a string I think. I am not sure how to fix it:
for (i in 2:4) {
for (j in c("A1.1","A1.2"))
{
print(with(get(paste0("group", i)),table(BMI_obese,j)))
}
}
I keep getting this error message:
Error in table(BMI_obese, j) : all arguments must have the same length

Okay, you are trying to construct a variable name using paste and then do a table. You are simply passing the name of the variable to table, not the variable object itself. For this sort of approach you want to use get()
for (i in 1:4) {
with(get(paste0("group", i), table(BMI_obese, A1.1))
}
#example saving as a list (using lapply rather than for loop)
group1 <- data.frame(x=LETTERS[1:10], y=(1:10)[sample(10, replace=TRUE)])
group2 <- data.frame(x=LETTERS[1:10], y=(1:10)[sample(10, replace=TRUE)])
result <- lapply(1:2, function(i) with(get(paste0("group", i)), table(x, y)))
#look at first six rows of each:
head(result[[1]])
head(result[[2]])
#example illustrating fetching objects from a string name
data(mtcars)
head(with(get("mtcars"), table(disp, cyl)))
head(with(get("mtcars"), table(disp, "cyl")))
#Error in table(disp, "cyl") : all arguments must have the same length
head(with(get("mtcars"), table(disp, get("cyl"))))

You could also use a combination of eval and parse like this:
x1 <- c(sample(10, 100, replace = TRUE))
y1 <- c(sample(10, 100, replace = TRUE))
table(eval(parse(text = paste0("x", 1))),
eval(parse(text = paste0("y", 1))))
But I'd also say it is not the nicest practice to access variables that way...

Your types are used wrong. See the difference:
table(group2$BMI_obese, group2$A1.1)
and
table(paste(...),paste(...))
So what type does paste return? Certainly some string.
EDIT:
paste(...) was not meant to be syntactically correct but an abbreviation for paste("group",i,"$", "BMI_obese",sep=""), or whatever you paste together.
paste(...) is returning some string. If you put that result into a table, you get a table of strings (the unexpected result that you got). What you want to do is acessing variables or fields with the name which is returned by your paste(...). Just an an eval to your paste like Daniel said and do it like this.
for (i in 1:4){
print (table(eval(paste("group",i,"$", "BMI_obese",sep="")),eval(paste("group",i,"$","A1.1", sep=""))))
}

Related

how to load() and view() a function argument [i.e. function(x) ] in R

I'm having trouble with load and view. I want to write functions that will allow me to do a whole lot of things by just naming what will be a dataframe
# example dataset
n <- 3
dat <- data.frame(
c1=sample(1:5, n, replace=TRUE),
c2=sample(1:5, n, replace=TRUE) )
save(dat, file = "dat.rda")
# my function to load and view a dataframe with a single call
jtest <- function(x) {
load( paste0( x,".Rda") )
View( paste0( x,".Rda") )
}
jtest("dat")
My desired output would be that it loads up the dataframe dat
c1 c2
1 1 3
2 1 5
3 5 5
Instead, when I run jtest("dat") it opens something with the same icon as a dataframe named paste0(x, ".Rda"), one column named V1, one row named "1", and a cell value of dat.Rda:
V1
1 dat.Rda
I've looked at a couple of options, trying to wrap variously in as.name() or use assign() but I must be really thick because I'm not getting it!
Ref:
Error in object[seq_len(ile)] : object of type 'symbol' is not subsettable
View needs a R object, you are passing character value to it. You can use get to get R object from it's name.
jtest <- function(x) {
load(paste0(x,".Rda"))
View(get(x), title = x)
}
jtest("dat")

Concerning R, when defining a Replacement Function, do the arguments have to be named as/like "x" and "value"?

By "replacement functions" I mean those mentioned in this thread What are Replacement Functions in R?, ones that look like 'length<-'(x, value). When I was working with such functions I encountered something weird. It seems that a replacement function only works when variables are named according to a certain rule.
Here is my code:
a <- c(1,2,3)
I will try to change the first element of a, using one of the 3 replacement functions below.
'first0<-' <- function(x, value){
x[1] <- value
x
}
first0(a) <- 5
a
# returns [1] 5 2 3.
The first one works pretty well... but then when I change the name of arguments in the definition,
'first1<-' <- function(somex, somevalue){
somex[1] <- somevalue
somex
}
first1(a) <- 9
# Error in `first1<-`(`*tmp*`, value = 9) : unused argument (value = 9)
a
# returns [1] 5 2 3
It fails to work, though the following code is OK:
a <- 'first1<-'(a, 9)
a
# returns [1] 9 2 3
Some other names work well, too, if they are similar to x and value, it seems:
'first2<-' <- function(x11, value11){
x11[1] <- value11
x11
}
first2(a) <- 33
a
# returns [1] 33 2 3
This doesn't make sense to me. Do the names of variables actually matter or did I make some mistakes?
There are two things going on here. First, the only real rule of replacement functions is that the new value will be passed as a parameter named value and it will be the last parameter. That's why when you specify the signature function(somex, somevalue), you get the error unused argument (value = 9) and the assignment doesn't work.
Secondly, things work with the signature function(x11, value11) thanks to partial matching of parameter names in R. Consider this example
f<-function(a, value1234=5) {
print(value1234)
}
f(value=5)
# [1] 5
Note that 5 is returned. This behavior is defined under argument matching in the language definition.
Another way to see what's going on is to print the call signature of what's actually being called.
'first0<-' <- function(x, value){
print(sys.call())
x[1] <- value
x
}
a <- c(1,2,3)
first0(a) <- 5
# `first0<-`(`*tmp*`, value = 5)
So the first parameter is actually passed as an unnamed positional parameter, and the new value is passed as the named parameter value=. This is the only parameter name that matters.

Substituting variables in a loop?

I am trying to write a loop in R but I think the nomenclature is not correct as it does not create the new objects, here is a simplified example of what I am trying to do:
for i in (1:8) {
List_i <-List
colsToGrab_i <-grep(predefinedRegex_i, colnames(List_i$table))
List_i$table <- List_i$table[,predefinedRegex_i]
}
I have created 'predefinedRegex'es 1:8 which the grep should use to search
The loop creates an object called "List_i" and then fails to find "predefinedRegex_i".
I have tried putting quotes around the "i" and $ in front of the i , also [i] but these do not work.
Any help much appreciated. Thank you.
#
Using #RyanGrammel's answer below::
#CREATING regular expressions for grabbing sets groups 1 -7 ::::
g_1 <- "DC*"
g_2 <- "BN_._X.*"
g_3 <- "BN_a*"
g_4 <- "BN_b*"
g_5 <- "BN_a_X.*"
g_6 <- "BN_b_X.*"
g_7 <- "BN_._Y.*"
for i in (1:8)
{
assign(x = paste("tableA_", i, sep=""), value = BigList$tableA)
assign(x = paste("Forgrep_", i, sep=""), value = colnames(get(x = paste("tableA_", i, sep=""))))
assign(x = paste("grab_", i, sep=""), value = grep((get(x = paste("g_",i, sep=""))), (get(x = paste("Forgrep_",i, sep="")))))
assign(x = paste("tableA_", i, sep=""), value = BigList$tableA[,get(x = paste("grab_",i, sep=""))])
}
This loop is repeated for each table inside "BigList".
I found I could not extract columnnames from
(get(x = paste("BigList_", i, "$tableA" sep=""))))
or from
(get(x = paste("BigList_", i, "[[2]]" sep=""))))
so it was easier to extract the tables first. I will now write a loop to repack the lists up.
Problem
Your syntax is off: you don't seem to understand how exactly R deals with variable names.
for(i in 1:10) name_i <- 1
The above code doesn't assign name_1, name_2,....,name_10. It assigns "name_i" over and over again
To create a list, you call 'list()', not List
creating a variable List_i in a loop doesn't assign List_1, List_2,...,List_8.
It repeatedly assigns an empty list to the name 'List_i'. Think about it; if R names variables in the way you tried to, it'd be equally likely to name your variables L1st_1, L2st_2...See 'Solution' for some valid R code do something similar
'predefinedRegex_i' isn't interpreted as an attempt to get the variable 'predefinedRegex_1', 'predefinedRegex_2', and so one.
However, get(paste0("predefinedRegex_", i)) is interpreted in this way. Just make sure i actually has a value when using this. See below.
Solution:
In general, use this to dynamically assign variables (List_1, List_2,..)
assign(x = paste0("prefix_", i), value = i)
if i is equal to 199, then this code assigns the variable prefix_199 the value 199.
In general, use this to dynamically get the variables you assigned using the above snippet of code.
get(x = paste0("prefix_", i))
if i is equal to 199, then this code gets the variable prefix_199.
That should solve the crux of your problem; if you need any further help feel free to ask for clarification here, or contact me via my Twitter Feed.

Assign names from one list to another

I got a bunch dynamically created regressions stored in some list called regressions. Now I´d like to rename their coefficients efficiently. What I have so far is this loop that works:
for (i in 1:length(params[,1])){
names(regressions[[i]]$coefficients)[pos] <- paste(params[i,1],".lag",params[i,2],sep="")
}
I've been trying for quite a while to get this done a little more generally with the help of a function, cause this not the only list of regressions I have. However I could not get anything else to work. Here a few other tries basically based on lapply:
correctNames <- function(reglist,namevec,pos){
names(reglist[[i]]$coefficients)[pos] <- as.character(namevec)
}
lapply(regressions,correctNames(reglist,namevec,pos),
reglist=regressions,namevec=params[,1],pos=2)
Another try was to write a function with a for loop which also works internally as print shows but does not assign the names globally (where the regressions list is stored).
correctNames <- function(reglist,pos,namevec){
for (i in 1:length(params[,1])){
names(reglist[[i]]$coefficients)[pos] <- paste(namevec,".lag",namevec,sep="")
}
#this test proves it's work inside the function...
print(reglist[[10]]
}
Ah, gimme a break.
There's no "i" inside that first version of "correctNames" function; and you probably don't realize that you are not assigning it to "regressions", only to a copy of the regression object. Try instead:
correctNames <- function(reglist,namevec,pos){
names(reglist$coefficients)[pos] <- as.character(namevec)
return(reglist) }
newregs <- mapply(correctNames,
reglist=regressions,
namevec=as.character(params[,1]),
MoreArgs= list( pos=2))
After seeing the note from Ramnath and noticing that the code did work but was giving flaky names for the "params" I looked at params and saw that it was a factor, and so changed the argument in the mapply call to as.character(params[,1]).
> newregs[1,1]
[[1]]
(Intercept) log(M1)
-5.753758 2.178137
If this is a follow up to your earlier question, then here is what I would do
coefs = plyr::ldply(regressions, coef)
coefs = transform(coefs, reg_name = paste(x, '.lag', l, sep = ""))[,-c(1, 2)]
names(coefs) = c('intercept', 'reg_coef', 'reg_name')
This gives you
intercept reg_coef reg_name
1 -5.753758 2.178137 log(M1).lag0
2 7.356434 7.532603 rs.lag0
3 7.198149 8.993312 rl.lag0
4 -5.840754 2.193382 log(M1).lag1
5 7.366914 7.419599 rs.lag1
6 7.211223 8.879969 rl.lag1
7 -5.988306 2.220994 log(M1).lag4
8 7.395494 7.127231 rs.lag4
9 7.246161 8.582998 rl.lag4

How to pass function arguments to within?

I wonder how I can pass arguments of some function to a subpart of the function that uses with / within. e.g.:
myfunction <- function(dataframe,col1,col2){
res <- within(dataframe, somenewcol <-paste(col1,"-","col2",sep=""))
return(res)
}
where col1 and col2 are columns contained in the dataframe. What´s the correct way to pass the arguments col1 and col2 to the within expression? When I just try to use it, i get :
Error in paste(col1, "-", , :
object 'Some_passed_col' not found
Here´s an example:
dataset <- data.frame(rnorm(20),2001:2020,rep(1:10,2))
names(dataset) <- c("mydata","col1","col2")
myfunction <- function(dataframe,arg1,arg2){
res <- with(dataframe, onesinglecol <- paste(arg1,"-","arg2",sep=""))
return(res)
}
# call function
myfunction(dataset,col1,col2)
EDIT:
the following works for me now, but I cannot completely understand why... so any further explanation is appreciated:
myfunction(dataset,arg1="col1",arg2="col2")
if I adjust
res <- with(dataframe, onesinglecol <- paste(get(arg1),"-",get(arg2),sep=""))
Try
myfunction <- function(dataframe,arg1,arg2){
dataframe["onesinglecol"] <- dataframe[[arg1]] -dataframe[[arg2]]
return(dataframe)
}
And call it with character-valued column names rather than object names that are nowhere defined:
myfunction(dataset,"col1","col2")
mydata col1 col2 onesinglecol
1 0.6834402 2001 1 2000
2 1.6623748 2002 2 2000
3 -0.5769926 2003 3 2000 .... etc
I think this is done via the ... directive:
E.g.:
myfunction <- function(dataframe, ...){
var <- anotherfunction( arg1= 1, arg2 = 2 , ...)
return(var)
}
... is a placeholder for additional arguments passed through to "anotherfunction".
You are missing the fact that col1 and col2 do not exist in dataframe (from your) nor in the user workspace.
Basically, with() and within() work like this:
> foo <- 10
> bar <- data.frame(FOO = 10)
> FOO + foo
Error: object 'FOO' not found
> with(bar, FOO + foo)
[1] 20
In the first case, FOO was not found as it is inside bar. In the second case, we set up an environment within which our expression is evaluated. Inside that environment FOO does exist. foo is also found in the workspace.
In your first example (please don't edit error messages etc, show us exactly what code you ran and what error was produced) either one col1 or col2 didn't exist in the environment created within which your expression was evaluated.
Further, you appear to want to store in col1 and col2 the name of a column (component) of your dataframe. DWin has shown you one way to use this information. An alternative maintaining the use of within() is to use get() like this:
res <- within(dataframe, somenewcol <- paste(get(col1), "-", get(col2), sep=""))
Why this works, as per your extra edit and quandary, is that get() returns the object named by it's first argument. get("foo") will return the object named foo (continuing from my example above):
get("foo") ## finds foo and returns it
1 10
In your example, you have a data frame with names inter alia "col1" and "col2". You changed your code to get(arg1) (where arg1 <- "col1"), where you are asking get() to return the object with name "col1" from the evaluation environment visible at the time your function is being evaluated. As your dataframe contained a col1 component, which was visible because within() had made it available, get() was able to find an object with the required name and include it in the expression.
But at this point you are trying to jump through too many hoops because your questions haven't been specific. I presume you are asking this because of my answer here to your previous Q. That answer suggested a better alternative than attach(). But you weren't clear there what some arguments were or what your really wanted to do. If I had known now what you really wanted to do then I would have suggested you use DWin's Answer above.
You seem to not want to hard code the column/component names. If you could hard code, this would be the solution:
res <- within(dataframe, somenewcol <- paste(col1, "-", col2, sep = ""))
But seeing as you don't want to hard code you need a get() version or DWin's solution.

Resources