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

У меня есть такой набор данных:

set.seed(71)
dat <- data.table(region = rep(c('A','B'), each=10),
    place = rep(c('C','D'), 10),
    start = sample.int(5, 20, replace = TRUE),
    end = sample.int(10, 20, replace = TRUE),
    count = sample.int(50, 20, replace = TRUE),
    para1 = rnorm(20,3,1),
    para2 = rnorm(20,4,1))

Я хотел бы перебрать эти данные, чтобы условно сгенерировать другую таблицу со следующими столбцами: регион, место, начало, конец, количество, count0 с потенциально более чем одной строкой для каждой строки в dat. в новой таблице данные для столбцов region, place и start будут скопированы из dat, а данные для столбцов end, count и count0 будут сгенерированы.

Вот правила перебора каждой строки данных:

end = end +1
if (count=0) {
  count0=0
} else {
  count0=start*para1 + end*para2
}
if (count0>count) {
  count0=count
}
count = count -count0

Я пытался использовать комбинацию цикла for, оператора if и mutate, но не смог сделать это правильно.

Я ожидаю получить такую ​​​​таблицу после прохождения первых двух строк данных:

region  place   start   end       count         count0
     A      C       2     7  6.01673062    17.98326938
     A      C       2     8           0     6.01673062
     A      D       3     2  5.34392419     7.65607581
     A      D       3     3           0     5.34392419


the first two rows of dat I have are:
region  place   start   end count   para1         para2
     A      C       2     6    24   0.39412969  2.45643
     A      D       3     1    13   0.64372127  2.862456

r
person Bigfoot    schedule 15.05.2019    source источник
comment
Я не понимаю из вашего псевдокода, как потенциально может быть более одной строки для каждой строки в dat. Что определяет, есть ли более одной строки из одной строки?   -  person Gregor Thomas    schedule 15.05.2019
comment
Обратите внимание: если вы проверяете логическое значение (T/F, как в операторе if), вы должны использовать ==, а не =.   -  person akash87    schedule 15.05.2019
comment
вызов функции count0=startpara1 + endpara2 генерирует значение, и это значение будет сравниваться со значением в переменной count. если оно меньше, чем count, будет сгенерирована новая строка, и новое значение функции будет сравниваться с остатком значения count. Этот процесс продолжается до тех пор, пока значение count не станет равным нулю. Поэтому, если значение счетчика велико, это может быть много строк.   -  person Bigfoot    schedule 15.05.2019


Ответы (1)


Редактировать. Вот ленивый подход, который должен быть очень быстрым за счет временного создания строк, которые мы удалим в конце. Вместо того, чтобы вычислять, сколько копий нужно сделать для каждой строки, я делаю кучу копий каждой строки, затем применяю быстрые векторизованные вычисления, чтобы получить обновленные значения end, count и count0, и удаляю строки, которые нам не нужны.

library(dplyr); library(tidyr)
output <-
  dat %>%
  mutate(orig_row = row_number()) %>%
  uncount(10) %>%   # I'm assuming here that 10 is enough columns
  group_by(orig_row) %>%
  mutate(row = row_number()) %>%
  mutate(
    end = end + row,
    count0 = pmin(count, start * para1 + end * para2), # Edit #2
    count = count - cumsum(count0)
  ) %>%
  filter(lag(count, default = 0) >= 0) %>%
  mutate(count = pmax(0, count),
         count0 = if_else(count == 0, lag(count), count0))
output


# A tibble: 4 x 10
# Groups:   orig_row [2]
  region place start   end count para1 para2 orig_row   row count0
  <chr>  <chr> <int> <int> <dbl> <dbl> <dbl>    <int> <int>  <dbl>
1 A      C         2     7  6.02 0.394  2.46        1     1  18.0 
2 A      C         2     8  0    0.394  2.46        1     2   6.02
3 A      D         3     2  5.34 0.644  2.86        2     1   7.66
4 A      D         3     3  0    0.644  2.86        2     2   5.34

Первоначальный ответ:

Я представляю, что это по соседству.

Предупреждение: я не получил те же образцы данных, которые вы показали, и я не понимаю, как конкретные числа в предоставленном вами образце будут генерировать предлагаемый результат. Например, из первой строки dat, которую вы показываете (отличается от того, что я получил), первый count0 должен быть 2*0.394 + 6*2.456 = 15.527, не так ли?

Мой подход здесь состоит в том, чтобы вычислить count0, а затем выяснить, сколько из count в него вписывается, а затем сделать столько копий строки, уменьшая count на count0 с каждой строкой.

library(dplyr); library(tidyr)
output <- dat %>%
  mutate(end = end + 1,
         orig_data = row_number(),
         count0 = if_else(count == 0, 0,
                          start*para1 + end*para2),
         copies = 1 + count %/% count0) %>%
  uncount(copies) %>%
  group_by(orig_data) %>%
  mutate(row = row_number() - 1,
         count = count - row * count0)

Кстати, мой dat инициализируется по-другому, используя set.seed(71). Не могли бы вы подтвердить, инициализируются ли ваши данные, как указано в OP? Нам будет легче договориться, если мы сможем начать с одного и того же места.

> head(dat)
   region place start end count    para1    para2
1:      A     C     2   7    19 3.400587 2.757140
2:      A     D     3   3    31 1.503740 6.089518
3:      A     C     2   8     2 2.561869 5.236298
4:      A     D     2   3    33 3.069835 3.770121
5:      A     C     2   2    21 2.989221 3.547926
6:      A     D     5   5    32 2.720636 5.379352
person Jon Spring    schedule 16.05.2019
comment
Да, это в правильном направлении, но не хватает пары деталей. Я уверен, что это потому, что мое описание проблемы недостаточно ясно. Переменная end должна добавлять 1 на каждой итерации. В результате вычисленное значение count0 также изменяется в каждой строке. Кроме того, общее количество count0 в нескольких строках новой таблицы должно быть суммировано со значением count строки в первой таблице, на которой они основаны. Наконец, значение счетчика в новой таблице уменьшается и должно заканчиваться нулем. - person Bigfoot; 16.05.2019
comment
Первый count0 должен быть 2*0,394 + 7*2,456 = 17,98326938. это вычисляется после добавления 1 к концу каждой итерации. - person Bigfoot; 16.05.2019
comment
Не могли бы вы убедиться, что предоставленный вами пример данных — это то, что генерируется сразу после запуска кода инициализации? Я добавил первые строки в свой ответ; он отличается от вашего, и это затрудняет понимание различий. - person Jon Spring; 16.05.2019
comment
Мой прогон приводит к разным данным даже с одним и тем же семенем. Пожалуйста, используйте свой вместо этого. Суть в том, что переменная end отличается в каждой строке новой таблицы (end=end+1), и count0 нужно вычислять с использованием новых конечных значений. Для каждого count0 его необходимо проверить, чтобы убедиться, что он не превышает оставшееся значение count. если это так, он должен принимать значение count. значение count должно соответственно уменьшаться в каждой строке, пока не достигнет нуля. - person Bigfoot; 16.05.2019
comment
В качестве альтернативы вы можете просто использовать две строки данных, которые я разместил, и не пытаться генерировать больше данных. - person Bigfoot; 16.05.2019
comment
Код по большей части дает желаемые результаты, за исключением случаев, когда первый вычисленный count0 больше, чем count, вместо того, чтобы принимать значение count, он создает NA. - person Bigfoot; 17.05.2019
comment
Я могу придумать исправление, чтобы вставить counts=count перед изменением count и добавить count0=ifelse(is.na(count0), counts, count0) в конце. Есть ли лучший способ справиться с этим? - person Bigfoot; 17.05.2019
comment
Я думаю, что строка count0 = pmin(count, start * para1 + end * para2), # Edit #2 должна исправить это. - person Jon Spring; 17.05.2019
comment
Да. Джон, большое спасибо за вашу любезную помощь. - person Bigfoot; 04.06.2019