Buscar
  • Carlos Silva

Publique um Dashboard online com Python e Streamlit

Em um processo de Ciência de Dados Espaciais (e não espaciais também) , existe um sequência de passos bem definida que segue em ETL (Extration Transform and Load) , Enriquecimento de dados , EDA (Exploratory Data Analysis), Modelagem e Visualização e Compartilhamento de Resultados, nem sempre nessa ordem mas geralmente sempre nessa lógica. Hoje quero trazer atenção para o último item. A Visualização e compartilhamento de dados.


Parece ser forçado mas a visualização e compartilhamento dos dados pode ser o grande ‘check-mate’ ou ‘continue’ de qualquer projeto, já que é nessa parte que geralmente o seu cliente avalia o valor do que você está trabalhando. Para garantir isso se utilizam recursos mais claros como de Storytelling de Dados, palavras chave, KPIs e outros recursos indiretos como de desempenho, e reprodutibilidade da própria visualização. Agilidade e praticidade conta muito também, já que alguns projetos dentro de provas de conceito tem um tempo de execução muito curto e a parte de apresentação de resultados acaba ficando para o que resta de tempo do projeto.


Seria muito bom simplesmente fazer um copia e cola dos códigos de visualização do Jupyter Notebook de analise e deixar pronto para cliente de maneira rápida e fácil através de um link da Web sem agregar grandes custos ao projeto final, como pagando uma licença de Power BI Premium ou tendo que subir uma estrutura para um servidor através de um Framework como o Dash. O Streamlit permite isso, e hoje vou mostrar um pouco de como funciona.


Sobre o Streamlit

Vou deixar aqui o que está escrito no próprio site oficial da Streamlit já que o foco aqui é praticidade, o Streamlit é "Uma maneira mais rápida de criar e compartilhar apps de dados" e "...transforma scripts de dados em aplicativos da Web compartilháveis ​​em minutos. Tudo em puro Python. Nenhuma experiência de front-end é necessária."

Basicamente você só precisa importar o Streamlit para seu código e utilizar de algumas funções dele para escolher quais visualizações quer mostrar na tela (gráficos, mapas e tabelas), joga isso tudo no GitHub e pede pra plataforma consultar o código de lá e "Voilà".


Sobre os dados


O conjunto de dados utilizado inclui um registro para cada árvore na cidade de Nova York e inclui a localização da árvore por bairro e latitude/longitude, espécies por nome latino e nomes comuns, tamanho e saúde. O censo de árvores de 2015 foi conduzido pela equipe da 'NYC Parks and Recreation, TreesCount!' e centenas de voluntários.


Montando a estrutura


O conjunto de bibliotecas foi o seguinte;

# Para carregar os dados
import pandas as pd
from json import load
# Para as vizualiazções 
import plotly.graph_objects as go
import streamlit as st

Carregando os dados;

#Base de dados das árvores. É um subconjunto dos dados originais
temp_df = pd.read_csv('pine_NY_2015.csv')

#Mapa de bairros de NY em geojson
f = open('NTA_map.geojson')
nta_json = load(f)
for i in list(range(0,len(nta_json['features']))):
    json_id=nta_json['features'][i]['properties']['ntacode']
    nta_json['features'][i]['id']=json_id

No Streamlit existem várias funções, algumas para definir a estrutura da tela, para mostrar dados e outras para dar input e filtrar os dados, aqui estão algumas;

#Mostra um texto na estrutura de markdown
st.markdown("# Relatório do senso das árvores de pinus de na cidade de Nova York em 2015")

#Mostra um controle deslizante com valores pré definidos. Nesse caso vai de 10 a 20 e iniciando com 10. Joga dentro de uma variável o valor.
rowstable = st.slider('Quantas linhas quer explorar?', 10, 30, 10)

#Mostra caixa de seleção na tela baseado nas opções de uma lista. Joga o valor escolhido em uma variável.
option = st.selectbox('Qual espécie você quer analisar?',(lista))

#Estrutura colunas para visualizações. Os valores dentro da Tupla vão ditar a proporção de cada coluna na visualização.
col2_1, col2_2 = st.columns([2.7, 1.0]) #Crio as colunas
#Adicionando os widgets as colunas
with col2_1: #Caixa de seleção de espécie
    option = st.selectbox('Qual esptécie você quer analisar?',(lista))

with col2_2: #Slider de DAP, pega o DAP máximo da esp. escolhida
    tree_dbh_slice = st.slider('Verificar DAP\'s maiores que;', 0, int(temp_df.loc[temp_df['spc_common']==option].tree_dbh.max()), 1)

#Mostra uma figura da biblioteca Plotly na tela
st.plotly_chart(BoxPlotfig, use_container_width=True)

Montei algumas funções para visualização de dados;

#Retorna um gráfico de pizza depois de receber um pandas Dataframe 
#e o nome da coluna que se quer quantificar
def PieChartPlotly (df,col):
    #df = Dataset;
    #col = Coluna para agrupar os dados;
    countCol = df.groupby(by=col).size()
    labels = list(countCol.index)
    values = list(countCol.values)
    figPie = go.Figure(data=[go.Pie(labels=labels,
                                    values=values,
                                    textinfo='label+percent+value',
                                    insidetextorientation='radial')
                            ])
    return figPie

#Ao receber um pandas Dataframe retorna um conjunto de gráficos BoxPlot 
#categorizados  por uma coluna e quantificados a partir de outra coluna
def BoxPlotPlotly(df,col,colValue):
    #df = Dataset;
    #col = Coluna para agrupar os dados;
    #colValue = Coluna dos valores que serão distribuídos;
    data=[]
    for t in df[col].value_counts().index:
        data.append(go.Box(y=df[colValue].loc[df[col]==t],
                       name = t))
    BoxPlotfig = go.Figure(data=data)
    return BoxPlotfig

#Mapa coroplético
def ChoropletEspPlotly(df,col_count,option,georef,colref,name):
    #df = Dataset;
    #col_count = Coluna que se quer contar;
    #option = Resultado da caixa de seleção;
    #georef = Referencia geográfica. Bairros de NY;
    #colref = Coluna que se vai agrupar;
    #name = Nome do mapa;
    ChoropletGRaf = go.Choroplethmapbox(geojson=georef, 
                                locations=df[colref].loc[df[col_count]==option].value_counts().index, 
                                z=df[colref].loc[df[col_count]==option].value_counts().values,
                                colorbar=dict(title=name),
                                colorscale="YlGn",marker_opacity=0.5
                                )
    return ChoropletGRaf

O resultado de algumas dessa funções é algo como mostrado abaixo;

Eu posso juntar as estruturas de input e as funções de gráfico para montar gráficos dinâmicos;

####Montando os inputs de filtro dos gráficos de espécie
col2_1, col2_2 = st.columns([2.7, 1.0]) #Estruturando a visualização
with col2_1: #Caixa de seleção de espécie
    option = st.selectbox('Qual espécie você quer analisar?',(esp))

with col2_2: #Slider de DAP da espécie
    tree_dbh_slice = st.slider('Verificar DAP\'s maiores que;', 0, int(temp_df.loc[temp_df['spc_common']==option].tree_dbh.max()), 0)

#Filtro o dataset baseado no Slider de DAP da espécie
df_filter = temp_df.loc[temp_df['tree_dbh']>=tree_dbh_slice]

#Mapa Coroplético de espécie
#Uso a variável option da Caixa de seleção de espécie e o dataset filtrado
Choroplet = ChoropletEspPlotly(df = df_filter,
                                   col_count = 'spc_common',
                                   option = option, 
                                   georef =nta_json,
                                   colref='nta',
                                   name='Num. Árvores')

#Pontos de cada árvore usando o dataset filtrado
Scatter = ScatterMapPotly(df=df_filter,
                          option=option,
                          colOption='spc_common',
                          label='tree_dbh')

#Layout do mapa
layout=go.Layout(margin={"r":0,"t":50,"l":0,"b":0},
                 mapbox_style="carto-positron",
                 mapbox_zoom=8.5,mapbox_center = {"lat": 40.7, "lon": -73.86},
                 title=f"Número de árvores de {option} por Bairro de NY com DAP >= que {tree_dbh_slice}",)
#Guardo em uma variável
figChoroplet_Scatter = go.Figure(data=[Choroplet,Scatter],layout=layout)

#Gráfico Box-Plot de espécie
figBoxEsp = BoxPlotPlotly(df=temp_df.loc[temp_df['spc_common']==option],
                           col = 'spc_common',
                           colValue = 'tree_dbh').update_layout(margin={"r":0,"t":50,"l":0,"b":0},yaxis=dict( title='DAP (cm)'),title=f"Box-plot DAP de {option}")

####Montando as visualizações dos gráficos de  Box-Plot e Choroplet de espécie
col3_1, col3_2 = st.columns([4, 1])

with col3_1:
    st.plotly_chart(figChoroplet_Scatter, use_container_width=True)
with col3_2:
    st.plotly_chart(figBoxEsp, use_container_width=True)

Com isso temos algo aproximadamente assim;


Fazendo o Deploy

Primeiro você precisa montar um arquivo 'requirements.txt' e colocar dentro da pasta do projeto, nele deve conter o nome de todas as bibliotecas não nativas, para o Streamlit entender que precisa delas para conseguir funcionar. No nosso caso esse arquivo é algo simples como;

pandas
plotly

O Streamlit pega o código do GitHub para executar, nesse caso você precisa criar um repositório para guardar os arquivos do seu projeto (aqui estou assumindo que você já tenha uma conta criada). Se você seguir as imagens abaixo creio que não terá problemas com isso.



Subindo dados em um repositório do GitHub pelo navegador
Subindo dados em um repositório do GitHub pelo navegador

Abaixo você pode ver os arquivos desse projeto que estão disponíveis para serem acessas pelo Streamlit. Você pode acessar o repositório neste link.


Dentro do site do Stremlit você pode fazer um login direto com sua conta do GitHub, com isso ele já vai ir identificando os repositórios.




Após clicar em 'New App' é só definir qual é o repositório do seu projeto e qual é o arquivo Python que vai inicia-lo. É interessante ir nas configurações avançadas e configurar a versão do Python para a mesma do seu projeto. Agora é só seguir com o deploy.


Depois de alguns instantes você terá um link na web com seu Dashboad. Abaixo você consegue ver o Dashboad montado para esse post. Você pode acessar ele usando esse link.


Conclusões e observações

O Streamlit pode ser uma das maneira mais rápidas de conseguir compartilhar as analises de um Jupyter Notebook em poucos minutos e em alguns casos de forma gratuita. Particularmente dá muito bem de considera-lo para entrega de POC's (Provas de Conceito) e MVP's (Produto Mínimo Viável), considerando que você pode conectar ele com banco de dados e até fazer deploy de modelos como de detecção de anomalias que fizemos em alguns posts anteriores (Confira essa postagem aqui, ela é bem legal).

Meu objetivo aqui foi só uma introdução, se quiser mais exemplos de aplicações usando o Streamlit acesse a Galeria deles aqui. Um dos meu projetos favoritos é esse que se integra com o Google Earth Engine produzido pelo Qiusheng Wu, ele é um máximo e sugiro acompanhar.


Referências


Repositório do post


Dashbord online montado nesse post


Tree Census Visualizations in plotly


Tree Census in New York City - Dataset


Streamlit: compartilhando sua aplicação de dados sem dor de cabeça


Documentação do Streamlit


Galeria do Stramlit




184 visualizações0 comentário