Skip to content

Impact of secondary indices on ordering after subset in i #2400

Description

@MarkusBonsch

When browsing the data.table.R source code, I came upon this comment in line 452, where appropriate indices for subsetting on column colare identified:
# Can't be any index with that col as the first one because those indexes will reorder within each group

This indicates to me that the data.table policy is as follows:

  1. secondary indices are used to change the physical ordering of data.tables
  2. If subsetting on a variable x, the use of a secondary index should never cause a reordering of any other variable y within groups of x.

Because I have messed with indices lately myself, I wanted to test this behaviour and came to the following conclusion (underlying code at the end of the comment):

Effect of secondary indices on subsetting in i

nature of i is index used for reordering? is order of other columns maintained?
call no not applicable
data.table no not applicable
list yes no

Essentially, this means that

  1. Indices are only used for physical reordering if i is a list
  2. In this case, order of additional columns within groups is not guaranteed.

Here is my code:

library(data.table)

DT <- data.table(x = c(3, 3, 2, 2, 1, 1),
                 y = c(2, 1, 2, 1, 2, 1))

DTix <- copy(DT)
setindex(DTix, x)

DTixy <- copy(DT)
setindex(DTixy, x, y)

## with reduced index after assign
DTir <- copy(DTixy)
DTir[1, y := 2]

i <- data.table(x = c(2, 2, 1, 1),
                y = c(2, 1, 2, 1))

iix <- copy(i)
setindex(iix, x)

iixy <- copy(i)
setindex(iixy, x, y)

iixr <- copy(i)
setindex(iixr, x, y)
iixr[1, y := 2]

## function to determine, which columns have been reordered in a data.table with x and y columns
isSortedTwoCol <- function(DT){
  if(identical(DT$x, sort(DT$x))){
    if(identical(setorder(copy(DT), x, y), DT)) {
      sorted <- "x & y" 
    } else {
      sorted <- "x"
    }
  } else if(identical(setorder(copy(DT), y), DT)){
    sorted <- "y"
  } else {
    sorted <- " none"
  }
  sorted
}

## function to determine, which columns have been reordered in a data.table with x, y, and i.y columns
isSortedThreeCol <- function(DT){
  if(identical(DT$x, sort(DT$x))){
    if(identical(setorder(copy(DT), x, y), DT)) {
      if(identical(setorder(copy(DT), x, y, i.y), DT)){
        sorted <- "x & y & i.y"
      } else {
        sorted <- "x & y"
      }
    } else {
      sorted <- "x"
    }
  } else if(identical(setorder(copy(DT), y), DT)){
    if(identical(setorder(copy(DT), y, i.y), DT)){
      sorted <- "y & i.y"
    } else {
      sorted <- "y"
    }
  } else if(identical(setorder(copy(DT), i.y), DT)){
    sorted <- " i.y"
  } else {
    sorted <- "none"
  }
  sorted
}

cat("\n\nsubsets with lists")
cat(paste0("\nDT[1,, on = 'x']: ", isSortedTwoCol(DT[.(c(1, 2)),, on = 'x'])))
cat(paste0("\nDTix[1,, on = 'x']: ", isSortedTwoCol(DTix[.(c(1, 2)),, on = 'x'])))
cat(paste0("\nDTixy[1,, on = 'x']: ", isSortedTwoCol(DTixy[.(c(1, 2)),, on = 'x'])))

cat("\n\nsubsets with calls")
cat(paste0("\nDT[x %in% c(1,2)]: ", isSortedTwoCol(DT[x %in% (c(1, 2)),])))
cat(paste0("\nDTix[x %in% c(1,2)]: ", isSortedTwoCol(DTix[x %in% (c(1, 2)),])))
cat(paste0("\nDTixy[x %in% c(1,2)]: ", isSortedTwoCol(DTixy[x %in% (c(1, 2)),])))


cat("\n\nsubsets with data.tables")
cat(paste0("\nDT[i,, on = 'x']: ", isSortedThreeCol(DT[i,, on = 'x'])))
cat(paste0("\nDT[iix,, on = 'x']: ", isSortedThreeCol(DT[iix,, on = 'x'])))
cat(paste0("\nDTix[i,, on = 'x']: ", isSortedThreeCol(DTix[i,, on = 'x'])))
cat(paste0("\nDTix[iix,, on = 'x']: ", isSortedThreeCol(DTix[iix,, on = 'x'])))

cat(paste0("\nDT[i,, on = c('x', 'y')]: ", isSortedTwoCol(DT[i,, on = c('x', 'y')])))

cat(paste0("\nDT[iixy,, on = c('x', 'y')]: ", isSortedTwoCol(DT[iixy,, on = c('x', 'y')])))
cat(paste0("\nDTixy[i,, on = c('x', 'y')]: ", isSortedTwoCol(DTixy[i,, on = c('x', 'y')])))
cat(paste0("\nDTixy[iixy,, on = c('x', 'y')]: ", isSortedTwoCol(DTixy[iixy,, on = c('x', 'y')])))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions