I have a function that will return a numeric class object, here is a simplified version:
count.matches <- function(x) {
ifelse(!is.na(a[,x] & a[,2]),1,0)
}
it just produces an object of 0s and 1s.
For example
count.matches(4)
[1] 0 0 0 0 1 1 0
I just want to do a simple for loop of this function and store the results in a data frame, i.e. each time the function loops through create a column, however I am having trouble.
p <- data.frame()
my.matches <- for(i in 2:100) {
p[i,] <- count.matches(i)
}
This produces nothing. Sorry if this is a really stupid question, but I have tried a bunch of things and nothing seems to work. If you need any more information, please let me know and I will provide it.
for does not return the last value in the loop and combine the results. It returns NULL.
Using for you will have to create the data.frame first, and fill it with the results:
my.matches <- as.data.frame(matrix(0, nrow(a), ncol(a) - 1))
for (i in 2:100) {
my.matches[,i] <- count.matches(i)
}
Alternative you could use the foreach package, which provides the functionality you're expecting from for.
library(foreach)
my.matches <- foreach(i = 2:100, .combine=data.frame) %do% {
count.matches(i)
}
Related
I'm a new guy in the R world and had to create a vector.
data <- rnorm(10, 0, 1)
Next question asked for a loop so I did:
for(i in 1:length(data)){
if(data[i] > 0)
print("postive")
else
print("negative")
But now it's asking for:
"Write a function called “clean data” that takes in a vector of numbers and returns a vector called “ret” of same length such that ret[i] = 1 if the input vector ith element was positive, and ret[i] = 0 otherwise. To get started, make a separate R Block for your function and use the following shell:
clean data <- function(input){ # your code here [...]
# ...
# your code here [...] return(ret) }
Professor also recommends reusing the loop from earlier.
I believe this is what you are looking for. Please note how ifelse is being used in the function instead of if and else functions separately. As you can see, this function would work with numeric string as input, but I've added an instance of it running on the previous data you've created. But I would like to add that the purpose of these exercices is really to make we scratch our heads and try to work the problem out for ourselves, so I recommend you persist on trying next time :)
data <- rnorm(10, 0, 1)
for(i in 1:length(data)){
if(data[i] > 0) print("positive") else print("negative")
}
clean_data <- function(input){
ret <- NULL
for(i in 1:length(input)){
ifelse(input[i] > 0, ret[i] <- 1 , ret[i] <- 0) # note ifelse structure
}
return(ret)
}
clean_data(data)
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.
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)
}
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.
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.