9 Dados quantitativos absolutos: mapa dos pontos de contagem (ou densidade de pontos)
Diferente do mapa de figuras proporcionais, onde o tamanho das figuras remete à ideia de proporção, os pontos de contagem permitem estabelecer a ideia de densidade ao representar determinada quantidade espalhada aleatoriamente dentro dos polígonos de unidades administrativas.
Utilizaremos como exemplo os dados de etnia da população na escala intraurbana do município de São Paulo. Cada ponto terá um valor de número de residentes por Distrito Administrativo. Aos pontos atribuiremos cores para proporcionar a visualização da distribuição espacial das etnias por área. Este mapa tem como inspiração o mapa de grupos étnicos de Chicago, desenvovido por Bill Rankin (2009). Foi replicado em Londres para os votos segundo partidos políticos Tom Campbell. Este script foi utilizado e adaptado para a elaboração do mapa da população segundo etnia em São Paulo, que será desenvolvido a seguir.
9.1 Bases de dados
Para a elaboração deste mapa precisaremos de dados da população segundo cor ou raça. O IBGE define cinco grupos de cor ou raça autodeclarados durante a pesquisa do Censo Demográfico: raça ou cor branca, preta, amarela, parda e indígena. Usaremos os dados disponíveis mais recentes que são os de 2010. A base cartográfica será a malha dos Distritos, também disponíveis no site do IBGE.
Para darmos início, vamos limpar a memória e verificar o diretório.
## [1] "C:/Users/l_viz/Documents/RCartoTematica"
É sempre bom deixarmos alinhado com a língua que usaremos para evitar problemas com acentuação nos títulos. Também vamos desativar a notação científica para que os códigos do IBGE apareçam por completo.
#para ajustar a lingua para portugues usando a funcao
Sys.setlocale(category = "LC_ALL", locale = "pt_BR.UTF-8")
## [1] "LC_COLLATE=pt_BR.UTF-8;LC_CTYPE=pt_BR.UTF-8;LC_MONETARY=pt_BR.UTF-8;LC_NUMERIC=C;LC_TIME=pt_BR.UTF-8"
Carregamos os pacotes que serão utilizados.
library(R.utils)
library(tidyverse)
library(sf)
library(ggplot2,warn.conflicts=FALSE)
library(ggspatial)
library(stringr)
O arquivo shapefile dos Distritos estão disponíveis no FTP do IBGE por Unidade da Federação. Desta forma, faremos o download do arquivo zip após a identificação da url (Uniform Resource Locator, ou Localizador Uniforme de Recursos - significa endereço web, ou seja, o texto que você digita na barra do navegador para acessar uma determinada página ou serviço). Para isso, usaremos a função download.file
do pacote R.utils. A documentação completa dos pacotes e funções estão disponíveis em R documentation.
url <- "https://geosampa.prefeitura.sp.gov.br/PaginasPublicas/downloadArquivo.aspx?orig=DownloadCamadas&arq=01_Limites%20Administrativos%5C%5CDistrito%5C%5CShapefile%5C%5CSIRGAS_SHP_distrito&arqTipo=Shapefile"
destfile <- "C:/Users/l_viz/Documents/RCartoTematica/shapefile.zip"
download.file(url, destfile, mode='wb')
Descompacte o arquivo zip diretamente na pasta de trabalho. Vamos abrir o shapefile com a função st_read
do pacote R.utils.
#para abrir o arquivo shapefile dos distritos da UF São Paulo
sp_distr <- st_read("C:/Users/l_viz/Documents/RCartoTematica/SIRGAS_SHP_distrito/SIRGAS_SHP_distrito.shp")
## Reading layer `SIRGAS_SHP_distrito' from data source
## `C:\Users\l_viz\Documents\RCartoTematica\SIRGAS_SHP_distrito\SIRGAS_SHP_distrito.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 96 features and 7 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 313389.7 ymin: 7343743 xmax: 360618.2 ymax: 7416156
## Projected CRS: SIRGAS 2000 / UTM zone 23S
Para verificar o tipo de objeto, usamos class
do pacote R.utils. Para olharmos as 6 primeiras linhas do objeto, usamos a função head
do mesmo pacote.
## [1] "sf" "data.frame"
## Simple feature collection with 6 features and 7 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 315542 ymin: 7343743 xmax: 360618.2 ymax: 7408231
## Projected CRS: SIRGAS 2000 / UTM zone 23S
## ds_codigo ds_nome ds_sigla ds_cd_sub ds_subpref ds_areamt ds_areakmt
## 1 51 MANDAQUI MAN 05 SANTANA-TUCURUVI 13247864 13.25
## 2 52 MARSILAC MAR 20 PARELHEIROS 208195746 208.20
## 3 32 MOEMA MOE 12 VILA MARIANA 9079516 9.08
## 4 31 GUAIANASES GUA 28 GUAIANASES 8938527 8.94
## 5 33 IGUATEMI IGU 30 SAO MATEUS 19583557 19.58
## 6 36 ITAIM PAULISTA IPA 24 ITAIM PAULISTA 12143789 12.14
## geometry
## 1 POLYGON ((330950.4 7407837,...
## 2 POLYGON ((336124.1 7355302,...
## 3 POLYGON ((331242.2 7392162,...
## 4 POLYGON ((355108.2 7393291,...
## 5 POLYGON ((350859.9 7389600,...
## 6 POLYGON ((359239.6 7402470,...
Vamos visualizar os Distritos usando ggplot
do pacote ggplot2, definindo a geometria a partir de geom_sf
com o contorno na cor preta e o preenchimento em cinza claro.
#para plotar a camada dos distritos do estado de SP
ggplot()+
geom_sf(data = sp_distr, colour = "black", fill = "grey90") #para plotar
## [1] "ds_codigo" "ds_nome" "ds_sigla" "ds_cd_sub" "ds_subpref" "ds_areamt"
## [7] "ds_areakmt" "geometry"
## Simple feature collection with 6 features and 7 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 315542 ymin: 7343743 xmax: 360618.2 ymax: 7408231
## Projected CRS: SIRGAS 2000 / UTM zone 23S
## ds_codigo ds_nome ds_sigla ds_cd_sub ds_subpref ds_areamt ds_areakmt
## 1 51 MANDAQUI MAN 05 SANTANA-TUCURUVI 13247864 13.25
## 2 52 MARSILAC MAR 20 PARELHEIROS 208195746 208.20
## 3 32 MOEMA MOE 12 VILA MARIANA 9079516 9.08
## 4 31 GUAIANASES GUA 28 GUAIANASES 8938527 8.94
## 5 33 IGUATEMI IGU 30 SAO MATEUS 19583557 19.58
## 6 36 ITAIM PAULISTA IPA 24 ITAIM PAULISTA 12143789 12.14
## geometry
## 1 POLYGON ((330950.4 7407837,...
## 2 POLYGON ((336124.1 7355302,...
## 3 POLYGON ((331242.2 7392162,...
## 4 POLYGON ((355108.2 7393291,...
## 5 POLYGON ((350859.9 7389600,...
## 6 POLYGON ((359239.6 7402470,...
A segunda etapa será a obtenção dos dados de raça ou cor a partir dos dados do Censo de 2010. Precisaremos fazer isso manualmente. Baixe os arquivos correspondentes ao município de São Paulo a partir do ftp do IBGE na sua pasta de trabalho https://ftp.ibge.gov.br/Censos/Censo_Demografico_2010/Resultados_do_Universo/Agregados_por_Setores_Censitarios/SP_Capital_20190823.zip.
Descompacte o arquivo manualmente na pasta do diretório onde se encontra para podermos selecionar e ler o arquivo que queremos. Depois de descompactado, execute a linha a seguir. read.table
lerá o arquivo Pessoa03_SP1 de extensão csv com separador entre colunas “;” e primeira linha contendo os nomes das variáveis “(header = TRUE)”, criando o objeto de nome “pessoa”. Aproveitaremos para já selecionar as colunas que nos interessam usando %>%
(pipe, que pode ser lido como “então”) e a função select
selecionando a coluna de código do setor e as variáveis “V001” a “V006”. Com a função head
verificamos as 6 primeiras linhas do objeto “pessoa”.
A documentação do Censo com todas as planilhas e variáveis encontra-se no arquivo BASE DE INFORMAÇÕES POR SETOR CENSITÁRIO Censo 2010 - Universo.pdf.
#para ler o arquivo Pessoa03_SP1.csv
pessoa <- read.table("C:/Users/l_viz/Documents/RCartoTematica/Base informaçoes setores2010 universo SP_Capital/CSV/Pessoa03_SP1.csv", sep=";", header=T) %>%
select(Cod_setor, V001, V002, V003, V004, V005, V006)
head(pessoa)
## Cod_setor V001 V002 V003 V004 V005 V006
## 1 355030801000001 806 661 23 3 119 0
## 2 355030801000002 913 730 16 4 163 0
## 3 355030801000003 625 521 25 8 71 0
## 4 355030801000004 572 430 26 8 108 0
## 5 355030801000005 754 580 28 23 123 0
## 6 355030801000006 643 552 11 0 80 0
O resultado da função head
nos mostra que as variáveis de “V002” a “V006” são do tipo caractere (chr). Teremos que alterar para “integer” para que possam ser executados cálculos com estes valores. Vamos usar mutate
que permite a transformação de todas estas variáveis de uma única vez. Ou seja, se a variável for caractere, será transformada em “integer”.
#para transformar todas as variáveis do tipo chr em int
pessoa <- pessoa %>%
mutate_if(is.character, as.integer)
O aviso resultante do comando diz que foram introduzidos valores do tipo “NA” (not available, ou seja, valores faltantes) onde havia valores não numéricos do tipo “x”. O IBGE restringe a informação para proteção de dados dos informantes. Segundo a documentação,
“No arquivo agregado por setores, o IBGE optou pela restrição de dados como forma de proteção dos dados dos informantes do Censo Demográfico 2010. Assim, em todos os setores com menos de cinco domicílios particulares permanentes foram omitidos os valores da maioria das variáveis de dados. Foram mantidas apenas as variáveis estruturais tais como: a identificação das subdivisões geográficas, o número de domicílios e a população por sexo. Para indicar a omissão dos dados, os valores das variáveis foram preenchidos com “x”. (IBGE, 2011, p.35)>
Como vimos, a coluna “Cod_setor” aparece como numérica “dbl”(double) e precisa ser convertida para caractere. Usamos a função a seguir.
#para converter "dbl" para "chr"
pessoa <- pessoa %>%
mutate(Cod_setor = as.character(Cod_setor))
#para ver as 6 primeiras linhas
head(pessoa)
## Cod_setor V001 V002 V003 V004 V005 V006
## 1 355030801000001 806 661 23 3 119 0
## 2 355030801000002 913 730 16 4 163 0
## 3 355030801000003 625 521 25 8 71 0
## 4 355030801000004 572 430 26 8 108 0
## 5 355030801000005 754 580 28 23 123 0
## 6 355030801000006 643 552 11 0 80 0
Como vamos precisar unir a tabela de dados com o shapefile, as colunas de identificadores devem ser exatamente iguais. No shapefile temos 96 Distritos Administrativos, cuja coluna de identificação é CD_GEOCODD, com 9 dígitos. Na tabela “pessoa”, o identificador é “Cod_setor”, com 15 dígitos. Desta forma, precisaremos compatibilizar os identificadores deixando “Cod_setor” com 9 dígitos também.
#para deixar o codigo do setor com o mesmo numero de caracteres do codigo do distrito
pessoa$Cod_setor <- substring(pessoa$Cod_setor, 8, 9)
#para ver as 6 primeiras linhas
head(pessoa)
## Cod_setor V001 V002 V003 V004 V005 V006
## 1 01 806 661 23 3 119 0
## 2 01 913 730 16 4 163 0
## 3 01 625 521 25 8 71 0
## 4 01 572 430 26 8 108 0
## 5 01 754 580 28 23 123 0
## 6 01 643 552 11 0 80 0
## [1] "data.frame"
Ao abrirmos a tabela com a função View
, percebemos que temos os códigos com 9 dígitos, mas continuamos com 18.363 linhas referentes aos setores censitários e não as 96 dos Distritos! Como cada Distrito tem vários setores (em média, 18.363/96=191 setores), precisamos somá-los dentro de cada Distrito para termos os valores corretos de cada variável, com um total de 96 linhas. Ou seja, precisamos agregar os valores dos setores por meio de soma.
# Para agregar as variáveis das colunas 2 a 7, usando Cod_setor como quebra, função de soma e ignorando os valores NA
pessoa_distr <- aggregate(pessoa[,2:7],by=list(pessoa$Cod_setor),FUN=sum, na.rm=TRUE)
Agora sim, temos uma tabela com os 96 Distritos administrativos de São Paulo e os valores somados de cada variável. Vamos, então, corrigir os nomes das colunas.
#para alterar os nomes das colunas de pessoa_distr
names(pessoa_distr) <- c('ds_codigo', 'Total', 'Branca', 'Preta', 'Amarela', 'Parda', 'Indigena')
#para verificar que a alteração foi feita
names(pessoa_distr)
## [1] "ds_codigo" "Total" "Branca" "Preta" "Amarela" "Parda" "Indigena"
## [1] "data.frame"
Observe que ‘ds_codigo’ em sp_distr tem apenas 1 dígito para os códigos menores do que 10 enquanto que ‘ds_codigo’ de pessoa_distr tem 2 dígitos para os códigos menores do que 10. Precisamos tornar estes campos iguais para que a união seja correta. Vamos usar o pacote stringr
para isso.
Com a tabela compatível com o shapefile, podemos uni-los. Vamos chamar o novo objeto de “sp_pessoa_distr” e usar a função left_join
, indicando o campo de união (ds_codigo). Uniremos o data.frame “pessoa_distr” ao objeto “sf” (simple feature) “sp_distr”. Então, tranformamos o novo objeto em classe “sf” com a função st_as_sf
.
#para unir o arquivo shapefile com o dataframe
sp_pessoa_distr <- sp_distr %>%
left_join(pessoa_distr, by = "ds_codigo") %>%
st_as_sf()
#para conferir os nomes das colunas
names(sp_pessoa_distr)
## [1] "ds_codigo" "ds_nome" "ds_sigla" "ds_cd_sub" "ds_subpref" "ds_areamt"
## [7] "ds_areakmt" "Total" "Branca" "Preta" "Amarela" "Parda"
## [13] "Indigena" "geometry"
## Simple feature collection with 6 features and 13 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 315542 ymin: 7343743 xmax: 360618.2 ymax: 7408231
## Projected CRS: SIRGAS 2000 / UTM zone 23S
## ds_codigo ds_nome ds_sigla ds_cd_sub ds_subpref ds_areamt ds_areakmt Total
## 1 51 MANDAQUI MAN 05 SANTANA-TUCURUVI 13247864 13.25 107580
## 2 52 MARSILAC MAR 20 PARELHEIROS 208195746 208.20 8258
## 3 32 MOEMA MOE 12 VILA MARIANA 9079516 9.08 83368
## 4 31 GUAIANASES GUA 28 GUAIANASES 8938527 8.94 103996
## 5 33 IGUATEMI IGU 30 SAO MATEUS 19583557 19.58 127662
## 6 36 ITAIM PAULISTA IPA 24 ITAIM PAULISTA 12143789 12.14 224074
## Branca Preta Amarela Parda Indigena geometry
## 1 79320 6209 2012 19985 54 POLYGON ((330950.4 7407837,...
## 2 4045 463 92 3533 42 POLYGON ((336124.1 7355302,...
## 3 75550 797 2915 4057 49 POLYGON ((331242.2 7392162,...
## 4 49907 9995 478 43557 59 POLYGON ((355108.2 7393291,...
## 5 61822 8559 785 56373 123 POLYGON ((350859.9 7389600,...
## 6 99761 19493 1333 103245 236 POLYGON ((359239.6 7402470,...
9.2 Definição dos pontos aleatórios
A terceira etapa para a elaboração do mapa de densidade de pontos é a geração de um objeto de pontos definindo o valor de cada ponto. Se temos mais de 11 milhões de pessoas em São Paulo, um mapa com 11 milhões de pontos não ajudaria muito, pois eles cobririam todos os polígonos. Se cada ponto tiver o valor de 200 pessoas, teremos 55 mil pontos. Precisamos ficar atentos com os valores baixos da tabela. A população indígena varia de 16 a 1002 pessoas nos Distritos com menor e maior número, respectivamente. Ou seja, na divisão por 200, distritos com menos de 200 pessoas autodeclaradas indígenas vão ter 1 ponto devido a àproximação que será feita (superestimando valores baixos para evitar polígono com zero ponto desta etnia).
# para gerar objeto do número de pontos para serem plotados para cada etnia (1 para cada 200 pessoas)
num.pontos200 <- ceiling(select(as.data.frame(sp_pessoa_distr), Branca:Indigena) / 200)
#para ver a classe do objeto criado
class(num.pontos200)
## [1] "data.frame"
Vamos olhar o objeto gerado.
## [1] "Branca" "Preta" "Amarela" "Parda" "Indigena"
A função sapply
aplica o comando para todas as colunas. No caso, usamos sapply
para ver a classe de todas as colunas.
#para converter valores numéricos em inteiros ("integer")
num.pontos200 <- num.pontos200 %>%
mutate_if(is.numeric, as.integer)
#para verificar as colunas e tipos de dados
sapply(num.pontos200, class)
## Branca Preta Amarela Parda Indigena
## "integer" "integer" "integer" "integer" "integer"
Agora vamos distribuir, de forma aleatória (random) os números de pontos gerados por polígono.
#para gerar os pontos aleatórios em cada polígono: st_sample gera os pontos em cada polígono
#st_cast lança o conjunto como "POINT"
#st_coordinates extrai as coordenadas para uma matriz
#converte como tibble
#setNames define os nomes das colunas
#cria a variável "Etnia"
#map_df une cada tibble da Etnia em um
pts <- map_df(names(num.pontos200),
~ st_sample(sp_pessoa_distr, size = num.pontos200[,.x], type = "random") %>%
st_cast("POINT") %>%
st_coordinates() %>%
as_tibble() %>%
setNames(c("lon","lat")) %>%
mutate(Etnia = factor(.x, levels = names((num.pontos200))))
)
Vamos definir as cores para cada etnia criando uma paleta de cores com o nome “pal”. Tanto o nome das colunas quanto as cores devem estar entre aspas. Por ser um dado qualitativo, as cores devem ser bem diferentes, evitando ordenamento entre elas.
9.3 Mapa de raça ou cor dos residentes do município de São Paulo
Finalmente vamos elaborar o mapa adicionando as duas camadas: polígono com os Distritos Administrativos e camada de pontos segundo etnia. O mapa terá o fundo em preto, com os limites dos Distritos e textos em branco. A primeira camada (sp_pessoa_distr) será transparente com contorno branco. A segunda camada conterá a informação do objeto “pts” com os pontos aleatórios por polígono (cada ponto valendo 200 pessoas). As cores dos pontos se referem a cada etnia e são definidas pela paleta “pal”. Definimos o CRS = 4989 (SIRGAS 2000). O tema será “theme_void” onde o tamanho da fonte será 30 pontos. labs
define os textos para o título, subtítulo e nota de rodapé. guides
define as cores da legenda e o tamanho. Por fim, theme
define as cores do painel, posição da legenda, margens, cor dos textos e o tamanho da fonte da nota de rodapé.
## Simple feature collection with 6 features and 13 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 315542 ymin: 7343743 xmax: 360618.2 ymax: 7408231
## Projected CRS: SIRGAS 2000 / UTM zone 23S
## ds_codigo ds_nome ds_sigla ds_cd_sub ds_subpref ds_areamt ds_areakmt Total
## 1 51 MANDAQUI MAN 05 SANTANA-TUCURUVI 13247864 13.25 107580
## 2 52 MARSILAC MAR 20 PARELHEIROS 208195746 208.20 8258
## 3 32 MOEMA MOE 12 VILA MARIANA 9079516 9.08 83368
## 4 31 GUAIANASES GUA 28 GUAIANASES 8938527 8.94 103996
## 5 33 IGUATEMI IGU 30 SAO MATEUS 19583557 19.58 127662
## 6 36 ITAIM PAULISTA IPA 24 ITAIM PAULISTA 12143789 12.14 224074
## Branca Preta Amarela Parda Indigena geometry
## 1 79320 6209 2012 19985 54 POLYGON ((330950.4 7407837,...
## 2 4045 463 92 3533 42 POLYGON ((336124.1 7355302,...
## 3 75550 797 2915 4057 49 POLYGON ((331242.2 7392162,...
## 4 49907 9995 478 43557 59 POLYGON ((355108.2 7393291,...
## 5 61822 8559 785 56373 123 POLYGON ((350859.9 7389600,...
## 6 99761 19493 1333 103245 236 POLYGON ((359239.6 7402470,...
p <- ggplot()+
geom_sf(data = sp_pessoa_distr, fill = "transparent", colour = "white")+
geom_point(data = pts, aes(lon, lat, colour = Etnia))+
scale_colour_manual(values = pal)+
coord_sf(crs = 31983)+
theme_void(base_family = "", base_size = 45)+
labs(x = NULL, y = NULL,
title = "Raça ou cor no município de São Paulo, Brasil",
subtitle = "1 ponto = 200 pessoas",
caption = "Fontes de dados: IBGE, Censo Demográfico 2010 | \n
Elaborado por: @Ligiaviz")+
guides(colour = guide_legend(override.aes = list(size = 5)))+
theme(legend.position = c(1, 1.01), legend.direction = "horizontal",
plot.background = element_rect(fill = "#212121", color = NA),
panel.background = element_rect(fill = "#212121", color = NA),
legend.background = element_rect(fill = "#212121", color = NA),
legend.key = element_rect(fill = "#212121", colour = NA),
plot.margin = margin(0.5, 0.5, 0.5, 0.5, "cm"),
text = element_text(color = "white"),
title = element_text(color = "white"),
plot.caption = element_text(size = 35, hjust = 1))+
ggspatial::annotation_scale(location = "br",
text_cex= 2,
text_col = "white",
bar_cols = c("white", "white"),
width_hint = 0.4,
line_width = 0.5,
height = unit(0.3,"cm"))
Vamos exportar no formato png com boa resolução.
#para exportar o mapa como figura
ggsave("etnia.png", plot = p, dpi = 320, width = 85, height = 70, units = "cm")
A figura exportada deverá ter a aparência a seguir.
9.4 Referências sugeridas
Grupos étnicos de Chicago, desenvovido por Bill Rankin (2009)
Votos em Londres, segundo partidos políticos, Tom Campbell