Скользящее вычитание по столбцам (не строкам!) в R

#r #dataframe #dplyr #data.table #tidyverse

#r #фрейм данных #dplyr #данные.таблица #tidyverse

Вопрос:

Я искал аналогичный вопрос, но все, кого я нашел, хотели выполнить скользящее вычитание по строкам.

То, что я хочу сделать, — это скользящее вычитание по столбцам моего фрейма данных. В частности, я хотел бы вычесть каждый столбец последовательно (слева направо), сохраняя при этом текущий вычитаемый совокупный, как «общий» столбец для вычитания в следующей последовательности.

Я нашел способ жестко закодировать это, но, очевидно, это выглядит некрасиво, и код сломается, если количество столбцов будет каким-либо образом отличаться от количества созданных dfs.

Допустим, у нас есть рамка данных о населении для каждого возраста за каждый год, а общая сумма-это суммы строк за каждый год:

 df lt;- data.frame(Age lt;- c(1:40),   Total lt;- rep(500,40),   Y1990 lt;- rep(100,40),   Y1991 lt;- rep(100,40),  Y1992 lt;- rep(100,40))  

Желаемый результат был достигнут с помощью следующего кода:

 df1 lt;- df$Total #or df[2] df2 lt;- df1 - df[3] df3 lt;- df2 - df[4] ... dfx lt;- df(x-1) - df[x 1]  #and then we join them together like so: final_df lt;- cbind(df$Age, df1, df2, df3,..., dfx)  #final_df should be the Age column, the Total column (500), df2 should be 400 (500-100 = 400), df3 should be 300, etc. etc.)  

Я возился с циклами, но не мог заставить работать первую/последнюю итерацию (часть x 1/x-1 продолжала выдавать мне ошибку, что индекс был вне диапазона). Я даже попытался использовать «перерыв» или «следующий» в цикле, но я не мог до конца понять это. У меня есть около 70 лет данных, и, возможно, в будущем их будет больше, поэтому мне нужно обновить свой код, чтобы он был устойчив к будущему, чтобы не иметь сотен строк кода «dfx».

Мне интересно, может ли кто-нибудь предоставить супер простой цикл или функцию для этого. Возможно, решение data.table проще всего, хотя мне трудно работать с синтаксисом data.table. Бонусные баллы, если вы сможете сохранить имя переменной на протяжении всей итерации (хотя это и не обязательно). Я просто хочу, чтобы мой код был красивым и надежным! Ваше здоровье и спасибо вам.

Ответ №1:

Я думаю, это то, чего ты хочешь. Нет необходимости в 40 одинаковых строках, 5 должно быть достаточно:

 df lt;- data.frame(Age = c(1:5), Total = rep(500, 5), Y1990 = rep(100, 5), Y1991 = rep(100, 5), Y1992 = rep(100, 5))  final_df lt;- data.frame(df[, 1:2], df$Total - t(apply(df[, 3:5], 1, cumsum))) colnames(final_df)[-(1:2)] lt;- c("df2", "df3", "df4") final_df # Age Total df2 df3 df4 # 1 1 500 400 300 200 # 2 2 500 400 300 200 # 3 3 500 400 300 200 # 4 4 500 400 300 200 # 5 5 500 400 300 200  

Комментарии:

1. Потрясающий, простой, интерпретируемый, использует базу R и обобщается для x столбцов. Большое вам спасибо!

2. Просто немного отредактировал свой код final_df lt;- data.frame(df[, 1:2], df$Total - t(apply(df[, 3:ncol(df)], 1, cumsum))) , заменив 3:5 на 3:ncol(df), так что теперь он должен работать бесконечное количество лет! Очень легко понять, спасибо!!

3. Спасибо за редактирование.

Ответ №2:

Вот решение с данными.таблица:

 library(data.table) df lt;- data.frame(Age = c(1:5), Total = rep(500, 5), Y1990 = rep(100, 5), Y1991 = rep(100, 5), Y1992 = rep(100, 5)) setDT(df) final_df lt;- cbind(df[, .(Age = Age)],   df[, Reduce(`-`, .SD, init = Total, accumulate = TRUE),   .SDcols = Y1990:Y1992]) final_df  Age V1 V2 V3 V4 1: 1 500 400 300 200 2: 2 500 400 300 200 3: 3 500 400 300 200 4: 4 500 400 300 200 5: 5 500 400 300 200  

Комментарии:

1. Это выглядит великолепно! Спасибо. Мне это нравится. Функция SDcols. Я начал чаще использовать Reduce, и это отличный инструмент. Работает как по волшебству. Просто теперь нужно лучше разбираться в синтаксисе data.table. Ура!

Ответ №3:

Различные способы сделать это:

 cbind(df[1], matrixStats::rowCumsums(as.matrix(df[-1])))  Age 1 2 3 4 1 1 500 600 700 800 2 2 500 600 700 800 3 3 500 600 700 800 4 4 500 600 700 800 5 5 500 600 700 800   cbind(df[1], list2DF(Reduce('-', df[-1], accumulate = TRUE)))   Age Var.2 Var.3 Var.4 Var.5 1 1 500 400 300 200 2 2 500 400 300 200 3 3 500 400 300 200 4 4 500 400 300 200 5 5 500 400 300 200  

Комментарии:

1. Спасибо Оньямбу, очень элегантно. Ваше решение похоже на решение Брайана. Просто любопытно, что это за функция list2DF ()? Работает ли это аналогично cbind()? Требуется ли для этого список в качестве входных данных? Меня это интересует. Ура!