From m@ech|er @end|ng |rom @t@t@m@th@ethz@ch  Wed Oct 30 12:29:45 2019
From: m@ech|er @end|ng |rom @t@t@m@th@ethz@ch (Martin Maechler)
Date: Wed, 30 Oct 2019 12:29:45 +0100
Subject: [Rd] 
 head.matrix can return 1000s of columns -- limit to n or add
 new argument?
In-Reply-To: <CAD4oTHFf7Zp+Xe3jvv7oirLWgZm7ZiSNWZvbrEsqJGfrL6Q2wQ@mail.gmail.com>
References: <CAPRVBcxsAkcgr3mDc4skTe8DTxJaYe2xyhk3QMk3odW1MGcyNA@mail.gmail.com>
 <CAB8pepxS2mRtd+522OwjixnM6ZjYxb+X0dc4vMGn6QxdFYBmVQ@mail.gmail.com>
 <CAD4oTHH=LpeEj6h6xYdOGsreS-GDCGdgJV5wpaUR3Pjuh5_M1w@mail.gmail.com>
 <CAPRVBczYZHO3JaFVfNeNNV0-YMRvX3CUYhUvLCjophDEoP+XgQ@mail.gmail.com>
 <23935.14449.965488.684663@stat.math.ethz.ch>
 <CAPRVBcwMY=GAVfFeCwowQsxP31Y=_B2ducosThN+8V91WESB8Q@mail.gmail.com>
 <20449_1568703158_x8H6qIDG009669_f8285123-371f-5d4b-1281-6cc085dba122@fredhutch.org>
 <22204_1568723392_x8HCSG3F015456_674779E0-B1F6-4DC2-9F1C-D5D48B044B66@mcmaster.ca>
 <AD725242-0FB7-4004-9F16-304E345A363A@mcmaster.ca>
 <23937.1791.389379.103733@stat.math.ethz.ch>
 <CAD4oTHF_e4rtHu0S5bUu38+kQSEHXwun3Sv9-CbK+xN9tSc2hA@mail.gmail.com>
 <CAD4oTHFf7Zp+Xe3jvv7oirLWgZm7ZiSNWZvbrEsqJGfrL6Q2wQ@mail.gmail.com>
Message-ID: <23993.29737.690600.313456@stat.math.ethz.ch>

>>>>> Gabriel Becker 
>>>>>     on Tue, 29 Oct 2019 12:43:15 -0700 writes:

    > Hi all,
    > So I've started working on this and I ran into something that I didn't
    > know, namely that for x a multi-dimensional (2+) array, head(x) and tail(x)
    > ignore dimension completely, treat x as an atomic vector, and return an
    > (unclassed) atomic vector:

Well, that's  (3+), not "2+" .

But I did write (on Sep 17 in this thread!)

  > The current source for head() and tail() and all their methods
  > in utils is just 83 lines of code  {file utils/R/head.R minus
  > the initial mostly copyright comments}.

and if've ever looked at these few dozen of R code lines, you'll
have seen that we just added two simple utilities with a few
reasonable simple methods.  To treat non-matrix (i.e. non-2d)
arrays as vectors, is typically not unreasonable in R, but
indeed with your proposals (in this thread), such non-2d arrays
should be treated differently either via new  head.array() /
tail.array() methods ((or -- only if it can be done more nicely -- by
the default method)).

Note however the following  historical quirk :

> sapply(setNames(,1:5), function(K) inherits(array(pi, dim=1:K), "array"))
    1     2     3     4     5 
 TRUE FALSE  TRUE  TRUE  TRUE 

(Is this something we should consider changing for R 4.0.0 -- to
 have it TRUE also for 2d-arrays aka matrix objects ??)

The consequence of that is that
currently, "often"   foo.matrix is just a copy of foo.array  in
the case the latter exists:
"base" examples: foo in {unique, duplicated, anyDuplicated}.

So I propose you change current  head.matrix and tail.matrix  to
head.array and tail.array
(and then have   head.matrix <- head.array  etc, at least if the
 above quirk must remain, or remains (which I currently guess to
 be the case)).


    >> x = array(100, c(4, 5, 5))

    >> dim(x)

    > [1] 4 5 5

    >> head(x, 1)

    > [1] 100

    >> class(head(x))

    > [1] "numeric"


    > (For a 1d array, it does return another 1d array).

    > When extending head/tail to understand multiple dimensions as discussed in
    > this thread, then, should the behavior for 2+d arrays be explicitly
    > retained, or should head and tail do the analogous thing (with a head(<2d
    array> ) behaving the same as head(<matrix>), which honestly is what I
    > expected to already be happening)?

    > Are people using/relying on this behavior in their code, and if so, why/for
    > what?

    > Even more generally, one way forward is to have the default methods check
    > for dimensions, and use length if it is null:

    > tail.default <- tail.data.frame <- function(x, n = 6L, ...)
    > {
    > if(any(n == 0))
    > stop("n must be non-zero or unspecified for all dimensions")
    > if(!is.null(dim(x)))
    > dimsx <- dim(x)
    > else
    > dimsx <- length(x)

    > ## this returns a list of vectors of indices in each
    > ## dimension, regardless of length of the the n
    > ## argument
    > sel <- lapply(seq_along(dimsx), function(i) {
    > dxi <- dimsx[i]
    > ## select all indices (full dim) if not specified
    > ni <- if(length(n) >= i) n[i] else dxi
    > ## handle negative ns
    > ni <- if (ni < 0L) max(dxi + ni, 0L) else min(ni, dxi)
    > seq.int(to = dxi, length.out = ni)
    > })
    > args <- c(list(x), sel, drop = FALSE)
    > do.call("[", args)
    > }


    > I think this precludes the need for a separate data.frame method at all,
    > actually, though (I would think) tail.data.frame would still be defined and
    > exported for backwards compatibility. (the matrix method has some extra
    > bits so my current conception of it is still separate, though it might not
    > NEED to be).

    > The question then becomes, should head/tail always return something with
    > the same dimensionally (number of dims) it got, or should data.frame and
    > matrix be special cased in this regard, as they are now?

    > What are people's thoughts?
    > ~G

    > [[alternative HTML version deleted]]



[Rd] head.matrix can return 1000s of columns -- limit to n or add new argument?
Martin Maechler