Capítulo 7 Machine Learning (en una aplicación urbana)

El así llamado machine learning consiste el empleo de aprendizaje estadístico automatizado para identificar patrones en grandes volúmenes de datos. El machine learning (de aquí en más ML) es utilizado en infinidad de campos debido a su creciente facilidad de uso y capacidad -en ciertos contextos- para predecir resultados con alta precisión.

A continuación veremos como se aplica ML para predecir el valor de venta de los departamentos en CABA a partir de un dataset publicado en el portal de datos abiertos BA Data que contiene el relevamiento de departamentos en venta que realizó el GCBA en 2016.

El objetivo del ejercicio es predecir el valor del metro cuadrado (USD x m2) de los departamentos en función de atributos como la cantidad de m2 descubiertos, la cantidad de ambientes, el barrio donde se ubican, la antiguedad de la construcción, etc.

Allá vamos.

7.1 Paso 0: Cargar paquetes

Además de las funciones de R “base”, vamos a usar las del paquete tidyverse para procesar y visualizar nuestros datos, las de sf para hacer algunos análisis espaciales y las de randomForest, para aplicar el algoritmo de ML homónimo, que es relativamente simple y a la vez efectivo.

7.2 Paso 1: Cargar los datos

Descargamos de BA Data el dataset del relevamiento de departamentos en venta del siguiente modo:

7.3 Paso 2: Examinar los datos

Echamos un vistazo a los nombres de las columnas y las primeras filas del dataset:

##  [1] "CALLE"                   "NUMERO"                 
##  [3] "ID_ZONAPRO"              "OPERACION"              
##  [5] "TIPO"                    "M2"                     
##  [7] "M2CUB"                   "PRECIOTEXT"             
##  [9] "PRECIOARS"               "PRECIOARSM"             
## [11] "DOLARES"                 "U_S_M2"                 
## [13] "AMBIENTES"               "ANTIGUEDAD"             
## [15] "BAÑOS"                   "DIRECCION"              
## [17] "LOCATION"                "PUBLICADO"              
## [19] "PROCESADO"               "URL"                    
## [21] "REVISION"                "NOTA"                   
## [23] "DIRECCION_NORMALIZADA"   "BARRIO"                 
## [25] "COMUNA"                  "CODIGO_POSTAL"          
## [27] "CODIGO_POSTAL_ARGENTINO" "LATITUD"                
## [29] "LONGITUD"
##                CALLE NUMERO ID_ZONAPRO OPERACION TIPO  M2 M2CUB  PRECIOTEXT
## 1          GUATEMALA   5574   42408691       VTA  DTO  57    50 U$S 170.150
## 2             ZAPATA    300   42408710       VTA  DTO  46    46 U$S 118.650
## 3             ZAPATA    300   42518390       VTA  DTO  61    56 U$S 181.470
## 4             ZAPATA    300   42518402       VTA  DTO 140    76 U$S 320.000
## 5 JUSTO, JUAN B. AV.   2300   42621693       VTA  DTO  39    33  U$S 82.116
## 6 JUSTO, JUAN B. AV.   2300   42621702       VTA  DTO  39    34  U$S 81.921
##   PRECIOARS PRECIOARSM DOLARES U_S_M2 AMBIENTES ANTIGUEDAD BAÑOS
## 1   2977625      59553  170150   3403         2       2016     1
## 2   2076375      45139  118650   2579         0       2016     1
## 3   3175725      56709  181470   3241         2       2016     1
## 4   5600000      73684  320000   4211         2       2016     1
## 5   1437030      43546   82116   2488         0       2016     1
## 6   1433617      42165   81921   2409         0       2016     1
##                      DIRECCION
## 1               GUATEMALA 5574
## 2                   ZAPATA 300
## 3                   ZAPATA 300
## 4                   ZAPATA 300
## 5 JUSTO  JUAN B. AVDA. AL 2300
## 6 JUSTO  JUAN B. AVDA. AL 2300
##                                                      LOCATION  PUBLICADO
## 1                  GUATEMALA 5574  PALERMO HOLLYWOOD  PALERMO 27/11/2016
## 2                       ZAPATA 300  BELGRANO  CAPITAL FEDERAL 27/11/2016
## 3                       ZAPATA 300  BELGRANO  CAPITAL FEDERAL 27/11/2016
## 4                       ZAPATA 300  BELGRANO  CAPITAL FEDERAL 27/11/2016
## 5 JUSTO  JUAN B. AVDA. AL 2300  VILLA CRESPO  CAPITAL FEDERAL  27/7/2016
## 6 JUSTO  JUAN B. AVDA. AL 2300  VILLA CRESPO  CAPITAL FEDERAL  27/7/2016
##    PROCESADO                                                    URL REVISION
## 1 12/10/2017 HTTP://WWW.ZONAPROP.COM.AR/PROPIEDADES/A-42408691.HTML       NA
## 2 12/10/2017 HTTP://WWW.ZONAPROP.COM.AR/PROPIEDADES/A-42408710.HTML       NA
## 3 11/10/2017 HTTP://WWW.ZONAPROP.COM.AR/PROPIEDADES/A-42518390.HTML       NA
## 4 11/10/2017 HTTP://WWW.ZONAPROP.COM.AR/PROPIEDADES/A-42518402.HTML       NA
## 5 10/10/2017 HTTP://WWW.ZONAPROP.COM.AR/PROPIEDADES/A-42621693.HTML       NA
## 6 10/10/2017 HTTP://WWW.ZONAPROP.COM.AR/PROPIEDADES/A-42621702.HTML       NA
##   NOTA   DIRECCION_NORMALIZADA       BARRIO    COMUNA CODIGO_POSTAL
## 1   NA          GUATEMALA 5574      PALERMO COMUNA 14          1425
## 2   NA              ZAPATA 300      PALERMO COMUNA 14          1426
## 3   NA              ZAPATA 300      PALERMO COMUNA 14          1426
## 4   NA              ZAPATA 300      PALERMO COMUNA 14          1426
## 5   NA JUSTO, JUAN B. AV. 2300 VILLA CRESPO COMUNA 15          1414
## 6   NA JUSTO, JUAN B. AV. 2300 VILLA CRESPO COMUNA 15          1414
##   CODIGO_POSTAL_ARGENTINO   LATITUD  LONGITUD
## 1                C1425BVH -34.58058 -58.43176
## 2                C1426AED -34.57387 -58.44061
## 3                C1426AED -34.57387 -58.44061
## 4                C1426AED -34.57387 -58.44061
## 5                C1414CWY -34.59298 -58.44154
## 6                C1414CWY -34.59298 -58.44154

El dataset contiene 29 columnas (mucha información!). Por lo tanto, debemos revisar las variables y hacer una preselección para incluir solo aquellas que consideremos relevantes para nuestro modelo:

  • La variable a predecir (dependiente) será el valor del m2 de los departamentos (U_S_M2) en CABA.

  • Las variables predictoras (independientes) serán: cantidad de ambientes (AMBIENTES), años de antiguedad de la construcción (ANTIGUEDAD), cantidad de baños (BAÑOS), superficie total (M2), superficie cubierta (M2CUB), barrio al que pertenece (BARRIO) y coordenadas (LATITUD, LONGITUD).

Ahora si, seleccionemos únicamente las variables que queremos incluir:

Y veamos un resumen del contenido:

##        M2             M2CUB            U_S_M2        AMBIENTES     
##  Min.   : 15.00   Min.   :  0.00   Min.   :    0   Min.   : 0.000  
##  1st Qu.: 41.00   1st Qu.: 37.00   1st Qu.: 2242   1st Qu.: 2.000  
##  Median : 54.00   Median : 47.00   Median : 2651   Median : 2.000  
##  Mean   : 70.38   Mean   : 61.12   Mean   : 2804   Mean   : 2.456  
##  3rd Qu.: 80.00   3rd Qu.: 70.00   3rd Qu.: 3131   3rd Qu.: 3.000  
##  Max.   :730.00   Max.   :625.00   Max.   :12500   Max.   :10.000  
##                                                                    
##    ANTIGUEDAD         BAÑOS                BARRIO        LATITUD      
##  Min.   :   0.0   Min.   :0.00                :1382   Min.   :-34.68  
##  1st Qu.:   1.0   1st Qu.:1.00   CABALLITO    : 643   1st Qu.:-34.62  
##  Median :  30.0   Median :1.00   PALERMO      : 629   Median :-34.60  
##  Mean   : 710.6   Mean   :1.25   SAN CRISTOBAL: 563   Mean   :-34.60  
##  3rd Qu.:2016.0   3rd Qu.:2.00   VILLA CRESPO : 532   3rd Qu.:-34.58  
##  Max.   :2016.0   Max.   :6.00   BELGRANO     : 473   Max.   :-34.54  
##                                  (Other)      :3342   NA's   :1382    
##     LONGITUD     
##  Min.   :-58.53  
##  1st Qu.:-58.45  
##  Median :-58.43  
##  Mean   :-58.43  
##  3rd Qu.:-58.41  
##  Max.   :-58.35  
##  NA's   :1382

El resumen nos muestra que las superficies totales de los departamentos relevados varían entre 15 y 730m2, siendo 70m2 la media. También podemos ver que las variables M2CUB, U_S_M2, AMBIENTES, ANTIGUEDAD y BAÑOS tienen valores mínimos de 0, lo cual resulta bastante extraño.

Pero no importa, no nos preocupemos porque ya aprendimos varias formas de limpiar datos, así que manos a la obra.

7.4 Paso 3: Limpiar los datos

7.4.1 Imputar valores faltantes

Es habitual que los algoritmos empleados para ML no acepten datos faltantes. Es por eso que la limpieza básica de un dataset casi siempre incluye la imputación de datos no disponibles, evitando descartar por incompletas filas que contienen información valiosa en los campos que si están disponibles.

Hasta acá pudimos observar varias inconsistencias en los datos cargados, como por ejemplo:

  • La variable M2CUB tiene valor 0 en algunos registros. Suponiendo que hubo un error en la carga de los datos, cuando M2CUB<15 vamos a imputar el valor de M2, asumiendo que esa propiedad no tiene m2 descubiertos.
  • Hay casos donde M2CUB>M2. Acá le imputaremos el valor del M2CUB al M2.
  • En la variable ANTIGUEDAD aparecen algunos registros con el valor 2016. Suponiendo que esas propiedades se construyeron en ese año, se imputará una antiguedad 1 ya que fue construida en el mismo año del relevamiento. Del mismo modo, y bajo el mismo supuesto, todos los registos que tengan ANTIGUEDAD=0, serán reemplazados por ANTIGUEDAD=1.
  • Las variables AMBIENTES y BAÑOS tiene 0 en algunos casos. Imputaremos estos datos entendiendo que cuando AMBIENTES=0, es un monoambiente, y que cuando BAÑOS=0, es porque tienen 1 solo.
  • Hay 1.382 valores faltantes en las columnas LONGITUD y LATITUD, y a su vez estos registros tampoco tienen comuna o barrio asignado, por lo tanto como nos va a resultar imposible ubicarlos en el espacio, estos sí debemos eliminarlos.
  • Por último, se puede ver que la variable a predecir (U_S_M2) varía entre 0 y 12500. Claramente ninguna propiedad publicada en CABA puede tener U_S_M2=0 o menos de 500 así que estos registros, que son pocos casos, también se eliminarán.

Para llevar a cabo todos los ajustes mencionados, utilizaremos las ya conocidadas mutate y filter:

Listo, ya tenemos preparadas las variables para nuestro modelo, pero aún estamos a tiempo de generar algunas nuevas que consideremos que, por tener capacidad predictiva sobre el valor del m2, lo mejorarían.

Por ejemplo, probemos sumar una nueva variable al modelo donde se calculen los metros descubiertos (M2DESC) de cada propiedad, ya que, es muy probable que tener alguna expansión (balcón o terraza) le de un valor agregado al departamento.

Ahora sí, volvamos a ver el resumen:

##        M2             M2CUB            U_S_M2        AMBIENTES     
##  Min.   : 15.00   Min.   : 15.00   Min.   :  608   Min.   : 1.000  
##  1st Qu.: 41.00   1st Qu.: 38.00   1st Qu.: 2230   1st Qu.: 2.000  
##  Median : 53.00   Median : 47.00   Median : 2631   Median : 2.000  
##  Mean   : 68.44   Mean   : 60.28   Mean   : 2768   Mean   : 2.518  
##  3rd Qu.: 78.00   3rd Qu.: 69.00   3rd Qu.: 3107   3rd Qu.: 3.000  
##  Max.   :730.00   Max.   :625.00   Max.   :10555   Max.   :10.000  
##                                                                    
##    ANTIGUEDAD          BAÑOS                 BARRIO        LATITUD      
##  Min.   :  1.000   Min.   :1.000   CABALLITO    : 643   Min.   :-34.68  
##  1st Qu.:  1.000   1st Qu.:1.000   PALERMO      : 629   1st Qu.:-34.62  
##  Median :  1.000   Median :1.000   SAN CRISTOBAL: 563   Median :-34.60  
##  Mean   :  9.535   Mean   :1.331   VILLA CRESPO : 532   Mean   :-34.60  
##  3rd Qu.: 10.000   3rd Qu.:2.000   BELGRANO     : 472   3rd Qu.:-34.58  
##  Max.   :110.000   Max.   :6.000   RECOLETA     : 406   Max.   :-34.54  
##                                    (Other)      :2933                   
##     LONGITUD          M2DESC       
##  Min.   :-58.53   Min.   :  0.000  
##  1st Qu.:-58.45   1st Qu.:  0.000  
##  Median :-58.43   Median :  4.000  
##  Mean   :-58.43   Mean   :  8.163  
##  3rd Qu.:-58.41   3rd Qu.:  8.000  
##  Max.   :-58.35   Max.   :282.000  
## 

Todo parece funcionar bien: Nos hemos librado de los NA y las inconsistencias que tenían los datos.

Pero antes de seguir con nuestro modelo, espiemos la distribución de algunas variables, como por ejemplo el valor del m2:

La superficie total:

La superficie descubierta:

La antigüedad de las viviendas:

Y el Barrio que, como se trata de una variable categórica en lugar de continua, lo veremos con un gráfico de barras en lugar de un histograma:

Todavía podemos agregar una variable más: Probemos con la distancia de los departamentos a las estaciones de subte, ya que la cercanía a estas es muy probable que impacte en el valor del m2. Acá utilizaremos uno de los geoprocesos que aprendimos algunos capítulos atrás: st_distance

Carguemos las estaciones:

## Reading layer `subte_estaciones' from data source `http://bitsandbricks.github.io/data/subte_estaciones.geojson' using driver `GeoJSON'
## Simple feature collection with 86 features and 3 fields
## geometry type:  POINT
## dimension:      XY
## bbox:           xmin: -58.48639 ymin: -34.64331 xmax: -58.36993 ymax: -34.55564
## epsg (SRID):    4326
## proj4string:    +proj=longlat +datum=WGS84 +no_defs

Transformemos dptos_2016 a un dataset espacial para poder medir distancias:

Y ahora calculemos la distancia (en metros) entre cada departamento en venta y la estación de subte más cercana:

Veamos un resumen de los resultados:

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1.35  282.77  477.19  706.81  762.33 5658.53

Y saquemos conclusiones: El departamento ubicado a menor distancia de alguna estación de subte es a 1.35 metros y el que está a mayor distancia es a 5658 metros (56 cuadras). Sin embargo, en promedio, las propiedades se ubican a 706 metros (7 cuadras) de alguna estación.

En el paso anterior, transformamos nuestro dataset tradicional en un dataset espacial para poder medir distancias, pero como queremos utilizar los datos de LATITUD y LONGITUD para el modelo, debemos volver a separar las coordenadas y transformarlo en dataframe tradicional:

7.4.2 Codificar variables categóricas

Rara vez es posible utilizar columnas categóricas en modelos estadísticos, pero por suerte podemos recurrir a la alternativa de reemplazar una columna de datos categóricos por una serie de variables binarias, o “dummy”. En nuestro dataset seleccionamos solamente una variable categórica: BARRIO.

Entonces, en lugar de…

dpto BARRIO
A PALERMO
B BELGRANO
C SAN TELMO

… deberíamos tener algo parecido a:

caso PALERMO BELGRANO SAN TELMO
A 1 0 0
B 0 1 0
C 0 0 1

Para evitar futuros problemas por tener espacios en los encabezados de las nuevas columnas, comencemos reemplazando los " " en los nombres de barrios por un "_". Por ejemplo, en vez de decir SAN TELMO, que pase a decir SAN_TELMO.

Como buen lenguaje creado por y para practicantes del análisis estadístico, R trae una función específica para realizar esta tarea: model.matrix() que se usa de la siguiente forma:

y el resultado es, ni más ni menos, una matriz de variables binarias que representan las categorías originales:

##   BARRIOAGRONOMIA BARRIOALMAGRO BARRIOBALVANERA BARRIOBARRACAS BARRIOBELGRANO
## 1               0             0               0              0              0
## 2               0             0               0              0              0
## 3               0             0               0              0              0
## 4               0             0               0              0              0
## 5               0             0               0              0              0
## 6               0             0               0              0              0
##   BARRIOBOCA BARRIOBOEDO BARRIOCABALLITO BARRIOCHACARITA BARRIOCOGHLAN
## 1          0           0               0               0             0
## 2          0           0               0               0             0
## 3          0           0               0               0             0
## 4          0           0               0               0             0
## 5          0           0               0               0             0
## 6          0           0               0               0             0
##   BARRIOCOLEGIALES BARRIOCONSTITUCION BARRIOFLORES BARRIOFLORESTA BARRIOLINIERS
## 1                0                  0            0              0             0
## 2                0                  0            0              0             0
## 3                0                  0            0              0             0
## 4                0                  0            0              0             0
## 5                0                  0            0              0             0
## 6                0                  0            0              0             0
##   BARRIOMATADEROS BARRIOMONSERRAT BARRIOMONTE_CASTRO BARRIONUEVA_POMPEYA
## 1               0               0                  0                   0
## 2               0               0                  0                   0
## 3               0               0                  0                   0
## 4               0               0                  0                   0
## 5               0               0                  0                   0
## 6               0               0                  0                   0
##   BARRIONUÑEZ BARRIOPALERMO BARRIOPARQUE_AVELLANEDA BARRIOPARQUE_CHACABUCO
## 1           0             1                       0                      0
## 2           0             1                       0                      0
## 3           0             1                       0                      0
## 4           0             1                       0                      0
## 5           0             0                       0                      0
## 6           0             0                       0                      0
##   BARRIOPARQUE_CHAS BARRIOPARQUE_PATRICIOS BARRIOPATERNAL BARRIOPUERTO_MADERO
## 1                 0                      0              0                   0
## 2                 0                      0              0                   0
## 3                 0                      0              0                   0
## 4                 0                      0              0                   0
## 5                 0                      0              0                   0
## 6                 0                      0              0                   0
##   BARRIORECOLETA BARRIORETIRO BARRIOSAAVEDRA BARRIOSAN_CRISTOBAL
## 1              0            0              0                   0
## 2              0            0              0                   0
## 3              0            0              0                   0
## 4              0            0              0                   0
## 5              0            0              0                   0
## 6              0            0              0                   0
##   BARRIOSAN_NICOLAS BARRIOSAN_TELMO BARRIOVELEZ_SARSFIELD BARRIOVERSALLES
## 1                 0               0                     0               0
## 2                 0               0                     0               0
## 3                 0               0                     0               0
## 4                 0               0                     0               0
## 5                 0               0                     0               0
## 6                 0               0                     0               0
##   BARRIOVILLA_CRESPO BARRIOVILLA_DEL_PARQUE BARRIOVILLA_DEVOTO
## 1                  0                      0                  0
## 2                  0                      0                  0
## 3                  0                      0                  0
## 4                  0                      0                  0
## 5                  1                      0                  0
## 6                  1                      0                  0
##   BARRIOVILLA_GRAL._MITRE BARRIOVILLA_LUGANO BARRIOVILLA_LURO
## 1                       0                  0                0
## 2                       0                  0                0
## 3                       0                  0                0
## 4                       0                  0                0
## 5                       0                  0                0
## 6                       0                  0                0
##   BARRIOVILLA_ORTUZAR BARRIOVILLA_PUEYRREDON BARRIOVILLA_REAL
## 1                   0                      0                0
## 2                   0                      0                0
## 3                   0                      0                0
## 4                   0                      0                0
## 5                   0                      0                0
## 6                   0                      0                0
##   BARRIOVILLA_SANTA_RITA BARRIOVILLA_SOLDATI BARRIOVILLA_URQUIZA
## 1                      0                   0                   0
## 2                      0                   0                   0
## 3                      0                   0                   0
## 4                      0                   0                   0
## 5                      0                   0                   0
## 6                      0                   0                   0

En breves agregaremos la matriz a nuestro dataframe de departamentos, pero antes terminemos con algunos ajustes que nos quedaron pendientes.

7.4.3 Unificar la escala de las variables numéricas

Este paso siempre es necesario cuando estamos trabajando con variables que utilizan distintas unidades de medida. Aquí tenemos superficies, ambientes, años de antigüedad… de todo. Muchos algoritmos asumen que todas las variables tienen escalas comparables, lo cual genera problemas con las que alcanzan los valores más altos (como m2, que llega a 730) versus las que tienen rangos mucho menores (como cantidad de baños, que llega a 6). Si las dejásemos así, varias de las técnicas habituales del ML adjudicarían mucho más peso a las variables con números grandes, “despreciando” a las que por su naturaleza se mueven en rango más reducidos.

En todo caso, no importa lo disímiles que sean las unidades de medida, la solución es simple: convertimos todas las variables a la famosa “distribución Z”, o función de estandarización, que convierte variables a una escala sin unidad de medida, que expresa cada valor como la cantidad de desvíos estándar que lo alejan de la media. Expresar todas las variables numéricas en forma de “z scores”, o “valores z”, las hace directamente comparables entre sí.

En R disponemos de la función scale(), que obtiene los z-scores. Tomaremos entonces nuestro dataframe y usaremos mutate_all() para aplicar una función a todas las columnas restantes de un tirón. Eso si, quitando antes ciertas variables: las variables categóricas (que no tiene sentido pasar a z-scores porque no son variables numéricas), y la variable que estamos intentando predecir, ya que su escala no afecta los modelos y podemos dejarla en su formato original fácil de interpretar.

##         M2.V1             M2CUB.V1           U_S_M2         AMBIENTES.V1    
##  Min.   :-1.139657   Min.   :-1.128859   Min.   :  608   Min.   :-1.648294  
##  1st Qu.:-0.585166   1st Qu.:-0.555401   1st Qu.: 2230   1st Qu.:-0.562206  
##  Median :-0.329247   Median :-0.331005   Median : 2631   Median :-0.562206  
##  Mean   : 0.000000   Mean   : 0.000000   Mean   : 2768   Mean   : 0.000000  
##  3rd Qu.: 0.203918   3rd Qu.: 0.217519   3rd Qu.: 3107   3rd Qu.: 0.523882  
##  Max.   :14.108855   Max.   :14.080225   Max.   :10555   Max.   : 8.126499  
##     ANTIGUEDAD.V1         BAÑOS.V1            M2DESC.V1     
##  Min.   :-0.538418   Min.   :-0.541044   Min.   :-0.520562  
##  1st Qu.:-0.538418   1st Qu.:-0.541044   1st Qu.:-0.520562  
##  Median :-0.538418   Median :-0.541044   Median :-0.265463  
##  Mean   : 0.000000   Mean   : 0.000000   Mean   : 0.000000  
##  3rd Qu.: 0.029315   3rd Qu.: 1.093465   3rd Qu.:-0.010364  
##  Max.   : 6.337456   Max.   : 7.631505   Max.   :17.463925  
##     DIST_SUBTE.V1         LATITUD.V1          LONGITUD.V1    
##  Min.   :-0.869389   Min.   :-2.8800908   Min.   :-3.404775  
##  1st Qu.:-0.522573   1st Qu.:-0.6580219   1st Qu.:-0.892539  
##  Median :-0.282983   Median :-0.0170810   Median :-0.085086  
##  Mean   : 0.000000   Mean   : 0.0000000   Mean   : 0.000000  
##  3rd Qu.: 0.068419   3rd Qu.: 0.7382813   3rd Qu.: 0.706665  
##  Max.   : 6.102341   Max.   : 2.3325377   Max.   : 2.520604

Obsérvese que scale() mediante, ahora todas las variables (menos U_S_M2) tienen promedio igual a 0, y se mueven en el mismo rango sin que esto haya cambiado la forma de las distribuciones.

Comparemos los “nuevos” histogramas con los que examinamos al inicio:

La superficie total:

La superficie descubierta:

La antigüedad de las viviendas:

¡Las formas son iguales! no hemos hemos perdido “información” respecto a que tan típico o extremo es cada valor, y hemos ganado la posibilidad de comparar en forma directa todas las variables.

7.4.4 Consolidar todas las variables generadas ad-hoc en un sólo dataframe

Nos ha quedado por un lado un dataframe de variables numéricas estandarizadas, y por otro una matriz que representa la pertenencia de cada departamento a un barrio de la Ciudad.

Primero convertimos la matriz de barrios en dataframe (paso simple ya que estas estructuras de datos son muy similares entre si), y luego unimos las columnas de ambos con la función cbind():

Ahora que ya tenemos tenemos los datos limpios y en orden, estamos en condiciones de comenzar con nuestro modelo predictivo.

7.5 Paso 4: Crear sets de entrenamiento y de testeo

Para poder evaluar la calidad de un modelo predictivo, es práctica común dividir los datos disponibles en dos porciones. Una parte será utilizada para “entrenar” el modelo de ML, es decir se le permitirá al algoritmo acceder a esos datos para establecer/aprender la forma en que cada variable predictora incide en la que se quiere predecir. El resto será preservado y utilizado para “tomarle examen” al modelo: se le mostraran sólo las variables predictoras de esos datos y se le pedirá una predicción del valor para cada una. Por último, contrastando aciertos y errores, se podrá establecer el grado de precisión del modelo.

Incluso podríamos tener varios modelos distintos, obtenidos con distintas técnicas de ML. No es difícil, ya que una vez que los datos han sido obtenidos y preparados, nada impide usarlos como insumo de distintos algoritmos. En ese caso, se puede comparar la performance de los distintos modelos evaluando cual acierta mejor con la data de testeo.

Definamos entonces cuales son las filas que van a incluirse en el set de entrenamiento, y cuáles en el de testeo, eligiéndolas al azar. De acuerdo a distintas recetas, a veces se separa el 90% de los datos para entrenamiento y el resto para testeo, otras veces es mitad y mitad… ya que siempre es más o menos arbitrario, aquí usaremos el 80% para entrenar, y el 20% para testear.

Tomamos al azar el 80% de las posiciones entre 1 y la cantidad total de filas de nuestro dataset:

Veamos cuantas observaciones quedaron en cada set de datos:

## [1] 4942   57
## [1] 1236   57

Ahora si, por fin, apliquemos un poco de machine learning.

7.5.1 Paso 5: Entrenar y testear un modelo

Random Forest, una implementación de árboles de decisión como los ilustrados en “Una introducción visual al machine learning”:

## 
## Call:
##  randomForest(formula = U_S_M2 ~ ., data = entrenamiento, ntree = 500,      importance = TRUE) 
##                Type of random forest: regression
##                      Number of trees: 500
## No. of variables tried at each split: 18
## 
##           Mean of squared residuals: 166450
##                     % Var explained: 76.48

Según lo que dice aquí, el modelo puede explicar casi el 80% de la varianza de valores encontrada entre los departamentos en venta en 2016 en base a todas las variables predictoras que decidimos emplear.

¿Qué tiene dentro el modelo?

##                 Length Class  Mode     
## call               5   -none- call     
## type               1   -none- character
## predicted       4942   -none- numeric  
## mse              500   -none- numeric  
## rsq              500   -none- numeric  
## oob.times       4942   -none- numeric  
## importance       112   -none- numeric  
## importanceSD      56   -none- numeric  
## localImportance    0   -none- NULL     
## proximity          0   -none- NULL     
## ntree              1   -none- numeric  
## mtry               1   -none- numeric  
## forest            11   -none- list     
## coefs              0   -none- NULL     
## y               4942   -none- numeric  
## test               0   -none- NULL     
## inbag              0   -none- NULL     
## terms              3   terms  call

De todo! Por ejemplo, “type” nos emite confirmar qué tipo de análisis realizó: Fue de regresión en este caso, pero podría haber sido otro, como clasificación (cuando se predice un atributo categórico en lugar de una variable continua):

## [1] "regression"

O “importance”, que contiene un ranking con la importancia relativa de cada predictor, es decir cuáles son los que más ayudan a estimar el valor a predecir (U_S_M2):

##                                %IncMSE IncNodePurity
## M2                      170405.3782376  270645569.04
## M2CUB                   134294.3277435  221452113.17
## AMBIENTES                48984.5707206   63495159.15
## ANTIGUEDAD              182327.5412411  245711058.04
## BAÑOS                    74607.5970445  112362124.22
## M2DESC                  180674.2790710  261275881.96
## DIST_SUBTE              176521.6801680  264617638.20
## LATITUD                 413992.5453703  500524607.66
## LONGITUD                699599.1593140  732943742.64
## BARRIOAGRONOMIA              6.1109342      50231.45
## BARRIOALMAGRO             3425.0417850    5174568.21
## BARRIOBALVANERA           5165.7775106   12632858.63
## BARRIOBARRACAS             602.4320171    1359571.12
## BARRIOBELGRANO           53703.7830256   82005552.59
## BARRIOBOCA                2226.6645687    8059977.35
## BARRIOBOEDO                129.7679756     692676.22
## BARRIOCABALLITO          24001.5913160   22779193.75
## BARRIOCHACARITA            849.1302415    5008125.78
## BARRIOCOGHLAN              147.9159700     842049.34
## BARRIOCOLEGIALES          1734.7723756    5905437.45
## BARRIOCONSTITUCION        1406.6594452    3775022.48
## BARRIOFLORES               623.3206156    2194421.66
## BARRIOFLORESTA              50.9720032     548406.17
## BARRIOLINIERS              208.3842665     444341.83
## BARRIOMATADEROS            156.9745908     767955.35
## BARRIOMONSERRAT           3457.9645045    6058827.59
## BARRIOMONTE_CASTRO         285.6709069     771603.69
## BARRIONUEVA_POMPEYA          0.6181519     102772.05
## BARRIONUÑEZ               4040.6787236    6723864.03
## BARRIOPALERMO            87558.1001621  135914531.21
## BARRIOPARQUE_AVELLANEDA      0.4058692      47294.50
## BARRIOPARQUE_CHACABUCO   23745.2613546   25354031.16
## BARRIOPARQUE_CHAS         3413.2075882   12766924.31
## BARRIOPARQUE_PATRICIOS     488.4688861     793202.79
## BARRIOPATERNAL               0.0000000     126268.58
## BARRIOPUERTO_MADERO      62005.3709601  188295054.36
## BARRIORECOLETA           22937.5507754   26385418.00
## BARRIORETIRO              1838.6391808    7408843.36
## BARRIOSAAVEDRA             906.9669375    2343163.07
## BARRIOSAN_CRISTOBAL      19076.5460187   15731802.46
## BARRIOSAN_NICOLAS         2205.8557488    3070218.74
## BARRIOSAN_TELMO             68.4319709    1541670.81
## BARRIOVELEZ_SARSFIELD       -5.7358774     200321.94
## BARRIOVERSALLES             -0.1234807      28998.64
## BARRIOVILLA_CRESPO       20471.7150953   17075092.55
## BARRIOVILLA_DEL_PARQUE      84.2250373     665843.63
## BARRIOVILLA_DEVOTO         -36.1429484    1687903.42
## BARRIOVILLA_GRAL._MITRE     42.4190298     408725.72
## BARRIOVILLA_LUGANO         221.7012795    2029868.93
## BARRIOVILLA_LURO             7.6858899     621766.65
## BARRIOVILLA_ORTUZAR        232.3501178    1753655.20
## BARRIOVILLA_PUEYRREDON     185.8436268     750870.65
## BARRIOVILLA_REAL             4.2441112      33564.97
## BARRIOVILLA_SANTA_RITA      37.5218071     324228.64
## BARRIOVILLA_SOLDATI        173.9582226    1532857.54
## BARRIOVILLA_URQUIZA        892.7016258    2082640.74

La columna “%IncMSE” representa el porcentaje de error promedio, la magnitud en la que el valor predicho por el modelo difiere del valor observado, cuando cada predictor se retira del modelo (es decir, cuanto peor sería la predicción si no se usara). Por eso los números mayores están asociados a los predictores de más peso, que en este caso son LONGITUD, LATITUD, M2DESC, ANTIGUEDAD y DIST_SUBTE. Además de encontrar la correlación esperable entre el valor del m2 y la superficie descubierta de las propiedades, los años de antiguedad y la distancia al subte, nuestro modelo ha encontrado que la ubicación (latitud y longitud) es la clave del valor de la propiedad… y sin saber nada de geografía ni urbanismo.

En “predicted” tenemos la mediana del valor del m2 predicho para cada observación:

##        1        2        3        4        5        6 
## 2644.151 3306.486 3782.182 2252.104 2144.658 2977.580

Aprovechando que dentro del modelo, “y” contiene los valores observados, evaluemos en forma gráfica cuánto se aproximan las predicciones de cada departamento al valor real (el observado):

Se ajusta bastante bien. Pero ahora veremos una manera de cuantificar la precisión del modelo.

7.5.2 Midiendo la performance del modelo contra datos que no conoce

Veamos ahora como se comporta nuestro modelo cuando debe predecir valores de observaciones que no se han utilizado para el entrenamiento, los que reservamos para el set de testeo.

##        1        2        3        4        5        6 
## 3524.172 2303.221 3805.219 3140.920 3200.704 3059.844

En un gráfico:

El gráfico es muy similar e incluso parecería que se ajusta mejor que con los datos ya conocidos utilizados para el entreneamiento.

7.5.3 Comparando performance

Es práctico obtener un sólo número, un indicador simple que nos diga que tan bien predice el modelo, y así poder comparar distintos modelos entre si (o distintos datasets contra el mismo modelo) utilizando esa medida. En estadística es común el uso del RMSE como indicador de grado de ajuste, o “Root Mean Square Error” - la raíz cuadrada de la media de los errores al cuadrado.

El modelo incluye el MSE (o sea la suma de los errores al cuadrado) que surge de comparar predicciones con valores observados. Y en el caso de un random forest, que intenta muchos árboles distintos, varios MSEs resultantes: 500 en nuestro caso, uno por cada árbol trazado.

Tomamos la media de todos los MSE para obtener un valor general, y luego tomamos la raíz cuadrada para obtener el RMSE:

## [1] 414.0307

Eso significa que la diferencia promedio entre valor esperado y valor hallado para cada distrito fue de 414.0306552 dólares.

Y en comparación, ¿Qué tan bueno resultó el modelo cuando se aplicó a datos que no conocía?

## [1] 422.246

Con un valor medio de error de 422.2460377 dólares, el modelo ha funcionado muy bien con datos desconocidos, incluso mejorando levemente su performance respecto al set de training.

Esto indica que no sufre de “overfitting”, la condición de estar excesivamente ajustado a los datos con los que fue entrenado. Por eso el modelo no pierde precisión cuando lidia con datos nuevos.

Como despedida, hagamos un exámen visual y representamos en un gráfico cada valor predicho y cada valor observado para los datos de entrenamiento:

7.6 Ejercicios

Examinando y prediciendo dinámicas urbanas

I. Elegir algun dataset relacionado a temas urbanos que contenga una variable que les resulte de interés como variable a predecir. Por ejemplo: valor del m2, población, etc.

  1. Realizar un modelo de árboles de decisión (Random Forest) a partir de los siguientes pasos:
  1. Realizar el análisis de variables y la limpieza necesaria.
  2. Crear set de entrenamiento y de testeo.
  3. Entrenar y testear el modelo: Medir la performance.