How to patch an S4 method in an R package? - r

If you find a bug in a package, it's usually possible to patch the problem with fixInNamespace, e.g. fixInNamespace("mean.default", "base").
For S4 methods, I'm not sure how to do it though. The method I'm looking at is in the gWidgetstcltk package. You can see the source code with
getMethod(".svalue", c("gTabletcltk", "guiWidgetsToolkittcltk"))
I can't find the methods with fixInNamespace.
fixInNamespace(".svalue", "gWidgetstcltk")
Error in get(subx, envir = ns, inherits = FALSE) :
object '.svalue' not found
I thought setMethod might do the trick, but
setMethod(".svalue", c("gTabletcltk", "guiWidgetsToolkittcltk"),
definition = function (obj, toolkit, index = NULL, drop = NULL, ...)
{
widget = getWidget(obj)
sel <- unlist(strsplit(tclvalue(tcl(widget, "selection")),
" "))
if (length(sel) == 0) {
return(NA)
}
theChildren <- .allChildren(widget)
indices <- sapply(sel, function(i) match(i, theChildren))
inds <- which(visible(obj))[indices]
if (!is.null(index) && index == TRUE) {
return(inds)
}
if (missing(drop) || is.null(drop))
drop = TRUE
chosencol <- tag(obj, "chosencol")
if (drop)
return(obj[inds, chosencol, drop = drop])
else return(obj[inds, ])
},
where = "package:gWidgetstcltk"
)
Error in setMethod(".svalue", c("gTabletcltk", "guiWidgetsToolkittcltk"), :
the environment "gWidgetstcltk" is locked; cannot assign methods for function ".svalue"
Any ideas?

How about the old-school way of getting the source, applying the change and rebuilding?

you can first get the generic out, and then fix the generic by setMethod in your global environment, and then assign it back to that namespace
.svalue <- gWidgetstcltk:::.svalue
setMethod(".svalue", c("gTabletcltk", "guiWidgetsToolkittcltk"),
definition = function (obj, toolkit, index = NULL, drop = NULL, ...)
{
widget = getWidget(obj)
sel <- unlist(strsplit(tclvalue(tcl(widget, "selection")),
" "))
if (length(sel) == 0) {
return(NA)
}
theChildren <- .allChildren(widget)
indices <- sapply(sel, function(i) match(i, theChildren))
inds <- which(visible(obj))[indices]
if (!is.null(index) && index == TRUE) {
return(inds)
}
if (missing(drop) || is.null(drop))
drop = TRUE
chosencol <- tag(obj, "chosencol")
if (drop)
return(obj[inds, chosencol, drop = drop])
else return(obj[inds, ])
}#,
#where = "package:gWidgetstcltk"
)
assignInNamespace(".svalue", .svalue, ns = "gWidgetstcltk")

Related

bind_rows() error: Error in `bind_rows()`: ! Can't combine `..1$comment_id` <character> and `..2$comment_id` <integer>

I am running a pretty long function that deals with reddit comment data from RedditExtractoR:
# Create an empty data frame to store the thread information
threads_df = data.frame(date = character(), title = character(), url = character(), subreddit = character())
# Bind threads
threads_df = bind_rows(
data.frame(threads1, subreddit = "SSBM"),
data.frame(threads2, subreddit = "funny"),
data.frame(threads3, subreddit = "meltyblood"),
data.frame(threads4, subreddit = "bloomington")
)
# Get the comments from each thread
comments_df = data.frame()
for (i in 1:nrow(threads_df)) {
result = get_thread_content(threads_df$url[i])[[2]]
result$subreddit = threads_df$subreddit[i]
comments_df = bind_rows(comments_df, result)
print(paste("Completed thread", i, "of", nrow(threads_df)))
if (nrow(result) == 0) {
stop("Failed to retrieve comments for thread", i)
}
if (i %% 100 == 0) {
print("Checking for timeouts")
Sys.sleep(10)
}
}
After getting to thread 115/977, I am greeted with the following error:
Error in `bind_rows()`:
! Can't combine `..1$comment_id` <character> and `..2$comment_id` <integer>.
---
Backtrace:
1. dplyr::bind_rows(comments_df, result)
4. vctrs::vec_rbind(!!!dots, .names_to = .id)
I have tried using trycatch to skip the error only to compile even more errors that I don't understand. It would be ideal to just skip threads that generated this error. I tried the following to do that, but it only complicated things past a level that I can comprehend:
# Get the comments from each thread
comments_df = data.frame()
for (i in 1:nrow(threads_df)) {
result = tryCatch(
expr = {
get_thread_content(threads_df$url[i])[[2]]
},
error = function(e) {
NULL
}
)
if (is.null(result)) {
print(paste("Skipped thread", i, "due to bind_rows error"))
next
}
result$subreddit = threads_df$subreddit[i]
comments_df = bind_rows(comments_df, result)
print(paste("Completed thread", i, "of", nrow(threads_df)))
if (nrow(result) == 0) {
stop("Failed to retrieve comments for thread", i)
}
if (i %% 100 == 0) {
print("Checking for timeouts")
Sys.sleep(10)
}
}
Does anyone have any insight into why this original error might be happening and what I could try to fix it? If not, is there maybe another way to bypass the error?

Creating Binary Search Tree in R

I have this code for creating a Binary Search Tree in a R6 class.
Creating a Node & BST class. In BST class, I am defining insert_recur function to create the BST by appropriately inserting the data.
library(R6)
Node <- R6Class(
classname = 'Node',
public = list(
val = NULL,
left = NULL,
right = NULL,
initialize = function(val = NULL, left = NULL, right = NULL){
self$val <- val
self$left <- left
self$right <- right
}
)
)
BST <- R6Class(
classname = 'BST',
public = list(
root = NULL,
# node = NULL,
insert = function(data){
if(is.null(self$root)){
self$node <- Node$new(data)
}else{
self$insert_recur(data, self$root)
}
},
insert_recur = function(data, cur_node){
if(data < cur_node$val){
if(is.null(cur_node$self)){
cur_node$left <- Node$new(data)
}else{
insert_recur(data, cur_node$left)
}
}else if(data > cur_node$val){
if(is.null(cur_node$self)){
cur_node$right <- Node$new(data)
}else{
insert_recur(data, cur_node$right)
}
}else{
print('value already in tree')
}
},
get_height = function(cur_node){
if(is.null(cur_node$val)){
return(-1)
}else{
return(max(self$get_height(cur_node$left),self$get_height(cur_node$right))+1)
}
}
)
)
bst <- BST$new()
bst$insert(3)
bst$insert(2)
bst$insert(1)
bst$insert(5)
bst$insert(6)
bst$insert(4)
bst$insert(7)
However I am getting this error -
Error in self$node <- Node$new(data) : cannot add bindings to a locked environment
If I put node <- NULL in the BST class, then the recursion fails & all nodes are NULL.
What will be the correct implementation?
Your Node implementation is fine. The BST isn't quite right though. It should have a NULL root node only. The problem lies in your insert_recur function. It's not possible for cur_node$self to ever be NULL, and the logic would seem to indicate that your if statements should be checking for the absence of cur_node$left and cur_node$right instead. Also, you need to remember to use self$insert_recur. The logic of your get_height argument doesn't seem right to me either. The following implementation seems to work as expected:
BST <- R6Class(
classname = 'BST',
public = list(
root = NULL,
insert = function(data) {
if(is.null(self$root)) {
self$root <- Node$new(data)
} else {
self$insert_recur(data, self$root)
}
},
insert_recur = function(data, cur_node) {
if(data < cur_node$val) {
if(is.null(cur_node$left)) {
cur_node$left <- Node$new(data)
} else {
self$insert_recur(data, cur_node$left)
}
} else if(data > cur_node$val){
if(is.null(cur_node$right)){
cur_node$right <- Node$new(data)
}else{
self$insert_recur(data, cur_node$right)
}
}else{
print('value already in tree')
}
},
get_height = function(cur_node){
if(is.null(cur_node$left) & is.null(cur_node$right)){
return(0)
}else{
return(max(self$get_height(cur_node$left),
self$get_height(cur_node$right)) + 1)
}
}
)
)
This allows
bst <- BST$new()
bst$insert(3)
bst$insert(2)
bst$insert(1)
bst$insert(5)
bst$insert(6)
bst$insert(4)
bst$insert(7)
bst$get_height(bst$root)
#> [1] 3
bst$get_height(bst$root$right)
#> [1] 2
Created on 2022-09-24 with reprex v2.0.2

Is there a variable listing in RStudio (or R) like in SPSS?

RStudio provides a nice function View (with uppercase V) to take a look into the data, but with R it's still nasty to get orientation in a large data set. The most common options are...
names(df)
str(df)
If you're coming from SPSS, R seems like a downgrade in this respect. I wondered whether there is a more user-friendly option? I did not find a ready-one, so I'd like to share my solution with you.
Using RStudio's built-in function View, it's white simple to have a variable listing for a data.frame similar to the one in SPSS. This function creates a new data.frame with the variable information and displays in the RStudio GUI via View.
# Better variables view
Varlist = function(sia) {
# Init varlist output
varlist = data.frame(row.names = names(sia))
varlist[["comment"]] = NA
varlist[["type"]] = NA
varlist[["values"]] = NA
varlist[["NAs"]] = NA
# Fill with meta information
for (var in names(sia)) {
if (!is.null(comment(sia[[var]]))) {
varlist[[var, "comment"]] = comment(sia[[var]])
}
varlist[[var, "NAs"]] = sum(is.na(sia[[var]]))
if (is.factor(sia[[var]])) {
varlist[[var, "type"]] = "factor"
varlist[[var, "values"]] = paste(levels(sia[[var]]), collapse=", ")
} else if (is.character(sia[[var]])) {
varlist[[var, "type"]] = "character"
} else if (is.logical(sia[[var]])) {
varlist[[var, "type"]] = "logical"
n = sum(!is.na(sia[[var]]))
if (n > 0) {
varlist[[var, "values"]] = paste(round(sum(sia[[var]], na.rm=T) / n * 100), "% TRUE", sep="")
}
} else if (is.numeric(sia[[var]])) {
varlist[[var, "type"]] = typeof(sia[[var]])
n = sum(!is.na(sia[[var]]))
if (n > 0) {
varlist[[var, "values"]] = paste(min(sia[[var]], na.rm=T), "...", max(sia[[var]], na.rm=T))
}
} else {
varlist[[var, "type"]] = typeof(sia[[var]])
}
}
View(varlist)
}
My recommendation is to store that as a file (e.g., Varlist.R) and whever you need it, just type:
source("Varlist.R")
Varlist(df)
Again please take note of the uppercase V using as function name.
Limitation: When working with data.frame, the listing will not be updated unless Varlist(df) is run again.
Note: R has a built-in option to view data with print. If working with pure R, just replace the View(varlist) by print(varlist). Yet, depending on screen size, Hmisc::describe() could be a better option for the console.

R storing variable value in alist

I am trying to use a function to modify another function default settings through formals but my problem is that when I check my function defaults afterwards then nothing has changed. My code (minus unrelated stuff) is:
ScouringSettings <- function(min.MAF=NULL, eq.thresh=NULL){
if (is.null(min.MAF) && is.null(eq.thresh)){
maf <- paste0("Minimum MAF criterion is: ", formals(GeneScour)$min.maf)
eq <- paste0("ChiĀ² HW equilibrium threshold: ", formals(GeneScour)$min.eq)
cat(paste(maf, eq, sep="\n"))
} else if (is.null(eq.thresh)) {
formals(GeneScour) <- alist(gene=, min.maf = min.MAF, min.eq = formals(GeneScour)$min.eq)
} else if (is.null()){
formals(GeneScour) <- alist(gene=, min.maf = formals(GeneScour)$min.maf, min.eq = eq.thresh)
} else {
formals(GeneScour) <- alist(gene=, min.maf = min.maf, min.eq = eq.thresh)
}
}
I thought that maybe it was because of a problem of scope or something so I tried printing out the defaults while still being in my first function and it printed :
$gene
$min.maf
min.MAF
$min.eq
formals(GeneScour)$min.eq
And even when I forcefully type
formals(GeneScour) <- alist(gene=, min.maf = 2, min.eq = formals(GeneScour)$min.eq)
The modification is not carried over outside of the ScouringSettings.
I am a bit lost, how could I manage that ?

Instantiation of reference classes within reference classes - problems with lock() and immutability

I have come across some behaviour from R reference classes I would like to work around. In the following code, reference class B has two fields of reference class A in it.
These fields in B appear to be instantiated (possibly twice) with a zero-argument (default) versions of reference class A before B's initialize() method is called. These instances are then replaced with the correct versions of instance A during B's initialization process. The problem is that if I use lock() from B's instance generator, the initial empty instantiation's of A cannot be replaced in B. Another problem is that reference class A needs a default value in initialize [or a missing(c) test].
Help - suggestions - etc. appreciated.
A <- setRefClass('A',
fields = list(
count = 'numeric'
),
methods = list(
initialize = function (c=0) {
cat('DEBUG: A$initialize(c); where c='); cat(c); cat('\n')
count <<- c
}
)
)
instance.of.A <- A$new(10)
str(instance.of.A)
B <- setRefClass('B',
field = list(
a = 'A',
b = 'A'
),
methods = list(
initialize = function(c) {
a <<- instance.of.A
b <<- getRefClass('A')$new(c)
}
)
)
instance.of.b <- B$new(100)
str(instance.of.b)
Here are two possible solutions:
Don't set fields attribute:
B <- setRefClass('B',
methods = list(
initialize = function(c) {
.self$a = instance.of.A
.self$b =getRefClass('A')$new(c)
}
)
)
Set fields, but use the ANY class:
B <- setRefClass('B',
field = (a="ANY", b="ANY"),
methods = list(
initialize = function(c) {
a <<- instance.of.A
b <<- getRefClass('A')$new(c)
}
)
)
The downside of both these solutions is the type isn't enforced in a and b, i.e.
B$a = "Fred"
is now possible.
Drawing on the above, the solution I am using is (a little long because of the type checking):
A <- setRefClass('A',
fields = list(
count = function(x) {
if (!missing(x)) {
if(class(x) != 'numeric')
stop('Class A: count set by non-number')
.self$count.private <- x
}
.self$count.private
}
),
methods = list(
initialize = function (c=0) {
cat('DEBUG: A$initialize(c); where c='); cat(c); cat('\n')
count <<- c
}
)
)
instance.of.A <- A$new(10)
str(instance.of.A)
B <- setRefClass('B',
field = list(
a = function(x) {
if (!missing(x)) {
if(!inherits(x, 'envRefClass') || class(x)[1] != 'A')
stop('Class B: expecting instance of class A')
.self$a.private <- x
}
.self$a.private
},
b = function(x) {
if (!missing(x)) {
if(!inherits(x, 'envRefClass') || class(x)[1] != 'A')
stop('Class B: expecting instance of class A')
.self$b.private <- x
}
.self$b.private
}
),
methods = list(
initialize = function(c) {
a <<- instance.of.A
b <<- getRefClass('A')$new(c)
}
)
)
instance.of.b <- B$new(100)
str(instance.of.b)

Resources