Создать цифры c выборок на основе нескольких условий нескольких векторов - PullRequest
4 голосов
/ 17 февраля 2020

С учетом следующего фрейма данных:

df <- tibble::tribble(
  ~pass_id, ~km_ini, ~km_fin,
        1L,    0.89,    2.39,
        2L,    1.53,    3.03,
        3L,    21.9,    23.4,
        4L,    23.4,    24.9,
        5L,      24,    25.5,
        6L,    25.9,    27.4,
        7L,    36.7,    38.2,
        8L,    41.4,    42.9,
        9L,    42.1,    43.6,
       10L,    45.5,      47
  )

Создано в 2020-02-17 пакетом Представить (v0.3.0)

Мне нужна выборка из любых 50 чисел, которые соответствуют всем следующим критериям для всех строк из df:

  1. >= .750
  2. <= 99.450
  3. < km_ini - .750
  4. > km_fin + .750

Мой лучший выстрел далеко от того, что я ожидаю. Сначала я сделал runif, затем enframe d и попытался filter, но я только заставил его работать для первых двух условий. В любом случае, мне не обязательно нужен результат в виде фрейма данных, это может быть цифра c вектор.

library(tidyverse, verbose = F)

set.seed(42)
sort(runif(100000, 0, 99.450)) %>% 
  enframe(., "ID", "km") %>% 
  filter(km >= .750 & km <= 99.450 - .750)
#> # A tibble: 98,467 x 2
#>       ID    km
#>    <int> <dbl>
#>  1   763 0.750
#>  2   764 0.751
#>  3   765 0.751
#>  4   766 0.753
#>  5   767 0.753
#>  6   768 0.754
#>  7   769 0.754
#>  8   770 0.755
#>  9   771 0.755
#> 10   772 0.757
#> # … with 98,457 more rows

РЕДАКТИРОВАТЬ: попытка визуально отобразить проблему

Конечным результатом должен быть числовой вектор (или df), который оценивает весь набор данных, а не только каждую строку в отдельности. В качестве примера для первых двух строк см. Следующее представление:

enter image description here

Итак, посмотрите, что:

  • черная линия указывает, что у меня не может быть данных меньше чем 0,750.
  • Синяя линия указывает, где я не могу иметь записи из-за зоны покрытия km_ini и km_fin (стрелки) строки 1 и еще одного приложения, учитывающего площадь + или - .750 (между стрелками и точками).
  • Красная линия указывает, где я не могу иметь записи из-за зоны покрытия km_ini и km_fin (стрелки) строки 2 и другого приложения, учитывая область + или - .750 (между стрелками и точками).

Таким образом, сразу же, случайный набор данных в пределах первых 4000 метров может иметь только числа от 3030 +. 750.

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

1 Ответ

3 голосов
/ 01 марта 2020

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

Я думаю, что было бы полезно получить более четкое визуальное понимание проблемы , На этом графике ось x представляет расстояние (ось y является просто фиктивной осью, поскольку нас интересует только ось x). Черные полосы - это «зоны отчуждения», в которых мы не можем проводить выборки. Есть также 750-метровые зоны по обе стороны от зон отчуждения, в которых мы не хотим брать пробы, которые здесь окрашены в красный цвет:

enter image description here

По сути , мы хотим получить равномерную выборку из незатененных областей оси x на этом графике.

Мое решение состоит в том, чтобы сначала объединить перекрывающиеся сегменты, а затем создать пространство выборки, которое будет взвешено в соответствии с размером каждого пробел и взять 50 одинаковых выборок из этого пространства.

Здесь я обобщил, чтобы разрешить произвольные пределы и размер выборки.

sample_space <- function(km_ini, km_fin, km_max = 99.45, buffer = 0.75, n = 50)
{
  # Find and merge overlaps
  i <- 1
  km_ini <- km_ini - buffer
  km_fin <- km_fin + buffer
  while(i <= length(km_ini))
  {
    overlaps <- which(km_ini < km_fin[i] & km_fin > km_ini[i])
    if(length(overlaps) < 2) {i <- i + 1; next;}
    km_ini <- c(km_ini, min(km_ini[overlaps]))
    km_fin <- c(km_fin, max(km_fin[overlaps]))
    km_ini <- km_ini[-overlaps]
    km_fin <- km_fin[-overlaps]
    i <- 1
  }

  # Create a matrix of appropriate gaps
  gaps <- cbind(sort(km_fin), c(sort(km_ini)[-1], km_max))

  print(gaps)
  # Create a weighted sample space
  splits <- c(0, cumsum(apply(gaps, 1, diff)))

  # Take a sample within that space
  runifs <- runif(n, 0, max(splits))

  # Add the appropriate starting value back on
  index <- as.numeric(cut(runifs, splits))
  runifs - splits[index] + gaps[index, 1]
}

Так что теперь мы можем сделать

sample_space(df$km_ini, df$km_fin)
#>  [1] 93.107858 92.216660 83.597703 86.341198 72.258245 86.591883 18.572744
#>  [8] 16.641163 73.344658 73.075426 78.230074 97.745802 52.654342 52.298444
#> [15] 70.029034 67.430346 95.328900 62.250864 79.144025 86.344868  7.063474
#> [22] 58.797335 79.304272 54.731057 32.137068 84.837576 94.226992 50.808135
#> [29] 65.987277 76.666933 29.225744 33.309866 13.013735  6.925277 65.207383
#> [36] 91.591293 61.614993 18.646062 97.550237 48.478875 12.860920 20.263471
#> [43] 34.980616 50.583291 15.813562 96.104448 91.310377 53.063613 17.376281
#> [50] 72.511153

Чтобы показать, что это делает то, что мы хотели, давайте построим образец на графике зон отчуждения:

set.seed(69)
sample_df <- data.frame(x = sample_space(df$km_ini, df$km_fin),
                      y = runif(50, 0.45, 0.55))

ggplot(df) + 
  geom_rect(aes(xmin = km_ini - 0.75, xmax = km_fin + 0.75, ymin = 0, ymax = 1), 
            alpha = 0.5, fill = "red") +
  geom_rect(aes(xmin = km_ini, xmax = km_fin, ymin = 0, ymax = 1), fill = "black") +
  geom_rect(aes(xmin = 0, xmax = 0.75, ymin = 0, ymax = 1), alpha = 0.5) +
  geom_rect(aes(xmin = 99.45, xmax = 100, ymin = 0, ymax = 1), alpha = 0.5) +
  labs(x = "distance", y = "dummy") +
  geom_point(data = sample_df, aes(x = x, y = y), col = "blue")

enter image description here

Создано в 2020-03-01 пакетом Представить (v0.3.0)

...