Voila를 활용한 Dashboard가 아닌 실제 Web 상에 Product Serving을 수행하고 싶을 때 활용하는 툴
JavaScript, React, Vue 등을 활용해 프로토타입(특히 프론트엔드)에 신경쓰는 것보다는 모델 배포나 활용, 진행 과정 등을 파악하는게 중요한 경우가 많으며, 이 때 유용하게 활용할 수 있음
기존 코드를 조금만 수정하여 웹 서비스를 구현하는 것을 도와주는 툴
Python Scrip Code를 조금만 수정하면 웹을 띄울 수 있음
백엔드 개발이나 HTTP Request 구현이 필요 없음
다양한 Component를 제공하여 Dashboard UI를 구성할 수 있음
화면 녹화 기능 존재
쉽게 배포 가능
pip install streamlit
streamlit run {실행시킬 Python 파일}
localhost:8501에서 접근 가능
--port
등을 통해 Port 번호를 바꿀 수는 있지만, 굳이 할 필요는 없음Streamlit이 애초에 CLI를 통해 웹에서 실행시키는 형식이다보니, Jupyter Notebook보다는 PyCharm에서 실험하는 것이 편하다. 따라서, 아래 있는 실험은 모두 PyCharm을 통해 실행하겠다.
import streamlit as st
st.title('Title')
st.header('Header')
st.subheader('subheader')
st.write("Write Something")
import streamlit as st
if st.button("1 + 1"):
st.write("2!!!")
if st.button("1 + 2"):
st.write("3!!!")
streamlit에서는 st.button()
을 통해 생성된 버튼을 클릭하면 st.button()
값이 True를 반환한다. 따라서 이런 성질을 이용해 Event 처리가 가능해진다
위 사진에서 1+1은 클릭하였으므로 st.write()
메서드가 수행되었지만, 1+2는 버튼이 클릭되지 않았으므로 아무것도 출력되지 않았다.
import streamlit as st
import pandas as pd
df= pd.DataFrame({
'first':[1,2,3,4],
'second':[5,6,7,8]
})
st.dataframe(df)
st.table(df)
위 이미지만 보면 차이점이 없어보일 수도 있다.
위 표는 st.dataframe
, 아래 표는 st.table
을 통해 만들어진 것이다.
이미지에서 볼 수 있듯 st.dataframe
은 Ascending, 혹은 Descending으로 (컬럼 기준으로) 정렬할 수 있다.
하지만, st.table
은 불가능하다
df.style.highlight_max(axis=0)
st.dataframe(df.style.highlight_max(axis=0))
으로 입력하면, df로 표를 그리는데, Style은 axis=0
기준 최댓값에 강조(highlight_max)하는 형식으로 표를 그리라는 의미이다import streamlit as st
import pandas as pd
import numpy as np
df= pd.DataFrame(
np.random.rand(20,3),
columns=['a','b','c']
)
st.line_chart(df)
오른쪽 위 3개 점을 클릭하여 Source Code를 보거나 그래프를 저장할 수 있음
columns를 통해 Data의 각 Column에 이름을 붙여줄 수 있음
import streamlit as st
selected_items = st.radio('Radio Part', ('A','B','C'))
st.write(selected_items+"!!!")
st.radio(label, options)
st.radio
에서 특정 값을 선택하면, 선택된 값을 반환한다. 따라서 위 코드 그대로 수행된다면, 선택된 값이 출력 될 것이다.
st.write()
메서드가 실행되는 것이다st.selectbox
st.radio
와 동일st.multiselect
st.selectbox
와 유사multiselect
이름을 가진 만큼 여러 개의 값을 동시에 선택 할 수 있음String 입력 : st.text_input
type='password'
: 인자로 전달할 경우, 입력한 값을 Password 형식으로 변환. 즉, 검은색 점으로 나오고 입력한 값은 보이지 않음숫자 입력 : st.number_input
날짜 입력 : st.date_input
시간 입력 : st.time_input
Latex 수식 입력 : st.latex
Caption 입력 : st.caption
st.write()
처럼 한 번 값을 입력한 이후 변경하지 못하게 한 것Code 입력 : st.code
Sidebar에 파라미터를 지정하거나 암호를 설정할 수 있음
Sidebar에 여러 Option을 넣어 Option에 맞는 함수를 지정해 줄 수 있음
활용 방식 : 기존 Method 앞에 sidebar
만 붙이면 Sidebar에 보이게 할 수 있음
st.sidebar.button('hi')
import streamlit as st
with st.expander('클릭!'):
st.write('이걸 그렇게 보고 싶었나?')
Expander(확장을 원하는 Section) 클릭 이전
Expander 클릭 이후
import streamlit as st
st.success("Success")
st.info('Info')
st.warning('Warning')
st.error('error')
import streamlit as st
uploaded_file = st.file_uploader('Choose File!', type=['png', 'jpg'])
# 업로드한 파일은 uploaded_file 객체에 저장됨
# type : Upload할 수 있는 파일의 확장자명
매우 핵심적인 내용이기 때문에 꼭 알아야 할 개념
Streamlit은 무언가 update될 경우 전체 Streamlit 코드가 다시 실행됨
따라서, 중복 이벤트를 수행시킬 수 없음
이런 중복 이벤트를 수행하기 위해 session_state_value
에 공유할 값을 저장하여 활용함
import streamlit as st
value = 0
increment = st.button('Increment')
decrement = st.button('Decrement')
if increment:
value+=1
if decrement:
value-=1
st.write("Value : ",value)
import streamlit as st
if 'count' not in st.session_state:
st.session_state.count = 0
# 'count'가 저장되어 있지 않다면(하위 폴더에 없다면) 새로 생성해줌
increment = st.button('Increment')
decrement = st.button('Decrement')
if increment:
st.session_state.count+=1
if decrement:
st.session_state.count-=1
st.write("Value : ",st.session_state.count)
st.session_state
밑에 내가 원하는 이름(위 코드에서는 count)로 변수명을 만들어 놓은 뒤 해당 공간에 값을 넣어둠st.session_state
의 "하위 폴더"에 변수를 저장한다고 이해하면 편할 것 같다.st.session_state.count
에 이전 연산 값의 결과가 저장되어 있으므로, "저장되어 있는 Session 값"에 내가 원하는 연산을 수행하기 때문에 위와 같은 결과가 나온다매번 다시 실행하는 Streamlit 특징 때문에 Data를 매번 다시 읽을 수 있음
매번 Data를 읽을 경우 시간이 오래 걸리므로, 캐싱을 통해 데이터를 저장함
Data를 읽는 함수를 만들어 @st.cache
데코레이터를 활용해 캐싱하면 Streamlit을 실행시킬 때 다시 데이터를 불러오는 코드를 수행시킬 필요가 없음
from io import BytesIO
from urllib import request
from PIL import Image
import streamlit as st
import time
url = "https://upload.wikimedia.org/wikipedia/en/2/24/Binary-icon.png"
def download_image():
start = time.time()
res = request.urlopen(url).read()
end = time.time()
img = Image.open(BytesIO(res))
return (end-start), img
if st.button("이미지 다운"):
time, image = download_image()
st.write(time)
st.image(image)
from io import BytesIO
from urllib import request
from PIL import Image
import streamlit as st
import time
url = "https://upload.wikimedia.org/wikipedia/en/2/24/Binary-icon.png"
@st.cache # Caching하겠다!
def download_image():
start = time.time()
res = request.urlopen(url).read()
end = time.time()
img = Image.open(BytesIO(res))
return (end-start), img
if st.button("이미지 다운"):
time, image = download_image()
st.write(time)
st.image(image)
몇 번을 클릭하더라도 똑같은 시간이 소요된다. Caching되어 메모리 상에 저장되어 있는 이미지를 불러오기 때문에 불러오는 시간대가 안정적이며, 따라서 데이터 흐름 등을 예측하기에 편하다는 장점을 가질 수 있다. 또한, 시간 자체도 덜 소요됨을 볼 수 있다
특히, 계속해서 데이터를 다운 받을 때(위 URL의 경우 더욱) 네트워크 트래픽 양에 의존적일 수밖에 없다. 하지만, Caching은 네트워크 트래픽과는 연관이 없으므로 안정적이다