homophily

Is everywhere!

By Roberto Cantillan

October 26, 2022

Homophily

Homophily

libraries

library(ergm)
library(ergm.ego)
library(car)
library(egor)
library(tidyverse)
library(tibble)
library(texreg)
library(purrr)
library(tidyr)
library(prioritizr)
library(questionr)

data

load("/home/rober/Documents/ricantillan.rbind.io/dat/ELSOC/ELSOC_W02_v3.00_R.RData")
load("/home/rober/Documents/ricantillan.rbind.io/dat/ELSOC/ELSOC_W04_v2.01_R.RData")
load("/home/rober/Documents/ricantillan.rbind.io/dat/ELSOC/ELSOC_W01_v4.01_R.RData")

ELSOC 2017

Renombrar ID

a<-elsoc_2017 %>% dplyr::rename(.egoID = idencuesta)

Crear data frame alteris para 2017=a

Creamos subset con data de cada uno de los alteris mencionados, manteniendo el ID de cada ego en el cual están anidados. Las columnas de cada uno de los subset deben tener los mismos nombres.

columnas <- c("sexo", "edad", "educ", "relig", "ideol", "barrio", "relacion")
num_alters <- 5

alter_list <- list()

for (i in 1:num_alters) {
  alter_cols <- paste0("r13_", columnas, "_", sprintf("%02d", i))
  alter <- a %>%
    dplyr::select(.egoID, all_of(alter_cols)) %>%
    rename_with(~ columnas, alter_cols) %>%
    mutate(n = i)
  
  alter_list[[i]] <- alter
}

alteris <- bind_rows(alter_list)
alteris<-arrange(alteris, .egoID)

Crear vector alter id

En el siguiente chunk creamos un vector identificador para cada uno de los alteris presentes en la data “alteris”. Lo identificamos como objeto tibble y eliminamos el vector “n”.

alteris   <- rowid_to_column(alteris, var = ".alterID")
alteris   <- as_tibble(alteris)
alteris$n <- NULL

Recod alteris

Recodificamos los valores de los atributos de los alteris.

# NA
alteris[alteris=="-999"]<-NA
alteris[alteris=="-888"]<-NA

# Educación 
#edulab<-c('ltsecondary', 'secondary', 'technicaled', 'collegeed')
alteris$educ <-factor(Recode(alteris$educ ,"1=1;2:3=2;4=3;5=4"))
table(alteris$educ)
   1    2    3    4 
1362 3543 1042 1719 
# Religión 
#relilab<-c('catholic','evangelical','other','none')
alteris$relig<-factor(Recode(alteris$relig,"1=1;2=2;3:4=4;5=3"))
table(alteris$relig)
   1    2    3    4 
4907 1407  373 1379 
# Ideología 
#ideolab<-c('rightwinger','centerright','center','centerleft','leftwinger','none')
alteris$ideol<-factor(Recode(alteris$ideol,"1=1;2=2;3=3;4=4;5=5;6=6"))
table(alteris$ideol)
   1    2    3    4    5    6 
 786  191  382  303  759 4644 
# Edad 
alteris$edad<-as.numeric(alteris$edad)
#alteris$edad <-factor(Recode(alteris$edad ,"0:18=1;19:29=2;30:40=3;41:51=4;52:62=5;63:100=6"))

# Sexo 
#sexolab<-c('male','female')
alteris$sexo <-factor(Recode(alteris$sexo ,"1=1;2=2"))
table(alteris$sexo)
   1    2 
3388 4678 
# Relación
alteris<-alteris%>%
  dplyr::mutate(rel=case_when(relacion%in%1:3~"fam",
                              relacion%in%4:5~"nofam"))
table(alteris$relacion)
   1    2    3    4    5 
 951 1181 2519 2577  838 
# Barrio 
alteris$barrio<-factor(Recode(alteris$barrio,"1=1;2=2"))
table(alteris$barrio)
   1    2 
4271 3788 
#alteris<-na.omit(alteris)

Borrar alteris con 5 parámetros con NA

# Función para borrar casos con un número determinado de NA's. 
#delete.na <- function(DF, n=0) {
#  DF[rowSums(is.na(DF)) <= n,]
#}
#
#alteris<-delete.na(alteris, 4) #borro los casos que tienen más de 4 NA.  

Data Frame Ego’s

Creamos un subset con la data de ego equivalente a la data de los alteris. Las nombramos de la misma manera.

egos <-a %>%
       dplyr::select(.egoID, 
                     sexo=m0_sexo, 
                     edad=m0_edad, 
                     educ=m01, 
                     relig=m38, 
                     ideol=c15,
                     ponderador02,
                     estrato,
                     segmento)

egos <- as_tibble(egos)

Recod data Ego’s

Recodificamos las variables de la data de ego siguiendo el patrón de la data de alteris.

# NA
egos[egos=="-999"]<-NA
egos[egos=="-888"]<-NA

# Educación
egos$educ <-factor(Recode(egos$educ,"1:3=1;4:5=2;6:7=3;8:10=4"))
table(egos$educ)
   1    2    3    4 
 597 1045  402  429 
# Religión
egos$relig<-factor(Recode(egos$relig,"1=1;2=2;3:6=3;7:9=4"))
table(egos$relig)
   1    2    3    4 
1383  499  276  308 
# Ideología
#ideolab2<-c('leftwinger','centerleft','center','centerright','rightwinger','none')
egos$ideol<-factor(Recode(egos$ideol,"0:2=5;3:4=4;5=3;6:7=2;8:10=1;11:12=6"))
table(egos$ideol)
   1    2    3    4    5    6 
 252  149  470  209  282 1080 
# Edad
egos$edad<-as.numeric(egos$edad)
#egos$edad <-factor(Recode(egos$edad,"18=1;19:29=2;30:40=3;41:51=4;52:62=5;63:100=6"))

# Sexo
egos$sexo <-factor(Recode(egos$sexo,"1=1;2=2"))
table(egos$sexo)
   1    2 
 951 1522 
# Barrio
egos$barrio <- matrix(rbinom(2473*5,1,0.6),2473,1) # Criterio minimalista
egos$barrio<-factor(Recode(egos$barrio,"1=1;0=2"))
table(egos$barrio)
   1    2 
1506  967 

Crear objeto Egor (requerido para trabajar con función ergm.ego)

# definir diseño complejo
elsoc_ego <- egor(alters = alteris, 
                  egos = egos,
                  alter_design = list(max = 5),
                  ID.vars = list(
                    ego = ".egoID",
                    alter = ".alterID")) %>% as.egor()

ego_design(elsoc_ego) <- list(weight = "ponderador02",
                              strata = "estrato",
                              cluster="segmento") 

# eliminar atributos de diseño
elsoc_ego[["ego"]][["variables"]][["ponderador02"]]<-NULL
elsoc_ego[["ego"]][["variables"]][["estrato"]]<-NULL
elsoc_ego[["ego"]][["variables"]][["segmento"]]<-NULL

# drop NA
variables <- c("sexo", "educ", "relig", "ideol", "barrio")
for (variable in variables) {
  elsoc_ego[["ego"]] <- elsoc_ego[["ego"]] %>% drop_na({{ variable }})
  elsoc_ego[["alter"]] <- elsoc_ego[["alter"]] %>% drop_na({{ variable }})
}

as_tibble(elsoc_ego$alter)
# A tibble: 6,849 × 10
   .altID .egoID  sexo   edad educ  relig ideol barrio relacion rel  
   <chr>  <chr>   <fct> <dbl> <fct> <fct> <fct> <fct>     <dbl> <chr>
 1 1      1101011 1        77 2     1     5     1             1 fam  
 2 6      1101012 1        60 2     1     6     1             1 fam  
 3 11     1101013 2        54 1     1     6     1             3 fam  
 4 12     1101013 2        33 2     1     6     1             3 fam  
 5 16     1101021 1        55 2     1     6     1             4 nofam
 6 21     1101022 2        44 4     4     6     1             2 fam  
 7 26     1101023 1        40 2     1     6     1             3 fam  
 8 31     1101032 2        31 4     1     6     1             2 fam  
 9 32     1101032 1        57 2     1     5     1             1 fam  
10 36     1101033 1        37 2     1     2     1             1 fam  
# ℹ 6,839 more rows

Degree distribution

Educación

degreedist(elsoc_ego, by="educ", prob=T, plot = T, weight=TRUE)

Sexo

degreedist(elsoc_ego, by="sexo", prob=T, plot = T, weight=TRUE)

Modelos

nodefactor = controla el grado de los diferentes grupos (ajustando las estimaciones de homofilia por el hecho de que algunos grupos, por ejemplo, los hombres, tienen más vínculos que otros grupos, como las mujeres).

El paquete “ergm” proporciona no sólo estadísticas resumidas sino también valores-p. Sin embargo, como indica Kolaczyk y Csárdi (2020), la justificación teórica para las distribuciones asintóticas chi-cuadrado y F utilizadas por ergm para calcular estos valores no se ha establecido hasta la fecha. Por lo tanto, puede ser pertinente interpretar estos valores de manera informal, como estadísticas resumidas adicionales.

## Set # replicates
reps = 1
## Set ppop size and construction
ppop = 15000
ppopwt = 'round' # Round gives consistent netsize/composition
constraint.formula <- (~ bd(maxout = 5)) # constraint por grado máximo para cada ego. 

Modelo 1

modelo1<-ergm.ego(elsoc_ego~
                    #edges + 
                    #degree(1:5) +
                    nodefactor("sexo", levels = -2) +
                    nodefactor("educ") +
                    nodefactor("ideol") +
                    nodefactor("relig") +
                    nodematch("sexo", diff=TRUE) +
                    nodematch("educ", diff=TRUE) +
                    nodematch("ideol", diff=TRUE)+
                    nodematch("relig", diff=TRUE)+
                    absdiff("edad"),
                 constraints = constraint.formula,
                 control=control.ergm.ego(ppopsize=5000,
                                         ppop.wt=ppopwt,
                                         stats.wt="data",
                                         stats.est="survey"),
                  ergm = control.ergm(init.method = "MPLE",
                                      init.MPLE.samplesize = 5e7,
                                      MPLE.constraints.ignore = TRUE,
                                      MCMLE.effectiveSize = NULL,
                                      MCMC.burnin = 5e4,
                                      MCMC.interval = 5e4,
                                      MCMC.samplesize = 1000000,
                                      parallel = 16,
                                      SAN.nsteps = 5e7))
Warning: Argument(s) 'ergm' were not recognized or used. Did you mistype an
argument name?
#?control.ergm.ego
#??ignore.max.alters

Summary

summary(modelo1)
Call:
ergm.ego(formula = elsoc_ego ~ nodefactor("sexo", levels = -2) + 
    nodefactor("educ") + nodefactor("ideol") + nodefactor("relig") + 
    nodematch("sexo", diff = TRUE) + nodematch("educ", diff = TRUE) + 
    nodematch("ideol", diff = TRUE) + nodematch("relig", diff = TRUE) + 
    absdiff("edad"), constraints = constraint.formula, control = control.ergm.ego(ppopsize = 5000, 
    ppop.wt = ppopwt, stats.wt = "data", stats.est = "survey"), 
    ergm = control.ergm(init.method = "MPLE", init.MPLE.samplesize = 5e+07, 
        MPLE.constraints.ignore = TRUE, MCMLE.effectiveSize = NULL, 
        MCMC.burnin = 50000, MCMC.interval = 50000, MCMC.samplesize = 1e+06, 
        parallel = 16, SAN.nsteps = 5e+07))

Monte Carlo Maximum Likelihood Results:

                     Estimate Std. Error MCMC % z value Pr(>|z|)    
offset(netsize.adj) -8.499436   0.000000      0    -Inf  < 1e-04 ***
nodefactor.sexo.1    0.026094   0.374902      0   0.070 0.944511    
nodefactor.educ.2    0.510454   0.111578      0   4.575  < 1e-04 ***
nodefactor.educ.3    0.453725   0.152892      0   2.968 0.003001 ** 
nodefactor.educ.4    0.495349   0.184745      0   2.681 0.007335 ** 
nodefactor.ideol.2  -0.322233   0.208094      0  -1.548 0.121503    
nodefactor.ideol.3  -0.383891   0.188433      0  -2.037 0.041622 *  
nodefactor.ideol.4  -0.073608   0.224878      0  -0.327 0.743423    
nodefactor.ideol.5   0.000392   0.215906      0   0.002 0.998551    
nodefactor.ideol.6   0.429487   0.205463      0   2.090 0.036587 *  
nodefactor.relig.2  -0.662670   0.153775      0  -4.309  < 1e-04 ***
nodefactor.relig.3  -0.224157   0.177162      0  -1.265 0.205776    
nodefactor.relig.4   0.207720   0.158543      0   1.310 0.190133    
nodematch.sexo.1     0.265240   0.377637      0   0.702 0.482450    
nodematch.sexo.2     0.605081   0.386875      0   1.564 0.117812    
nodematch.educ.1     0.822089   0.135593      0   6.063  < 1e-04 ***
nodematch.educ.2     0.376890   0.112730      0   3.343 0.000828 ***
nodematch.educ.3     0.689269   0.142157      0   4.849  < 1e-04 ***
nodematch.educ.4     1.656137   0.139848      0  11.842  < 1e-04 ***
nodematch.ideol.1    1.708763   0.199848      0   8.550  < 1e-04 ***
nodematch.ideol.2    1.075087   0.323500      0   3.323 0.000890 ***
nodematch.ideol.3   -0.019859   0.238084      0  -0.083 0.933523    
nodematch.ideol.4    0.291631   0.256309      0   1.138 0.255199    
nodematch.ideol.5    1.565110   0.191898      0   8.156  < 1e-04 ***
nodematch.ideol.6    0.548568   0.134902      0   4.066  < 1e-04 ***
nodematch.relig.1    0.859990   0.130173      0   6.607  < 1e-04 ***
nodematch.relig.2    2.415272   0.171209      0  14.107  < 1e-04 ***
nodematch.relig.3    0.787513   0.262693      0   2.998 0.002719 ** 
nodematch.relig.4    0.740375   0.169100      0   4.378  < 1e-04 ***
absdiff.edad        -0.031325   0.002267      0 -13.817  < 1e-04 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


 The following terms are fixed by offset and are not estimated:
  offset(netsize.adj) 

Para interpretar los coeficientes, es útil pensar en términos de la probabilidad de que un par dado de nodos tenga un vínculo, condicionada al estado del nodo entre todos los demás pares. Para el término de homofilia de género (segundo orden) en el modelo anterio: El empate entre dos nodos mujeres, casi cuatriplica las probabilidades (odds) de tener un vínculo en la red observada (por su puesto, manteniendo todo lo demás igual).

Vale indicar también que para todas las variables el coeficiente difiere de cero en al menos un error estándar, lo que sugiere algún efecto no trivial de estas variables en la formación de vínculos en la red.

Bondad de ajuste

La práctica actual para evaluar la bondad de ajuste en modelos ERGM es simular primero numerosos gráficos aleatorios del modelo ajustado y luego comparar varios resúmenes de estos gráficos con los del gráfico observado originalmente. Si las características de los grafos de red observados no coinciden con los valores típicos que surgen de las realizaciones del modelo de gráfico aleatorio ajustado, esto sugiere diferencias sistemáticas entre la clase especificada de modelos y los datos y, por lo tanto, una falta de bondad.

En general, al evaluar la bondad de ajuste en el modelado de redes, los resúmenes de uso común incluyen la distribución de cualquier número de los diversos resúmenes de la estructura de la red: como el grado, la centralidad y la distancia geodésica. Con los ERGMs, sin embargo, una elección natural de resumen son las propias estadísticas $g$ que definen el ERGM (es decir, las llamadas estadísticas suficientes). Para evaluar la bondad de ajuste de nuestro modelo anterior de homofilia, la función ergm ejecuta las simulaciones de Monte Carlo necesarias y calcula las comparaciones con la red original en términos de las distribuciones de cada uno de los estadísticos en el modelo.

plot(gof(modelo1))

Considerando las características particulares capturadas por las estadísticas, el ajuste del modelo es bastante bueno en general, toda vez que las estadísticas observadas están bastante cerca de la mediana de los valores simulados en la mayoría de los casos.

plot(gof(modelo1, GOF="degree"))

MCMC

mcmc.diagnostics(modelo1, which = "plots")

Note: To save space, only one in every 2 iterations of the MCMC sample
  used for estimation was stored for diagnostics. Sample size per chain
  was originally around 4418 with thinning interval 16384.

Note: MCMC diagnostics shown here are from the last round of
  simulation, prior to computation of final parameter estimates.
  Because the final estimates are refinements of those used for this
  simulation run, these diagnostics may understate model performance.
  To directly assess the performance of the final model on in-model
  statistics, please use the GOF command: gof(ergmFitObject,
  GOF=~model).

simulate

sim.modelo1 <- simulate(modelo1, popsize=500,
                       control=control.simulate.ergm.ego(
                       simulate=control.simulate.formula(MCMC.burnin=2e6)))
Note: Constructed network has size 244 different from requested 500. Simulated statistics may need to be rescaled.
plot(sim.modelo1, vertex.col="educ")
legend('bottomleft',fill=1:4,legend=paste('Eduación',1:4),cex=0.75)

Tidy

broom::tidy(modelo1, exponentiate = TRUE, conf.int = TRUE, conf.level = 0.99)
Warning in tidy.ergm(modelo1, exponentiate = TRUE, conf.int = TRUE, conf.level
= 0.99): Exponentiating but model didn't use log or logit link.

# A tibble: 30 × 8
   term      estimate std.error mcmc.error  statistic p.value conf.low conf.high
   <chr>        <dbl>     <dbl>      <dbl>      <dbl>   <dbl>    <dbl>     <dbl>
 1 offset(n… 0.000204     0              0 -Inf       0       0.000204  0.000204
 2 nodefact… 1.03         0.375          0    0.0696  9.45e-1 0.391     2.70    
 3 nodefact… 1.67         0.112          0    4.57    4.77e-6 1.25      2.22    
 4 nodefact… 1.57         0.153          0    2.97    3.00e-3 1.06      2.33    
 5 nodefact… 1.64         0.185          0    2.68    7.33e-3 1.02      2.64    
 6 nodefact… 0.725        0.208          0   -1.55    1.22e-1 0.424     1.24    
 7 nodefact… 0.681        0.188          0   -2.04    4.16e-2 0.419     1.11    
 8 nodefact… 0.929        0.225          0   -0.327   7.43e-1 0.521     1.66    
 9 nodefact… 1.00         0.216          0    0.00182 9.99e-1 0.574     1.74    
10 nodefact… 1.54         0.205          0    2.09    3.66e-2 0.905     2.61    
# ℹ 20 more rows

Predict

#predict(modelo1, type = "response")
Posted on:
October 26, 2022
Length:
9 minute read, 1858 words
See Also: