R에서 선형 회귀 및 그룹화


93

lm()함수를 사용하여 R에서 선형 회귀를 수행하고 싶습니다 . 내 데이터는 연도 (22 년)에 대한 필드와 주 (50 개 주)에 대한 필드가있는 연간 시계열입니다. 나는 각 상태에 대한 회귀를 맞추고 싶어서 결국에는 작품 반응의 벡터를 가지게됩니다. 각 상태에 대해 for 루프를 수행 한 다음 루프 내부에서 회귀를 수행하고 각 회귀의 결과를 벡터에 추가하는 것을 상상할 수 있습니다. 그러나 그것은 그다지 R처럼 보이지 않습니다. SAS에서는 'by'문을 수행하고 SQL에서는 'group by'를 수행합니다. 이 작업을 수행하는 R 방식은 무엇입니까?


1
R에는 많은 그룹 별 함수가 있지만 모든 함수가 그룹 별 회귀에 적합한 것은 아니라고 사람들에게 말하고 싶습니다. 예를 들어, aggregate는 올바른 것이 아닙니다 . 둘 다tapply .
李哲源

답변:


48

lme4패키지를 사용하는 한 가지 방법이 있습니다.

 library(lme4)
 d <- data.frame(state=rep(c('NY', 'CA'), c(10, 10)),
                 year=rep(1:10, 2),
                 response=c(rnorm(10), rnorm(10)))

 xyplot(response ~ year, groups=state, data=d, type='l')

 fits <- lmList(response ~ year | state, data=d)
 fits
#------------
Call: lmList(formula = response ~ year | state, data = d)
Coefficients:
   (Intercept)        year
CA -1.34420990  0.17139963
NY  0.00196176 -0.01852429

Degrees of freedom: 20 total; 16 residual
Residual standard error: 0.8201316

2
이 두 모델 모두에 대해 R2를 나열하는 방법이 있습니까? 예를 들어, 연 후에 R2 열을 추가합니다. 또한 각 계수에 대해 p- 값을 추가 하시겠습니까?
ToToRo

3
@ToToRo 여기에서 실행 가능한 솔루션을 찾을 수 있습니다 (결코 늦음) : Your.df [, summary (lm (Y ~ X)) $ r.squared, by = Your.factor] 여기서 : Y, X 및 Your.factor 당신의 변수입니다. Your.df는 data.table 클래스 여야합니다
FraNut

60

다음은 plyr 패키지를 사용하는 접근 방식입니다 .

d <- data.frame(
  state = rep(c('NY', 'CA'), 10),
  year = rep(1:10, 2),
  response= rnorm(20)
)

library(plyr)
# Break up d by state, then fit the specified model to each piece and
# return a list
models <- dlply(d, "state", function(df) 
  lm(response ~ year, data = df))

# Apply coef to each model and return a data frame
ldply(models, coef)

# Print the summary of each model
l_ply(models, summary, .print = TRUE)

데이터에서 NA로 표시되는 모든 주 (예 : miles.of.ocean.shoreline)에서 사용할 수 없었던 추가 독립 변수를 추가했다고 가정 해 보겠습니다. 작품 호출이 실패하지 않을까요? 어떻게 처리 할 수 ​​있습니까?
MikeTP 2013 년

함수 내에서 해당 사건에 대한 테스트에 필요한 것과 다른 공식 사용
해들리

요약 (마지막 단계)의 각 호출에 하위 그룹의 이름을 추가 할 수 있습니까?
비트 뿌리

당신이 실행하는 경우 layout(matrix(c(1,2,3,4),2,2)) # optional 4 graphs/page 다음 l_ply(models, plot)당신은 너무 잔차 플롯의 각을 얻을. 그룹으로 각 플롯에 레이블을 지정할 수 있습니까 (예 :이 경우 "상태")?
Brian D

51

2009 년부터 dplyrSAS가 수행하는 것과 매우 유사한 이러한 종류의 그룹화를 수행 할 수있는 매우 좋은 방법을 실제로 제공하는이 릴리스되었습니다.

library(dplyr)

d <- data.frame(state=rep(c('NY', 'CA'), c(10, 10)),
                year=rep(1:10, 2),
                response=c(rnorm(10), rnorm(10)))
fitted_models = d %>% group_by(state) %>% do(model = lm(response ~ year, data = .))
# Source: local data frame [2 x 2]
# Groups: <by row>
#
#    state   model
#   (fctr)   (chr)
# 1     CA <S3:lm>
# 2     NY <S3:lm>
fitted_models$model
# [[1]]
# 
# Call:
# lm(formula = response ~ year, data = .)
# 
# Coefficients:
# (Intercept)         year  
#    -0.06354      0.02677  
#
#
# [[2]]
# 
# Call:
# lm(formula = response ~ year, data = .)
# 
# Coefficients:
# (Intercept)         year  
#    -0.35136      0.09385  

계수와 Rsquared / p. 값을 검색하려면 broom패키지를 사용할 수 있습니다 . 이 패키지는 다음을 제공합니다.

세 가지 S3 제네릭 : tidy, 회귀 계수와 같은 모델의 통계 결과를 요약합니다. 증가 : 예측, 잔차 및 클러스터 할당과 같은 원래 데이터에 열을 추가합니다. 모델 수준 통계의 한 행 요약을 제공하는 glance.

library(broom)
fitted_models %>% tidy(model)
# Source: local data frame [4 x 6]
# Groups: state [2]
# 
#    state        term    estimate  std.error  statistic   p.value
#   (fctr)       (chr)       (dbl)      (dbl)      (dbl)     (dbl)
# 1     CA (Intercept) -0.06354035 0.83863054 -0.0757668 0.9414651
# 2     CA        year  0.02677048 0.13515755  0.1980687 0.8479318
# 3     NY (Intercept) -0.35135766 0.60100314 -0.5846187 0.5749166
# 4     NY        year  0.09385309 0.09686043  0.9689519 0.3609470
fitted_models %>% glance(model)
# Source: local data frame [2 x 12]
# Groups: state [2]
# 
#    state   r.squared adj.r.squared     sigma statistic   p.value    df
#   (fctr)       (dbl)         (dbl)     (dbl)     (dbl)     (dbl) (int)
# 1     CA 0.004879969  -0.119510035 1.2276294 0.0392312 0.8479318     2
# 2     NY 0.105032068  -0.006838924 0.8797785 0.9388678 0.3609470     2
# Variables not shown: logLik (dbl), AIC (dbl), BIC (dbl), deviance (dbl),
#   df.residual (int)
fitted_models %>% augment(model)
# Source: local data frame [20 x 10]
# Groups: state [2]
# 
#     state   response  year      .fitted   .se.fit     .resid      .hat
#    (fctr)      (dbl) (int)        (dbl)     (dbl)      (dbl)     (dbl)
# 1      CA  0.4547765     1 -0.036769875 0.7215439  0.4915464 0.3454545
# 2      CA  0.1217003     2 -0.009999399 0.6119518  0.1316997 0.2484848
# 3      CA -0.6153836     3  0.016771076 0.5146646 -0.6321546 0.1757576
# 4      CA -0.9978060     4  0.043541551 0.4379605 -1.0413476 0.1272727
# 5      CA  2.1385614     5  0.070312027 0.3940486  2.0682494 0.1030303
# 6      CA -0.3924598     6  0.097082502 0.3940486 -0.4895423 0.1030303
# 7      CA -0.5918738     7  0.123852977 0.4379605 -0.7157268 0.1272727
# 8      CA  0.4671346     8  0.150623453 0.5146646  0.3165112 0.1757576
# 9      CA -1.4958726     9  0.177393928 0.6119518 -1.6732666 0.2484848
# 10     CA  1.7481956    10  0.204164404 0.7215439  1.5440312 0.3454545
# 11     NY -0.6285230     1 -0.257504572 0.5170932 -0.3710185 0.3454545
# 12     NY  1.0566099     2 -0.163651479 0.4385542  1.2202614 0.2484848
# 13     NY -0.5274693     3 -0.069798386 0.3688335 -0.4576709 0.1757576
# 14     NY  0.6097983     4  0.024054706 0.3138637  0.5857436 0.1272727
# 15     NY -1.5511940     5  0.117907799 0.2823942 -1.6691018 0.1030303
# 16     NY  0.7440243     6  0.211760892 0.2823942  0.5322634 0.1030303
# 17     NY  0.1054719     7  0.305613984 0.3138637 -0.2001421 0.1272727
# 18     NY  0.7513057     8  0.399467077 0.3688335  0.3518387 0.1757576
# 19     NY -0.1271655     9  0.493320170 0.4385542 -0.6204857 0.2484848
# 20     NY  1.2154852    10  0.587173262 0.5170932  0.6283119 0.3454545
# Variables not shown: .sigma (dbl), .cooksd (dbl), .std.resid (dbl)

2
나는 rowwise(fitted_models) %>% tidy(model)빗자루 패키지를 작동시키기 위해해야했지만 그렇지 않으면 훌륭한 대답입니다.
pedram

3
훌륭하게 작동합니다 ... 파이프를 떠나지 d %>% group_by(state) %>% do(model = lm(response ~ year, data = .)) %>% rowwise() %>% tidy(model)
않고도이

@pedram 및 @holastello, 적어도 R 3.6.1, broom_0.7.0, dplyr_0.8.3에서는 더 이상 작동하지 않습니다. d %>% group_by(state) %>% do(model=lm(response ~year, data = .)) %>% rowwise() %>% tidy(model) Error in var(if (is.vector(x) || is.factor(x)) x else as.double(x), na.rm = na.rm) : Calling var(x) on a factor x is defunct. Use something like 'all(duplicated(x)[-1L])' to test for a constant vector. In addition: Warning messages: 1: Data frame tidiers are deprecated and will be removed in an upcoming release of broom. ...
Chris Nolte

23

제 생각에는 혼합 선형 모델이 이러한 종류의 데이터에 대한 더 나은 접근 방식입니다. 아래 코드는 전체적인 추세를 고정 효과로 제공합니다. 랜덤 효과는 각 개별 상태의 추세가 글로벌 추세와 어떻게 다른지 나타냅니다. 상관 구조는 시간적 자기 상관을 고려합니다. Pinheiro & Bates (S 및 S-Plus의 혼합 효과 모델)를 살펴보십시오.

library(nlme)
lme(response ~ year, random = ~year|state, correlation = corAR1(~year))

3
이것은 내가 고려하지 않은 몇 가지 사항에 대해 생각하게 만드는 정말 좋은 일반 통계 이론 답변입니다. 질문을하게 만든 응용 프로그램은이 솔루션에 적용 할 수 없지만 문제를 제기 해 주셔서 감사합니다. 감사합니다.
JD Long

1
혼합 모델로 시작하는 것은 좋지 않습니다. 가정이 타당하다는 것을 어떻게 알 수 있습니까?
hadley

7
모델 검증 (및 데이터에 대한 지식)을 통해 가정을 확인해야합니다. BTW 당신은 ​​개별 작품에 대한 가정도 보증 할 수 없습니다. 모든 모델을 별도로 검증해야합니다.
Thierry

14

사용하는 멋진 솔루션 은 @Zach에 의해 CrossValidated data.table에 게시 되었습니다 . 회귀 계수 r ^ 2도 반복적으로 얻을 수 있다고 덧붙입니다.

## make fake data
    library(data.table)
    set.seed(1)
    dat <- data.table(x=runif(100), y=runif(100), grp=rep(1:2,50))

##calculate the regression coefficient r^2
    dat[,summary(lm(y~x))$r.squared,by=grp]
       grp         V1
    1:   1 0.01465726
    2:   2 0.02256595

다음의 다른 모든 출력도 포함됩니다 summary(lm).

dat[,list(r2=summary(lm(y~x))$r.squared , f=summary(lm(y~x))$fstatistic[1] ),by=grp]
   grp         r2        f
1:   1 0.01465726 0.714014
2:   2 0.02256595 1.108173

8

purrr::map이 문제에 대한 접근 방식을 추가하는 것이 가치가 있다고 생각합니다 .

library(tidyverse)

d <- data.frame(state=rep(c('NY', 'CA'), c(10, 10)),
                                 year=rep(1:10, 2),
                                 response=c(rnorm(10), rnorm(10)))

d %>% 
  group_by(state) %>% 
  nest() %>% 
  mutate(model = map(data, ~lm(response ~ year, data = .)))

broom이러한 결과와 함께 패키지 를 사용하는 방법에 대한 추가 아이디어는 @Paul Hiemstra의 답변을 참조하십시오 .


적합치 또는 잔차 열을 원하는 경우 약간의 확장 : lm () 호출을 resid () 호출로 감싼 다음 마지막 줄의 모든 것을 unnest () 호출로 파이프합니다. 물론 변수 이름을 "모델"에서 더 관련성있는 것으로 변경하고 싶을 것입니다.
randy

8
## make fake data
 ngroups <- 2
 group <- 1:ngroups
 nobs <- 100
 dta <- data.frame(group=rep(group,each=nobs),y=rnorm(nobs*ngroups),x=runif(nobs*ngroups))
 head(dta)
#--------------------
  group          y         x
1     1  0.6482007 0.5429575
2     1 -0.4637118 0.7052843
3     1 -0.5129840 0.7312955
4     1 -0.6612649 0.9028034
5     1 -0.5197448 0.1661308
6     1  0.4240346 0.8944253
#------------ 
## function to extract the results of one model
 foo <- function(z) {
   ## coef and se in a data frame
   mr <- data.frame(coef(summary(lm(y~x,data=z))))
   ## put row names (predictors/indep variables)
   mr$predictor <- rownames(mr)
   mr
 }
 ## see that it works
 foo(subset(dta,group==1))
#=========
              Estimate Std..Error   t.value  Pr...t..   predictor
(Intercept)  0.2176477  0.1919140  1.134090 0.2595235 (Intercept)
x           -0.3669890  0.3321875 -1.104765 0.2719666           x
#----------
## one option: use command by
 res <- by(dta,dta$group,foo)
 res
#=========
dta$group: 1
              Estimate Std..Error   t.value  Pr...t..   predictor
(Intercept)  0.2176477  0.1919140  1.134090 0.2595235 (Intercept)
x           -0.3669890  0.3321875 -1.104765 0.2719666           x
------------------------------------------------------------ 
dta$group: 2
               Estimate Std..Error    t.value  Pr...t..   predictor
(Intercept) -0.04039422  0.1682335 -0.2401081 0.8107480 (Intercept)
x            0.06286456  0.3020321  0.2081387 0.8355526           x

## using package plyr is better
 library(plyr)
 res <- ddply(dta,"group",foo)
 res
#----------
  group    Estimate Std..Error    t.value  Pr...t..   predictor
1     1  0.21764767  0.1919140  1.1340897 0.2595235 (Intercept)
2     1 -0.36698898  0.3321875 -1.1047647 0.2719666           x
3     2 -0.04039422  0.1682335 -0.2401081 0.8107480 (Intercept)
4     2  0.06286456  0.3020321  0.2081387 0.8355526           x

6

이제 대답이 조금 늦었지만 비슷한 기능을 찾고있었습니다. R의 내장 함수 'by'도 그룹화를 쉽게 수행 할 수 있습니다.

? by에는 그룹당 적합하고 sapply로 계수를 추출하는 다음 예제가 포함되어 있습니다.

require(stats)
## now suppose we want to extract the coefficients by group 
tmp <- with(warpbreaks,
            by(warpbreaks, tension,
               function(x) lm(breaks ~ wool, data = x)))
sapply(tmp, coef)

3

lm()위 의 함수는 간단한 예입니다. 그건 그렇고, 당신의 데이터베이스에 다음과 같은 열이 있다고 상상합니다.

연도 상태 var1 var2 y ...

내 관점에서 다음 코드를 사용할 수 있습니다.

require(base) 
library(base) 
attach(data) # data = your data base
             #state is your label for the states column
modell<-by(data, data$state, function(data) lm(y~I(1/var1)+I(1/var2)))
summary(modell)

0

질문은 루프 내에서 수정 된 공식을 사용하여 회귀 함수를 호출하는 방법에 관한 것 같습니다.

다음은 다이아몬드 데이터 세트를 사용하여 수행 할 수있는 방법입니다.

attach(ggplot2::diamonds)
strCols = names(ggplot2::diamonds)

formula <- list(); model <- list()
for (i in 1:1) {
  formula[[i]] = paste0(strCols[7], " ~ ", strCols[7+i])
  model[[i]] = glm(formula[[i]]) 

  #then you can plot the results or anything else ...
  png(filename = sprintf("diamonds_price=glm(%s).png", strCols[7+i]))
  par(mfrow = c(2, 2))      
  plot(model[[i]])
  dev.off()
  }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.