Plotando uma linha de regressão por partes

10

Existe uma maneira de plotar a linha de regressão de um modelo por partes assim, além de usar linespara plotar cada segmento separadamente ou usar geom_smooth(aes(group=Ind), method="lm", fill=FALSE)?

m.sqft <- mean(sqft)
model <- lm(price~sqft+I((sqft-m.sqft)*Ind))
# sqft, price: continuous variables, Ind: if sqft>mean(sqft) then 1 else 0

plot(sqft,price)
abline(reg = model)
Warning message:
In abline(reg = model) :
  only using the first two of 3regression coefficients

Obrigado.

George Dontas
fonte

Respostas:

6

A única maneira de saber como fazer isso facilmente é prever a partir do modelo em toda a extensão sqfte traçar as previsões. Não existe uma maneira geral com ablineou similar. Você também pode dar uma olhada no pacote segmentado que se ajustará a esses modelos e fornecerá a infraestrutura de plotagem para você.

Fazendo isso através de previsões e gráficos de base. Primeiro, alguns dados fictícios:

set.seed(1)
sqft <- runif(100)
sqft <- ifelse((tmp <- sqft > mean(sqft)), 1, 0) + rnorm(100, sd = 0.5)
price <- 2 + 2.5 * sqft
price <- ifelse(tmp, price, 0) + rnorm(100, sd = 0.6)
DF <- data.frame(sqft = sqft, price = price,
                 Ind = ifelse(sqft > mean(sqft), 1, 0))
rm(price, sqft)
plot(price ~ sqft, data = DF)

Encaixe o modelo:

mod <- lm(price~sqft+I((sqft-mean(sqft))*Ind), data = DF)

Gere alguns dados para prever e prever:

m.sqft <- with(DF, mean(sqft))
pDF <- with(DF, data.frame(sqft = seq(min(sqft), max(sqft), length = 200)))
pDF <- within(pDF, Ind <- ifelse(sqft > m.sqft, 1, 0))
pDF <- within(pDF, price <- predict(mod, newdata = pDF))

Traçar as linhas de regressão:

ylim <- range(pDF$price, DF$price)
xlim <- range(pDF$sqft, DF$sqft)
plot(price ~ sqft, data = DF, ylim = ylim, xlim = xlim)
lines(price ~ sqft, data = pDF, subset = Ind > 0, col = "red", lwd = 2)
lines(price ~ sqft, data = pDF, subset = Ind < 1, col = "red", lwd = 2)

Você pode codificar isso em uma função simples - você só precisa das etapas nos dois blocos de código anteriores - que você pode usar no lugar de abline:

myabline <- function(model, data, ...) {
    m.sqft <- with(data, mean(sqft))
    pDF <- with(data, data.frame(sqft = seq(min(sqft), max(sqft),
                                            length = 200)))
    pDF <- within(pDF, Ind <- ifelse(sqft > m.sqft, 1, 0))
    pDF <- within(pDF, price <- predict(mod, newdata = pDF))
    lines(price ~ sqft, data = pDF, subset = Ind > 0, ...)
    lines(price ~ sqft, data = pDF, subset = Ind < 1, ...)
    invisible(model)
}

Então:

ylim <- range(pDF$price, DF$price)
xlim <- range(pDF$sqft, DF$sqft)
plot(price ~ sqft, data = DF, ylim = ylim, xlim = xlim)
myabline(mod, DF, col = "red", lwd = 2)

Através do pacote segmentado

require(segmented)
mod2 <- lm(price ~ sqft, data = DF)
mod.s <- segmented(mod2, seg.Z = ~ sqft, psi = 0.5,
                   control = seg.control(stop.if.error = FALSE))
plot(price ~ sqft, data = DF)
plot(mod.s, add = TRUE)
lines(mod.s, col = "red")

Com esses dados, ele não estima o ponto de interrupção em mean(sqft), mas os métodos plote linesnesse pacote podem ajudá-lo a implementar algo mais genérico do myablineque realizar esse trabalho diretamente a partir do lm()modelo ajustado .

Editar: se você deseja segmentar para estimar a localização do ponto de interrupção, defina o 'psi'argumento como NA:

mod.s <- segmented(mod2, seg.Z = ~ sqft, psi = NA,
                   control = seg.control(stop.if.error = FALSE))

Em seguida segmented, tentará K = 10quantis de sqft, com Ka configuração seg.control()e com o padrão 10. Veja ?seg.controlpara mais.

Gavin Simpson
fonte
@ Gavin (+1) Resposta muito mais completa que a minha; Eu apenas gosto disso.
chl
@ Gavin A seção "Via the segmented package" não funcionou para os meus dados. Eu recebi um "Nenhum ponto de interrupção estimado" depois de executar o segmentedcomando.
George Dontas
@ gd047: Desculpas, houve um erro no código que mostrei. Você precisa fornecer ao argumento seq.Zuma fórmula unilateral das variáveis ​​que têm um relacionamento segmentado com a resposta. Editei minha resposta para incluir seq.Z = ~ sqfte adicionei uma observação sobre a segmentedescolha de valores psipara você.
Gavin Simpson
@ gd047 Gostaria de remover minha resposta, pois esta aborda sua pergunta original de uma maneira mais melhor. Importa-se de aceitar este em vez do meu?
chl
model<mf:argumentisnotinterpretableaslogicalInaddition:Warningmessage:Inif(model)objF