Рваная строкаСуммы в R

Я пытаюсь сделать rowSum для фактических столбцов. Однако я хотел бы включить значения до даты UpTo для некоторых наблюдений. Вот фрейм данных:

dat <- structure(list(Company = c("ABC", "DEF", "XYZ"), UpTo = c(NA, 
"Q2", "Q3"), Actual.Q1 = c(100L, 80L, 100L), Actual.Q2 = c(50L, 
75L, 50L), Forecast.Q3 = c(80L, 50L, 80L), Forecast.Q4 = c(90L, 
80L, 100L)), .Names = c("Company", "UpTo", "Actual.Q1", "Actual.Q2", 
"Forecast.Q3", "Forecast.Q4"), class = "data.frame", row.names = c("1", 
"2", "3"))

  Company UpTo Actual.Q1 Actual.Q2 Forecast.Q3 Forecast.Q4
1     ABC   NA       100        50          80          90
2     DEF   Q2        80        75          50          80
3     XYZ   Q3       100        50          80         100
  • Для компании ABC, поскольку даты UpTo нет, будет просто Actual.Q1 + Actual.Q2, то есть 150.
  • Для компании DEF, поскольку дата UpToQ2, это будет Actual.Q1 + Actual.Q2, что равно 155.
  • Для компании XYZ, поскольку дата UpToQ3, это будет Actual.Q1 + Actual.Q2 + Forecast.Q3, что равно 230.

Результирующий фрейм данных будет выглядеть так:

  Company UpTo Actual.Q1 Actual.Q2 Forecast.Q3 Forecast.Q4 SumRecent
1     ABC   NA       100        50          80          90       150
2     DEF   Q2        80        75          50          80       155
3     XYZ   Q3       100        50          80         100       230

Я пытался использовать функцию rowSums. Однако это не влияет на переменную UpTo. Любая помощь приветствуется. Спасибо!


person J Kang    schedule 23.11.2016    source источник
comment
Если это NA, это будет только Q1 + Q2, поскольку нам нужны фактические значения только в том случае, если не указано актуальное значение.   -  person J Kang    schedule 23.11.2016


Ответы (4)


Вот возможность:

df$SumRecent <- sapply(1:nrow(df), function(x) {sum(df[x,3:ifelse(is.na(grep(df[x,2], colnames(df))[1]), 4, grep(df[x,2], colnames(df))[1])])})


#   Company UpTo Actual.Q1 Actual.Q2 Forecast.Q3 Forecast.Q4 SumRecent
# 1     ABC <NA>       100        50          80          90       150
# 2     DEF   Q2        80        75          50          80       155
# 3     XYZ   Q3       100        50          80         100       230

Ищем с использованием grep совпадение значения в столбце UpTo (df[x,2]) с именами столбцов df (colnames(df)). Если мы находим его, мы получаем сумму, если мы не находим его, мы просто суммируем значения в столбцах 3 и 4.

person etienne    schedule 23.11.2016
comment
По опыту я действительно очень ненавидел себя за использование числовых ссылок в data.frame. Вместо этого всегда используйте имена — поверьте мне! Краткость в этом случае не стоит потери рассудка, когда что-то ломается из-за изменения позиции столбца. - person Brandon Bertelsen; 23.11.2016
comment
@BrandonBertelsen: я согласен, что это может вызвать некоторые проблемы, но в этом случае, не используя числовые ссылки, нам придется просмотреть каждое имя столбца, оканчивающееся на QXXX, убедиться, что они расположены в порядке возрастания, и так далее, что кажется, слишком усложняет проблему. (или у вас есть более быстрое решение?) Я предположил, что формат data.frame был исправлен, чтобы ответить на вопрос. - person etienne; 23.11.2016
comment
Это не критика, а просто предостережение относительно такого программирования в R в целом. Ответы Stackoverflow имеют большой охват. Так что предостережение полезно для новых программистов :) - person Brandon Bertelsen; 23.11.2016
comment
@BrandonBertelsen о, я не воспринял это как критику, извините, если мой комментарий заставил вас так подумать, я ценю слово предостережения - person etienne; 23.11.2016

Мы можем использовать двоичные взвешенные суммы строк.

UpTo <- as.character(dat$UpTo)  ## in case you have factor column
UpTo[is.na(UpTo)] <- "Q2"  ## replace `NA` to "Q2"
w <- outer(as.integer(substr(UpTo, 2, 2)), 1:4, ">=")
#     [,1] [,2]  [,3]  [,4]
#[1,] TRUE TRUE FALSE FALSE
#[2,] TRUE TRUE FALSE FALSE
#[3,] TRUE TRUE  TRUE FALSE

У нас есть логическая матрица. Но это не влияет на арифметические вычисления, так как TRUE равно 1, а FALSE равно 0. Затем мы выполняем взвешенные суммы строк:

X <- data.matrix(dat[3:6])
dat$SumRecent <- rowSums(X * w)

#  Company UpTo Actual.Q1 Actual.Q2 Forecast.Q3 Forecast.Q4 SumRecent
#1     ABC <NA>       100        50          80          90       150
#2     DEF   Q2        80        75          50          80       155
#3     XYZ   Q3       100        50          80         100       230

Преимуществом этого подхода является его скорость/эффективность, так как он полностью векторизован. Этот метод очень быстрый. Вы можете обратиться к результатам теста в быстром способе создания двоичной матрицы с известным числом 1 в каждой строке в R.

person Zheyuan Li    schedule 23.11.2016

Это также должно работать:

df$UpTo <- as.character(df$UpTo)
df$SumRecent <- apply(df, 1, function(x) ifelse(is.na(x[2]), sum(as.integer(x[3:4])), 
                                           sum(as.integer(x[3:(grep(x[2], names(df)))]))))
df

#     Company UpTo Actual.Q1 Actual.Q2 Forecast.Q3 Forecast.Q4 SumRecent
#1     ABC <NA>       100        50          80          90       150
#2     DEF   Q2        80        75          50          80       155
#3     XYZ   Q3       100        50          80         100       230
person Sandipan Dey    schedule 23.11.2016

Другой подход с использованием таблицы данных:

require(data.table)
dat <- fread('Company UpTo Actual.Q1 Actual.Q2 Forecast.Q3 Forecast.Q4
             ABC   NA       100        50          80          90
             DEF   Q2        80        75          50          80
             XYZ   Q3       100        50          80         100')

dat[, SumRecent:= ifelse(is.na(UpTo), Actual.Q1 + Actual.Q2,  
                                      sum(.SD[, grepl(paste0("Q[1-", substring(UpTo, 2), "]$"), names(.SD)), with = F]) ), by = Company]
person User2321    schedule 24.11.2016