library(readxl)
library(dplyr)
library(tidyr)
library(stringr)
library(leaflet)
library(ggplot2)
library(spdep)
library(spatialreg)
library(tidytext)
library(stopwords)
library(wordcloud)
library(RColorBrewer)
library(knitr)
library(kableExtra)
library(scales)

Introduccion

Este reporte analiza el ecosistema turistico y comercial de la Zona Metropolitana de Monterrey (ZMM) mediante visualizacion espacial, modelos de regresion espacial autorregresiva (SAR), analisis de sentimientos y recomendaciones prescriptivas.

Parte A: Visualizacion Espacial

Carga de Datos

airbnb_file <- find_dataset("AirBnB_Data.xlsx")
airbnb_reviews_file <- find_dataset("AirBnB_Raitings_Reviews_Mty.xlsx")
entertainment_file <- find_dataset("zmm_data_entretenimiento.xlsx")

rename_if_exists <- function(df, old, new) {
  if (old %in% names(df) && !new %in% names(df)) {
    names(df)[names(df) == old] <- new
  }
  df
}

# Airbnb
airbnb <- read_excel(airbnb_file, sheet = "Database_Airbnb") %>%
  clean_names_basic()

airbnb <- airbnb %>%
  mutate(
    booking_price = numify(booking_price),
    overall_raiting = numify(overall_raiting),
    number_reviews = numify(number_reviews),
    max_guests = numify(max_guests),
    bedroom = numify(bedroom),
    bath = numify(bath),
    lat = numify(lat),
    lon = numify(lon),
    Dist_km_Downtown = numify(Dist_km_Downtown)
  ) %>%
  filter(!is.na(lat), !is.na(lon), !is.na(booking_price))

# Hoteles
hotels <- read_excel(airbnb_file, sheet = "Database_Hoteles") %>%
  clean_names_basic()

# Arreglo robusto para la columna de calificacion
if (!"Calificacion_1" %in% names(hotels)) {
  posible_cal <- grep("^Cal.*1$", names(hotels), value = TRUE)
  if (length(posible_cal) > 0) {
    names(hotels)[names(hotels) == posible_cal[1]] <- "Calificacion_1"
  } else {
    hotels$Calificacion_1 <- NA_real_
  }
}

hotels <- hotels %>%
  mutate(
    Precio = numify(Precio),
    No_Camas = numify(No_Camas),
    Lat = numify(Lat),
    Lon = numify(Lon),
    Calificacion_1 = numify(Calificacion_1),
    No_Comentarios = numify(No_Comentarios),
    Dist_km_Centro = numify(Dist_km_Centro)
  ) %>%
  filter(!is.na(Lat), !is.na(Lon), !is.na(Precio))

# Reviews adicionales de Airbnb
airbnb_reviews <- read_excel(airbnb_reviews_file, sheet = "Sheet1") %>%
  clean_names_basic()

if ("Dist_km_downtown" %in% names(airbnb_reviews) && !"Dist_km_Downtown" %in% names(airbnb_reviews)) {
  airbnb_reviews <- airbnb_reviews %>% rename(Dist_km_Downtown = Dist_km_downtown)
}

airbnb_reviews <- airbnb_reviews %>%
  mutate(
    booking_price = numify(booking_price),
    overall_raiting = numify(overall_raiting),
    number_reviews = numify(number_reviews),
    max_guests = numify(max_guests),
    bedroom = numify(bedroom),
    bath = numify(bath),
    lat = numify(lat),
    lon = numify(lon),
    Dist_km_Downtown = numify(Dist_km_Downtown)
  ) %>%
  filter(!is.na(lat), !is.na(lon), !is.na(reviews))

# Entretenimiento
restaurants <- read_excel(entertainment_file, sheet = "restaurant") %>% clean_names_basic()
coffee      <- read_excel(entertainment_file, sheet = "coffee shops") %>% clean_names_basic()
bares       <- read_excel(entertainment_file, sheet = "bares & antros") %>% clean_names_basic()
plazas      <- read_excel(entertainment_file, sheet = "plazas comerciales") %>% clean_names_basic()

# Arreglo robusto de nombres con acentos en archivos de entretenimiento
if (!"calificacion_open_table" %in% names(restaurants)) {
  posible <- grep("open_table", names(restaurants), value = TRUE)
  if (length(posible) > 0) names(restaurants)[names(restaurants) == posible[1]] <- "calificacion_open_table"
}

for (obj_name in c("coffee", "bares", "plazas")) {
  obj <- get(obj_name)
  if (!"calificacion" %in% names(obj)) {
    posible <- grep("^cal", names(obj), value = TRUE)
    if (length(posible) > 0) names(obj)[names(obj) == posible[1]] <- "calificacion"
  }
  assign(obj_name, obj)
}

glimpse(airbnb)
## Rows: 250
## Columns: 16
## $ property_id      <chr> "ab-595200358660223000", "ab-46511094", "ab-54194714"…
## $ counts           <dbl> 90, 185, 78, 167, 257, 303, 84, 128, 86, 116, 97, 128…
## $ booking_price    <dbl> 2119, 2058, 1432, 4904, 2863, 1804, 3329, 931, 1930, …
## $ room_type        <chr> "private room", "private room", "entire loft", "entir…
## $ number_nights    <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2…
## $ overall_raiting  <dbl> 4.78, 4.74, 4.81, 4.97, 4.85, 4.71, 4.97, 4.66, 4.76,…
## $ number_reviews   <dbl> 225, 213, 146, 194, 291, 315, 287, 141, 135, 192, 97,…
## $ max_guests       <dbl> 2, 2, 3, 4, 6, 2, 3, 2, 4, 2, 2, 2, 2, 2, 6, 5, 2, 2,…
## $ bedroom          <dbl> 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1,…
## $ beds             <dbl> 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1,…
## $ bath             <dbl> 1.0, 1.0, 1.0, 2.0, 1.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0…
## $ lat              <dbl> 25.64840, 25.66249, 25.71809, 25.67600, 25.72078, 25.…
## $ lon              <dbl> -100.3030, -100.3320, -100.3348, -100.3716, -100.3658…
## $ Dist_km_Downtown <dbl> 4.4444472, 3.1197327, 3.9727067, 3.7051499, 6.2602638…
## $ Municipio        <chr> "Monterrey", "Monterrey", "Monterrey", "Monterrey", "…
## $ reviews          <chr> "mi estancia estuvo muy bien, el lugar muy agradable …
glimpse(hotels)
## Rows: 60
## Columns: 20
## $ Municipio               <chr> "Monterrey", "Monterrey", "Monterrey", "Monter…
## $ Hotel                   <chr> "City Express Plus by Marriott Monterrey Nuevo…
## $ Habitaci_on_No_Personas <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2…
## $ No_Noches               <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2…
## $ Precio                  <dbl> 4912, 3094, 3602, 2976, 2049, 4520, 1658, 3232…
## $ No_Camas                <dbl> 1, 1, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1…
## $ No_Estrellas            <chr> "4", "3", "3", "4", "3", "4", "Sin Información…
## $ Ubicaci_on              <chr> "Ave. Revolución #2703 Col. La Ladrillera, 648…
## $ Lat                     <dbl> 25.65074, 25.68363, 25.68704, 25.66859, 25.684…
## $ Lon                     <dbl> -100.3317, -100.3107, -100.3278, -100.3337, -1…
## $ Calificacion_1          <dbl> 8.0, 8.0, 8.4, 8.0, 6.6, 8.9, 6.3, 8.3, 9.1, 8…
## $ Calificaci_on_2         <chr> "Muy bien", "Muy bien", "Muy bien", "Muy bien"…
## $ No_Comentarios          <dbl> 715, 1968, 995, 3288, 899, 472, 47, 3451, 466,…
## $ Comentarios_1           <chr> "“La atención al cliente, las habitaciones y l…
## $ Comentarios_2           <chr> "“La atención de Lobby , el señor de limpieza …
## $ Comentarios_3           <chr> "Me encantó que tengan servicio de café de cor…
## $ Alberca_Spa             <chr> "No", "No", "Si", "Si", "No", "Si", "No", "No"…
## $ Gimnasio                <chr> "Si", "Si", "Si", "Si", "No", "Si", "No", "Si"…
## $ Restaurante_Bar         <chr> "Si", "Si", "Si", "Si", "Si", "No", "No", "Si"…
## $ Dist_km_Centro          <dbl> 4.2836295, 0.6369406, 1.1723978, 2.6656509, 0.…
glimpse(airbnb_reviews)
## Rows: 249
## Columns: 16
## $ property_id      <chr> "ab-595200358660223000", "ab-46511094", "ab-54194714"…
## $ counts           <dbl> 90, 185, 78, 167, 257, 303, 84, 128, 86, 116, 97, 128…
## $ booking_price    <dbl> 2119, 2058, 1432, 4904, 2863, 1804, 3329, 931, 1930, …
## $ room_type        <chr> "private room", "private room", "entire loft", "entir…
## $ number_nights    <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2…
## $ overall_raiting  <dbl> 4.78, 4.74, 4.81, 4.97, 4.85, 4.71, 4.97, 4.66, 4.76,…
## $ number_reviews   <dbl> 225, 213, 146, 194, 291, 315, 287, 141, 135, 192, 97,…
## $ max_guests       <dbl> 2, 2, 3, 4, 6, 2, 3, 2, 4, 2, 2, 2, 2, 2, 6, 5, 2, 2,…
## $ bedroom          <dbl> 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1,…
## $ beds             <dbl> 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1,…
## $ bath             <dbl> 1.0, 1.0, 1.0, 2.0, 1.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0…
## $ lat              <dbl> 25.64840, 25.66249, 25.71809, 25.67600, 25.72078, 25.…
## $ lon              <dbl> -100.3030, -100.3320, -100.3348, -100.3716, -100.3658…
## $ Dist_km_Downtown <dbl> 4.4444472, 3.1197327, 3.9727067, 3.7051499, 6.2602638…
## $ Municipio        <chr> "Monterrey", "Monterrey", "Monterrey", "Monterrey", "…
## $ reviews          <chr> "mi estancia estuvo muy bien, el lugar muy agradable …

1. Mapeo de Ubicaciones

clean_coords <- function(df, lat_col, lon_col) {
  df %>%
    mutate(
      .lat = numify(.data[[lat_col]]),
      .lon = numify(.data[[lon_col]])
    ) %>%
    filter(
      !is.na(.lat), !is.na(.lon),
      .lat >= -90, .lat <= 90,
      .lon >= -180, .lon <= 180
    )
}

safe_add_markers <- function(map, data, color, radius, popup_col, group_name, label_prefix) {
  if (nrow(data) == 0) return(map)
  if (!popup_col %in% names(data)) data[[popup_col]] <- group_name
  map %>%
    addCircleMarkers(
      data = data,
      lng = ~.lon,
      lat = ~.lat,
      color = color,
      radius = radius,
      fillOpacity = 0.8,
      stroke = FALSE,
      popup = as.formula(paste0("~paste0('<b>", label_prefix, ":</b> ', ", popup_col, ")")),
      group = group_name
    )
}

airbnb_map      <- clean_coords(airbnb, "lat", "lon")
hotels_map      <- clean_coords(hotels, "Lat", "Lon")
restaurants_map <- clean_coords(restaurants, "latitud", "longitud")
plazas_map      <- clean_coords(plazas, "latitud", "longitud")
coffee_map      <- clean_coords(coffee, "latitud", "longitud")
bares_map       <- clean_coords(bares, "latitud", "longitud")

m <- leaflet() %>% addProviderTiles(providers$CartoDB.Positron)

m <- safe_add_markers(m, airbnb_map, "#E74C3C", 5, "property_id", "Airbnb", "Airbnb")
m <- safe_add_markers(m, hotels_map, "#3498DB", 6, "Hotel", "Hoteles", "Hotel")
m <- safe_add_markers(m, restaurants_map, "#2ECC71", 5, "nombre_negocio", "Restaurantes", "Restaurante")
m <- safe_add_markers(m, plazas_map, "#F39C12", 7, "nombre", "Plazas Comerciales", "Plaza")
m <- safe_add_markers(m, coffee_map, "#9B59B6", 4, "nombre_negocio", "Cafeterias", "Cafeteria")
m <- safe_add_markers(m, bares_map, "#E91E63", 4, "nombre", "Bares & Antros", "Bar/Antro")

m %>%
  addLayersControl(
    overlayGroups = c("Airbnb", "Hoteles", "Restaurantes", "Plazas Comerciales", "Cafeterias", "Bares & Antros"),
    options = layersControlOptions(collapsed = FALSE)
  ) %>%
  addLegend(
    position = "bottomright",
    colors = c("#E74C3C", "#3498DB", "#2ECC71", "#F39C12", "#9B59B6", "#E91E63"),
    labels = c("Airbnb", "Hoteles", "Restaurantes", "Plazas", "Cafeterias", "Bares"),
    title = "Tipo de lugar"
  )

Interpretacion

Al analizar el mapa de la Zona Metropolitana de Monterrey, observamos que existe una concentracion importante de alojamientos y establecimientos comerciales en las zonas mas urbanizadas y con mayor actividad economica. Los hoteles se encuentran principalmente cerca del centro y de corredores comerciales consolidados, lo que les permite ofrecer un acceso mas directo a servicios y puntos de interes. Por otro lado, los alojamientos de Airbnb presentan una distribucion mas amplia, llegando a zonas residenciales y areas perifericas. Esto sugiere que Airbnb ofrece una mayor diversidad de ubicaciones para distintos perfiles de visitantes, mientras que los hoteles mantienen una estrategia enfocada en zonas con alta demanda turistica y empresarial.

2. Analisis de Isocronas

# Aproximacion visual de accesibilidad con distancia al centro disponible en los datos.
access_summary <- bind_rows(
  airbnb %>% transmute(tipo = "Airbnb", distancia_centro = Dist_km_Downtown),
  hotels %>% transmute(tipo = "Hoteles", distancia_centro = Dist_km_Centro)
) %>%
  filter(!is.na(distancia_centro))

access_summary %>%
  group_by(tipo) %>%
  summarise(
    n = n(),
    distancia_promedio = mean(distancia_centro, na.rm = TRUE),
    distancia_mediana = median(distancia_centro, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  kable(caption = "Comparacion de distancia al centro por tipo de alojamiento") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Comparacion de distancia al centro por tipo de alojamiento
tipo n distancia_promedio distancia_mediana
Airbnb 250 3.714308 3.095493
Hoteles 60 2.596732 2.322010
ggplot(access_summary, aes(x = tipo, y = distancia_centro, fill = tipo)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Accesibilidad aproximada por distancia al centro",
       x = "Tipo de alojamiento", y = "Distancia al centro (km)") +
  theme_minimal() +
  theme(legend.position = "none")

Interpretacion de accesibilidad

Los resultados muestran que los hoteles se encuentran, en promedio, mas cerca del centro de la ciudad que los alojamientos de Airbnb. La distancia promedio para los hoteles fue de aproximadamente 2.6 km, mientras que para Airbnb fue de 3.7 km. Esto indica que los hoteles suelen ubicarse en zonas con mejor acceso a servicios, comercios y atractivos turisticos. En contraste, Airbnb presenta una mayor dispersion geografica, lo que permite ofrecer opciones en diferentes sectores de la ciudad y atender necesidades mas especificas de los visitantes. Esta diferencia representa una oportunidad para que cada tipo de alojamiento compita mediante propuestas de valor distintas segun su ubicacion.

Parte B: Regresion Espacial (SAR)

3. Modelo Autorregresivo Espacial

airbnb_model <- airbnb %>%
  select(booking_price, overall_raiting, number_reviews, Dist_km_Downtown,
         max_guests, bedroom, bath, lat, lon, Municipio) %>%
  mutate(across(c(booking_price, overall_raiting, number_reviews, Dist_km_Downtown,
                  max_guests, bedroom, bath, lat, lon), numify)) %>%
  filter(complete.cases(.))

coords_airbnb <- cbind(airbnb_model$lon, airbnb_model$lat)
k_airbnb <- min(5, nrow(airbnb_model) - 1)
nb_airbnb <- knn2nb(knearneigh(coords_airbnb, k = k_airbnb))
lw_airbnb <- nb2listw(nb_airbnb, style = "W", zero.policy = TRUE)

moran_airbnb <- moran.test(airbnb_model$booking_price, lw_airbnb, zero.policy = TRUE)
print(moran_airbnb)
## 
##  Moran I test under randomisation
## 
## data:  airbnb_model$booking_price  
## weights: lw_airbnb    
## 
## Moran I statistic standard deviate = 3.7684, p-value = 8.215e-05
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##       0.128277193      -0.004016064       0.001232431
sar_airbnb <- lagsarlm(
  booking_price ~ overall_raiting + number_reviews + Dist_km_Downtown +
    max_guests + bedroom + bath,
  data = airbnb_model,
  listw = lw_airbnb,
  zero.policy = TRUE
)

summary(sar_airbnb)
## 
## Call:lagsarlm(formula = booking_price ~ overall_raiting + number_reviews + 
##     Dist_km_Downtown + max_guests + bedroom + bath, data = airbnb_model, 
##     listw = lw_airbnb, zero.policy = TRUE)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -3053.10  -599.17  -223.17   312.25 13618.19 
## 
## Type: lag 
## Coefficients: (asymptotic standard errors) 
##                     Estimate  Std. Error z value  Pr(>|z|)
## (Intercept)      -1298.54318  2111.77748 -0.6149  0.538617
## overall_raiting     26.58394   447.26078  0.0594  0.952604
## number_reviews       0.20441     0.93645  0.2183  0.827214
## Dist_km_Downtown    97.58408    44.11527  2.2120  0.026965
## max_guests         201.42718    61.85853  3.2563  0.001129
## bedroom            408.30505   250.31012  1.6312  0.102849
## bath              1339.41838   300.09651  4.4633 8.071e-06
## 
## Rho: 0.22119, LR test value: 6.9075, p-value: 0.0085833
## Asymptotic standard error: 0.07425
##     z-value: 2.979, p-value: 0.0028915
## Wald statistic: 8.8747, p-value: 0.0028915
## 
## Log likelihood: -2190.104 for lag model
## ML residual variance (sigma squared): 2363100, (sigma: 1537.2)
## Number of observations: 250 
## Number of parameters estimated: 9 
## AIC: 4398.2, (AIC for lm: 4403.1)
## LM test for residual autocorrelation
## test value: 11.519, p-value: 0.00068884
hotels_model <- hotels %>%
  mutate(
    tiene_alberca = ifelse(str_to_lower(Alberca_Spa) %in% c("si", "sí"), 1, 0),
    tiene_gimnasio = ifelse(str_to_lower(Gimnasio) %in% c("si", "sí"), 1, 0),
    tiene_restaurante = ifelse(str_to_lower(Restaurante_Bar) %in% c("si", "sí"), 1, 0)
  ) %>%
  select(Precio, Calificacion_1, No_Camas, Dist_km_Centro, tiene_alberca,
         tiene_gimnasio, tiene_restaurante, Lat, Lon, Municipio, Hotel) %>%
  mutate(across(c(Precio, Calificacion_1, No_Camas, Dist_km_Centro, tiene_alberca,
                  tiene_gimnasio, tiene_restaurante, Lat, Lon), numify)) %>%
  filter(complete.cases(.))

coords_hotels <- cbind(hotels_model$Lon, hotels_model$Lat)
k_hotels <- min(5, nrow(hotels_model) - 1)
nb_hotels <- knn2nb(knearneigh(coords_hotels, k = k_hotels))
lw_hotels <- nb2listw(nb_hotels, style = "W", zero.policy = TRUE)

moran_hotels <- moran.test(hotels_model$Precio, lw_hotels, zero.policy = TRUE)
print(moran_hotels)
## 
##  Moran I test under randomisation
## 
## data:  hotels_model$Precio  
## weights: lw_hotels    
## 
## Moran I statistic standard deviate = 3.044, p-value = 0.001167
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##       0.195589140      -0.016949153       0.004875161
sar_hotels <- lagsarlm(
  Precio ~ Calificacion_1 + No_Camas + Dist_km_Centro +
    tiene_alberca + tiene_gimnasio + tiene_restaurante,
  data = hotels_model,
  listw = lw_hotels,
  zero.policy = TRUE
)

summary(sar_hotels)
## 
## Call:lagsarlm(formula = Precio ~ Calificacion_1 + No_Camas + Dist_km_Centro + 
##     tiene_alberca + tiene_gimnasio + tiene_restaurante, data = hotels_model, 
##     listw = lw_hotels, zero.policy = TRUE)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -1663.195  -869.456    73.474   658.261  2322.205 
## 
## Type: lag 
## Coefficients: (asymptotic standard errors) 
##                    Estimate Std. Error z value Pr(>|z|)
## (Intercept)       -2221.999   1476.618 -1.5048 0.132378
## Calificacion_1      557.782    180.947  3.0826 0.002052
## No_Camas            -21.782    226.802 -0.0960 0.923489
## Dist_km_Centro      245.234     87.441  2.8046 0.005039
## tiene_alberca       722.423    297.726  2.4265 0.015246
## tiene_gimnasio      841.681    325.277  2.5876 0.009665
## tiene_restaurante   395.334    301.292  1.3121 0.189476
## 
## Rho: -0.053978, LR test value: 0.12181, p-value: 0.72708
## Asymptotic standard error: 0.14934
##     z-value: -0.36143, p-value: 0.71777
## Wald statistic: 0.13064, p-value: 0.71777
## 
## Log likelihood: -494.8655 for lag model
## ML residual variance (sigma squared): 853640, (sigma: 923.93)
## Number of observations: 60 
## Number of parameters estimated: 9 
## AIC: 1007.7, (AIC for lm: 1005.9)
## LM test for residual autocorrelation
## test value: 5.7651e-06, p-value: 0.99808
rho_airbnb <- sar_airbnb$rho
rho_hotels <- sar_hotels$rho
moran_airbnb_i <- moran_airbnb$estimate[["Moran I statistic"]]
moran_airbnb_p <- moran_airbnb$p.value
moran_hotels_i <- moran_hotels$estimate[["Moran I statistic"]]
moran_hotels_p <- moran_hotels$p.value

sar_summary <- data.frame(
  Modelo = c("Airbnb", "Hoteles"),
  Moran_I = c(moran_airbnb_i, moran_hotels_i),
  P_value_Moran = c(moran_airbnb_p, moran_hotels_p),
  Rho_SAR = c(rho_airbnb, rho_hotels)
)

sar_summary %>%
  mutate(across(where(is.numeric), ~round(.x, 4))) %>%
  kable(caption = "Resumen de autocorrelacion espacial y coeficiente SAR") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Resumen de autocorrelacion espacial y coeficiente SAR
Modelo Moran_I P_value_Moran Rho_SAR
Airbnb 0.1283 0.0001 0.2212
Hoteles 0.1956 0.0012 -0.0540

Interpretacion de Moran’s I y SAR (Airbnb)

Antes de estimar el modelo espacial, realizamos la prueba de Moran’s I para identificar si existian patrones geograficos en los precios de Airbnb. El resultado obtenido fue de 0.128 con un valor p menor a 0.01, lo que indica que los precios presentan una autocorrelacion espacial significativa. En otras palabras, las propiedades con precios altos suelen ubicarse cerca de otras propiedades con precios altos, mientras que los precios bajos tambien tienden a concentrarse entre si.

Al estimar el modelo SAR, encontramos que el coeficiente espacial (rho = 0.221) es positivo y significativo. Esto significa que el precio de una propiedad no depende unicamente de sus caracteristicas internas, sino tambien de los precios observados en las propiedades cercanas. Ademas, las variables que mostraron una influencia importante sobre el precio fueron el numero maximo de huespedes y la cantidad de banos. Esto sugiere que los huespedes estan dispuestos a pagar mas por alojamientos con mayor capacidad y mejores amenidades.

Interpretacion de Moran’s I y SAR (Hoteles)

En el caso de los hoteles, la prueba de Moran’s I tambien confirmo la existencia de patrones espaciales en los precios, obteniendo un valor de 0.196 con significancia estadistica. Esto indica que los hoteles con precios similares tienden a ubicarse en areas cercanas dentro de la ciudad.

Sin embargo, al estimar el modelo SAR, el coeficiente espacial resulto muy pequeno y no significativo (rho = -0.054). Esto sugiere que, una vez consideradas las caracteristicas del hotel, el efecto de los precios de establecimientos vecinos pierde relevancia. Los factores que mostraron mayor impacto sobre el precio fueron la calificacion del hotel, la presencia de alberca y la disponibilidad de gimnasio. En consecuencia, la calidad percibida y las amenidades ofrecidas parecen ser elementos mas importantes para explicar los precios hoteleros que la influencia de hoteles cercanos.

4. Comparacion de Modelos: SAR vs OLS

ols_airbnb <- lm(
  booking_price ~ overall_raiting + number_reviews + Dist_km_Downtown +
    max_guests + bedroom + bath,
  data = airbnb_model
)
summary(ols_airbnb)
## 
## Call:
## lm(formula = booking_price ~ overall_raiting + number_reviews + 
##     Dist_km_Downtown + max_guests + bedroom + bath, data = airbnb_model)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -3165.4  -654.6  -217.6   314.1 13728.2 
## 
## Coefficients:
##                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      -1290.6120  2179.7089  -0.592  0.55433    
## overall_raiting    134.7190   461.2291   0.292  0.77047    
## number_reviews       0.2615     0.9650   0.271  0.78664    
## Dist_km_Downtown    91.9370    44.4283   2.069  0.03957 *  
## max_guests         201.3757    63.8416   3.154  0.00181 ** 
## bedroom            483.5575   255.4664   1.893  0.05957 .  
## bath              1332.7597   309.6294   4.304 2.43e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1587 on 243 degrees of freedom
## Multiple R-squared:  0.4102, Adjusted R-squared:  0.3956 
## F-statistic: 28.17 on 6 and 243 DF,  p-value: < 2.2e-16
ols_hotels <- lm(
  Precio ~ Calificacion_1 + No_Camas + Dist_km_Centro +
    tiene_alberca + tiene_gimnasio + tiene_restaurante,
  data = hotels_model
)
summary(ols_hotels)
## 
## Call:
## lm(formula = Precio ~ Calificacion_1 + No_Camas + Dist_km_Centro + 
##     tiene_alberca + tiene_gimnasio + tiene_restaurante, data = hotels_model)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1659.70  -844.61    54.15   639.59  2301.66 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)   
## (Intercept)       -2342.11    1563.59  -1.498  0.14009   
## Calificacion_1      553.65     189.04   2.929  0.00501 **
## No_Camas            -23.40     241.57  -0.097  0.92321   
## Dist_km_Centro      232.55      83.84   2.774  0.00763 **
## tiene_alberca       723.23     316.89   2.282  0.02652 * 
## tiene_gimnasio      816.95     342.94   2.382  0.02083 * 
## tiene_restaurante   404.59     319.16   1.268  0.21045   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 984.2 on 53 degrees of freedom
## Multiple R-squared:  0.5785, Adjusted R-squared:  0.5307 
## F-statistic: 12.12 on 6 and 53 DF,  p-value: 1.581e-08
model_comparison <- data.frame(
  Modelo = c("OLS Airbnb", "SAR Airbnb", "OLS Hoteles", "SAR Hoteles"),
  AIC = c(AIC(ols_airbnb), AIC(sar_airbnb), AIC(ols_hotels), AIC(sar_hotels)),
  Log_Lik = c(as.numeric(logLik(ols_airbnb)), as.numeric(logLik(sar_airbnb)),
              as.numeric(logLik(ols_hotels)), as.numeric(logLik(sar_hotels)))
)

model_comparison %>%
  mutate(across(where(is.numeric), ~round(.x, 2))) %>%
  kable(caption = "Comparacion de modelos OLS vs SAR") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Comparacion de modelos OLS vs SAR
Modelo AIC Log_Lik
OLS Airbnb 4403.12 -2193.56
SAR Airbnb 4398.21 -2190.10
OLS Hoteles 1005.85 -494.93
SAR Hoteles 1007.73 -494.87

Al comparar los modelos OLS y SAR para Airbnb, observamos que el modelo SAR presenta un AIC ligeramente menor, lo que indica un mejor ajuste a los datos. Esto confirma que incorporar la dimension espacial ayuda a explicar de forma mas adecuada la variaci??n de los precios en este mercado. Los resultados sugieren que la ubicacion y las condiciones del entorno tienen un papel importante en la determinacion del precio de los alojamientos de Airbnb.

Por el contrario, para los hoteles el modelo OLS mostro un desempe??o similar e incluso ligeramente mejor que el SAR. Esto indica que la influencia espacial es menos relevante en este segmento y que las caracteristicas propias de cada hotel explican gran parte de las diferencias de precio observadas. Por lo tanto, la incorporacion del componente espacial genera beneficios limitados en comparacipn con Airbnb.

Parte C: Analisis de Sentimientos de Resenas

5. Preprocesamiento de Texto

reviews_rest <- restaurants %>%
  transmute(
    nombre = nombre_negocio,
    municipio = municipio,
    lat = numify(latitud),
    lon = numify(longitud),
    calificacion = numify(calificacion_open_table),
    `review 1` = `review_1`,
    `review 2` = `review_2`,
    `review 3` = `review_3`,
    tipo = "Restaurante"
  )

reviews_plazas <- plazas %>%
  transmute(
    nombre = nombre,
    municipio = municipio,
    lat = numify(latitud),
    lon = numify(longitud),
    calificacion = numify(calificacion),
    `review 1` = `review_1`,
    `review 2` = `review_2`,
    `review 3` = `review_3`,
    tipo = "Plaza Comercial"
  )

reviews_airbnb <- airbnb_reviews %>%
  transmute(
    nombre = property_id,
    municipio = Municipio,
    lat = numify(lat),
    lon = numify(lon),
    calificacion = numify(overall_raiting),
    `review 1` = reviews,
    `review 2` = NA_character_,
    `review 3` = NA_character_,
    tipo = "Airbnb"
  )

reviews_all <- bind_rows(reviews_rest, reviews_plazas, reviews_airbnb) %>%
  unite("texto_completo", `review 1`, `review 2`, `review 3`, sep = " ", na.rm = TRUE) %>%
  mutate(id = row_number(), texto_completo = str_to_lower(texto_completo)) %>%
  filter(!is.na(lat), !is.na(lon), texto_completo != "")

stopwords_es <- tibble(word = stopwords("es"))

reviews_tokens <- reviews_all %>%
  select(id, texto_completo) %>%
  unnest_tokens(word, texto_completo) %>%
  mutate(
    word = str_remove_all(word, "[^[:alpha:]áéíóúñü]"),
    word = str_squish(word)
  ) %>%
  filter(nchar(word) > 2) %>%
  anti_join(stopwords_es, by = "word")

reviews_tokens %>%
  count(word, sort = TRUE) %>%
  slice_head(n = 20) %>%
  kable(caption = "Top 20 palabras mas frecuentes en resenas") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Top 20 palabras mas frecuentes en resenas
word n
excelente 171
lugar 139
buena 79
servicio 74
buen 73
comida 69
bien 61
atención 52
the 46
ubicación 44
and 36
siempre 34
agradable 30
ambiente 28
cerca 28
zona 28
calidad 25
deliciosa 25
estancia 25
recomendable 22

El preprocesamiento elimina ruido, estandariza texto y permite que las palabras con carga emocional sean identificadas con mayor precision.

6. Mapeo de Sentimientos

palabras_positivas <- c(
  "excelente", "bueno", "buena", "buen", "increible", "perfecto", "perfecta",
  "recomendado", "recomendable", "recomiendo", "rico", "rica", "agradable", "genial",
  "delicioso", "deliciosa", "rapido", "rapida", "amable", "limpio", "limpia",
  "fantastico", "fantastica", "maravilloso", "maravillosa", "espectacular",
  "atencion", "fresco", "fresca", "comodo", "comoda", "bonito", "bonita", "seguro", "segura"
)

palabras_negativas <- c(
  "malo", "mala", "pesimo", "pesima", "caro", "cara", "lento", "lenta", "sucio", "sucia",
  "horrible", "terrible", "tardado", "tardada", "frio", "fria", "grosero", "grosera",
  "decepcionante", "ruidoso", "ruidosa", "pequeno", "pequena", "incomodo", "incomoda",
  "suciedad", "mal", "tarde", "cancelacion"
)

lexicon_es <- bind_rows(
  tibble(word = palabras_positivas, sentiment = "positive"),
  tibble(word = palabras_negativas, sentiment = "negative")
)

sentiment_scores <- reviews_tokens %>%
  inner_join(lexicon_es, by = "word") %>%
  count(id, sentiment) %>%
  pivot_wider(names_from = sentiment, values_from = n, values_fill = 0)

if (!"positive" %in% names(sentiment_scores)) sentiment_scores$positive <- 0
if (!"negative" %in% names(sentiment_scores)) sentiment_scores$negative <- 0

sentiment_scores <- sentiment_scores %>%
  mutate(sentiment_score = positive - negative)

sentiment_map_data <- reviews_all %>%
  select(id, nombre, municipio, lat, lon, tipo, calificacion) %>%
  left_join(sentiment_scores, by = "id") %>%
  mutate(
    positive = replace_na(positive, 0),
    negative = replace_na(negative, 0),
    sentiment_score = replace_na(sentiment_score, 0)
  )

sentiment_map_data %>%
  arrange(desc(sentiment_score)) %>%
  select(nombre, tipo, municipio, positive, negative, sentiment_score, calificacion) %>%
  kable(caption = "Puntajes de sentimiento por lugar") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Puntajes de sentimiento por lugar
nombre tipo municipio positive negative sentiment_score calificacion
Taberna Atope - Pueblo Serena Restaurante Monterrey 8 0 8 4.90
Rincão Brasil Restaurante Santa Catarina 8 0 8 4.50
Gallo 71 Restaurante San Pedro 7 0 7 4.70
Catrinas Chilaquiles San Nicolas Restaurante San Nicolás 7 0 7 4.50
LA FAENA Restaurante Guadalupe 6 0 6 4.50
Los Agaves Restaurante Guadalupe 6 0 6 4.50
Los Toreados del Álamo Suc. Las Américas Restaurante Guadalupe 6 0 6 4.50
La Torrada Restaurante San Pedro 6 0 6 4.80
Koli Cocina de Origen Restaurante San Pedro 6 0 6 4.80
Cenacolo Restaurante San Pedro 6 0 6 4.60
Los Nogales Restaurante San Nicolás 6 0 6 4.70
Los Legendarios Suc. Universidad Restaurante San Nicolás 6 0 6 4.60
La Karlota - Santa Catarina Restaurante Santa Catarina 6 0 6 4.60
Catrinas Chilaquiles Escobedo Restaurante Escobedo 6 0 6 4.70
La fonda chiquita Restaurante Escobedo 6 0 6 4.60
Esquina Bajan Chilaquiles Restaurante Guadalupe 5 0 5 4.70
Cuerno Restaurante San Pedro 5 0 5 4.70
La Buena Barra Restaurante San Pedro 5 0 5 4.50
Mr. Pampas - Mty. Citadel Restaurante San Nicolás 5 0 5 4.80
El Gusto de Puebla Restaurante San Nicolás 5 0 5 4.50
DOLCE BISQUET - VIA CORDILLERA Restaurante Santa Catarina 5 0 5 4.90
LA ANACUA STA.CATARINA Restaurante Santa Catarina 5 0 5 4.60
La Pasadita Buffet Restaurante Santa Catarina 5 0 5 4.50
FRIES N’ FRIES - El Paseo Santa Catarina Restaurante Santa Catarina 5 0 5 4.50
El Sazon de la Esquina Restaurante Escobedo 5 0 5 4.90
Punta Pérula Restaurant Restaurante Escobedo 5 0 5 4.60
Bella Restaurant Restaurante Escobedo 5 0 5 4.60
Galerías Monterrey Plaza Comercial Monterrey 5 0 5 4.60
Plaza las Torres Plaza Comercial Monterrey 5 0 5 4.20
Fashion Drive Mty Plaza Comercial San Pedro 5 0 5 4.70
Plaza Sendero Santa Catarina Plaza Comercial Santa Catarina 5 0 5 4.60
Taberna Atope - Barrio Antiguo Restaurante Monterrey 4 0 4 4.70
Pizza Studio Restaurante Monterrey 4 0 4 4.70
Laly’s Restaurante Restaurante Guadalupe 4 0 4 4.40
Las Bugambilias Cocina Mexicana Restaurante Guadalupe 4 0 4 4.40
Mar del Zur Restaurante San Pedro 4 0 4 4.90
Pangea Restaurante San Pedro 5 1 4 4.90
Los Hidalgos - Lázaro Cárdenas Restaurante San Pedro 4 0 4 4.80
Los Primos Restaurante (Plaza Aviana) Restaurante Escobedo 4 0 4 4.70
MIMA CHILAQUILES Restaurante Escobedo 4 0 4 4.50
Plaza Nuevo Sur Plaza Comercial Monterrey 5 1 4 4.60
Plaza La Silla Plaza Comercial Monterrey 4 0 4 4.00
Plaza las Fuentes Plaza Comercial Escobedo 4 0 4 4.50
ab-46066827 Airbnb Monterrey 4 0 4 4.87
ab-33718164 Airbnb Monterrey 4 0 4 4.96
ab-44446883 Airbnb Monterrey 4 0 4 4.72
ab-53704111 Airbnb Monterrey 4 0 4 4.84
El As de la Silla Restaurante Guadalupe 3 0 3 4.90
Ragnarok Restaurant & Bar Suc Guadalupe Restaurante Guadalupe 3 0 3 4.70
El Garza Cocina de Tradición Restaurante Guadalupe 4 1 3 4.40
Holsteins Restaurante San Pedro 4 1 3 4.60
El Lugar de Max Las Puentes Restaurante San Nicolás 3 0 3 4.70
Maria’s Roots Restaurante San Nicolás 3 0 3 4.70
Los Legendarios - Romulo Garza Restaurante San Nicolás 3 0 3 4.50
Sirloin Stockade Santa Catarina Restaurante Santa Catarina 3 0 3 4.70
Multiplaza Lindavista Plaza Comercial Guadalupe 3 0 3 4.40
Plaza Uno Plaza Comercial Guadalupe 3 0 3 4.30
Punto Valle, The Town Center Plaza Comercial San Pedro 3 0 3 4.60
PLAZA LAS ROCAS Plaza Comercial San Pedro 3 0 3 4.40
Paseo Almenares Plaza Comercial San Nicolás 3 0 3 4.60
Plaza Citadel Plaza Comercial San Nicolás 3 0 3 4.60
Plaza Via Cordillera Plaza Comercial Santa Catarina 3 0 3 4.70
Plaza Clouthier Plaza Comercial Santa Catarina 3 0 3 4.50
Mi Plaza Escobedo Plaza Comercial Escobedo 3 0 3 4.50
Paseo Las Torres Plaza Comercial Escobedo 3 0 3 4.20
ab-51755946 Airbnb Monterrey 3 0 3 4.76
ab-32329550 Airbnb Monterrey 3 0 3 4.82
ab-50707235 Airbnb Monterrey 3 0 3 4.74
ab-589026511449325000 Airbnb Monterrey 3 0 3 4.85
ab-50702963 Airbnb Monterrey 3 0 3 4.75
ab-45480018 Airbnb Monterrey 3 0 3 4.74
ab-19038148 Airbnb Monterrey 3 0 3 4.81
ab-52556064 Airbnb Monterrey 3 0 3 4.96
ab-41861790 Airbnb Monterrey 3 0 3 4.90
ab-53150322 Airbnb Monterrey 3 0 3 4.82
ab-28630370 Airbnb Monterrey 3 0 3 4.80
ab-44138253 Airbnb Monterrey 3 0 3 4.57
ab-53891892 Airbnb Monterrey 3 0 3 4.74
ab-37996689 Airbnb Monterrey 3 0 3 4.66
ab-6562108 Airbnb Monterrey 3 0 3 4.84
ab-31210372 Airbnb Monterrey 3 0 3 4.83
La Nacional - San Jerónimo Restaurante Monterrey 2 0 2 4.80
Los Arcos - Morones Prieto Restaurante Monterrey 2 0 2 4.80
Il Gallo Nero Trattoria Pizzeria Restaurante Monterrey 2 0 2 4.80
Botanero Moritas - Santa Lucía Restaurante Monterrey 2 0 2 4.70
Los Legendarios Suc. Fleteros Restaurante Monterrey 2 0 2 4.60
Aletzio’s Pizzeria y Ristorante Restaurante San Nicolás 2 0 2 4.80
La Horda Bar Arcade Sanico Restaurante San Nicolás 2 0 2 4.60
Mercado Cordillera Restaurante Santa Catarina 2 0 2 4.70
Los Legendarios - Santa Catarina Restaurante Santa Catarina 2 0 2 4.70
Aromali Cerradas de Anáhuac Restaurante Escobedo 2 0 2 5.00
Restaurant Chayito Restaurante Escobedo 2 0 2 4.80
La Patrona Antojeria Restaurante Escobedo 3 1 2 4.80
Paseo Tec Plaza Comercial Monterrey 2 0 2 4.50
Plaza Fiesta Anáhuac Plaza Comercial San Nicolás 2 0 2 4.50
Plaza Los Girasoles Plaza Comercial Escobedo 2 0 2 4.10
ab-36805159 Airbnb Monterrey 2 0 2 4.66
ab-25033863 Airbnb Monterrey 2 0 2 4.86
ab-50156206 Airbnb Monterrey 2 0 2 4.63
ab-41616619 Airbnb Monterrey 2 0 2 4.92
ab-42580151 Airbnb Monterrey 2 0 2 4.86
ab-41887115 Airbnb Monterrey 2 0 2 4.70
ab-41060877 Airbnb Monterrey 2 0 2 4.49
ab-23855645 Airbnb Monterrey 2 0 2 4.94
ab-45760291 Airbnb Monterrey 2 0 2 4.92
ab-31266463 Airbnb Monterrey 2 0 2 4.92
ab-21135640 Airbnb Monterrey 2 0 2 4.63
ab-568695439747690000 Airbnb Monterrey 2 0 2 4.86
ab-48526165 Airbnb Monterrey 2 0 2 4.95
ab-51615575 Airbnb Monterrey 2 0 2 4.48
ab-31835583 Airbnb Monterrey 2 0 2 4.81
ab-50549472 Airbnb Monterrey 2 0 2 4.78
ab-32810057 Airbnb Monterrey 2 0 2 4.68
ab-39525241 Airbnb Monterrey 2 0 2 4.85
ab-53443586 Airbnb Monterrey 2 0 2 4.89
ab-2086830 Airbnb Monterrey 2 0 2 4.78
ab-22108760 Airbnb Monterrey 2 0 2 4.85
ab-38237912 Airbnb Monterrey 2 0 2 4.91
ab-37331784 Airbnb Monterrey 2 0 2 4.80
ab-581901874398961000 Airbnb Monterrey 2 0 2 4.75
ab-42222087 Airbnb Monterrey 2 0 2 4.72
ab-42722974 Airbnb Monterrey 2 0 2 4.92
ab-32398259 Airbnb Monterrey 2 0 2 4.91
ab-36000373 Airbnb Monterrey 2 0 2 4.91
ab-36151663 Airbnb Monterrey 2 0 2 4.78
ab-11452234 Airbnb Monterrey 2 0 2 1.95
ab-41492431 Airbnb Monterrey 2 0 2 4.90
ab-35041394 Airbnb Monterrey 2 0 2 4.91
ab-47393173 Airbnb Monterrey 2 0 2 4.81
ab-37052751 Airbnb Monterrey 2 0 2 4.88
ab-35217530 Airbnb Monterrey 2 0 2 4.87
ab-23613070 Airbnb Monterrey 2 0 2 4.69
ab-47716738 Airbnb Monterrey 2 0 2 4.81
ab-52011798 Airbnb Monterrey 2 0 2 4.65
ab-20589588 Airbnb Monterrey 2 0 2 4.83
ab-19294808 Airbnb Monterrey 2 0 2 4.85
ab-44096887 Airbnb Monterrey 2 0 2 4.73
Mirador Restaurante Monterrey 1 0 1 4.70
Jabalina - Barrio Antiguo Restaurante Monterrey 1 0 1 4.50
WAKA HOUSE Restaurante Guadalupe 1 0 1 4.80
Plaza Arcadia Plaza Comercial Guadalupe 1 0 1 4.60
Plaza Andenes Guadalupe Plaza Comercial Guadalupe 1 0 1 4.50
Plaza Eloy 2401 Plaza Comercial Guadalupe 1 0 1 4.30
Paseo La Fe Plaza Comercial San Nicolás 1 0 1 4.70
Plaza Santa Catarina Plaza Comercial Santa Catarina 1 0 1 4.40
Pekitas Citadina Escobedo Plaza Comercial Escobedo 1 0 1 4.50
ab-595200358660223000 Airbnb Monterrey 1 0 1 4.78
ab-46511094 Airbnb Monterrey 1 0 1 4.74
ab-54194714 Airbnb Monterrey 1 0 1 4.81
ab-13414647 Airbnb Monterrey 1 0 1 4.71
ab-845186830565140000 Airbnb Monterrey 1 0 1 4.97
ab-50729435 Airbnb Monterrey 1 0 1 4.71
ab-42748546 Airbnb Monterrey 1 0 1 4.93
ab-30883890 Airbnb Monterrey 1 0 1 4.83
ab-54241808 Airbnb Monterrey 1 0 1 4.76
ab-50120739 Airbnb Monterrey 1 0 1 4.88
ab-53808167 Airbnb Monterrey 1 0 1 4.73
ab-39247329 Airbnb Monterrey 1 0 1 4.77
ab-31163722 Airbnb Monterrey 1 0 1 4.95
ab-568853325119747000 Airbnb Monterrey 1 0 1 4.85
ab-39938415 Airbnb Monterrey 1 0 1 4.51
ab-598409918595811000 Airbnb Monterrey 1 0 1 4.76
ab-51494818 Airbnb Monterrey 1 0 1 4.87
ab-22319879 Airbnb Monterrey 1 0 1 4.83
ab-42961801 Airbnb Monterrey 1 0 1 4.81
ab-50259963 Airbnb Monterrey 1 0 1 4.81
ab-36698820 Airbnb Monterrey 1 0 1 4.79
ab-52067589 Airbnb Monterrey 1 0 1 4.81
ab-51613569 Airbnb Monterrey 1 0 1 4.68
ab-51643372 Airbnb Monterrey 1 0 1 4.74
ab-44306636 Airbnb Monterrey 1 0 1 4.57
ab-50897753 Airbnb Monterrey 1 0 1 4.58
ab-41350420 Airbnb Monterrey 1 0 1 4.84
ab-14900491 Airbnb Monterrey 1 0 1 4.80
ab-50384086 Airbnb Monterrey 1 0 1 4.75
ab-46249299 Airbnb Monterrey 1 0 1 4.80
ab-48718576 Airbnb Monterrey 2 1 1 4.84
ab-35382819 Airbnb Monterrey 1 0 1 4.87
ab-53847385 Airbnb Monterrey 1 0 1 4.85
ab-49081388 Airbnb Monterrey 1 0 1 4.83
ab-13478075 Airbnb Monterrey 1 0 1 4.70
ab-13446901 Airbnb Monterrey 1 0 1 4.73
ab-46715251 Airbnb Monterrey 1 0 1 4.73
ab-52777775 Airbnb Monterrey 1 0 1 4.74
ab-45846772 Airbnb Monterrey 1 0 1 4.82
ab-50242509 Airbnb Monterrey 1 0 1 4.63
ab-53861236 Airbnb Monterrey 1 0 1 4.81
ab-44390220 Airbnb Monterrey 1 0 1 4.65
ab-39758004 Airbnb Monterrey 1 0 1 4.66
ab-50939990 Airbnb Monterrey 1 0 1 4.58
ab-27954394 Airbnb Monterrey 1 0 1 4.75
ab-53147113 Airbnb Monterrey 1 0 1 4.82
ab-549377944181858000 Airbnb Monterrey 1 0 1 4.66
ab-54355925 Airbnb Monterrey 1 0 1 4.50
ab-32615731 Airbnb Monterrey 1 0 1 4.93
ab-31265632 Airbnb Monterrey 1 0 1 4.91
ab-34308222 Airbnb Monterrey 1 0 1 4.44
ab-36885823 Airbnb Monterrey 1 0 1 4.89
ab-10751983 Airbnb Monterrey 1 0 1 4.86
ab-24228251 Airbnb Monterrey 1 0 1 4.66
ab-52407829 Airbnb Monterrey 1 0 1 4.85
ab-796326861226003000 Airbnb Monterrey 1 0 1 4.93
ab-31865153 Airbnb Monterrey 1 0 1 4.82
ab-46333250 Airbnb Monterrey 1 0 1 4.66
ab-43886005 Airbnb Monterrey 1 0 1 4.66
ab-41129373 Airbnb Monterrey 1 0 1 4.89
ab-50662713 Airbnb Monterrey 1 0 1 4.77
ab-34863902 Airbnb Monterrey 1 0 1 4.89
ab-556422389979156000 Airbnb Monterrey 1 0 1 4.99
ab-42563511 Airbnb Monterrey 1 0 1 4.82
ab-651371449845472000 Airbnb Monterrey 1 0 1 4.94
ab-43946091 Airbnb Monterrey 1 0 1 4.61
ab-51108235 Airbnb Monterrey 1 0 1 4.55
ab-32963393 Airbnb Monterrey 1 0 1 4.65
ab-13187824 Airbnb Monterrey 1 0 1 4.55
ab-9260523 Airbnb Monterrey 1 0 1 4.94
ab-7554738 Airbnb Monterrey 1 0 1 4.83
ab-44171802 Airbnb Monterrey 1 0 1 4.81
ab-573883656961351000 Airbnb Monterrey 1 0 1 4.75
ab-23671445 Airbnb Monterrey 1 0 1 4.87
ab-50939750 Airbnb Monterrey 1 0 1 4.58
ab-41471420 Airbnb Monterrey 1 0 1 4.91
ab-14896889 Airbnb Monterrey 1 0 1 4.94
ab-46398278 Airbnb Monterrey 1 0 1 4.84
ab-842287066907833000 Airbnb Monterrey 1 0 1 4.85
ab-35379246 Airbnb Monterrey 1 0 1 4.64
ab-46770583 Airbnb Monterrey 1 0 1 4.81
ab-25867291 Airbnb Monterrey 1 0 1 4.78
ab-40727222 Airbnb Monterrey 1 0 1 4.91
ab-635382051706418000 Airbnb Monterrey 1 0 1 4.92
ab-49028724 Airbnb Monterrey 1 0 1 4.53
ab-45024965 Airbnb Monterrey 1 0 1 4.82
ab-48898598 Airbnb Monterrey 1 0 1 4.93
ab-46752346 Airbnb Monterrey 1 0 1 4.54
ab-37782306 Airbnb Monterrey 2 1 1 4.94
ab-37054437 Airbnb Monterrey 1 0 1 4.84
ab-45568699 Airbnb Monterrey 1 0 1 4.90
ab-51709432 Airbnb Monterrey 1 0 1 4.62
ab-20745506 Airbnb Monterrey 1 0 1 4.48
ab-594800 Airbnb Monterrey 1 0 1 4.93
ab-43577472 Airbnb Monterrey 1 0 1 4.65
ab-38135697 Airbnb Monterrey 1 0 1 4.56
ab-47490741 Airbnb Monterrey 1 0 1 4.86
ab-19564402 Airbnb Monterrey 1 0 1 4.97
ab-20439444 Airbnb Monterrey 1 0 1 4.83
ab-41274698 Airbnb Monterrey 1 0 1 4.95
ab-34058310 Airbnb Monterrey 1 0 1 4.91
Casa Benell - Cordillera Restaurante Santa Catarina 0 0 0 4.50
Paseo San Pedro Plaza Comercial San Pedro 0 0 0 4.60
Avanta Gardens Plaza Comercial San Pedro 2 2 0 4.60
Plaza La Joya Plaza Comercial San Nicolás 1 1 0 4.40
El Paseo Santa Catarina Plaza Comercial Santa Catarina 0 0 0 4.30
ab-24443952 Airbnb Monterrey 0 0 0 4.97
ab-28179952 Airbnb Monterrey 0 0 0 4.85
ab-42279290 Airbnb Monterrey 0 0 0 4.80
ab-24990202 Airbnb Monterrey 0 0 0 4.92
ab-18378076 Airbnb Monterrey 0 0 0 4.95
ab-25188845 Airbnb Monterrey 0 0 0 4.71
ab-44170066 Airbnb Monterrey 0 0 0 4.86
ab-42928754 Airbnb Monterrey 0 0 0 4.82
ab-571766657656347000 Airbnb Monterrey 0 0 0 4.78
ab-46771738 Airbnb Monterrey 0 0 0 4.81
ab-46397947 Airbnb Monterrey 0 0 0 4.89
ab-581844487017923000 Airbnb Monterrey 0 0 0 4.67
ab-678417820581264000 Airbnb Monterrey 0 0 0 4.94
ab-35839615 Airbnb Monterrey 0 0 0 4.70
ab-22675366 Airbnb Monterrey 0 0 0 4.97
ab-31206305 Airbnb Monterrey 0 0 0 4.53
ab-54307699 Airbnb Monterrey 0 0 0 4.62
ab-47248665 Airbnb Monterrey 0 0 0 4.28
ab-32810899 Airbnb Monterrey 0 0 0 4.77
ab-53158041 Airbnb Monterrey 0 0 0 4.45
ab-593674185137152000 Airbnb Monterrey 0 0 0 4.66
ab-41596923 Airbnb Monterrey 0 0 0 4.82
ab-31120751 Airbnb Monterrey 0 0 0 4.94
ab-51613688 Airbnb Monterrey 0 0 0 4.51
ab-50663107 Airbnb Monterrey 0 0 0 4.68
ab-32965779 Airbnb Monterrey 0 0 0 4.49
ab-42747885 Airbnb Monterrey 0 0 0 4.92
ab-36639157 Airbnb Monterrey 0 0 0 4.92
ab-47135493 Airbnb Monterrey 0 0 0 4.85
ab-45054259 Airbnb Monterrey 0 0 0 4.89
ab-32218531 Airbnb Monterrey 1 1 0 4.70
ab-27551016 Airbnb Monterrey 0 0 0 4.53
ab-46727887 Airbnb Monterrey 0 0 0 4.57
ab-632984326837823000 Airbnb Monterrey 0 0 0 4.86
ab-52014114 Airbnb Monterrey 1 1 0 4.69
ab-609211172483905000 Airbnb Monterrey 0 0 0 4.96
ab-42694317 Airbnb Monterrey 0 0 0 4.92
ab-35549244 Airbnb Monterrey 0 0 0 4.85
ab-49666883 Airbnb Monterrey 0 0 0 4.89
ab-42926536 Airbnb Monterrey 0 0 0 4.87
ab-24059531 Airbnb Monterrey 0 0 0 4.90
ab-32086289 Airbnb Monterrey 0 0 0 4.75
ab-38932219 Airbnb Monterrey 0 0 0 4.81
ab-33289164 Airbnb Monterrey 0 0 0 4.41
ab-51643041 Airbnb Monterrey 0 0 0 4.82
ab-51333114 Airbnb Monterrey 0 0 0 4.85
ab-50795286 Airbnb Monterrey 0 0 0 4.63
ab-35877122 Airbnb Monterrey 0 0 0 4.74
ab-45941387 Airbnb Monterrey 0 0 0 4.92
ab-739393651126694000 Airbnb Monterrey 0 0 0 4.85
ab-42824913 Airbnb Monterrey 0 0 0 4.63
ab-24242910 Airbnb Monterrey 0 0 0 4.72
ab-45257888 Airbnb Monterrey 0 0 0 4.79
ab-32959829 Airbnb Monterrey 0 0 0 4.91
ab-52006830 Airbnb Monterrey 0 0 0 4.74
ab-37622388 Airbnb Monterrey 0 0 0 4.92
ab-30234375 Airbnb Monterrey 0 0 0 4.85
ab-50046150 Airbnb Monterrey 0 0 0 4.66
ab-42387290 Airbnb Monterrey 0 0 0 4.95
ab-15832767 Airbnb Monterrey 0 0 0 4.78
ab-46418039 Airbnb Monterrey 0 0 0 4.76
ab-26597142 Airbnb Monterrey 0 0 0 4.83
ab-27851439 Airbnb Monterrey 0 0 0 4.86
ab-30887779 Airbnb Monterrey 0 0 0 4.89
ab-45319395 Airbnb Monterrey 0 0 0 4.86
ab-632990683386635000 Airbnb Monterrey 0 0 0 4.80
ab-45566119 Airbnb Monterrey 0 0 0 4.71
ab-11510512 Airbnb Monterrey 0 0 0 4.85
ab-38932133 Airbnb Monterrey 0 0 0 4.60
ab-34817940 Airbnb Monterrey 0 0 0 4.87
ab-674625658964805000 Airbnb Monterrey 0 0 0 4.87
ab-23467320 Airbnb Monterrey 0 0 0 4.76
ab-51969259 Airbnb Monterrey 0 0 0 4.93
ab-49842592 Airbnb Monterrey 0 0 0 4.91
ab-28423727 Airbnb Monterrey 0 0 0 4.69
ab-48227628 Airbnb Monterrey 0 0 0 4.80
ab-50703906 Airbnb Monterrey 0 0 0 4.68
ab-39512088 Airbnb Monterrey 0 0 0 4.92
ab-22357915 Airbnb Monterrey 0 0 0 4.85
ab-51643480 Airbnb Monterrey 0 0 0 4.61
ab-47249372 Airbnb Monterrey 0 0 0 4.53
ab-839301080425348000 Airbnb Monterrey 0 0 0 4.91
ab-46508804 Airbnb Monterrey 0 0 0 4.74
ab-44858234 Airbnb Monterrey 0 0 0 4.73
ab-32810882 Airbnb Monterrey 0 0 0 4.86
ab-48999146 Airbnb Monterrey 0 1 -1 4.61
ab-41938444 Airbnb Monterrey 0 2 -2 4.58
pal_sent <- colorNumeric(
  palette = c("#D73027", "#FFFFBF", "#1A9850"),
  domain  = sentiment_map_data$sentiment_score
)

leaflet(sentiment_map_data) %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addCircleMarkers(
    lng = ~lon,
    lat = ~lat,
    color = ~pal_sent(sentiment_score),
    fillColor = ~pal_sent(sentiment_score),
    fillOpacity = 0.85,
    radius = ~ifelse(tipo == "Plaza Comercial", 10, 7),
    stroke = FALSE,
    popup = ~paste0(
      "<b>", nombre, "</b><br>",
      "Tipo: ", tipo, "<br>",
      "Municipio: ", municipio, "<br>",
      "Palabras positivas: ", positive, "<br>",
      "Palabras negativas: ", negative, "<br>",
      "<b>Puntaje sentimiento: ", sentiment_score, "</b><br>",
      "Calificacion: ", calificacion
    )
  ) %>%
  addLegend(
    pal = pal_sent,
    values = ~sentiment_score,
    title = "Puntaje de Sentimiento",
    position = "bottomright"
  )
par(mfrow = c(1, 2))

pos_words <- reviews_tokens %>%
  inner_join(filter(lexicon_es, sentiment == "positive"), by = "word") %>%
  count(word, sort = TRUE)

if (nrow(pos_words) > 0) {
  wordcloud(words = pos_words$word, freq = pos_words$n,
            max.words = 40, colors = brewer.pal(8, "Greens"),
            scale = c(3, 0.5), random.order = FALSE)
  title("Palabras Positivas")
} else {
  plot.new(); title("Sin palabras positivas")
}

neg_words <- reviews_tokens %>%
  inner_join(filter(lexicon_es, sentiment == "negative"), by = "word") %>%
  count(word, sort = TRUE)

if (nrow(neg_words) > 0) {
  wordcloud(words = neg_words$word, freq = neg_words$n,
            max.words = 40, colors = brewer.pal(8, "Reds"),
            scale = c(3, 0.5), random.order = FALSE)
  title("Palabras Negativas")
} else {
  plot.new(); title("Sin palabras negativas")
}

Interpretacion de sentimientos

El analisis de texto permitio identificar las palabras que aparecen con mayor frecuencia en las resenas de los usuarios. Terminos como gexcelenteh, glugarh, gservicioh, gcomidah, gagradableh y gubicacionh fueron los mas recurrentes, lo que refleja una percepcion generalmente positiva sobre la experiencia de los visitantes.

Ademas, los puntajes de sentimiento muestran que la mayoria de los establecimientos presentan una mayor cantidad de expresiones positivas que negativas. Restaurantes como Taberna Atope, Rincao Brasil y Gallo 71 destacaron por acumular los puntajes mas altos, lo que indica altos niveles de satisfaccion entre los clientes. En terminos generales, los resultados sugieren que la calidad del servicio, la ubicacion y la experiencia ofrecida son factores altamente valorados dentro del ecosistema turistico y comercial de la ZMM.

Parte D: Analitica Prescriptiva

7. Recomendaciones Estrategicas

Airbnb hosts

  • Ajustar precios considerando la competencia cercana y caracteristicas del alojamiento, ya que existe dependencia espacial en los precios.
  • Destacar capacidad, banos y ubicacion en las publicaciones.
  • Mantener buenas resenas mediante una experiencia consistente para el huesped.

Hoteles

  • Invertir en amenidades como alberca y gimnasio, ya que influyen positivamente en el precio.
  • Fortalecer la calidad del servicio para mejorar calificaciones.
  • Generar alianzas con negocios cercanos para aumentar el valor de la estancia.

Restaurantes y plazas comerciales

  • Aprovechar zonas con alta concentracion de turistas para realizar promociones.
  • Monitorear las resenas para identificar oportunidades de mejora.
  • Crear colaboraciones con hoteles y Airbnb para atraer visitantes.

Planificadores urbanos

  • Mejorar la conectividad en zonas turisticas con potencial de crecimiento.
  • Impulsar servicios complementarios cerca de areas con alta oferta de alojamiento.
  • Utilizar los resultados de sentimiento para priorizar mejoras urbanas.

8. Diseno de Escenario: Gran Evento Internacional en Monterrey

Ante un evento internacional, la demanda turistica aumentaria principalmente en las zonas con mayor concentracion de hospedaje y servicios. Los hoteles y Airbnb podrian optimizar precios y capacidad de acuerdo con la demanda esperada, mientras que restaurantes y plazas comerciales tendrian la oportunidad de implementar promociones dirigidas a visitantes para maximizar el impacto economico.

Parte E: Preguntas Clave

1. Que zonas de la ZMM muestran dependencia espacial de precios entre Airbnb?
Los resultados muestran que existe dependencia espacial en los precios de Airbnb, ya que los alojamientos cercanos tienden a presentar precios similares. Esto sugiere que zonas con alta concentracion de propiedades, principalmente en Monterrey y areas cercanas al centro, influyen en la estrategia de precios de los anfitriones.

2. Como revelan las isocronas/accesibilidad diferencias entre Airbnb y hoteles?
Observamos que los hoteles se ubican, en promedio, mas cerca del centro de la ciudad, mientras que los Airbnb presentan una distribucion mas dispersa. Esto implica diferencias en accesibilidad y cercania a servicios, permitiendo que cada tipo de alojamiento atienda distintos perfiles de visitantes.

3. Que sugieren los mapas de sentimiento?
Los mapas de sentimiento indican que la percepcion de los usuarios es generalmente positiva. Ademas, muestran que la satisfaccion depende no solo de la ubicacion, sino tambien de factores como la calidad del servicio, la experiencia, la limpieza y la atencion recibida.

4. Que acciones prescriptivas se recomiendan para hoteles?
Recomendariamos fortalecer las amenidades y mejorar continuamente la experiencia del huesped, ya que variables como la calificacion, la alberca y el gimnasio tuvieron un impacto positivo en los precios. Tambien seria util generar alianzas con negocios cercanos para incrementar el valor de la estancia.

5. Como pueden restaurantes y plazas usar sentimiento geolocalizado?
Pueden utilizar esta informacion para identificar areas de oportunidad, mejorar aspectos mencionados por los clientes y disenar promociones dirigidas a visitantes hospedados en zonas cercanas. Ademas, pueden crear alianzas estrategicas con hoteles y Airbnb para atraer una mayor cantidad de consumidores.

Conclusiones

A traves de este analisis identificamos que la ubicacion tiene un papel importante en la dinamica turistica de la Zona Metropolitana de Monterrey. Los resultados muestran que los precios de Airbnb presentan una influencia espacial significativa, mientras que en los hoteles destacan factores relacionados con la calidad y las amenidades ofrecidas. Asimismo, el analisis de sentimientos reflejo una percepcion mayormente positiva por parte de los visitantes. En conjunto, estos hallazgos permiten generar recomendaciones practicas para mejorar la competitividad y fortalecer la experiencia turistica en la region.