3

I want an easy way to access the last values of objects. I have implemented a function that allows for Python-like negative indexing (count-backwards-and-keep-only-those-elements instead of R's remove-those-elements-and-keep-all-other-elements). There are some questions about this, but no one attempted to do this:

Basically, I want to redefine [ and [[, so that one can user a special operator (e.g. _) to use Python-like indexing. For instance, like this:

iris[1, ] #first row
iris[-1, ] #everything but first row
iris[_1, ] #last row, equivalent to tail(iris, 1)
iris[, _1] #last column, equivalent to iris[ncol(iris)]

Is it possible? I know it is possible to redefine e.g. + to introduce character concatenation like other languages have (see this answer).

In case it's not possible, here's my workaround function:

extract_last = function(x, margin_1, margin_2, drop = FALSE) {
  #check types
  if (!(is.vector(x) || is.matrix(x) || is.data.frame(x))) stop("x was an unsupported type (not a vector, matrix or data.frame)!")

  #vector
  if (is.vector(x)) return(rev(x)[margin_1])

  #get dims
  x_dims = dim(x)

  #make indices
  if (missing("margin_1")) {
    margin_1 = 1:x_dims[1]
  } else {
    margin_1 = (x_dims[1] + 1) - margin_1
  }
  if (missing("margin_2")) {
    margin_2 = 1:x_dims[2]
  } else {
    margin_2 = (x_dims[2] + 1) - margin_2
  }

  #subset
  return(x[margin_1, margin_2, drop = drop])
}

#tests
extract_last(iris, 1) == tail(iris, 1)
extract_last(iris, 10:1) == tail(iris, 10)
extract_last(iris, , 1) == iris[5]
extract_last(iris, , 2:1) == iris[4:5]
extract_last(iris, 10:1, 1) == iris[141:150, 5, drop = FALSE]
8
  • 5
    Is iris[nrow(iris), ] or iris[, ncol(iris)] too much typing? Commented Feb 2, 2016 at 16:48
  • 1
    I don't think this can be done in general (i.e., not only for a specific class). E.g., you can't really define [ methods for the implicit classes (character , numeric, ...). Commented Feb 2, 2016 at 16:55
  • You could write a function called `` (see [this chapter written by Hadley Wickam), but I wouldn't advise it for so many reasons. I'd argue the easiest solution is T <- function(...){a <- list(...); do.call(tail,a)}.. this is pretty short and does your stuff: a <- matrix(1:9,3,3); T(a[1,],1); T(a,1). Commented Feb 2, 2016 at 17:06
  • 1
    By using backticks `[`. So something like `[` <- function(x,y,z=NULL){if (!is.null(z)){print("this was a bad idea")} else {do.call(.Primitive("["),list(x,y))}}; b <- 1:10; b[3]; b[2,3]. [And in case you can't figure it out, use rm(`[`) to get rid of it again.] Commented Feb 2, 2016 at 17:29
  • 1
    I agree that it would be kinda cool if R added a keyword like, say, "last" so that foo[1:(last-5)] would skip the last 5 elements, but in the long run it's really not that hard to use ncol or nrow as Roman suggested. If you're going to be accessing the same array a lot, just define a couple variables nrowmat <- nrow(mat) and use them. Commented Feb 2, 2016 at 17:54

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.