[Rd] Puzzled about a new method for "[".

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Mon Nov 4 01:06:46 CET 2019


On 03/11/2019 6:43 p.m., Rolf Turner wrote:
> 
> On 4/11/19 10:31 AM, Duncan Murdoch wrote:
> 
>> On 03/11/2019 4:11 p.m., Rolf Turner wrote:
>>>
>>> I recently tried to write a new method for "[", to be applied to data
>>> frames, so that the object returned would retain (all) attributes of the
>>> columns, including attributes that my code had created.
>>>
>>> I thrashed around for quite a while, and then got some help from Rui
>>> Barradas who showed me how to do it, in the following manner:
>>>
>>> `[.myclass` <- function(x, i, j, drop = if (missing(i)) TRUE else
>>> length(cols) == 1)[{
>>>       SaveAt <- lapply(x, attributes)
>>>       x <- NextMethod()
>>>       lX <- lapply(names(x),function(nm, x, Sat){
>>>         attributes(x[[nm]]) <- Sat[[nm]]
>>>         x[[nm]]}, x = x, Sat = SaveAt)
>>>       names(lX) <- names(x)
>>>       x <- as.data.frame(lX)
>>>       x
>>> }
>>>
>>> If I set class(X) <- c("myclass",class(X)) and apply "[" to X (e.g.
>>> something like X[1:42,]) the attributes are retained as desired.
>>>
>>> OK.  All good.  Now we finally come to my question!  I want to put this
>>> new method into a package that I am building.  When I build the package
>>> and run R CMD check I get a complaint:
>>>
>>> ... no visible binding for global variable ‘cols’
>>>
>>> And indeed, there is no such variable.  At first I thought that maybe
>>> the code should be
>>>
>>> `[.myclass` <- function(x, i, j, drop = if (missing(i)) TRUE else
>>>                                          length(j) == 1)[{
>>>
>>> But I looked at "[.data.frame" and it has "cols" too; not "j".
>>>
>>> So why doesn't "[.data.frame" throw a warning when R gets built?
>>>
>>> Can someone please explain to me what's going on here?
>>
>> Defaults for parameters are evaluated in the evaluation frame of the
>> function, at the time the parameter is first used.
>>
>> If you look at the source for "[.data.frame", you should see that "cols"
>> is defined there as a local variable.  The "drop" argument shouldn't be
>> used until it is.  (There's a call to "missing(drop)" early in the
>> source that doesn't count:  it doesn't evaluate "drop", it just checks
>> whether it is specified by the caller.)
> 
> 
> OK.  As I understand what you're saying, the reason there isn't a
> "no visible binding" problem in [.data.frame is that "cols" *is* defined
> in the body of the function.  Whereas, in my method, "cols" does not get
> defined anywhere in the function, and thus triggers the warning.
> 
> I guess that a workaround would be to do a dummy assignment, like unto
> cols <- 42 at the start of the code for my method.
> 
> (a) Are there perils involved with this strategy?

Only that 42 might not be the right value.

> 
> (b) Is there anything wrong with my current strategy of replacing
> 
>      drop = if (missing(i)) TRUE else length(cols) == 1)
> 
> by
> 
>      drop = if (missing(i)) TRUE else length(j) == 1)

[.data.frame is pretty complicated, and I haven't read it closely enough 
to know if this is equivalent.  I would suggest you consider not 
including "drop" at all, just implicitly including it in "..." .

Duncan Murdoch

> 
> ???
> 
> As I said, this *seems* to work OK, by I cannot work through what the
> implications might be.
> 
> Can anyone reassure me?
> 
> cheers,
> 
> Rolf
>



More information about the R-devel mailing list