O tema da violência urbana suscita debates na imprensa, redes sociais e casas legislativas. A par da relevância eleitoral, suscitada pela exploração da política do medo, publicações científicas sérias divulgam estudos elaborados com rigor metodológico.
O Atlas da Violência, produzido pelo Instituto de Pesquisa Econômica Aplicada (Ipea) e pelo Fórum Brasileiro de Segurança Pública (FBSP), fornece dados para compreender a evolução da violência no país, a partir de 1996.
Com base nesse estudo, iniciamos análise exploratória do conjunto de dados, que representa a série histórica da taxa de homicídios dos municípios mineiros, com população acima de 200 mil habitantes, através de técnicas gráficas.
Para tanto, utilizamos o RStudio, interface funcional e amigável para o R, linguagem para cálculo estatístico e visualização gráfica, largamente utilizada por estatísticos e cientistas de dados.
Para adicionar funcionalidades extras, necessárias para baixar e ler páginas web, manipular dados e ler arquivos em diversos formatos, dentre outras utilidades, carregamos as seguintes bibilotecas:
library(tidyverse)
library(rvest)
library(ggthemes)
library(gridExtra)
Definimos o diretório de trabalho, onde serão salvos os arquivos importados:
setwd("/home/gf/Scripts/Atlas")
Baixamos manualmente o arquivo no formato csv, disponível em http://www.ipea.gov.br/atlasviolencia/dados-series/20, contendo a série histórica da taxa de violência de todos os municípios brasileiros, de 1996 a 2016.
Para identificar os municípios com o perfil desejado, utilizamos uma listagem disponível no seguinte endereço: https://pt.wikipedia.org/wiki/Lista_de_munic%C3%ADpios_do_Brasil_por_popula%C3%A7%C3%A3o.
Após definirmos o diretório de trabalho, importamos o arquivo no formato csv, renomeamos os cabeçalhos das colunas e arredondamos a taxa:
homicidios <- read.csv2('taxa-homicidios-brasil-por-municipio-1996-01-15-2016-01-15.csv', header=TRUE, sep=';', stringsAsFactors = FALSE)
homicidios <- plyr::rename(homicidios, c("cod"="COD", "nome"="Município", "período"="Ano", "valor"="Taxa"))
homicidios$Taxa <- round(as.numeric(homicidios$Taxa))
Para lermos a página web, utilizamos uma função que retorna todas as informações do código html:
url <- "https://pt.wikipedia.org/wiki/Lista_de_munic%C3%ADpios_do_Brasil_por_popula%C3%A7%C3%A3o"
page <- read_html(url)
Consultamos a quantidade de tabelas disponíveis na página:
tbls <- html_nodes(page, "table")
length((tbls))
## [1] 1
head(tbls)
## {xml_nodeset (1)}
## [1] <table class="wikitable sortable" style="text-align: center;"><tbody>\n<t ...
Em seguida, extraimos o conteúdo da única tabela existente:
tab <- as.data.frame(html_table(page, fill = FALSE))
Inspecionamos o objeto e efetuamos as modificações desejadas; como por exemplo, simplificar os nomes das variáveis, para facilitar a manipulação:
str(tab)
## 'data.frame': 5570 obs. of 5 variables:
## $ Posição : chr "1º" "2º" "3º" "4º" ...
## $ Código.IBGE : int 3550308 3304557 5300108 2927408 2304400 3106200 1302603 4106902 2611606 5208707 ...
## $ Município : chr "São Paulo" "Rio de Janeiro" "Brasília" "Salvador" ...
## $ Unidade.federativa: chr "São Paulo" "Rio de Janeiro" "Distrito Federal" "Bahia" ...
## $ População : int 12252023 6718903 3015268 2872347 2669342 2512070 2182763 1933105 1645727 1516113 ...
names(tab)[which(names(tab)=="Código.IBGE")] <- "COD"
names(tab)[which(names(tab)=="Unidade.federativa")] <- "UF"
str(tab)
## 'data.frame': 5570 obs. of 5 variables:
## $ Posição : chr "1º" "2º" "3º" "4º" ...
## $ COD : int 3550308 3304557 5300108 2927408 2304400 3106200 1302603 4106902 2611606 5208707 ...
## $ Município: chr "São Paulo" "Rio de Janeiro" "Brasília" "Salvador" ...
## $ UF : chr "São Paulo" "Rio de Janeiro" "Distrito Federal" "Bahia" ...
## $ População: int 12252023 6718903 3015268 2872347 2669342 2512070 2182763 1933105 1645727 1516113 ...
Por fim, extraimos o subconjunto representado pelos municípios que atendam, simultaneamente, os critérios definidos, isto é, pertençam ao Estado de Minas Gerais e tenham população igual ou superior a 200.000 habitantes. No total, treze municípios se encaixam no perfil desejado.
pop <- tab %>%
filter(UF == "Minas Gerais" & População >= 200000) %>%
select(COD, Município, População)
pop
## COD Município População
## 1 3106200 Belo Horizonte 2512070
## 2 3170206 Uberlândia 691305
## 3 3118601 Contagem 663855
## 4 3136702 Juiz de Fora 568873
## 5 3106705 Betim 439340
## 6 3143302 Montes Claros 409341
## 7 3154606 Ribeirão das Neves 334858
## 8 3170107 Uberaba 333783
## 9 3127701 Governador Valadares 279885
## 10 3131307 Ipatinga 263410
## 11 3167202 Sete Lagoas 239639
## 12 3122306 Divinópolis 238230
## 13 3157807 Santa Luzia 219134
Construimos o conjunto de dados a ser analisado, a partir da junção de duas tabelas: a primeira, denominada “homicidios”, contendo a série histórica da taxa de homicidios de todos os municípios brasileiros, de 1996 a 2016; a segunda, denominada “pop”, contendo a relação dos municípios mineiros com mais de 200 mil habitantes. Ao mesclar as duas tabelas, adotando como chave o código do município no IBGE, extraímos um subconjunto que é a representação tabular do recorte amostral, contendo vinte e uma observações, por município, uma para cada registro das variáveis mapeadas:
tm <- pop %>%
select(COD, População) %>%
left_join(homicidios, by = "COD")
Ao todo temos então 273 observações de 4 variáveis:
str(tm)
## 'data.frame': 273 obs. of 5 variables:
## $ COD : int 3106200 3106200 3106200 3106200 3106200 3106200 3106200 3106200 3106200 3106200 ...
## $ População: int 2512070 2512070 2512070 2512070 2512070 2512070 2512070 2512070 2512070 2512070 ...
## $ Município: chr "Belo Horizonte" "Belo Horizonte" "Belo Horizonte" "Belo Horizonte" ...
## $ Ano : int 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 ...
## $ Taxa : num 16 18 22 23 29 30 34 48 52 45 ...
O quadro abaixo apresenta um resumo das estatísticas descritivas para a variável numérica que descreve a taxa de homicídios:
summary(tm$Taxa)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.00 13.00 23.00 27.32 41.00 86.00
Podemos utilizar o diagrama de caule e folha apara apresentar, de forma concisa e numérica, o conjunto de dados referente às taxas de homicídio:
stem(tm$Taxa)
##
## The decimal point is 1 digit(s) to the right of the |
##
## 0 | 111111222222333344444
## 0 | 55566666777778888888999999999999
## 1 | 0000011112222233334444444
## 1 | 555566666666666677777788888888999999
## 2 | 000000011111112222233333333444444
## 2 | 5555556666788899999
## 3 | 001111233444444
## 3 | 56666666677777788899
## 4 | 00011111122223333444444
## 4 | 566778888999999
## 5 | 001112233
## 5 | 5668889
## 6 | 0023344
## 6 | 55
## 7 | 0002
## 7 | 556
## 8 | 0
## 8 | 6
Embora o diagrama de caule e folha permita visualizar a distribuição dos valores, uma boa representação da distribuição dos dados pode ser obtida com o histograma:
histtm <- ggplot(tm) + geom_histogram(aes(x=Taxa), binwidth=5, fill="gray")
histtm
Percebemos que se trata de uma distribuição positivamente assimétrica; isto é, os dados não se distribuem normalmente ao redor da média. Nesse caso, a mediana é a medida mais apropriada para representar os dados.
Como se comportam os dados agrupados por município?
histtm <- ggplot(tm) + geom_histogram(aes(x=Taxa), binwidth=15, fill="gray")
histtm <- histtm + facet_wrap(~Município, scales="free")
histtm
Definida a medida de tendência central apropriada para o conjunto de dados, calculamos a taxa média da série histórica, por município, com base na mediana. Após classificar o resultado em ordem decrescente, giramos o eixo x em noventa graus, no sentido horário, para que a informação visual seja extraída de modo mais eficiente.
hpm <- aggregate(Taxa ~ Município, data = tm, FUN = median, na.rm = TRUE)
gshpm <- ggplot(hpm) + geom_bar(aes(x=reorder(Município, +Taxa), y= Taxa),
stat="identity",
fill="gray")
gshpm <- gshpm + coord_flip() +
theme(axis.text.y = element_text(size=rel (0.8))) +
xlab("") +
ylab("Taxa média")
gshpm
Adicionalmente, podemos construir um gráfico de densidade, que é uma variação do histograma, para estudar a distribuição das variáveis, utilizando a escala contínua, para o conjunto de dados:
dtm <- ggplot(tm) + geom_density(aes(x=Taxa)) +
scale_x_continuous()
dtm
Observamos que as formas de distribuição dos dados variam entre si, quando visualizamos os subconjuntos, separados por município, como já demonstrado anteriormente, quando utilizamos a mesma técnica para visualização de múltiplos histogramas.
dtma <- ggplot(tm) + geom_density(aes(x=Taxa)) +
scale_x_continuous()
dtma <- dtma + facet_wrap(~Município, scales="free")
dtma
Se sobrepusermos um modelo teórico, representado pela distribuição normal, à distribuição do conjunto de dados em análise, utilizando a função de densidade para o eixo vertical, e acrescentarmos a média, ilustrada por uma linha tracejada, visualizamos mais facilmente a assimetria, consoante o recorte intencional de dados, obtido segundo o método de seleção não-probabilístico.
gg <- ggplot(tm, aes(x=tm$Taxa))
gg <- gg + geom_histogram(binwidth=5, colour="black",
aes(y=..density.., fill=..count..))
gg <- gg + scale_fill_gradient("Count", low="#DCDCDC", high="#7C7C7C")
gg <- gg + stat_function(fun=dnorm,
color="red",
args=list(mean=mean(tm$Taxa),
sd=sd(tm$Taxa)))
gg <- gg + xlab("Taxa de homicídios/100 mil habitantes")
gg + geom_vline(xintercept = mean(tm$Taxa, na.rm = T), size = 1, colour = "#FF3721",
linetype = "dashed")
Para uma visão condensada, utilizamos o diagrama de caixa, que permite identificar valores extremos, além de exibir graficamente as cinco medidas fundamentais: a mínima, o primeiro quartil, a mediana, o segundo quartil, a máxima. O intervalo interquatílico, que é uma medida da variabilidade, representa 50% dos dados. Logo, metade das observações está situada na faixa de valores entre a mínima e a mediana. Como a mediana está próxima do primeiro quartil, confirmamos mais uma vez que os dados são positivamente assimétricos. As caudas, representadas pelas linhas verticais, indicam a direção da assimetria.
boxtm <- ggplot(tm, aes(x = factor(0), y = Taxa)) + geom_boxplot() + xlab("") +
scale_x_discrete(breaks = NULL)
out <- ggplot_build(boxtm)[["data"]][[1]][["outliers"]]
names(out) <- levels(factor(tm$x))
tidyout <- purrr::map_df(out, tibble::as_tibble, .id = "Taxa")
boxtm <- boxtm + geom_text(data = tidyout, aes(Taxa, value, label = value),
hjust = -.3)
boxtm
Embora seja unidimensional, isto é, dependa de apenas uma variável para ser construído, variáveis categóricas podem ser utilizadas como critério de agrupamento. Desse modo, desenhamos diagramas de caixa, separadamente, para todos os níveis da variável.
boxtm <- ggplot(tm, aes(Município, Taxa)) +
geom_boxplot() + xlab("")
boxtm <- boxtm + theme(axis.text.x = element_text(angle = 45, hjust = 1))
boxtm
Apesar da baixa variabilidade, indicada pela forma achatada do diagrama, e valor da mediana situado na parte inferior do eixo y, Juiz de Fora contém a maior quantidade de outiliers. Para investigar esta anomalia, primeiro selecionamos o subconjunto de dados para o município; depois, calculamos as estatísticas básicas:
tmjf <- tm %>%
filter(Município == "Juiz de Fora") %>%
select(Ano, Taxa)
summary(tmjf$Taxa)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 6.00 8.00 9.00 12.14 12.00 27.00
Em seguida, visualizamos o diagrama de caixa isoladamente:
boxtmjf <- ggplot(tmjf, aes(x = factor(0), y = Taxa)) + geom_boxplot() + xlab("") +
scale_x_discrete(breaks = NULL)
out <- ggplot_build(boxtmjf)[["data"]][[1]][["outliers"]]
names(out) <- levels(factor(tmjf$x))
tidyout <- purrr::map_df(out, tibble::as_tibble, .id = "Taxa")
boxtmjf <- boxtmjf + geom_text(data = tidyout, aes(Taxa, value, label = value),
hjust = -.3)
boxtmjf
Observem a ausência da cauda superior: 75% dos dados estão situados entre a mínima e o primeiro quartil. Do mesmo modo, a distribuição dos dados é positivamente assimétrica. No entanto, cabe investigar por que um quarto dos dados são considerados outliers. Ou seja, os dados situados entre o terceiro quartil e a máxima se distanciam muito da maioria, ou por erro de mensuração ou por ocorrência de valores atípicos.
Os dados do IPEA estão defasados dois anos em relação ao ano da publicação. Portanto, após a importação do arquivo de dados da série, do Atlas 2017, adicionei à tabela o valor obtido para o ano de 2016, para o município de Juiz de Fora, calculado com base em informações divulgadas na imprensa: http://g1.globo.com/mg/zona-da-mata/noticia/2017/01/delegado-analisa-numero-de-homicidios-em-juiz-de-fora.html.
O Atlas 2018 incluiu os indicadores de 2016; no entanto, foram identificadas divergências entre as duas bases de dados, sem justificativa aparente. Questionado, o IPEA não se manifestou a respeito.
Nesse ponto, vamos direto aos dados.
h <- read.csv2('taxa-homicidios-brasil-por-municipio-1996-2015.csv', header=FALSE, sep=';', stringsAsFactors = FALSE)
h <- plyr::rename(h, c("V1"="Município", "V2"="Ano", "V3"="Taxa"))
hjf <- h %>%
filter(Município == "Juiz de Fora") %>%
select(Ano, Taxa)
hjf <- hjf %>% add_row(Ano = "2016", Taxa = "25.194")
hjf$Taxa <- round(as.numeric(hjf$Taxa))
hjf
## Ano Taxa
## 1 1996 16
## 2 1997 13
## 3 1998 21
## 4 1999 17
## 5 2000 18
## 6 2001 14
## 7 2002 16
## 8 2003 17
## 9 2004 21
## 10 2005 11
## 11 2006 12
## 12 2007 16
## 13 2008 16
## 14 2009 16
## 15 2010 20
## 16 2011 24
## 17 2012 28
## 18 2013 34
## 19 2014 46
## 20 2015 22
## 21 2016 25
summary(hjf$Taxa)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 11.00 16.00 17.00 20.14 22.00 46.00
Colocamos os dois diagramas de caixa lado a lado, para facilitar a comparação.
boxhjf <- ggplot(hjf, aes(x = factor(0), y = Taxa)) + geom_boxplot() + xlab("") +
scale_x_discrete(breaks = NULL)
out <- ggplot_build(boxhjf)[["data"]][[1]][["outliers"]]
names(out) <- levels(factor(hjf$x))
tidyout <- purrr::map_df(out, tibble::as_tibble, .id = "Taxa")
boxhjf <- boxhjf + geom_text(data = tidyout, aes(Taxa, value, label = value),
hjust = -.3)
boxhjf <- boxhjf + xlab("Atlas 2017")
boxtmjf <- boxtmjf + xlab("Atlas 2018")
grid.arrange(boxhjf, boxtmjf, ncol=2)
Referências:
Kabacoff, R. (2011). R in action: Data analysis and graphics with R.
Zumel, Nina and Mount, John. (2014). Practical Data Science with R.