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]
Community
  • 1
  • 1
CoderGuy123
  • 6,219
  • 5
  • 59
  • 89
  • 5
    Is `iris[nrow(iris), ]` or `iris[, ncol(iris)]` too much typing? – Roman Luštrik Feb 02 '16 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`, ...). – Roland Feb 02 '16 at 16:55
  • You could write a function called \`[\` (see [this chapter](http://adv-r.had.co.nz/Functions.html) 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)`. – slamballais Feb 02 '16 at 17:06
  • @RomanLuštrik That only works for the very last element. I want to be able to use Python-like syntax as stated in the question. For instance, `extract_last(iris, c(20, 15, 5, 1))` which is equivalent to `iris[(nrow(iris)+1) - c(20, 15, 5, 1), ]`. – CoderGuy123 Feb 02 '16 at 17:10
  • @Laterow I read the book but could not figure out a way to change the `[` function in the same way that I've changed `+` (see the answer linked to in the question). – CoderGuy123 Feb 02 '16 at 17:25
  • 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.] – slamballais Feb 02 '16 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. – Carl Witthoft Feb 02 '16 at 17:54
  • It does not suffice in general. That approach only works when one wants the last element. What if I want the last 10 elements? It quickly gets cumbersome. It is easy with the function I gave above, see e.g. the last example. – CoderGuy123 Mar 08 '16 at 13:35

0 Answers0