A continuación se procede a hacer clustering sobre los atributos numéricos de las cuentas. Es decir, sobre las diferencias followers, following y updates, y el crecimiento por día de followers y updates. El fin de este experimento es observar si existen clases naturales producidas por la distribución de los datos en estas características. De existir, podrían incluirse a futuro como otra forma de definir popularidad.
library(tidyverse)
## Registered S3 methods overwritten by 'ggplot2':
## method from
## [.quosures rlang
## c.quosures rlang
## print.quosures rlang
## -- Attaching packages --------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.1.1 v purrr 0.3.2
## v tibble 2.1.1 v dplyr 0.8.1
## v tidyr 0.8.3 v stringr 1.4.0
## v readr 1.3.1 v forcats 0.4.0
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
data <- read.csv("datos_numericos_cuentas.csv")
data$X<- NULL
Intento ingenuo de hacer clustering con todos los atributos
set.seed(413)
scaled <- scale(data %>% select(2:14))
wss <- 0
clust = 15 # graficaremos hasta 15 clusters
for (i in 1:clust){
wss[i] <-
sum(kmeans(scaled, centers=i)$withinss)
}
plot(1:clust, wss, type="b", xlab="Numero de clusters", ylab="wss")
Se usan 6 nodos
kmall.out <- kmeans(scaled, 6, nstart = 20)
pairs(data, col=kmall.out$cluster)
Conclusión: FRACASO
No hay un patrón descifrable en las figuras
set.seed(413)
scaledday <- scale(data %>% select(12,14))
wss <- 0
clust = 15 # graficaremos hasta 15 clusters
for (i in 1:clust){
wss[i] <-
sum(kmeans(scaledday, centers=i)$withinss)
}
plot(1:clust, wss, type="b", xlab="Numero de clusters", ylab="wss")
Se usan 4 nodos.
kmday.out <- kmeans(scaledday, 4, nstart = 20)
plot(data %>% select(12,14), main="Clusters de cambios por dia", col =(kmday.out$cluster), xlab="follower/day", ylab="updates/day")
Dos outliers arruinan el clustering. Limpiando..
set.seed(413)
scaledday <- scale(data %>% select(12,14) %>% filter(followers_day < 2000, updates_day < 20000))
wss <- 0
clust = 15 # graficaremos hasta 15 clusters
for (i in 1:clust){
wss[i] <-
sum(kmeans(scaledday, centers=i)$withinss)
}
plot(1:clust, wss, type="b", xlab="Numero de clusters", ylab="wss")
Se usan 4 nodos
kmday.out <- kmeans(scaledday, 4, nstart = 20)
plot(data %>% select(12,14) %>% filter(followers_day < 2000, updates_day < 20000), main="Clusters de cambios por dia", col =(kmday.out$cluster), xlab="follower/day", ylab="updates/day")
Conclusión: Factible
Posible interpretación
Es posible que todos los datos sin crecimiento de followers sean bots.
set.seed(413)
scaleddiff <- scale(data %>% select(4, 10, 11))
wss <- 0
clust = 15 # graficaremos hasta 15 clusters
for (i in 1:clust){
wss[i] <-
sum(kmeans(scaleddiff, centers=i)$withinss)
}
plot(1:clust, wss, type="b", xlab="Numero de clusters", ylab="wss")
Se usan 6 nodos
kmdiff.out <- kmeans(scaleddiff, 6, nstart = 20)
pairs(data %>% select(4, 10, 11), main="Clusters de diferencias", col=(kmdiff.out$cluster))
Varios outliers afectan los clusters. Limpiando..
set.seed(413)
scaleddiff <- scale(data %>% select(4, 10, 11) %>% filter(diff_updates < 30000, diff_publish_date < 800, diff_followers < 40000))
wss <- 0
clust = 15 # graficaremos hasta 15 clusters
for (i in 1:clust){
wss[i] <-
sum(kmeans(scaleddiff, centers=i)$withinss)
}
plot(1:clust, wss, type="b", xlab="Numero de clusters", ylab="wss")
Se usan 5 nodos
kmdiff.out <- kmeans(scaleddiff, 5, nstart = 20)
pairs(data %>% select(4, 10, 11) %>% filter(diff_updates < 30000, diff_publish_date < 800, diff_followers < 40000), main="Clusters de diferencias", col=(kmdiff.out$cluster))
Conclusión: Factible
Posible interpretación
kmday.out
## K-means clustering with 4 clusters of sizes 7, 2623, 17, 115
##
## Cluster means:
## followers_day updates_day
## 1 13.851241269 1.8842424
## 2 -0.158133165 -0.0916677
## 3 -0.009894483 11.4104558
## 4 2.765154865 0.2893647
##
## Within cluster sum of squares by cluster:
## [1] 177.8485 367.7555 118.2409 300.3675
## (between_SS / total_SS = 82.5 %)
##
## Available components:
##
## [1] "cluster" "centers" "totss" "withinss"
## [5] "tot.withinss" "betweenss" "size" "iter"
## [9] "ifault"
kmdiff.out
## K-means clustering with 5 clusters of sizes 1637, 144, 54, 46, 860
##
## Cluster means:
## diff_followers diff_updates diff_publish_date
## 1 -0.22387810 -0.29092429 -0.5996253
## 2 0.58740368 3.00179453 0.7465959
## 3 0.47613078 0.50475606 4.4456397
## 4 6.40969955 2.20746268 1.2197632
## 5 -0.04494758 -0.09862262 0.6719805
##
## Within cluster sum of squares by cluster:
## [1] 332.0654 679.2508 102.3365 517.9097 677.6000
## (between_SS / total_SS = 71.9 %)
##
## Available components:
##
## [1] "cluster" "centers" "totss" "withinss"
## [5] "tot.withinss" "betweenss" "size" "iter"
## [9] "ifault"
La tabla contiene los resultados de ambos modelos.
cluster_day <- (data %>% filter(followers_day < 2000, updates_day < 20000) %>% select(1))
cluster_day['cl_day'] = kmday.out$cluster
cluster_diff <- (data %>% filter(diff_updates < 30000, diff_publish_date < 800, diff_followers < 40000) %>% select(1))
cluster_diff['cl_diff'] = kmdiff.out$cluster
res_clusters <- (cluster_day %>% left_join(cluster_diff, by="author"))
write.csv(res_clusters, "clusters_cuentas.csv", row.names=F, na="")
Si bien existen patrones en los datos que indican categorías (o clusters) en el crecimiento de followers y updates, para poder aplicar estos como definición de popularidad deben refinarse los procedimientos. Por ejemplo, en el clustering de crecimiento por día, se podría preasignar una clase a los elementos que no crecen en followers.
También existe el problema de que estos patrones sean propios del dataset, pues en el clustering por diferencias absolutas se puede observar claramente que la distribución de los intervalos de tiempo entre el primer y el último tweet registrado de cada cuenta no es uniforme, sino que tiene ciertos peaks. Este es un fenomeno que podría ser fácilmente clasificado en el dataset, pero que puede causar dificultades a la hora de escalar el algoritmo.