RyanNerd
라덕'Story
RyanNerd
  • 분류 전체보기 (117)
    • Study Note (80)
      • Python (3)
      • R (1)
      • Airflow (7)
      • 통계 (26)
      • Machine Learning (16)
      • Streamlit (9)
      • Django (1)
      • Deep Learning (12)
      • dbt (2)
      • SQL (3)
    • 빅데이터분석기사 (1)
      • 필기 (1)
    • Programmers (28)
      • Python (13)
      • SQL (15)
    • Project (3)
      • Django (3)
    • Mac (2)

블로그 메뉴

  • NaverBlog
  • 홈

최근 글

전체 방문자
오늘
어제
hELLO · Designed By 정상우.
RyanNerd

라덕'Story

Study Note/Streamlit

[Streamlit 대시보드] 숫자로 말하는 데이터: st.metric과 멀티페이지 앱(MPA) 설계

2025. 12. 29. 17:08

대시보드의 핵심은 "중요한 정보를 3초 안에 파악하게 하는 것"입니다. 이를 위해 Streamlit은 st.metric이라는 강력한 도구를 제공하며, 앱이 복잡해질 것을 대비해 멀티페이지(MPA) 기능을 지원합니다.

 

오늘은 가상의 병원 데이터를 활용해, 병원장님이 매일 아침 확인하고 싶어 할 '병원 운영 현황 대시보드'를 만들어보겠습니다.

 

1. 대시보드의 꽃: st.metric (지표 표시)

공식 문서에 따르면, st.metric은 큰 글씨로 핵심 지표(KPI)를 보여주고, 이전 대비 변화량(Delta)을 화살표와 함께 표시하는 함수입니다.

주요 파라미터 3가지

  1. label: 지표의 이름 (예: "총 내원 환자 수")
  2. value: 현재 값 (예: "150명")
  3. delta: 변화량 (예: "+10", "-5%"). 초록색(상승)이나 빨간색(하락)으로 자동 표시됩니다.
    • delta_color="inverse": 혈압이나 대기시간처럼 수치가 오르는 게 나쁜 경우, 색상을 반대로(빨강↔초록) 뒤집어줍니다.

[Code] 예시

import streamlit as st

col1, col2, col3 = st.columns(3)

col1.metric(label="평균 대기시간", value="45분", delta="-5분") # 줄어서 좋음 (초록)
col2.metric(label="환자 만족도", value="4.8/5.0", delta="+0.1") # 늘어서 좋음 (초록)
# delta_color="inverse": 재입원율은 오르면 안 좋음 -> 빨간색으로 표시
col3.metric(label="재입원율", value="12%", delta="+2%", delta_color="inverse")

 

2. 화면 설계 전략: 컨테이너와 레이아웃

정보를 무작정 나열하면 사용자가 피로를 느낍니다. 구획을 나눠야 합니다.

  1. st.columns: 화면을 가로로 쪼개어 지표를 나란히 배치할 때 필수입니다.
  2. st.container: 관련된 내용끼리 묶어줍니다. border=True 옵션을 쓰면 테두리가 생겨서 깔끔한 카드 UI를 만들 수 있습니다.
  3. st.tabs: 좁은 공간에 여러 차트나 데이터를 겹쳐서 보여줄 때 유용합니다.

 

3. [실전] 멀티페이지 병원 통합 대시보드 구축

이제 st.navigation을 사용해 [현황판] - [분석] - [예측] 3개의 페이지를 가진 앱을 만듭니다.

시나리오: "Smart Hospital Admin"

  • Page 1 (Dashboard): st.metric으로 주요 지표 모니터링
  • Page 2 (Analysis): st.tabs로 데이터 탐색
  • Page 3 (Prediction): st.sidebar로 입력받아 AI 예측

[Code] app.py (전체 코드)

import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import time

# --- 1. 공통 데이터 생성 함수 (가상 의료 데이터) ---
@st.cache_data
def get_hospital_data():
    np.random.seed(42)
    n = 1000
    df = pd.DataFrame({
        'Patient_ID': range(1, n+1),
        'Department': np.random.choice(['내과', '외과', '응급의학과', '소아과'], n),
        'Wait_Time': np.random.normal(30, 15, n).round(1), # 대기시간
        'Satisfaction': np.random.randint(1, 6, n), # 만족도
        'Revisit_Risk': np.random.uniform(0, 1, n).round(2) # 재방문 위험도
    })
    return df

# 데이터 로드
df = get_hospital_data()

# --- 2. 각 페이지별 화면 구성 함수 (Pages) ---

def page_dashboard():
    st.header("병원 운영 현황 (Dashboard)")
    st.markdown("실시간 병원 주요 지표(KPI)를 한눈에 확인합니다.")
    
    # [핵심] st.metric + st.container로 카드 UI 구현
    with st.container(border=True):
        st.subheader("오늘의 핵심 지표")
        
        # 화면을 4등분하여 지표 배치
        kpi1, kpi2, kpi3, kpi4 = st.columns(4)
        
        # 1. 환자 수 (증가하면 좋음)
        kpi1.metric("내원 환자 수", f"{len(df)}명", "+23명")
        
        # 2. 대기 시간 (감소해야 좋음 -> delta_color='inverse')
        avg_wait = df['Wait_Time'].mean()
        kpi2.metric("평균 대기시간", f"{avg_wait:.1f}분", "-1.2분", delta_color="inverse")
        
        # 3. 만족도 (증가하면 좋음)
        avg_sat = df['Satisfaction'].mean()
        kpi3.metric("평균 만족도", f"{avg_sat:.1f}점", "+0.2점")
        
        # 4. 고위험군 비율 (감소해야 좋음 -> delta_color='inverse')
        high_risk_ratio = (df['Revisit_Risk'] > 0.7).mean() * 100
        kpi4.metric("고위험 환자 비율", f"{high_risk_ratio:.1f}%", "-0.5%", delta_color="inverse")

    # 차트 영역
    st.markdown("---")
    c1, c2 = st.columns(2)
    with c1:
        st.markdown("##### 진료과별 환자 분포")
        fig1 = px.pie(df, names='Department', hole=0.4)
        st.plotly_chart(fig1, use_container_width=True)
    with c2:
        st.markdown("##### 대기시간 히스토그램")
        fig2 = px.histogram(df, x='Wait_Time', nbins=30, color_discrete_sequence=['#FF6F61'])
        st.plotly_chart(fig2, use_container_width=True)


def page_analysis():
    st.header("환자 데이터 심층 분석")
    
    # [핵심] st.tabs로 공간 효율화
    tab1, tab2 = st.tabs(["데이터셋 보기", " 상관관계 분석"])
    
    with tab1:
        st.caption("전체 환자 리스트입니다. 컬럼 헤더를 눌러 정렬할 수 있습니다.")
        st.dataframe(df, use_container_width=True)
        
    with tab2:
        st.caption("대기시간이 길어질수록 만족도가 어떻게 변할까요?")
        fig = px.scatter(df, x='Wait_Time', y='Satisfaction', color='Department', opacity=0.6)
        st.plotly_chart(fig, use_container_width=True)


def page_prediction():
    st.header("AI 재방문 예측 서비스")
    
    # [핵심] 레이아웃: 왼쪽(입력) vs 오른쪽(결과)
    left_col, right_col = st.columns([1, 2])
    
    with left_col:
        with st.container(border=True):
            st.subheader("환자 정보 입력")
            dept = st.selectbox("진료과", ['내과', '외과', '응급의학과', '소아과'])
            wait = st.slider("예상 대기시간(분)", 0, 120, 30)
            
            predict_btn = st.button("위험도 예측", type="primary", use_container_width=True)

    with right_col:
        if predict_btn:
            # 가상의 예측 로직 (Loading UI)
            with st.spinner('AI 모델이 분석 중입니다...'):
                time.sleep(1.5) # 연산 척하기
                risk_score = np.random.rand() * 100
            
            st.success("분석이 완료되었습니다!")
            
            # 결과도 st.metric으로 깔끔하게!
            st.metric("예측 재방문 확률", f"{risk_score:.1f}%")
            
            if risk_score > 70:
                st.error("고위험군입니다. 집중 관리가 필요합니다.")
            else:
                st.info("안정군입니다.")
        else:
            st.info("왼쪽에서 정보를 입력해주세요.")


# --- 3. 네비게이션 설정 (MPA의 핵심) ---
pages = {
    "병원 관리": [
        st.Page(page_dashboard, title="종합 현황판"),
        st.Page(page_analysis, title="데이터 분석"),
    ],
    "AI 도구": [
        st.Page(page_prediction, title="재방문 예측"),
    ]
}

pg = st.navigation(pages)

st.sidebar.title("Smart Hospital")
st.sidebar.caption("Administrator Mode")

pg.run()

 

요약

  1. st.metric의 전략적 사용:
    • 단순히 숫자만 보여주는 게 아니라, delta와 delta_color="inverse"를 통해 "상황이 좋아졌는지 나빠졌는지" 직관적으로 전달했습니다. (의료 데이터에서 수치 상승이 부정적인 경우에 필수!)
  2. 화면 분할 (st.columns):
    • Dashboard 페이지 상단에 4개의 KPI를 나란히 배치하여 정보의 밀도를 높였습니다.
  3. 컨테이너 (st.container):
    • 예측 페이지의 입력 폼에 border=True를 주어, 입력 영역과 결과 영역을 시각적으로 명확히 분리했습니다.
  4. 네비게이션 (st.navigation):
    • 기능별로 페이지를 그룹핑("병원 관리", "AI 도구")하여 사용자가 원하는 기능을 쉽게 찾을 수 있게 했습니다.

 

'Study Note > Streamlit' 카테고리의 다른 글

[Django x Streamlit] 의료 데이터 분석 플랫폼 구축하기: ORM부터 웹 대시보드까지  (0) 2025.12.30
AI 모델, 직접 만들어서 웹으로 배포하기 (A to Z)  (1) 2025.12.29
"내 앱은 왜 자꾸 까먹을까?" : Session State와 Caching 완벽 해부  (0) 2025.12.29
속도는 올리고 코드는 깔끔하게  (0) 2025.12.29
의료 데이터 분석, 웹으로 띄우기 (기초편)  (0) 2025.12.29
    'Study Note/Streamlit' 카테고리의 다른 글
    • [Django x Streamlit] 의료 데이터 분석 플랫폼 구축하기: ORM부터 웹 대시보드까지
    • AI 모델, 직접 만들어서 웹으로 배포하기 (A to Z)
    • "내 앱은 왜 자꾸 까먹을까?" : Session State와 Caching 완벽 해부
    • 속도는 올리고 코드는 깔끔하게
    RyanNerd
    RyanNerd
    라이언 덕후의 일상 스토리~

    티스토리툴바