Search code examples
rlistloopsformattable

R : value not replaced in for loop with a function


I am trying to create a list which I will then put into a function.

My problem is that "col" is never replaced by the increment variable (j). It remains "j". So at the end of my for loop when j is 11 all the col of all elements in the list are also j so 11 if I apply it to my function.

library(formattable)

x <- data.frame("value" = rep(1:5,2), "cats" = c(10,20,30,40,NA), "dogs" = c(15,25,35,36,48), "fish" = c(20,30,14,89,NA), "chicken"=c(14,23,12,9,3), "test"=c(TRUE,TRUE,FALSE, TRUE,FALSE))

l=list()
for (j in 1:ncol(x)) {
  if (is.na(x[nrow(x),j])) {
    l[[j]]=area(row=1:(nrow(x)-1), col=j) ~ color_bar(color = "#E0E0E0", na.rm = TRUE)
  } 
  else {
    l[[j]]=area(row=1:(nrow(x)-1), col=j) ~ color_bar(color = "#99CCFF", na.rm = TRUE)
  }
}
l
formattable(x, l)

What could be the problem?


Solution

  • This happens because

    The function creates an area object to store the representation of row and column selector expressions. When the function is called, the expressions and environment of row and column are captured for format_table to evaluate within the context of the input data.frame, that is,rownames and colnames are defined in the context to be the indices of rows and columns, respectively. Therefore, the row names and column names are available symbols when row and col are evaluate

    (emphasis added, from help("area")).

    So it's accessing the value bound to the symbol j.

    What you want to do instead is pass it a formula with the proper value of j:

    l=list()
    for (j in 1:ncol(x)) {
        if (is.na(x[nrow(x),j])) {
            print("yes")
            l[[j]] <- as.formula(
                paste("area(row=1:(nrow(x)-1), col=", j, ") ~ color_bar(",
                      "color = \"#E0E0E0\", na.rm = TRUE)")
            )
        } 
        else {
            print("no")
            l[[j]] <- as.formula(
                paste("area(row=1:(nrow(x)-1), col=", j, ") ~ color_bar(",
                      "color = \"#99CCFF\", na.rm = TRUE)")
            )
        }
    }
    l
    formattable(x, l)
    

    enter image description here

    Update: Overall proportion

    By default, with this approach, the size of the bars will be the within-column proportions. If you want overall proportions, you can pass a custom fun argument to color_bar() like so:

    l=list()
    for (j in 1:ncol(x)) {
        if (is.na(x[nrow(x),j])) {
            print("yes")
            l[[j]] <- as.formula(
                paste("area(row=1:(nrow(x)-1), col=", j, ") ~ color_bar(",
                      "color = \"#E0E0E0\",",
                      "fun = function(y) y / max(x, na.rm = TRUE))")
            )
        } 
        else {
            print("no")
            l[[j]] <- as.formula(
                paste("area(row=1:(nrow(x)-1), col=", j, ") ~ color_bar(",
                      "color = \"#99CCFF\",",
                      "fun = function(y) y / max(x, na.rm = TRUE))")
            )
        }
    }
    l
    formattable(x, l)
    

    enter image description here