ProyectoFinal_AnalisisViabilidad_FastFood

Authors

Alfredo Israel Becerra Ortiz

Edgar Gerardo Bustamante Lopez

Hector Gonzalo Monroy Muñoz

Análisis espacial y preparación de datos (California)

Para hacer la tabla de variables que utilizaremos ya en el modelo logístico, utilizaremos datos espaciales, mapas, etc. que no sólo nos ayudaran a determinar de forma numérica ciertos eventos que creemos cruciales para analizar si es viable poner un restaurante o no.

Sino también nos ayudarán a visualizar la información a traves de mapas etc.

Todo se hará en tramos censales, la unidad de medida utilizada por el US Census. Utilizando los TIGER files de la base de datos estadounidense la cual contiene datos en formato sf (spatial data) los cuales son datos geográficos donde están los edificios al igual que vias de acceso, rutas, etc.

Ocuparemos los tramos censales en lugar de los bloques censales, la diferencia siendo que un bloque censal es muy pequeño, un tramo representa lo que sería una colonia mientras que un bloque censal puede representar una manzana o cuadra, por lo que nuestras posibilidades de clasificar bien nuestras variables se disminuye.

También dentro de los TIGER files utilizaremos la base de datos de “landmarks”, que representa edificios importantes dentro de cada estado, escuelas, hospitales, iglesias ,edificios administrativos, Malls, aeropuertos entre otros.

Lectura de información en formato shape file

raw_finalrestaurant_df<-read.csv("C:/Users/wars_/Documents/Universidad/Actuaría/2025-A/Computo_cientifico/Proyecto_final/datafini_fastfood_restaurants_incomedata.csv", 
                                 stringsAsFactors = FALSE, fileEncoding = "UTF-8") %>% 
  select(city,latitude,longitude,name,state,Median_income,Poberty_rate)

glimpse(raw_finalrestaurant_df)
Rows: 10,000
Columns: 7
$ city          <chr> "Thibodaux", "Thibodaux", "Pigeon Forge", "Pigeon Forge"…
$ latitude      <dbl> 29.81470, 29.81470, 35.80379, 35.78234, 33.56274, 42.368…
$ longitude     <dbl> -90.81474, -90.81474, -83.58055, -83.55141, -84.32114, -…
$ name          <chr> "SONIC Drive In", "SONIC Drive In", "Taco Bell", "Arby's…
$ state         <chr> "Louisiana", "Louisiana", "Tennessee", "Tennessee", "Geo…
$ Median_income <int> 46658, 46658, 50711, 50711, 70075, 34762, 34762, 80837, …
$ Poberty_rate  <int> 2515, 2515, 1394, 1394, 775, 202311, 202311, 16404, 1790…
#Reduciremos la base de datos al estado que necesitamos
raw_finalrestaurant_df<-raw_finalrestaurant_df %>% filter(state=="California")

cat("Las variables útiles de la base de datos exploratoria son: \n")
Las variables útiles de la base de datos exploratoria son: 
colnames(raw_finalrestaurant_df)
[1] "city"          "latitude"      "longitude"     "name"         
[5] "state"         "Median_income" "Poberty_rate" 
Reading layer `tl_2018_06_pointlm' from data source 
  `C:\Users\wars_\Documents\Universidad\Actuaría\2025-A\Computo_cientifico\Proyecto_final\sf_california_files\tl_2018_06_pointlm\tl_2018_06_pointlm.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 82815 features and 5 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -124.407 ymin: 32.53756 xmax: -114.1762 ymax: 42.0025
Geodetic CRS:  NAD83

Creación de la tabla final para modelo LOGIT

#Conversion de coordenadas de restaurantes a sf
restaurants_sf<-st_as_sf(raw_finalrestaurant_df, coords = c("longitude","latitude"), crs=4326)

#Checar sistema de coordenada de datos TRIGER/Line files
st_crs(restaurants_sf)
Coordinate Reference System:
  User input: EPSG:4326 
  wkt:
GEOGCRS["WGS 84",
    ENSEMBLE["World Geodetic System 1984 ensemble",
        MEMBER["World Geodetic System 1984 (Transit)"],
        MEMBER["World Geodetic System 1984 (G730)"],
        MEMBER["World Geodetic System 1984 (G873)"],
        MEMBER["World Geodetic System 1984 (G1150)"],
        MEMBER["World Geodetic System 1984 (G1674)"],
        MEMBER["World Geodetic System 1984 (G1762)"],
        MEMBER["World Geodetic System 1984 (G2139)"],
        MEMBER["World Geodetic System 1984 (G2296)"],
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]],
        ENSEMBLEACCURACY[2.0]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["geodetic latitude (Lat)",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["geodetic longitude (Lon)",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    USAGE[
        SCOPE["Horizontal component of 3D system."],
        AREA["World."],
        BBOX[-90,-180,90,180]],
    ID["EPSG",4326]]
st_crs(california_censustracts)
Coordinate Reference System:
  User input: NAD83 
  wkt:
GEOGCRS["NAD83",
    DATUM["North American Datum 1983",
        ELLIPSOID["GRS 1980",6378137,298.257222101,
            LENGTHUNIT["metre",1]]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["latitude",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["longitude",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    ID["EPSG",4269]]
#Coincidencia de sistema de coordenadas
california_censustracts<-st_transform(california_censustracts,4326)
california_landmarks<-st_transform(california_landmarks,4326)

#Contar numero de restaurantes por tramo censal:

no_restaurantes<-st_join(restaurants_sf,california_censustracts["GEOID"]) %>% 
  count(GEOID) %>% rename("restaurants_count"="n")

no_restaurantes<-st_drop_geometry(no_restaurantes)

#Contar numero de edificios importantes por tramo censal:
no_edificios<-st_join(california_landmarks,california_censustracts["GEOID"]) %>% 
  count(GEOID) %>% rename("landmarks_count"="n")
no_edificios<-st_drop_geometry(no_edificios)

#Juntar ya en la base de datos para el modelo
finalrestaurant_california_df<-left_join(california_censustracts,no_restaurantes, by="GEOID")
finalrestaurant_california_df<-left_join(finalrestaurant_california_df,no_edificios, by="GEOID")

finalrestaurant_california_df<-finalrestaurant_california_df %>% mutate(restaurants_count=ifelse(is.na(restaurants_count),0,restaurants_count),
                                                                        landmarks_count=ifelse(is.na(landmarks_count),0,landmarks_count))

Ahora juntaremos esta informacion con datos censales del tidycensus package, como hemos mencionado, utilizaremos diferentes variables sociales y económicas y algunas serán calculadas como una tasa de relación. Por ejemplo, la tasa de pobreza es el número de personas en pobreza de ese tramo censal entre las personas totales del tramo. Para la tasa de empleo, es el número de personas en el momento con un empleo entre la fuerza laboral total.

acs_vars<-c(
  median_income = "B19013_001",
  poverty = "B17001_002",
  population = "B01003_001",
  employment = "B23025_004",
  labor_force="B23025_003",
  poverty_status_determination="B17001_001"
)

acs_data <- get_acs(
  geography = "tract",
  state = "CA",
  variables = acs_vars,
  year = 2018,
  survey = "acs5"
)

glimpse(acs_data)
Rows: 48,342
Columns: 5
$ GEOID    <chr> "06001400100", "06001400100", "06001400100", "06001400100", "…
$ NAME     <chr> "Census Tract 4001, Alameda County, California", "Census Trac…
$ variable <chr> "population", "poverty_status_determination", "poverty", "med…
$ estimate <dbl> 3115, 3105, 111, 200893, 1669, 1578, 2025, 2018, 131, 160536,…
$ moe      <dbl> 219, 219, 57, 49177, 200, 199, 110, 111, 51, 29320, 95, 95, 3…
acs_data_2<-acs_data %>% select(GEOID,variable,estimate) %>% 
  pivot_wider(names_from = variable, values_from = estimate)

glimpse(acs_data_2)
Rows: 8,057
Columns: 7
$ GEOID                        <chr> "06001400100", "06001400200", "0600140030…
$ population                   <dbl> 3115, 2025, 5000, 3843, 3786, 1638, 5116,…
$ poverty_status_determination <dbl> 3105, 2018, 4962, 3830, 3768, 1638, 5106,…
$ poverty                      <dbl> 111, 131, 264, 434, 447, 151, 659, 462, 2…
$ median_income                <dbl> 200893, 160536, 94732, 113036, 103846, 12…
$ labor_force                  <dbl> 1669, 1264, 3285, 2528, 2584, 1171, 3216,…
$ employment                   <dbl> 1578, 1211, 3122, 2454, 2469, 1084, 3038,…
#Juntar con la base de datos normal:
finalrestaurant_california_df<-left_join(finalrestaurant_california_df,acs_data_2, by="GEOID") %>% 
  mutate(poverty_rate=poverty/poverty_status_determination,
         employment_rate=employment/labor_force)

#Generar base de datos sólo con información numérica
finalrestaurant_california_df_nogeometry<-st_drop_geometry(finalrestaurant_california_df)

finalrestaurant_california_df_nogeometry<-finalrestaurant_california_df_nogeometry %>% 
  select(STATEFP,GEOID,13:22) %>% mutate(viable=ifelse(restaurants_count>=1,1,0))

La variable “viable” es nuestra variable dependiente, 0 es no viable, 1 sí lo es.

Análisis Gráfico California

Veremos cuál es la relacion entre las condiciones socio-económicas que hay en los tramos censales y la ubicación de los restaurantes posicionados en ese estado. Esto como un análisis previo visual antes de hacer el modelo logit y ver si hay algún patrón a identificar fácilmente.

california_restaurantes_censustract_graph<-finalrestaurant_california_df %>% ggplot() + geom_sf(color="black") + 
  geom_sf(data=restaurants_sf, color="blue", size=0.5) +
  labs(title = "Restaurantes de Fast Food en California, EU")
california_restaurantes_censustract_graph

california_restaurantes_median_income<-finalrestaurant_california_df %>%
  mutate(median_income=ifelse(is.na(median_income),0,median_income)) %>% 
  ggplot() + geom_sf(aes(fill=median_income),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="green", size=0.35) +
  scale_fill_scico(palette = "lajolla", direction=1) +
  labs(title = "Ingreso medio y restaurantes en California, EU")
california_restaurantes_median_income

california_sanfrancisco_restaurantes_median_income<-finalrestaurant_california_df %>%
  mutate(median_income=ifelse(is.na(median_income),0,median_income)) %>% 
  ggplot() + geom_sf(aes(fill=median_income),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="green", size=0.75) +
  coord_sf(xlim = c(-122.5, -121.5), ylim = c(37.2, 38)) +
  scale_fill_scico(palette="lajolla", direction=1) +
  labs(title = "Ingreso medio y restaurantes en San Francisco, California, EU")
california_sanfrancisco_restaurantes_median_income

california_restaurantes_poverty_rate<-finalrestaurant_california_df %>%
  drop_na(poverty_rate) %>% 
  ggplot() + geom_sf(aes(fill=poverty_rate),linewidth = 0.05) +
  geom_sf(data=restaurants_sf, color="green", size=0.5) +
  scale_fill_scico(palette = "oslo", direction=1) +
  labs(title = "Tasa desempleo y restaurantes en California, EU")
california_restaurantes_poverty_rate

california_sanfrancisco_restaurantes_poverty_rate<-finalrestaurant_california_df %>%
  drop_na(poverty_rate) %>%
  ggplot() + geom_sf(aes(fill=poverty_rate),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="green", size=0.35) +
  coord_sf(xlim = c(-122.5, -121.5), ylim = c(37.2, 38)) +
  scale_fill_scico(palette="oslo", direction=1) +
  labs(title = "Tasa desempleo y restaurantes en San Francisco, California, EU")
california_sanfrancisco_restaurantes_poverty_rate

california_sanfrancisco_restaurantes_poverty<-finalrestaurant_california_df %>%
  drop_na(poverty) %>%
  ggplot() + geom_sf(aes(fill=poverty),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="green", size=0.35) +
  coord_sf(xlim = c(-122.5, -121.5), ylim = c(37.2, 38)) +
  scale_fill_scico(palette="lipari", direction=1) +
  labs(title = "Población desempleada y restaurantes en San Francisco, California, EU")
california_sanfrancisco_restaurantes_poverty

california_sanfrancisco_restaurantes_employment<-finalrestaurant_california_df %>%
  drop_na(employment) %>%
  ggplot() + geom_sf(aes(fill=employment),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="red", size=0.75) +
  coord_sf(xlim = c(-122.5, -121.5), ylim = c(37.2, 38)) +
  scale_fill_scico(palette="devon", direction=1,limits = c(0, 7500)) +
  labs(title = "Población con empleo y restaurantes en San Francisco, California, EU")
california_sanfrancisco_restaurantes_employment

california_sanfrancisco_restaurantes_population<-finalrestaurant_california_df %>%
  drop_na(population) %>%
  ggplot() + geom_sf(aes(fill=population),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="red", size=0.5) +
  coord_sf(xlim = c(-122.5, -121.5), ylim = c(37.2, 38)) +
  scale_fill_scico(palette="bamako", direction=1,limits = c(0, 20000)) +
  labs(title = "Población total y restaurantes en San Francisco, California, EU")
california_sanfrancisco_restaurantes_population

california_restaurantes_population<-finalrestaurant_california_df %>%
  #drop_na(population) %>%
  ggplot() + geom_sf(aes(fill=population),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="red", size=0.35) +
  # coord_sf(xlim = c(-122.5, -121.5), ylim = c(37.2, 38)) +
  scale_fill_scico(palette="bamako", direction=1) +
  labs(title = "Población total y restaurantes en San Francisco, California, EU")
california_restaurantes_population

Por otro lado generaremos la base de datos del Estado de Nueva York, California nos servirá como la base de datos para alimentar al modelo, y el estado de Nueva York para hacer comparaciones predictivas y análisis de conclusión.

Rows: 10,000
Columns: 7
$ city          <chr> "Thibodaux", "Thibodaux", "Pigeon Forge", "Pigeon Forge"…
$ latitude      <dbl> 29.81470, 29.81470, 35.80379, 35.78234, 33.56274, 42.368…
$ longitude     <dbl> -90.81474, -90.81474, -83.58055, -83.55141, -84.32114, -…
$ name          <chr> "SONIC Drive In", "SONIC Drive In", "Taco Bell", "Arby's…
$ state         <chr> "Louisiana", "Louisiana", "Tennessee", "Tennessee", "Geo…
$ Median_income <int> 46658, 46658, 50711, 50711, 70075, 34762, 34762, 80837, …
$ Poberty_rate  <int> 2515, 2515, 1394, 1394, 775, 202311, 202311, 16404, 1790…
Coordinate Reference System:
  User input: EPSG:4326 
  wkt:
GEOGCRS["WGS 84",
    ENSEMBLE["World Geodetic System 1984 ensemble",
        MEMBER["World Geodetic System 1984 (Transit)"],
        MEMBER["World Geodetic System 1984 (G730)"],
        MEMBER["World Geodetic System 1984 (G873)"],
        MEMBER["World Geodetic System 1984 (G1150)"],
        MEMBER["World Geodetic System 1984 (G1674)"],
        MEMBER["World Geodetic System 1984 (G1762)"],
        MEMBER["World Geodetic System 1984 (G2139)"],
        MEMBER["World Geodetic System 1984 (G2296)"],
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]],
        ENSEMBLEACCURACY[2.0]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["geodetic latitude (Lat)",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["geodetic longitude (Lon)",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    USAGE[
        SCOPE["Horizontal component of 3D system."],
        AREA["World."],
        BBOX[-90,-180,90,180]],
    ID["EPSG",4326]]
Coordinate Reference System:
  User input: NAD83 
  wkt:
GEOGCRS["NAD83",
    DATUM["North American Datum 1983",
        ELLIPSOID["GRS 1980",6378137,298.257222101,
            LENGTHUNIT["metre",1]]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["latitude",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["longitude",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    ID["EPSG",4269]]

Seccion de modelo logit

Desarrollaremos el modelo al igual que haremos sus pruebas estadísticas para ver la veracidad del modelo conforma a la información estadística, cabe mencionar que los datos que usamos para California son del año 2018 para que coincida con la última actualización de nuestra base de datos de restaurantes pública de Datafini.

df <- finalrestaurant_california_df_nogeometry

impute_median <- function(x) {
  x[is.na(x)] <- median(x, na.rm = TRUE)
  return(x)
}

df$median_income <- impute_median(df$median_income)
df$poverty_rate <- impute_median(df$poverty_rate)
df$employment_rate <- impute_median(df$employment_rate)
df$population <- impute_median(df$population)

p_ing <- quantile(df$median_income, 0.75, na.rm = TRUE)  # Top 25% en ingreso

df$viable <- ifelse(df$median_income > p_ing, 1, 0)
table(df$viable)

   0    1 
6043 2014 
colnames(df)
 [1] "STATEFP"                      "GEOID"                       
 [3] "restaurants_count"            "landmarks_count"             
 [5] "population"                   "poverty_status_determination"
 [7] "poverty"                      "median_income"               
 [9] "labor_force"                  "employment"                  
[11] "poverty_rate"                 "employment_rate"             
[13] "viable"                      
summary(df)
   STATEFP             GEOID           restaurants_count landmarks_count 
 Length:8057        Length:8057        Min.   :0.0000    Min.   :  0.00  
 Class :character   Class :character   1st Qu.:0.0000    1st Qu.:  0.00  
 Mode  :character   Mode  :character   Median :0.0000    Median :  3.00  
                                       Mean   :0.1491    Mean   : 10.29  
                                       3rd Qu.:0.0000    3rd Qu.: 12.00  
                                       Max.   :6.0000    Max.   :469.00  
   population    poverty_status_determination    poverty     median_income   
 Min.   :    0   Min.   :    0                Min.   :   0   Min.   :  2499  
 1st Qu.: 3457   1st Qu.: 3391                1st Qu.: 265   1st Qu.: 49750  
 Median : 4605   Median : 4533                Median : 517   Median : 69643  
 Mean   : 4859   Mean   : 4767                Mean   : 681   Mean   : 77264  
 3rd Qu.: 5902   3rd Qu.: 5819                3rd Qu.: 924   3rd Qu.: 96066  
 Max.   :38932   Max.   :31988                Max.   :5554   Max.   :250001  
  labor_force      employment     poverty_rate     employment_rate 
 Min.   :    0   Min.   :    0   Min.   :0.00000   Min.   :0.0000  
 1st Qu.: 1720   1st Qu.: 1601   1st Qu.:0.06583   1st Qu.:0.9121  
 Median : 2329   Median : 2162   Median :0.11650   Median :0.9389  
 Mean   : 2436   Mean   : 2272   Mean   :0.14595   Mean   :0.9306  
 3rd Qu.: 3009   3rd Qu.: 2809   3rd Qu.:0.19955   3rd Qu.:0.9580  
 Max.   :14860   Max.   :13862   Max.   :1.00000   Max.   :1.0000  
     viable    
 Min.   :0.00  
 1st Qu.:0.00  
 Median :0.00  
 Mean   :0.25  
 3rd Qu.:0.00  
 Max.   :1.00  
df$viable <- factor(df$viable, levels = c(0, 1))
df_clean <- subset(df, select = -c(STATEFP, GEOID,median_income))
modelo_logit <- glm(viable ~ ., data = df_clean, family = binomial)
Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
summary(modelo_logit)

Call:
glm(formula = viable ~ ., family = binomial, data = df_clean)

Coefficients:
                               Estimate Std. Error z value Pr(>|z|)    
(Intercept)                  -1.123e+01  2.975e+00  -3.776 0.000159 ***
restaurants_count            -2.667e-01  8.328e-02  -3.202 0.001364 ** 
landmarks_count               3.032e-03  2.081e-03   1.457 0.145171    
population                   -8.001e-05  1.063e-04  -0.753 0.451507    
poverty_status_determination -7.002e-05  1.232e-04  -0.568 0.569911    
poverty                      -1.531e-03  5.374e-04  -2.849 0.004383 ** 
labor_force                  -9.467e-04  1.119e-03  -0.846 0.397537    
employment                    1.710e-03  1.167e-03   1.465 0.142992    
poverty_rate                 -3.150e+01  2.659e+00 -11.847  < 2e-16 ***
employment_rate               1.340e+01  3.127e+00   4.285 1.83e-05 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9060.9  on 8056  degrees of freedom
Residual deviance: 4922.6  on 8047  degrees of freedom
AIC: 4942.6

Number of Fisher Scoring iterations: 7
vif(modelo_logit)
           restaurants_count              landmarks_count 
                    1.013890                     1.243657 
                  population poverty_status_determination 
                   46.089407                    61.585090 
                     poverty                  labor_force 
                   12.605004                  1413.724303 
                  employment                 poverty_rate 
                 1376.894663                     5.632273 
             employment_rate 
                    4.400021 
alias(modelo_logit)
Model :
viable ~ restaurants_count + landmarks_count + population + poverty_status_determination + 
    poverty + labor_force + employment + poverty_rate + employment_rate
df_clean2 <- subset(df_clean, select = -c(population,poverty_status_determination,labor_force,employment))
modelo_logit2 <- glm(viable ~ ., data = df_clean2, family = binomial)
Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
summary(modelo_logit2)

Call:
glm(formula = viable ~ ., family = binomial, data = df_clean2)

Coefficients:
                    Estimate Std. Error z value Pr(>|z|)    
(Intercept)       -1.493e+01  1.438e+00 -10.382  < 2e-16 ***
restaurants_count -2.152e-01  8.156e-02  -2.639  0.00832 ** 
landmarks_count    1.085e-03  1.934e-03   0.561  0.57479    
poverty            6.320e-04  1.922e-04   3.289  0.00101 ** 
poverty_rate      -4.121e+01  1.512e+00 -27.254  < 2e-16 ***
employment_rate    1.831e+01  1.504e+00  12.174  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9060.9  on 8056  degrees of freedom
Residual deviance: 4996.2  on 8051  degrees of freedom
AIC: 5008.2

Number of Fisher Scoring iterations: 7
vif(modelo_logit2)
restaurants_count   landmarks_count           poverty      poverty_rate 
         1.008542          1.166386          1.967914          1.866007 
  employment_rate 
         1.013979 
alias(modelo_logit2)
Model :
viable ~ restaurants_count + landmarks_count + poverty + poverty_rate + 
    employment_rate
probabilidades <- predict(modelo_logit2, newdata = df_clean2, type = "response")
predicciones <- factor(ifelse(probabilidades > 0.5, 1, 0), levels = c(0, 1))
real <- df_clean2$viable

# Matriz de confusión
confusion <- confusionMatrix(predicciones, real, positive = "1")
print(confusion)
Confusion Matrix and Statistics

          Reference
Prediction    0    1
         0 5504  613
         1  539 1401
                                          
               Accuracy : 0.857           
                 95% CI : (0.8492, 0.8646)
    No Information Rate : 0.75            
    P-Value [Acc > NIR] : < 2e-16         
                                          
                  Kappa : 0.614           
                                          
 Mcnemar's Test P-Value : 0.03149         
                                          
            Sensitivity : 0.6956          
            Specificity : 0.9108          
         Pos Pred Value : 0.7222          
         Neg Pred Value : 0.8998          
             Prevalence : 0.2500          
         Detection Rate : 0.1739          
   Detection Prevalence : 0.2408          
      Balanced Accuracy : 0.8032          
                                          
       'Positive' Class : 1               
                                          

Define control de entrenamiento con 5-fold cross-validation (se puede cambiar a 10)

train_control <- trainControl(method = "cv", number = 5, 
                              classProbs = TRUE, 
                              summaryFunction = twoClassSummary)

df_clean2$viable <- factor(df_clean2$viable, levels = c(0,1), labels = c("No", "Yes"))

set.seed(123)

modelo_cv <- train(viable ~ ., data = df_clean2,
                   method = "glm",
                   family = binomial(),
                   trControl = train_control,
                   metric = "ROC")  # Optimiza AUC

print(modelo_cv)
Generalized Linear Model 

8057 samples
   5 predictor
   2 classes: 'No', 'Yes' 

No pre-processing
Resampling: Cross-Validated (5 fold) 
Summary of sample sizes: 6445, 6447, 6445, 6446, 6445 
Resampling results:

  ROC        Sens       Spec     
  0.9181168  0.9108032  0.6976026
print(modelo_cv$results)
  parameter       ROC      Sens      Spec       ROCSD     SensSD     SpecSD
1      none 0.9181168 0.9108032 0.6976026 0.007291901 0.01474752 0.02438521

Ahora checaremos la exactitud del modelo para clasificar la viabilidad en comparación de los datos originales:

# Accuracy
accuracy <- confusion$overall['Accuracy']
print(accuracy)
 Accuracy 
0.8570187 
# F1-score
f1_score <- confusion$byClass['F1']
print(f1_score)
       F1 
0.7086495 
# Objeto ROC
roc_obj <- roc(response = real, predictor = probabilidades)
# Graficar curva ROC
plot(roc_obj, col = "blue", main = "Curva ROC")

# Calcular AUC
auc_value <- auc(roc_obj)
print(auc_value)
Area under the curve: 0.9183

Predicciones para Nueva York

Haremos nuevas predicciones y haremos comparaciones a través de mapas de calor sobre la viabilidad vs la ubicación real de los restaurantes en el estado y también en le ciudad.

# Predicciones para NY City
nuevos_datos <- finalrestaurant_new_york_df_nogeometry

#Imputar faltantes si es necesario
nuevos_datos$median_income <- impute_median(nuevos_datos$median_income)
nuevos_datos$poverty_rate <- impute_median(nuevos_datos$poverty_rate)
nuevos_datos$employment_rate <- impute_median(nuevos_datos$employment_rate)

colnames(nuevos_datos)
 [1] "STATEFP"                      "GEOID"                       
 [3] "restaurants_count"            "landmarks_count"             
 [5] "population"                   "poverty_status_determination"
 [7] "poverty"                      "median_income"               
 [9] "labor_force"                  "employment"                  
[11] "poverty_rate"                 "employment_rate"             
[13] "viable"                      
# Seleccionar sólo variables para el modelo
nuevos_datos <- nuevos_datos[, c("GEOID","restaurants_count", "landmarks_count", "poverty", "median_income", "poverty_rate", "employment_rate")]

# Predicción
prob_pred <- predict(modelo_cv, newdata = nuevos_datos, type = "prob")
class_pred <- predict(modelo_cv, newdata = nuevos_datos)

# Conversión a factor de predicción
pred_binarias <- ifelse(class_pred == "Yes", 1, 0)

# Crear dataframe con resultados
resultados <- data.frame(nuevos_datos,
                         prob_viable = prob_pred$Yes,
                         pred_class = class_pred,
                         pred_binaria = pred_binarias)

# Guardar resultados
write.csv(resultados, "C:/Users/wars_/Documents/Universidad/Actuaría/2025-A/Computo_cientifico/Proyecto_final/Tablas_modelo_logit_newyork/resultados_prediccion_ny.csv", row.names = FALSE)
resultados$GEOID <- as.character(resultados$GEOID)

map_data <- new_york_censustracts %>% left_join(resultados, by = "GEOID")

Gráfico de viabilidad, en términos probabilísticos (entre 0 y 1) del Estado de Nueva York.

La viabilidad determinada como una matriz de probabilidades dependiendo del tramo censal, identificado por un “GEOID” que es el identificador dentro de las bases de datos de US CENSUS, la mostraremos en seguida a través de un mapa de calor, probabilidades más cercanas a 1 indican una viabilidad mayor siendo entonces más conveniente intentar colocar una franquicia en esa zona, una investigación más a fondo podría ingresar un umbral de “alta competencia”, donde si es un lugar con ya muchas franquicias establecidas será más conveniente no colocar el restaurante en esa zona.

library(ggplot2)
ggplot(map_data) +
  geom_sf(aes(fill = prob_viable), color = NA) +
  scale_fill_viridis_c(option = "plasma") +
  labs(title = "Viabilidad por tracto censal en NY",
       fill = "Probabilidad") +
  theme_minimal()

NewYork_Viability<-ggplot(map_data) +
  geom_sf(aes(fill = prob_viable), color = NA) +
  scale_fill_viridis_c(option = "plasma") +
  coord_sf(xlim = c(-79.76259, -71.77749), ylim = c(40.47658, 45.01586)) +
  labs(fill = "Probabilidad") +
  theme_minimal()

También es conveniente hacer un análisis de otras variables como el ingreso medio, que es una de nuestras variables con mayor significancia para determinar la viabilidad y el mapa de calor de viabilidad que acabamos de mostrar.

NewYork_restaurantes_population<-ggplot(finalrestaurant_new_york_df) + geom_sf(aes(fill=population),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="red", size=0.35) +
  coord_sf(xlim = c(-79.76259, -71.77749), ylim = c(40.47658, 45.01586)) +
  scale_fill_scico(palette="bamako", direction=1) + theme_minimal()

NewYork_restaurantes_income<-ggplot(finalrestaurant_new_york_df) + geom_sf(aes(fill=median_income),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="red", size=0.35) +
  coord_sf(xlim = c(-79.76259, -71.77749), ylim = c(40.47658, 45.01586)) +
  scale_fill_scico(palette="lajolla", direction=1) + theme_minimal()

NewYork_population_vs_income<-plot_grid(NewYork_restaurantes_population,NewYork_restaurantes_income,
          ggdraw() + draw_label("Análisis de ingreso promedio y densidad de población", fontface = 'bold',size = 14,x = 0,hjust = 0),
          labels = c("Población total y restaurantes","Ingreso promedio y restaurantes"),
          ncol=2,
          align = "hv",
          axis = "tblr"
          )
ggsave("C:/Users/wars_/Documents/Universidad/Actuaría/2025-A/Computo_cientifico/Proyecto_final/Tablas_modelo_logit_newyork/Plots_finales/Plots_tramocensal/NewYork_population_vs_income_plot.png", 
       plot = NewYork_population_vs_income, width = 10, height = 10, dpi = 300)
NewYork_restaurantes_population

NewYork_restaurantes_income

NewYork_population_vs_income

NewYork_income_vs_viability<-plot_grid(NewYork_restaurantes_income,NewYork_Viability,
                                        ggdraw() + draw_label("Viabilidad vs Ingreso Promedio - Estado de Nueva York", fontface = 'bold', size = 14,x = 0,hjust = 0),
                                        labels = c("Ingreso promedio y restaurantes","Viabilidad de ubicación de restaurantes"),
                                        ncol=2,
                                        label_x = 0,      # 0 = far left, 1 = far right
                                        label_y = 1,      # Optional: top of the plot
                                        hjust = 0,        # Align text to the left edge of the label box
                                        align = "hv",
                                        axis = "tblr"
)
ggsave("C:/Users/wars_/Documents/Universidad/Actuaría/2025-A/Computo_cientifico/Proyecto_final/Tablas_modelo_logit_newyork/Plots_finales/Plots_tramocensal/NewYork_income_vs_viability_plot.png", 
       plot = NewYork_income_vs_viability, width = 10, height = 10, dpi = 300)
NewYork_income_vs_viability

Gráfico de viabilidad, en términos probabilísticos (entre 0 y 1) de la ciudad de Nueva York

Análogo al análisis previo sólo que enfocado en la ciudad de Nueva York, para ello, investigamos los condados que delimitan a la ciudad y en función a ello, filtrar nuestra base de datos.

#Filtracion para NY City
nyc_counties <- c("005", "047", "061", "081", "085")
new_york_censustracts <- new_york_censustracts %>% filter(COUNTYFP %in% nyc_counties)

map_data_nyc <- new_york_censustracts %>% left_join(resultados, by = "GEOID")


NewYorkCity_viability<-ggplot(map_data_nyc) +
  geom_sf(aes(fill = prob_viable), color = NA) +
  coord_sf(xlim = c(-74.25884, -73.70017), ylim = c(40.47658, 40.91770)) +
  scale_fill_viridis_c(option = "plasma") +
  labs(fill = "Probabilidad") +
  theme_minimal()

NewYorkCity_restaurantes_median_income<-finalrestaurant_new_york_df %>%
  mutate(median_income=ifelse(is.na(median_income),0,median_income)) %>%
  filter(COUNTYFP %in% nyc_counties) %>% 
  ggplot() + geom_sf(aes(fill=median_income),linewidth = 0.05) + 
  geom_sf(data=restaurants_sf, color="green", size=0.75) +
  coord_sf(xlim = c(-74.25884, -73.70017), ylim = c(40.47658, 40.91770)) +
  scale_fill_scico(palette="lajolla", direction=1)+theme_minimal()

NewYorkCity_income_vs_viability<-plot_grid(NewYork_restaurantes_income,NewYork_Viability,
                                       ggdraw(),
                                       labels = c("Ingreso promedio y restaurantes","Viabilidad de ubicación de restaurantes"),
                                       ncol=2,
                                       rel_widths = c(2, 2),
                                       label_x = 0,      # 0 = far left, 1 = far right
                                       label_y = 1,      # Optional: top of the plot
                                       hjust = 0,        # Align text to the left edge of the label box
                                       align = "hv",
                                       axis = "tblr"
)
ggsave("C:/Users/wars_/Documents/Universidad/Actuaría/2025-A/Computo_cientifico/Proyecto_final/Tablas_modelo_logit_newyork/Plots_finales/Plots_tramocensal/NewYorkCity_income_vs_viability_plot.png", 
       plot = NewYorkCity_income_vs_viability, width = 10, height = 10, dpi = 300)
NewYorkCity_restaurantes_median_income

NewYorkCity_income_vs_viability

Viabilidad de la ciudad de Nueva York, análisis individual

NewYorkCity_viability