Расчет линейной линии тренда для каждой строки таблицы в R

возможно ли каким-то образом провести линейную регрессию для каждой отдельной строки фрейма данных без использования цикла? Выходные данные (точка пересечения + наклон) линии тренда должны быть добавлены к исходному фрейму данных в виде новых столбцов.

Чтобы прояснить свое намерение, я подготовил очень небольшой пример данных:

day1 <- c(1,3,1)
day2 <- c(2,2,1)
day3 <- c(3,1,5)
output.intercept <- c(0,4,-1.66667)
output.slope <- c(1,-1,2)
data <- data.frame(day1,day2,day3,output.intercept,output.slope)

Входные переменные: день 1-3; допустим, это продажи в разных магазинах за 3 дня подряд. Я хочу вычислить линейную линию тренда для трех строк и добавить выходные параметры в исходную таблицу (см. Output.intercept + output.slope) в качестве новых столбцов.

Решение должно быть очень эффективным с точки зрения времени вычислений, поскольку в реальном фрейме данных содержится много 100 тыс. Строк.

С уважением, Кристоф


person user2635656    schedule 14.02.2014    source источник
comment
Что такое переменная ответа?   -  person Sven Hohenstein    schedule 14.02.2014
comment
@SvenHohenstein Ответы показаны, ковариата подразумевается как 1:3 (в данном случае), seq_len(nrow(dat)) в более общем случае.   -  person Gavin Simpson    schedule 14.02.2014


Ответы (4)


design.mat <- cbind(1,1:3)
response.mat <- t(data[,1:3])

reg <- lm.fit(design.mat, response.mat)$coefficients
data <- cbind(data, t(reg))
#  day1 day2 day3 output.intercept output.slope        x1 x2
#1    1    2    3          0.00000            1  0.000000  1
#2    3    2    1          4.00000           -1  4.000000 -1
#3    1    1    5         -1.66667            2 -1.666667  2

Однако, если у вас большой объем данных, может потребоваться цикл из-за ограничений памяти. В этом случае я бы использовал длинный формат data.table и использовал синтаксис пакета by для цикла.

person Roland    schedule 14.02.2014
comment
Вау, отлично работает. Большое спасибо! Я попробую позже с большим набором данных. Для чего нужен exaclty design.mat? Чтобы смоделировать x-значения? - person user2635656; 14.02.2014
comment
Если вы не знаете, что такое матрица дизайна, вам следует изучить учебник по регрессии. - person Roland; 14.02.2014
comment
Еще раз спасибо, решение отлично работает даже с большими данными. Однако одна проблема возникает, когда данные содержат отсутствующие точки данных в форме NA. (Ошибка в lm.fit (design.mat, response.mat): NA / NaN / Inf в 'y') Есть ли способ решить проблему отсутствия некоторых точек данных? Я уже пытался включить функцию na.exclude в оператор lm.fit, но в данном случае она не работает. - person user2635656; 19.02.2014
comment
Перед использованием lm.fit необходимо удалить строки, содержащие значения NA из матрицы плана и ответа. Это одна из многих вещей, которые lm удобно делать для вас, но в сумме это требует большой производительности. - person Roland; 19.02.2014
comment
К сожалению, удаление строк, содержащих NA, не является вариантом, по крайней мере, в одном частном случае, поскольку в одной моей таблице данных почти каждый столбец содержит NA. Нет ли другой возможности провести регрессию только на имеющихся данных и просто не учитывая значения с NA? В противном случае мне придется каким-то образом заранее манипулировать моим файлом исходных данных. - person user2635656; 19.02.2014
comment
Подумайте о том, о чем вы просите. - person Roland; 19.02.2014

Используя ваши данные,

day1 <- c(1,3,1)
day2 <- c(2,2,1)
day3 <- c(3,1,5)
output.intercept <- c(0,4,-1.66667)
output.slope <- c(1,-1,2)
dat <- data.frame(day1,day2,day3)

Я думаю, вам нужно что-то вроде этого:

fits <- lm.fit(cbind(1, seq_len(nrow(dat))), t(dat))
t(coef(fits))

Который дает

R> t(coef(fits))
         x1 x2
[1,]  0.000  1
[2,]  4.000 -1
[3,] -1.667  2

Их можно добавить в dat вот так

dat <- cbind(dat, t(coef(fits)))
names(dat)[-(1:3)] <- c("Intercept","Slope")

R> dat
  day1 day2 day3 Intercept Slope
1    1    2    3     0.000     1
2    3    2    1     4.000    -1
3    1    1    5    -1.667     2

Возможно, было бы проще хранить данные другим способом, используя столбцы в качестве временных рядов, а не строки, если у вас есть какой-либо контроль над первоначальным расположением данных, поскольку это позволило бы избежать необходимости транспонировать большую матрицу при подгонке с помощью lm.fit() . В идеале вы хотите, чтобы данные изначально были расположены следующим образом:

     [,1] [,2] [,3]
day1    1    3    1
day2    2    2    1
day3    3    1    5

Т.е. строки как временные точки, а не отдельные серии, как сейчас. Это связано с тем, как R ожидает расположения данных. Обратите внимание, что мы должны транспонировать ваш dat в вызов lm.fit(), который повлечет за собой копию большого объекта. Следовательно, если вы можете контролировать, как эти данные организованы / поставляются, прежде чем они попадут в R, это поможет в решении большой проблемы.

lm.fit() используется, поскольку это базовый минимальный код, используемый lm(), но мы избегаем сложностей, связанных с анализом формулы и созданием матриц моделей. Если вы хотите быть более эффективным, вам, возможно, придется самостоятельно выполнить разложение QR (код находится в lm.fit() для этого), поскольку есть несколько вещей, которые lm.fit() выполняет в качестве проверок работоспособности, с которыми вы могли бы отказаться, если вы уверены, что ваши данные не приведут к сингулярным матрицам и т. д.

person Gavin Simpson    schedule 14.02.2014
comment
Большое тебе спасибо. Я понимаю, что мне еще ооочень многому нужно научиться в R, даже базовым вещам. И спасибо за подсказку относительно структуры данных. У меня есть контроль над расположением данных, так как я заранее подготовил данные в R. Я подумал, что так будет эффективнее, поскольку мой настоящий файл данных содержит 600 тыс. Строк и только 100 столбцов. - person user2635656; 14.02.2014
comment
Одно замечание: я предполагаю, что утверждение подходит ‹- lm.fit (cbind (1, seq_len (nrow (dat))), t (dat)) должно быть скорректировано для соответствия‹ - lm.fit (cbind (1, seq_len (ncol (дат))), т (дат)). Или я не прав? В примере это сработало, потому что ncol (dat) = nrow (dat). - person user2635656; 19.02.2014

У меня была та же проблема, что и у OP. Это решение будет работать с данными с НА. В этом случае все предыдущие ответы вызывают для меня ошибку:

slp = function(x) {
  y = t(x)
  y = y[!is.na(y)]
  len = length(y):1
  b = cov(y,len)/var(len)
  return(b)}

reg_slp <- apply(data,1,slp)

Получается только уклон, а вот перехват можно легко добавить. Я сомневаюсь, что это особенно эффективно, но в моем случае это было эффективно.

person user3055034    schedule 18.01.2016

Или вот так?

day1 <- c(1,3,1)
day2 <- c(2,2,1)
day3 <- c(3,1,5)
data <- data.frame(day1,day2,day3)
y<-1:3

reg<-apply(data,1,function(x) lm(as.numeric(x)~y))
data[,c("intercept","slope")]<-rbind(reg[[1]]$coef,reg[[2]]$coef,reg[[3]]$coef)
person DatamineR    schedule 14.02.2014
comment
Это правильно, но неэффективно. Обратите внимание, что lm() должен анализировать формулу nrow(dat) раз, что быстро, если вы выполняете 3 раза, и медленно, если вы выполняете 100 000 раз. Кроме того, при этом упускается возможность lm() в том, что он принимает матричный ответ. Таким образом, вам вообще не нужен apply() или цикл; вы можете уместить все серии за один lm() вызов: lm(t(data[, 1:3]) ~ I(1:3)). Однако вы не хотите анализировать формулу и генерировать модели model.frame и model.matrix плюс всякая лишняя болтовня, которую lm() дает вам, если вы хотите работать эффективно. Используйте lm.fit() для улучшения. - person Gavin Simpson; 14.02.2014