Modelo de relatório para Redes Sociais

Modelo para redes não direcionadas

Amilcar L. do Prado G. Gramacho

2022-02-16

1 Importando dados

Este relatório está por padrão com a visualização habilitada dos códigos utilizados para gerar os outputs que aqui serão visualizados. Caso não seja de seu interesse, basta mudar a opção localizada no canto superior direito da tela para “Hide”, e você poderá ler o relatório de forma mais limpa.

1.1 Sobre os dados

Os dados que iremos tratar neste modelo são sobre as interações entre os alunos de uma escola. Para explorar as possibilidades do Rmd e da exportação em HTML, insiro abaixo um mapa com as orientações de um centro de ensino para fingirmos que os dados são de lá.

1.2 Importação

Para a montagem do nosso relatório, devemos inserir os arquivos arestas.csv e vertices.csv na pasta dados do diretório.

Uma vez feito isso, o seguinte código importará os dados para o ambiente R.

path_arestas <- here::here("dados", "arestas.csv")

path_vertices <- here::here("dados", "vertices.csv")

df_arestas <- readr::read_csv(path_arestas); rm(path_arestas)

df_vertices <- readr::read_csv(path_vertices); rm(path_vertices)

1.3 Limpeza

Antes de seguir com a análise é importante ter certeza de que todos os dados estão corretos.

Caso o arquivo arestas.csv seja composto por apenas duas colunas com as interções entre os vértices, o próximo passo será o de agrupar tais linhas e criar uma nova coluna no dataframe que inclua a frequência com que a interação se repete.

Considerando que este é o caso dos dados que estamos analisando neste modelo, o próximo código será rodado.

df_arestas <- as.data.frame(table(df_arestas))

df_arestas <- subset(df_arestas,Freq>0)

1.4 Criação de objetos

Agora que os dados já estão no formato que precisamos, podemos criar o objeto igraph que será usado para plotar a rede.

sn_data <- igraph::graph_from_data_frame(df_arestas, 
                                         directed = FALSE, 
                                         vertices = df_vertices)

igraph::E(sn_data)$weight <- igraph::E(sn_data)$Freq

Aqui declaramos para o R tanto as interações quanto os metadados associados aos vértices.

Importante destacar que os dados analisados são não direcionados, por isso o argumento directed = FALSE foi inserido.

Pelo conhecimento sobre a base que estamos trabalhando, sabemos que existem valores Unknown no atributo Gender, vamos declará-los de forma com que o R consiga entender.

igraph::V(sn_data)$Gender[igraph::V(sn_data)$Gender == 'Unknown'] <- NA

2 Explorando os dados

Primeiramente vamos analisar os dados gerais do objeto sn_data.

summary(sn_data)
## IGRAPH 63e733f UNW- 242 8317 -- 
## + attr: name (v/c), Class (v/c), Gender (v/c), Freq (e/n), weight (e/n)

Como observado, a rede criada possui 242 vértices, que interagem entre si formando 8317 arestas.

Como esperado, considerando os dados que inserimos como output, os metadados associados aos vértices são:

  • name, Class e Gender

Analisando o atributo Class, verificamos que possuímos os seguintes valores: 1A, 1B, 2A, 2B, 3A, 3B, 4A, 4B, 5A, 5B, e Teachers.

Antes de avançarmos para as análises de métricas de Redes Sociais, podemos finalizar esse breve exploratório plotando uma pequena parte da matriz de adjacências associada à rede que vamos criar

sn_data[c(1:10), c(1:10)]
## 10 x 10 sparse Matrix of class "dgCMatrix"
##    [[ suppressing 10 column names '1426', '1427', '1428' ... ]]
##                                     
## 1426  .  27 45  75 19 43  8 12 23 27
## 1427 27   .  4 100  4 63 20  5 44 13
## 1428 45   4  .   9  4 16  2  4 19 14
## 1429 75 100  9   .  9 75 11  5 62 36
## 1430 19   4  4   9  . 15  4  7  4  3
## 1431 43  63 16  75 15  . 43 16 42 41
## 1434  8  20  2  11  4 43  .  3  8  8
## 1435 12   5  4   5  7 16  3  .  6 11
## 1437 23  44 19  62  4 42  8  6  . 29
## 1439 27  13 14  36  3 41  8 11 29  .

Como esperado, pelo fato de se tratar de uma rede não direcionada, a matriz é simétrica.

3 Métricas de Centralidade

3.1 Grau de centralidade

Vamos calcular quantas arestas estão conectadas aos vértices para entender seus graus de centralidade.

Como a rede é não direcionada, consideraremos qualquer tipo de aresta que tenha contato com o vértice, por isso o argumento mode = c("All").

Uma vez calculado, inseriremos essa informação como um metadado do vértice para podermos usar essa informação futuramente.

sn_data_deg <- igraph::degree(sn_data, mode = c("All"))

igraph::V(sn_data)$degree <- sn_data_deg

Assim, temos que o vértice que possui o maior grau de centralidade é o 1551, com 56 graus.

3.2 Eigenvector

A Eigenvector é uma medida que atribui valores maiores a vértices que estão conectados com mais vértices.

A diferença com relação à métrica anterior é que esta considera também conexões de segundo grau e nos gera um resultado entre 0 e 1.

Faremos o mesmo processo da sessão anterior.

sn_data_eig <- igraph::evcent(sn_data)$vector

igraph::V(sn_data)$Eigen <- sn_data_eig

Assim, temos que o vértice que possui o maior grau de centralidade do autovetor é o 1665, com 99 graus.

3.3 Betweeness

Esta métrica busca demonstrar o cálculo percentual de caminhos que passam por um dado vértice.

Novamente, o argumento directed = FALSE precisa estar presente.

Mais uma vez, repetiremos os procedimentos.

sn_data_bw <- igraph::betweenness(sn_data, directed = FALSE)

igraph::V(sn_data)$betweenness <- sn_data_bw



Assim, temos que o vértice que possui o maior betweeness é o 1551, com 56 graus.



3.4 Tabela com todos os indicadores

Agora que calculamos as métricas mais interessantes e as inserimos no objeto igraph, podemos exportar o arquivo em formato tabular para ser usado em outras análises.

df_sn <- igraph::as_long_data_frame(sn_data)

readr::write_csv(df_sn, "output/complete_sn.csv")

Assim, teremos acesso a um arquivo com as colunas indicando as variáveis que acabamos de calcular semelhante ao representado abaixo, mas com 8317 linhas.

from to Freq weight from_name from_Class from_Gender from_degree from_Eigen from_betweenness to_name to_Class to_Gender to_degree to_Eigen to_betweenness
1 2 27 27 1426 5B M 83 0.0432715 55.57971 1427 5B F 47 0.0226443 60.74399
1 3 45 45 1426 5B M 83 0.0432715 55.57971 1428 5B M 82 0.0307249 61.88006
2 3 4 4 1427 5B F 47 0.0226443 60.74399 1428 5B M 82 0.0307249 61.88006
1 4 75 75 1426 5B M 83 0.0432715 55.57971 1429 5B F 64 0.0386201 81.36161
2 4 100 100 1427 5B F 47 0.0226443 60.74399 1429 5B F 64 0.0386201 81.36161
3 4 9 9 1428 5B M 82 0.0307249 61.88006 1429 5B F 64 0.0386201 81.36161
1 5 19 19 1426 5B M 83 0.0432715 55.57971 1430 5B M 37 0.0043822 21.08789
2 5 4 4 1427 5B F 47 0.0226443 60.74399 1430 5B M 37 0.0043822 21.08789
3 5 4 4 1428 5B M 82 0.0307249 61.88006 1430 5B M 37 0.0043822 21.08789
4 5 9 9 1429 5B F 64 0.0386201 81.36161 1430 5B M 37 0.0043822 21.08789

Seguindo essa lógica, sugere-se que sempre sejam adicionados atributos aos vértices caso seja de interesse, para que no final seja possível gerar um arquivo tabular.

4 Medindo a estrutura da rede

4.1 Network Density

Esta medida serve para nos informar o quão densa é a rede. Isto é, o quão conectados estão os pontos entre si.

Neste caso em um primeiro momento o código abaixo calcula a densidade da rede em geral, e depois de apenas uma das turmas da escola, para mostrar o quão mais interativos tendem os alunos a serem entre seus colegas de turma.

Este é um hábito comum quando se faz análise de redes, comparar a densidade entre os grupos que fazem parte dela.

igraph::edge_density(sn_data) 
## [1] 0.2852097
sn_data_1A <- igraph::induced_subgraph(sn_data, 
                                                igraph::V(sn_data)[Class=="1A"], 
                                                impl=c("auto"))

igraph::edge_density(sn_data_1A)
## [1] 0.9841897

4.2 Assortativity

Esta medida busca demonstrar exatamente o que acabamos de calcular no tópico anterior: indivíduos de mesma comunidade tendem a interagir mais entre si.

Para calcular a assortativity, é necessário em um primeiro momento converter a variável que acreditamos que determine os clusters de acordo com a primeira linha do código abaixo. Ela basicamente pega cada um das entradas de texto e transforma em um valor numérico diferente que poderá ser usado como input em outros lugares.

A linha seguinte busca calcular a assortatividade de acordo com a turma do estudante.

cluster_tratado <- as.numeric(factor(igraph::V(sn_data)$Class))

igraph::assortativity_nominal(sn_data, types=cluster_tratado)
## [1] 0.2337739

Para entender se o resultado encontrado é alto ou baixo, vamos comparar com um que será criado aleatoriamente.

A primeira linha apenas armazena o resultado encontrado para fins comparativos.

As linhas seguintes buscam criar um objeto que armazenará um conjunto de dados formado por 1000 vértices aleatórios da base de dados com as turmas de que fazem parte realmente apagadas e substituídas por outras turmas aleatórias.

assortativity_observada <- igraph::assortativity_nominal(sn_data, types=cluster_tratado)

assortativity_aleatoria <- vector('list', 1000)

for(i in 1:1000){assortativity_aleatoria[[i]] <- igraph::assortativity_nominal(sn_data, sample(cluster_tratado))}



Agora vamos plotar os resultados observados para comprovar que os indivíduos dentro da mesma turma interagem de fato mais entre si.

O histograma representa a distribuição aleatória, e a linha vermelha o resultado encontrado em nossos dados.



hist(unlist(assortativity_aleatoria), xlim = c(0,0.4))

abline(v = assortativity_observada,col = "red", lty = 3, lwd=2)

O resultado encontrado nos mostra claramente que o índice dentro da turma é consideravelmente maior quando comparado com uma situação aleatória.

5 Visualização da rede

Plotaremos a rede utilizando o tamanho do vértice de acordo com as 3 métricas de centralidade calculadas anteriormente:

  • Grau de centralidade;
  • Eigenvector;
  • Betweeness.

Para garantir que todos os gráficos sairão com o mesmo formato, definiremos a seed antes de cada plotagem.

Tudo que tiver com RColorBrewer é questão estética com relação às cores usadas nos gráficos.

Sendo assim, para a base que estamos usando como modelo para este relatório, cada cor está relacionada à turma do estudante.

O tamanho do vértice está com o grau de centralidade dividido por 3 e dentro da raíz por que se não ficaria muito grande, então quando for fazer a análise de uma rede diferente, isso pode mudar.

O mesmo vale para as arestas.

O Layout também vale a pena mudar dependendo da forma com que a rede criada pelos dados se comportar.

5.1 Plotando com o grau de centralidade

set.seed(1001)

pal<-RColorBrewer::brewer.pal(length(unique(igraph::V(sn_data)$Class)), "Set3")

plot(sn_data,edge.color = 'black',vertex.label.cex =0.5,
     vertex.color = pal[as.numeric(as.factor(igraph::vertex_attr(sn_data, "Class")))],
     vertex.size = sqrt(sn_data_deg)/3, edge.width=sqrt(igraph::E(sn_data)$weight/800),
     layout = igraph::layout.fruchterman.reingold)

5.1.1 Plotando com o Eigenvector

set.seed(1001)
plot(sn_data,edge.color = 'black',vertex.label.cex =0.5,
     vertex.color=pal[as.numeric(as.factor(igraph::vertex_attr(sn_data, "Class")))],
     vertex.size = sqrt(sn_data_eig)*10, edge.width=sqrt(igraph::E(sn_data)$weight/800),
     layout = igraph::layout.fruchterman.reingold)

5.1.2 Plotando com o Betweeness

set.seed(1001)
plot(sn_data,edge.color = 'black',vertex.label.cex =0.5,
     vertex.color=pal[as.numeric(as.factor(igraph::vertex_attr(sn_data, "Class")))],
     vertex.size = sqrt(sn_data_bw)/3, edge.width=sqrt(igraph::E(sn_data)$weight/800),
     layout = igraph::layout.fruchterman.reingold)

Em algum momento, fazer comentários sobre os gráficos encontrados é essencial.

5.1.3 Plotando um gráfico de dispersão para verificar a correlação

O objetivo dessa parte do relatório é mostrar a correlação entre algumas medidas de centralidade em nossa base de dados.

5.1.3.1 Entre grau e betweeness

plot(igraph::V(sn_data)$degree, igraph::V(sn_data)$betweenness)

5.1.3.2 Entre Grau e eigenvector

plot(igraph::V(sn_data)$degree, igraph::V(sn_data)$Eigen)

6 Detecção de comunidades

Utilizando o método de Louvain o algoritmo irá encontrar as comunidades de nossa rede

comunidades <- igraph::cluster_louvain(sn_data)

O código acima chegou à conclusão de que em nossa rede temo 6 comunidades.

Dentro do objeto comunidades também conseguimos identificar em qual comunidade cada vértice foi alocado, por exemplo, caso tenhamos este interesse.

6.1 Plotando comunidades

Finalmente chegamos ao objetivo final, que seria plotar o gráfico com as comunidades.

set.seed(1001) 

plot(comunidades, sn_data, edge.color = 'black',vertex.label.cex =0.5,
     vertex.color=pal[as.numeric(as.factor(igraph::vertex_attr(sn_data, "Class")))],
     vertex.size = sqrt(sn_data_bw)/3, edge.width=sqrt(igraph::E(sn_data)$weight/800),
     layout = igraph::layout.fruchterman.reingold)

Com esta imagem podemos escrever nossa análise.

Neste caso em específico, a grande surpresa é que foram identificadas menos comunidades do que existiam de classes na escola, o que indica um alto grau de interação entre alguns indivíduos de classes diferentes.

7 Conclusão

Este modelo foi criado como um parâmetro para realizar uma análise de redes básica. De modo geral ele foi pensado para ser usado apenas pelo autor, então não houve uma grande preocupação em linguagem.

Importante destacar também que ele é resultado de trabalhos de conclusão de cursos diferentes, então é sabido que ainda há muito espaço para melhora, e sugestões serão sempre muito bem vindas.

As devidas referências estão mencionadas no arquivo readme.md deste repositório.