Spaces:
Sleeping
Sleeping
| ####################################################### | |
| # 1. Getting setup -- using our HF template | |
| ####################################################### | |
| # We have a few options for how to proceed. I'll start by showing the process in | |
| # PL and then I'll move to my local installation of my template so that I can make | |
| # sure I am pushing code at various intervals so folks can check that out. | |
| # NOTE: during this process, you can click on "Always Rerun" for automatic updates. | |
| # See the class notes on this with some photos for reference! | |
| # **this has to be implemented!** | |
| ################################################################### | |
| # 2. Review of where we got to last time, in template app.py file | |
| ################################################################### | |
| # Let's start by copying things we did last time | |
| import streamlit as st | |
| import altair as alt | |
| # Let's recall a plot that we made with Altair in Jupyterlab: | |
| # Make sure we copy the URL as well! | |
| mobility_url = 'https://raw.githubusercontent.com/UIUC-iSchool-DataViz/is445_data/main/mobility.csv' | |
| st.title('This is my fancy app for HuggingFace!!') | |
| scatters = alt.Chart(mobility_url).mark_point().encode( | |
| x='Mobility:Q', # "Q for quantiative" | |
| #y='Population:Q', | |
| y=alt.Y('Population:Q', scale=alt.Scale(type='log')), | |
| color=alt.Color('Income:Q', scale=alt.Scale(scheme='sinebow'),bin=alt.Bin(maxbins=5)) | |
| ) | |
| st.header('More complex Dashboards') | |
| brush = alt.selection_interval(encodings=['x','y']) | |
| chart1 = alt.Chart(mobility_url).mark_rect().encode( | |
| alt.X("Student_teacher_ratio:Q", bin=alt.Bin(maxbins=10)), | |
| alt.Y("State:O"), | |
| alt.Color("count()") | |
| ).properties( | |
| height=400 | |
| ).add_params( | |
| brush | |
| ) | |
| chart2 = alt.Chart(mobility_url).mark_bar().encode( | |
| alt.X("Mobility:Q", bin=True,axis=alt.Axis(title='Mobility Score')), | |
| alt.Y('count()', axis=alt.Axis(title='Mobility Score Distribution')) | |
| ).transform_filter( | |
| brush | |
| ) | |
| chart = (chart1.properties(width=300) | chart2.properties(width=300)) | |
| tab1, tab2 = st.tabs(["Mobility interactive", "Scatter plot"]) | |
| with tab1: | |
| st.altair_chart(chart, theme=None, use_container_width=True) | |
| with tab2: | |
| st.altair_chart(scatters, theme=None, use_container_width=True) | |
| ################################################ | |
| # 3. Adding features, Pushing to HF | |
| ################################################ | |
| st.header('Requirements, README file, Pushing to HuggingFace') | |
| ### 3.1 Make a plot ### | |
| # Let's say we want to add in some matplotlib plots from some data we read | |
| # in with Pandas. | |
| import pandas as pd | |
| df = pd.read_csv(mobility_url) | |
| # There are a few ways to show the dataframe if we want our viewer to see the table: | |
| #df | |
| st.write(df) | |
| # Now, let's plot with matplotlib: | |
| import matplotlib.pyplot as plt | |
| fig, ax = plt.subplots() | |
| df['Seg_income'].plot(kind='hist', ax=ax) | |
| #plt.show() # but wait! this doesn't work! | |
| # We need to use the streamlit-specific way of showing matplotlib plots: https://docs.streamlit.io/develop/api-reference/charts/st.pyplot | |
| st.pyplot(fig) | |
| ### 3.2 Push these changes to HF -- requirements.txt ### | |
| # In order to push these changes to HF and have things actually show up we need to | |
| # add the packages we've added to our requirements.txt file. | |
| st.write('''The requirements.txt file contains all the packages needed | |
| for our app to run. These include (for our application):''') | |
| st.code(''' | |
| streamlit==1.39.0 | |
| altair | |
| numpy | |
| pandas | |
| matplotlib | |
| ''') | |
| # NOTE: for any package you want to use in your app.py file, you must include it in | |
| # the requirements.txt file! | |
| # Note #2: we specified a version of streamlit so we can use some specific widgets | |
| ### 3.3 Push these changes to HF -- README.md ### | |
| # While we're doing this, let's also take a look at the README.md file! | |
| st.header('Build in HF: README.md & requirements.txt files') | |
| st.code(''' | |
| --- | |
| title: Prep notebook -- My Streamlit App | |
| emoji: 🏢 | |
| colorFrom: blue | |
| colorTo: gray | |
| sdk: streamlit | |
| sdk_version: 1.39.0 | |
| app_file: app.py | |
| pinned: false | |
| license: mit | |
| --- | |
| ''') | |
| st.write("Note: the sdk version has to match what is in your requirements.txt (and with whatever widgets you want to be able to use).") | |
| # Some important things to note here: | |
| st.write('Some important items to note about these:') | |
| st.markdown(''' | |
| * the "emoji" is what will show up as an identifier on your homepage | |
| * the sdk *must* be streamlit | |
| * the "app_file" *must* link to the app file you are developing in | |
| ''') | |
| ################################################ | |
| # 4. TODO Quick intro to widgets | |
| ################################################ | |
| st.header('Widgets in Streamlit apps') | |
| ### 4.1 Widget basics: A few widget examples ### | |
| st.markdown(""" | |
| These will be very similar to how we used the `ipywidgets` package in Jupyter notebooks. | |
| """) | |
| st.markdown(""" | |
| We won't go over all of them, but you can check out the [list of widgets](https://docs.streamlit.io/develop/api-reference/widgets) | |
| linked. | |
| """) | |
| st.markdown("""Let's try a few!""") | |
| st.subheader('Feedback Widget') | |
| st.markdown(""" | |
| For example, we could try the [feedback widget](https://docs.streamlit.io/develop/api-reference/widgets/st.feedback). | |
| """ | |
| ) | |
| st.markdown(""" | |
| If we check out the docs for this widget, we see some familiar looking functions like | |
| `on_change` and the example they give looks very similar to an | |
| "observation" function that we built before using widgets: | |
| """) | |
| st.code( | |
| """ | |
| sentiment_mapping = ["one", "two", "three", "four", "five"] | |
| selected = st.feedback("stars") | |
| if selected is not None: | |
| st.markdown(f"You selected {sentiment_mapping[selected]} star(s).") | |
| """) | |
| # Let's give this a shot! | |
| st.write("How great are you feeling right now?") | |
| sentiment_mapping = ["one", "two", "three", "four", "five"] # map to these numers | |
| selected = st.feedback("stars") | |
| if selected is not None: # make sure we have a selection | |
| st.markdown(f"You selected {sentiment_mapping[selected]} star(s).") | |
| if selected < 1: | |
| st.markdown('Sorry to hear you are so sad :(') | |
| elif selected < 3: | |
| st.markdown('A solid medium is great!') | |
| else: | |
| st.markdown('Fantastic you are having such a great day!') | |
| st.subheader('Radio Buttons') | |
| st.markdown(""" | |
| Let's try out a [radio button](https://docs.streamlit.io/develop/api-reference/widgets/st.radio) example. | |
| """) | |
| favoriteViz = st.radio( | |
| "What's your visualization tool so far?", | |
| [":rainbow[Streamlit]", "vega-lite :sparkles:", "matplotlib :material/Home:"], | |
| captions=[ | |
| "New and cool!", | |
| "So sparkly.", | |
| "Familiar and comforting.", | |
| ], | |
| ) | |
| if favoriteViz == ":rainbow[Streamlit]": | |
| st.write("You selected Streamlit!") | |
| else: | |
| st.write("You didn't select Streamlit but that's ok, Data Viz still likes you :grin:") | |
| st.markdown(""" | |
| Note here that we made use of text highlight [colors](https://docs.streamlit.io/develop/api-reference/text/st.markdown) | |
| and [emoji's](https://streamlit-emoji-shortcodes-streamlit-app-gwckff.streamlit.app/) | |
| and [icons](https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Rounded). | |
| """) | |
| ### 4.2 Connecting widgets with plots ### | |
| st.subheader('Connecting Widgets and Plots') | |
| st.markdown(""" | |
| There are actually [many types of charts](https://docs.streamlit.io/develop/api-reference/charts) | |
| supported in Streamlit (including the Streamlit-based "Simple Charts"), | |
| though we will just mainly be focusing on [Altair-related](https://docs.streamlit.io/develop/api-reference/charts/st.altair_chart) plots | |
| and their interactivity options since we'll also be making use of these when | |
| we move to building Jekyll webpages. | |
| """) | |
| st.markdown("""Since `matplotlib` is relatively familiar though, let's do a quick | |
| example using `pandas` and `matplotlib` to plot as | |
| Streamlit [does support `matplotlib`](https://docs.streamlit.io/develop/api-reference/charts/st.pyplot) | |
| as a plotting engine. """) | |
| st.markdown("""First, let's just make a simple plot with `pandas` and `matplotlib`. | |
| Let's re-do the matplotlib plots we did before with the mobility dataset | |
| with some interactivity. """) | |
| import pandas as pd | |
| import numpy as np | |
| # first, let's make a static plot: | |
| st.write("We'll start with a static plot:") | |
| # read in dataset | |
| df = pd.read_csv("https://raw.githubusercontent.com/UIUC-iSchool-DataViz/is445_data/main/mobility.csv") | |
| # make bins along student-teacher ratio | |
| bins = np.linspace(df['Student_teacher_ratio'].min(),df['Student_teacher_ratio'].max(), 10) | |
| # make pivot table | |
| table = df.pivot_table(index='State', columns=pd.cut(df['Student_teacher_ratio'], bins), aggfunc='size') | |
| # our plotting code before was: | |
| st.code(""" | |
| import matplotlib.pyplot as plt | |
| fig,ax = plt.subplots(figsize=(10,8)) | |
| ax.imshow(table.values, cmap='hot', interpolation='nearest') | |
| ax.set_yticks(range(len(table.index))) | |
| ax.set_yticklabels(table.index) | |
| plt.show() | |
| """) | |
| st.write("Let's translate it into something that will work with Streamlit:") | |
| import matplotlib.pyplot as plt | |
| fig,ax = plt.subplots() # this changed | |
| ax.imshow(table.values, cmap='hot', interpolation='nearest') | |
| ax.set_yticks(range(len(table.index))) | |
| ax.set_yticklabels(table.index) | |
| st.pyplot(fig) # this is different | |
| st.markdown("""But this is too big! The trick is that we can save this as a buffer: """) | |
| from io import BytesIO | |
| fig,ax = plt.subplots(figsize=(4,8)) # this changed | |
| ax.imshow(table.values, cmap='hot', interpolation='nearest') | |
| ax.set_yticks(range(len(table.index))) | |
| ax.set_yticklabels(table.index) | |
| buf = BytesIO() | |
| fig.tight_layout() | |
| fig.savefig(buf, format="png") | |
| st.image(buf, width = 500) # can mess around with width, figsize/etc | |
| st.write("Now, let's make this interactive.") | |
| st.markdown("""We'll first use the [multiselect](https://docs.streamlit.io/develop/api-reference/widgets/st.multiselect) | |
| tool in order to allow for multiple state selection. """) | |
| # vertical alignment so they end up side by side | |
| fig_col, controls_col = st.columns([2,1], vertical_alignment='center') | |
| # multi-select | |
| states_selected = controls_col.multiselect('Which states do you want to view?', table.index.values) | |
| if len(states_selected) > 0: | |
| df_subset = df[df['State'].isin(states_selected)] # changed | |
| # make pivot table -- changed | |
| table_sub = df_subset.pivot_table(index='State', | |
| columns=pd.cut(df_subset['Student_teacher_ratio'], bins), | |
| aggfunc='size') | |
| base_size = 4 | |
| # this resizing doesn't 100% work great | |
| #factor = len(table.index)*1.0/df['State'].nunique() | |
| #if factor == 0: factor = 1 # for non-selections | |
| #fig,ax = plt.subplots(figsize=(base_size,2*base_size*factor)) # this changed too for different size | |
| fig,ax = plt.subplots(figsize=(base_size,2*base_size)) # this changed too for different size | |
| # extent is (xmin, xmax, ymax (buttom), ymin (top)) | |
| extent = [bins.min(), bins.max(), 0, len(table_sub.index)] | |
| ax.imshow(table_sub.values, cmap='hot', interpolation='nearest', | |
| extent=extent) | |
| ax.set_yticks(range(len(table_sub.index))) | |
| ax.set_yticklabels(table_sub.index) | |
| #ax.set_xticklabels(bins) | |
| buf = BytesIO() | |
| fig.tight_layout() | |
| fig.savefig(buf, format="png") | |
| fig_col.image(buf, width = 400) # changed here to fit better | |
| else: | |
| fig,ax = plt.subplots(figsize=(4,8)) # this changed | |
| extent = [bins.min(), bins.max(), 0, len(table.index)] | |
| ax.imshow(table.values, cmap='hot', interpolation='nearest', extent=extent) | |
| ax.set_yticks(range(len(table.index))) | |
| ax.set_yticklabels(table.index) | |
| #ax.set_xticklabels(bins) | |
| buf = BytesIO() | |
| fig.tight_layout() | |
| fig.savefig(buf, format="png") | |
| fig_col.image(buf, width = 500) # can mess around with width, figsize/etc | |
| st.markdown(""" | |
| Now let's add more in by including a [range slider](https://docs.streamlit.io/develop/api-reference/widgets/st.slider) | |
| widget. | |
| """) | |
| # vertical alignment so they end up side by side | |
| fig_col2, controls_col2 = st.columns([2,1], vertical_alignment='center') | |
| # multi-select | |
| states_selected2 = controls_col2.multiselect('Which states do you want to view?', | |
| table.index.values, key='unik1155') | |
| # had to pass unique key to have double widgets with same value | |
| # range slider -- added | |
| student_teacher_ratio_range = controls_col2.slider("Range of student teacher ratio:", | |
| df['Student_teacher_ratio'].min(), | |
| df['Student_teacher_ratio'].max(), | |
| (0.25*df['Student_teacher_ratio'].mean(), | |
| 0.75*df['Student_teacher_ratio'].mean())) | |
| # note all the "2's" here, probably will just update the original one | |
| if len(states_selected2) > 0: # here we set a default value for the slider, so no need to have a tag | |
| min_range = student_teacher_ratio_range[0] # added | |
| max_range = student_teacher_ratio_range[1] # added | |
| df_subset2 = df[(df['State'].isin(states_selected2)) & (df['Student_teacher_ratio'] >= min_range) & (df['Student_teacher_ratio']<=max_range)] # changed | |
| # just 10 bins over the full range --> changed | |
| bins2 = 10 #np.linspace(df['Student_teacher_ratio'].min(),df['Student_teacher_ratio'].max(), 10) | |
| # make pivot table -- changed | |
| table_sub2 = df_subset2.pivot_table(index='State', | |
| columns=pd.cut(df_subset2['Student_teacher_ratio'], bins2), | |
| aggfunc='size') | |
| base_size = 4 | |
| fig2,ax2 = plt.subplots(figsize=(base_size,2*base_size)) # this changed too for different size | |
| extent2 = [df_subset2['Student_teacher_ratio'].min(), | |
| df_subset2['Student_teacher_ratio'].max(), | |
| 0, len(table_sub2.index)] | |
| ax2.imshow(table_sub2.values, cmap='hot', interpolation='nearest', extent=extent2) | |
| ax2.set_yticks(range(len(table_sub2.index))) | |
| ax2.set_yticklabels(table_sub2.index) | |
| #ax2.set_xticklabels() | |
| buf2 = BytesIO() | |
| fig2.tight_layout() | |
| fig2.savefig(buf2, format="png") | |
| fig_col2.image(buf2, width = 400) # changed here to fit better | |
| else: | |
| min_range = student_teacher_ratio_range[0] # added | |
| max_range = student_teacher_ratio_range[1] # added | |
| df_subset2 = df[(df['Student_teacher_ratio'] >= min_range) & (df['Student_teacher_ratio']<=max_range)] # changed | |
| # just 10 bins over the full range --> changed | |
| bins2 = 10 #np.linspace(df['Student_teacher_ratio'].min(),df['Student_teacher_ratio'].max(), 10) | |
| # make pivot table -- changed | |
| table_sub2 = df_subset2.pivot_table(index='State', | |
| columns=pd.cut(df_subset2['Student_teacher_ratio'], bins2), | |
| aggfunc='size') | |
| base_size = 4 | |
| fig2,ax2 = plt.subplots(figsize=(base_size,2*base_size)) # this changed too for different size | |
| extent2 = [df_subset2['Student_teacher_ratio'].min(), | |
| df_subset2['Student_teacher_ratio'].max(), | |
| 0, len(table_sub2.index)] | |
| ax2.imshow(table_sub2.values, cmap='hot', interpolation='nearest', extent=extent2) | |
| ax2.set_yticks(range(len(table_sub2.index))) | |
| ax2.set_yticklabels(table_sub2.index) | |
| #ax2.set_xticklabels() | |
| buf2 = BytesIO() | |
| fig2.tight_layout() | |
| fig2.savefig(buf2, format="png") | |
| fig_col2.image(buf2, width = 400) # changed here to fit better | |
| st.header('Push final page to HF') | |
| st.markdown("""When ready, do:""") | |
| st.code(""" | |
| git add -A | |
| git commit -m "final push of day 1" | |
| git push | |
| """) |