У меня есть фрейм данных в R, где мне нужно сдвинуть значения вверх в определенном столбце, используя числовое значение. Числовое значение, которое используется в качестве входных данных для сдвига значений вверх, связано с группирующей переменной и часто отличается от группы к группе. Вот пример макета фрейма данных
mock data frame
df<-data.frame(ID=c("a","a","a","a","a","b","b","b","b","b","c","c","c","c","c"),
var_description = c("description 1", "description 2","description 3","description 4","description 5",
"description 1", "description 2","description 3","description 4","description 5",
"description 1", "description 2","description 3","description 4","description 5"),
var_value = c(NA,NA,NA,"desc 1 val","desc 2 val",
NA,"desc 1 val","desc 2 val","desc 3 val","desc 4 val",
NA,NA,NA,NA,"desc 1 val"),
shift_val = c(3,3,3,3,3,
1,1,1,1,1,
4,4,4,4,4))
Вот текущее рабочее решение: -
#getting unique vals
unique_id = unique(df$ID)
#initialising dataframe to collect results
df_results<-data.frame()
#function to shift values up
shift <- function(x,n){
c(x[-(seq(n))],rep(NA,n))
}
#for loop
for (i in unique_id) {
#filtering by each ID
one_id<-df%>%filter(ID==i)
#getting value to shift values by
Move_by_val = unique(one_id$shift_val)
#shifting
one_id$new_var_value<-shift(one_id$var_value, Move_by_val)
#binding one_id onto df_results
df_results<-rbind(df_results,one_id)
}
head(df_results,10)
# ID var_description var_value shift_val new_var_value
# 1 a description 1 <NA> 3 desc 1 val
# 2 a description 2 <NA> 3 desc 2 val
# 3 a description 3 <NA> 3 <NA>
# 4 a description 4 desc 1 val 3 <NA>
# 5 a description 5 desc 2 val 3 <NA>
# 6 b description 1 <NA> 1 desc 1 val
# 7 b description 2 desc 1 val 1 desc 2 val
# 8 b description 3 desc 2 val 1 desc 3 val
# 9 b description 4 desc 3 val 1 desc 4 val
# 10 b description 5 desc 4 val 1 <NA>
Используя shift_val
, который связан с каждым уникальным df$ID
, я могу сдвинуть var_value
вверх, где значение соответствует var_description
, как показано в столбце new_var_value
.
Текущее решение, которое я использую, работает, но при применении к гораздо большему фрейму данных (например, имеющему ~> 100 тысяч строк) оно становится медленным и неэффективным.
Может ли кто-нибудь порекомендовать альтернативное решение, которое дает мне тот же результат, что и то, что у меня есть, но потенциально может быть намного более эффективным? возможно, лучше всего подойдет решение {data.table}
или {purrr}
, но я ни в одном из них не очень хорошо разбираюсь.
Версия R: 3.6.1
Используя свой shift()
shift = function(x, k) c(x[-seq(k)], rep(NA, k)) # replaced n by k
пытаться
РЕДАКТИРОВАТЬ
Вероятно, это работает на старых версиях R
достаточно быстро,
Reduce(
tapply(df, df$ID, \(i) transform(i, new_var_value = shift(var_description, shift_val[[1L]]))
), f = rbind)
— это классический подход «разделить-применить-объединить» и использовать чистую основу R
(без зависимостей). дает
ID var_description var_value shift_val new_var_value
1 a description 1 <NA> 3 desc 1 val
2 a description 2 <NA> 3 desc 2 val
3 a description 3 <NA> 3 <NA>
4 a description 4 desc 1 val 3 <NA>
5 a description 5 desc 2 val 3 <NA>
6 b description 1 <NA> 1 desc 1 val
7 b description 2 desc 1 val 1 desc 2 val
8 b description 3 desc 2 val 1 desc 3 val
9 b description 4 desc 3 val 1 desc 4 val
10 b description 5 desc 4 val 1 <NA>
11 c description 1 <NA> 4 desc 1 val
12 c description 2 <NA> 4 <NA>
13 c description 3 <NA> 4 <NA>
14 c description 4 <NA> 4 <NA>
15 c description 5 desc 1 val 4 <NA>
ОРИГИНАЛ удален
Данные
df = data.frame(ID = c("a","a","a","a","a","b","b","b","b","b","c","c","c","c","c"),
var_description = c("description 1", "description 2","description 3","description 4","description 5",
"description 1", "description 2","description 3","description 4","description 5",
"description 1", "description 2","description 3","description 4","description 5"),
var_value = c(NA,NA,NA,"desc 1 val","desc 2 val",
NA,"desc 1 val","desc 2 val","desc 3 val","desc 4 val",
NA,NA,NA,NA,"desc 1 val"),
shift_val = c(3,3,3,3,3,1,1,1,1,1,4,4,4,4,4))
Использование data.table. Сначала избавляемся от ненужных повторов в сдвиге_val и переносим информацию в другую таблицу.
library(data.table)
setDT(df)
df_shift <- df[, unique(.SD), .SDcols = c("ID", "shift_val")]
setkey(df_shift, "ID") # For faster lookup
get_n <- \(id) df_shift[unlist(id), shift_val]
df[, shift_val := NULL]
df[, var_value := shift(var_value, n = get_n(.BY)), by = ID]
# ID var_description var_value
# <char> <char> <char>
# 1: a description 1 desc 1 val
# 2: a description 2 desc 2 val
# 3: a description 3 <NA>
# 4: a description 4 <NA>
# 5: a description 5 <NA>
# 6: b description 1 desc 1 val
# 7: b description 2 desc 2 val
# 8: b description 3 desc 3 val
# 9: b description 4 desc 4 val
# 10: b description 5 <NA>
# 11: c description 1 desc 1 val
# 12: c description 2 <NA>
# 13: c description 3 <NA>
# 14: c description 4 <NA>
# 15: c description 5 <NA>
@metaltoaster, я думаю, было бы полезно, если бы вы добавили в свой вопрос некоторую информацию о вашем сеансе, например, версию R и версии пакета для dplyr, purrr и data.table, чтобы пользователи не догадывались, что может работать для вас.
Спасибо @LMc, сейчас обновлю.
@metaltoaster. Используйте function(id)
вместо \(id)
на старом R.
Вот опция purrr
, которая должна работать в более старых версиях:
library(purrr)
shift <- function(dat){
amt <- dat$shift_val[1]
within(dat, new_var_val <- c(dat$var_value[-seq_len(amt)], rep(NA, amt)))
}
df %>%
split(. , .$ID) %>%
map_dfr(shift)
Выход
ID var_description var_value shift_val new_var_val
1 a description 1 <NA> 3 desc 1 val
2 a description 2 <NA> 3 desc 2 val
3 a description 3 <NA> 3 <NA>
4 a description 4 desc 1 val 3 <NA>
5 a description 5 desc 2 val 3 <NA>
6 b description 1 <NA> 1 desc 1 val
7 b description 2 desc 1 val 1 desc 2 val
8 b description 3 desc 2 val 1 desc 3 val
9 b description 4 desc 3 val 1 desc 4 val
10 b description 5 desc 4 val 1 <NA>
11 c description 1 <NA> 4 desc 1 val
12 c description 2 <NA> 4 <NA>
13 c description 3 <NA> 4 <NA>
14 c description 4 <NA> 4 <NA>
15 c description 5 desc 1 val 4 <NA>
просто используйте lead
из dplyr
:
Обновлено: поскольку вы используете более старую версию, используйте:
df %>%
group_by(ID) %>%
mutate(var_value1 = lead(var_value, shift_val[1])) %>%
ungroup()
df %>%
mutate(var_value1 = lead(var_value,shift_val[1]), .by = ID)
ID var_description var_value shift_val var_value1
1 a description 1 <NA> 3 desc 1 val
2 a description 2 <NA> 3 desc 2 val
3 a description 3 <NA> 3 <NA>
4 a description 4 desc 1 val 3 <NA>
5 a description 5 desc 2 val 3 <NA>
6 b description 1 <NA> 1 desc 1 val
7 b description 2 desc 1 val 1 desc 2 val
8 b description 3 desc 2 val 1 desc 3 val
9 b description 4 desc 3 val 1 desc 4 val
10 b description 5 desc 4 val 1 <NA>
11 c description 1 <NA> 4 desc 1 val
12 c description 2 <NA> 4 <NA>
13 c description 3 <NA> 4 <NA>
14 c description 4 <NA> 4 <NA>
15 c description 5 desc 1 val 4 <NA>
К вашему сведению, пользователь, похоже, использует более старую версию R, поэтому я сомневаюсь, что .by
доступен. ОП, возможно, захочет связаться с group_by(ID)
.
Спасибо за это, я работаю со старой версией R (3.6.1), и при запуске решения
get_n <- \(id) df_shift[unlist(id), shift_val]
вызывает ошибку. Подскажите, пожалуйста, есть ли способ адаптировать это для моей текущей версии? Спасибо