Democracia Mexicana VS Cambridge Analytica

¿Qué pretendía hacer Cambridge Analytica en México?

Como lo explicamos en nuestro blog post, la metodología de Cambridge Analytica implica crear cámaras de eco para dividir al electorado en grupos, y transmitir mensajes específicos para estos grupos para moverlos a una acción determinada.

Por ejemplo, si queremos empujar una estrategia en la que un grupo 1 deba salir a las urnas a votar, y un grupo 2 deba quedarse en sus casas, confiados en que el partido político que apoyan resultará ganador, solo tenemos que mostrar mensajes de voto útil a dicho grupo 1, y mensajes de dominio de preferencia electoral en sondeos y encuestas para el grupo 2.

¿Por qué esto es factible?

La clave de su efectividad está en aislar ambos grupos para que los mensajes no se crucen entre ellos. Para esto, Cambridge Analytica y otras empresas de marketing político buscan crear cámaras de eco mediante técnicas avanzadas de segmentación de mercados, pero potenciadas en su efectividad por las grandes cantidades de observaciones y datos que dichas agencias podían (ya no, pero mañana quién sabe) obtener de redes sociales.

En este ejercicio vamos a mostrar cómo CA forma cámaras de eco, y cómo podemos romperlas.

Disclaimer: este trabajo es enteramente apartidista, y más bien pretende mostrar como se arman las cámaras de eco, y como podemos romperlas, para que nuestro ejercicio democrático sea lo más objetivo posible.

Metodología

CA tuvo acceso al trabajo del Dr. Michal Kosinski, en el cual se basó CA para construir su negocio.

Aunque el paper de Kosinski es libre, no tenemos el dataset que lo soporta, pero dado que conocemos que el flujo de trabajo, podemos reproducir la sección marcada con naranja, y éste será el enfoque de este trabajo.

Datos utilizados

El dataset de personality inventory que usaremos es el de 16 Personality Factors (16PF) del psicólogo británico Raymond Cattell, disponible en el sitio openpsychometrics.org (el test también está libre para tomarlo aquí).

El dataset incluye 49,159 respuestas a 163 reactivos, rankeadas en escala de Likert. El dataset puede descargarse aquí, junto con otros inventarios y encuestas de personalidad.

A continuación mostramos los primeros 10 de los 163 reactivos:

library(readr)
library(tidyverse)
library(cluster)
library(flexclust)
library(factoextra)  # clustering algorithms & visualization

# Cargar el dataset que contiene los 163 reactivos. Reactivos y respuestas
# están por separado.
pf16_questions <- read_delim("./data/16PF/codebook.csv", delim = ",")
# Seleccionar primeros 10
pf16_10_questions <- pf16_questions %>% filter(row_number() <= 10) %>% select(1, 
    3)
# Imprimirlos
kable(pf16_10_questions)
Field Description
A1 “I know how to comfort others” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A2 “I enjoy bringing people together” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A3 “I feel others’ emotions” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A4 “I take an interest in other people’s lives” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A5 “I cheer people up” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A6 “I make people feel at ease” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A7 “I take time out for others” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A8 “I don’t like to get involved in other people’s problems” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A9 “I am not really interested in others” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.
A10 “I try not to think about the needy” rated on a five point scale (1-5;0 if missed). 1 was labeled as “strongly disagree”, 2 was labeled as “disagree”, 3 was labeled as “neither agree not disagree”, 4 was labeled as “agree” and 5 was labeled as “strongly agree”.

Como reactivos y respuestas se encuentran separados, debemos cargar los datos en otro archivo. Entre los reactivos se encuentran al final 6 que levantan algunos demográficos de los encuestados, como país de orígen, tiempo que se llevó contestar la encuesta, y otros, la cuales vamos a eliminar por no formar parte de la encuesta original y no contener información sobre la personalidad de los encuestados. Aquí las variables:

# Cargar datos de las 49K respuestas al 16PF
pf16 <- read_delim("./data/16PF/data.csv", delim = "\t")
# Nombres de las columnas que vamos a eliminar
cols_to_drop <- names(pf16)[164:169]
# Imprimir columnas
cols_to_drop
[1] "age"      "gender"   "accuracy" "country"  "source"   "elapsed" 

Finalmente, mostraremos los primeros 10 registros del archivo de datos con las respuestas, aunque en el ejercicio utilizaremos todos.

# Eliminar columnas
pf16_clean <- pf16 %>% dplyr::select(-one_of(cols_to_drop))
kable(filter(pf16_clean, row_number() <= 10))
A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 E1 E2 E3 E4 E5 E6 E7 E8 E9 E10 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 G1 G2 G3 G4 G5 G6 G7 G8 G9 G10 H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 I1 I2 I3 I4 I5 I6 I7 I8 I9 I10 J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 M1 M2 M3 M4 M5 M6 M7 M8 M9 M10 N1 N2 N3 N4 N5 N6 N7 N8 N9 N10 O1 O2 O3 O4 O5 O6 O7 O8 O9 O10 P1 P2 P3 P4 P5 P6 P7 P8 P9 P10
1 4 2 3 3 2 3 4 4 3 4 4 5 4 5 4 5 4 1 2 1 1 1 4 5 4 4 2 4 4 3 3 2 4 3 5 5 4 4 3 2 4 3 1 1 4 3 4 5 1 3 3 4 2 2 5 1 1 4 3 5 4 4 3 1 4 2 3 4 3 1 5 1 5 5 5 3 3 3 3 1 2 4 3 4 4 3 4 4 2 4 2 4 5 5 5 5 5 5 5 2 1 1 2 3 2 4 4 5 2 5 2 5 3 4 3 3 5 3 4 4 2 4 5 4 4 4 4 1 1 1 3 3 4 4 4 4 4 5 5 4 4 3 4 2 3 4 3 2 2 4 4 4 5 5 5 4 4 5 1 2 5 2
4 3 4 3 4 4 4 4 2 2 4 4 4 4 5 4 3 2 3 2 4 1 1 1 2 3 3 2 5 4 4 3 3 4 2 4 4 4 5 4 2 3 1 1 2 4 1 4 2 2 4 2 1 3 4 2 4 5 2 4 4 2 4 3 4 2 4 4 2 3 2 3 3 5 5 5 1 4 3 4 2 1 2 5 4 3 2 4 2 2 2 3 2 5 4 5 4 4 2 4 3 2 2 3 4 2 4 3 2 2 2 3 4 3 4 4 4 2 1 4 2 2 3 4 2 4 4 2 1 1 1 2 2 4 2 4 5 5 5 5 2 4 1 3 2 4 2 3 2 2 3 4 4 4 4 4 2 3 2 3 2 2 2
3 4 4 4 4 4 4 3 2 2 4 4 5 5 4 4 4 4 2 2 2 2 2 2 4 4 3 3 3 4 2 3 0 3 3 2 2 3 4 3 1 3 3 1 1 3 1 4 2 3 4 2 4 3 4 1 2 3 3 3 2 3 4 2 2 1 3 3 3 3 3 5 4 3 4 3 4 1 4 2 3 0 2 3 4 4 3 4 2 2 3 4 4 4 4 4 5 5 3 3 3 1 1 3 4 4 4 4 3 2 2 3 4 4 3 3 4 4 4 2 3 3 4 3 4 5 4 4 2 1 2 1 2 4 4 5 4 4 5 4 3 4 1 3 2 3 3 3 2 3 4 3 3 3 3 4 2 4 2 2 3 3 3
4 5 4 4 4 3 3 2 2 2 4 2 4 5 4 5 4 4 3 3 3 2 4 3 2 3 4 3 3 2 2 3 4 3 2 3 4 2 3 3 3 4 3 3 2 4 1 4 4 1 3 2 2 4 3 1 3 2 4 3 4 3 3 2 4 4 4 3 3 3 2 3 3 4 5 2 1 4 2 3 2 2 2 2 2 4 3 4 2 4 2 4 1 5 5 5 5 5 4 4 1 1 1 3 3 1 4 3 2 2 2 3 3 3 2 4 3 4 4 4 4 3 2 4 4 5 3 2 1 4 2 2 2 4 5 5 5 5 5 5 1 3 3 2 2 2 2 3 4 5 4 5 4 4 2 2 2 3 4 2 3 4 4
4 0 4 4 4 3 5 1 2 4 2 4 4 5 5 4 4 5 4 1 5 1 2 2 4 3 3 4 4 4 4 3 2 5 4 3 4 5 4 1 1 1 3 1 1 3 1 4 2 4 5 2 3 4 4 4 4 4 2 4 4 1 3 3 1 4 2 5 2 1 1 2 2 4 4 3 3 1 4 2 4 1 2 4 2 4 4 2 2 3 2 3 3 3 4 2 4 4 1 2 5 4 2 4 4 1 4 4 2 3 2 2 4 4 2 4 4 4 4 4 1 3 3 2 4 4 4 5 1 2 2 1 2 3 3 4 4 5 4 5 4 4 2 3 3 5 3 4 2 1 2 1 4 4 3 4 3 4 3 4 2 3 2
3 5 4 4 4 5 5 1 1 4 4 1 4 5 3 4 3 3 2 2 1 2 2 2 3 4 2 4 2 2 4 3 1 4 4 5 5 4 3 2 2 2 3 3 4 3 2 4 3 2 3 2 4 2 3 2 2 1 4 4 5 4 3 3 4 4 4 4 2 4 2 4 2 5 5 4 2 4 4 1 1 1 2 4 4 2 3 4 1 4 4 4 4 3 3 3 4 4 3 4 2 3 3 4 4 3 2 3 4 3 3 5 4 2 2 5 4 4 4 2 1 4 3 4 4 4 3 4 2 5 2 2 1 4 4 5 4 4 5 4 4 4 2 2 1 4 4 4 4 2 5 3 4 2 4 5 2 3 3 4 4 2 5
4 2 5 4 2 4 4 2 1 3 5 3 5 5 5 3 5 5 2 2 1 1 1 1 4 4 2 3 4 5 1 2 3 4 3 3 3 4 3 3 2 3 3 1 1 2 1 1 1 2 5 3 4 3 4 1 4 2 1 2 3 1 2 3 1 4 3 3 4 3 2 4 4 3 5 3 2 3 2 2 3 1 1 4 2 3 2 2 1 4 4 3 2 1 5 4 4 3 2 3 3 1 1 1 2 3 2 3 4 4 4 4 5 4 2 4 5 4 5 4 1 2 3 4 4 5 4 4 2 1 2 1 2 2 4 4 4 2 3 5 2 2 2 4 3 4 4 4 3 3 3 4 3 2 2 4 4 2 2 4 2 3 4
4 5 4 4 4 4 5 2 2 2 3 3 4 5 4 4 4 5 4 2 3 1 1 3 4 4 4 2 2 2 3 2 3 4 3 3 3 4 4 2 2 2 3 2 2 3 1 4 1 2 5 1 4 3 5 5 4 5 2 4 4 2 4 4 3 2 2 4 4 3 2 3 3 5 4 5 5 4 5 2 2 5 2 3 4 2 2 4 2 3 3 4 4 2 4 1 2 2 1 2 4 4 2 4 4 2 4 4 4 4 2 4 4 4 4 3 4 4 5 3 3 2 3 4 4 4 4 4 2 1 2 1 2 4 3 4 4 4 5 5 3 4 2 4 3 4 4 4 4 2 4 4 3 2 3 2 2 3 2 1 4 4 3
2 4 2 5 5 4 5 4 2 2 5 3 5 5 4 4 5 4 3 4 1 2 2 4 4 2 4 4 2 0 2 2 4 2 4 2 2 4 4 4 2 3 2 1 2 5 2 4 2 4 4 2 2 4 4 1 5 5 1 4 2 2 2 4 2 4 3 4 2 2 2 4 4 4 4 4 1 2 4 2 1 2 1 2 4 4 2 4 2 4 5 5 4 4 2 2 4 2 2 4 4 4 4 2 2 2 4 4 4 4 4 4 4 4 2 2 4 4 2 4 2 2 4 4 4 4 2 4 2 1 2 2 2 2 2 4 2 4 4 4 0 4 4 4 4 4 5 4 4 4 4 4 4 2 2 1 2 2 3 2 4 4 4
5 5 3 3 5 4 4 3 1 3 4 4 5 5 5 3 4 5 2 1 2 1 2 4 4 4 4 4 2 2 1 1 2 3 3 3 4 3 4 2 2 3 2 3 4 5 3 4 3 1 2 2 2 3 4 5 5 5 1 3 1 2 3 4 4 3 5 4 2 1 2 2 3 3 4 2 1 1 3 2 3 2 1 2 3 3 3 4 1 4 4 4 3 3 3 3 3 3 4 3 4 2 2 3 1 3 3 3 3 2 4 1 3 3 2 2 3 3 2 2 3 4 4 3 4 4 4 3 2 2 3 2 2 3 2 3 3 4 4 4 4 4 1 4 2 4 4 4 1 3 1 2 2 2 1 1 3 2 3 2 4 4 3

Como ven, las columnas están nombradas a como está identificada cada pregunta en el el dataset de reactivos.

La escala de Likert que mencionamos arriba es como sigue:

  1. Sin respuesta
  2. Totalmente en desacuerdo
  3. En desacuerdo
  4. Ni de acuerdo ni en desacuerdo
  5. De acuerdo
  6. Totalmente de acuerdo

Con esto estamos simulando como CA pasó de mensajes, posts y likes en FB, a inventarios de personalidad para cada uno de los millones de registros individuales de usuarios que tuvieron en su poder, para después tratar de formar grupos con estos 49,000 registros de 163 respuestas.

Suposiciones

Haremos las siguientes suposiciones. Favor de considerarlas en todo momento al leer este trabajo:

  1. Usaremos distancia euclidiana: aunque son respuestas de tipo categórico, existe una graduación de menor a mayor del 0 al 5. Probamos la métrica de disimilitud de Gower como alternativa para variables categóricas, pero aunque este algoritmo nos permite dar pesos a cada variable, el grado de separación no difiere mucho de métricas más simples.

  2. Agruparemos con el algoritmo de K-means para buscar 4 grupos, correspondientes a 4 candidatos de la elección presidencial mexicana de 2018.

  3. Cada grupo identificado por K-means representa el electorado que apoya a uno de los 4 candidatos; una vez identificadas las características de cada grupo, se podrían crear briefs de marketing político para ajustar mensajes, pautas, anuncios, memes, robo-llamadas, etc.

¿Qué segmentos/grupos vamos a buscar?

Para poder visualizar los clusters en 2 dimensiones o variables (y no en 163 del dataset original) proyectaremos el conjunto de datos en un plano de 2 dimensiones, que son los 2 componentes principales de explicación de varianza, obtenidos con PCA.

A continuación mostramos los grupos encontrados por el algoritmo:

# Establecer hiperparams del algopritmo con el objeto flexclustControl
fc_cont <- new("flexclustControl")
fc_cont@tolerance <- 0.1
fc_cont@iter.max <- 30
fc_cont@verbose <- 0
fc_family <- "kmeans"  # aquí pueden ir otras alternativas, como gower, jaccard, etc.
# Asignando la semilla
fc_seed <- 12345
# Suponemos 4 grupos, 1 por cada candidato presidencial
num_clusters <- 4
# Establecemos la semilla
set.seed(fc_seed)
# Creamos los clusters
kcca_clust <- kcca(pf16_clean, k = num_clusters, save.data = T, control = fc_cont, 
    family = kccaFamily(fc_family))
# Para poder visualizar los clusters, dado que tenemos 163 variables,
# proyectaremos el conjunto de datos en un plano de 2 dimensiones, que son
# los 2 componentes principales de explicación de varianza, obtenidos con
# PCA.
kcca_pca <- prcomp(pf16_clean)
# Create plot
clusters_plot <- flexclust::plot(kcca_clust, data = pf16_clean, project = kcca_pca)

Si observamos las conexiones entre los 4 grupos, vemos que hay uno que es más distante que el resto, mientras que los otros se encuentran más cercanos entre ellos. Este grupo es candidato perfecto para ser bombardeado con mensajes que lo aislen aún más, y aprovecharlo para que estos encuestados nunca puedan ver los mensajes de otros grupos.

¿Cómo logra CA hacer cámaras de eco con estos grupos?

Después de obtener los grupos, CA bautiza los grupos con algún nombre representativo (por ejemplo, ‘personas de clase media-baja con pretensiones derechistas’) y pone a trabajar a su equipo de sociólogos y comunicólogos para descubrir sus características, para luego probar algunos mensajes y verificar su efectividad.

Finalmente se formulan marketing briefs que decantan en memes y fake news hiperpersonalizados y dirigidos a mover a dicho grupo a una acción de acuerdo a la estrategia.

Cabe mencionar que este proceso de bautizo de grupos implica numerosas interaciones entre politólogos, sociólogos y analistas de datos antes de que se definan bien los grupos, sus características y los mensajes con los cuales abordarlos.

¿Cómo combatimos las cámaras de eco?

Tomemos ahora a una muestra al azar del dataset original. Lo haremos con índices, obteniendo 49,000 muestras de una distribución uniforme, marcando con TRUE aquellas muestras mayores a 0.998, y finalmente relacionando estos índices a la muestra.

Marcaremos las observaciones seleccionadas en negro.

# Crear plot
clusters_plot <- flexclust::plot(kcca_clust, data = pf16_clean, project = kcca_pca)
# Crear índices de manera aleatoria, con una distribución uniforme, probando
# que cada indicencia sea mayor a .995, y marcando cada ocurrencia como TRUE
indices <- runif(nrow(pf16_clean)) > 0.998
# Mostrar en negro las observaciones seleccionadas por los índices
points(kcca_pca$x[indices, 1], kcca_pca$x[indices, 2], pch = 5)

Ahora tomaremos estas observaciones, y modificaremos sus respuestas registradas originalmente en el cuestionario 16 Personality Factors.

¿Qué efecto estamos buscando?

Como dijimos arriba, cuando CA recoge nuestros posts y likes de FB, los conecta con un inventario de personalidad para posteriormente segmentarnos. Al cambiar las respuestas de dicho inventario, estamos haciendo que nuestros posts, likes y follows de FB sean diferentes a lo que típicamente posteamos, y por tanto, nos salimos del segmento en el que CA nos ha ubicado.

Es decir, si tenemos un perfil liberal, según la clasificación de CA ejecutada via FB, y si seguimos páginas, compartimos memes, y damos like a posts que también cumplen con esta clasificación, y si súbitamente seguimos páginas, damos like a posts y compartimos contenido que no cumple este criterio, entonces, literalmente, estaremos saliendo de la clasificación liberal determinada por FB.

El efecto de salir de la cámara de eco sucede porque logramos confundir a los algoritmos de FB cuando nos salimos del segmento, provocando que nos muestren noticias de segmentos diferentes, en un intento por reubicarnos en otro.

¿Cómo lo haremos en este ejercicio?

Con el grupo seleccionado arriba (los puntos negros), simularemos un cambio de personalidad de los encuestados modificando sus respuestas al 16PF.

Seleccionaremos el 50% de las 163 columnas de la muestra extraída arriba, y las llevaremos todas a la respuesta 1, que significa “en total desacuerdo”. Es importante mencionar que entre más preguntas (o columnas) sean alteradas al mismo valor, más cerrado será este grupo, y menos disperso estará en el plano, dado lo atípico que es que 104 individuos tengan la misma respuesta a preguntas desconectadas entre si.

Después vamos a obtener su nuevo lugar en el plano bidimensional que construímos con PCA, que esperamos sea diferente a su lugar original por estos cambios de personalidad simulados.

# Crear nuevamente el plot
clusters_plot <- flexclust::plot(kcca_clust, data = pf16_clean, project = kcca_pca)
# Copiar del dataset original solamente las observaciones marcadas por los
# índices ficticios
new_obs <- pf16_clean[indices, ]
# Crear también un vector de índices aleatorios representando las columnas
cols_to_change <- runif(ncol(new_obs)) > 0.5
# De las columnas que representan reactivos del 16PF que fueron
# seleccionadas por los índices aleatorios de arriba, llevarlas todas a 1,
# simulando que todas esas columnas tuvieron una respuesta de 'totalmente en
# desacuerdo'
new_obs[, cols_to_change] <- 1
# Obtener las nuevas posiciones de la muestra modificada por la línea
# anterior.  Aquí la cuestión técnica más importante es que el objeto PCA es
# también un objeto de tipo fit/model, justo como un objeto kmeans, o un
# objeto randomforest, etc.  y como tal, está sujeto a ser enviado como
# argumento al método predict. Nice!
pca_new_obs <- predict(kcca_pca, new_obs)
# Desplegar los puntos con sus nuevas posiciones, dado el cambio a 1, en el
# plano.
points(pca_new_obs[, 1], pca_new_obs[, 2], pch = 5)

Como podemos ver, el haber llevado el 50% de las preguntas a una respuesta totalmente en desacuerdo ha ayudado a este grupo de 104 personas de varios segmentos a salirse de las mayores concentraciones de los grupos 1, 2 y 4, y acercarse más al grupo 3, que es el más diferente al resto.

No solo eso, sino que hay observaciones que originalmente estaban en un grupo, y cambiaron a otro, como se muestra a continuación:

# Obtenemos el nuevo grupo al que pertenecen las observaciones a las que le
# hemos modificado su 'personalidad'
new_clust <- predict(kcca_clust, new_obs)
# Obtenemos las observaciones en su estado original, a como fueron obtenidas
# de todo el universo de 49K observaciones
old_clust <- kcca_clust@cluster[indices]
# formamos el dataframe para desplegar esta info
summary_diff_clust <- tibble(antes_de_cambiar_personalidad = old_clust, despues_de_cambiar_personalidad = new_clust)
# desplegamos las primeras 10 observaciones
kable(head(filter(summary_diff_clust, antes_de_cambiar_personalidad != despues_de_cambiar_personalidad), 
    10))
antes_de_cambiar_personalidad despues_de_cambiar_personalidad
4 1
2 4
2 4
2 4
2 4
4 1
2 1
2 4
4 1
4 1

Veamos qué sucede si llevamos esas mismas respuestas al valor 5, que significa ‘totalmente de acuerdo’.

# Crear nuevamente el plot
clusters_plot <- flexclust::plot(kcca_clust, data = pf16_clean, project = kcca_pca)
# De las columnas que representan reactivos del 16PF que fueron
# seleccionadas por los índices aleatorios de arriba, llevarlas todas a 1,
# simulando que todas esas columnas tuvieron una respuesta de 'totalmente en
# desacuerdo'
new_obs[, cols_to_change] <- 5
# Obtener las nuevas posiciones de la muestra modificada por la línea
# anterior.  Aquí la cuestión técnica más importante es que el objeto PCA es
# también un objeto de tipo fit/model, justo como un objeto kmeans, o un
# objeto randomforest, etc.  y como tal, está sujeto a ser enviado como
# argumento al método predict. Nice!
pca_new_obs <- predict(kcca_pca, new_obs)
# Desplegar los puntos con sus nuevas posiciones, dado el cambio a 1, en el
# plano.
points(pca_new_obs[, 1], pca_new_obs[, 2], pch = 5)

Podemos ver que ahora el grupo se ha ido a otro extremo de la nube de datos en el plano. Efectivamente, mover las preferencias mueve nuestra pertenencia a otros grupos.

¿Cómo interpreto este efecto en mis redes sociales?

El efecto, entonces, es en 2 partes:

  1. Se reduce tu ‘grado de pertenencia’ a tu grupo original. Si imaginas que los grupos tienen un centro, y entre más cerca del centro, más perteneces a él, entonces alejarte del centro reduce este grado de pertenencia, y por tanto FB comenzará a mostrarte mensajes de otros grupos, para intentar ubicarte.
  2. Cambias enteramente de grupo. Esto es lo que buscamos, y se da cuando estás tan alejado del centro de tu grupo, que te acercas al centro de otro grupo diferente, efectivamente cambiando tu pertenencia, con lo cual FB te mostrará contenido relacionado a ese nuevo grupo.

Aquí es importante mencionar que casi todos los algoritmos de agrupación/clustering intentan partir todo el espacio de datos, así que no habrá observaciones que pertenezcan a ningún cluster, porque entonces habría un grupo de esos que no pertenecen a ninguno.

Ambos efectos abonan al objetivo de romper las cámaras de eco. Ambas te ayudarán a abrir tus redes sociales (y tu persona) a nuevas ideas. No todas serán de calidad, y seguro no estarás de acuerdo con muchas, dado que vienes de otro grupo con el cual si concordabas, pero este es el espíritu de este ejercicio, y de poco a poco romper las barreras que han aprovechado las empresas de mkting político para aislarnos y polarizarnos.

Mi propia experiencia rompiendo cámaras de eco

Yo soy de Torreón, Coahuila, pero en 2002 me vine a la CDMX. Tengo mucha familia allá. Como egresado del ITESM, en ese tiempo mi alineación era de centro-derecha. Pero la CDMX me mostró varias caras de México que no conocía, y poco a poco fui cambiando mis preferencias políticas, pero algunos de mis familiares no las han cambiado del todo.

Mi TL de FB me muestra entonces que tengo un pié en un grupo, y otro pié en otro, con mis primas compartiendo memes, posts y contenido alineado a Ricardo Anaya por un lado, y por otro, mis amigos compartían contenido apoyando a AMLO. FB me mostraba ambos por considerarlos relevantes para mi, los mensajes de mis primas por las conexiones, y los mensajes de mis amigos por el perfil liberal que compartimos.

Involuntariamente, rompí mi cámara de eco al mantener mis conexiones en redes sociales con mis familiares, y pude enterarme de las posiciones y sentimientos de ambos lados. Si este contenido hubiera sido menos inocuo, más virulento o más dañino, hubiera perdido todo su efecto gracias a la probabilidad de que se muestren mensajes contradictorios, al pertenecer a grupos opuestos.

Conclusiones

Las 5 dimensiones culturales de Hofstede son métricas de aspectos de cultura organizacional. Una de sus métricas es la ‘distancia al poder’, que para México es alta (81/120). Las redes sociales nos permiten reducirla dramáticamente, además de darnos agencia, por primera vez en muchos años, sobre nuestra relación con procesos electorales y candidatos. No solo eso, sino que en el terreno de las redes sociales el poder queda desconcentrado, y podemos activamente contrarrestar iniciativas que deseen manipular nuestra opinión, como lo deseaba hacer CA en su momento.

Finalmente, las cámaras de eco buscan dividir y aislar, y este proceso erosiona las coincidencias y el common ground entre grupos políticos. Esto es peligroso porque las instituciones, y por tanto la democracia, funciona sobre coincidencias. El atentar contra ellas, sobre todo en una democracia con instituciones débiles como la nuestra, es moralmente cuestionable. Por tanto, abrir nuestras redes a otras ideas no solo es bueno para nosotros, sino para el país.

Créditos y agradecimientos

Agradecemos a:

  • The Data Pub por el espacio para presentar este proyecto.
  • AIXSW.mx por los recursos otorgados para su ejecución.
  • SocialTIC, particularmente a Juan M. Casanueva, por la difusión, revisiones, comentarios, y constante recordatorio de que este trabajo se mantenga aterrizado.

Jesús Ramos / @xuxoramos / xuxoramos[at]aixsw[dot]mx

2019-03-11