Python Streamlit 사용법 - 프로토타입 만들기


  • Python Streamlit에 대한 글입니다
    • python streamlit tutorial, python streamlit dashboard, python streamlit install, python streamlit vs dash, python dashboard, python streamlit example
  • 목차
    • 회사 또는 프로젝트하며 생길 수 있는 일
    • 대표적인 프로토타이핑 도구
    • Streamlit 소개
    • Streamlit 맛보기
    • Streamlit API 소개
    • Streamlit Component
    • Streamlit 배포하기

회사 또는 프로젝트하며 생길 수 있는 일

  • 1) 머신러닝 모델 학습
  • 2) 머신러닝 모델 배포
  • 3) 머신러닝 모델이 잘 동작하는지 Jupyter Notebook 등에서 확인
  • 4) 모델을 만든 당사자는 노트북 파일을 기반으로 여러 동작을 할 수 있음
    • 그러나 자신만 디버깅하는게 아닌 다른 분들에게 공유하고 싶은 경우엔 이슈가 존재
      • 개발 환경 셋팅, 프리트레인 파일 다운, 노트북 실행 등을 해야 함
      • 개발을 어느정도 아는 사람이라면 괜찮지만 회사에서 기획자 또는 운영팀분들에게 보여드리기엔 굳이?라는 생각이 듬
      • 그냥 웹 형태에서 간단하게 실행해볼 수 있는 도구들을 만들기 시작
  • 5) 최초엔 Flask + 부트스트랩 등을 사용해 만드는 분들도 있지만 웹 개발에 신경을 쓰는 시간이 많아짐 => 다른 방법을 찾음
  • 6) R의 Shiny 같이 인터랙티브한 것을 찾다가 자바스크립트 D3 등을 시도
    • 허나 만만치 않구나..를 깨닫고 다시 회귀
  • 이런 경우 Python을 사용해서 빠르게 프로토타입을 만들고 싶은 욕구가 생김
    • 프로토타입을 하기 위해 찾아보면 대략 대시보드 도구를 찾아보게 됨
  • 꼭 머신러닝 모델이 아니여도, 대시보드를 만드는 것도 비슷함
  • 주로 필요한 부분은
    • 시각화(Visualization)
    • 인터랙티브(Interactive)
    • 서빙(Serving)



대표적인 프로토타이핑 도구

  • 프로토타입은 다양한 방법으로 만들 수 있고, 대표적으로 도구는 다음과 같음. 개인적으로 프로토타이핑 도구는 대시보드 도구랑 맥락이 비슷하다고 생각
    • 1) Jupyter Notebook
    • 2) Flask : 백엔드를 직접 구성 + 프론트엔드 작업도 진행
    • 3) Dash : 제일 기능이 풍부한 Python 대시보드 라이브러리
    • 4) Voila : Jupyter Notebook을 바로 시각화 가능
    • 5) Streamlit : 이 글에서 소개할 도구
  • 개인적으로 모두 사용해보고 느낀 점 정리
    • 개인 의견이라 주관적인 의견입니다. 꼭 직접 사용해보시길 추천드려요



Streamlit 소개

  • 공식 문서 : The fastest way to build and share data apps
    • 가장 빠르게 데이터 어플리케이션을 만들 수 있는 방법
  • 앱을 만드는 미니멀한 프레임워크
  • 21년 2월 기준 Github Star 13K
  • 장점
    • 간단하게 파이썬 코드로 앱을 빌드할 수 있음
    • 인터랙티브한 기능 제공(백엔드 개발이나 HTTP 요청 구현할 필요 없음)
    • 다양한 예시 제공
    • 커뮤니티에서 개발한 Component도 존재
    • Streamlit에서 배포할 수 있는 시스템 제공(단, 신청 필요)
    • 화면을 녹화할 수 있는 Record 기능도 제공
      • app을 빌드한 후, 오른쪽 ☰ 버튼을 클릭하면 Record a screencast를 확인할 수 있음

  • Awesome Streamlit Github에 참고할 수 있는 자료가 매우 많음



Streamlit 맛보기

  • 설치

      pip3 install streamlit
    
  • 파이썬 파일 작성(공식 홈페이지 샘플 코드 - sample_code.py로 저장)

      import streamlit as st
      import pandas as pd
      import numpy as np
    	
      st.title('Uber pickups in NYC')
    	
      DATE_COLUMN = 'date/time'
      DATA_URL = ('https://s3-us-west-2.amazonaws.com/'
                  'streamlit-demo-data/uber-raw-data-sep14.csv.gz')
    	
      @st.cache
      def load_data(nrows):
          data = pd.read_csv(DATA_URL, nrows=nrows)
          lowercase = lambda x: str(x).lower()
          data.rename(lowercase, axis='columns', inplace=True)
          data[DATE_COLUMN] = pd.to_datetime(data[DATE_COLUMN])
          return data
    	
      data_load_state = st.text('Loading data...')
      data = load_data(10000)
      data_load_state.text("Done! (using st.cache)")
    	
      if st.checkbox('Show raw data'):
          st.subheader('Raw data')
          st.write(data)
    	
      st.subheader('Number of pickups by hour')
      hist_values = np.histogram(data[DATE_COLUMN].dt.hour, bins=24, range=(0,24))[0]
      st.bar_chart(hist_values)
    	
      hour_to_filter = st.slider('hour', 0, 23, 17)
      filtered_data = data[data[DATE_COLUMN].dt.hour == hour_to_filter]
    	
      st.subheader('Map of all pickups at %s:00' % hour_to_filter)
      st.map(filtered_data)
    
  • Streamlit 실행

      streamlit run sample_code.py
    

  • http://localhost:8501/로 접속 가능

  • Show raw data를 클릭시 데이터프레임이 출력되고, hour 슬라이더를 조절하면 지도의 데이터가 변하는 것을 볼 수 있음



Streamlit API 소개

  • 제목 설정하기
  • 캐싱하기
  • 위젯 만들기
    • 버튼 만들기
    • 체크 박스 만들기
    • 라디오 버튼 만들기
    • 선택 박스 만들기
    • 다중 선택 박스 만들기
    • 슬라이더 만들기
  • 데이터 입력
  • 데이터 출력
  • 차트 출력
  • Progress, Status 메세지
  • 각종 컨텐츠 출력
  • 사이드바, 레이아웃



제목 설정하기

  • 타이틀

      st.title("Title")
    
  • 헤더

      st.header("Header")
    
  • 서브헤더

      st.subheader("subheader")
    


텍스트 작성하기

st.write("Write Something")


캐싱하기

  • 특정 버튼을 누를 때 데이터를 불러오고 싶은 경우가 존재
    • 데이터가 많은 경우에 반복적으로 데이터를 불러오는 것보다, 중간에 캐싱을 하면 속도도 개선되고 좋은 UX 경험을 가질 수 있음
  • Streamlit에선 st.cache 데코레이터를 사용하면 캐싱을 진행
    • 함수 이름 확인
    • 함수를 구성하는 코드 확인
    • 함수 호출시 사용한 매개변수
    • 위 3개를 확인하며 로컬에 저장해두고(=캐싱) 다시 호출시 캐싱을 사용할 수 있다면 그대로 사용함
  • 캐싱 관련은 공식 문서를 확인하면 더 자세한 내용을 확인할 수 있음

      @st.cache
      def load_data():
           # data load
           return data
    


위젯 만들기

  • 버튼 만들기

      if st.button("click button"):
          st.write("Data Loading..")
          # 데이터 로딩 함수는 여기에!
    
  • 체크 박스 만들기

      checkbox_btn = st.checkbox('Checktbox Button')
    	
      if checkbox_btn:
          st.write('Great!')
    
  • 만약 Default로 체크하고 싶다면 st.checkbox(value=True)를 주면 됨

      checkbox_btn2 = st.checkbox('Checktbox Button2', value=True)
    	
      if checkbox_btn2:
          st.write('Button2')
    
  • 라디오 버튼 만들기
    • 첫 요소가 default로 선택됨
      selected_item = st.radio("Radio Part", ("A", "B", "C"))
    	
      if selected_item == "A":
          st.write("A!!")
      elif selected_item == "B":
          st.write("B!")
      elif selected_item == "C":
          st.write("C!")
    
  • 선택 박스 만들기

      option = st.selectbox('Please select in selectbox!',
                           ('kyle', 'seongyun', 'zzsza'))
    	
      st.write('You selected:', option)
    
  • 다중 선택 박스 만들기
    • 결과가 배열로 나옴
    • 100개 이상될 경우 유저 경험이 떨어질 수 있음
      multi_select = st.multiselect('Please select somethings in multi selectbox!',
                                    ['A', 'B', 'C', 'D'])
    	
      st.write('You selected:', multi_select)
    
  • 슬라이더 만들기

      values = st.slider('Select a range of values', 0.0, 100.0, (25.0, 75.0))
      st.write('Values:', values)
    


데이터 입력

  • 텍스트 데이터를 입력하고 싶은 경우

      st.text_input(value)
    
  • 텍스트 데이터를 암호로 사용하고 싶은 경우엔 type=”password” 인자를 추가

      st.text_input(label, value, type="password")
    
  • 숫자 데이터를 입력하고 싶은 경우

      st.number_input(label, value)
    
  • 여러 줄의 텍스트 데이터를 입력하고 싶은 경우

      st.text_area(label, value)
    
  • 날짜를 입력하고 싶은 경우

      st.date_input(label, value)
    
  • 시간을 입력하고 싶은 경우

      st.time_input(label, value)
    


데이터 출력

  • st.dataframe을 사용하면 인터랙티브한 테이블을 볼 수 있고, st.table을 사용하면 static한 테이블을 볼 수 있음
  • df.style.highlight_max(axis=0)를 사용하면 최대값이 하이라이트를 줄 수 있음(이 기능은 pandas 0.24에선 오류가 발생하고 1점대에서 실행됨)

      st.write("st.dataframe api")
      df = pd.DataFrame(np.random.randn(5, 2), columns=('col %d' % i for i in range(2)))
      st.dataframe(df.style.highlight_max(axis=0))
    	
      st.write("st.table api")
      st.table(df)
    

차트 출력

  • 현재 Matplotlib, Altair, Deck.gl, Plot.ly, Bokeh, Graphviz 등을 사용해 시각화함
    • 대부분 df를 넣으면 바로 시각화할 수 있음
  • st.line_chart
  • st.area_chart
  • st.bar_chart
  • st.pyplot
  • st.altair_chart
  • st.vega_lite_chart
  • st.plotly_chart
  • st.bokeh_chart
  • st.pydeck_chart
  • st.graphviz_chart
  • st.map


Progress, Status 메세지

  • 성공 메세지

      st.success("Success")
    
  • 에러 메세지

      st.error("Error")
    
  • 경고 메세지

      st.warning("Warning")
    
  • 정보 메세지

      st.info("Info")
    
  • 코드가 실행되는 도중에 출력할 메세지

      import time
    	
      with st.spinner('Wait for it...'):
          time.sleep(5)
      st.success('Done!')
    


각종 컨텐츠 출력

  • 이미지 출력

      from PIL import Image
      image = Image.open('img.jpg')
    	
      st.image(image)
    
  • 비디오 출력

      video_file = open('video.mp4', 'rb')
      video_bytes = video_file.read()
    	
      st.video(video_bytes)
    
  • 오디오 출력

      audio_file = open('audio.ogg', 'rb')
      audio_bytes = audio_file.read()
    	
      st.audio(audio_bytes, format='audio/ogg')
    


사이드바, 레이아웃

  • 웹페이지 왼쪽에 사이드바를 추가하고 싶은 경우
add_selectbox = st.sidebar.selectbox("왼쪽 사이드바 Select Box", ("A", "B", "C"))

  • 레이아웃
    • st.beta_columns를 사용해 나눌 수 있음
col1, col2, col3 = st.beta_columns(3)

with col1:
   st.header("A cat")
   st.image("https://static.streamlit.io/examples/cat.jpg", use_column_width=True)

with col2:
   st.header("Button")
   if st.button("Button!!"):
       st.write("Yes")

with col3:
	st.header("Chart Data")
	chart_data = pd.DataFrame(np.random.randn(50, 3), columns=["a", "b", "c"])
	st.bar_chart(chart_data)



Streamlit Component



Streamlit 앱

  • 비밀번호 입력 기능
    • 비밀번호를 입력하는 기능을 만들어 앱의 보안을 강화하려고 함
    • 최초엔 간단하게 password를 입력 받고, 그 password가 맞으면 기존의 함수를 실행하는 방식으로 구현함
    • 이 방식으로 구현하니 위젯에서 데이터가 갱신될 때, 다시 password 입력 창으로 이동하는 상황을 발견
    • 이슈를 찾아보니 Feature request: confirm_button를 찾았고, gist에 참고할 수 있는 코드를 올려주셨음
  • 완성된 코드는 Github Repository에서 확인할 수 있음



Streamlit 배포하기

  • 간단히 사용하기 위해선 Heroku를 사용해도 좋고, Streamlit 자체에서 제공하는 기능을 사용해도 좋음. 여기선 Heroku를 사용함
  • Heroku 로그인
    • 홈페이지에서 로그인(계정이 없다면 가입 후 로그인)
  • Create New App
    • Create New App을 클릭해 앱 생성
    • App name은 헤로쿠 내에서 Unique해야 하며, [Appname].herokuapp.com/ 으로 URL이 생성됨
    • 저는 zzsza-streamlit-deploy로 생성

  • Github Repository 생성 및 코드 업로드
    • 이 때, setup.sh와 Procfile, requirements.txt가 필요함
    • setup.sh

        mkdir -p ~/.streamlit/
      		
        echo "\
        [server]\n\
        headless = true\n\
        enableCORS=false\n\
        port = $PORT\n\
        " > ~/.streamlit/config.toml
      
    • Procfile

        web: sh setup.sh && streamlit run app.py
      
    • requirements.txt는 pipreqs나 virtualenv에 있는 라이브러리 정보를 저장해서 업로드
  • Heroku에서 Github 연결
    • Deployment method에 Github 선택 - Connect to Github에서 인증한 후, Repo 연결

  • Settings - Config Vars - Reveal Config Vars를 클릭한 후 Key : STREAMLIT_PASSWORD, Value: 원하는 비밀번호 입력

  • 이제 Heroku App에 접근하면(https://[AppName].herokuapp.com/ 같은 URL이 생성됨) Streamlit 페이지가 보임
    • 암호를 입력하면 그래프가 보이고, 암호가 틀릴 경우 보이지 않음



Reference


카일스쿨 유튜브 채널을 만들었습니다. 데이터 사이언스, 성장, 리더십, BigQuery 등을 이야기할 예정이니, 관심 있으시면 구독 부탁드립니다 :)

PM을 위한 데이터 리터러시 강의를 만들었습니다. 문제 정의, 지표, 실험 설계, 문화 만들기, 로그 설계, 회고 등을 담은 강의입니다

이 글이 도움이 되셨거나 다양한 의견이 있다면 댓글 부탁드립니다 :)

Buy me a coffeeBuy me a coffee





© 2017. by Seongyun Byeon

Powered by zzsza