使用Streamlit快速搭建數據科學Web App


 

 
 

Streamlit 是 Python 的一個用於創建 Web App 的框架,類似於 R 的 Shiny。其功能雖不如 R Shiny 強大,更不如專門的 Python Web 框架如 Flask、Djiango 等強大,但其使用非常直觀和簡單,不需要過多的 Web 知識。如果之前用過 Markdown 或 Jupyter Notebook 等,便能快速上手,創建自己的 Web App。

Streamlit 的使用場景,如:

  • 研究開發了一個臨床預測模型,使用 Streamlit 將該預測模型搭建在網站上隨論文發表或供別人體檢。
  • 公司某部門人員需要批量處理 Excel 文件,因而可以寫一個 python 腳本自動化處理;但部門人員並不會用 python、也未安裝 python 環境,此時便可將該腳本使用 Streamlit 部署成 Web App,部門人員使用時只需點擊上傳 Excel 文件,然后點擊下載處理完成的文件即可。

Streamlit 安裝

pip install streamlit

Streamlit 自帶示例 App

命令行中運行:streamlit hello,便可創建一個本地的 Web App,點擊 URL 地址(http://localhost:8502)在瀏覽器打開。

可以看到幾個 App 的 Demo,可分別點擊查看:

創建自己的 App

Streamlit 寫 App 就是創建一個 py 文件,而不需要分別創建前端和后端。一個示例 py 文件(example.py)如下:

import streamlit as st
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 展示文本;文本直接使用Markdown語法
st.markdown("# Streamlit示例")
st.markdown("""
            - 這是
            - 一個
            - 無序列表
            """)

# 展示pandas數據框
st.dataframe(pd.DataFrame([[1, 2], [3, 4]], columns=["a", "b"]))

# 展示matplotlib繪圖
arr = np.random.normal(1, 1, size=100)
plt.hist(arr, bins=20)
plt.title("matplotlib plot")
st.pyplot()

# 加入交互控件,如輸入框
number = st.number_input("Insert a number", 123)
st.write("輸入的數字是:", number)

將該 py 文件放在某文件夾下,cmd 中 cd 到該文件夾,輸入命令 streamlit run example.py,點擊出現的 URL 后可見界面:

  • 可以使用 streamlit run example.py --server.port 80 指定 App 在 80 端口運行
  • 上述方式將 App 搭建在本地機上,如果想將 App 供別人訪問,則需將 App 搭建在服務器如阿里雲服務器或者部署在托管網站如 heroku 再或者使用內網穿透軟件如 花生殼。網上不少教程,可自行查閱~

Streamlit 主要用法總結

import streamlit as st
import pandas as pd
import numpy as np
import time
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.figure_factory as ff
import altair as alt
from PIL import Image
import base64

文字、公式

對照輸出查看顯示效果

st.title("st.title")
st.header("st.header")
st.subheader("st.subheader")

st.markdown("# markdown一級標題")
st.markdown("## markdown二級標題")
st.markdown("### markdown三級標題")

# ------------------------------------

st.write("st.write")
st.markdown("markdown普通文字")
st.text("st.text")

# ------------------------------------

st.markdown("""
Markdown列表:
- hello 
- world
- China
""")

st.markdown("***markdown粗斜體***")

# ------------------------------------

st.latex("\sum_{i=1}^{n}")

# ------------------------------------

st.write(1234)
st.write("1234")
st.write("1 + 1 = ", 2)

st.write()

st.write () 是一個泛型函數,根據傳入對象不同采取不同的展示方式,比如傳入 pandas.DataFrame 時,st.write (df) 默認調用 st.dataframe (),傳入 markdown 時,st.write (markdown) 默認調用 st.markdown ();跟 R 的泛型函數非常類似。可傳入的對象有:

  • write(data_frame) : Displays the DataFrame as a table.
  • write(func) : Displays information about a function.
  • write(module) : Displays information about the module.
  • write(dict) : Displays dict in an interactive widget.
  • write(obj) : The default is to print str(obj).
  • write(mpl_fig) : Displays a Matplotlib figure.
  • write(altair) : Displays an Altair chart.
  • write(keras) : Displays a Keras model.
  • write(graphviz) : Displays a Graphviz graph.
  • write(plotly_fig) : Displays a Plotly figure.
  • write(bokeh_fig) : Displays a Bokeh figure.
  • write(sympy_expr) : Prints SymPy expression using LaTeX.
  • write(markdown):

比如:

# 字典
st.write({"a": [1, 2, 3], 
          "b": [2, 3, 4]})

# pandas數據框
st.write(pd.DataFrame({
    "a": [1, 2, 3, 4, 5],
    "b": [4, 5, 6, 7, 8]
}))

# Markdown文字
st.write("Hello, *World!* :sunglasses:")

# 繪圖對象
df = pd.DataFrame(
    np.random.randn(200, 3),
    columns=["a", "b", "c"]
)

c = alt.Chart(df).mark_circle().encode(
    x="a", y="b", size="c", color="c", tooltip=["a", "b", "c"])
st.write(c)

表格

兩種表格,動態表格和靜態表格

# 動態表格(表格太大時只展示一部分,可滑動表格下方滑動條查看不同部分)
# st.write默認調用st.dataframe()
df = pd.DataFrame(
    np.random.randn(10, 20),
    columns=('col %d' % i for i in range(20)))

st.write(df)
# 靜態表格(展示表格全部內容,太大時滑動App界面下方的滑動條查看不同部分)
df = pd.DataFrame({
    "a": [1, 2, 3, 4, 5],
    "b": [4, 5, 6, 7, 8]
})

st.table(df)

pandas.DataFrame 的 style 也可正常顯示

df = pd.DataFrame(
    np.random.randn(10, 20),
    columns=('col %d' % i for i in range(20)))

st.dataframe(df.style.highlight_max(axis=0))

<streamlit.DeltaGenerator.DeltaGenerator at 0x28b60f62048>

Code

僅展示 Code,Code 不執行

code = """
def sum_(x):
    return np.sum(x)
"""
st.code(code, language="python")

code = """
for (i i 1:10) {
    print(i)
}
"""
st.code(code, language="r")

st.markdown("""
​```python
print("hello")
""")

展示 Code,同時執行 Code;需要將 code 放入 st.echo() 內

with st.echo():
    for i in range(5):
        st.write("hello")

各種控件

# 數字輸入框
number = st.number_input("Insert a number", 123)

# 單行文本輸入框
word = st.text_input("Insert a word", "123")
st.write("The number is", number, "The word is", word)

# 多行文本輸入框
st.text_area("Text to analyze", "I love China")

# 日期輸入框
st.date_input("Insert a date")

# 時間輸入框
st.time_input("Insert a time")

# 點擊按鈕
number = st.button("click it")
st.write("返回值:", number)

# 滑動條
x = st.slider("Square", min_value=0, max_value=80)
st.write(x, "squared is", np.power(x, 2))

# 檢查框
res = st.checkbox("I agree")
st.write(res)

# 單選框
st.selectbox("Which would you like", [1, 2, 3])

# 單選按鈕
st.radio("Which would you like", [1, 2, 3])

# 多選框
selector = st.multiselect("Which would you like", [1, 2, 3])
st.write(selector)
# 氣球效果
st.balloons()

上傳 csv 文件

uploaded_file = st.file_uploader("Choose a CSV file", type="csv")

if uploaded_file is not None:
    data = pd.read_csv(uploaded_file)
    st.write(data)

下載:目前 Streamlit 還沒有專門的下載控件,下載 pandas Dataframe 為 csv 文件可通過以下方式實現
點擊 Download CSV File 便可下載文件

data = [(1, 2, 3)]
df = pd.DataFrame(data, columns=["Col1", "Col2", "Col3"])
csv = df.to_csv(index=False)

b64 = base64.b64encode(csv.encode()).decode()
href = f'<a href="data:file/csv;base64,{b64}">Download CSV File</a> (right-click and save as &lt;some_name&gt;.csv)'
st.markdown(href, unsafe_allow_html=True)

側邊欄控件

以上控件大部分都有對應的側邊欄形式,如上述 st.selectbox 若想放置在側邊欄,可使用 st.sidebar.selectbox

# 單選框
st.sidebar.selectbox("Which would you like", [1, 2, 3], key="1")

# 單選按鈕
st.sidebar.radio("Which would you like", [1, 2, 3], key="1")

# 多選框
selector = st.sidebar.multiselect("Which would you like", [1, 2, 3], key="3")
st.write(selector)

繪圖、圖片、音頻、視頻

支持的繪圖庫:

  • streamlit 自帶繪圖:st.line_chart()bar_chart() 等
  • matplotlib 或 seaborn:st.pyplot()
  • altair:st.altair_chart()
  • vega: st.vega_lite_chart()
  • plotly: st.plotly_chart()
  • bokeh: st.bokeh_chart()

streamlit 自帶繪圖

st.line_chart() 和 st.bar_chart()

st.line_chart(np.random.randn(10, 2))

chart_data = pd.DataFrame(
    np.random.randn(50, 3),
    columns=["a", "b", "c"]
)
st.bar_chart(chart_data)

matplotlib 或 seaborn 繪圖

st.markdown("# matplotlib繪圖")

arr = np.random.normal(1, 1, size=100)
plt.hist(arr, bins=20)
st.pyplot()

st.markdown("# seaborn繪圖")

tips = sns.load_dataset("tips")
sns.set(style="darkgrid")
sns.scatterplot(x="total_bill", y="tip", hue="smoker", data=tips)
st.pyplot()

plotly 繪圖

x1 = np.random.randn(200) - 2
x2 = np.random.randn(200)
x3 = np.random.randn(200) + 2

hist_data = [x1, x2, x3]
group_labels = ["Group 1", "Group 2", "Group 3"]
fig = ff.create_distplot(
    hist_data, group_labels, 
    bin_size=[0.1, 0.25, 0.5])

st.markdown("# plotly繪圖")
st.plotly_chart(fig)

地圖

# 繪制1000個點的坐標
map_data = pd.DataFrame(
    np.random.randn(1000, 2) / [50, 50] + [37.76, -122.4],
    columns=['lat', 'lon'])

st.map(map_data)

展示圖片、音頻、視頻

  • 圖片:st.image ()
  • 音頻:st.audio ()
  • 視頻:st.video ()

狀態

st.error("錯誤顯示為")
st.warning("警告顯示為")
st.info("普通消息顯示為")
st.success("成功消息顯示為")

展示進度

占位符

占位符可在后續向其中添加內容

# 添加兩個占位符
slot1 = st.empty()
slot2 = st.empty()

# 占位符中插入文字
time.sleep(0.5)
slot1.markdown("# This will appear")

# 占位符中畫圖
time.sleep(0.5)
slot2.line_chart(np.random.randn(20, 2))

進度條

# 添加占位符
placeholder = st.empty()
# 創建進度條
bar = st.progress(0)

for i in range(100):
    time.sleep(0.05)
    # 不斷更新占位符的內容
    placeholder.text(f"Iteration {i+1}")
    # 不斷更新進度條
    bar.progress(i + 1)

# 狀態
st.success("Finished")

等待條

with st.spinner("Wait for it..."): 
    for i in range(100):
        print("hello")
        time.sleep(0.05)

st.success("Done!")

動態擴增表格或動態繪圖

表格、圖片可以使用 add_rows() 方法添加新數據

動態擴增表格

df1 = pd.DataFrame(
    np.random.randn(5, 5),
    columns=("col %d" % i for i in range(5))
)
tb_table = st.table(df1)

for i in range(10):
    df2 = pd.DataFrame(
        np.random.randn(1, 5),
        columns=("col %d" % i for i in range(5))
    )
    tb_table.add_rows(df2)
    time.sleep(0.5)

動態折線圖

pb = st.progress(0)
status_txt = st.empty()
chart = st.line_chart(np.random.randn(10, 2))

for i in range(100):
    pb.progress(i)
    new_rows = np.random.randn(10, 2)
    status_txt.text(
        "The latest number is: %s" % new_rows[-1, 1]
    )
    chart.add_rows(new_rows)
    time.sleep(0.05)

緩存

對於費時間的步驟,可打包成函數,並使用緩存;第一次調用函數時,正常運行過程;第二次調用函數時,則不再重新計算,而是直接使用上一步調用函數時返回的結果。

@st.cache() # 使用緩存
def compute_long_time():
    SUM = 0
    for i in range(100):
        SUM += i
        time.sleep(0.05)
    return SUM

第一次調用函數,費時較長

st.write(compute_long_time())

再次調用該函數,瞬間出結果

st.write(compute_long_time())

Magic commands

Streamlit 提供了魔法方法,對於某些內容,直接書寫便會自動調用 st.write ()

"***hello world***"

"""
# This is the document title

This is some _markdown_.
"""

# ---------------------------------

st.write("***hello world***")

st.write("""
# This is the document title

This is some _markdown_.
""")

# ---------------------------------

st.markdown("***hello world***")

st.markdown("""
# This is the document title

This is some _markdown_.
""")

可以看到,以上三種書寫展示結果相同,原因為第一種方式會自動調用 st.write()(魔法方法),而 st.write() 為一個泛型函數,內部傳入 Markdown 時會自動調用 st.markdown(),因此三種方式歸根結底都是使用了 st.markdown()

展示 df 和 x,兩者效果相同

df = pd.DataFrame({"col1": [1, 2, 3]})
df

x = 10
"x", x

# ---------------------------------

df = pd.DataFrame({"col1": [1, 2, 3]})
st.write(df) # 默認調用st.dataframe()

x = 10
st.write("x", x)

其他可參考的資料

Streamlit 官方教程:有大量示例,非常好懂

awesome-streamlit:Streamlit 各類資源,App 示例、教程、展示某些功能如何實現等

編輯於 2020-07-27
 
原文鏈接:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM