From r@turner @end|ng |rom @uck|@nd@@c@nz  Mon Nov  4 01:52:33 2019
From: r@turner @end|ng |rom @uck|@nd@@c@nz (Rolf Turner)
Date: Mon, 4 Nov 2019 13:52:33 +1300
Subject: [Rd] Puzzled about a new method for "[".
In-Reply-To: <ca5c37d7-1951-d35f-1d20-5a74b8976969@gmail.com>
References: <74a6cf10-3266-fef8-9ea0-0ba6d36db807@auckland.ac.nz>
 <b795d8d3-d20b-0576-9d8d-011307e51707@gmail.com>
 <8faee382-b436-ce7d-19b9-b6bdb65f2a66@auckland.ac.nz>
 <ca5c37d7-1951-d35f-1d20-5a74b8976969@gmail.com>
Message-ID: <920b5ff0-f1a0-f71d-d7d2-69bae19f3a0a@auckland.ac.nz>


On 4/11/19 1:06 PM, Duncan Murdoch wrote:

> 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 "..." .

OK.  I'll try that!

Thanks.

cheers,

Rolf

-- 
Honorary Research Fellow
Department of Statistics
University of Auckland
Phone: +64-9-373-7599 ext. 88276



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