NOTA: DOCUMENTO EM ELABORAÇÃO
Este documento é um registro do meu aprendizado de redes sociais e sua implementação no R. Para esse objetivo farei uso, inicialmente, do pacote igraph
do R embora à medida que eu for avançando no aprendizado de redes sociais não descarte a possibilidade de explorar outros pacotes disponíveis com igual finalidade a exemplo do sand
, visNetwork
, statnet
ou mesmo outros aplicativos como o Gephi.
Para obter ajuda sobre o pacote igraph
recomendo consultar o site igraph.org/r/.
O conjunto de dados utilizado neste documento está disponível para download no GitHub.
Inicia-se por tentar responder à seguinte questão: o que é uma rede social?
Uma rede social é qualquer estrutura constituída de um conjunto de itens (pessoas, objetos, etc) que possuem algum tipo de relação (vínculo) entre si.
Essa estrutura é representada por objetos matemáticos denominados grafos nos quais os itens são representados por vértices e o vínculo existente entre os itens por arestas.
Um grafo é um objeto matemático formado por dois conjuntos. O primeiro deles é o conjunto dos vértices (V) e o segundo é o conjunto das relações existentes entre os vértices denominado arestas (E).
Os grafos podem ser direcionados ou não direcionados. Nos grafos direcionados a relação entre os itens possuem uma direção. Por exemplo, a pessoa A emprestou dinheiro à pessoa B. Esta relação pode ser representada no grafo com uma aresta que consiste numa “seta”" apontando de A para B. Nos grafos não direcionados a relação não tem um direcionamento. Exemplo: a pessoa A conhece a pessoa B e vice-versa.
Os dois grafos a seguir ilustram o que foi comentado.
Na análise de redes sociais um conceito importante é o de homofilia. Este é um conceito emprestado da sociologia e significa que as pessoas tem uma forte tendência a associar-se a outras com quem elas percebam como sendo semelhantes a elas de alguma forma. O ditado popular “diga-me com quem andas que te direi quem és” expressa muito bem este conceito.
Uma rede social é homofílica se os vértices com o atributo X estão em sua maioria associados com outros vértices que também possuam o atributo X. Não será objeto de deste documento, mas existem medidas para avaliar se uma rede é homofílica.
Dois outros conceitos relevantes são o conceito de matriz de adjacência e matriz de incidência.
Uma matriz de adjacência é uma matriz quadrada onde o valor 1 indica a existência de vínculo entre dois vértices e 0 indica a ausência de vínculo. Um exemplo é a matriz a seguir:
## A B C D E
## A 0 1 1 0 0
## B 0 0 1 1 0
## C 0 1 0 0 0
## D 0 0 0 0 0
## E 0 0 1 0 0
Já uma matriz de incidência indica como se dá o vínculo entre dois conjuntos de vértices com características distintas. Por exemplo, considere um conjunto de \(m\) estudantes que pertençam a \(n\) turmas. A matriz de incidência é então uma matriz retangular \(m \times n\), onde o valor 1 indica que determinado estudante pertence a determinada turma e 0 caso não pertença. Exemplo:
## T1 T2 T3 T4
## E1 1 0 0 1
## E2 0 0 1 1
## E3 0 1 1 0
## E4 0 1 0 0
## E5 1 1 0 0
## E6 1 0 0 1
Embora o pacote igraph
ofereça algumas opções para a criação de grafos, uma opção bastante conveniente é a criação do grafo a partir de um data frame.
Neste documento será utilizado um conjunto de dados relativo aos investimentos feitos por RPPS fluminenses em diversos ativos incluindo fundos de investimento. Os dados estão contidos no arquivo dados_dair_DtGer_12-12-2017.rds
criado a partir da extração de dados de arquivos xml do DAIR de alguns dos já mencionados RPPS.
Os comandos a seguir ilustram a importação dos dados e retenção dos registros relativos aos investimentos em fundos de investimento:
setwd("I:\\Redes Sociais e RPPS")
# Importação dos dados
dados <- readRDS("dados_dair_DtGer_12-12-2017.rds")
# Retenção dos registros relativos a aplicações em Fundos de Investimentos
dados <- subset(dados, !is.na(cnpj_fi))
str(dados)
## 'data.frame': 6163 obs. of 13 variables:
## $ ente : chr "Pinheiral" "Pinheiral" "Pinheiral" "Pinheiral" ...
## $ ano : chr "2017" "2017" "2017" "2017" ...
## $ mes : chr "1" "1" "1" "1" ...
## $ identificacaoDoAtivo : chr "14.386.926/0001-71 - CAIXA BRASIL IDKA 2 A TITULOS PUBLICOS FI RENDA FIXA LP" "18.598.288/0001-03 - CAIXA BRASIL2024I TITULOS PUBLICOS FI RENDA FIXA" "11.060.913/0001-10 - CAIXA BRASIL IMA B 5 TITULOS PUBLICOS FI RENDA FIXA LP" "10.740.658/0001-93 - CAIXA BRASIL IMA B TITULOS PUBLICOS FI RENDA FIXA LP" ...
## $ nome : chr "FI 100% títulos TN" "FI 100% títulos TN" "FI 100% títulos TN" "FI 100% títulos TN" ...
## $ textoFundamentoLegal : chr "Art. 7º, I, \"b\"" "Art. 7º, I, \"b\"" "Art. 7º, I, \"b\"" "Art. 7º, I, \"b\"" ...
## $ quantidade : chr "2996314.7144273080" "300000.0000000000" "1793117.7799846900" "4552179.1893383010" ...
## $ valorAtualAtivo : chr "1.6425240000" "1.3566020000" "2.1790120000" "2.2783610000" ...
## $ valorTotalAtual : chr "4921518.83" "406980.60" "3907225.16" "10371507.53" ...
## $ pctTotalRecursosSPPS : chr "14.79" "1.22" "11.74" "31.16" ...
## $ valorAtualPatrimonioLiquidoFundo: chr "3664210527.36" "362551374.08" "5503989684.97" "5218065279.62" ...
## $ pctPatrimonioLiquidoFundo : chr "0.13" "0.11" "0.07" "0.20" ...
## $ cnpj_fi : chr "14.386.926/0001-71" "18.598.288/0001-03" "11.060.913/0001-10" "10.740.658/0001-93" ...
Feita a importação dos dados é sempre uma boa ideia explorar um pouco o conjunto de dados. Os dados se referem aos DAIR do exercício de 2017. Mas quais RPPS estão na base e quais meses? O código abaixo busca responder a esta questão:
table(dados$ente, ordered(dados$mes, levels=1:12))
##
## 1 2 3 4 5 6 7 8 9 10 11 12
## Angra dos Reis 31 31 31 31 31 31 31 31 0 0 0 0
## Areal 16 16 18 18 18 18 18 18 0 0 0 0
## Armação dos Búzios 8 8 8 8 9 9 10 10 0 0 0 0
## Barra Mansa 2 2 2 2 2 2 2 2 0 0 0 0
## Belford Roxo 18 18 17 17 17 17 17 17 0 0 0 0
## Bom Jardim 10 10 10 10 10 10 10 10 0 0 0 0
## Cabo Frio 5 5 5 5 5 5 5 5 0 0 0 0
## Cambuci 7 7 10 10 11 10 10 10 0 0 0 0
## Cantagalo 10 12 12 12 12 13 14 15 0 0 0 0
## Cardoso Moreira 8 8 9 9 9 9 9 9 0 0 0 0
## Carmo 11 11 11 11 11 11 11 11 0 0 0 0
## Casimiro de Abreu 33 33 33 33 33 34 33 33 34 0 0 0
## Comendador Levy Gasparian 8 8 8 9 9 9 9 9 0 0 0 0
## Conceição de Macabu 15 15 15 15 15 15 15 15 15 0 0 0
## Duas Barras 17 17 17 17 17 17 19 19 0 0 0 0
## Italva 17 17 17 17 17 17 17 17 0 0 0 0
## Itaocara 10 10 10 10 10 11 11 11 0 0 0 0
## Itaperuna 7 7 6 6 6 6 6 6 0 0 0 0
## Itatiaia 29 29 28 30 30 31 31 31 0 0 0 0
## Japeri 15 16 15 16 16 16 16 16 0 0 0 0
## Macaé 25 24 24 24 24 24 24 24 0 0 0 0
## Maricá 9 9 9 9 9 9 9 9 0 0 0 0
## Mesquita 13 15 15 15 15 15 15 17 0 0 0 0
## Miguel Pereira 17 17 18 18 18 20 20 20 0 0 0 0
## Natividade 12 12 12 12 12 12 12 12 0 0 0 0
## Nilópolis 1 1 2 2 2 2 2 3 0 0 0 0
## Niterói 11 15 15 15 15 15 13 14 14 14 0 0
## Nova Friburgo 9 10 9 9 9 9 10 12 0 0 0 0
## Nova Iguaçu 5 6 6 6 6 5 5 5 0 0 0 0
## Paty do Alferes 18 18 17 17 18 18 18 18 18 19 0 0
## Petrópolis 3 3 4 3 3 3 3 3 0 0 0 0
## Pinheiral 14 14 14 14 14 28 0 14 0 0 0 0
## Piraí 13 13 13 13 13 13 13 13 13 13 0 0
## Porciúncula 20 20 20 20 23 21 21 19 0 0 0 0
## Quatis 9 9 9 9 9 9 13 13 0 0 0 0
## Queimados 28 26 24 24 24 24 24 24 0 0 0 0
## Resende 28 29 29 29 29 29 29 29 29 29 0 0
## Rio Claro 11 11 11 11 12 12 11 11 0 0 0 0
## Rio das Ostras 34 41 38 40 40 40 37 38 0 0 0 0
## Rio de Janeiro 28 28 28 28 29 29 29 33 0 0 0 0
## São Gonçalo 7 7 7 7 7 7 6 6 0 0 0 0
## São João da Barra 3 3 3 4 4 4 4 4 0 0 0 0
## São Pedro da Aldeia 14 17 19 19 19 19 20 20 0 0 0 0
## São Sebastião do Alto 4 4 4 4 4 4 4 6 0 0 0 0
## Sapucaia 18 18 18 18 18 18 18 18 0 0 0 0
## Saquarema 10 10 11 11 11 11 11 11 0 0 0 0
## Silva Jardim 10 11 11 11 12 13 13 16 0 0 0 0
## Sumidouro 22 22 23 23 23 23 23 23 0 0 0 0
## Trajano de Moraes 15 15 15 15 15 16 0 0 0 0 0 0
## Valença 0 12 0 0 0 12 12 13 0 0 0 0
## Varre-Sai 10 10 11 11 11 11 11 8 0 0 0 0
## Vassouras 14 14 14 14 14 14 14 14 0 0 0 0
Os valores nesta tabela indicam, para cada ente e mês, em quantos fundos de investimento o RPPS mantinha recursos aplicados.
A base de dados possui registros relativos aos meses de janeiro a outubro de 2017 (apenas alguns RPPS). Como a maioria dos RPPS forneceu os dados até agosto de 2017, será utilizado os dados deste mês para a elaboração do grafo.
# Obtenção dos registros relativos ao mês de agosto
dados_ago <- subset(dados, mes == 8)
Feita a importação dos dados o passo seguinte é construir o grafo desejado. O objetivo inicial é verificar como os RPPS e os fundos de investimento se relacionam. A ideia é verificar se esta abordagem (análise de redes sociais) pode ser utilizada para ajudar na detecção de fraudes. Por ora o objetivo é só aprender alguma teoria de redes sociais e realizar análises usando o R.
Deve ser observado que o interesse está no relacionamento de duas entidades: os RPPS e os fundos de investimento. O interesse poderia estar em criar um grafo só com os fundos de investimento considerando que dois fundos tenham um vínculo, por exemplo, se possuirem o mesmo gestor ou o mesmo administrador.
Redes que apresentam o relacionamento entre dois tipos de objeto são chamadas bipartites
. Neste tipo de rede os vínculos ocorrem apenas entre entidades de tipos diferentes. No caso em análise os vínculos ocorrem apenas entre RPPS e fundos de investimento. Não existem vínculos entre RPPS ou entre fundos de investimento.
O pacote igraph
possui a função graph_from_data_frame()
que possibilita a construção de um grafo a partir de um data frame no qual as duas primeiras colunas contenham a identificação dos itens (vértices) cuja relação se deseja examinar.
No conjunto de dados utilizado como exemplo, as duas primeiras colunas contém o nome do ente a que o RPPS pertence e na segunda coluna o CNPJ do fundo de investimento no qual o RPPS possuia recursos aplicados em agosto de 2017. As demais colunas referem-se a outras variáveis existentes no conjunto de dados. Assim, cada linha representa um vínculo (aresta) existente entre RPPS e fundo de investimento.
O código a seguir ilustra como construir o grafo a partir da base de dados. A primeira coisa a fazer é carregar o pacote igraph
:
library(igraph)
O grafo pode ser elaborado da seguinte forma:
grafo <- graph_from_data_frame(dados_ago[,c("ente", "cnpj_fi", "textoFundamentoLegal", "pctTotalRecursosSPPS")], directed = FALSE)
Deve ser notado que foram utilizadas apenas algumas colunas da base de dados e não toda a base. Isto porque as demais colunas, além das duas primeiras, serão consideradas como atributos das arestas. Estas variáveis também podem ser utilizadas na elaboração de visualizações dos grafos como será mostrado mais adiante.
A visualização do grafo pode ser obtida com a função plot()
.
plot(grafo)
Como pode ser visto, o resultado não é nada interessante. É possível melhorar sensivelmente o gráfico alterando os valores de alguns argumentos da função:
plot(grafo,
vertex.size=7,
vertex.label=NA)
Uma rápida inspeção do grafo revela um ponto interessante. Alguns RPPS apresentam vínculos com um punhado de fundos de investimentos que não tem relação com mais nenhum outro RPPS (pelo menos em relação aos RPPS que constam da base de dados).
Mais adiante será visto como diferenciar, no grafo, os RPPS dos fundos de investimento.
A partir de um grafo é possível obter algumas medidas descritivas tais como o tamanho do grafo, sua densidade e o seu diâmetro.
Além dessas medidas, também é útil obter a relação dos vértices e arestas de um grafo. Esta seção ilustra como obter estas informações.
Com as funções V()
e E()
obtém-se a relação dos vértices e das arestas.
V(grafo)
## + 229/229 vertices, named, from 865641f:
## [1] Pinheiral Armação dos Búzios
## [3] Mesquita Niterói
## [5] Cabo Frio Bom Jardim
## [7] Duas Barras Nova Friburgo
## [9] Itaocara São Gonçalo
## [11] São Sebastião do Alto Cantagalo
## [13] Barra Mansa Silva Jardim
## [15] São Pedro da Aldeia Itaperuna
## [17] Natividade Porciúncula
## [19] Rio Claro Valença
## + ... omitted several vertices
E(grafo)
## + 765/765 edges from 865641f (vertex names):
## [1] Pinheiral --14.386.926/0001-71
## [2] Pinheiral --18.598.288/0001-03
## [3] Pinheiral --11.060.913/0001-10
## [4] Pinheiral --10.740.658/0001-93
## [5] Pinheiral --10.740.670/0001-06
## [6] Pinheiral --06.018.364/0001-85
## [7] Pinheiral --07.861.554/0001-22
## [8] Pinheiral --05.164.356/0001-84
## [9] Pinheiral --13.077.415/0001-05
## [10] Pinheiral --13.077.418/0001-49
## + ... omitted several edges
As medidas descritivas anteriormente mencionadas (tamanho, densidade e diâmetro) são obtidas, respectivamente, com as funções gorder()
, edge_density()
e diameter()
.
Tamanho: é a quantidade de vértices que uma rede possui. No conjunto de dados em análise tem-se 229 vértices entre fundos de investimento e RPPS.
gorder(grafo)
## [1] 229
A quantidade de arestas pode ser obtida com a função gsize()
.
gsize(grafo)
## [1] 765
Densidade: a densidade da rede é a proporção de arestas existente na rede em relação a todas as arestas possíveis entre os vértices. Sendo uma proporção, esta quantidade varia entre 0 e 1, sendo que quanto mais próxima de 1 mais densa é rede.
edge_density(grafo)
## [1] 0.02930361
Diâmetro: o diâmetro de uma rede é o maior caminho dentre todos os caminhos mais curtos entre dois pares quaisquer de vértices. Um caminho é uma série de passos necessários para ir de um vértice A até um vértice B.
O diâmetro de uma rede é uma medida bastante útil de sua compacticidade.
diameter(grafo)
## [1] 6
Coesão:
vertex_connectivity(grafo)
## [1] 1
Além de obter algumas medidas descritivas dos grafos que se está a examinar, algumas operações sobre grafos são bem corriqueiras. Exemplos de tais operações são: consultar e definir atributos de vértices e arestas, filtrar grafos, adicionar ou excluir vértices e arestas ao grafo ou ainda combinar múltiplos grafos.
Atributos dos vértices, das arestas ou mesmo dos grafos podem ser obtidos, respectivamente com as funções vertex_attr()
, edge_attr()
e graph_attr()
.
Para identificar os atributos existentes em um grafo pode-se utilizar a função summary()
.
summary(grafo)
## IGRAPH 865641f UN-- 229 765 --
## + attr: name (v/c), textoFundamentoLegal (e/c),
## | pctTotalRecursosSPPS (e/c)
O resultado fornece algumas inforamções interessantes: primeiro a string UN-- 229 765 --
indica tratar-se de um grafo não direcionado (U), com vértices nomeados (N) possuindo 229 vértices e 765 arestas. Outras opções possíveis são (D) para indicar grafos direcionados e (W) para indicar grafos com pesos atribuídos às arestas.
A string name (v/c), textoFundamentoLegal (e/c), pctTotalRecursosSPPS (e/c)
indica os atributos existentes no grafo, a que componentes (vértice ou aresta) pertencem e o tipo do atributo (textual, numérico). Exemplo: name (v/c)
indica a existência de atributo de nome name
atribuído aos vértices sendo um atributo textual (v/c)
; textoFundamentoLegal (e/c)
indica a existência de um atributo de nome textoFundamentoLegal
atribuídos as arestas sendo também um atributo textual. O mesmo para o último atributo (pctTotalRecursosSPPS
).
Agora que se conhece os atributos exístentes no grafo, pode-se consultar seu conteúdo da seguinte forma:
# Os 10 primeiros
vertex_attr(grafo, name="name")[1:10]
## [1] "Pinheiral" "Armação dos Búzios" "Mesquita"
## [4] "Niterói" "Cabo Frio" "Bom Jardim"
## [7] "Duas Barras" "Nova Friburgo" "Itaocara"
## [10] "São Gonçalo"
# Os 10 primeiros
edge_attr(grafo, "textoFundamentoLegal")[1:10]
## [1] "Art. 7º, I, \"b\"" "Art. 7º, I, \"b\"" "Art. 7º, I, \"b\""
## [4] "Art. 7º, I, \"b\"" "Art. 7º, I, \"b\"" "Art. 7º, VII, \"a\""
## [7] "Art. 7º, III, \"a\"" "Art. 7º, IV, \"a\"" "Art. 7º, IV, \"a\""
## [10] "Art. 7º, IV, \"a\""
Visto como consultar os atributos de vértices e arestas, será visto agora como definir novos atributos para vértices. A função set_vertex_attr()
pode ser utilizada com esta finalidade.
Será criado um novo atributo denominado fundo_vedado
que irá indicar se o fundo de investimento é vedado para aplicações do RPPS ou não. Para isso será necessário importar a base de dados de fundos vedados divulgada pelo Ministério da Fazenda.
library(readxl)
setwd("I:\\Melhores Praticas TCE e Atricon\\ATRICON\\3. Parte Pratica - Laboratorio\\extracao-dados-pdf-fundos-vedados")
fundos_vedados <- read_excel("fundos_vedados_28-08-2018.xlsx")[,1:2]
head(fundos_vedados)
## # A tibble: 6 x 2
## `CNPJ DO FUNDO` `NOME DO FUNDO`
## <chr> <chr>
## 1 00.828.035/0001-13 FATOR MAX CORPORATIVO FUNDO DE INVESTIMENTO DE RENDA~
## 2 01.107.772/0001-90 CONCÓRDIA EXTRA FUNDO DE INVESTIMENTO RENDA FIXA CRÉ~
## 3 01.375.954/0001-41 PLANNER FUNDO DE INVESTIMENTO MULTIMERCADO
## 4 01.653.201/0001-50 GRAU SAVANA FUNDO DE INVESTIMENTO MULTIMERCADO
## 5 03.362.624/0001-47 GWI CLASSIC FUNDO DE INVESTIMENTO EM AÇÕES
## 6 04.877.280/0001-71 SANTOS CREDIT YIELD FUNDO DE INVESTIMENTO RENDA FIXA~
A definição do novo atributo é feita a seguir:
grafo <- set_vertex_attr(grafo,
name="fundos_vedados",
value=ifelse(V(grafo)$name %in% fundos_vedados$`CNPJ DO FUNDO`, TRUE, FALSE))
summary(grafo)
## IGRAPH 865641f UN-- 229 765 --
## + attr: name (v/c), fundos_vedados (v/l), textoFundamentoLegal
## | (e/c), pctTotalRecursosSPPS (e/c)
O resumo indica a criação do novo atributo para os vértices com o nome fundos_vedados
do tipo lógico. Mas erá que existe algum valor verdadeiro? Ou seja, algum RPPS tinha aplicações em fundos de investimento vedados?
any(vertex_attr(grafo, name="fundos_vedados"))
## [1] TRUE
O resultado indica que sim. Este atributo poderá ser utilizado para identificar os RPPS que investiram em fundos vedados.
Para remover atributos dos vértices, das arestas ou do grafo pode-se utilizar, respectivamente, as funções delete_vertex_attr()
, delete_edge_attr()
e delete_graph_attr()
.
Uma outra tarefa comum na análise de redes sociais é a filtragem de grafos com base nos atributos dos vértices ou arestas. O comando a seguir mostra como filtrar os vértices que tenham o atributo fundos_vedados
igual a TRUE
.
grafo_filtrado <- subgraph.edges(grafo, E(grafo)[inc(V(grafo)[V(grafo)$fundos_vedados])])
plot(grafo_filtrado,
vertex.size=7)
Centralidade é uma medida de quão importante um vértice é no contexto de toda a rede. Trata-se de conceito bem amplo e diversas medidas de centralidade estão disponíveis que buscam capturar as diferentes noções de importância de um vértice.
A medida de centralidade mais simples é o grau do vértice. Esta medida atribui a cada vértice a quantidade de arestas ao qual está conectado, considerando dessa forma que são importantes os vértices que mais conexões tém com outros vértices.
O grau de um vértice indica a quantos outros vértices o vértice em exame está conectado. Esta medida é calculada com a função degree()
Assim, são considerados mais centrais os vértices com maior quantidade de arestas, de forma que quando a rede é plotada, quanto maior o grau de um vétice, mais ao centro da rede ele estará. O códig a seguir mostra os cinco vértices com maior grau:
sort(degree(grafo), decreasing = TRUE)[1:5]
## 10.740.670/0001-06 Rio das Ostras Casimiro de Abreu
## 39 38 33
## Rio de Janeiro 11.328.882/0001-35
## 33 32
Para obter o grau de um vértice em particular, por exemplo, o vértice que representa o RPPS de Japeri, podemos fazer:
degree(grafo)["Japeri"]
## Japeri
## 16
Quais vertices apresentam grau maior que 30?
degree(grafo)[degree(grafo) > 30]
## Casimiro de Abreu Angra dos Reis Itatiaia
## 33 31 31
## Rio das Ostras Rio de Janeiro 10.740.670/0001-06
## 38 33 39
## 11.328.882/0001-35
## 32
Quais fundos estão vinculados a um único RPPS :
degree(grafo)[degree(grafo) == 1]
## 05.100.221/0001-55 02.998.164/0001-85 08.692.888/0001-82
## 1 1 1
## 23.957.101/0001-50 11.898.280/0001-13 05.073.656/0001-58
## 1 1 1
## 16.599.968/0001-16 10.309.539/0001-80 07.899.238/0001-40
## 1 1 1
## 04.288.966/0001-27 15.154.220/0001-47 03.394.711/0001-86
## 1 1 1
## 17.517.779/0001-10 30.822.936/0001-69 15.486.093/0001-83
## 1 1 1
## 18.800.239/0001-01 12.330.846/0001-79 07.539.298/0001-51
## 1 1 1
## 09.630.188/0001-26 01.525.057/0001-77 04.877.280/0001-71
## 1 1 1
## 21.347.528/0001-01 18.598.088/0001-50 14.507.699/0001-95
## 1 1 1
## 17.073.556/0001-00 17.116.227/0001-08 18.270.783/0001-99
## 1 1 1
## 15.188.380/0001-07 02.887.290/0001-62 06.916.384/0001-73
## 1 1 1
## 03.737.211/0001-08 02.228.453/0001-03 19.303.794/0001-90
## 1 1 1
## 10.418.362/0001-50 23.176.675/0001-91 19.542.287/0001-00
## 1 1 1
## 00.840.011/0001-80 14.171.644/0001-57 21.098.129/0001-54
## 1 1 1
## 19.391.026/0001-36 13.594.673/0001-69 17.213.849/0001-46
## 1 1 1
## 11.198.684/0001-02 13.344.834/0001-66 07.187.570/0001-81
## 1 1 1
## 11.357.758/0001-06 17.412.812/0001-47 59.285.411/0001-13
## 1 1 1
## 13.593.438/0001-72 07.972.299/0001-95 11.351.413/0001-37
## 1 1 1
## 12.053.694/0001-04 15.153.656/0001-11 13.651.947/0001-04
## 1 1 1
## 15.769.621/0001-01 14.655.180/0001-54 12.360.621/0001-65
## 1 1 1
## 18.373.362/0001-93 12.312.767/0001-35 14.631.148/0001-39
## 1 1 1
## 10.896.292/0001-46 19.523.306/0001-50 21.918.953/0001-03
## 1 1 1
## 20.139.342/0001-02 22.791.074/0001-26 20.139.534/0001-00
## 1 1 1
## 24.022.566/0001-82 10.787.647/0001-69 00.975.480/0001-06
## 1 1 1
## 11.046.645/0001-81 02.296.928/0001-90 09.215.250/0001-13
## 1 1 1
## 03.069.104/0001-40
## 1
Eventualmente pode ser de interesse calcular o grau médio de uma rede. Isso pode ser feito da seguinte forma:
# grau médio
mean(degree(grafo))
## [1] 6.681223
# plotar vértices mais centrais, ou seja, os que possuem maior grau
plot(grafo,
vertex.label=ifelse(V(grafo)$name %in% c("10.740.670/0001-06", "Rio das Ostras"), V(grafo)$name, NA),
vertex.size=7,
vertex.color=ifelse(V(grafo)$name %in% c("10.740.670/0001-06", "Rio das Ostras"), "red", NA))
No grafo acima, foram plotados os labels e coloridos de vermelho apenas 2 vértices identificados como os de maior grau.
Também pode ser útil verificar como se distribui o grau dos vértices. Um histograma ou um gráfico de pontos pode ser usados com esta finalidade
par(mfrow=c(1,2), bty="n")
hist(degree(grafo), col="lightblue", main = "")
stripchart(degree(grafo), method = "stack", pch=16, cex=1.2, at=0, col="lightblue")
Esta medida de centralidade tenta expressar a importância de um vértice pelo fato dele estar próximo a muitos outros vértices. Os cinco vértices com maior valor desta medida é apresentada a seguir:
sort(closeness(grafo), decreasing = TRUE)[1:5]
## 10.740.670/0001-06 10.740.658/0001-93 11.328.882/0001-35
## 0.002036660 0.001869159 0.001821494
## 13.322.205/0001-35 13.077.418/0001-49
## 0.001808318 0.001795332
Esta medida de centralidade reconhece como mais importantes os vértices que se localizam na rede de tal forma se localizem entre outros pares de vértices.
sort(betweenness(grafo), decreasing = TRUE)[1:5]
## 10.740.670/0001-06 Rio de Janeiro Rio das Ostras
## 4431.757 3157.816 2833.575
## Queimados Angra dos Reis
## 2449.070 2358.505
Uma outra medida de centralidade é a eingenvector centrality que atribui maiores pesos aos vértices que estão conectados a outros vértices também importantes.
eigen_centrality(grafo)$vector[1:5]
## Pinheiral Armação dos Búzios Mesquita
## 0.3342700 0.4325819 0.3347853
## Niterói Cabo Frio
## 0.4311459 0.1528061
Os vizinhos de um vértice (vértices relacionados ao vértice em análise) podem ser identificados com as funções neighbors()
ou adjacent_vertices()
. Por exemplo, se fosse necessário identificar os vértices adjacentes ao RPPS de Japeri (fundos em que o RPPS de Japeri investe), pode-se utilizar o seguinte comando:
neighbors(grafo, "Japeri")
## + 16/229 vertices, named, from 865641f:
## [1] 10.740.670/0001-06 05.164.356/0001-84 13.555.918/0001-49
## [4] 07.111.384/0001-69 09.315.625/0001-17 20.886.575/0001-60
## [7] 19.391.026/0001-36 13.594.673/0001-69 17.213.849/0001-46
## [10] 12.845.801/0001-37 11.198.684/0001-02 13.344.834/0001-66
## [13] 07.187.570/0001-81 11.357.758/0001-06 17.412.812/0001-47
## [16] 59.285.411/0001-13
Também é possível identificar a “vizinhança” de um determinado vértice. A função neighborhood()
implementa essa funcionalidade. Para identificar a “vizinhança”" do Fundo de Investimento “05.100.221/0001-55” a duas arestas de distância, isto é, identificar os RPPS que investem no referido Fundo e todos os demais Fundos nos quais tais RPPS investem.
neighborhood(grafo, order = 2, "05.100.221/0001-55")
## [[1]]
## + 15/229 vertices, named, from 865641f:
## [1] 05.100.221/0001-55 Pinheiral 14.386.926/0001-71
## [4] 18.598.288/0001-03 11.060.913/0001-10 10.740.658/0001-93
## [7] 10.740.670/0001-06 06.018.364/0001-85 07.861.554/0001-22
## [10] 05.164.356/0001-84 13.077.415/0001-05 13.077.418/0001-49
## [13] 11.898.349/0001-09 08.973.942/0001-68 02.998.164/0001-85
Uma ego net
(rede egocêntrica) é um subgrafo consistindo de um vértice \(v\) e seus vizinhos imediatos.
Para a definição de subgrafos énecessário apenas que se identifique quais vértices deseja-se que o subgrafo contenha. Estes vértices podem ser definidos ad hoc pelo analista.
A identificação de comunidades, por outro lado, consiste em achar subgrafos cujos vértices serão definidos em razão da coesão existente entre eles.
Um subgrafo pode ser criado conforme mostrado a seguir, onde apenas os vértices relativos aos RPPS de Belford Roxo e Japeri comporão o subgrafo.
A função subgraph.edges()
cuidará de identificar as arestas associadas a estes vértices que, no caso em análise, são os fundos de investimento.
grafo_sub <- subgraph.edges(grafo, E(grafo)[inc(c("Belford Roxo", "Japeri"))])
plot(grafo_sub, vertex.color="lightgreen", vertex.size=7)
O subgrafo acima foi elaborado simplesmente escolhendo-se alguns vértices cujos nomes foram passados à função como um vetor.
Outros subgrafos podem ser obtidos levando-se em consideração padrões existentes nas arestas que definam subgrafos com características particulares. Exemplos desses tipos de subgrafo são os cliques
e os k-cores
.
Um “clique” consiste em um subgrafo no qual todos os vértices estão conectados entre si. Na figura a seguir, é possível identificar dois cliques: ABCD e EFG
Para determinar o tamanho (quantidade de vértices) do maior clique em um grafo pode-se utilizar a função clique.number()
. No conjunto de dados em análise:
clique.number(grafo)
## [1] 2
Considerando a natureza do nosso conjunto de dados, onde cada RPPS se vincula a fundos de investimento e, por sua vez, cada fundo se vincula a RPPSs que não se vinculam entre si, o mesmo valendo para os fundos de investimento, não seria possível esperar cliques maiores que 2. Na prática apenas cliques maiores que 3 são relevantes.
Outras funções úteis quando se deseja analisar cliques são: cliques()
, largest_cliques()
, maximal.cliques()
e max_cliques()
. Recomenda-se consultar a ajuda das funções para mais informação.
Um k-core é um subgrafo com a seguinte característica: máximo subgrafo no qual cada vértice está conectado a pelo menos k outros vértices no subgrafo.
Este tipo de subgrafo consiste num relaxamento quanto a exigência existente no clique de que todos os vértices estejam interconectados entre si, fato que faz com que na prática os cliques de maiores tamanhos sejam raros mesmo em redes com grande quantidade de vértices.
A identificação de subgrafos k-core é realizada com o uso da função graph.coreness()
grafo_kcores <- graph.coreness(grafo)
table(grafo_kcores)
## grafo_kcores
## 1 2 3 4 5 6 7 8
## 73 31 25 18 9 22 6 45
O resultado mostra que a rede possui cores de tamanho variando de 1 a 8, sendo que 45 vértices constituem um 8-core.
A detecção de comunidades engloba um conjunto de algoritmos que tem por objetivo identificar conjuntos de vértices que, de alguma forma, são coesos entre si e não tão associados com outros grupos ou vértices.
Uma característica importante das redes e que é usada com frequência na detecção de comunidades é expressa pelo conceito de modularidade. A modularidade é uma medida da estrutura da rede. Espeficicamente, a extenção na qual os vértices parecem estar agrupados de forma a exibir uma maior densidade dentro dos grupos e uma menor densidade entre os grupos.
A modularidade é uma estatística que varia de -1/2 a 1, sendo que quanto mais próxima de 1 mais agrupados estarão os vértices.
O cálculo da modularidade de uma rede dá-se de duas formas: (1) de maneira exploratória, na qual utiliza-se um algoritmo que busque classificar os vértices de forma a maximizar o valor da modularidade. (2) de forma descritiva, onde a modularidade é calculada para algum atributo dos vértices com o objetivo de se verificar se o referido atributo define um bom agrupamento dos vértices.
Existem diversos algoritmos para detecção de comunidades. O comando abaixo utiliza o algoritmo edge-betweenness
implementado pela função cluster_edge_betweeness()
cm <- cluster_edge_betweenness(grafo)
modularity(cm)
## [1] 0.1575753
A tabela a seguir elenca os algoritmos de detecção de comunidades e as correspondentes funções no pacote igraph
que as implementam.
Algoritmo | Função |
---|---|
Edge-betweenness | cluster_edge_betweenness() |
Leading eigenvector | cluster_leading_eigen() |
Fast-greedy | cluster_fast_greedy() |
Louvain | cluster_louvain() |
Walktrap | cluster_walktrap() |
Label propagation | cluster_label_prop() |
InfoMAP | cluster_infomap() |
Spinglass | cluster_spinglass() |
Optimal | cluster_optimal() |
Fonte: A User’s Guide to Network Analysis in R
Para visualizar os grupos identificados pelo algoritmo, utiliza-se a função plot()
conforme mostrado a seguir:
plot(cm, grafo, vertex.label=NA, vertex.size=6)
Embora a visualização dê uma boa indicação dos grupos detectados pelo algoritmo, pode ser necessário obter a identificação dos vértices pertencentes a cada grupo. Isso pode ser feito com a função membership()
conforme mostrado a seguir:
mbp <- membership(cm)
table(mbp)
## mbp
## 1 2 3 4 5 6 7 8
## 156 8 5 4 15 12 13 16
Foram identificados 8 subgrafos, sendo que no grupo 1 foram classificados 156 vertices, no grupo 2, oito, e assim por diante. Para identificar quais vértices estão em cada grupo é só fazer:
# Vertices pertencentes ao grupo 8
mbp[mbp == 8]
## Rio de Janeiro 25.078.994/0001-90 24.117.278/0001-01
## 8 8 8
## 18.687.230/0001-36 19.523.306/0001-50 21.918.953/0001-03
## 8 8 8
## 20.139.342/0001-02 22.791.074/0001-26 20.139.534/0001-00
## 8 8 8
## 24.022.566/0001-82 10.787.647/0001-69 00.975.480/0001-06
## 8 8 8
## 11.046.645/0001-81 02.296.928/0001-90 09.215.250/0001-13
## 8 8 8
## 03.069.104/0001-40
## 8
# Vertices pertencentes ao grupo 4
mbp[mbp == 4]
## São Gonçalo 17.517.779/0001-10 09.613.226/0001-32
## 4 4 4
## 06.175.696/0001-73
## 4
Para identicar no gráfico cada vértice com o número do grupo a que pertencem, o código a seguir pode ser utilizado.
plot(cm, grafo,
vertex.size=7,
vertex.label=as.character(mbp))
Neste tópico serão apresentados alguns recursos adicionais para a visualização de grafos além dos que já foram apresentados anteriormente.
O pacote igraph
oferece um amplo conjunto de possibilidades com esta finalidade. Elenca-se a seguir alguns parâmetros que podem ser usados na função plot()
com o objetivo de dar uma maior flexibilidade à visualização das redes.
Os principais parâmetros gráficos são apresentados a seguir:
Parâmetros relacionados aos vértices:
Parâmetro | Descrição |
---|---|
vertex.color | define a cor do vértice |
vextex.frame.color | define a cor da borda do vértice |
vertex.shape | define o formato do vértice |
vertex.size | define o tamanho do vértice. O tamanho “default” é 15 |
vertex.size2 | define um segundo tamanho para o vértice |
vextex.label | define os nomes (labels) dos vértices |
vertex.label.family | define a fonte a para os nomes dos vértices (ex:“Times”, “Helvetica”) |
vertex.label.font | define o tipo da fonte: 1-plano, 2-negrito, 3-italico, 4-negrito itálico, 5-symbol |
vertex.label.cex | define o tamanho da fonte |
vertex.label.dist | define a distância entre o label e o vértice |
vertex.label.degree | define a posição do label em relação ao vértice: ‘0’-direita, ‘pi’-esquerda, ‘pi/2’-abaixo, ‘-pi/2’- acima |
Parâmetros relacionados às arestas:
Parâmetro | Descrição |
---|---|
edge.color | define a cor das arestas |
edge.width | define a espessura das arestas. O valor padrão é 1 |
edge.arrow.size | define o tamanho da seta. O valor padrão é 1 |
edge.arrow.width | define a espessura da seta. O valor padrão é 1 |
edge.lty | define o tipo da linha. Valores possíveis: 0 ou “blank”, 1 ou “solid”, 2 ou " dashed“, 3 ou”dotted“, 4 ou”dotdash“, 5 ou”longdash“, 6 ou”twodash“ |
edge.label | vetor de caracteres para dar nomes às arestas |
edge.label.family | define a fonte a para os nomes das arestas (ex: “Times”, “Helvetica”) |
edge.label.font | define o tipo da fonte: 1-plano, 2-negrito, 3-italico, 4-negrito itálico, 5-symbol |
edge.label.cex | define o tamanho da fonte |
edge.curved | define a curvatura da aresta. Valores entre 0 e 1 (FALSE equivale a 0 e TRUE a 0.5) |
Outros parâmetros
Parâmetro | Descrição |
---|---|
margin | define o tamanho das margens ao redor do gráfico |
frame | se TRUE desenha uma borda ao redor do gráfico |
main | adiciona título ao gráfico |
sub | adiciona subtítulo ao gráfico |
asp | valor numérico que define a relação altura/largura do gráfico |
palette | paleta de cor a ser usada para colorir os vértices |
rescale | valor lógico utilizado para indicar se as coordenadas devem ser reescaladas para [-1, 1]. A opção padrão é TRUE |
Os layouts da rede referem-se aos diveros algoritmos utilizados para definir as coordenadas dos vértices em um gráfico da rede.
Existem diversos layouts disponíveis para representar as redes. Alguns serão mais adequados que outros em determinadas situações. Existe a possibilidade de passar o layout diretamente como um valor do parâmetro layout=
da função plot()
ou utilizar uma função para o cálculo das coordenadas.
Alguns layouts disponíveis no pacote igraph são:
layout_in_circle()
layout_randomly()
layout_on_sphere()
layout_with_fr()
layout_with_kk()
layout_with_lgl()
layout_with_mds()
layout.fruchterman.reingold()
layout.kamada.kawai()
layout.reingold.tilford()
windowsFonts(JP1 = windowsFont("MS Mincho"),
JP2 = windowsFont("MS Gothic"),
JP3 = windowsFont("Arial Unicode MS"))
lout <- layout.fruchterman.reingold(grafo_sub)
plot(grafo_sub, layout=lout,
vertex.label.family="JP2",
edge.curved=0.2,
vertex.label.cex=0.8,
vertex.label.font=2,
vertex.color="lightgreen")
Um grafo ponderado apresenta pesos associados a cada uma de suas arestas. Estes pesos podem ser alguma medida da “força” do vínculo existente entre os vértices.
No exemplo em análise, será criada uma rede usando a variável pctTotalRecursosSPPS
como peso. Essa variável informa o percentual dos recursos do RPPS aplicado em determinado fundo de investimento. Para ilustrar o procedimento será utilizado o subgrafo grafo_sub
construído anteriormente.
# Definição do atributo width
E(grafo_sub)$width <- as.numeric(E(grafo_sub)$pctTotalRecursosSPPS)
# Criação do gráfico
plot(grafo_sub, vertex.size=9)
Um grafo bipartite é um grafo no qual estão representadas as relações entre dois tipos de objeto. No grafo em análise estão representados tanto os RPPS quanto os fundos de investimentos. Para criar um gráfo bipartite é necessário alterar o atributo type
dos vértices de forma a indicar o tipo de objeto de cada um deles. Isso pode ser feito da seguinte forma:
# Testa para verificar se o grafo é bipartite
is_bipartite(grafo)
## [1] FALSE
# Criar o grafo bipartite
V(grafo)$type <- grepl("^\\d", V(grafo)$name)
# Verificando
is_bipartite(grafo)
## [1] TRUE
No código acima foram utilizados os nomes dos vértices para indicar se o vértice se refere ao RPPS ou ao fundo de investimento. Especificamente se o nome começa com um dígito foi atribuido o valor TRUE
e FALSE
caso contrário. Com isso foi associado o valor TRUE
aos fundos de investimento e FALSE
aos RPPS.
Para atribuir cores diferentes a cada um dos tipos de vértices, define-se o atributo color
com as cores desejadas. No caso em análise será utilizado o atributo type
para auxiliar na definição das cores.
V(grafo)$color <- ifelse(V(grafo)$type, "blue", "red")
Em azul estarão o fundos de investimento e em vermelho os RPPS.
plot(grafo,
vertex.size=7,
vertex.label=NA)
legend("topright",
legend = c("RPPS", "FUNDOS"),
fill = c("red", "blue"), bty="n")
Em gráficos bipartite é possível obter dois subgrafos denominados projeções. No exemplo em análise, um subgrafo contendo os vértices relativos aos RPPS e outro subgrafo com os vértices relativos aos fundos de investimento.
No subgrafo dos RPPS estes estarão conectados se investem em fundos de investimento em comum. Da mesma forma, no subgrafo dos fundos de investimento estes estarão conectados caso tenham recebido investimentos de ao menos um RPPS comum.
Percebe-se assim que, diferentemente dos grafos onde o vínculo é direto, nas projeções o vínculo entre os vértices é índireto.
grafo.proj <- bipartite.projection(grafo)
Projeção dos RPPS
plot(grafo.proj$proj1,
vertex.size=9,
edge.color="lightgrey",
edge.lty=3,
vertex.label.color="black",
vertex.label.font=2,
vertex.color="lightgreen")
Projeção dos Fundos de Investimentos
plot(grafo.proj$proj2,
vertex.size=9,
edge.color="lightgrey",
edge.lty=3,
vertex.label.color="black",
vertex.label.font=2,
vertex.color="lightgreen")
Statistical Analysis of Network Data with R - Eric D. Kolaczyk; Gàbor Csàrdi
A User’s Guide to Network Analytis in R - Douglas A. Luke
Network visualization with R - Katherine Ognyanova www.kateto.net
Network anlysis and visualization with R and igraph - Katherine Ognyanova www.kateto.net
Fraud Analytics - Bart Baesens, Véronique van Vlasselaer, Wouter Verbeke