Static Variables in R

Static Variables in R

Here's one way by using a closure (in the programming language sense), i.e. store the count variable in an enclosing environment accessible only by your function:

make.f <- function() {
count <- 0
f <- function(x) {
count <<- count + 1
return( list(mean=mean(x), count=count) )
}
return( f )
}

f1 <- make.f()
result <- f1(1:10)
print(result$count, result$mean)
result <- f1(1:10)
print(result$count, result$mean)

f2 <- make.f()
result <- f2(1:10)
print(result$count, result$mean)
result <- f2(1:10)
print(result$count, result$mean)

Emulating static variable within R functions

Define f within a local like this:

f <- local({ 
static <- 0
function() { static <<- static + 1; static }
})
f()
## [1] 1
f()
## [1] 2

How to keep some variables static for the summary in R

Base R Solution:

# Vector containing the all unique elements of the uid vector: 
# unique_ids => character vector:
reporting_vars <- c("seq", "marking", "batch_no")

# Empty list to store all unique reported vector's values: report_struc_list => list
report_struc_list <- vector("list", length(reporting_vars))

# Populate the list: report_struc_list => list
report_struc_list <- lapply(df[, reporting_vars], function(x){sort(unique(x))})

# Simplify to a data.frame: report_struc => data.frame
report_struc <- cbind(
data.frame(lapply(report_list, function(x) {
length(x) <- max(lengths(report_list))
return(x)
})),
counter = 0,
perc = 0
)

# Order the reporting data.frame: report_struc_ordered => data.frame
report_struc_ordered <- report_struc[, c("seq", "marking", "batch_no",
"counter", "perc")]

# Function to generate reports, input data.frame: analysed_df => data.frame
report_func <- function(df){
# Function to count elements and calculate perc of total:
# analyse_func => function
analyse_func <- function(df, vec){
vec_summary <- data.frame(lapply(within(
merge(rbind(setNames(
aggregate(
rep(1, nrow(df))~vec,
df,
FUN = sum,
na.action = na.pass
),
c(gsub(".*\\$", "", deparse(
substitute(vec)
)), "counter")
), c(NA, sum(
is.na(df[, gsub(".*\\$", "", deparse(substitute(vec)))])
))),
report_struc_ordered[!(report_struc_ordered[, gsub(".*\\$", "", deparse(substitute(vec)))]
%in% vec),
c(grep(
gsub(".*\\$", "", deparse(substitute(vec))),
names(report_struc_ordered),
value = TRUE
),
"counter", "perc")],
all = TRUE),
{
perc = paste0(round(counter / sum(counter) * 100, 2), "%")

}
), as.character), stringsAsFactors = FALSE)

# Append a total to the bottom of the data.frame: vec_summary => data.frame
vec_summary <- setNames(rbind(vec_summary,
c("TOTAL",
as.character(sum(
as.numeric(vec_summary$counter)
)),
as.character(paste0(
sum(as.double(gsub(
"\\%", "",
vec_summary$perc
))),
"%"
)))), c(gsub(".*\\$", "", deparse(substitute(vec))),
paste(gsub(".*\\$", "",
deparse(substitute(vec))),
names(vec_summary)[2:ncol(vec_summary)],
sep = "_")))
}

# Apply the function to each of the vectors required: vec_summ_list => list
vec_summ_list <- list(
seq_df = analyse_func(df, df$seq),
marking_df = analyse_func(df, df$marking),
batch_no_df = analyse_func(df, df$batch_no)
)

# Store a scalar containing the row count of the data.frame
# with the most rows in the vec_summ_list: max_df_length => numeric vector
max_df_length <- max(sapply(vec_summ_list, nrow))

# Extend each data.frame to be the same length
# (pad with NAs if necessary): vec_summ_list => list
vec_summ_list <- setNames(lapply(seq_along(vec_summ_list), function(i){
# Replicate the amount rows required to be padded: y => data.frame
y <- data.frame(vec_summ_list[[i]][rep(seq_len(max_df_length - nrow(vec_summ_list[[i]])),
each = 1),])
# Nullify the rows: y => data.frame
y[1:(nrow(y)),] <- "-"

# If necessary bind the replicated rows to the underlying data.frame:
# x => data.frame
if(length(y) > 0){
x <- data.frame(rbind(vec_summ_list[[i]], y)[1:max_df_length,])
}else{
x <- data.frame(df_list[[i]][1:max_df_length,])
}
# Move the total row to the bottom of the data.frame: x => data.frame
x[nrow(x),] <- x[which(grepl("TOTAL", x[,1])),]

# Nullify the total row thats not the last row: x => data.frame
suppressWarnings(if(length(which(grepl("TOTAL", x[,1]) < nrow(x))) > 0){
tmp <- x[which(grepl("TOTAL", x[,1])),]
x[which(grepl("TOTAL", x[,1])),] <- as.character("-")
x[nrow(x),] <- tmp
}else{
x
})

# Define the return object:
return(x)

}
), names(vec_summ_list))

# Flatten the list into a data.frame: analysed_df => data.frame
analysed_df <- Reduce(cbind, vec_summ_list)

}

# Store an empty list to contain each unique date: df_list => list
df_list <- vector("list", length(unique(df$Date)))

# Store an empty list to hold the daily reports: report_list => list
report_list <- df_list

# Split the data.frame into many data.frames by date: df_list => list
df_list <- split(df, df$Date)

# Store the base report as a list element for each date: report_list => list
report_list <- lapply(df_list, function(x) report_func(x))

Data:

df <-
structure(
list(
uid = c("K-1", "K-1", "K-2", "K-3", "K-4", "K-5",
"K-6", "K-7", "K-8"),
Date = structure(
c(
1584321093,
1584321093,
1584321093,
1584321093,
1584321093,
1584321093,
1584321093,
1584321093,
1584321093
),
class = c("POSIXct", "POSIXt"),
tzone = ""
),
batch_no = c(7L,
7L, 8L, 9L, 8L, NA, 7L, NA, 6L),
marking = c("S1", "S1", "SE_hold1",
"SD_hold2", "S1", NA, NA, "S2", "S3"),
seq = c("FRD", "FHL",
"ABC", "DEF", "XYZ", "ABC", "ZZZ", NA, "FRD")
),
row.names = c(NA,-9L),
class = "data.frame"
)

How to make static variable dynamic?

I'm side-stepping some issues of why you'd want to do this, but here's one way that's somewhat....advanced:

makeActiveBinding("n",function() sample(2,5,replace = TRUE),.GlobalEnv)
> n
[1] 2 2 1 2 1
> n
[1] 1 2 2 2 2
> n
[1] 2 1 1 1 1

What's happening here is that n is being created in the global environment and then linked to the function, so that whenever a value for n is needed it calls the function.

r reference classes - do they have static field members/variables?

The short answer - based on Hadley's comments above - is no. R's reference classes do not have static variables.



Related Topics



Leave a reply



Submit