<jupyter_start><jupyter_text>"Fazendo uma Pirâmide Etária com Plotly"> "De quebra aprender a fazer subplots com Python"- toc: true- branch: master- badges: true- comments: true- author: <NAME>- categories: [Plotly, Gráficos, Python]<jupyter_code># hide_input
# This cell is required for the export to HTML to work.
import plotly.io as pio
#pio.renderers.default = 'plotly_mimetype+notebook'
pio.renderers.default = 'colab'
# Default is plotly_mimetype+notebook, but jekyll fails to parse plotly_mimetype.
#pio.renderers.default = 'notebook_connected'
# Uncomment below to avoid using a CDN for plotly.js
# pio.renderers.default = 'notebook'

# Inject the missing require.js dependency.
from IPython.display import display, HTML
#js = '<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg==" crossorigin="anonymous"></script>'
#display(HTML(js))<jupyter_output><empty_output><jupyter_text>A Pirâmide EtáriaFrequentemente, nos meus diagnósticos para **Planejamento Urbano e Regional**, preciso fazer análise da **estrutura etária da população**, para identificar demandas por creches, escolas, trabalho e atendimento de saúde para idosos. Esse tipo de trabalho é realizado com base nas pirâmides etárias. Dada a importância desse tipo de gráfico nas análises populacionais, adiante demonstrarei como elaborá-los com Python, utilizando o pacote [Plotly](https://plotly.com/python/).> Tip: Neste texto, vamos direto ao trabalho operacional. Para saber sobre definição do que é a pirâmide etária e suas mudanças no Brasil, veja [aqui um ótimo vídeo do IBGE](https://www.youtube.com/watch?v=UPgR_LL0Fz0).Aqui está o gráfico que vamos gerar para o município de São José dos Pinhais/PR.![Pirâmide Etária](https://raw.githubusercontent.com/augustogeog/augustogeog/master/images/piramide_etaria.png)  Obtenção e Tratamento dos DadosSempre penso no meu fluxo de trabalho analítico como obtenção, tratamento, análise e dados e divulgação de resultados. Aqui não será diferente. Então vamos começar pela obtenção e tratamento. Assim, os dados sobre população de São José dos Pinhais/PR, segundo faixas etárias, foram obtidos na base **[SIDRA](https://sidra.ibge.gov.br/Tabela/200)** do IBGE e armazenados no arquivo estruturaetaria.csv. Vamos carregar os dados em Pandas DataFrame e observar o seu conteúdo.<jupyter_code>import pandas as pd # importando o pacote pandas

df_estrutura_etaria = pd.read_csv('https://raw.githubusercontent.com/augustogeog/augustogeog/master/assets/data/estruturaetaria.csv', sep=';') # carregando os dados do CSV
df_estrutura_etaria # visualização dos dados importados<jupyter_output><empty_output><jupyter_text>Podemos ver que a tabela se encontra em formato largo, com cada variável, grupo etário, representada por uma coluna. Para trabalharmos com Plotly, é mais adequado transformarmos o DataFrame em formato longo, com uma coluna chamada faixa, que conterá cada faixa etária correspondente ao respectivo montante populacional. Para tanto, podemos utilizar o método _melt_, para fazer essas alterações.<jupyter_code># melt, derreter em Inglês, derrete uma tabela larga e a transforma em longa 
df_estrutura_etaria = df_estrutura_etaria.melt(id_vars=['Codmun', 'Sexo'], var_name='Faixa', value_name='População')<jupyter_output><empty_output><jupyter_text>Vejamos as dez primeiras linhas do DataFrame resultante.<jupyter_code>df_estrutura_etaria.head(10) # chamando as dez primeiras linhas<jupyter_output><empty_output><jupyter_text>É conveniente também separarmos os dados do sexo masculino e do sexo feminino em diferentes DataFrames.<jupyter_code>df_estrutura_etaria_f = df_estrutura_etaria[df_estrutura_etaria['Sexo'] == 'Feminino']
df_estrutura_etaria_m = df_estrutura_etaria[df_estrutura_etaria['Sexo'] == 'Masculino']<jupyter_output><empty_output><jupyter_text>Adiante estão as dez primeiras linhas para o DataFrame com a população do sexo feminino.<jupyter_code>df_estrutura_etaria_f.head(10)<jupyter_output><empty_output><jupyter_text>Criando os Gráficos de Estrutura Etária Separadamente  Agora que temos os dados na estrutura certa, precisamos importar a função **make_subplots**, pois faremos um gráfico com dois pares de eixos, ou seja, dois subplots. Também é preciso importar **Plotly Express**, API de alto nível de abstração que permite fazer gráficos com apenas uma função.<jupyter_code>from plotly.subplots import make_subplots #importando a função make_subplots
import plotly.express as px # importando plotly express<jupyter_output><empty_output><jupyter_text>A lógica que vamos adotar para produzir esse gráfico é produzir dois gráficos distintos e colocá-los em justaposição, lado a lado, como subplots de uma outra figura. Para tanto, vamos fazer o primeiro gráfico de barras, com a **função bar** do subpacote Plotly Express. Essa função apresenta vários argumentos nos quais podemos avançar significativamente no desenvolvimento do produto analítico. Vamos indicar que o Data Frame a ser considerado deve ser aquele para a estrutura etária feminina, que a coluna para o eixo x deve ser População, a coluna para o eixo y deve ser Faixa, as cores devem ser associadas à coluna Sexo, a orientação do gráfico deve ser horizontal e a cor para o sexo feminino deve ser vermelha, segundo padrão rgb.<jupyter_code>plot_fem = px.bar( # função para criar um gráfico de barras
    data_frame=df_estrutura_etaria_f # indicação de do DataFrame a ser considerado para fazer o plot
    , x='População'  # associação da coluna População ao eixo x
    , y='Faixa' # associação da coluna Faixa ao eixo y
    , color='Sexo' # associação da coluna Sexo a cor
    , orientation='h' # seleção da orientação horizontal
    , color_discrete_map={'Feminino':'rgb(228, 26,28)'}) # mapeamento da cor vermelha ao sexo feminino

plot_fem.show() # fazendo a renderização do gráfico com o método show<jupyter_output><empty_output><jupyter_text>Agora vamos fazer o mesmo para os dados da população de sexo masculino. A única diferença é que não vamos escolher uma cor específica e ficar com aquela que for aplicada por padrão pela função.<jupyter_code>plot_masc = px.bar( # função para criar um gráfico de barras
    data_frame=df_estrutura_etaria_m # indicação de do DataFrame a ser considerado para fazer o plot
    , x='População' # associação da coluna População ao eixo x
    , y='Faixa' # associação da coluna Faixa ao eixo y
    , color='Sexo' # associação da coluna Sexo a cor
    , orientation='h') # seleção da orientação horizontal

plot_masc.show()<jupyter_output><empty_output><jupyter_text>Gerando uma Figura com Espaços para SubplotsAcima temos dois gráficos separados, mas precisamos que eles estejam lado a lado para uma adequada visualização da estrutura etária. Assim, vamos utilizar a função **make_suplots**, que gera uma figura com espaços separados em linhas e colunas para que possam ser associados eixos de gráficos, os subplots.Na função, vamos indicar que queremos espaço para dois subplots, em uma única linha, argumento rows, dividida em duas colunas, argumento cols. Também é preciso atribuir True ao argumento shared_yaxes, o que indica que os dois subplots compartilharão um único eixo vertical. Por fim, precisamos estabelecer que não deve haver espaçamento entre ambos os subplots - horizontal_spacing=0.<jupyter_code>fig = make_subplots( # Função para fazer uma figura com subplots
    rows=1 # indicação que a figura deve ter uma linha para subplots
    , cols=2 # indicação que a figura deve ter duas colunas para subplots
    , shared_yaxes=True # indicação de que os subplots deve comparilhar um único eixo vertical, y
#    , shared_xaxes=True
    , horizontal_spacing=0) # indicação de que o espaçamento horizontal entre ambas os plots deve ser zero<jupyter_output><empty_output><jupyter_text>Quando pedimos para Plotly renderizar essa figura vazia, ela mostra um dos espaços reservado para um dos subplots.<jupyter_code>fig.show()<jupyter_output><empty_output><jupyter_text>Inserindo os Gráficos como SubplotsPrecisamos inserir os dois plots elaborados primeiramente, plot_fem e plot_masc, no espaço criado para eles na figura fig. Para podermos fazer isso, devemos entender a estrutura desses objetos. Vamos começar com a figura fig.<jupyter_code>print(fig)<jupyter_output>Figure({
    'data': [],
    'layout': {'template': '...',
               'xaxis': {'anchor': 'y', 'domain': [0.0, 0.5]},
               'xaxis2': {'anchor': 'y2', 'domain': [0.5, 1.0]},
               'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0]},
               'yaxis2': {'anchor': 'x2', 'domain': [0.0, 1.0], 'matches': 'y', 'showticklabels': False}}
})<jupyter_text>Notamos acima que fig é um objeto **Plot Figure**, uma série de instruções em forma de árvore, como um dicionário, que apresenta, na chave **data** (dados), uma lista vazia, ou seja, nenhum dado, ao passo que tem um **layout** com espaço para dois subplots, o primeiro formado pelos eixos x e y, e o segundo formado pelos eixos x2 e y2. O primeiro subplot toma metade do espaçamento horizontal da figura (0.0 a 0.5), e o segundo indo de 0.5, metade do eixo horizontal, até o o ponto mais a direita, 1.0. Por essa razão, o que precisamos inserir aqui são os valores que devem ir em data, ou seja, o mapeamento dos dados aos respectivos eixo e cores. É importante lembrar que data não se refere somente aos dados tabulares, mas à sua codificação a atributos gráficos, indicando que dado estará mapeado no eixo x, qual dado estará mapeado ao eixo y, às cores e assim por diante, ainda com a especificação de atributos desses elementos gráficos (cores específicas de conjunto de barras, por exemplo). Em Plotly, essa estrutura de mapeamento de dados a suas representações gráficas é chamada de **traces**.Vejamos agora o que é o objeto plot_fem.<jupyter_code>print(plot_fem)<jupyter_output>Figure({
    'data': [{'alignmentgroup': 'True',
              'hovertemplate': 'Sexo=Feminino<br>População=%{x}<br>Faixa=%{y}<extra></extra>',
              'legendgroup': 'Feminino',
              'marker': {'color': 'rgb(228, 26,28)', 'opacity': 0.5},
              'name': 'Feminino',
              'offsetgroup': 'Feminino',
              'orientation': 'h',
              'showlegend': True,
              'textposition': 'auto',
              'type': 'bar',
              'x': array([10269, 10483, 11945, 11636, 12055, 12834, 12195, 10928, 10021,  8334,
                           6692,  5508,  3715,  2642,  1906,  1068,  1382], dtype=int64),
              'xaxis': 'x',
              'y': array(['0 a 4 anos', '5 a 9 anos', '10 a 14 anos', '15 a 19 anos',
                          '20 a 24 anos', '25 a 29 anos', '30 a 34 anos', '35 a 39 anos',
                          '40 a 44 anos', '45 a 49 anos', '50 a 54 anos', '55 a 59 anos',
                          '60 a 64 anos', '65 a 69 anos[...]<jupyter_text>Nota-se que o plot_fem também é um objeto **Plotly Figure**, com especificação de **data** e de **layout**. Por essa razão, não podemos simplesmente inserir plot_fem como um subplot de fig. Cada um apresenta seu próprio layout. O que precisamos é passar apenas os valores da chave data, que pode ser acessada pela notação de recuperação de dados de dicionário plot_fem\['data'\]\[0\]. Assim, podemos utilizar o método add_traces, chamado no objeto fig, indicando em que linha, row, e coluna, cols, esses traces devem ser inseridos.<jupyter_code>fig.add_traces(plot_fem['data'][0], rows=1, cols=1);
fig.add_traces(plot_masc['data'][0], rows=1, cols=2);

fig.show()<jupyter_output><empty_output><jupyter_text>Ajustes Finais Estamos quase lá. Precisamos fazer alguns ajustes. O eixo x da primeira figura precisa ter seu sentido invertido.<jupyter_code>fig.layout.xaxis.autorange = 'reversed'
fig.show()<jupyter_output><empty_output><jupyter_text>Precisamos indicar que as barras nao podem estar agrupadas em diferentes espaços, mas sobrepostas, em overlay.<jupyter_code>fig.layout.barmode = 'overlay'

fig.show()<jupyter_output><empty_output><jupyter_text>Agora podemos tirar as legendas, inserir títulos de eixos x, além de inserir o título do gráfico.<jupyter_code>fig.data[0].showlegend= False
fig.data[1].showlegend= False

fig.layout.xaxis.title.text = 'Feminino'
fig.layout.xaxis2.title.text = 'Masculino'

fig.layout.title.text = f'<b>Pirâmide Etária de São José dos Pinhais - 2010<b>'

fig.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0862663352842139, 'fraction_numerical': 0.01827948368475908, 'mean_word_length': 4.823529411764706, 'pattern_counts': {'":': 0, '<': 76, '<?xml version=': 0, '>': 78, 'https://': 6, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '50309', 'license': 'Apache-2.0', 'n_tokens_mistral': 4547, 'n_tokens_neox': 4326, 'n_words': 1453, 'path': '_notebooks/2021-06-06-piramide-etaria-plotly.ipynb', 'repo_name': 'augustogeog/augustogeog', 'size': '12493'}