Detecting first iteration (cycle) in R loop (without counter) - r

I'd like to detect the first iteration in a loop within a function from inside the body of the loop (i.e., without using some counter variable defined outside the loop), and in the most flexible possible manner.
Here would be one basic solution, just to demonstrate the idea:
vect = c('x', 'y', 'z')
for (elem in vect) {
print(elem)
isfirst(elem, vect)
}
isfirst = function(ele, vec) {
if (ele == vec[1]) {
print('this is the first cycle!')
} else {
print('this is NOT the first cycle!')
}
}
The "problem" with this is that I want this function to be easily reusable in any loop: that means that it should not need loop-specific arguments such as elem and vect. That is: another loop might use e.g. for (my_item in my_list) etc., and so then the isfirst arguments would need to be modified correspondingly, e.g. isfirst(my_item, my_list). The ideal way would be to just have an isfirst() without any arguments needed.
I'm not sure whether this is even possible, but I welcome any ideas.
(About why I need this: I would simply want to provide users with a function that behaves differently based on whether or not the iteration is the first, and that they can flexibly use in any loop and don't need to make even this small adjustment of changing the arguments.)

Well, here is the closest I could get:
vect = c('x', 'y', 'z')
for (elem in enum(vect)) {
print(elem)
isfirst()
}
enum = function(vec) {
assign("first_iteration", TRUE, envir = .GlobalEnv)
vec = mapply(c, 1:length(vec), vec, SIMPLIFY = FALSE) # this is just a small extra, not related to the question
return(vec)
}
isfirst = function() {
if (first_iteration == TRUE) {
print('this is the first cycle!')
assign("first_iteration", FALSE, envir = .GlobalEnv)
} else {
print('this is NOT the first cycle!')
}
}
But I'm still hoping for a better solution.

Related

How do you generate the output of for loop and if statement into a list or vector?

I have tried the following but the output brings an argument stating,
Error in append("0") : argument "values" is miss
for (rowz in final_data$Ingridients) {
Cobalt_row<-lst()
if (sum(str_detect(rowz, 'Cobalt'))>0) {
Cobalt_row.append(1)
} else {
Cobalt_row<-append(0)
}
print(Cobalt_row)
}
I intended to loop through the list and generate a boolean of ones and twos depending on
whether or not I had the value.
Please help
Without the data, I can't test it, but this should work:
Cobalt_row<-lst()
k <- 1
for (rowz in final_data$Ingridients) {
Cobalt_row[[k]] <- ifelse(str_detect(rowz, 'Cobalt'), 1, 0)
k <- k+1
}
or even simpler if you need a list:
Cobalt_row <- as.list(as.numeric(str_detect(final_data$Ingredients, "Cobalt")))

User-defined function does not function, but line-by-line works

I am aiming to define a function that essentially copies a row based on the number of selectable values in another table. Each new row will contain combinations of unique selectable values. I ran the following code on one measure, but plan to use a loop for multiple measures after I can successfully define a function. However, the function does not run, but I can chunk the code and it works fine. Thanks in advance!
output_template_v2<-output_template_v1
measure <- "A"
col <- "1"
add_selOptions_to_output<-function(output_template_v2, measure, col, attributes){
if (tolower(str_sub(attributes$Attribute,-4, -1)) == "_sel"){
selOptions<-attributes[attributes$Measure.Name ==measure & attributes$Attribute == col & attributes$Program == "Blue",]
}
if (length(selOptions$Attribute > 0)){
subcopy<- output_template_v2[output_template_v2$Measure == measure,]
output_template_v2<-output_template_v2[output_template_v2$Measure != measure,]
subcopy<-subcopy[rep(1, length(selOptions$Attribute)),]
}
for (i in seq_along(selOptions)){
subcopy[,col][i]<-selOptions$Attribute[i]
}
output_template_v2 <-rbind(output_template_v2, subcopy)
}
The function works — but it does not modify its function arguments, because function arguments are copied into the function. Instead, the function returns the modified table. This already works, but the assignment in the last line of your function is redundant, so remove it:
add_selOptions_to_output <- function (output_template_v2, measure, col, attributes) {
if (tolower(str_sub(attributes$Product.Attribute, -4L, -1L)) == "_sel") {
selOptions <- attributes[
attributes$Catalog.Measure.Name == measure &
attributes$Product.Attribute == col &
attributes$Program == "Blue", ]
}
if (length(selOptions$Attribute.Values > 0)) {
subcopy <- output_template_v2[output_template_v2$`Measure #` == measure, ]
output_template_v2 <- output_template_v2[output_template_v2$`Measure #` != measure, ]
subcopy <- subcopy[rep(1L, length(selOptions$Attribute.Values)), ]
}
for (i in seq_along(selOptions)) {
subcopy[, col][i] <- selOptions$Attribute.Values[i]
}
rbind(output_template_v2, subcopy)
}
Either way, you’ll need to assign the return value of the function back to the argument with which you’re calling it, e.g.:
tmpl = add_selOptions_to_output(tmpl, measure, col, attributes)

why c() does not working in this recursive function in R?

enter image description here
I know there exists function 'unique' which works similar to what I want to make, but I want to make this function.
I want this function finally returns 'result' which contains unique elements of input vector.
But I don't know why this function's result is totally different from my expect.
Why c which is to combine before result and new unique element is not working.
Please tell me how to fix my code.
Thank you.
I think what you expect might be something like below, where result should be an argument of m_uni:
m_uni <- function(x,result = c()) {
if (class(x)=='numeric'| class(x)=='character') {
if (length(x) <= 1){
return(result)
} else {
if (x[length(x)] %in% result) {
x <- x[-length(x)]
m_uni(x,result)
} else {
result <- c(result,x[length(x)])
x <- x[-length(x)]
m_uni(x,result)
}
}
} else {
return('This function only gets numeric or character vector')
}
}
such that
> m_uni(x)
[1] 0 4 5 -2

R: Collect intermediate output of recursive function

I have a recursive function that uses the output of the previous call as the input of the next call:
recurse_foo = function(input) {
if(identical(input, character(0))) return(NULL)
else {
uu = get_incremental_output(input) ## <-- interested in collecting this
return(recurse_foo(uu))
}
}
As is evident, the terminal output is not very interesting, and I am interested in collecting the intermediate output, but I cannot imagine that growing a global list or any other side effect would be elegant (which is the only thing I can think of).
Any other abstractions that might be useful here?
Thanks.
Specific example:
final_countdown = function(input) {
if (input/2 < 1) return(NULL)
else {
uu = input/2 # <-- interested in collecting this
print(uu)
return(final_countdown(uu))
}
}
final_countdown(100)
In this case, I am interested in collecting the sequence of uus that are printed.
This is a solution, if all intermediate outputs are of the same type:
final_countdown = function(input) {
if (input/2 < 1) return(NA)
else {
c(input, final_countdown(input/2))
}
}

R -- screening Excel rows according to characteristics of multiple cells

I am trying to eliminate all rows in excel that have he following features:
First column is an integer
Second column begins with an integer
Third column is empty
The code I have written appears to run indefinitely. CAS.MULT is the name of my dataframe.
for (i in 1:nrow(CAS.MULT)) {
testInteger <- function(x) {
test <- all.equal(x, as.integer(x), check.attributes = FALSE)
if (test == TRUE) {
return (TRUE)
}
else {
return (FALSE)
}
}
if (testInteger(as.integer(CAS.MULT[i,1])) == TRUE) {
if (testInteger(as.integer(substring(CAS.MULT[i,2],1,1))) == TRUE) {
if (CAS.MULT[i,3] == '') {
CAS.MULT <- data.frame(CAS.MULT[-i,])
}
}
}
}
You should be very wary of deleting rows within a for loop, if often leads to undesired behavior. There are a number of ways you could handle this. For instance, you can flag the rows for deletion and then delete them after.
Another thing I noticed is that you are converting your columns to integers before passing them to your function to test if they are integers, so you will be incorrectly returning true for all values passed to the function.
Maybe something like this would work (without a reproducible example it's hard to say if it will work or not):
toDelete <- numeric(0)
for (i in 1:nrow(CAS.MULT)) {
testInteger <- function(x) {
test <- all.equal(x, as.integer(x), check.attributes = FALSE)
if (test == TRUE) {
return (TRUE)
}
else {
return (FALSE)
}
}
if (testInteger(CAS.MULT[i,1]) == TRUE) {
if (testInteger(substring(CAS.MULT[i,2],1,1)) == TRUE) {
if (CAS.MULT[i,3] == '') {
toDelete <- c(toDelete, i)
}
}
}
}
CAS.MULT <- CAS.MULT[-1*toDelete,]
Hard to be sure without testing my code on your data, but this might work. Instead of a loop, the code below uses logical indexing based on the conditions you specified in your question. This is vectorized (meaning it operates on the entire data frame at once, rather than by row) and is much faster than looping row by row:
CAS.MULT.screened = CAS.MULT[!(CAS.MULT[,1] %% 1 == 0 |
as.numeric(substring(CAS.MULT[,2],1,1)) %% 1 == 0 |
CAS.MULT[,3] == ""), ]
For more on checking whether a value is an integer, see this SO question.
One other thing: Just for future reference, for efficiency you should define your function outside the loop, rather than recreating the function every time through the loop.

Resources