Julian Andres Ospina Cuesta

Maestría en Ciencia de Datos - Universidad Javeriana Cali
Cali, Colombia
Código: 8984933 +57 3043912471

1.Introducción

El siguiente documento constituye un análisis de las viviendas proporcionadas en el archivo ‘vivienda_faltantes.csv’. El propósito principal es examinar el comportamiento de las ventas y desarrollar estrategias de marketing enfocadas en segmentos especializados, así como servicios diseñados para satisfacer las necesidades específicas de nuestros clientes.


2.Objetivo

Realizar un análisis descriptivo de los datos proporcionados con el propósito de identificar patrones y tendencias de importancia. Estos hallazgos actuarán como pilares orientativos esenciales para la elaboración de planes de marketing en el futuro.


3.Metodo

Para llevar a cabo el análisis siguiente, se contó con las siguientes fases y sub procesos respectivos:

3.1 Fase 1: Cargando librerias, funciones de apoyo y dataset

  • Cargar librerias requeridas
library(readr)
library(dplyr)
library(stringr)
library(mice)
library(knitr)
library(kableExtra)
library(ggplot2)
library(wordcloud)
library(tidyr)
library(psych)
library(gridExtra)
  • Funciones de apoyo
extractor_datos <- function(x) {
  #Función empleada para hacer quitar descripción de texto innecesario en datos
  #ejemplo: 'Mean:80.5' se desea quitar el 'Mean:'
  #**NOTA**: No se trata de una función genérica, sino de una función personalizada 
  #construida específicamente para estos datos.
  #
  #@texto_con_indicador(string): texto a remover descripción del indicador
  #@return(string): indicador sin la descripción del campo
  trimws(str_split(x, ':', n = 2)[[1]][2])
}

ajustar_coordenadas <-function(x) {
  #Función empleada para hacer procesamiento de las coordenadas longitud y latitud
  #**NOTA**: No se trata de una función genérica, sino de una función personalizada 
  #construida específicamente para estos datos.
  #
  #@coordenada(float): coordeanda a procesar
  #@return(float): coordenada previamente procesada
  
  if (abs(x) >1000){
    return (x /1000)
  } else {
    return (x)
  }
}

reemplazar_caracteres <- function(texto) {
  #Función empleada para hacer limpieza de los caracteres encontrados en la base de datos
  #**NOTA**: No se trata de una función genérica, sino de una función personalizada 
  #construida específicamente para estos datos.
  #
  #@texto(string): el texto que vamos a procesar
  #@return(string): el texto previamente tratado
  
  caractares_remplazar <- c('Á','É','Í','Ó','Ú','√©','√∫')
  caracteres_remplazado <- c('A','E','I','O','U','E','U')
  texto <- toupper(texto)
  for (i in 1:length(caractares_remplazar)){
    texto <- gsub(caractares_remplazar[i],caracteres_remplazado[i],texto)
  }
  return (texto)
}

contiene_caracteres_especiales <- function(texto){
  #Función empleada para verificar que no tenga mas caracteres especiales en mi etiqueta
  #luego de haberlos tratado con la función 'reemplazar_caracteres'
  #**NOTA**: No se trata de una función genérica, sino de una función personalizada 
  #construida específicamente para estos datos.
  #
  #@texto(string): el texto que vamos a procesar
  #@return(bool): validación si contiene caracter especial
  
  patron <- '[^[:alpha:]]'
  if (gsub(' ','',texto) == gsub(patron,'',texto)){
    return (FALSE)
  } else {return(TRUE)}
}
  • Extracción de datos
data <- read_csv('vivienda_faltantes.csv')

3.2 Fase 2: Exploración del dataset en aspectos generales

  • Diagnostico inicial
resumen_data <- list(
  num_filas = nrow(data),
  num_col= ncol(data),
  vacios_totales = sum(is.na(data)), #sum(is.na(data), na.rm = TRUE),
  porc_vacios = sprintf('%.2f%%',sum(is.na(data))/(nrow(data)*ncol(data))*100),
  filas_con_mas_un_vacio = sum(apply(is.na(data), 1, any)),
  filas_all_vacios = sum(apply(is.na(data), 1, all)),
  col_all_vacios = sum(colSums(is.na(data)) == nrow(data)),
  filas_duplicadas= nrow(data[duplicated(data), ])
)

kable(do.call(data.frame, resumen_data),
      format = "markdown", 
      caption = "**Tabla n°1 Resumen de dataset antes de tratamientos**",
      align = "c", escape = FALSE,
      row.names = FALSE,
      booktabs = TRUE)
Tabla n°1 Resumen de dataset antes de tratamientos
num_filas num_col vacios_totales porc_vacios filas_con_mas_un_vacio filas_all_vacios col_all_vacios filas_duplicadas
8330 13 4279 3.95% 3518 2 0 9

Se ha identificado la existencia de dos filas vacías y nueve registros duplicados en el conjunto de datos. Estos elementos, por varias razones, no parecen ser esenciales para los objetivos de este estudio:

  1. En relación con el total de 8,330 observaciones, la presencia de estos 9 registros duplicados parece no ser representativa ni influyente.
  2. Las filas con valores vacíos a menudo se asocian con errores en el procesamiento de la información y, por lo tanto, no contribuyen de manera significativa a este análisis.
  • Tratamiento de registros vacios y duplicados
data <- data[rowSums(is.na(data)) < ncol(data), ]
data <- data[!duplicated(data), ]

resumen_data <- list(
 num_filas = nrow(data),
 num_col= ncol(data),
 vacios_totales = sum(is.na(data)), #sum(is.na(data), na.rm = TRUE),
 porc_vacios = sprintf("%.2f%%",sum(is.na(data))/(nrow(data)*ncol(data))*100),
 filas_con_mas_un_vacio = sum(apply(is.na(data), 1, any)),
 filas_all_vacios = sum(apply(is.na(data), 1, all)),
 col_all_vacios = sum(colSums(is.na(data)) == nrow(data)),
 filas_duplicadas= nrow(data[duplicated(data), ])
)

kable(do.call(data.frame, resumen_data),
      format = "markdown", 
      caption = "**Tabla n°2 Resumen de dataset con tratamientos**",
      align = "c", escape = FALSE,
      row.names = FALSE,
      booktabs = TRUE)
Tabla n°2 Resumen de dataset con tratamientos
num_filas num_col vacios_totales porc_vacios filas_con_mas_un_vacio filas_all_vacios col_all_vacios filas_duplicadas
8320 13 4249 3.93% 3512 0 0 0
  • Se procede a revisar el resumen de los campos faltantes en la datos procesados
grafico_vacios <-md.pattern(data, rotate.names = TRUE)

  • Se observa que existe 1 solo registro que tiene casi todos los campos vacios, muy probablemente es un error y se procedera a eliminar el registro.
data <- data[!is.na(data$id), ]
resumen_data <- list(
 num_filas = nrow(data),
 num_col= ncol(data),
 vacios_totales = sum(is.na(data)), #sum(is.na(data), na.rm = TRUE),
 porc_vacios = sprintf("%.2f%%",sum(is.na(data))/(nrow(data)*ncol(data))*100),
 filas_con_mas_un_vacio = sum(apply(is.na(data), 1, any)),
 filas_all_vacios = sum(apply(is.na(data), 1, all)),
 col_all_vacios = sum(colSums(is.na(data)) == nrow(data)),
 filas_duplicadas= nrow(data[duplicated(data), ])
)


kable(do.call(data.frame, resumen_data),
      format = "markdown",  
      caption = "**Tabla n°3 Resumen de dataset con tratamientos y registros mayormente completados**",
      align = "c", escape = FALSE,
      row.names = FALSE,
      booktabs = TRUE)
Tabla n°3 Resumen de dataset con tratamientos y registros mayormente completados
num_filas num_col vacios_totales porc_vacios filas_con_mas_un_vacio filas_all_vacios col_all_vacios filas_duplicadas
8319 13 4237 3.92% 3511 0 0 0

Se obtiene la Tabla n°3 Resumen de dataset con tratamientos donde se aprecia el total de registros con el que se procederan a realizar las siguientes fases.

3.3 Fase 3: Descripción estadistica de cada campo

Se inicia con una clasificación de cada uno de los campos entre cuantitativos y cualitativos

  • Separación de campos entre tipos de variables
resumen_df <- as.data.frame.matrix(summary(data))
resumen_df <- t(resumen_df)

types <- sapply(data, class)
resumen_cuantitativa <- resumen_df[types %in% c("numeric", "integer"), ]
resumen_cuantitativa <- rownames(resumen_cuantitativa)
resumen_cuantitativa <- gsub(" ", "", resumen_cuantitativa) # para quitar los espacios en blanco

resumen_cualitativa <- resumen_df[!(types %in% c("numeric", "integer")), ]
resumen_cualitativa <- rownames(resumen_cualitativa)
resumen_cualitativa <- gsub(" ", "", resumen_cualitativa) # para quitar los espacios en blanco

3.3.1 Analisis por tipo de variable

Variables cuantitativas

Algunas variables directamente no se ven representantes para nuestro estudio, como es el caso de: ‘id’

piso
col <- 'piso'
frecuencias <- table(data$piso)
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
valores_ordenados <- factor(names(sort(frecuencias, decreasing = TRUE)), levels = names(frecuencias))

ggplot(data = data.frame(valores = valores_ordenados, frecuencias), aes(x = valores_ordenados, y = frecuencias)) +
  geom_bar(stat = "identity", fill = "#0576FF") +
  geom_text(aes(label = paste(porcentajes, "%")), vjust = -0.5) +
  labs(title = paste("Grafico de Barras",col), x = paste("Valores de ",col), y = "Frecuencia") +
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

describe(data$piso)
##    vars    n mean   sd median trimmed  mad min max range skew kurtosis   se
## X1    1 5684 3.77 2.61      3    3.37 1.48   1  12    11 1.28     1.05 0.03

El gráfico indica que los registros estan sesgados a la izquierda (el 80.77% de los datos estan entre el 1-5 piso), con una media del 3.77 y una desviación 2.61 nota este campo presenta una cantidad considerabe de registros vacios muy importante 2635 vacios, por lo que se ha decidido crear una nueva categoria y examinarlo como un caso especial.

estrato
col <- 'estrato'
frecuencias <- table(data$estrato)
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
valores_ordenados <- factor(names(sort(frecuencias, decreasing = TRUE)), levels = names(frecuencias))

ggplot(data = data.frame(valores = valores_ordenados, frecuencias), aes(x = valores_ordenados, y = frecuencias)) +
  geom_bar(stat = "identity", fill = "#0576FF") +
  geom_text(aes(label = paste(porcentajes, "%")), vjust = -0.5) +
  labs(title = paste("Grafico de Barras",col), x = paste("Valores de ",col), y = "Frecuencia") +
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

describe(data$estrato)
##    vars    n mean   sd median trimmed  mad min max range  skew kurtosis   se
## X1    1 8319 4.63 1.03      5    4.67 1.48   3   6     3 -0.18    -1.11 0.01

Vemos que no estan estrato 1 y 2,(nicho de mercado nuestro son cliente con poder adquisitivo medio alto). El gráfico indica que los registros estan algo sesgados a la derecha, aunque no esta muy concentrado y con una media en alrededor del estrato 4.63 y desviacion estandar de 1.03.

preciom
col<-'preciom'
ggplot(data = data, aes(x = !!as.name(col))) +
  geom_histogram(bins = 30, color = "#515354", fill = "#0576FF") +
  geom_vline(aes(xintercept = mean(!!as.name(col))), color = "#FF0576", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = median(!!as.name(col))), color = "#50AC05", linetype = "dashed", size = 1) +
  theme_bw() +
  labs(title = paste('Histograma de', col), x = col, y = "Frecuencia")+
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

describe(data$preciom)
##    vars    n  mean     sd median trimmed    mad min  max range skew kurtosis
## X1    1 8319 433.9 328.67    330  374.48 207.56  58 1999  1941 1.85     3.67
##     se
## X1 3.6

El gráfico indica que los registros estan sesgados a la izquierda, con una media en 433.9 millones y desviación de 368.67 millones muy alta para ser el campo mas representativo para el estudio. Vemos tambien, una kurtosis de 3.67 muy por arriba del 0; indicandonos que tenemos un precio muy concentrado y punteagudo en el grafico.

areaconst
col<-'areaconst'
ggplot(data = data, aes(x = !!as.name(col))) +
  geom_histogram(bins = 30, color = "#515354", fill = "#0576FF") +
  geom_vline(aes(xintercept = mean(!!as.name(col))), color = "#FF0576", linetype = "dashed", size = 1) +
  geom_vline(aes(xintercept = median(!!as.name(col))), color = "#50AC05", linetype = "dashed", size = 1) +
  theme_bw() +
  labs(title = paste('Histograma de', col), x = col, y = "Frecuencia")+
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

describe(data$areaconst)
##    vars    n   mean     sd median trimmed   mad min  max range skew kurtosis
## X1    1 8319 174.93 142.96    123  149.15 84.51  30 1745  1715 2.69    12.91
##      se
## X1 1.57

El gráfico indica que los registros estan sesgados a la izquierda, con una media de 174.93 y desviación de 142.96 muy alto, casi igual a la media, su kurtosis es muy alta 12.91 dando una distribución muy punteaguda.

parquea
col <- 'parquea'
frecuencias <- table(data$parquea)
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
valores_ordenados <- factor(names(sort(frecuencias, decreasing = TRUE)), levels = names(frecuencias))

ggplot(data = data.frame(valores = valores_ordenados, frecuencias), aes(x = valores_ordenados, y = frecuencias)) +
  geom_bar(stat = "identity", fill = "#0576FF") +
  geom_text(aes(label = paste(porcentajes, "%")), vjust = -0.5) +
  labs(title = paste("Grafico de Barras",col), x = paste("Valores de ",col), y = "Frecuencia") +
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

describe(data$parquea)
##    vars    n mean   sd median trimmed  mad min max range skew kurtosis   se
## X1    1 6717 1.84 1.12      2    1.62 1.48   1  10     9 2.33     8.31 0.01

El gráfico revela que los registros muestran un sesgo hacia la izquierda, el 83.82% de los datos estan en el 1 y 2 piso.nota recordemos que esta variable representa una alta cantidad de valores perdido 1602, por lo que se manejara los faltantes como una nueva categoria y se revisara mas adelante como un caso especial.

banios
col <- 'banios'
frecuencias <- table(data$banios)
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
valores_ordenados <- factor(names(sort(frecuencias, decreasing = TRUE)), levels = names(frecuencias))

ggplot(data = data.frame(valores = valores_ordenados, frecuencias), aes(x = valores_ordenados, y = frecuencias)) +
  geom_bar(stat = "identity", fill = "#0576FF") +
  geom_text(aes(label = paste(porcentajes, "%")), vjust = -0.5) +
  labs(title = paste("Grafico de Barras",col), x = paste("Valores de ",col), y = "Frecuencia") +
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

describe(data$banios)
##    vars    n mean   sd median trimmed  mad min max range skew kurtosis   se
## X1    1 8319 3.11 1.43      3    2.99 1.48   0  10    10 0.93     1.13 0.02

El gráfico no muestra un comportamiento de los datos, de hecho llama la atención su skew de 0.93, muy cercana al cero. no obstante la media en 3.11 con su desviacion de 1.41 todavia algo a revisar. NOTA vemos que aparecen casas con 0 baños 0.85% , pueden tratarse de error o de apartaestudios con baños comunitarios algo improbable para la ciudad de Cali no obstante es un valor poco representativo dentro del estudio y creemos que no se debe tener encuenta.

habitac
col <- 'habitac'
frecuencias <- table(data$habitac)
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
valores_ordenados <- factor(names(sort(frecuencias, decreasing = TRUE)), levels = names(frecuencias))

ggplot(data = data.frame(valores = valores_ordenados, frecuencias), aes(x = valores_ordenados, y = frecuencias)) +
  geom_bar(stat = "identity", fill = "#0576FF") +
  geom_text(aes(label = paste(porcentajes, "%")), vjust = -0.5) +
  labs(title = paste("Grafico de Barras",col), x = paste("Valores de ",col), y = "Frecuencia") +
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

describe(data$habitac)
##    vars    n mean   sd median trimmed  mad min max range skew kurtosis   se
## X1    1 8319 3.61 1.46      3    3.41 1.48   0  10    10 1.63     3.98 0.02

El gráfico revela que los registros muestran un sesgo hacia la derecha. media en 3.61 con desviacion de 1.46 muy alta para ser habitaciones. NOTA vemos que aparecen casas con 0 habitaciones de 1.66% , pueden tratarse de error o de apartaestudios con un solo ambiente. no obstante es un valor poco representativo dentro del estudio, y creemos que no se debe tener encuenta.

longitud y latitud
ggplot(data=data) +
  geom_point(aes(x=longitud,y=latitud, col=tipo),
             col='#50AC05',
             size=3, alpha = 1/5    )+
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

El gráfico pone de manifiesto problemas en los registros, ya que las coordenadas de la ciudad no superan las tres cifras en su parte entera. Por lo tanto, es necesario llevar a cabo un proceso de tratamiento en este campo.

Variables cualitativas

lista_graficos_cuali <- list()
for (col in resumen_cualitativa) {
 frecuencia_palabras_temp = aggregate(data[[col]], list(data[[col]]), FUN=length)
 colnames(frecuencia_palabras_temp)=c("Palabra","Frecuencia")
 lista_graficos_cuali[[col]] <- as.data.frame(frecuencia_palabras_temp)
}
tipo
frecuencia_palabras_temp = lista_graficos_cuali[['tipo']]
wordcloud(words = frecuencia_palabras_temp$Palabra,
                     freq = frecuencia_palabras_temp$Frecuencia,
                     min.freq = 2,
                     max.words = 60,
                     colors =  c("#515354","#50AC05","#0576FF", "#FF0576"),
                     random.order = F,
                     random.color = F,
                     scale = c(4 ,0.5),
                     rot.per = 0.25)

Se pueden identificar tipo de apartamentos con falta de estandarización en los datos. Algunos ejemplos de esto son el uso de mayúsculas y abreviaciones.

zona
frecuencia_palabras_temp = lista_graficos_cuali[['zona']]
wordcloud(words = frecuencia_palabras_temp$Palabra,
                     freq = frecuencia_palabras_temp$Frecuencia,
                     min.freq = 2,
                     max.words = 60,
                     colors =  c("#515354","#50AC05","#0576FF", "#FF0576"),
                     random.order = F,
                     random.color = F,
                     scale = c(4 ,0.5),
                     rot.per = 0.25)

Es evidente la distinción de cinco zonas, siendo la zona Sur la más destacada, seguida por las zonas Norte y Oeste en términos de participación de registros.

barrio
frecuencia_palabras_temp = lista_graficos_cuali[['barrio']]
wordcloud(words = frecuencia_palabras_temp$Palabra,
                     freq = frecuencia_palabras_temp$Frecuencia,
                     min.freq = 2,
                     max.words = 60,
                     colors =  c("#515354","#50AC05","#0576FF", "#FF0576"),
                     random.order = F,
                     random.color = F,
                     scale = c(4 ,0.5),
                     rot.per = 0.25)

Se puede observar que hay barrios con una falta de estandarización en los datos. Ejemplos de esto incluyen el uso inconsistente de mayúsculas, abreviaciones, puntuaciones y símbolos, lo que indica problemas en el manejo de la normalización de la información.


3.4 Fase 4: Limpieza de algunos campos

-Se estandariza el campo tipo de vivienda

data$tipo <- gsub("^Apartamento", "Apartamento", data$tipo, ignore.case = TRUE)
data$tipo <- gsub("^apto", "Apartamento", data$tipo, ignore.case = TRUE)
data$tipo <- gsub("^Apartamento", "Apartamento", data$tipo, ignore.case = TRUE)
data$tipo <- gsub("^casa", "Casa", data$tipo, ignore.case = TRUE)
data$tipo <- gsub("^casa", "casa", data$tipo, ignore.case = TRUE)

-Se estandariza el campo barrio

data$barrio <- sapply(data$barrio, reemplazar_caracteres)
  • Se aprecia que la longitud y la latitud de cali tiene datos que no corresponden Nota Cali reporta coordenadas cercanas a 3.450695743589349, -76.5313271692115
data$longitud   <- sapply(data$longitud, ajustar_coordenadas)
data$latitud    <- sapply(data$latitud, ajustar_coordenadas)

Veamos como quedaron las variables que normalizamos:

tipo
col <- 'tipo'
frecuencia_palabras_temp = aggregate(data[[col]], list(data[[col]]), FUN=length)
colnames(frecuencia_palabras_temp)=c("Palabra","Frecuencia")
frecuencia_palabras_temp = as.data.frame(frecuencia_palabras_temp)
wordcloud(words = frecuencia_palabras_temp$Palabra,
                     freq = frecuencia_palabras_temp$Frecuencia,
                     min.freq = 2,
                     max.words = 60,
                     colors =  c("#515354","#50AC05","#0576FF", "#FF0576"),
                     random.order = F,
                     random.color = F,
                     scale = c(4 ,0.5),
                     rot.per = 0.25)

Veamos que Aparatmentos tiene una alta participación en la base de datos, un dato muy interesante de revisar frente al precio y otras varaibles de interes

barrio
col <- 'barrio'
frecuencia_palabras_temp = aggregate(data[[col]], list(data[[col]]), FUN=length)
colnames(frecuencia_palabras_temp)=c("Palabra","Frecuencia")
frecuencia_palabras_temp = as.data.frame(frecuencia_palabras_temp)
wordcloud(words = frecuencia_palabras_temp$Palabra,
                     freq = frecuencia_palabras_temp$Frecuencia,
                     min.freq = 2,
                     max.words = 60,
                     colors =  c("#515354","#50AC05","#0576FF", "#FF0576"),
                     random.order = F,
                     random.color = F,
                     scale = c(4 ,0.5),
                     rot.per = 0.25)
## Warning in wordcloud(words = frecuencia_palabras_temp$Palabra, freq =
## frecuencia_palabras_temp$Frecuencia, : VALLE DEL LILI could not be fit on page.
## It will not be plotted.

Veamos que el barrio mas representativo son: Ciudad Jardin, Pance , La flora y santa teresita. De hecho son barrios que representan a la Zona sur, el norte de Cali y el oeste de Cali corroborando el comportamiento que tiene las zonas.

longitud y latitud
ggplot(data=data) +
  geom_point(aes(x=longitud,y=latitud, col=tipo),
             col='#50AC05',
             size=3, alpha = 1/5    )+
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

El gráfico representa la ciudad de Cali; sin embargo, las concentraciones visibles nos llevan a cuestionar si la transformación de los registros ocurrió de acuerdo a nuestras expectativas. Para abordar esto, procedimos a realizar una clasificación del mismo gráfico, dividiéndolo en zonas. Esto nos brindará una comprensión más precisa de los datos en juego

ggplot(data=data) +
  geom_point(aes(x=longitud,y=latitud, col=zona),
             size=3, alpha = 1/5    )+
  theme_minimal()+
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

Vemos que si se define en gran manera las zonas, pero muchos puntos fuera de las zonas en donde se deberian demarcar, lo que nos hace un dato que no es muy confiable


3.5 Fase 5: Estadisticos despues de limpieza de algunos campos

resumen_df <- as.data.frame.matrix(summary(data))
resumen_df <- t(resumen_df)

columns_with_text <- sapply(resumen_df, is.character)
for (col in colnames(resumen_df)) {
 resumen_df[,col] <- sapply(resumen_df[,col], extractor_datos)
}

# Separamos el resumen en dos dataframes: uno para variables cuantitativas y otro para cualitativas
types <- sapply(data, class)
resumen_cuantitativa <- resumen_df[types %in% c("numeric", "integer"), ]
nuevos_nombres <- c("Min.","1st Qu.","Median","Mean","3rd Qu.","Max","NA's")
colnames(resumen_cuantitativa) <- nuevos_nombres


kable(as.data.frame(resumen_cuantitativa),
      format = "markdown", 
      caption = "**Tabla n°4 Tabla de resumen estadistico -  campos cuantitativos**",
      align = "c", escape = FALSE,
      row.names = TRUE,
      booktabs = TRUE,
      table.attr = 'style="border-collapse: collapse;  border-spacing: 0 5px; border: 1px solid black;"')
Tabla n°4 Tabla de resumen estadistico - campos cuantitativos
Min. 1st Qu. Median Mean 3rd Qu. Max NA’s
id 1 2080 4160 4160 6240 8319 NA
piso 1.000 2.000 3.000 3.771 5.000 12.000 2635
estrato 3.000 4.000 5.000 4.634 5.000 6.000 NA
preciom 58.0 220.0 330.0 433.9 540.0 1999.0 NA
areaconst 30.0 80.0 123.0 174.9 229.0 1745.0 NA
parquea 1.000 1.000 2.000 1.835 2.000 10.000 1602
banios 0.000 2.000 3.000 3.111 4.000 10.000 NA
habitac 0.000 3.000 3.000 3.605 4.000 10.000 NA
longitud -76.59 -76.54 -76.53 -76.53 -76.52 -76.46 NA
latitud 3.333 3.381 3.416 3.418 3.452 3.498 NA

Un resumen que nos muestra los indicadores de tendencia central y de posición, pero que nos muestra o al menos para mi una tendencia generalizada en todos los campos de la tabla. por lo que el siguiente paso sera revisar los campos que nos llamo la atención:

  1. El precio como variable principal y la distribución de las zonas (muy complementada con lo visto en barrios y algo aproximado a lo que nos muestran las coordenadas)
  2. El precio en los estratos, una hipotesis que obedece a las clasificacioens mismas del negocio.

3.6 Manejo de caso especial

En los campos piso y habitación tenemos campos vacios, los vamos a rellenar con un valor arbitrario para diferenciarlos y estudiarlos de una manera aislada posteriormente, valor arbitrario sera 0, para poder procesar modelos númericos y mas adelante, realiza los estudio con estos valores y sin ellos

data$parquea <- replace(data$parquea, is.na(data$parquea),0)
data$piso <- replace(data$piso, is.na(data$piso),0)

4. Resultados

4.1 Analisis de precio de viviendas por zonas

Partiendo del estudio de la varaible de precios,podemos partir de un valor atipico si supera ciertas caracteristicas, especialmente en la parte superiro del precio. para ello vamos a calcular el limite teorico del valor atipico y revisar el impacto con otras variables

#Calculamos la linea de valor atipico con base en los valores generales de la base de datos
Q3 = as.numeric(resumen_cuantitativa[which(rownames(resumen_cuantitativa) == "   preciom"), "3rd Qu."])
Q1 = as.numeric(resumen_cuantitativa[which(rownames(resumen_cuantitativa) == "   preciom"), "1st Qu."])
datos_atipicos_precio= Q3 + 1.5 * (Q3 - Q1)

valores_promedio <- aggregate(preciom ~ zona, data = data, mean)
ggplot(data=data, aes(x=zona, y=preciom))+
 geom_boxplot(fill="#0576FF")+
 geom_point(data = subset(data, preciom > datos_atipicos_precio), aes(color = "Atípico"), size = 3,color = "#FF0576") +
 stat_summary(fun = "mean", geom = "point", shape = 18, size = 4, color = "green") +
 geom_text(data = valores_promedio, aes(label = round(preciom, 2)), vjust = -0.5) +
 labs(x="Zonas", y="precio venta($millones)")+
 ggtitle("Precio de las viviendas en las diferentes zonas de Cali")+
 theme_minimal() +
 theme(plot.background = element_rect(fill = "white"),
       panel.grid = element_blank(),
       panel.border = element_blank(),
       plot.title = element_text(size = 16, hjust = 0.5),
       axis.line = element_line(size = 1, color = "#515354"))

Con el pasado grafico de clasificación de zonas, podemos destacar: + la zona Oesta, destaca por aportar muchos valores por encima de nuestro valor atipico teorico y una alta variación entre los precios con que maneja sus viviedas. no obstante su media de precios es superior con diferencia del resto de las zonas. + las zonas sur y norte aportan registros con precios que superan el valor atipico teorico, pero su boxplot son mas compactos, lo que nos dice que sus desviaciones no son tan altas con respecto a la zona Oeste + la zona oriente y centro tiene cajas de bigotes mas compactas, sus medias de precios son las mas bajas entre el resto de las zonas y cuentan con pocos valores atipicos De hecho, llama la atención que tengan registros que superan el valor atipico teorico

data$estrato_categoria <- factor(data$estrato, levels = sort(unique(data$estrato)))

valores_promedio <- aggregate(preciom ~ estrato_categoria, data = data, mean)
ggplot(data=data, aes(x=estrato_categoria, y=preciom))+
 geom_boxplot(fill="#0576FF")+
 geom_point(data = subset(data, preciom > datos_atipicos_precio), aes(color = "Atípico"), size = 3,color = "#FF0576") +
   stat_summary(fun = "mean", geom = "point", shape = 18, size = 4, color = "green") +
   geom_text(data = valores_promedio, aes(label = round(preciom, 2)), vjust = -0.5) +
   labs(x="Estratos", y="precio venta($millones)")+
   ggtitle("Precio de las viviendas en los diferentes estratos de la base de datos")+
   theme_minimal() +
   theme(plot.background = element_rect(fill = "white"),
         panel.grid = element_blank(),
         panel.border = element_blank(),
         plot.title = element_text(size = 16, hjust = 0.5),
         axis.line = element_line(size = 1, color = "#515354"))

Con el pasado grafico de clasificación de estratos, podemos destacar: + Se confirma la tendencia del engocio de que el estrato marca el precio de la vivienda, entre mas alto el estrato , mas alto es el estrato , mayor sera el precio almenos en una tendencia general + Todos los estratos tiene mucha variacion en los precios + Todos los estratos cuentan con registros que superan el precio atipico teorico calculado, lo que nos hace sospechar de que debe existir otro campo o campos igualmente influyentes en el precio fuera del estrato.

4.2 Comparacion de participación estratos y zonas

Tal como observamos en el punto anterior, el estrato revela ciertas tendencias en los precios. Paralelamente, notamos que las zonas también pueden ejercer una influencia significativa en los precios. Por esta razón, consideramos valioso visualizar la interacción de estos dos aspectos con el precio promedio mediante un diagrama de burbujas.

# Contar registros para cada combinación de "estrato" y "zona"
conteo <- table(data$estrato_categoria, data$zona)
conteo_df <- as.data.frame(as.table(conteo))
names(conteo_df) <- c("estrato", "zona", "conteo")
diagrama_conteo <- ggplot(conteo_df, aes(x = estrato, y = zona, size= conteo, color = conteo)) +
 geom_point() +
 scale_size_continuous(range = c(5, 20), guide = "none") +
 scale_color_gradient(low = "blue", high = "darkblue") +
 labs(title = "Diagrama de Burbuja",
      x = "Estrato",
      y = "Zona",
      size = "Conteo")+
 theme_minimal() +
 theme(plot.background = element_rect(fill = "white"),
       panel.grid = element_blank(),
       panel.border = element_blank(),
       plot.title = element_text(size = 16, hjust = 0.5),
       axis.line = element_line(size = 1, color = "#515354"))

promedio_precio <- aggregate(preciom ~ estrato_categoria + zona, data = data, FUN = mean)
tabla_mediaPrecios <- xtabs(preciom ~ estrato_categoria + zona, data = promedio_precio)
mediaPrecios_df <- as.data.frame(as.table(tabla_mediaPrecios))
names(mediaPrecios_df) <- c("estrato", "zona", "MediaPrecios")
diagrama_precio <- ggplot(mediaPrecios_df, aes(x = estrato, y = zona, size= MediaPrecios, color = MediaPrecios)) +
 geom_point() +
 scale_size_continuous(range = c(5, 20), guide = "none") +
 scale_color_gradient(low = "blue", high = "#FF0576") +
 labs(title = "Diagrama de Burbuja de precios en Millones",
      x = "Estrato",
      y = "Zona",
      size = "Precio medio ($milones)")+
 theme_minimal() +
 theme(plot.background = element_rect(fill = "white"),
       panel.grid = element_blank(),
       panel.border = element_blank(),
       plot.title = element_text(size = 16, hjust = 0.5),
       axis.line = element_line(size = 1, color = "#515354"))

grid.arrange(diagrama_conteo, diagrama_precio, ncol = 1,
             heights = c(2, 2))

Observamos comportamientos bien interesantes: + El precio medio si es muy marcado alto en estratos altos, con una excepción estrato 5 - zona oriente, + dependiendo de las zonas, asi mismo se afecta la participación en el estrato, es decir: + la zona sur destaca en estratos 4 y 5 + la zona oriente en estrato 3 (Tiene sentido, es zona con pocos barrieos de estrato alto) + la zona norte esta muy disribuida entre 3,4, 5 con inclinación a este ultimo. + la zona oeste con una alta concentración el estrato 6 + Llama mucha la atención el precio medio de estrato 6 - zona oriente con un precio medio por encima del resto de las zonas y estratos.

Dejando la conclusión, deben existir otros vehiculos que acompañen a estas variables de estrato y zona que estan dando tendencia en nuestro analisis.

4.2 Analisis por Tipo de viviendas más vendidas en Cali

resumen_portipo = aggregate(preciom ~ tipo, data = data, FUN= function(x) c(Conteo = length(x), Total =sum(x)))
resultado <-as.data.frame(resumen_portipo$preciom)
resumen_portipo$Conteo <- resultado$Conteo
resumen_portipo$Total <- resultado$Total
resumen_portipo$Porc_Conteo <- prop.table(resumen_portipo$Conteo) * 100
resumen_portipo$Porc_Total <- prop.table(resumen_portipo$Total) * 100

resumen_portipo_long = gather(resumen_portipo[,c('tipo','Porc_Conteo','Porc_Total')], key="Tipo_Porc", value= "Porc",-tipo)
 
ggplot(resumen_portipo_long, aes(x = Tipo_Porc, y = Porc, fill = tipo)) +
   geom_bar(stat = "identity", width = 0.5) +
   geom_text(aes(label =scales::percent(Porc/100)) ,
             position = position_stack(vjust = 0.5), size = 4) +
   labs(title = "% de Apartamentos y Casas ( % en evento y $$$ ) n. 3",
        x = "Tipo de Apartamento",
        y = "") +
   theme_minimal() +
   theme(axis.text.y= element_blank(),
         panel.background = element_rect(fill="white"),
         panel.grid=element_blank(),
         panel.border = element_blank(),
         plot.title = element_text(size = 16, hjust = 0.5),)+
   scale_fill_manual(values = c("#0576FF", "#50AC05", "red", "orange", "purple")) 

Cuando vimos el analisis de la variable tipo de vivienda, recordemos que Apartamento se veia con tendencia frente a las casas. 61.3% del total de registros, lo llamativo es que a pesar de su aprticipación , en ventas totales fue casi igualadad por las casas bajo al 51.8% su participación global. dejando un campo muy interesante a revisar para nuestro estudio de determinar campos que son conductores del precio.

4.3 Principal vehiculo del precio de vivienda: correlaciones entre los campos

Con varios campos ya perfilados como interesantes para nuestro analisis, vamos a realizar una correlación entre los campos presenten en la base para determinar nuevos candidatos de vehiculos del precio de vivienda.

#vamos a buscar cuales caracteristicas tiene una correlación con preciom
campos_cuantitativos <- gsub(" ", "", rownames(resumen_cuantitativa))

data$zona_index = as.integer(factor(data$zona, levels = unique(data$zona)))
data$tipo_index = as.integer(factor(data$tipo, levels = unique(data$tipo)))
data$barrio_index = as.integer(factor(data$barrio, levels = unique(data$barrio)))

cor_data <- cor(select(data, -tipo, -barrio, -zona, -id,-estrato_categoria))

grafico_n4 <- corPlot(cor_data, 
   cex = 0.7,
   xsrt = 75,
   main = "Matriz de correlación")  # Color de línea entre los números

Nota: Es importante recordar que en el caso de los campos “piso” y “parqueo” teníamos muchos registros faltantes, por lo que debemos tener en cuenta la relevancia de sus valores de correlación.

Hemos observado las siguientes relaciones interesantes:

  • En relación al precio, las correlaciones más notables son: el área de construcción, el número de espacios de parqueo, la cantidad de baños y el estrato.
  • Se destaca una correlación interesante entre el área construida y la cantidad de baños, lo cual tiene total sentido, ya que a medida que la casa es más grande, se espera que haya más baños.
  • El área de construcción también muestra cierta relación con el número de habitaciones.
  • Respecto al tipo de vivienda, aunque su correlación con el precio no es muy evidente, guarda una relación interesante con el área de construcción y el número de habitaciones. Este es un dato muy revelador, ya que ambos atributos destacan por su influencia en los precios.
  • La relación entre las zonas, los barrios y las coordenadas es clara, y está relacionada con su ubicación geográfica.

5. Discusiones

Con base en las fase de resultados, se plantean revisar las siguientes hipotesis y sacar conclusiones

5.1 El tipo de vivienda es una categoria indirecta del precio

Para confirmar este comportamiento vamos que tener que revisar una variable que conectes el precio con el tipo dado su relación con ambas variables: area de contrucción

5.1.1 Áreas de contrucción y precio segun Estrato - previo

ggplot(data=data) +
  geom_point(aes(x=areaconst,y=preciom, col=estrato_categoria),
             #col='red',
             size=3, alpha = 1/5    )+
  geom_smooth(aes(x=areaconst,y=preciom, linetype=estrato_categoria),
              method = 'lm', se=FALSE, col='#FF0576')+
  facet_wrap(~ estrato_categoria, nrow =4)+
  labs(title = "Gráfico de Relación entre Área y Precio por estratos")+
  theme(panel.background = element_rect(fill = "white"))
## `geom_smooth()` using formula = 'y ~ x'

Observamos que el area de contrucción, clasificando por estrato tiene algo muy interesante, su pendiente aumenta a medida que subimos de estrato y subimos las areas de contrucción. bastante llamativo tambien es las dispersión que se muestra en pocas áreas de contrucción.

5.1.1 Áreas de contrucción y precio segun Estrato - previo

ggplot(data=data) +
  geom_point(aes(x=areaconst,y=preciom, col=zona),
             #col='red',
             size=3, alpha = 1/5    )+
  geom_smooth(aes(x=areaconst,y=preciom, linetype=zona),
              method = 'lm', se=FALSE, col='#FF0576')+
  facet_wrap(~ zona, nrow =4)+
  labs(title = "Gráfico de Relación entre Área y Precio por zonas")+
  theme(panel.background = element_rect(fill = "white"))
## `geom_smooth()` using formula = 'y ~ x'

En definitiva el área de contrucción es una variable que destaca tambien zonas, donde vemos que viendo esta relación que nos adelantaba las correlación. aunque, sigue llamando al dispersión del precio en áreas muy pequeñas

5.1.2 Área de contrucción y precio segun Tipo de vivienda

ggplot(data=data) +
  geom_point(aes(x=areaconst,y=preciom, col=tipo),
             #col='red',
             size=3, alpha = 1/5    )+
  geom_smooth(aes(x=areaconst,y=preciom, linetype=tipo),
              method = 'lm', se=FALSE, col='#FF0576')+
  facet_wrap(~ tipo, nrow =4)+
  labs(title = "Gráfico de Relación entre Área y Precio por tipo de vivienda")+
  theme(panel.background = element_rect(fill = "white"))
## `geom_smooth()` using formula = 'y ~ x'

Fantanstico! una pendiente muy distinta al separarlo con vivienda, y una dispersión menor al separar estas dos variables, lo que nos deja muy claro que el tipo de vivienda debe ser separada en nuestro estudio y construir dos tipos de modelos

5.2 Modelo de vivienda Apartamento

cor_data <- cor(select(filter(data, tipo == 'Apartamento'), -tipo,-tipo_index, -barrio, -zona, -id,-estrato_categoria))

grafico_n4 <- corPlot(cor_data, 
                      cex = 0.7,
                      xsrt = 75,
                      main = "Matriz de correlación")  # Color de línea entre los números

Vemos que las relaciones se fortalecieron trayendo nuevaos campos: + Los baños destacan con una alta correlacion frente al modelo sin segregación del tipo de apartamento, pero recordemos que guarda una relación de logica con el área de contrucción + parqueo destaca como nuevo campo, pero tiene una restricción esta relaccion peude ser afectada por nuestra categoria sin valor, que esta en cero. + habitaciones aparece tener una relación con baños,y no tener relación con el precio, caso similar con el tipo de casa en el modelo inicial.

5.2.1 Modelo de vivienda Apartamento en parqueo sin faltante

Veamos si dejando los valores de parqueo con valores diferentes a nulo, nos da una mirada diferente.

cor_data <- cor(select(filter(data, tipo == 'Apartamento', parquea > 0), -tipo,-tipo_index, -barrio, -zona, -id,-estrato_categoria))

grafico_n4 <- corPlot(cor_data, 
                      cex = 0.7,
                      xsrt = 75,
                      main = "Matriz de correlación")  # Color de línea entre los números

Vemos que la relación mejoro frente al precio (de 0.69 a 0.74 ) y el área de construcción (de 0.62 a 0.68). es decir que el cambio de llenar los datos faltante con valor cero, estaba perjudicando la relación de los datos.

5.2.3 Modelo de vivienda Apartamento por parquea (Número de parqueaderos)

data_temp <- filter(data, tipo == 'Apartamento', parquea > 0)
data_temp$parquea_categoria <- factor(data_temp$parquea, levels = sort(unique(data_temp$parquea)))
ggplot(data=data_temp) +
  geom_point(aes(x=areaconst,y=preciom, col=parquea_categoria),
             #col='red',
             size=3, alpha = 1/5    )+
  geom_smooth(aes(x=areaconst,y=preciom, linetype=parquea_categoria),
              method = 'lm', se=FALSE, col='#FF0576')+
  facet_wrap(~ parquea_categoria, nrow =4)+
  labs(title = "Gráfico de Relación entre Área y Precio por número de paqueos")+
  theme(panel.background = element_rect(fill = "white"))
## `geom_smooth()` using formula = 'y ~ x'

Se observa que a medida que vamos sumando el número de parqueo la pendiente de precio por área de contrucción es mas inclinada, confirmando la relacción con parqueo

5.2.4 Modelo de vivienda Apartamento por número de habitaciones

ggplot(data=filter(data, tipo == 'Apartamento', parquea > 0)) +
  geom_point(aes(x=areaconst,y=preciom, col=habitac),
             #col='red',
             size=3, alpha = 1/5    )+
#  geom_smooth(aes(x=areaconst,y=preciom, linetype=habitac),
#              method = 'lm', se=FALSE, col='#FF0576')+
  facet_wrap(~ habitac, nrow =4)+
  labs(title = "Gráfico de Relación entre Área y Precio por n° habitaciones")+
  theme(panel.background = element_rect(fill = "white"))

Se podria decir que en la pendiente de los datos, independiente de la habitación se mantiene igual. por lo que creemos q el número de habitaciones podria ser un facot r no tan relevante para nuestro estudio para el casod e los apartamentos

5.3 Modelo de vivienda casa

cor_data <- cor(select(filter(data, tipo == 'casa'), -tipo,-tipo_index, -barrio, -zona, -id,-estrato_categoria))

grafico_n4 <- corPlot(cor_data, 
                      cex = 0.7,
                      xsrt = 75,
                      main = "Matriz de correlación")  # Color de línea entre los números

Se destaca las siguientes correlaciones: * parqueo vuelve a jugar un papel imporante en las correlaciones * habitaciones aparece tener una relación con baños,y no tener relación con el precio, caso similar con el tipo de casa en el modelo inicial.

5.3.1 Modelo de vivienda casa por número de habitaciones

ggplot(data=filter(data, tipo == 'casa', parquea > 0)) +
  geom_point(aes(x=areaconst,y=preciom, col=habitac),
             #col='red',
             size=3, alpha = 1/5    )+
  #geom_smooth(aes(x=areaconst,y=preciom, linetype=habitac),
  #            method = 'lm', se=FALSE, col='#FF0576')+
  facet_wrap(~ habitac, nrow =4)

Se podria decir que en la pendiente de los datos va bajando con el base en el número de habitaciones. por lo que creemos q el número de habitaciones podria ser un factor no tan relevante para nuestro estudio para el caso de las casas

5.4 Modelos de apartamento y casa frente estratos y zonas

Unos de los graficos que determino tendencias muy marcadas por geolocalización fue un comparativo tipo diagrama de burbuja, que me gustaria revisarlo bajo la mirada de los modelos de apartamento y casa

5.4.1 Modelos de casa frente estratos, zonas precio

# Contar registros para cada combinación de "estrato" y "zona"
data_temp <- filter(data, tipo == 'casa', parquea > 0)
conteo <- table(data_temp$estrato_categoria, data_temp$zona)
conteo_df <- as.data.frame(as.table(conteo))
names(conteo_df) <- c("estrato", "zona", "conteo")
diagrama_conteo <- ggplot(conteo_df, aes(x = estrato, y = zona, size= conteo, color = conteo)) +
 geom_point() +
 scale_size_continuous(range = c(5, 20), guide = "none") +
 scale_color_gradient(low = "blue", high = "darkblue") +
 labs(title = "Diagrama de Burbuja de número de observaciones - modelo casa",
      x = "Estrato",
      y = "Zona",
      size = "Conteo")+
 theme_minimal() +
 theme(plot.background = element_rect(fill = "white"),
       panel.grid = element_blank(),
       panel.border = element_blank(),
       plot.title = element_text(size = 16, hjust = 0.5),
       axis.line = element_line(size = 1, color = "#515354"))

promedio_precio <- aggregate(preciom ~ estrato_categoria + zona, data = data_temp, FUN = mean)
tabla_mediaPrecios <- xtabs(preciom ~ estrato_categoria + zona, data = promedio_precio)
mediaPrecios_df <- as.data.frame(as.table(tabla_mediaPrecios))
names(mediaPrecios_df) <- c("estrato", "zona", "MediaPrecios")
diagrama_precio <- ggplot(mediaPrecios_df, aes(x = estrato, y = zona, size= MediaPrecios, color = MediaPrecios)) +
 geom_point() +
 scale_size_continuous(range = c(5, 20), guide = "none") +
 scale_color_gradient(low = "blue", high = "#FF0576") +
 labs(title = "Diagrama de Burbuja de precios en Millones - modelo casa",
      x = "Estrato",
      y = "Zona",
      size = "Precio medio ($milones)")+
 theme_minimal() +
 theme(plot.background = element_rect(fill = "white"),
       panel.grid = element_blank(),
       panel.border = element_blank(),
       plot.title = element_text(size = 16, hjust = 0.5),
       axis.line = element_line(size = 1, color = "#515354"))

grid.arrange(diagrama_conteo, diagrama_precio, ncol = 1,
             heights = c(2, 2))

Observamos comportamientos bien interesantes:

  • La zona sur destaca en el uso de casas, por lo que seria un buen sector nicho de clientes y tiene un promedio favorable de precios.especialmente estratos 4,5 y 6 (coincide con el crecimiente número de proyectos que tiene la ciudad en esta zona)

  • La zona Norte en estratos 4 y 5 seria interesante realizar inversiones en casas, aunque sus precios medios no destacan frente al resto de zonas

  • la zona oriente podria verse en estratos 3,unque el precio de venta no es para nada llamativo.

5.4.2 Modelos de Apartamento frente estratos, zonas precio

# Contar registros para cada combinación de "estrato" y "zona"
data_temp <- filter(data, tipo == 'Apartamento', parquea > 0)
conteo <- table(data_temp$estrato_categoria, data_temp$zona)
conteo_df <- as.data.frame(as.table(conteo))
names(conteo_df) <- c("estrato", "zona", "conteo")
diagrama_conteo <- ggplot(conteo_df, aes(x = estrato, y = zona, size= conteo, color = conteo)) +
 geom_point() +
 scale_size_continuous(range = c(5, 20), guide = "none") +
 scale_color_gradient(low = "blue", high = "darkblue") +
 labs(title = "Diagrama de Burbuja de número de observaciones - modelo apartamento",
      x = "Estrato",
      y = "Zona",
      size = "Conteo")+
 theme_minimal() +
 theme(plot.background = element_rect(fill = "white"),
       panel.grid = element_blank(),
       panel.border = element_blank(),
       plot.title = element_text(size = 16, hjust = 0.5),
       axis.line = element_line(size = 1, color = "#515354"))

promedio_precio <- aggregate(preciom ~ estrato_categoria + zona, data = data_temp, FUN = mean)
tabla_mediaPrecios <- xtabs(preciom ~ estrato_categoria + zona, data = promedio_precio)
mediaPrecios_df <- as.data.frame(as.table(tabla_mediaPrecios))
names(mediaPrecios_df) <- c("estrato", "zona", "MediaPrecios")
diagrama_precio <- ggplot(mediaPrecios_df, aes(x = estrato, y = zona, size= MediaPrecios, color = MediaPrecios)) +
 geom_point() +
 scale_size_continuous(range = c(5, 20), guide = "none") +
 scale_color_gradient(low = "blue", high = "#FF0576") +
 labs(title = "Diagrama de Burbuja de precios en Millones - modelo apartamento",
      x = "Estrato",
      y = "Zona",
      size = "Precio medio ($milones)")+
 theme_minimal() +
 theme(plot.background = element_rect(fill = "white"),
       panel.grid = element_blank(),
       panel.border = element_blank(),
       plot.title = element_text(size = 16, hjust = 0.5),
       axis.line = element_line(size = 1, color = "#515354"))

grid.arrange(diagrama_conteo, diagrama_precio, ncol = 1,
             heights = c(2, 2))

Observamos comportamientos bien interesantes:

  • La zona sur destaca en estratos 4 y 5 , aunque sorprende sus precios, no son tan llamativos

  • la zona oeste es un sector interesante para apartamentos en estratos altos, con una media de precios bastantes

  • la zona norte en apartamentos esta en los estratos 5, con un precio normal medio frente a otras zonas.

6. Conclusiones para la gerencia

Frente a estrategias de marketing que podemos recomendar a partir de este estudio:

  • El precio de las viviendas debe variar según el tipo de propiedad, ya sea apartamento o casa, de manera independiente. [5.1.2]

  • Para ambos modelos, el área de construcción desempeña un papel fundamental en la determinación del precio, y esta área siempre guarda una relación directa con el número de baños. [4.3, 5.2 y 5.3]

  • Para el modelo de apartamento, es evidente que a medida que aumentamos el número de plazas de estacionamiento, la pendiente del precio en función del área de construcción se vuelve más pronunciada, lo que confirma la relación con el estacionamiento. [5.2.3]

  • Para el modelo de casas, aparte del área de construcción, no se identifica ninguna otra variable destacada que podamos ofrecer a nuestros clientes. [5.3.1]

  • En relación a los planes de expansión del negocio inmobiliario, no se sugiere invertir en la zona céntrica, tanto para apartamentos como para casas. [5.4.1 y 5.4.2]

  • En cuanto a la zona Oriente, se proponen planes para casas de estrato 3, con precios asequibles que varían según los metros cuadrados. [5.4.1 y 5.4.2]

  • En la zona oeste se resaltan los apartamentos de estratos altos, ofreciendo precios promedios atractivos en comparación con otras zonas. [5.4.1 y 5.4.2]

  • Las zonas sur y norte, que están en auge en la ciudad de Cali, son adecuadas para proyectos de viviendas, tanto en forma de casas como apartamentos. Sin embargo, en términos generales, sus precios promedios no son tan llamativos (por encima) frente a otras zonas. [5.4.1 y 5.4.2]