How to Automate NULL Object Creation - r

This question actual just became alot more interesting.
My question is simple. How do I automate this?
g.names<-NULL
g.names1<-NULL
g.names2<-NULL
g.names3<-NULL
g.names4<-NULL
g.names5<-NULL
g.names6<-NULL
g.names7<-NULL
g.names8<-NULL
g.names9<-NULL
g.names10<-NULL
g.names11<-NULL
g.names12<-NULL
gt.names<-list(
g.names1=g.names1,
g.names2=g.names2,
g.names3=g.names3,
g.names4=g.names4,
g.names5=g.names5,
g.names6=g.names6,
g.names7=g.names7,
g.names8=g.names8,
g.names9=g.names9,
g.names10=g.names10,
g.names11=g.names11,
g.names12=g.names12,
g.names=g.names)
gt.names
Initially the problem seemed to be logically solved with the proposed solution below,
g.names <- list()
for (i in 1:13) {
g.names[[i]] <- NULL
}
g.names
Interestingly, this did not work! Assigning NULL to an object within a list is actually equivalent to delete. So, now the question still stands.

The "polluted" way of doing this is through assign and for loop:
x <- c("", 1:12)
y <- paste("g.names", x, sep="")
for (v in y) {
assign(v, NULL)
}
As commenter noted, the method above is very polluted... Here's an example of using list:
g.names <- list()
for (i in 1:13) {
g.names[[i]] <- NULL
}
g.names
Here you would create a list object that hold the values of 13 variables, corresponding to g.names, g.names1, ... , g.names12 in your original example. You can use g.names[[6]] to refer to your g.names5. It's less polluted and more "elegant" I guess.
And if the 13 g.names are going to be just series of characters or numbers, a vector will do:
g.names <- rep(NULL, 13)
g.names
and in this case g.names[6] will be equivalent to g.names5 in your question.

Related

User defined function - issue with return values

I regularly come up against the issue of how to categorise dataframes from a list of dataframes according to certain values within them (E.g. numeric, factor strings, etc). I am using a simplified version using vectors here.
After writing messy for loops for this task a bunch of times, I am trying to write a function to repeatedly solve the problem. The code below returns a subscripting error (given at the bottom), however I don't think this is a subscripting problem, but to do with my use of return.
As well as fixing this, I would be very grateful for any pointers on whether there are any cleaner / better ways to code this function.
library(plyr)
library(dplyr)
#dummy data
segmentvalues <- c('1_P', '2_B', '3_R', '4_M', '5_D', '6_L')
trialvec <- vector()
for (i in 1:length(segmentvalues)){
for (j in 1:20) {
trialvec[i*j] <- segmentvalues[i]
}
}
#vector categorisation
vcategorise <- function(categories, data) {
#categorises a vector into a list of vectors
#requires plyr and dyplyr
assignment <- list()
catlength <- length(categories)
for (i in 1:length(catlength)){
for (j in 1:length(data)) {
if (any(contains(categories[i], ignore.case = TRUE,
as.vector(data[j])))) {
assignment[[i]][j] <- data[j]
}
}
}
return (assignment)
}
result <- vcategorise(categories = segmentvalues, data = trialvec)
Error in *tmp*[[i]] : subscript out of bounds
You are indexing assignments -- which is ok, even if at an index that doesn't have a value, that just gives you NULL -- and then indexing into what you get there -- which won't work if you get NULL. And NULL you will get, because you haven't allocated the list to be the right size.
In any case, I don't think it is necessary for you to allocate a table. You are already using a flat indexing structure in your test data generation, so why not do the same with assignment and then set its dimensions afterwards?
Something like this, perhaps?
vcategorise <- function(categories, data) {
assignment <- vector("list", length = length(data) * length(categories))
n <- length(data)
for (i in 1:length(categories)){
for (j in 1:length(data)) {
assignment[(i-1)*n + j] <-
if (any(contains(categories[i],
ignore.case = TRUE,
as.vector(data[j])))) {
data[j]
} else {
NA
}
}
}
dim(assignment) <- c(length(data), length(categories))
assignment
}
It is not the prettiest code, but without fully understanding what you want to achieve, I don't know how to go further.

R - Saving the values from a For loop in a vector or list

I'm trying to save each iteration of this for loop in a vector.
for (i in 1:177) {
a <- geomean(er1$CW[1:i])
}
Basically, I have a list of 177 values and I'd like the script to find the cumulative geometric mean of the list going one by one. Right now it will only give me the final value, it won't save each loop iteration as a separate value in a list or vector.
The reason your code does not work is that the object ais overwritten in each iteration. The following code for instance does what precisely what you desire:
a <- c()
for(i in 1:177){
a[i] <- geomean(er1$CW[1:i])
}
Alternatively, this would work as well:
for(i in 1:177){
if(i != 1){
a <- rbind(a, geomean(er1$CW[1:i]))
}
if(i == 1){
a <- geomean(er1$CW[1:i])
}
}
I started down a similar path with rbind as #nate_edwinton did, but couldn't figure it out. I did however come up with something effective. Hmmmm, geo_mean. Cool. Coerce back to a list.
MyNums <- data.frame(x=(1:177))
a <- data.frame(x=integer())
for(i in 1:177){
a[i,1] <- geomean(MyNums$x[1:i])
}
a<-as.list(a)
you can try to define the variable that can save the result first
b <- c()
for (i in 1:177) {
a <- geomean(er1$CW[1:i])
b <- c(b,a)
}

incorrect output from for loop inside function

I've created a simple loop to calculate the efficiency of some simulated data. It performs perfectly well whilst as a loop:
NSE_cal <- NULL
for(i in 1:6) {
Qobs <- flowSummary_NSE1[[i]][[3]]
Qsim <- flowSummary_NSE1[[i]][[1]]
object_cal <- NSEsums("NSE")
NSE_cal <- c(NSE_cal, object_cal)
}
#NSE_cal
#[1] 0.8466699 0.7577019 0.8128499 0.9163561 0.7868013 0.8462228
However, I want to apply this loop quite a few times - I need to vary the object flowSummary_NSE# and I have four different transformation types to apply. As a start, I put the loop inside a function, with only transformation needing to be specified, like so:
badFunction <- function(transformation){
NSE_cal <- NULL
for(i in 1:6) {
Qobs <- flowSummary_NSE1[[i]][[3]]
Qsim <- flowSummary_NSE1[[i]][[1]]
object_cal <- NSEsums(transformation)
NSE_cal <- c(NSE_cal, object_cal)
}
print(NSE_cal)
}
badFunction("NSE")
# [1] 0.8462228 0.8462228 0.8462228 0.8462228 0.8462228 0.8462228
The function has exactly the same information input as in the for loop on its own, except, for some reason, it outputs the same value for each case of i.
It is clear that I have done something wrong. But as far as I can see, it must be something simple contained to the function itself. However, incase it is an error elsewhere, I have attached the code that generates the necessary data and dependent functions (here)
Any help would be much appreciated
You need to pass objects into the nested function as arguments.
In your function_NSEsums.r script change the first line to NSEsums <- function(i, Qobs, Qsim) {
In your example_script.r change your code to the following:
badFunction <- function(transformation){
NSE_cal <- NULL
for(i in 1:6) {
Qobs <- flowSummary_NSE1[[i]][[3]]
Qsim <- flowSummary_NSE1[[i]][[1]]
object_cal <- NSEsums(transformation, Qobs = Qobs, Qsim = Qsim)
NSE_cal <- c(NSE_cal, object_cal)
}
print(NSE_cal)
}
badFunction("NSE")
[1] 0.8466699 0.7577019 0.8128499 0.9163561 0.7868013 0.8462228

Trying to vectorize a for loop in R

UPDATE
Thanks to the help and suggestions of #CarlWitthoft my code was simplified to this:
model <- unlist(sapply(1:length(model.list),
function(i) ifelse(length(model.list[[i]][model.lookup[[i]]] == "") == 0,
NA, model.list[[i]][model.lookup[[i]]])))
ORIGINAL POST
Recently I read an article on how vectorizing operations in R instead of using for loops are a good practice, I have a piece of code where I used a big for loop and I'm trying to make it a vector operation but I cannot find the answer, could someone help me? Is it possible or do I need to change my approach? My code works fine with the for loop but I want to try the other way.
model <- c(0)
price <- c(0)
size <- c(0)
reviews <- c(0)
for(i in 1:length(model.list)) {
if(length(model.list[[i]][model.lookup[[i]]] == "") == 0) {
model[i] <- NA
} else {
model[i] <- model.list[[i]][model.lookup[[i]]]
}
if(length(model.list[[i]][price.lookup[[i]]] == "") == 0) {
price[i] <- NA
} else {
price[i] <- model.list[[i]][price.lookup[[i]]]
}
if(length(model.list[[i]][reviews.lookup[[i]]] == "") == 0) {
reviews[i] <- NA
} else {
reviews[i] <- model.list[[i]][reviews.lookup[[i]]]
}
size[i] <- product.link[[i]][size.lookup[[i]]]
}
Basically the model.list variable is a list from which I want to extract a particular vector, the location from that vector is given by the variables model.lookup, price.lookup and reviews.lookup which contain logical vectors with just one TRUE value which is used to return the desired vector from model.list. Then every cycle of the for loop the extracted vectors are stored on variables model, price, size and reviews.
Could this be changed to a vector operation?
In general, try to avoid if when not needed. I think your desired output can be built as follows.
model <- unlist(sapply(1:length(model.list), function(i) model.list[[i]][model.lookup[[i]]]))
model[model=='']<-NA
And the same for your other variables. This assumes that all model.lookup[[i]] are of length one. If they aren't, you won't be able to write the output to a single element of model in the first place.
I would also note that you are grossly overcoding, e.g. x<-0 is better than x<-c(0), and don't bother with length evaluation on a single item.

Keep assigned objects in workspace through a function

I am trying to keep an assigned object from a function (building a ts function to begin to model a univariate process, simple I know!). I am having trouble finding a method to keep objects in my workspace. It works fine just using a for loop but I would like to parameterize the following:
ts.builder<-function(x,y,z){
for(i in 9:13){
assign(paste(x,i,sep="_"),ts(yardstick[1:528,i], freq=24))
assign(paste(y,i,sep="_"),ts(yardstick[529:552,i], freq=24))
assign(paste(z,i,sep="_"),ts(yardstick[1:552,i], freq=24))
}
}
ts.builder("yard.book.training","yard.book.small.valid", "yard.book.valid")
Any pointers?
I am thinking it may need a return statement, yet I have not found this to be of use yet.
Untested (a reproducible example helps a lot):
ts.builder <- function() {
xd <- list()
yd <- list()
zd <- list()
for (i in 9:13) {
xd[[i]] <- ts(yardstick[1:528,i], freq=24)
yd[[i]] <- ts(yardstick[529:552,i], freq=24)
zd[[i]] <- ts(yardstick[1:552,i], freq=24)
}
list(yard.book.training=xd, yard.book.small.valid=yd, yard.book.valid=zd)
}
l <- ts.builder()
Then here are the returned values:
l$yard.book.training[[9]]
etc.

Resources