1. Regresion Lineal Multiple

a. Carga de datos

Cargamos los datos e inspeccionamos brevemente las variables y sus tipos

# carga librerias tidyverse y ggplot2
suppressPackageStartupMessages(library(tidyverse))
suppressPackageStartupMessages(library(ggplot2))

#Cargamos datos y eliminamos columna id que no ayuda al analisis
ar_properties <- readRDS('ar_properties.rds') %>% select( -c('id') )

#Convertimos en factores las variables tipo chr
ar_properties <- ar_properties %>% mutate_if( is.character, as.factor )

#Inspeccionamos los datos
nrow(ar_properties)
[1] 45904
glimpse(ar_properties)
Observations: 45,904
Variables: 7
$ l3              <fct> Velez Sarsfield, Nuñez, Almagro, Almagro, Almagro, Almagro, Almagro, Almagro, Almagro, Almag...
$ rooms           <dbl> 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 2,...
$ bathrooms       <dbl> 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,...
$ surface_total   <dbl> 95, 44, 40, 49, 40, 40, 40, 49, 40, 40, 40, 32, 40, 36, 90, 45, 54, 40, 33, 40, 40, 36, 38, ...
$ surface_covered <dbl> 69, 38, 37, 44, 37, 37, 37, 44, 37, 37, 37, 30, 34, 33, 90, 35, 48, 40, 32, 34, 36, 33, 34, ...
$ price           <dbl> 199900, 147000, 92294, 115000, 77000, 88900, 88798, 110975, 92943, 99000, 96984, 125000, 995...
$ property_type   <fct> Casa, Departamento, Departamento, Departamento, Departamento, Departamento, Departamento, De...

Observamos 5 variables del tipo numericas (rooms, bathrooms, surface_total, surface_covered y price), y 2 categoricas (property_type y l3)

b. Modelo de regresion lineal

# Calculamos el modelo
model <- lm(price~., data=ar_properties)
summary(model)

Call:
lm(formula = price ~ ., data = ar_properties)

Residuals:
    Min      1Q  Median      3Q     Max 
-400904  -33817   -3307   24660  560915 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -109406.61    4788.67 -22.847  < 2e-16 ***
l3Agronomía                   623.53    8846.14   0.070 0.943807    
l3Almagro                   -4520.04    4295.24  -1.052 0.292650    
l3Balvanera                -24788.27    4551.65  -5.446 5.18e-08 ***
l3Barracas                 -10128.24    5351.06  -1.893 0.058397 .  
l3Barrio Norte              49921.81    4417.82  11.300  < 2e-16 ***
l3Belgrano                  69648.12    4283.55  16.259  < 2e-16 ***
l3Boca                     -47540.60    7076.20  -6.718 1.86e-11 ***
l3Boedo                    -19034.38    5219.54  -3.647 0.000266 ***
l3Caballito                  6220.15    4301.29   1.446 0.148153    
l3Catalinas                -76321.95   33563.74  -2.274 0.022974 *  
l3Centro / Microcentro     -29046.49    6781.80  -4.283 1.85e-05 ***
l3Chacarita                 11903.39    5299.02   2.246 0.024687 *  
l3Coghlan                   40820.55    5462.90   7.472 8.02e-14 ***
l3Colegiales                34073.02    4816.54   7.074 1.52e-12 ***
l3Congreso                 -32314.97    5494.75  -5.881 4.10e-09 ***
l3Constitución             -47292.98    6321.63  -7.481 7.50e-14 ***
l3Flores                   -22510.27    4536.15  -4.962 6.99e-07 ***
l3Floresta                 -28315.65    5069.38  -5.586 2.34e-08 ***
l3Las Cañitas               90455.90    5883.38  15.375  < 2e-16 ***
l3Liniers                  -20080.34    5366.27  -3.742 0.000183 ***
l3Mataderos                -33863.43    5424.79  -6.242 4.35e-10 ***
l3Monserrat                -32431.49    5228.46  -6.203 5.59e-10 ***
l3Monte Castro              -8770.72    5949.63  -1.474 0.140445    
l3Nuñez                     56958.42    4559.69  12.492  < 2e-16 ***
l3Once                     -30757.83    5456.51  -5.637 1.74e-08 ***
l3Palermo                   66169.58    4221.50  15.674  < 2e-16 ***
l3Parque Avellaneda        -34398.95    7598.09  -4.527 5.99e-06 ***
l3Parque Centenario        -12288.30    5016.45  -2.450 0.014305 *  
l3Parque Chacabuco         -22537.83    5314.36  -4.241 2.23e-05 ***
l3Parque Chas                5195.26    7542.97   0.689 0.490981    
l3Parque Patricios         -36808.02    5973.29  -6.162 7.24e-10 ***
l3Paternal                 -13314.50    5189.69  -2.566 0.010304 *  
l3Pompeya                  -79977.17    8035.74  -9.953  < 2e-16 ***
l3Puerto Madero            259015.83    5095.12  50.836  < 2e-16 ***
l3Recoleta                  64088.22    4360.34  14.698  < 2e-16 ***
l3Retiro                    26067.40    5281.27   4.936 8.01e-07 ***
l3Saavedra                  19492.00    4914.18   3.966 7.31e-05 ***
l3San Cristobal            -23739.75    4955.13  -4.791 1.67e-06 ***
l3San Nicolás              -26247.55    5168.96  -5.078 3.83e-07 ***
l3San Telmo                 -5653.85    4877.12  -1.159 0.246356    
l3Tribunales               -34608.17    8924.63  -3.878 0.000106 ***
l3Velez Sarsfield          -25943.69    8303.75  -3.124 0.001783 ** 
l3Versalles                -22232.13    6758.40  -3.290 0.001004 ** 
l3Villa Crespo               1595.26    4317.54   0.369 0.711770    
l3Villa del Parque          -3290.17    4866.59  -0.676 0.498997    
l3Villa Devoto              13301.39    4807.08   2.767 0.005659 ** 
l3Villa General Mitre      -19170.08    6802.25  -2.818 0.004831 ** 
l3Villa Lugano             -83039.18    6533.35 -12.710  < 2e-16 ***
l3Villa Luro                -7579.11    5404.78  -1.402 0.160833    
l3Villa Ortuzar             18667.61    6829.18   2.734 0.006269 ** 
l3Villa Pueyrredón          10516.80    5349.56   1.966 0.049314 *  
l3Villa Real                -8823.37    8745.56  -1.009 0.313030    
l3Villa Riachuelo          -32775.66   17171.10  -1.909 0.056298 .  
l3Villa Santa Rita          -5767.71    6383.86  -0.903 0.366274    
l3Villa Soldati           -136489.91   18944.29  -7.205 5.90e-13 ***
l3Villa Urquiza             30648.43    4418.91   6.936 4.09e-12 ***
rooms                       -3961.27     444.58  -8.910  < 2e-16 ***
bathrooms                   34040.98     644.28  52.836  < 2e-16 ***
surface_total                 919.08      23.52  39.069  < 2e-16 ***
surface_covered              1457.18      28.73  50.715  < 2e-16 ***
property_typeDepartamento   92653.32    2191.23  42.284  < 2e-16 ***
property_typePH             46779.37    2274.94  20.563  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 66580 on 45841 degrees of freedom
Multiple R-squared:  0.7764,    Adjusted R-squared:  0.7761 
F-statistic:  2568 on 62 and 45841 DF,  p-value: < 2.2e-16

Observaciones:

  • La mayoria (pero no todos) los coeficientes figuran como significativos. Para los barrios de Retiro y Colegiales, pareceria que estos no afectan al precio; pero podria interpretarse tambien que esto se debe a que estos barrios se encuentran cerca del caso basal, y no tiran el precio final ni hacia arriba ni hacia abajo.
  • Los coeficientes grandes para las variables dummies de l3 parecerian indicar en general los barrios caros, y los coeficientes bajos los barrios baratos.
  • Siempre hay un caso de las variables dummies con coeficiente N/A (o que son directamente eliminados del modelo), producto de la dependencia lineal entre esta y el resto de las variables dummies (por como es construida).
  • Contrario a lo esperado, el coeficiente de la variable room es negativo (uno esperaria que a mayor cantidad de dambientes, mayor precio). Pero si interpretamos que esto es la variacion esperada de precios, manteniendo todas las demas variables constantes entonces interpretamos que para departamentos de igual tamaño, si se agrega un ambiente mas (achicando en promedio el tamaño de cada ambiente) el precio esperado bajaria.

c. Prediccion

# Funcion para crear un caso de estudio
crear_caso <- function( barrio, rooms, bathrooms, surface_total, surface_covered, property_type )
{
    new=ar_properties[FALSE,] %>% select( -c('price') )
    new[1,] = list(barrio, rooms, bathrooms, surface_total, surface_covered, property_type)
    return(new)
}

#Creamos los casos, y los 'etiquetamos'
casos <- do.call(rbind, list(
    crear_caso(barrio='Abasto', rooms=3, bathrooms=2, surface_total=120, surface_covered=120, property_type='Departamento'), 
    crear_caso(barrio='Balvanera', rooms=2, bathrooms=3, surface_total=100, surface_covered=80, property_type='Departamento')
    )) %>%
    
    # creamos columna temporal para agrupar los casos
    cbind(caso=c(1,2)) %>%

    #agrupamos los casos
    group_by(caso) %>% nest() %>%

    #hacemos las predicciones para cada caso
    mutate( prediction=map(data,function(data) data.frame(predict(model, data, se.fit=TRUE)))) %>%

    unnest(data) %>% unnest(prediction) %>%

    #descartamos la columna temporal de agrupacion
    ungroup() %>% select(-'caso')

head(casos)

Observando las predicciones, podermos afirmar que segun el modelo el departamento mas grande (de Abasto) tendria considerablemente mas chances de ser el mas caro, aun teniendo el cuenta el desvio de la prediccion.

2. Creacion de variables

a. Deteccion de barrios por precio

Se buscara a continuacion agrupar los barrios en 3 categorias de precio, dependiendo de su precio/m2 promedio. Al haber una mayor correlacion del precio con la superficie cubierta, utilizaremos a esta como referencia:

# calculamos el precio por metro cuadrado
mean_price_area <- ar_properties %>%
  select( 'l3', 'price', 'surface_covered' ) %>%
  mutate(precio_area=price/surface_covered) %>%
  select(-c('price', 'surface_covered')) %>%
    
  # y luego agrupamos por barrio, y calculamos la media
  group_by( l3 ) %>%
  summarise(mean_price_area=mean(precio_area))

# Calculamos los centroides de 3 categorias usando K-means
groups=kmeans(mean_price_area$mean_price_area, 3, centers=c(2000,2800,3500) )

# graficamos los puntos, junto con sus categorias
ggplot(mean_price_area, aes(x=mean_price_area, y='', color=as.factor(groups$cluster))) +
  geom_jitter(width=0, height=0.5, size=2,alpha=0.8) +
  coord_fixed(ratio=1000) +
  theme(legend.position = "none") +
  geom_vline(xintercept=2400, color = "blue") +
  geom_vline(xintercept=3200, color = "blue")


# agregamos la columna de cuan caro es el barrio
mean_price_area$group <- groups$cluster
mean_price_area <- mean_price_area %>% arrange(mean_price_area)

Para realizar la particion en 3 grupos de precios, utilizamos el algoritmo k-means para encontrar los limites de los mismos. Realizando una inspeccion visual podemos observar un claro corte entre los mas caros y los del medio, y uno no tan claro (pero acceptable) entre los mas bajos y los medios. De aqui utilizamos estos limites encontrados para definir los precios altos, medios y bajos. Cabe tambien destacar que se forzo el valor inicial de los centroides del algoritmo k-means, ya que no todas las inicializacions aleatorias generaban resultados satisfactorios

ar_properties_range <- ar_properties %>%
  inner_join( mean_price_area, by="l3" ) %>%
  select( -'mean_price_area' )%>% 
  mutate(precio_barrio = case_when(group == 1   ~ 'bajo',
                                 group == 2   ~ 'medio',
                                 TRUE ~     'alto')) %>%
  select( -'l3', -'group' )

ar_properties_range$precio_barrio <- as.factor(ar_properties_range$precio_barrio)
glimpse(ar_properties_range)
Observations: 45,904
Variables: 7
$ rooms           <dbl> 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 2,...
$ bathrooms       <dbl> 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,...
$ surface_total   <dbl> 95, 44, 40, 49, 40, 40, 40, 49, 40, 40, 40, 32, 40, 36, 90, 45, 54, 40, 33, 40, 40, 36, 38, ...
$ surface_covered <dbl> 69, 38, 37, 44, 37, 37, 37, 44, 37, 37, 37, 30, 34, 33, 90, 35, 48, 40, 32, 34, 36, 33, 34, ...
$ price           <dbl> 199900, 147000, 92294, 115000, 77000, 88900, 88798, 110975, 92943, 99000, 96984, 125000, 995...
$ property_type   <fct> Casa, Departamento, Departamento, Departamento, Departamento, Departamento, Departamento, De...
$ precio_barrio   <fct> bajo, alto, medio, medio, medio, medio, medio, medio, medio, medio, medio, alto, alto, alto,...

b. Calculo modelo por precio_barrio

Mediante la agrupacion de barrios por precio promedio del punto anterior, se procede a calcular el modelo lineal:

#funcion para ajustar el caso base
relevel.variable <- function(df, variable.name, new.base.level)
{
  df[variable.name] <- relevel( df[[variable.name]], ref=new.base.level)
  return(df)
}

# Calculamos el modelo utilizando solo el precio_barrio, en lugar del barrio actual
model <- lm( price~., data=ar_properties_range %>% relevel.variable('precio_barrio', 'medio'))
summary(model)

Call:
lm(formula = price ~ ., data = ar_properties_range %>% relevel.variable("precio_barrio", 
    "medio"))

Residuals:
    Min      1Q  Median      3Q     Max 
-438724  -36706   -4394   25242  689396 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -121842.62    2477.45  -49.18   <2e-16 ***
rooms                       -7452.96     466.69  -15.97   <2e-16 ***
bathrooms                   36345.31     678.74   53.55   <2e-16 ***
surface_total                 857.97      24.63   34.83   <2e-16 ***
surface_covered              1628.21      29.98   54.31   <2e-16 ***
property_typeDepartamento  103846.01    2263.16   45.88   <2e-16 ***
property_typePH             54991.30    2393.93   22.97   <2e-16 ***
precio_barrioalto           64692.14     723.90   89.37   <2e-16 ***
precio_barriobajo          -29384.95    1046.44  -28.08   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 70720 on 45895 degrees of freedom
Multiple R-squared:  0.7474,    Adjusted R-squared:  0.7474 
F-statistic: 1.698e+04 on 8 and 45895 DF,  p-value: < 2.2e-16
  • Podemos observar como el R ajustado de este nuevo modelo es muy parecido al que utiliza la informacion de todos los barrios (aunque ligeramente peor; 0.776 vs 0.747)
  • Igual que en el caso anterior, una variable de cada set de variables dummy no tiene coeficiente, al estar linealmente correlacionada con las otras
  • Al concentrar todos los barrios en 3 casos, ahora figura claramente que todas las variables con significativas
  • Los coeficientes modificadores de precio segun el barrio tienen valores esperados, siendo engativo para la cateogria precio_barriobajo, y positivo para el otro coeficiente precio_barrioalto, y dejando el coeficiente de 0 (por omision) para precio_barriomedio (se forzo el caso baso a medio intencionalmente)

c. Comparacion de modelos

Si nos atenemos estrictamente a los resultados numericos, el modelo de todos los barrios explica mejor la variabilidad cuando comparamos el R ajustado (utilizamos el R ajustado para comparar, ya que los modelos tienen distinta cantidad de variables, por lo cual se utiliza este mecanismo de compensacion para lgorar una comparacion mas ‘justa’). Aun asi el modelo de rango de precios parece ser mas simple y mas facilmente interpretable. Si la precision de la prediccion deseada lo permite, este 2do modelo simplificado podria ser una mejor opcion cuando no se necesita demasiada granularidad o presicion. Es necesario destacar que estos resultados varian dependiendo del corte de precio de los barrios que se realice, lo cual este modelo agrega un poco mas de arbitrareidad a la ecuacion.

d. Variables fuertemente correlacionadas

Notando que la superficie cubierta y superficie total estan fuertemente correlacionadas, por lo que procedemos a crear una nueva variable a partir de estas:

# Obervamos si hay registros que tengan mas superficie cubierta que total (que seria imposible)
print(paste(
    'Cantidad de registros con superficie total < superficie cubierta :',
    nrow(ar_properties %>% filter(surface_total < surface_covered))
    ))
[1] "Cantidad de registros con superficie total < superficie cubierta : 0"
ar_properties_superficie_patio <-ar_properties_range %>% mutate(surface_patio=surface_total-surface_covered) %>% select(-'surface_total' )
glimpse(ar_properties_superficie_patio)
Observations: 45,904
Variables: 7
$ rooms           <dbl> 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 2,...
$ bathrooms       <dbl> 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,...
$ surface_covered <dbl> 69, 38, 37, 44, 37, 37, 37, 44, 37, 37, 37, 30, 34, 33, 90, 35, 48, 40, 32, 34, 36, 33, 34, ...
$ price           <dbl> 199900, 147000, 92294, 115000, 77000, 88900, 88798, 110975, 92943, 99000, 96984, 125000, 995...
$ property_type   <fct> Casa, Departamento, Departamento, Departamento, Departamento, Departamento, Departamento, De...
$ precio_barrio   <fct> bajo, alto, medio, medio, medio, medio, medio, medio, medio, medio, medio, alto, alto, alto,...
$ surface_patio   <dbl> 26, 6, 3, 5, 3, 3, 3, 5, 3, 3, 3, 2, 6, 3, 0, 10, 6, 0, 1, 6, 4, 3, 4, 3, 7, 0, 3, 0, 3, 5, ...
# Calculamos el modelo
model <- lm( price~., data=ar_properties_superficie_patio %>% relevel.variable( 'precio_barrio', 'medio' ))
summary(model)

Call:
lm(formula = price ~ ., data = ar_properties_superficie_patio %>% 
    relevel.variable("precio_barrio", "medio"))

Residuals:
    Min      1Q  Median      3Q     Max 
-438724  -36706   -4394   25242  689396 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -121842.62    2477.45  -49.18   <2e-16 ***
rooms                       -7452.96     466.69  -15.97   <2e-16 ***
bathrooms                   36345.31     678.74   53.55   <2e-16 ***
surface_covered              2486.18      15.83  157.03   <2e-16 ***
property_typeDepartamento  103846.01    2263.16   45.88   <2e-16 ***
property_typePH             54991.30    2393.93   22.97   <2e-16 ***
precio_barrioalto           64692.14     723.90   89.37   <2e-16 ***
precio_barriobajo          -29384.95    1046.44  -28.08   <2e-16 ***
surface_patio                 857.97      24.63   34.83   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 70720 on 45895 degrees of freedom
Multiple R-squared:  0.7474,    Adjusted R-squared:  0.7474 
F-statistic: 1.698e+04 on 8 and 45895 DF,  p-value: < 2.2e-16

Si comparamos los dos modelos, podemos apreciar que estos son identicos en cuanto a prediccion. El efecto final de aplicar una transformacion lineal entre variables tuvo como efecto el ajuste de los coeficientes, pero la predictivilidad sigue siendo exactamente la misma: mismo R2 ajustado, mismos residuos y mismos coeficientes con la excpecion descrita abajo.

Modelo original

surface_total 857.97 24.63 34.83 <2e-16 *** surface_covered 1628.21 29.98 54.31 <2e-16 ***

Modelo con transformacion lineal

surface_covered 2486.18 15.83 157.03 <2e-16 *** surface_patio 857.97 24.63 34.83 <2e-16 ***

donde podemos apreciar que el componente de precio generado por el par de variables arriba es el mismo que el de abajo, si consideramos que surface_patio = surface_total - surface_covered

3. Evaluacion del modelo

a. Residuos del modelo

Graficamos los residuos del modelo anterior:

ggplot(model, aes(.fitted, .resid)) + 
  geom_point(color = "lightcoral",alpha=0.2) +
  geom_smooth() +
  labs(x="precio estimado", y="Residuos", title='Residuos vs precio estimado')


ggplot(model, aes(sample=.resid)) + 
  stat_qq(color = "lightcoral")   +
  stat_qq_line()

Al analizar los residuos (y en especial la tendencia de estos) se puede observar que el modelo lineal no logra ajustar de forma que pueda explicar la tendencia observada. Tambein se puede observar que el modelo no es homocedastico, al aumentar la variabilidad mientras mas altos son los precios estimados. Tambien podemos observar en el q-q plot de los residuos que cuando estos son grandes se desvian de lo esperado para una distribucion normal

b. Modelo Log

model <- lm( log(price)~log(rooms)+
                  log(bathrooms)+
                  log(surface_covered)+
                  surface_patio +
                  property_type +
                  precio_barrio
            , data=ar_properties_superficie_patio)
summary(model)

Call:
lm(formula = log(price) ~ log(rooms) + log(bathrooms) + log(surface_covered) + 
    surface_patio + property_type + precio_barrio, data = ar_properties_superficie_patio)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.33122 -0.14607 -0.00747  0.13441  1.34718 

Coefficients:
                            Estimate Std. Error  t value Pr(>|t|)    
(Intercept)                8.672e+00  1.865e-02  464.909   <2e-16 ***
log(rooms)                -3.111e-02  3.775e-03   -8.241   <2e-16 ***
log(bathrooms)             1.800e-01  3.804e-03   47.319   <2e-16 ***
log(surface_covered)       8.089e-01  4.432e-03  182.507   <2e-16 ***
surface_patio              3.744e-03  7.938e-05   47.161   <2e-16 ***
property_typeDepartamento  2.479e-01  7.171e-03   34.564   <2e-16 ***
property_typePH            7.914e-02  7.619e-03   10.388   <2e-16 ***
precio_barriobajo         -4.546e-01  3.405e-03 -133.514   <2e-16 ***
precio_barriomedio        -2.664e-01  2.329e-03 -114.372   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2274 on 45895 degrees of freedom
Multiple R-squared:  0.822, Adjusted R-squared:  0.8219 
F-statistic: 2.649e+04 on 8 and 45895 DF,  p-value: < 2.2e-16
ggplot(model, aes(.fitted, .resid)) + 
  geom_point(color = "lightcoral",alpha=0.2) +
  geom_smooth() +
  labs(x="log(precio estimado)", y="Residuos", title='Residuos vs precio estimado')


ggplot(model, aes(sample=.resid)) + 
  stat_qq(color = "lightcoral")   +
  stat_qq_line()

Al analizar los resultados del modelo construido a partir del logaritmo de ciertas variables, podemos observar como este ajusta mejor que los casos anteriores, pudiendo explicar ~82% de la variabilidad del mismo, y al mismo tiempo mejorando el grafico de residuos: en este nuevo modelo pareceria haber una mejor homocedasticidad en un rango mas grande, y la distribucion de los residuos ajustan mejor a la distribucion normal.

4. Modelos discriminados por tipo de propiedad

suppressPackageStartupMessages(library(generics))

#analizamos las poblaciones
ar_properties_superficie_patio %>% count(property_type) %>% mutate(porcentage=n/nrow(ar_properties_superficie_patio))

#Calculamos el modelo lineal para las poblaciones discriminadas
modelos_por_tipo <- ar_properties_superficie_patio %>%
  # agrupamos y anidamos
  group_by(property_type) %>% nest() %>%

  # calculamos el modelo lineal por tipo de propiedad, y descartamos los datos originales
  mutate(lm=map(data,function(df) lm( price~., data=df))) %>% select( -'data' ) %>%

  # Explayamos la columna de resumen del modelo
  mutate(modeldata=map(lm, function(mod) glance(mod))) %>%

  # explayamos la columna de detalles del modelo
  mutate(tidy_lm=map(lm, function(lm) tidy(lm))) %>%
  
  # descartamos la columna demodelo
  select(-'lm')

# Imprimimos los detalles del modelo en general
modelos_por_tipo %>% unnest(modeldata) %>% select('property_type','adj.r.squared', 'df.residual', 'sigma')

# Imprimimos los detalles de los coeficientes
modelos_por_tipo %>% unnest(tidy_lm)

Cuando comparamos los distintos modelos generados cuando discriminamos por tipo de propiedad, comparando el R2 ajustado de cada uno, notamos que estos difieren considerablemente entre sí. Observamos que para departamentos, el modelo explica el 77% (recordamos que cuando no discriminabamos por tipo de propiedad el modelo explicaba el 74%); pero tambien notamos como para casa este valor es de tan solo 60%, y de 69% para PHs. De estos datos podemos estimar que los departamentos son mas predecibles para un modelo lineal, y al representar la gran mayoria de la poblacion total, la predictibilidad de esta poblacion aumentaba la predictibilidad del modelo combinado (respecto a predictibilidad de Casas y PHs por separado).

Aun asi, podemos extraer otras conclusiones al separar los modelos, como por ej, la variacion esperada de precio por metro cuadrado cubierto es distinta en los modelos, y con una gran significatividad en todos los casos (Siendo los departamentos los mas caros por metro cuadrado, seguido por los PHs y finalmente por las casas), dato que en el modelo combinado solo era representado como una diferencia “constante” que dependía solamente del tipo de propiedad.

LS0tDQp0aXRsZTogIlRyYWJham8gUHJhY3RpY28gMiINCmF1dGhvcjogTmljb2xhcyBTdWJvdG92c2t5DQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdGhlbWU6IHNwYWNlbGFiDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KLS0tDQoNCiMgMS4gUmVncmVzaW9uIExpbmVhbCBNdWx0aXBsZQ0KDQojIyMgYS4gQ2FyZ2EgZGUgZGF0b3MNCg0KQ2FyZ2Ftb3MgbG9zIGRhdG9zIGUgaW5zcGVjY2lvbmFtb3MgYnJldmVtZW50ZSBsYXMgdmFyaWFibGVzIHkgc3VzIHRpcG9zIA0KDQpgYGB7cn0NCiMgY2FyZ2EgbGlicmVyaWFzIHRpZHl2ZXJzZSB5IGdncGxvdDINCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHRpZHl2ZXJzZSkpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkNCg0KI0NhcmdhbW9zIGRhdG9zIHkgZWxpbWluYW1vcyBjb2x1bW5hIGlkIHF1ZSBubyBheXVkYSBhbCBhbmFsaXNpcw0KYXJfcHJvcGVydGllcyA8LSByZWFkUkRTKCdhcl9wcm9wZXJ0aWVzLnJkcycpICU+JSBzZWxlY3QoIC1jKCdpZCcpICkNCg0KI0NvbnZlcnRpbW9zIGVuIGZhY3RvcmVzIGxhcyB2YXJpYWJsZXMgdGlwbyBjaHINCmFyX3Byb3BlcnRpZXMgPC0gYXJfcHJvcGVydGllcyAlPiUgbXV0YXRlX2lmKCBpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvciApDQoNCiNJbnNwZWNjaW9uYW1vcyBsb3MgZGF0b3MNCm5yb3coYXJfcHJvcGVydGllcykNCmdsaW1wc2UoYXJfcHJvcGVydGllcykNCmBgYA0KDQpPYnNlcnZhbW9zIDUgdmFyaWFibGVzIGRlbCB0aXBvIG51bWVyaWNhcyAoYHJvb21zYCwgYGJhdGhyb29tc2AsIGBzdXJmYWNlX3RvdGFsYCwgYHN1cmZhY2VfY292ZXJlZGAgeSBgcHJpY2VgKSwgeSAyIGNhdGVnb3JpY2FzIChgcHJvcGVydHlfdHlwZWAgeSBgbDNgKQ0KDQoNCiMjIyBiLiBNb2RlbG8gZGUgcmVncmVzaW9uIGxpbmVhbA0KDQpgYGB7cn0NCiMgQ2FsY3VsYW1vcyBlbCBtb2RlbG8NCm1vZGVsIDwtIGxtKHByaWNlfi4sIGRhdGE9YXJfcHJvcGVydGllcykNCnN1bW1hcnkobW9kZWwpDQpgYGANCk9ic2VydmFjaW9uZXM6DQoNCiogTGEgbWF5b3JpYSAocGVybyBubyB0b2RvcykgbG9zIGNvZWZpY2llbnRlcyBmaWd1cmFuIGNvbW8gc2lnbmlmaWNhdGl2b3MuIFBhcmEgbG9zIGJhcnJpb3MgZGUgUmV0aXJvIHkgQ29sZWdpYWxlcywgcGFyZWNlcmlhIHF1ZSBlc3RvcyBubyBhZmVjdGFuIGFsIHByZWNpbzsgcGVybyBwb2RyaWEgaW50ZXJwcmV0YXJzZSB0YW1iaWVuIHF1ZSBlc3RvIHNlIGRlYmUgYSBxdWUgZXN0b3MgYmFycmlvcyBzZSBlbmN1ZW50cmFuIGNlcmNhIGRlbCBjYXNvIGJhc2FsLCB5IG5vICp0aXJhbiogZWwgcHJlY2lvIGZpbmFsIG5pIGhhY2lhIGFycmliYSBuaSBoYWNpYSBhYmFqby4NCiogTG9zIGNvZWZpY2llbnRlcyBncmFuZGVzIHBhcmEgbGFzIHZhcmlhYmxlcyBkdW1taWVzIGRlIGwzIHBhcmVjZXJpYW4gaW5kaWNhciBlbiBnZW5lcmFsIGxvcyBiYXJyaW9zICpjYXJvcyosIHkgbG9zIGNvZWZpY2llbnRlcyBiYWpvcyBsb3MgYmFycmlvcyAqYmFyYXRvcyouDQoqIFNpZW1wcmUgaGF5IHVuIGNhc28gZGUgbGFzIHZhcmlhYmxlcyBkdW1taWVzIGNvbiBjb2VmaWNpZW50ZSBOL0EgKG8gcXVlIHNvbiBkaXJlY3RhbWVudGUgZWxpbWluYWRvcyBkZWwgbW9kZWxvKSwgcHJvZHVjdG8gZGUgbGEgZGVwZW5kZW5jaWEgbGluZWFsIGVudHJlIGVzdGEgeSBlbCByZXN0byBkZSBsYXMgdmFyaWFibGVzIGR1bW1pZXMgKHBvciBjb21vIGVzIGNvbnN0cnVpZGEpLg0KKiBDb250cmFyaW8gYSBsbyBlc3BlcmFkbywgZWwgY29lZmljaWVudGUgZGUgbGEgdmFyaWFibGUgYHJvb21gIGVzIG5lZ2F0aXZvICh1bm8gZXNwZXJhcmlhIHF1ZSBhIG1heW9yIGNhbnRpZGFkIGRlIGRhbWJpZW50ZXMsIG1heW9yIHByZWNpbykuIFBlcm8gc2kgaW50ZXJwcmV0YW1vcyBxdWUgZXN0byBlcyBsYSB2YXJpYWNpb24gZXNwZXJhZGEgZGUgcHJlY2lvcywgbWFudGVuaWVuZG8gdG9kYXMgbGFzIGRlbWFzIHZhcmlhYmxlcyBjb25zdGFudGVzIGVudG9uY2VzIGludGVycHJldGFtb3MgcXVlIHBhcmEgZGVwYXJ0YW1lbnRvcyBkZSBpZ3VhbCB0YW1hw7FvLCBzaSBzZSBhZ3JlZ2EgdW4gYW1iaWVudGUgbWFzIChhY2hpY2FuZG8gZW4gcHJvbWVkaW8gZWwgdGFtYcOxbyBkZSBjYWRhIGFtYmllbnRlKSBlbCBwcmVjaW8gZXNwZXJhZG8gYmFqYXJpYS4NCg0KDQojIyMgYy4gUHJlZGljY2lvbg0KDQpgYGB7cn0NCiMgRnVuY2lvbiBwYXJhIGNyZWFyIHVuIGNhc28gZGUgZXN0dWRpbw0KY3JlYXJfY2FzbyA8LSBmdW5jdGlvbiggYmFycmlvLCByb29tcywgYmF0aHJvb21zLCBzdXJmYWNlX3RvdGFsLCBzdXJmYWNlX2NvdmVyZWQsIHByb3BlcnR5X3R5cGUgKQ0Kew0KICAgIG5ldz1hcl9wcm9wZXJ0aWVzW0ZBTFNFLF0gJT4lIHNlbGVjdCggLWMoJ3ByaWNlJykgKQ0KICAgIG5ld1sxLF0gPSBsaXN0KGJhcnJpbywgcm9vbXMsIGJhdGhyb29tcywgc3VyZmFjZV90b3RhbCwgc3VyZmFjZV9jb3ZlcmVkLCBwcm9wZXJ0eV90eXBlKQ0KICAgIHJldHVybihuZXcpDQp9DQoNCiNDcmVhbW9zIGxvcyBjYXNvcywgeSBsb3MgJ2V0aXF1ZXRhbW9zJw0KY2Fzb3MgPC0gZG8uY2FsbChyYmluZCwgbGlzdCgNCiAgICBjcmVhcl9jYXNvKGJhcnJpbz0nQWJhc3RvJywgcm9vbXM9MywgYmF0aHJvb21zPTIsIHN1cmZhY2VfdG90YWw9MTIwLCBzdXJmYWNlX2NvdmVyZWQ9MTIwLCBwcm9wZXJ0eV90eXBlPSdEZXBhcnRhbWVudG8nKSwgDQogICAgY3JlYXJfY2FzbyhiYXJyaW89J0JhbHZhbmVyYScsIHJvb21zPTIsIGJhdGhyb29tcz0zLCBzdXJmYWNlX3RvdGFsPTEwMCwgc3VyZmFjZV9jb3ZlcmVkPTgwLCBwcm9wZXJ0eV90eXBlPSdEZXBhcnRhbWVudG8nKQ0KICAgICkpICU+JQ0KICAgIA0KICAgICMgY3JlYW1vcyBjb2x1bW5hIHRlbXBvcmFsIHBhcmEgYWdydXBhciBsb3MgY2Fzb3MNCiAgICBjYmluZChjYXNvPWMoMSwyKSkgJT4lDQoNCiAgICAjYWdydXBhbW9zIGxvcyBjYXNvcw0KICAgIGdyb3VwX2J5KGNhc28pICU+JSBuZXN0KCkgJT4lDQoNCiAgICAjaGFjZW1vcyBsYXMgcHJlZGljY2lvbmVzIHBhcmEgY2FkYSBjYXNvDQogICAgbXV0YXRlKCBwcmVkaWN0aW9uPW1hcChkYXRhLGZ1bmN0aW9uKGRhdGEpIGRhdGEuZnJhbWUocHJlZGljdChtb2RlbCwgZGF0YSwgc2UuZml0PVRSVUUpKSkpICU+JQ0KDQogICAgdW5uZXN0KGRhdGEpICU+JSB1bm5lc3QocHJlZGljdGlvbikgJT4lDQoNCiAgICAjZGVzY2FydGFtb3MgbGEgY29sdW1uYSB0ZW1wb3JhbCBkZSBhZ3J1cGFjaW9uDQogICAgdW5ncm91cCgpICU+JSBzZWxlY3QoLSdjYXNvJykNCg0KaGVhZChjYXNvcykNCmBgYA0KDQoNCk9ic2VydmFuZG8gbGFzIHByZWRpY2Npb25lcywgcG9kZXJtb3MgYWZpcm1hciBxdWUgc2VndW4gZWwgbW9kZWxvIGVsIGRlcGFydGFtZW50byBtYXMgZ3JhbmRlIChkZSBBYmFzdG8pIHRlbmRyaWEgY29uc2lkZXJhYmxlbWVudGUgbWFzIGNoYW5jZXMgZGUgc2VyIGVsIG1hcyBjYXJvLCBhdW4gdGVuaWVuZG8gZWwgY3VlbnRhIGVsIGRlc3ZpbyBkZSBsYSBwcmVkaWNjaW9uLg0KDQoNCiMgMi4gQ3JlYWNpb24gZGUgdmFyaWFibGVzDQoNCiMjIyBhLiBEZXRlY2Npb24gZGUgYmFycmlvcyBwb3IgcHJlY2lvDQoNClNlIGJ1c2NhcmEgYSBjb250aW51YWNpb24gYWdydXBhciBsb3MgYmFycmlvcyBlbiAzIGNhdGVnb3JpYXMgZGUgcHJlY2lvLCBkZXBlbmRpZW5kbyBkZSBzdSBwcmVjaW8vbV4yXiBwcm9tZWRpby4gQWwgaGFiZXIgdW5hIG1heW9yIGNvcnJlbGFjaW9uIGRlbCBwcmVjaW8gY29uIGxhIHN1cGVyZmljaWUgY3ViaWVydGEsIHV0aWxpemFyZW1vcyBhIGVzdGEgY29tbyByZWZlcmVuY2lhOg0KDQpgYGB7cn0NCiMgY2FsY3VsYW1vcyBlbCBwcmVjaW8gcG9yIG1ldHJvIGN1YWRyYWRvDQptZWFuX3ByaWNlX2FyZWEgPC0gYXJfcHJvcGVydGllcyAlPiUNCiAgc2VsZWN0KCAnbDMnLCAncHJpY2UnLCAnc3VyZmFjZV9jb3ZlcmVkJyApICU+JQ0KICBtdXRhdGUocHJlY2lvX2FyZWE9cHJpY2Uvc3VyZmFjZV9jb3ZlcmVkKSAlPiUNCiAgc2VsZWN0KC1jKCdwcmljZScsICdzdXJmYWNlX2NvdmVyZWQnKSkgJT4lDQogICAgDQogICMgeSBsdWVnbyBhZ3J1cGFtb3MgcG9yIGJhcnJpbywgeSBjYWxjdWxhbW9zIGxhIG1lZGlhDQogIGdyb3VwX2J5KCBsMyApICU+JQ0KICBzdW1tYXJpc2UobWVhbl9wcmljZV9hcmVhPW1lYW4ocHJlY2lvX2FyZWEpKQ0KDQojIENhbGN1bGFtb3MgbG9zIGNlbnRyb2lkZXMgZGUgMyBjYXRlZ29yaWFzIHVzYW5kbyBLLW1lYW5zDQpncm91cHM9a21lYW5zKG1lYW5fcHJpY2VfYXJlYSRtZWFuX3ByaWNlX2FyZWEsIDMsIGNlbnRlcnM9YygyMDAwLDI4MDAsMzUwMCkgKQ0KDQojIGdyYWZpY2Ftb3MgbG9zIHB1bnRvcywganVudG8gY29uIHN1cyBjYXRlZ29yaWFzDQpnZ3Bsb3QobWVhbl9wcmljZV9hcmVhLCBhZXMoeD1tZWFuX3ByaWNlX2FyZWEsIHk9JycsIGNvbG9yPWFzLmZhY3Rvcihncm91cHMkY2x1c3RlcikpKSArDQogIGdlb21faml0dGVyKHdpZHRoPTAsIGhlaWdodD0wLjUsIHNpemU9MixhbHBoYT0wLjgpICsNCiAgY29vcmRfZml4ZWQocmF0aW89MTAwMCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTI0MDAsIGNvbG9yID0gImJsdWUiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD0zMjAwLCBjb2xvciA9ICJibHVlIikNCg0KIyBhZ3JlZ2Ftb3MgbGEgY29sdW1uYSBkZSBjdWFuIGNhcm8gZXMgZWwgYmFycmlvDQptZWFuX3ByaWNlX2FyZWEkZ3JvdXAgPC0gZ3JvdXBzJGNsdXN0ZXINCm1lYW5fcHJpY2VfYXJlYSA8LSBtZWFuX3ByaWNlX2FyZWEgJT4lIGFycmFuZ2UobWVhbl9wcmljZV9hcmVhKQ0KYGBgDQoNClBhcmEgcmVhbGl6YXIgbGEgcGFydGljaW9uIGVuIDMgZ3J1cG9zIGRlIHByZWNpb3MsIHV0aWxpemFtb3MgZWwgYWxnb3JpdG1vICoqay1tZWFucyoqIHBhcmEgZW5jb250cmFyIGxvcyBsaW1pdGVzIGRlIGxvcyBtaXNtb3MuIFJlYWxpemFuZG8gdW5hIGluc3BlY2Npb24gdmlzdWFsIHBvZGVtb3Mgb2JzZXJ2YXIgdW4gY2xhcm8gY29ydGUgZW50cmUgbG9zIG1hcyBjYXJvcyB5IGxvcyBkZWwgbWVkaW8sIHkgdW5vIG5vIHRhbiBjbGFybyAocGVybyBhY2NlcHRhYmxlKSBlbnRyZSBsb3MgbWFzIGJham9zIHkgbG9zIG1lZGlvcy4gRGUgYXF1aSB1dGlsaXphbW9zIGVzdG9zIGxpbWl0ZXMgZW5jb250cmFkb3MgcGFyYSBkZWZpbmlyIGxvcyBwcmVjaW9zIGFsdG9zLCBtZWRpb3MgeSBiYWpvcy4gQ2FiZSB0YW1iaWVuIGRlc3RhY2FyIHF1ZSBzZSBmb3J6byBlbCB2YWxvciBpbmljaWFsIGRlIGxvcyBjZW50cm9pZGVzIGRlbCBhbGdvcml0bW8gay1tZWFucywgeWEgcXVlIG5vIHRvZGFzIGxhcyBpbmljaWFsaXphY2lvbnMgYWxlYXRvcmlhcyBnZW5lcmFiYW4gcmVzdWx0YWRvcyBzYXRpc2ZhY3Rvcmlvcw0KDQoNCmBgYHtyfQ0KYXJfcHJvcGVydGllc19yYW5nZSA8LSBhcl9wcm9wZXJ0aWVzICU+JQ0KICBpbm5lcl9qb2luKCBtZWFuX3ByaWNlX2FyZWEsIGJ5PSJsMyIgKSAlPiUNCiAgc2VsZWN0KCAtJ21lYW5fcHJpY2VfYXJlYScgKSU+JSANCiAgbXV0YXRlKHByZWNpb19iYXJyaW8gPSBjYXNlX3doZW4oZ3JvdXAgPT0gMSAgIH4gJ2Jham8nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPT0gMiAgIH4gJ21lZGlvJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAgICAgJ2FsdG8nKSkgJT4lDQogIHNlbGVjdCggLSdsMycsIC0nZ3JvdXAnICkNCg0KYXJfcHJvcGVydGllc19yYW5nZSRwcmVjaW9fYmFycmlvIDwtIGFzLmZhY3Rvcihhcl9wcm9wZXJ0aWVzX3JhbmdlJHByZWNpb19iYXJyaW8pDQpnbGltcHNlKGFyX3Byb3BlcnRpZXNfcmFuZ2UpDQpgYGANCg0KDQojIyMgYi4gQ2FsY3VsbyBtb2RlbG8gcG9yIHByZWNpb19iYXJyaW8NCg0KTWVkaWFudGUgbGEgYWdydXBhY2lvbiBkZSBiYXJyaW9zIHBvciBwcmVjaW8gcHJvbWVkaW8gZGVsIHB1bnRvIGFudGVyaW9yLCBzZSBwcm9jZWRlIGEgY2FsY3VsYXIgZWwgbW9kZWxvIGxpbmVhbDoNCg0KYGBge3J9DQojZnVuY2lvbiBwYXJhIGFqdXN0YXIgZWwgY2FzbyBiYXNlDQpyZWxldmVsLnZhcmlhYmxlIDwtIGZ1bmN0aW9uKGRmLCB2YXJpYWJsZS5uYW1lLCBuZXcuYmFzZS5sZXZlbCkNCnsNCiAgZGZbdmFyaWFibGUubmFtZV0gPC0gcmVsZXZlbCggZGZbW3ZhcmlhYmxlLm5hbWVdXSwgcmVmPW5ldy5iYXNlLmxldmVsKQ0KICByZXR1cm4oZGYpDQp9DQoNCiMgQ2FsY3VsYW1vcyBlbCBtb2RlbG8gdXRpbGl6YW5kbyBzb2xvIGVsIHByZWNpb19iYXJyaW8sIGVuIGx1Z2FyIGRlbCBiYXJyaW8gYWN0dWFsDQptb2RlbCA8LSBsbSggcHJpY2V+LiwgZGF0YT1hcl9wcm9wZXJ0aWVzX3JhbmdlICU+JSByZWxldmVsLnZhcmlhYmxlKCdwcmVjaW9fYmFycmlvJywgJ21lZGlvJykpDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoNCiogUG9kZW1vcyBvYnNlcnZhciBjb21vIGVsIFIgYWp1c3RhZG8gZGUgZXN0ZSBudWV2byBtb2RlbG8gZXMgbXV5IHBhcmVjaWRvIGFsIHF1ZSB1dGlsaXphIGxhIGluZm9ybWFjaW9uIGRlIHRvZG9zIGxvcyBiYXJyaW9zIChhdW5xdWUgbGlnZXJhbWVudGUgcGVvcjsgMC43NzYgdnMgMC43NDcpDQoqIElndWFsIHF1ZSBlbiBlbCBjYXNvIGFudGVyaW9yLCB1bmEgdmFyaWFibGUgZGUgY2FkYSBzZXQgZGUgdmFyaWFibGVzIGR1bW15IG5vIHRpZW5lIGNvZWZpY2llbnRlLCBhbCBlc3RhciBsaW5lYWxtZW50ZSBjb3JyZWxhY2lvbmFkYSBjb24gbGFzIG90cmFzDQoqIEFsIGNvbmNlbnRyYXIgdG9kb3MgbG9zIGJhcnJpb3MgZW4gMyBjYXNvcywgYWhvcmEgZmlndXJhIGNsYXJhbWVudGUgcXVlIHRvZGFzIGxhcyB2YXJpYWJsZXMgY29uIHNpZ25pZmljYXRpdmFzDQoqIExvcyBjb2VmaWNpZW50ZXMgbW9kaWZpY2Fkb3JlcyBkZSBwcmVjaW8gc2VndW4gZWwgYmFycmlvIHRpZW5lbiB2YWxvcmVzIGVzcGVyYWRvcywgc2llbmRvIGVuZ2F0aXZvIHBhcmEgbGEgY2F0ZW9ncmlhIGBwcmVjaW9fYmFycmlvYmFqb2AsIHkgcG9zaXRpdm8gcGFyYSBlbCBvdHJvIGNvZWZpY2llbnRlIGBwcmVjaW9fYmFycmlvYWx0b2AsIHkgZGVqYW5kbyBlbCBjb2VmaWNpZW50ZSBkZSAwIChwb3Igb21pc2lvbikgcGFyYSBgcHJlY2lvX2JhcnJpb21lZGlvYCAoc2UgZm9yem8gZWwgY2FzbyBiYXNvIGEgYG1lZGlvYCBpbnRlbmNpb25hbG1lbnRlKQ0KDQojIyMgYy4gQ29tcGFyYWNpb24gZGUgbW9kZWxvcw0KDQpTaSBub3MgYXRlbmVtb3MgZXN0cmljdGFtZW50ZSBhIGxvcyByZXN1bHRhZG9zIG51bWVyaWNvcywgZWwgbW9kZWxvIGRlIHRvZG9zIGxvcyBiYXJyaW9zIGV4cGxpY2EgbWVqb3IgbGEgdmFyaWFiaWxpZGFkIGN1YW5kbyBjb21wYXJhbW9zIGVsIFIgYWp1c3RhZG8gKHV0aWxpemFtb3MgZWwgUiBhanVzdGFkbyBwYXJhIGNvbXBhcmFyLCB5YSBxdWUgbG9zIG1vZGVsb3MgdGllbmVuIGRpc3RpbnRhIGNhbnRpZGFkIGRlIHZhcmlhYmxlcywgcG9yIGxvIGN1YWwgc2UgdXRpbGl6YSBlc3RlIG1lY2FuaXNtbyBkZSBjb21wZW5zYWNpb24gcGFyYSBsZ29yYXIgdW5hIGNvbXBhcmFjaW9uIG1hcyAnanVzdGEnKS4gQXVuIGFzaSBlbCBtb2RlbG8gZGUgcmFuZ28gZGUgcHJlY2lvcyBwYXJlY2Ugc2VyIG1hcyBzaW1wbGUgeSBtYXMgZmFjaWxtZW50ZSBpbnRlcnByZXRhYmxlLiBTaSBsYSBwcmVjaXNpb24gZGUgbGEgcHJlZGljY2lvbiBkZXNlYWRhIGxvIHBlcm1pdGUsIGVzdGUgMmRvIG1vZGVsbyBzaW1wbGlmaWNhZG8gcG9kcmlhIHNlciB1bmEgbWVqb3Igb3BjaW9uIGN1YW5kbyBubyBzZSBuZWNlc2l0YSBkZW1hc2lhZGEgZ3JhbnVsYXJpZGFkIG8gcHJlc2ljaW9uLg0KRXMgbmVjZXNhcmlvIGRlc3RhY2FyIHF1ZSBlc3RvcyByZXN1bHRhZG9zIHZhcmlhbiBkZXBlbmRpZW5kbyBkZWwgY29ydGUgZGUgcHJlY2lvIGRlIGxvcyBiYXJyaW9zIHF1ZSBzZSByZWFsaWNlLCBsbyBjdWFsIGVzdGUgbW9kZWxvIGFncmVnYSB1biBwb2NvIG1hcyBkZSBhcmJpdHJhcmVpZGFkIGEgbGEgZWN1YWNpb24uDQoNCg0KIyMjIGQuIFZhcmlhYmxlcyBmdWVydGVtZW50ZSBjb3JyZWxhY2lvbmFkYXMNCg0KTm90YW5kbyBxdWUgbGEgc3VwZXJmaWNpZSBjdWJpZXJ0YSB5IHN1cGVyZmljaWUgdG90YWwgZXN0YW4gZnVlcnRlbWVudGUgY29ycmVsYWNpb25hZGFzLCBwb3IgbG8gcXVlIHByb2NlZGVtb3MgYSBjcmVhciB1bmEgbnVldmEgdmFyaWFibGUgYSBwYXJ0aXIgZGUgZXN0YXM6DQoNCmBgYHtyfQ0KIyBPYmVydmFtb3Mgc2kgaGF5IHJlZ2lzdHJvcyBxdWUgdGVuZ2FuIG1hcyBzdXBlcmZpY2llIGN1YmllcnRhIHF1ZSB0b3RhbCAocXVlIHNlcmlhIGltcG9zaWJsZSkNCnByaW50KHBhc3RlKA0KICAgICdDYW50aWRhZCBkZSByZWdpc3Ryb3MgY29uIHN1cGVyZmljaWUgdG90YWwgPCBzdXBlcmZpY2llIGN1YmllcnRhIDonLA0KICAgIG5yb3coYXJfcHJvcGVydGllcyAlPiUgZmlsdGVyKHN1cmZhY2VfdG90YWwgPCBzdXJmYWNlX2NvdmVyZWQpKQ0KICAgICkpDQoNCmFyX3Byb3BlcnRpZXNfc3VwZXJmaWNpZV9wYXRpbyA8LWFyX3Byb3BlcnRpZXNfcmFuZ2UgJT4lIG11dGF0ZShzdXJmYWNlX3BhdGlvPXN1cmZhY2VfdG90YWwtc3VyZmFjZV9jb3ZlcmVkKSAlPiUgc2VsZWN0KC0nc3VyZmFjZV90b3RhbCcgKQ0KZ2xpbXBzZShhcl9wcm9wZXJ0aWVzX3N1cGVyZmljaWVfcGF0aW8pDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBDYWxjdWxhbW9zIGVsIG1vZGVsbw0KbW9kZWwgPC0gbG0oIHByaWNlfi4sIGRhdGE9YXJfcHJvcGVydGllc19zdXBlcmZpY2llX3BhdGlvICU+JSByZWxldmVsLnZhcmlhYmxlKCAncHJlY2lvX2JhcnJpbycsICdtZWRpbycgKSkNCnN1bW1hcnkobW9kZWwpDQpgYGANCg0KU2kgY29tcGFyYW1vcyBsb3MgZG9zIG1vZGVsb3MsIHBvZGVtb3MgYXByZWNpYXIgcXVlIGVzdG9zIHNvbiBpZGVudGljb3MgZW4gY3VhbnRvIGEgcHJlZGljY2lvbi4gRWwgZWZlY3RvIGZpbmFsIGRlIGFwbGljYXIgdW5hIHRyYW5zZm9ybWFjaW9uIGxpbmVhbCBlbnRyZSB2YXJpYWJsZXMgdHV2byBjb21vIGVmZWN0byBlbCBhanVzdGUgZGUgbG9zIGNvZWZpY2llbnRlcywgcGVybyBsYSBwcmVkaWN0aXZpbGlkYWQgc2lndWUgc2llbmRvIGV4YWN0YW1lbnRlIGxhIG1pc21hOiBtaXNtbyBSXjJeIGFqdXN0YWRvLCBtaXNtb3MgcmVzaWR1b3MgeSBtaXNtb3MgY29lZmljaWVudGVzIGNvbiBsYSBleGNwZWNpb24gZGVzY3JpdGEgYWJham8uDQoNCioqTW9kZWxvIG9yaWdpbmFsKioNCg0KYHN1cmZhY2VfdG90YWwgICAgICAgICAgICAgICAgIDg1Ny45NyAgICAgIDI0LjYzICAgMzQuODMgICA8MmUtMTYgKioqYA0KYHN1cmZhY2VfY292ZXJlZCAgICAgICAgICAgICAgMTYyOC4yMSAgICAgIDI5Ljk4ICAgNTQuMzEgICA8MmUtMTYgKioqYA0KDQoqKk1vZGVsbyBjb24gdHJhbnNmb3JtYWNpb24gbGluZWFsKioNCg0KYHN1cmZhY2VfY292ZXJlZCAgICAgICAgICAgICAgMjQ4Ni4xOCAgICAgIDE1LjgzICAxNTcuMDMgICA8MmUtMTYgKioqYA0KYHN1cmZhY2VfcGF0aW8gICAgICAgICAgICAgICAgIDg1Ny45NyAgICAgIDI0LjYzICAgMzQuODMgICA8MmUtMTYgKioqYA0KDQpkb25kZSBwb2RlbW9zIGFwcmVjaWFyIHF1ZSBlbCBjb21wb25lbnRlIGRlIHByZWNpbyBnZW5lcmFkbyBwb3IgZWwgcGFyIGRlIHZhcmlhYmxlcyBhcnJpYmEgZXMgZWwgbWlzbW8gcXVlIGVsIGRlIGFiYWpvLCBzaSBjb25zaWRlcmFtb3MgcXVlIGBzdXJmYWNlX3BhdGlvYCA9IGBzdXJmYWNlX3RvdGFsYCAtIGBzdXJmYWNlX2NvdmVyZWRgDQoNCg0KDQojIDMuIEV2YWx1YWNpb24gZGVsIG1vZGVsbw0KDQojIyMgYS4gUmVzaWR1b3MgZGVsIG1vZGVsbw0KDQpHcmFmaWNhbW9zIGxvcyByZXNpZHVvcyBkZWwgbW9kZWxvIGFudGVyaW9yOg0KDQoNCmBgYHtyfQ0KZ2dwbG90KG1vZGVsLCBhZXMoLmZpdHRlZCwgLnJlc2lkKSkgKyANCiAgZ2VvbV9wb2ludChjb2xvciA9ICJsaWdodGNvcmFsIixhbHBoYT0wLjIpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGxhYnMoeD0icHJlY2lvIGVzdGltYWRvIiwgeT0iUmVzaWR1b3MiLCB0aXRsZT0nUmVzaWR1b3MgdnMgcHJlY2lvIGVzdGltYWRvJykNCg0KZ2dwbG90KG1vZGVsLCBhZXMoc2FtcGxlPS5yZXNpZCkpICsgDQogIHN0YXRfcXEoY29sb3IgPSAibGlnaHRjb3JhbCIpICAgKw0KICBzdGF0X3FxX2xpbmUoKQ0KYGBgDQoNCg0KQWwgYW5hbGl6YXIgbG9zIHJlc2lkdW9zICh5IGVuIGVzcGVjaWFsIGxhIHRlbmRlbmNpYSBkZSBlc3Rvcykgc2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGVsIG1vZGVsbyBsaW5lYWwgbm8gbG9ncmEgYWp1c3RhciBkZSBmb3JtYSBxdWUgcHVlZGEgZXhwbGljYXIgbGEgdGVuZGVuY2lhIG9ic2VydmFkYS4gVGFtYmVpbiBzZSBwdWVkZSBvYnNlcnZhciBxdWUgZWwgbW9kZWxvIG5vIGVzIGhvbW9jZWRhc3RpY28sIGFsIGF1bWVudGFyIGxhIHZhcmlhYmlsaWRhZCBtaWVudHJhcyBtYXMgYWx0b3Mgc29uIGxvcyBwcmVjaW9zIGVzdGltYWRvcy4NClRhbWJpZW4gcG9kZW1vcyBvYnNlcnZhciBlbiBlbCBxLXEgcGxvdCBkZSBsb3MgcmVzaWR1b3MgcXVlIGN1YW5kbyBlc3RvcyBzb24gZ3JhbmRlcyBzZSBkZXN2aWFuIGRlIGxvIGVzcGVyYWRvIHBhcmEgdW5hIGRpc3RyaWJ1Y2lvbiBub3JtYWwNCg0KDQojIyMgYi4gTW9kZWxvIExvZw0KDQoNCmBgYHtyfQ0KbW9kZWwgPC0gbG0oIGxvZyhwcmljZSl+bG9nKHJvb21zKSsNCiAgICAgICAgICAgICAgICAgIGxvZyhiYXRocm9vbXMpKw0KICAgICAgICAgICAgICAgICAgbG9nKHN1cmZhY2VfY292ZXJlZCkrDQogICAgICAgICAgICAgICAgICBzdXJmYWNlX3BhdGlvICsNCiAgICAgICAgICAgICAgICAgIHByb3BlcnR5X3R5cGUgKw0KICAgICAgICAgICAgICAgICAgcHJlY2lvX2JhcnJpbw0KICAgICAgICAgICAgLCBkYXRhPWFyX3Byb3BlcnRpZXNfc3VwZXJmaWNpZV9wYXRpbykNCnN1bW1hcnkobW9kZWwpDQoNCmdncGxvdChtb2RlbCwgYWVzKC5maXR0ZWQsIC5yZXNpZCkpICsgDQogIGdlb21fcG9pbnQoY29sb3IgPSAibGlnaHRjb3JhbCIsYWxwaGE9MC4yKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBsYWJzKHg9ImxvZyhwcmVjaW8gZXN0aW1hZG8pIiwgeT0iUmVzaWR1b3MiLCB0aXRsZT0nUmVzaWR1b3MgdnMgcHJlY2lvIGVzdGltYWRvJykNCg0KZ2dwbG90KG1vZGVsLCBhZXMoc2FtcGxlPS5yZXNpZCkpICsgDQogIHN0YXRfcXEoY29sb3IgPSAibGlnaHRjb3JhbCIpICAgKw0KICBzdGF0X3FxX2xpbmUoKQ0KDQpgYGANCg0KDQpBbCBhbmFsaXphciBsb3MgcmVzdWx0YWRvcyBkZWwgbW9kZWxvIGNvbnN0cnVpZG8gYSBwYXJ0aXIgZGVsIGxvZ2FyaXRtbyBkZSBjaWVydGFzIHZhcmlhYmxlcywgcG9kZW1vcyBvYnNlcnZhciBjb21vIGVzdGUgYWp1c3RhIG1lam9yIHF1ZSBsb3MgY2Fzb3MgYW50ZXJpb3JlcywgcHVkaWVuZG8gZXhwbGljYXIgfjgyJSBkZSBsYSB2YXJpYWJpbGlkYWQgZGVsIG1pc21vLCB5IGFsIG1pc21vIHRpZW1wbyBtZWpvcmFuZG8gZWwgZ3JhZmljbyBkZSByZXNpZHVvczogZW4gZXN0ZSBudWV2byBtb2RlbG8gcGFyZWNlcmlhIGhhYmVyIHVuYSBtZWpvciBob21vY2VkYXN0aWNpZGFkIGVuIHVuIHJhbmdvIG1hcyBncmFuZGUsIHkgbGEgZGlzdHJpYnVjaW9uIGRlIGxvcyByZXNpZHVvcyBhanVzdGFuIG1lam9yIGEgbGEgZGlzdHJpYnVjaW9uIG5vcm1hbC4NCg0KDQojIDQuIE1vZGVsb3MgZGlzY3JpbWluYWRvcyBwb3IgdGlwbyBkZSBwcm9waWVkYWQNCg0KYGBge3J9DQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShnZW5lcmljcykpDQoNCiNhbmFsaXphbW9zIGxhcyBwb2JsYWNpb25lcw0KYXJfcHJvcGVydGllc19zdXBlcmZpY2llX3BhdGlvICU+JSBjb3VudChwcm9wZXJ0eV90eXBlKSAlPiUgbXV0YXRlKHBvcmNlbnRhZ2U9bi9ucm93KGFyX3Byb3BlcnRpZXNfc3VwZXJmaWNpZV9wYXRpbykpDQoNCiNDYWxjdWxhbW9zIGVsIG1vZGVsbyBsaW5lYWwgcGFyYSBsYXMgcG9ibGFjaW9uZXMgZGlzY3JpbWluYWRhcw0KbW9kZWxvc19wb3JfdGlwbyA8LSBhcl9wcm9wZXJ0aWVzX3N1cGVyZmljaWVfcGF0aW8gJT4lDQogICMgYWdydXBhbW9zIHkgYW5pZGFtb3MNCiAgZ3JvdXBfYnkocHJvcGVydHlfdHlwZSkgJT4lIG5lc3QoKSAlPiUNCg0KICAjIGNhbGN1bGFtb3MgZWwgbW9kZWxvIGxpbmVhbCBwb3IgdGlwbyBkZSBwcm9waWVkYWQsIHkgZGVzY2FydGFtb3MgbG9zIGRhdG9zIG9yaWdpbmFsZXMNCiAgbXV0YXRlKGxtPW1hcChkYXRhLGZ1bmN0aW9uKGRmKSBsbSggcHJpY2V+LiwgZGF0YT1kZikpKSAlPiUgc2VsZWN0KCAtJ2RhdGEnICkgJT4lDQoNCiAgIyBFeHBsYXlhbW9zIGxhIGNvbHVtbmEgZGUgcmVzdW1lbiBkZWwgbW9kZWxvDQogIG11dGF0ZShtb2RlbGRhdGE9bWFwKGxtLCBmdW5jdGlvbihtb2QpIGdsYW5jZShtb2QpKSkgJT4lDQoNCiAgIyBleHBsYXlhbW9zIGxhIGNvbHVtbmEgZGUgZGV0YWxsZXMgZGVsIG1vZGVsbw0KICBtdXRhdGUodGlkeV9sbT1tYXAobG0sIGZ1bmN0aW9uKGxtKSB0aWR5KGxtKSkpICU+JQ0KICANCiAgIyBkZXNjYXJ0YW1vcyBsYSBjb2x1bW5hIGRlbW9kZWxvDQogIHNlbGVjdCgtJ2xtJykNCg0KIyBJbXByaW1pbW9zIGxvcyBkZXRhbGxlcyBkZWwgbW9kZWxvIGVuIGdlbmVyYWwNCm1vZGVsb3NfcG9yX3RpcG8gJT4lIHVubmVzdChtb2RlbGRhdGEpICU+JSBzZWxlY3QoJ3Byb3BlcnR5X3R5cGUnLCdhZGouci5zcXVhcmVkJywgJ2RmLnJlc2lkdWFsJywgJ3NpZ21hJykNCg0KIyBJbXByaW1pbW9zIGxvcyBkZXRhbGxlcyBkZSBsb3MgY29lZmljaWVudGVzDQptb2RlbG9zX3Bvcl90aXBvICU+JSB1bm5lc3QodGlkeV9sbSkNCmBgYA0KDQoNCkN1YW5kbyBjb21wYXJhbW9zIGxvcyBkaXN0aW50b3MgbW9kZWxvcyBnZW5lcmFkb3MgY3VhbmRvIGRpc2NyaW1pbmFtb3MgcG9yIHRpcG8gZGUgcHJvcGllZGFkLCBjb21wYXJhbmRvIGVsIFJeMl4gYWp1c3RhZG8gZGUgY2FkYSB1bm8sIG5vdGFtb3MgcXVlIGVzdG9zIGRpZmllcmVuIGNvbnNpZGVyYWJsZW1lbnRlIGVudHJlIHPDrS4gT2JzZXJ2YW1vcyBxdWUgcGFyYSBkZXBhcnRhbWVudG9zLCBlbCBtb2RlbG8gZXhwbGljYSBlbCA3NyUgKHJlY29yZGFtb3MgcXVlIGN1YW5kbyBubyBkaXNjcmltaW5hYmFtb3MgcG9yIHRpcG8gZGUgcHJvcGllZGFkIGVsIG1vZGVsbyBleHBsaWNhYmEgZWwgNzQlKTsgcGVybyB0YW1iaWVuIG5vdGFtb3MgY29tbyBwYXJhIGNhc2EgZXN0ZSB2YWxvciBlcyBkZSB0YW4gc29sbyA2MCUsIHkgZGUgNjklIHBhcmEgUEhzLiBEZSBlc3RvcyBkYXRvcyBwb2RlbW9zIGVzdGltYXIgcXVlIGxvcyBkZXBhcnRhbWVudG9zIHNvbiBtYXMgKnByZWRlY2libGVzKiBwYXJhIHVuIG1vZGVsbyBsaW5lYWwsIHkgYWwgcmVwcmVzZW50YXIgbGEgZ3JhbiBtYXlvcmlhIGRlIGxhIHBvYmxhY2lvbiB0b3RhbCwgbGEgcHJlZGljdGliaWxpZGFkIGRlIGVzdGEgcG9ibGFjaW9uIGF1bWVudGFiYSBsYSBwcmVkaWN0aWJpbGlkYWQgZGVsIG1vZGVsbyBjb21iaW5hZG8gKHJlc3BlY3RvIGEgcHJlZGljdGliaWxpZGFkIGRlIENhc2FzIHkgUEhzIHBvciBzZXBhcmFkbykuDQoNCkF1biBhc2ksIHBvZGVtb3MgZXh0cmFlciBvdHJhcyBjb25jbHVzaW9uZXMgYWwgc2VwYXJhciBsb3MgbW9kZWxvcywgY29tbyBwb3IgZWosIGxhIHZhcmlhY2lvbiBlc3BlcmFkYSBkZSBwcmVjaW8gcG9yIG1ldHJvIGN1YWRyYWRvIGN1YmllcnRvIGVzIGRpc3RpbnRhIGVuIGxvcyBtb2RlbG9zLCB5IGNvbiB1bmEgZ3JhbiBzaWduaWZpY2F0aXZpZGFkIGVuIHRvZG9zIGxvcyBjYXNvcyAoU2llbmRvIGxvcyBkZXBhcnRhbWVudG9zIGxvcyBtYXMgY2Fyb3MgcG9yIG1ldHJvIGN1YWRyYWRvLCBzZWd1aWRvIHBvciBsb3MgUEhzIHkgZmluYWxtZW50ZSBwb3IgbGFzIGNhc2FzKSwgZGF0byBxdWUgZW4gZWwgbW9kZWxvIGNvbWJpbmFkbyBzb2xvIGVyYSByZXByZXNlbnRhZG8gY29tbyB1bmEgZGlmZXJlbmNpYSAiY29uc3RhbnRlIiBxdWUgZGVwZW5kw61hIHNvbGFtZW50ZSBkZWwgdGlwbyBkZSBwcm9waWVkYWQuDQo=