Skip to main content

pumpwood-streamlit-pkg

Python Package to deploy Streamlit Dashboards at Pumpwood Systems.


Pumpwood is a native brasilian tree which has a symbiotic relation with ants (Murabei)

This packages help to deploy Streamlit Dashboard associated with Pumpwood based Systems registering routes at Kong for single end-point for all microservice.

It implements an abstract class that should be inherited. When rendering the dashboard, it uses cookie PumpwoodAuthorization to get authentication token and validates if user is logged at Pumpwood Auth microservice. If authentication token is not valid, authentication_error_page is called to render unauthorized page.

Documentation Page

You can check the doc page here.

Example

Example using dashboard example made avaiable by Streamlit oficial website.

import os
import streamlit as st
import pandas as pd
import altair as alt
import plotly.express as px
from pumpwood_streamlit.dashboard import PumpwoodStreamlitDashboard


class Dashboard(PumpwoodStreamlitDashboard):
    def set_page_config(self):
        #######################
        # Page configuration
        st.set_page_config(
            page_title="US Population Dashboard",
            page_icon="🏂",
            layout="wide",
            initial_sidebar_state="expanded")

    def main_view(self):
        alt.themes.enable("dark")

        #######################
        # Load data
        file_path = os.path.join(
            os.path.dirname(__file__),
            'data/us-population-2010-2019-reshaped.csv')
        df_reshaped = pd.read_csv(file_path)


        #######################
        # Sidebar
        with st.sidebar:
            st.title('🏂 US Population Dashboard')

            year_list = list(df_reshaped.year.unique())[::-1]

            selected_year = st.selectbox('Select a year', year_list)
            df_selected_year = df_reshaped[df_reshaped.year == selected_year]
            df_selected_year_sorted = df_selected_year.sort_values(by="population", ascending=False)

            color_theme_list = ['blues', 'cividis', 'greens', 'inferno', 'magma', 'plasma', 'reds', 'rainbow', 'turbo', 'viridis']
            selected_color_theme = st.selectbox('Select a color theme', color_theme_list)


        #######################
        # Plots

        # Heatmap
        def make_heatmap(input_df, input_y, input_x, input_color, input_color_theme):
            heatmap = alt.Chart(input_df).mark_rect().encode(
                    y=alt.Y(f'{input_y}:O', axis=alt.Axis(title="Year", titleFontSize=18, titlePadding=15, titleFontWeight=900, labelAngle=0)),
                    x=alt.X(f'{input_x}:O', axis=alt.Axis(title="", titleFontSize=18, titlePadding=15, titleFontWeight=900)),
                    color=alt.Color(f'max({input_color}):Q',
                                     legend=None,
                                     scale=alt.Scale(scheme=input_color_theme)),
                    stroke=alt.value('black'),
                    strokeWidth=alt.value(0.25),
                ).properties(width=900
                ).configure_axis(
                labelFontSize=12,
                titleFontSize=12
                )
            # height=300
            return heatmap

        # Choropleth map
        def make_choropleth(input_df, input_id, input_column, input_color_theme):
            choropleth = px.choropleth(input_df, locations=input_id, color=input_column, locationmode="USA-states",
                                       color_continuous_scale=input_color_theme,
                                       range_color=(0, max(df_selected_year.population)),
                                       scope="usa",
                                       labels={'population':'Population'}
                                      )
            choropleth.update_layout(
                template='plotly_dark',
                plot_bgcolor='rgba(0, 0, 0, 0)',
                paper_bgcolor='rgba(0, 0, 0, 0)',
                margin=dict(l=0, r=0, t=0, b=0),
                height=350
            )
            return choropleth


        # Donut chart
        def make_donut(input_response, input_text, input_color):
          if input_color == 'blue':
              chart_color = ['#29b5e8', '#155F7A']
          if input_color == 'green':
              chart_color = ['#27AE60', '#12783D']
          if input_color == 'orange':
              chart_color = ['#F39C12', '#875A12']
          if input_color == 'red':
              chart_color = ['#E74C3C', '#781F16']

          source = pd.DataFrame({
              "Topic": ['', input_text],
              "% value": [100-input_response, input_response]
          })
          source_bg = pd.DataFrame({
              "Topic": ['', input_text],
              "% value": [100, 0]
          })

          plot = alt.Chart(source).mark_arc(innerRadius=45, cornerRadius=25).encode(
              theta="% value",
              color= alt.Color("Topic:N",
                              scale=alt.Scale(
                                  #domain=['A', 'B'],
                                  domain=[input_text, ''],
                                  # range=['#29b5e8', '#155F7A']),  # 31333F
                                  range=chart_color),
                              legend=None),
          ).properties(width=130, height=130)

          text = plot.mark_text(align='center', color="#29b5e8", font="Lato", fontSize=32, fontWeight=700, fontStyle="italic").encode(text=alt.value(f'{input_response} %'))
          plot_bg = alt.Chart(source_bg).mark_arc(innerRadius=45, cornerRadius=20).encode(
              theta="% value",
              color= alt.Color("Topic:N",
                              scale=alt.Scale(
                                  # domain=['A', 'B'],
                                  domain=[input_text, ''],
                                  range=chart_color),  # 31333F
                              legend=None),
          ).properties(width=130, height=130)
          return plot_bg + plot + text

        # Convert population to text
        def format_number(num):
            if num > 1000000:
                if not num % 1000000:
                    return f'{num // 1000000} M'
                return f'{round(num / 1000000, 1)} M'
            return f'{num // 1000} K'

        # Calculation year-over-year population migrations
        def calculate_population_difference(input_df, input_year):
          selected_year_data = input_df[input_df['year'] == input_year].reset_index()
          previous_year_data = input_df[input_df['year'] == input_year - 1].reset_index()
          selected_year_data['population_difference'] = selected_year_data.population.sub(previous_year_data.population, fill_value=0)
          return pd.concat([selected_year_data.states, selected_year_data.id, selected_year_data.population, selected_year_data.population_difference], axis=1).sort_values(by="population_difference", ascending=False)


        #######################
        # Dashboard Main Panel
        col = st.columns((1.5, 4.5, 2), gap='medium')

        with col[0]:
            st.markdown('#### Gains/Losses')

            df_population_difference_sorted = calculate_population_difference(df_reshaped, selected_year)

            if selected_year > 2010:
                first_state_name = df_population_difference_sorted.states.iloc[0]
                first_state_population = format_number(df_population_difference_sorted.population.iloc[0])
                first_state_delta = format_number(df_population_difference_sorted.population_difference.iloc[0])
            else:
                first_state_name = '-'
                first_state_population = '-'
                first_state_delta = ''
            st.metric(label=first_state_name, value=first_state_population, delta=first_state_delta)

            if selected_year > 2010:
                last_state_name = df_population_difference_sorted.states.iloc[-1]
                last_state_population = format_number(df_population_difference_sorted.population.iloc[-1])
                last_state_delta = format_number(df_population_difference_sorted.population_difference.iloc[-1])
            else:
                last_state_name = '-'
                last_state_population = '-'
                last_state_delta = ''
            st.metric(label=last_state_name, value=last_state_population, delta=last_state_delta)


            st.markdown('#### States Migration')

            if selected_year > 2010:
                # Filter states with population difference > 50000
                # df_greater_50000 = df_population_difference_sorted[df_population_difference_sorted.population_difference_absolute > 50000]
                df_greater_50000 = df_population_difference_sorted[df_population_difference_sorted.population_difference > 50000]
                df_less_50000 = df_population_difference_sorted[df_population_difference_sorted.population_difference < -50000]

                # % of States with population difference > 50000
                states_migration_greater = round((len(df_greater_50000)/df_population_difference_sorted.states.nunique())*100)
                states_migration_less = round((len(df_less_50000)/df_population_difference_sorted.states.nunique())*100)
                donut_chart_greater = make_donut(states_migration_greater, 'Inbound Migration', 'green')
                donut_chart_less = make_donut(states_migration_less, 'Outbound Migration', 'red')
            else:
                states_migration_greater = 0
                states_migration_less = 0
                donut_chart_greater = make_donut(states_migration_greater, 'Inbound Migration', 'green')
                donut_chart_less = make_donut(states_migration_less, 'Outbound Migration', 'red')

            migrations_col = st.columns((0.2, 1, 0.2))
            with migrations_col[1]:
                st.write('Inbound')
                st.altair_chart(donut_chart_greater)
                st.write('Outbound')
                st.altair_chart(donut_chart_less)

        with col[1]:
            st.markdown('#### Total Population')

            choropleth = make_choropleth(df_selected_year, 'states_code', 'population', selected_color_theme)
            st.plotly_chart(choropleth, use_container_width=True)

            heatmap = make_heatmap(df_reshaped, 'year', 'states', 'population', selected_color_theme)
            st.altair_chart(heatmap, use_container_width=True)

        with col[2]:
            st.markdown('#### Top States')

            st.dataframe(df_selected_year_sorted,
                         column_order=("states", "population"),
                         hide_index=True,
                         width=None,
                         column_config={
                            "states": st.column_config.TextColumn(
                                "States",
                            ),
                            "population": st.column_config.ProgressColumn(
                                "Population",
                                format="%f",
                                min_value=0,
                                max_value=max(df_selected_year_sorted.population),
                             )}
                         )

            with st.expander('About', expanded=True):
                st.write('''
                    - Data: [U.S. Census Bureau](https://www.census.gov/data/datasets/time-series/demo/popest/2010s-state-total.html).
                    - :orange[**Gains/Losses**]: states with high inbound/ outbound migration for selected year
                    - :orange[**States Migration**]: percentage of states with annual inbound/ outbound migration > 50,000
                    ''')

Metadata

Release files for pumpwood-streamlit 0.18.12

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pumpwood-streamlit 0.18.12
File Size Uploaded
pumpwood_streamlit-0.18.12.tar.gz 18.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pumpwood-streamlit 0.18.12
File Interpreter ABI Platform
pumpwood_streamlit-0.18.12-py3-none-any.whl Python 3 none any Details

Total release size: 39.4 kB

Release files / pumpwood_streamlit-0.18.12.tar.gz

Download URL pumpwood_streamlit-0.18.12.tar.gz
Size 18.0 kB
Tags Source
SHA-256 checksum
How to use checksums
71126a70248ba9ec5d97d8f8ceb5eff7821055c413d683457bf91859a6c14e1f
BLAKE2b-256 checksum
How to use checksums
d02b240f28e2761c2188eaff4d46485738d41b259a169bbdeca233a1e30ba01b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.1 CPython/3.12.13 Linux/6.17.0-1022-azure

Release files / pumpwood_streamlit-0.18.12-py3-none-any.whl

Download URL pumpwood_streamlit-0.18.12-py3-none-any.whl
Size 21.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a6c271845f4e514a738ed4f3b4f124f673802622e67e4217bdc82de8f4cedb22
BLAKE2b-256 checksum
How to use checksums
67383cf5444ff0636de01b67317a4c7ed11faaf3206061cf27cf3df91cef8019
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.1 CPython/3.12.13 Linux/6.17.0-1022-azure

Release history Release notifications | RSS feed

This release

0.18.12 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page