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


카일스쿨 유튜브 채널을 만들었습니다. 데이터 분석, 커리어에 대한 내용을 공유드릴 예정입니다.

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

이 글이 도움이 되셨거나 의견이 있으시면 댓글 남겨주셔요.

Buy me a coffeeBuy me a coffee





© 2017. by Seongyun Byeon

Powered by zzsza