Как эффективно проверить, находится ли вектор чисел в интервале, определяемом фреймом данных

У меня следующая проблема: у меня есть один вектор n1, который содержит определенные значения (например, я рандомизировал значения в коде). У меня есть фрейм данных df.int, который содержит один столбец верхних пределов для интервалов и один столбец определенных значений (снова рандомизированный, на самом деле значения являются режимами чего-то другого). Я хочу проверить для каждой записи n1, в каком интервале фрейма данных она находится, а затем перезаписать значение n1 значением второго столбца соответствующего интервала.

В общем, мой код должен работать, но так как n1 и интервалы довольно длинные, мой скрипт работает слишком долго. Поэтому я хочу спросить, как я могу настроить свой код, чтобы он работал более эффективно.

Вот код:

set.seed(123)
seq.vec <- c(seq(400,800000,by=200))
n1 <- sample(100:800000, 2000, replace=TRUE)
df.int <- data.frame(matrix( nrow=length(seq.vec), ncol=2))
df.names <- c("Upper.Limit", "Value")
colnames(df.int) <- df.names
df.int$Upper.Limit <- seq.vec
df.int$Value <- sample(100:800000, length(seq.vec), replace=TRUE)
j <- 1
m <- 1
for (k in seq_len(n1)){
  for (i in seq_len(df.int$Upper.Limit)){
    if (j==1) {
      n1[m] <- ifelse(n1<=df.int$Upper.Limit[j],df.int$Value[j],n1[m])
    } else{
      n1[m] <- ifelse(n1<=df.int$Upper.Limit[j] & n1>df.int$Upper.Limit[j-1]
                            ,df.int$Value[j],n1[m])
    }
    j <- j+1
  }
  m <- m+1
}

Спасибо!

Может начать с findInterval(n1, df.int$Upper.Limit).

Rui Barradas 30.05.2019 13:20

Под перезаписать значение n1 значением второго столбца соответствующего интервала вы имеете в виду значение столбца Value? А какая из границ интервала нижняя или верхняя?

Rui Barradas 30.05.2019 13:25

Да, я хочу заменить n1 соответствующим значением столбца Value. И вообще интервал определяется как Upper.Limit[i-1] < n1 <= Upper.Limit[i]

sh_student 30.05.2019 15:31

Смотрите ответы. Я считаю, что они то, что вы хотите.

Rui Barradas 30.05.2019 16:17
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
63
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете использовать approx с method = "constant" и указать, какие ограничения использовать, установив аргумент f:

## dummy data
n1 <- runif (10, 0, 100)
df.int <- data.frame(
   upper = seq(1, 100, by = 1), 
   value = runif (100, 0, 100)
)


approx(x = df.int$upper, 
       y = df.int$value, 
       xout = n1, 
       method = "constant",  
       f = 1,                
       rule = 2             ## extrapolation behavior outside domain
)
Ответ принят как подходящий

Функция findInterval имеет хорошую производительность и может выполнять свою работу.
Сначала посмотрите, как это работает только с первым элементом n1.

i <- findInterval(n1[1], c(df.int$Upper.Limit, Inf))
j <- findInterval(n1[1], c(-Inf, df.int$Upper.Limit))

df.int$Upper.Limit[i]
#[1] 189000
n1[1]
#[1] 189041
df.int$Upper.Limit[j]
#[1] 189200

df.int$Upper.Limit[i] < n1[1] & n1[1] <= df.int$Upper.Limit[j]
#[1] TRUE

Теперь универсальное решение.

subsInterval <- function(x, DF, colLimits = 1, colValues = 2, lower = TRUE){
  vec <- if (lower) 
    c(DF[[colLimits]], Inf) 
  else 
    c(-Inf, DF[[colLimits]])
  i <- findInterval(x, vec, left.open = TRUE)
  DF[[colValues]][i]
}


system.time(
  n2 <- subsInterval(n1, df.int)
)
#     user    system   elapsed 
#    0.000     0.000     0.001 


system.time(
  n3 <- subsInterval(n1, df.int, lower = FALSE)
)
#     user    system   elapsed 
#        0         0         0 

Спасибо работает и очень быстро. Просто нужно было настроить его на DF[[colValues]][i+1], так как в противном случае он использует Value интервала раньше.

sh_student 31.05.2019 04:06

Если я правильно понимаю, OP ищет эффективный метод для выбора значения из соответствующего закрытого справа интервала, где указаны верхние пределы.

Для больших наборов данных стоит обратить внимание на прокатное соединение:

library(data.table)
setDT(df.int)[data.table(n1), on = .(Upper.Limit = n1), roll = -Inf]$Value

или, заменив n1 по желанию на OP

n1 <- setDT(df.int)[data.table(n1), on = .(Upper.Limit = n1), roll = -Inf]$Value

Поскольку ОП требует эффективности, вот эталон с различными размерами проблем, который сравнивает опубликованные до сих пор методы:

library(data.table)
bm <- bench::press(
  n_int = 10*c(1e2L, 1e4L, 1e6L),
  n_n1 = 10*c(1e2L, 1e4L, 1e6L),
  {
  seq.vec <- seq(400L, length.out = n_int, by = 200L)
  df.int <- data.frame(Upper.Limit = seq.vec,
                       Value = seq_along(seq.vec))
  set.seed(123)
  n0 <- sample(400:max(seq.vec), n_n1, replace = TRUE)
  # include edge cases
  n0[1:5] <- c(seq.vec[1L] - 1L, seq.vec[1L], seq.vec[2L], 
               max(seq.vec), max(seq.vec) + 1L)
  n1 <- data.table::copy(n0)

    bench::mark(
      rollJoin = {
        setDT(df.int)[data.table(n1), on = .(Upper.Limit = n1), roll = -Inf]$Value
      }
      , findInt = {
        i <- findInterval(n1, df.int$Upper.Limit, left.open = TRUE)
        df.int[["Value"]][i+1]
      }
      , approx = {
      approx(x = df.int$Upper.Limit, 
             y = df.int$Value, 
             xout = n1, 
             method = "constant",  
             f = 1,                
             rule = 2:1             ## extrapolation behavior outside domain
      )$y
    }
    , subsInt = {
      subsInterval <- function(x, DF, colLimits = 1, colValues = 2, lower = TRUE){
        vec <- if (lower)
          c(DF[[colLimits]], Inf)
        else
          c(-Inf, DF[[colLimits]])
        i <- findInterval(x, vec, left.open = TRUE)
        DF[[colValues]][i]
      }
      subsInterval(n1, df.int, lower = FALSE)
    }
    , min_time = 1.5
    , check = TRUE
  )
  }
)

который возвращает следующие тайминги

print(bm, n = Inf)
# A tibble: 36 x 15
   expression  n_int   n_n1      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory time  gc   
   <bch:expr>  <dbl>  <dbl> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list> <lis> <lis>
 1 rollJoin   1.00e3 1.00e3   1.84ms   2.24ms   4.31e+2  144.57KB   1.37     630     2      1.46s <int ~ <Rpro~ <bch~ <tib~
 2 findInt    1.00e3 1.00e3   73.9us   77.2us   1.15e+4   35.44KB   2.31    9998     2   866.63ms <int ~ <Rpro~ <bch~ <tib~
 3 approx     1.00e3 1.00e3  124.9us  129.7us   7.04e+3   63.16KB   2.11    9997     3      1.42s <dbl ~ <Rpro~ <bch~ <tib~
 4 subsInt    1.00e3 1.00e3   71.8us     74us   1.23e+4   23.63KB   1.23    9999     1   813.65ms <int ~ <Rpro~ <bch~ <tib~
 5 rollJoin   1.00e5 1.00e3   3.17ms   3.65ms   2.65e+2  918.01KB   1.35     392     2      1.48s <int ~ <Rpro~ <bch~ <tib~
 6 findInt    1.00e5 1.00e3  455.7us  603.6us   1.58e+3  808.88KB   5.99    2107     8      1.34s <int ~ <Rpro~ <bch~ <tib~
 7 approx     1.00e5 1.00e3   4.26ms   5.28ms   1.88e+2    4.83MB   4.46     253     6      1.35s <dbl ~ <Rpro~ <bch~ <tib~
 8 subsInt    1.00e5 1.00e3    516us  659.4us   1.46e+3  797.07KB   5.94    1960     8      1.35s <int ~ <Rpro~ <bch~ <tib~
 9 rollJoin   1.00e7 1.00e3  80.21ms  83.39ms   1.12e+1   76.43MB   2.64      17     4      1.52s <int ~ <Rpro~ <bch~ <tib~
10 findInt    1.00e7 1.00e3  37.72ms  48.19ms   1.66e+1   76.32MB   7.98      25    12       1.5s <int ~ <Rpro~ <bch~ <tib~
11 approx     1.00e7 1.00e3  931.5ms 934.27ms   1.07e+0  509.49MB   2.14       2     4      1.87s <dbl ~ <Rpro~ <bch~ <tib~
12 subsInt    1.00e7 1.00e3  46.98ms  49.05ms   1.64e+1   76.31MB   4.59      25     7      1.53s <int ~ <Rpro~ <bch~ <tib~
13 rollJoin   1.00e3 1.00e5   9.05ms  10.56ms   9.42e+1    3.16MB   0.683    138     1      1.47s <int ~ <Rpro~ <bch~ <tib~
14 findInt    1.00e3 1.00e5    6.6ms   7.17ms   1.37e+2    2.68MB   0.680    202     1      1.47s <int ~ <Rpro~ <bch~ <tib~
15 approx     1.00e3 1.00e5   6.95ms   7.54ms   1.31e+2    1.57MB   0.682    192     1      1.47s <dbl ~ <Rpro~ <bch~ <tib~
16 subsInt    1.00e3 1.00e5   5.78ms   6.35ms   1.56e+2    1.53MB   0.681    229     1      1.47s <int ~ <Rpro~ <bch~ <tib~
17 rollJoin   1.00e5 1.00e5  13.24ms  14.34ms   6.93e+1    3.92MB   0.686    101     1      1.46s <int ~ <Rpro~ <bch~ <tib~
18 findInt    1.00e5 1.00e5  20.74ms  22.21ms   4.48e+1    3.43MB   0         68     0      1.52s <int ~ <Rpro~ <bch~ <tib~
19 approx     1.00e5 1.00e5  17.69ms   19.4ms   5.14e+1    6.34MB   1.41      73     2      1.42s <dbl ~ <Rpro~ <bch~ <tib~
20 subsInt    1.00e5 1.00e5  20.17ms  21.29ms   4.39e+1    2.29MB   0         66     0       1.5s <int ~ <Rpro~ <bch~ <tib~
21 rollJoin   1.00e7 1.00e5   98.3ms  104.8ms   9.02e+0   79.45MB   1.29      14     2      1.55s <int ~ <Rpro~ <bch~ <tib~
22 findInt    1.00e7 1.00e5 202.72ms 204.44ms   4.47e+0   78.97MB   1.28       7     2      1.57s <int ~ <Rpro~ <bch~ <tib~
23 approx     1.00e7 1.00e5    1.11s    1.14s   8.76e-1     511MB   2.19       2     5      2.28s <dbl ~ <Rpro~ <bch~ <tib~
24 subsInt    1.00e7 1.00e5 208.82ms 211.26ms   4.57e+0   77.82MB   0.653      7     1      1.53s <int ~ <Rpro~ <bch~ <tib~
25 rollJoin   1.00e3 1.00e7    1.02s    1.12s   8.93e-1  305.29MB   1.34       2     3      2.24s <int ~ <Rpro~ <bch~ <tib~
26 findInt    1.00e3 1.00e7 797.56ms 807.58ms   1.24e+0  267.04MB   1.86       2     3      1.61s <int ~ <Rpro~ <bch~ <tib~
27 approx     1.00e3 1.00e7 747.18ms 844.75ms   1.18e+0  152.63MB   0.592      2     1      1.69s <dbl ~ <Rpro~ <bch~ <tib~
28 subsInt    1.00e3 1.00e7  639.3ms 642.26ms   1.53e+0   152.6MB   0.510      3     1      1.96s <int ~ <Rpro~ <bch~ <tib~
29 rollJoin   1.00e5 1.00e7    1.68s    1.68s   5.95e-1  306.04MB   1.19       1     2      1.68s <int ~ <Rpro~ <bch~ <tib~
30 findInt    1.00e5 1.00e7    2.34s    2.34s   4.27e-1  267.79MB   0.427      1     1      2.34s <int ~ <Rpro~ <bch~ <tib~
31 approx     1.00e5 1.00e7    1.45s    1.46s   6.86e-1   157.4MB   0.343      2     1      2.92s <dbl ~ <Rpro~ <bch~ <tib~
32 subsInt    1.00e5 1.00e7    2.08s    2.08s   4.81e-1  153.35MB   0          1     0      2.08s <int ~ <Rpro~ <bch~ <tib~
33 rollJoin   1.00e7 1.00e7    1.82s    1.82s   5.49e-1  381.57MB   0.549      1     1      1.82s <int ~ <Rpro~ <bch~ <tib~
34 findInt    1.00e7 1.00e7   18.21s   18.21s   5.49e-2  343.32MB   0.110      1     2     18.21s <int ~ <Rpro~ <bch~ <tib~
35 approx     1.00e7 1.00e7     6.2s     6.2s   1.61e-1  662.06MB   0.323      1     2       6.2s <dbl ~ <Rpro~ <bch~ <tib~
36 subsInt    1.00e7 1.00e7   16.57s   16.57s   6.03e-2  228.88MB   0.0603     1     1     16.57s <int ~ <Rpro~ <bch~ <tib~
library(ggplot2)
autoplot(bm)

Обратите внимание на логарифмическую шкалу времени.

Для меньших размеров задач findInterval() или функция, которая оборачивает findInterval(), кажутся самыми быстрыми методами, в то время как для увеличения размеров задач прокатное соединение берет на себя инициативу.

При больших размерах задач выделение памяти (см. таблицу) может стать проблемой, которая также может повлиять на производительность.

Другие вопросы по теме