jnaiman commited on
Commit
9052957
·
1 Parent(s): 64804fb

app files:

Browse files
Files changed (1) hide show
  1. app.py +350 -18
app.py CHANGED
@@ -3,19 +3,27 @@
3
 
4
  # day 2/3 -- "grab bag" of other things
5
  # multi-page apps? ==> maybe day 2? ==> does this work with HF apps??
6
- #* https://docs.streamlit.io/develop/tutorials/databases <- touch on but say we'll be just doing csv files
7
- # embedding streamlit spaces on other webpages? wait until Jekyll? https://huggingface.co/docs/hub/en/spaces-sdks-streamlit#embed-streamlit-spaces-on-other-webpages
8
- # how to search/duplicate other spaces on HF (make sure you cite this!)
9
 
10
- # start with "this is how we publish with streamlit" -- README file, requirements, etc
11
- # ---> make sure to mention the "yaml-ness" of the README file
12
- # ---> say that the easiest way to start is with an already hosted app on HF -- luckily we alread have a lab on this!
13
- # ---> make this like the "jekyll updates" folders that have all these prep and in class files in them
14
- # Then: more streamlit extras with all of those ones listed above
15
 
16
- ################################################
17
- # 1. Review of where we got to last time
18
- ################################################
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
 
21
  # Let's start by copying things we did last time
@@ -67,12 +75,12 @@ with tab2:
67
 
68
 
69
  ################################################
70
- # 2. Adding features, Pushing to HF
71
  ################################################
72
 
73
  st.header('Requirements, README file, Pushing to HuggingFace')
74
 
75
- ### 2.1 Make a plot ###
76
 
77
  # Let's say we want to add in some matplotlib plots from some data we read
78
  # in with Pandas.
@@ -94,21 +102,345 @@ df['Seg_income'].plot(kind='hist', ax=ax)
94
  # We need to use the streamlit-specific way of showing matplotlib plots: https://docs.streamlit.io/develop/api-reference/charts/st.pyplot
95
  st.pyplot(fig)
96
 
97
- ### 2.2 Push these changes to HF ###
98
  # In order to push these changes to HF and have things actually show up we need to
99
  # add the packages we've added to our requirements.txt file.
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  # While we're doing this, let's also take a look at the README.md file!
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  ################################################
105
- # 3. TODO Quick intro to widgets
106
  ################################################
107
 
108
- ### 3.1 A few widget examples ###
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- ### 3.2 Connecting widgets with plots ###
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
  ################################################
113
- # 4. TODO Multi-page apps (?) this might be for next week/extra
114
  ################################################
 
3
 
4
  # day 2/3 -- "grab bag" of other things
5
  # multi-page apps? ==> maybe day 2? ==> does this work with HF apps??
6
+ # Week 12 -- https://docs.streamlit.io/develop/tutorials/databases <- touch on but say we'll be just doing csv files
7
+ # Week 12 -- embedding streamlit spaces on other webpages? wait until Jekyll? https://huggingface.co/docs/hub/en/spaces-sdks-streamlit#embed-streamlit-spaces-on-other-webpages
 
8
 
 
 
 
 
 
9
 
10
+ #######################################################
11
+ # 1. Getting setup -- using our HF template
12
+ #######################################################
13
+
14
+ # We have a few options for how to proceed. I'll start by showing the process in
15
+ # PL and then I'll move to my local installation of my template so that I can make
16
+ # sure I am pushing code at various intervals so folks can check that out.
17
+
18
+ # NOTE: during this process, you can click on "Always Rerun" for automatic updates.
19
+
20
+ # See the class notes on this with some photos for reference!
21
+ # **this has to be implemented!**
22
+
23
+
24
+ ###################################################################
25
+ # 2. Review of where we got to last time, in template app.py file
26
+ ###################################################################
27
 
28
 
29
  # Let's start by copying things we did last time
 
75
 
76
 
77
  ################################################
78
+ # 3. Adding features, Pushing to HF
79
  ################################################
80
 
81
  st.header('Requirements, README file, Pushing to HuggingFace')
82
 
83
+ ### 3.1 Make a plot ###
84
 
85
  # Let's say we want to add in some matplotlib plots from some data we read
86
  # in with Pandas.
 
102
  # We need to use the streamlit-specific way of showing matplotlib plots: https://docs.streamlit.io/develop/api-reference/charts/st.pyplot
103
  st.pyplot(fig)
104
 
105
+ ### 3.2 Push these changes to HF -- requirements.txt ###
106
  # In order to push these changes to HF and have things actually show up we need to
107
  # add the packages we've added to our requirements.txt file.
108
 
109
+ st.write('''The requirements.txt file contains all the packages needed
110
+ for our app to run. These include (for our application):''')
111
+ st.code('''
112
+ streamlit
113
+ altair
114
+ numpy
115
+ pandas
116
+ matplotlib
117
+ ''')
118
+
119
+ # NOTE: for any package you want to use in your app.py file, you must include it in
120
+ # the requirements.txt file!
121
+
122
+ ### 3.3 Push these changes to HF -- README.md ###
123
+
124
  # While we're doing this, let's also take a look at the README.md file!
125
 
126
+ st.header('Build in HF: README.md & requirements.txt files')
127
+
128
+ st.code('''
129
+ ---
130
+ title: Prep notebook -- My Streamlit App
131
+ emoji: 🏢
132
+ colorFrom: blue
133
+ colorTo: gray
134
+ sdk: streamlit
135
+ sdk_version: 1.36.0
136
+ app_file: app.py
137
+ pinned: false
138
+ license: mit
139
+ ---
140
+ ''')
141
+
142
+ # Some important things to note here:
143
+
144
+ st.write('Some important items to note about these:')
145
+ st.markdown('''
146
+ * the "emoji" is what will show up as an identifier on your homepage
147
+ * the sdk *must* be streamlit
148
+ * the "app_file" *must* link to the app file you are developing in
149
+ ''')
150
 
151
  ################################################
152
+ # 4. TODO Quick intro to widgets
153
  ################################################
154
 
155
+ st.header('Widgets in Streamlit apps')
156
+
157
+ ### 4.1 Widget basics: A few widget examples ###
158
+
159
+ st.markdown("""
160
+ These will be very similar to how we used the `ipywidgets` package in Jupyter notebooks.
161
+ """)
162
+
163
+ st.markdown("""
164
+ 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)
165
+ linked.
166
+ """)
167
+
168
+ st.markdown("""Let's try a few!""")
169
+
170
+ st.subheader('Feedback Widget')
171
+
172
+ st.markdown("""
173
+ For example, we could try the [feedback widget](https://docs.streamlit.io/develop/api-reference/widgets/st.feedback).
174
+ """
175
+ )
176
+ st.markdown("""
177
+ If we check out the docs for this widget, we see some familiar looking functions like
178
+ `on_change` and the example they give looks very similar to an
179
+ "observation" function that we built before using widgets:
180
+ """)
181
+
182
+ st.code(
183
+ """
184
+ sentiment_mapping = ["one", "two", "three", "four", "five"]
185
+ selected = st.feedback("stars")
186
+ if selected is not None:
187
+ st.markdown(f"You selected {sentiment_mapping[selected]} star(s).")
188
+ """)
189
+
190
+ # Let's give this a shot!
191
+
192
+ st.write("How great are you feeling right now?")
193
+ sentiment_mapping = ["one", "two", "three", "four", "five"] # map to these numers
194
+ selected = st.feedback("stars")
195
+ if selected is not None: # make sure we have a selection
196
+ st.markdown(f"You selected {sentiment_mapping[selected]} star(s).")
197
+ if selected < 1:
198
+ st.markdown('Sorry to hear you are so sad :(')
199
+ elif selected < 3:
200
+ st.markdown('A solid medium is great!')
201
+ else:
202
+ st.markdown('Fantastic you are having such a great day!')
203
+
204
+ st.subheader('Radio Buttons')
205
+
206
+ st.markdown("""
207
+ Let's try out a [radio button](https://docs.streamlit.io/develop/api-reference/widgets/st.radio) example.
208
+ """)
209
+
210
+ favoriteViz = st.radio(
211
+ "What's your visualization tool so far?",
212
+ [":rainbow[Streamlit]", "vega-lite :sparkles:", "matplotlib :material/Home:"],
213
+ captions=[
214
+ "New and cool!",
215
+ "So sparkly.",
216
+ "Familiar and comforting.",
217
+ ],
218
+ )
219
+
220
+ if favoriteViz == ":rainbow[Streamlit]":
221
+ st.write("You selected Streamlit!")
222
+ else:
223
+ st.write("You didn't select Streamlit but that's ok, Data Viz still likes you :grin:")
224
+
225
+ st.markdown("""
226
+ Note here that we made use of text highlight [colors](https://docs.streamlit.io/develop/api-reference/text/st.markdown)
227
+ and [emoji's](https://streamlit-emoji-shortcodes-streamlit-app-gwckff.streamlit.app/)
228
+ and [icons](https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Rounded).
229
+ """)
230
+
231
+ ### 4.2 Connecting widgets with plots ###
232
+
233
+ st.subheader('Connecting Widgets and Plots')
234
+
235
+ st.markdown("""
236
+ We can also
237
+ """)
238
+
239
+ st.markdown("""
240
+ There are actually [many types of charts](https://docs.streamlit.io/develop/api-reference/charts)
241
+ supported in Streamlit (including the Streamlit-based "Simple Charts"),
242
+ though we will just mainly be focusing on [Altair-related](https://docs.streamlit.io/develop/api-reference/charts/st.altair_chart) plots
243
+ and their interactivity options since we'll also be making use of these when
244
+ we move to building Jekyll webpages.
245
+ """)
246
+
247
+ st.markdown("""Since `matplotlib` is relatively familiar though, let's do a quick
248
+ example using `pandas` and `matplotlib` to plot as
249
+ Streamlit [does support `matplotlib`](https://docs.streamlit.io/develop/api-reference/charts/st.pyplot)
250
+ as a plotting engine. """)
251
+
252
+ st.markdown("""First, let's just make a simple plot with `pandas` and `matplotlib`.
253
+ Let's re-do the matplotlib plots we did before with the mobility dataset
254
+ with some interactivity. """)
255
+
256
+ import pandas as pd
257
+ import numpy as np
258
+
259
+ # first, let's make a static plot:
260
+ st.write("We'll start with a static plot:")
261
+ # read in dataset
262
+ df = pd.read_csv("https://raw.githubusercontent.com/UIUC-iSchool-DataViz/is445_data/main/mobility.csv")
263
+
264
+ # make bins along student-teacher ratio
265
+ bins = np.linspace(df['Student_teacher_ratio'].min(),df['Student_teacher_ratio'].max(), 10)
266
+
267
+ # make pivot table
268
+ table = df.pivot_table(index='State', columns=pd.cut(df['Student_teacher_ratio'], bins), aggfunc='size')
269
+
270
+ # our plotting code before was:
271
+ st.code("""
272
+ import matplotlib.pyplot as plt
273
+
274
+ fig,ax = plt.subplots(figsize=(10,8))
275
+ ax.imshow(table.values, cmap='hot', interpolation='nearest')
276
+ ax.set_yticks(range(len(table.index)))
277
+ ax.set_yticklabels(table.index)
278
+ plt.show()
279
+ """)
280
+
281
+ st.write("Let's translate it into something that will work with Streamlit:")
282
+
283
+ import matplotlib.pyplot as plt
284
 
285
+ fig,ax = plt.subplots() # this changed
286
+ ax.imshow(table.values, cmap='hot', interpolation='nearest')
287
+ ax.set_yticks(range(len(table.index)))
288
+ ax.set_yticklabels(table.index)
289
+
290
+ st.pyplot(fig) # this is different
291
+
292
+ st.markdown("""But this is too big! The trick is that we can save this as a buffer: """)
293
+
294
+ from io import BytesIO
295
+
296
+ fig,ax = plt.subplots(figsize=(4,8)) # this changed
297
+ ax.imshow(table.values, cmap='hot', interpolation='nearest')
298
+ ax.set_yticks(range(len(table.index)))
299
+ ax.set_yticklabels(table.index)
300
+
301
+ buf = BytesIO()
302
+ fig.tight_layout()
303
+ fig.savefig(buf, format="png")
304
+ st.image(buf, width = 500) # can mess around with width, figsize/etc
305
+
306
+ st.write("Now, let's make this interactive")
307
+ st.markdown("""We'll first use the [multiselect](https://docs.streamlit.io/develop/api-reference/widgets/st.multiselect)
308
+ tool in order to allow for multiple state selection. """)
309
+
310
+ # vertical alignment so they end up side by side
311
+ fig_col, controls_col = st.columns([2,1], vertical_alignment='center')
312
+
313
+ # multi-select
314
+ states_selected = controls_col.multiselect('Which states do you want to view?', table.index.values)
315
+
316
+ if len(states_selected) > 0:
317
+ df_subset = df[df['State'].isin(states_selected)] # changed
318
+
319
+ # make pivot table -- changed
320
+ table_sub = df_subset.pivot_table(index='State',
321
+ columns=pd.cut(df_subset['Student_teacher_ratio'], bins),
322
+ aggfunc='size')
323
+
324
+ base_size = 4
325
+ # this resizing doesn't 100% work great
326
+ #factor = len(table.index)*1.0/df['State'].nunique()
327
+ #if factor == 0: factor = 1 # for non-selections
328
+ #fig,ax = plt.subplots(figsize=(base_size,2*base_size*factor)) # this changed too for different size
329
+ fig,ax = plt.subplots(figsize=(base_size,2*base_size)) # this changed too for different size
330
+ # extent is (xmin, xmax, ymax (buttom), ymin (top))
331
+ extent = [bins.min(), bins.max(), 0, len(table_sub.index)]
332
+ ax.imshow(table_sub.values, cmap='hot', interpolation='nearest',
333
+ extent=extent)
334
+ ax.set_yticks(range(len(table_sub.index)))
335
+ ax.set_yticklabels(table_sub.index)
336
+ #ax.set_xticklabels(bins)
337
+
338
+ buf = BytesIO()
339
+ fig.tight_layout()
340
+ fig.savefig(buf, format="png")
341
+ fig_col.image(buf, width = 400) # changed here to fit better
342
+ else:
343
+ fig,ax = plt.subplots(figsize=(4,8)) # this changed
344
+ extent = [bins.min(), bins.max(), 0, len(table.index)]
345
+ ax.imshow(table.values, cmap='hot', interpolation='nearest', extent=extent)
346
+ ax.set_yticks(range(len(table.index)))
347
+ ax.set_yticklabels(table.index)
348
+ #ax.set_xticklabels(bins)
349
+
350
+ buf = BytesIO()
351
+ fig.tight_layout()
352
+ fig.savefig(buf, format="png")
353
+ fig_col.image(buf, width = 500) # can mess around with width, figsize/etc
354
+
355
+
356
+ st.markdown("""
357
+ Now let's add more in by including a [range slider](https://docs.streamlit.io/develop/api-reference/widgets/st.slider)
358
+ widget.
359
+ """)
360
+
361
+ # vertical alignment so they end up side by side
362
+ fig_col2, controls_col2 = st.columns([2,1], vertical_alignment='center')
363
+
364
+ # multi-select
365
+ states_selected2 = controls_col2.multiselect('Which states do you want to view?',
366
+ table.index.values, key='unik1155')
367
+ # had to pass unique key to have double widgets with same value
368
+
369
+ # range slider -- added
370
+ student_teacher_ratio_range = controls_col2.slider("Range of student teacher ratio:",
371
+ df['Student_teacher_ratio'].min(),
372
+ df['Student_teacher_ratio'].max(),
373
+ (0.25*df['Student_teacher_ratio'].mean(),
374
+ 0.75*df['Student_teacher_ratio'].mean()))
375
+
376
+ # note all the "2's" here, probably will just update the original one
377
+ if len(states_selected2) > 0: # here we set a default value for the slider, so no need to have a tag
378
+ min_range = student_teacher_ratio_range[0] # added
379
+ max_range = student_teacher_ratio_range[1] # added
380
+
381
+ df_subset2 = df[(df['State'].isin(states_selected2)) & (df['Student_teacher_ratio'] >= min_range) & (df['Student_teacher_ratio']<=max_range)] # changed
382
+
383
+ # just 10 bins over the full range --> changed
384
+ bins2 = 10 #np.linspace(df['Student_teacher_ratio'].min(),df['Student_teacher_ratio'].max(), 10)
385
+
386
+ # make pivot table -- changed
387
+ table_sub2 = df_subset2.pivot_table(index='State',
388
+ columns=pd.cut(df_subset2['Student_teacher_ratio'], bins2),
389
+ aggfunc='size')
390
+
391
+ base_size = 4
392
+ fig2,ax2 = plt.subplots(figsize=(base_size,2*base_size)) # this changed too for different size
393
+ extent2 = [df_subset2['Student_teacher_ratio'].min(),
394
+ df_subset2['Student_teacher_ratio'].max(),
395
+ 0, len(table_sub2.index)]
396
+ ax2.imshow(table_sub2.values, cmap='hot', interpolation='nearest', extent=extent2)
397
+ ax2.set_yticks(range(len(table_sub2.index)))
398
+ ax2.set_yticklabels(table_sub2.index)
399
+ #ax2.set_xticklabels()
400
+
401
+ buf2 = BytesIO()
402
+ fig2.tight_layout()
403
+ fig2.savefig(buf2, format="png")
404
+ fig_col2.image(buf2, width = 400) # changed here to fit better
405
+ else:
406
+ fig2,ax2 = plt.subplots(figsize=(4,8)) # this changed
407
+ extent2 = [bins.min(), bins.max(), 0, len(table.index)]
408
+ ax2.imshow(table.values, cmap='hot', interpolation='nearest', extent=extent2)
409
+ ax2.set_yticks(range(len(table.index)))
410
+ ax2.set_yticklabels(table.index)
411
+
412
+ buf2 = BytesIO()
413
+ fig2.tight_layout()
414
+ fig2.savefig(buf2, format="png")
415
+ fig_col2.image(buf2, width = 500) # can mess around with width, figsize/etc
416
+
417
+ # THEN: slider for range of student teacher ratios -- do the RANGE slider: https://docs.streamlit.io/develop/api-reference/widgets/st.slider
418
+
419
+ # with st.expander('Favorite product by Gender within city'):
420
+ # column1, column2 = st.columns([3,1])
421
+
422
+ # # Allow the user to select a gender.
423
+ # selected_gender = st.radio('What is your Gender:', df.gender.unique(), index = 0)
424
+
425
+ # # Apply gender filter.
426
+ # gender_product = df[df['gender'] == selected_gender]
427
+
428
+ # # Allow the user to select a city.
429
+ # select_city = column2.selectbox('Select City', df.sort_values('City').City.unique())
430
+
431
+ # # Apply city filter
432
+ # city_gender_product = gender_product[gender_product['City'] == select_city]
433
+
434
+ # # Use the city_gender_product dataframe as it has filters for gender and city.
435
+ # fig = px.histogram(city_gender_product.sort_values('product_line') ,x='product_line', y='gross_income', color = 'product_line',)
436
+
437
+ # if selected_gender == 'Male':
438
+ # st.write('What men buy most!')
439
+ # else:
440
+ # st.write('What female buy most!')
441
+
442
+ # st.plotly_chart(fig, use_container_width=True)
443
 
444
  ################################################
445
+ # 5. TODO Multi-page apps (?) this might be for next week/extra
446
  ################################################