#r
#r
Вопрос:
Я создал этот фрейм данных:
Count <- c(1:10)
Give <- c(0,0,5,0,0,5,0,5,0,5)
X <- c(rep(0,10))
Y <- c(rep(0,10))
Z <- c(rep(0,10))
X_Target <- 5
Y_Target <- 10
Z_Target <- 5
В принципе, у меня есть 3 вектора (X, Y, Z) и цель для каждого из них.
Я хочу иметь новое вычисление для X, Y и Z, основанное на заданном векторе.
Как только число в Give больше 0, его необходимо добавить к вектору X, пока оно не сравняется с X_Target. Затем — вычисление должно перейти к следующему вектору (Y) и сделать то же самое, а затем к следующему вектору…
Результат должен быть следующим:
Count Give X Y Z
1 0 0 0 0
2 0 0 0 0
3 5 5 0 0
4 0 5 0 0
5 0 5 0 0
6 5 5 5 0
7 0 5 5 0
8 5 5 10 0
9 0 5 10 0
10 5 5 10 5
В этом примере у меня всего 3 вектора, но, пожалуйста, имейте в виду, что у меня будет не менее 60 векторов, поэтому мне нужно, чтобы это было как можно более автоматическим.
Надеюсь, мне удастся объясниться 🙂 Спасибо!
Ответ №1:
Это некрасиво, но дает желаемый результат.
tab1 = data.frame(
Count = c(1:10),
Give = c(0,0,5,0,0,5,0,5,0,5),
X = c(rep(0,10)),
Y = c(rep(0,10)),
Z = c(rep(0,10))
)
targets <- c(5,10,5)
tab2 <- tab1
start <- 2
for(col in 3:ncol(tab2)) {
target <- targets[col-2]
for(row in start:nrow(tab2)) {
if(tab2[row, 2] > 0 amp; tab2[row, col] < target) {
tab2[row, col] <- pmin(tab2[row - 1, col] tab2[row, col - 1], target)
} else {
tab2[row, col] <- tab2[row - 1, col]
}
}
start <- which(tab2[, 2] > 0 amp; tab2[, col] == target)[2]
}
> tab2
Count Give X Y Z
1 1 0 0 0 0
2 2 0 0 0 0
3 3 5 5 0 0
4 4 0 5 0 0
5 5 0 5 0 0
6 6 5 5 5 0
7 7 0 5 5 0
8 8 5 5 10 0
9 9 0 5 10 0
10 10 5 5 10 5
Ответ №2:
Превратите его во фрейм данных :
tab1 = data.frame(
Count = c(1:10),
Give =c(0,0,5,0,0,5,0,5,0,5),
X = c(rep(0,10)),
Y = c(rep(0,10)),
Z = c(rep(0,10))
)
# create a list of targets for looping
targets = c(X_Target, Y_Target, Z_Target)
Без использования data.table вы можете просто поместить все это в цикл. Это будет работать, но будет намного медленнее.
# loop through each column
for(col in seq(1,length(targets))){
print(col)
# loop through each row
for(row in seq(1, dim(tab1[2 col])[1])){
# condition
while(tab1[row,(2 col)] < targets[col] amp; tab1[row,2]>0){
tab1[row,(2 col)] = tab1[row,(2 col)] tab1[row,2]
}
}
}
Комментарии:
1. Спасибо, но я получаю сообщение об ошибке — не могли бы вы показать мне пример с фреймом данных вместо таблицы данных? Нужно ли мне создавать цикл while для каждого вектора? потому что у меня их около 60..
2. все ли ваши векторы одинаковой длины?
3. Да, все они имеют одинаковую длину
4. обновлено для использования без data.table, будет намного медленнее, но это может быть не большой проблемой, если у вас не слишком много данных
5. Спасибо, это очень близко, но все же не тот результат, которого я желаю. Вывод должен быть таким, как пример, который я отправил в основном вопросе.
Ответ №3:
Вот что еще можно попробовать, используя tidyverse
.
Поместите свои данные в длинную форму и включите цели с помощью объединения.
В цикле Count
найдите первую строку для заданного Count
значения, которое находится ниже целевого. Для текущей и следующих строк, которые имеют совпадающие имена (X, Y или Z), добавьте Give
сумму.
В конце верните результат в широкую форму.
library(tidyverse)
df <- data.frame(Count, Give, X, Y, Z) %>%
pivot_longer(cols = X:Z) %>%
left_join(data.frame(X_Target, Y_Target, Z_Target) %>%
pivot_longer(cols = everything(),
names_to = c("name", ".value"),
names_pattern = "(\w )_(\w )"))
for (i in seq_along(Count)) {
below_target <- min(which(df$Count == i amp; df$value < df$Target))
name_rows <- which(df$name == df[below_target, "name", drop = T])
rows_to_change <- name_rows[name_rows >= below_target]
df[rows_to_change, "value"] <- df[rows_to_change, "value"] df[below_target, "Give", drop = T]
}
df %>%
pivot_wider(id_cols = Count)
Вывод
Count X Y Z
<int> <dbl> <dbl> <dbl>
1 1 0 0 0
2 2 0 0 0
3 3 5 0 0
4 4 5 0 0
5 5 5 0 0
6 6 5 5 0
7 7 5 5 0
8 8 5 10 0
9 9 5 10 0
10 10 5 10 5
Ответ №4:
Мой подход заключался в том, чтобы использовать совокупные суммы Give
, а затем отслеживать, превышает ли это целевые значения для столбцов. Затем выполните некоторую очистку.
targets <- c(X_Target, Y_Target, Z_Target)
targets_0 <- c(0, targets)
csum_give <- cumsum(Give)
# from cumsum give take off sum of previous targets
result <- sapply(1:length(targets),
function(x) csum_give - sum(targets_0[1:x]))
# Set max value to target max of column
sapply(1:length(targets),
function(x) result[result[, x] > targets[x], x] <<- targets[x])
# set min value to zero
result[which(result < 0)] <- 0
result
# [,1] [,2] [,3]
# [1,] 0 0 0
# [2,] 0 0 0
# [3,] 5 0 0
# [4,] 5 0 0
# [5,] 5 0 0
# [6,] 5 5 0
# [7,] 5 5 0
# [8,] 5 10 0
# [9,] 5 10 0
# [10,] 5 10 5
Комментарии:
1. Спасибо, это очень близко к результату, который я хочу — просто нужно показать увеличение по строкам — например, для вектора Y я хочу, чтобы результат был равен 5 для строк 5 и 6 (как я показал в своем примере в основном вопросе)
2. Готово! Я отредактировал ответ, я думаю, это то, что вам нужно