Q:

R: reducing set to fixed size

Suppose I have the following data:
library(data.table); set.seed(55)
dat <- data.table(id=1:50, x=sample(100:200,50,replace=TRUE), y=sample(500:600,50,replace=TRUE), z=sample(900:1000,50,replace=TRUE))

> head(dat)
   id   x   y   z
1:  1 155 583 912
2:  2 122 574 945
3:  3 103 524 963
4:  4 179 587 993
5:  5 156 592 915
6:  6 107 545 996

from which I pick one row at random, say row number 5. 
5:  5 156 592 915

My goal is now to find those n_min=10 other rows that are most similar, according to a ranking of variables, say x, y, z. That is, I would like to take the first variable, see how many rows are within a particular interval and keep adding variables until one is reached that would reduce my n below n_min. 
For example, in the example above, x and y jointly reduce the set to 16 rows, but adding z would reduce n below n_min.
x_possible <- (156-round(sd(dat$x))):(156+round(sd(dat$x)))
y_possible <- (592-round(sd(dat$y))):(592+round(sd(dat$y)))
z_possible <- (915-round(sd(dat$z))):(915+round(sd(dat$z)))

> nrow(dat[x%in%x_possible])
[1] 32    
> nrow(dat[x%in%x_possible & y%in%y_possible])
[1] 16   
> nrow(dat[x%in%x_possible & y%in%y_possible & z%in%z_possible])
[1] 6

Such tasks are completely new to me and I didn't even know which terms to use to start looking. I wonder whether there is an efficient way of automatizing this such that I could throw in another dat and get the relevant rows out.

A:

Here's a way to use non-equi joins. There's probably a more efficient way to assign the sd results to the data.table but this is what I have:
library(data.table)
set.seed(55)
dat <- data.table(id=1:50, x=sample(100:200,50,replace=TRUE), y=sample(500:600,50,replace=TRUE), z=sample(900:1000,50,replace=TRUE))
n_min <- 10

col_name <- names(dat)[-1]
sds <- dat[, lapply(.SD, sd), .SDcols = col_name]

dat[dat[, .(id,x, y, z, x_sd_min = x - sds[['x']], x_sd_plus = x + sds[['x']])]
    , on = .(x > x_sd_min, x < x_sd_plus)
    , j = .(id, i.id
            , y_inrange = between(y, i.y - sds[['y']], i.y + sds[['y']], incbounds = F)
            , z_inrange = between(z, i.z - sds[['z']], i.z + sds[['z']], incbounds = F))
    , allow.cartesian = T
    , nomatch = 0L
    ][, .(x = .N, x_y = sum(y_inrange), x_y_z = sum(y_inrange & z_inrange)), keyby = id
      ][, .(id, x, x_y, x_y_z, threshold_breaker = c('x','y','z')[max.col(.SD[, -1] > n_min, ties.method = 'last')])]

    id  x x_y x_y_z threshold_breaker
 1:  1 31  17     6                 y
 2:  2 24  15    12                 z
 3:  3 17   8     5                 x
 4:  4 28  14     5                 y
 5:  5 32  16     6                 y
 6:  6 19  13     6                 y
 7:  7 20   4     2                 x
 8:  8 29  14     4                 y
 9:  9 29  18    11                 z
10: 10 19  11     7                 y
11: 11 22  11     6                 y
12: 12 19  10     7                 x
13: 13 30  20    13                 z
14: 14 29  17    11                 z
15: 15 26  11     5                 y
16: 16 29  12     7                 y
17: 17 32  15     5                 y
18: 18 21   7     2                 x
19: 19 33  15    12                 z
20: 20 27  20    10                 y
21: 21 27  13     5                 y
22: 22 26  13     8                 y
23: 23 24  12     4                 y
24: 24 23  15    10                 y
25: 25 16  11     6                 y
26: 26 32  11     4                 y
27: 27 27  20    12                 z
28: 28 23  11     7                 y
29: 29 27  17    10                 y
30: 30 28  12     3                 y
31: 31 27  16    11                 z
32: 32 30  16     8                 y
33: 33 19   9     6                 x
34: 34 17   9     8                 x
35: 35 24  13     7                 y
36: 36 30  14     5                 y
37: 37 32  17     6                 y
38: 38 22  11     4                 y
39: 39 26  13     7                 y
40: 40 28  14     7                 y
41: 41 19  13     8                 y
42: 42 19   9     6                 x
43: 43 19  11     3                 y
44: 44 26  13     3                 y
45: 45 27  10     6                 x
46: 46 25  12     7                 y
47: 47 25  10     6                 x
48: 48 16   8     2                 x
49: 49 29  10     6                 x
50: 50 33  16     7                 y
    id  x x_y x_y_z threshold_breaker

# Mostly original, here for reference
col_name <- names(dat)[-1]
dat[, paste0(col_name, '_sd') := lapply(.SD, sd), .SDcols = col_name]
dat[, paste0(col_name, '_sd_min') := .SD - mget(paste0(col_name, '_sd')), .SDcols = col_name]
dat[, paste0(col_name, '_sd_plus') := .SD + mget(paste0(col_name, '_sd')), .SDcols = col_name]
dat

dat[dat
    , on = .(x > x_sd_min, x < x_sd_plus
             , y > y_sd_min, y < y_sd_plus
             , z > z_sd_min, z < z_sd_plus
    )
    , j = .(id, i.id, x.x, x.y, x.z)
    , allow.cartesian = T
    , nomatch = 0L][id == 5, ]

   id i.id x.x x.y x.z
1:  5    1 156 592 915
2:  5    5 156 592 915
3:  5   16 156 592 915
4:  5   24 156 592 915
5:  5   29 156 592 915
6:  5   37 156 592 915

I need more details about fixed sized but you can probably get a maximum number of subset using this way.

