Data visualization framework (Phần 1): Tạo ứng dụng siêu tốc với Streamlit

Với cú pháp và kiến trúc đơn giản, Streamlit - một Python framework mã nguồn mở giúp lập trình viên nhanh chóng tạo ứng dụng data visualization hoàn chỉnh từ việc xác thực, tích hợp cho đến hiển thị dữ liệu mà không cần kiến thức sâu về lập trình web.

Một data application sẽ bao gồm 4 bước: thu thập (integration), xử lý (processing), phân tích (analysis), trực quan hóa (visualization).

Data visualization (trực quan hoá dữ liệu) là việc mô tả một lượng dữ liệu dưới dạng các hình ảnh trực quan như bảng, biểu đồ, đồ thị... Đây là bước cuối cùng, cũng là bước quan trọng nhất vì người dùng sẽ tương tác trực tiếp với hệ thống, nhanh chóng nắm bắt những xu hướng, mối quan hệ của thông tin và đưa ra những quyết định chính xác hơn.

Biểu đồ và số liệu là những thành phần chủ yếu của một data app, các framework hỗ trợ xây dựng data app cũng sẽ khác với web app do tập trung vào tăng trải nghiệm của người dùng trong việc hiển thị và tương tác với dashboard.

Hiện nay có nhiều loại framework và công cụ hỗ trợ cho data visualization. Trong khuôn khổ của series này, mình sẽ bỏ qua các giải pháp no-code và low-code quen thuộc như PowerBI, Looker, QuickSight, Tableau... và sẽ tập trung vào Python framework, bắt đầu với Streamlit.

Upload image

Streamlit là gì?

Streamlit là một Python framework mã nguồn mở giúp lập trình viên nhanh chóng xây dựng các ứng dụng data visualization. Với kiến trúc đơn giản và sử dụng cú pháp khai báo (declarative syntax), lập trình viên không cần kiến thức sâu về lập trình web vẫn có thể tạo ứng dụng.

Framework này rất hữu ích trong việc tạo các prototype cũng như các ứng dụng đơn giản một cách nhanh chóng, hoàn chỉnh từ việc xác thực, tích hợp, hiển thị dữ liệu cho đến triển khai đến cho người dùng cuối.

Đặc trưng của Streamlit

Để hỗ trợ tạo data app một cách nhanh gọn và đơn giản, Streamlit cung cấp một số tính năng đặc trưng dưới đây:

Display: Magic function để hiển thị mọi thứ

Vì là data visualization framework, Streamlit hỗ trợ nhiều thư viện về chart và map Matplotlib, Altair, deck.gl... Streamlit rất tự hào về magic function vì chỉ bằng một function duy nhất là write sẽ có thể hiển thị mọi thứ từ title, data, charts, widget...

Dataflow: Điểm đặc trưng lớn nhất

Nguyên tắc hết sức đơn giản: Bất cứ lúc nào người dùng tương tác với giao diện, Streamlit sẽ chạy lại toàn bộ file Python từ đầu đến cuối. Việc này bao gồm 3 bước:

  1. Xử lý lại toàn bộ dữ liệu trên giao diện ở server
  2. Gửi toàn bộ dữ liệu đã xử lý ở server về trình duyệt của người dùng
  3. Render lại toàn bộ giao diện trên trình duyệt

Nguyên tắc này giúp việc phát triển trên Streamlit trở nên cực kì đơn giản, bạn không cần phải quan tâm đến các loại callback event. Có lẽ 'Viết thế nào, chạy thế nấy' mô tả rất đúng trải nghiệm của lập trình viên trên Streamlit.

Caching: Hỗ trợ cache cho data và resource

Để dataflow ở trên có thể hoạt động hiệu quả, Streamlit hỗ trợ cache cho cả data và resource. Trường hợp kết quả truy vấn đã được lưu trong cache trước đó, Streamlit ngay lập tức trả về kết quả mà không cần tương tác với cơ sở dữ liệu. Do đó, Streamlit sẽ tránh những tính toán không cần thiết và giảm thời gian xử lý, đặc biệt với những dataset lớn. Cache này được lưu trên memory (mặc định) và cả trên disk.

Layout: Đơn giản và cơ bản

Streamlit hỗ trợ layout ở mức cơ bản như multipage, side bar, chia layout theo hàng, cột. Những tính năng này rất dễ dùng, thậm chí người dùng còn không cần phải biết HTML, CSS.

Hands-on

Bước đầu tiên của mọi data visualization app đều là kết nối dữ liệu. Vì Python là ngôn ngữ rất phổ biến nên thư viện để kết nối đến cơ sở dữ liệu đều có thể dễ dàng tìm kiếm.

# Read the data
dataset_id = "atekco_data"
table_id = "Atekco-GA-User"

# Construct the full table reference
table_ref = client.dataset(dataset_id).table(table_id)
query = f"SELECT * FROM {table_ref}"

# Run the query
df = run_query(query)

# Process data
df['ts'] = pd.to_datetime(df['DATE'], format="%m/%d/%Y")
df['MONTH'] = df['ts'].dt.strftime('%Y-%B')
dataset = df.groupby(['MONTH', 'ts'])[['total_users', 'screen_page_views']].sum().sort_values(by='ts', ascending=True).reset_index()

Sử dụng magic function write để hiển thị biểu đồ (chart).

# Layout
st.write("Atekco Traffic")

# Filter the dataset for the month of September 2024
dataset_month = dataset[dataset["MONTH"] == "2024 September"]

# Create a line chart to visualize the total users and screen page views over time
st.line_chart(dataset_month, x="ts", y=["total_users", "screen_page_views"])

Upload image

Biểu đồ đường (line chart) thể hiện lượt view và user trong tháng

Chúng ta có thể tùy chọn thêm widget filter để tương tác với chart. Việc triển khai hết sức đơn-giản-và-thân-thiện, vì framework hầu như đã hỗ trợ toàn bộ nên lập trình viên thậm chí không cần quan tâm đến việc tạo event cho từng filter, chỉ cần quan tâm đến logic hiển thị của dữ liệu. Như trong ví dụ bên dưới, chúng ta chỉ cần tạo ra list các tháng, sau đó filter data từ dataframe tương ứng là xong.

# Process data
df['ts'] = pd.to_datetime(df['DATE'], format="%m/%d/%Y")
df['MONTH'] = df['ts'].dt.strftime('%Y %B')

dataset = df.groupby(['MONTH', 'ts'])[['total_users', 'screen_page_views']].sum().sort_values(by='ts', ascending=True).reset_index()

# Create a list of months sorted in descending order
months_option = sorted(df['MONTH'].unique(), key=lambda x: pd.to_datetime(x, format='%Y-%B'), reverse=True)

# Add a sidebar to select the month
selected_target = st.sidebar.selectbox('Select Month:', months_option)

# Layout
st.write("Atekco Traffic")
dataset_month = dataset[dataset["MONTH"] == selected_target]
st.line_chart(dataset_month, x="ts", y=["total_users", "screen_page_views"])

Upload image

Thêm bộ lọc theo tháng cho biểu đồ

Tùy thuộc vào nhu cầu của hệ thống, Streamlit hỗ trợ đa dạng các loại xác thực người dùng, từ xác thực 'kiểu sinh viên' (lưu vào file secret) hoặc xác thực của các bên thứ ba khác như MSAD, Keycloak, Google, Github...

import streamlit as st
from msal_streamlit_authentication import msal_authentication


login_token = msal_authentication(
    auth={
        "clientId": "aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee",
        "authority": "https://login.microsoftonline.com/aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee",
        "redirectUri": "/",
        "postLogoutRedirectUri": "/"
    }, # Corresponds to the 'auth' configuration for an MSAL Instance
    cache={
        "cacheLocation": "sessionStorage",
        "storeAuthStateInCookie": False
    }, # Corresponds to the 'cache' configuration for an MSAL Instance
    login_request={
        "scopes": ["aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee/.default"]
    }, # Optional
    logout_request={}, # Optional
    login_button_text="Login", # Optional, defaults to "Login"
    logout_button_text="Logout", # Optional, defaults to "Logout"
    class_name="css_button_class_selector", # Optional, defaults to None. Corresponds to HTML class.
    html_id="html_id_for_button", # Optional, defaults to None. Corresponds to HTML id.
    key=1 # Optional if only a single instance is needed
)
st.write("Recevied login token:", login_token)

Việc triển khai Streamlit trên môi trường production cũng rất đơn giản, chỉ bằng câu lệnh streamlit run.

Streamlit còn hỗ trợ Cloud bao gồm bản miễn phí và tính phí. Việc deploy lên Streamlit Cloud cực kì đơn giản, người dùng chỉ cần liên kết đến repo trên Github, điền đường dẫn file Python, thêm secret vào là phần mềm sẽ được triển khai và sẵn sàng chia sẻ cho những người dùng khác.

Trải nghiệm cá nhân của mình với phiên bản free là khá ổn với nhu cầu vừa và nhỏ, thích hợp với các loại data size nhỏ, và quan trọng là miễn phí. Không dễ để có thể tìm một giải pháp miễn phí để hosting data app.

Upload image

Triển khai app trên Streamlit Cloud

Ưu điểm

Đơn giản, dễ dùng là ưu điểm lớn nhất của Streamlit. Lập trình viên có thể ngay lập tức triển khai một data app mà không cần quá nhiều kiến thức về lập trình web. Framework phù hợp cho việc phát triển nhanh chóng các mockup hoặc prototype do đòi hỏi cần có tốc độ phát triển nhanh.

Lượng thành viên trong cộng đồng cực kì đông đảo trên Github (khoảng 35K stars) và cả trên diễn đàn của Streamlit. Lập trình viên mới tiếp cận với Streamlit sẽ choáng ngợp với số lượng document được viết công phu và rất nhiều code mẫu trên trang chủ của Streamlit cũng như trên các blog khác. Có rất nhiều thứ có thể tìm được ở đây, từ những thứ chi tiết như tùy biến biểu đồ như thế nào, tự làm xác thực hay tích hợp với MSAD ra sao, đến những thứ không liên quan đến Streamlit như làm sao để tích hợp và truy xuất dữ liệu trong Bigquery.

Vì là Python framework nên Streamlit dễ dàng tích hợp với rất nhiều thư viện Python khác từ xử lý dữ liệu (Numpy, Pandas, Spark), trực quan hóa dữ liệu (Matplotlib, Plotly).

Khuyết điểm

Giao diện của Streamlit chỉ cho phép tùy biến ở mức độ tương đối, không thể so sánh với các framework khác như Flask hoặc Ploty Dash. Có những thứ trông có vẻ đơn giản như tùy chỉnh lại grid, hoặc sửa-một-chút-CSS sẽ khiến lập trình viên phải vất vả để thay đổi source code.

Kiến trúc của Streamlit là xử lý dữ liệu tập trung (Centralized Data Processing), Streamlit không phù hợp để offload việc xử lý dữ liệu cho worker hoặc hệ thống khác. Do đó, hiệu suất luôn là dấu hỏi lớn của Streamlit khi xử lý lượng data lớn.

Streamlit là server-side rendering, không hỗ trợ client-side rendering. Mỗi lần có thay đổi (dù là rất nhỏ) trên giao diện, server sẽ chạy lại toàn bộ source code từ đầu đến cuối để xử lý, sau đó gửi toàn bộ dữ liệu lên trình duyệt của người dùng, trình duyệt cũng render lại toàn bộ giao diện. Khi chạy với số lượng data tương đối (khoảng vài trăm nghìn), hoặc tốc độ mạng chậm, người dùng sẽ phải chờ rất lâu mới có đủ dữ liệu hiển thị và hầu như không thể tương tác với các biểu đồ (đặc biệt là với các widget tương tác liên tục như slider).

Kết luận

Với khả năng tạo data app siêu tốc, Streamlit rất phù hợp với các prototype, POC, đồng thời cũng là 'mảnh ghép' dành cho các đội dự án không chuyên về lập trình web.

Tuy nhiên, với những hệ thống cần tối ưu về hiệu suất và tùy biến cao về giao diện, dự án có thể sẽ tốn rất nhiều nguồn lực để có thể tùy biến giao diện nếu không theo chuẩn của Streamlit. Do đó, cần xác định rõ những hạn chế này trước khi quyết định sử dụng Streamlit, đồng thời có thể cân nhắc những framework khác như Taipy, Plotty Dash.

Trong phần tiếp theo của series Data visualization framework, mình sẽ có một bài viết chi tiết về Taipy, một Python framework mạnh mẽ khác cũng đang nhận được nhiều sự quan tâm từ cộng đồng. Mời các độc giả đón theo dõi.

Atekco - Home for Authentic Technical Consultants