hyeonzzz's Tech Blog

Streamlit 실습 본문

Others

Streamlit 실습

hyeonzzz 2024. 6. 21. 11:15
참고강의 : https://www.youtube.com/playlist?list=PLIMb_GuNnFweSpt4s8BhlN7EggZnWWqy6
GitHub : https://github.com/teddylee777/streamlit-tutorial?tab=readme-ov-file
Streamlit Docs : https://docs.streamlit.io/develop/api-reference

1. Streamlit으로 빠르게 웹앱 생성하기 - 설치, 환경설정, 실습파일

가상환경 생성

1. 명령 프롬프트(cmd) 열어서 작업 경로로 이동

cd "파일경로"

 

2. 가상환경 초기화

python -m venv .venv
  • .venv는 가상환경 이름이라 변경 가능

 

3. 가상환경 활성화

.venv\Scripts\activate.bat
  • 이후 가상환경 진입은 모두 해당 명령어를 사용한다.

4. 가상환경에 streamlit 설치

pip install streamlit

 

5. 제대로 설치가 되었는지 테스트

streamlit hello

 

가상환경 종료

deactivate
  • 명령어 입력창이 나오지 않는다면 Ctrl + C 여러번 눌러서 명령어 창 나오게 하기

 

가상환경 활성화

Visual Studio Code를 사용하는 경우)

  • 작업 경로 폴터에서 Editor 열기 -> PowerShell Terminal 제거하고 Command Prompt로 열기

 

2. 텍스트 컴포넌트

타이틀 적용

st.title('이것은 타이틀 입니다')

 

특수 이모티콘 삽입

https://streamlit-emoji-shortcodes-streamlit-app-gwckff.streamlit.app/

st.title('스마일 :sunglasses:')

 

Header 적용

st.header('헤더를 입력할 수 있어요! :sparkles:')

 

Subheader 적용

st.subheader('이것은 subheader 입니다')

 

캡션 적용

st.caption('캡션을 한 번 넣어 봤습니다')

 

코드 표시

sample_code = '''
def function():
    print('hello, world')
'''
st.code(sample_code, language="python")

 

일반 텍스트

st.text('일반적인 텍스트를 입력해 보았습니다.')

 

마크다운 문법 지원

st.markdown('streamlit은 **마크다운 문법을 지원**합니다.')
# 컬러코드: blue, green, orange, red, violet
st.markdown("텍스트의 색상을 :green[초록색]으로, 그리고 **:blue[파란색]** 볼드체로 설정할 수 있습니다.")
st.markdown(":green[$\sqrt{x^2+y^2}=1$] 와 같이 latex 문법의 수식 표현도 가능합니다 :pencil:")

 

LaTex 수식 지원

st.latex(r'\sqrt{x^2+y^2}=1')

 

 

3. 데이터프레임(DataFrame), 테이블 출력

데이터프레임 출력

# DataFrame 생성
dataframe = pd.DataFrame({
    'first column': [1, 2, 3, 4],
    'second column': [10, 20, 30, 40],
})

# DataFrame
# use_container_width 기능은 데이터프레임을 컨테이너 크기에 확장할 때 사용합니다. (True/False)
st.dataframe(dataframe, use_container_width=False) # 확장X

 

테이블 출력

st.table(dataframe)
  • 데이터프레임의 경우 오름차순/내림차순으로 변경할 수 있고 column크기를 늘릴수도 있습니다.
  • 테이블의 경우 interactive 한 UI 를 제공하지 않습니다.

매트릭

st.metric(label="온도", value="10°C", delta="1.2°C")
st.metric(label="삼성전자", value="61,000 원", delta="-1,200 원")
  • delta : 차이 (양수: 초록색, 음수: 빨강색)

컬럼으로 영역을 나누어 표기한 경우

col1, col2, col3 = st.columns(3)
col1.metric(label="달러USD", value="1,228 원", delta="-12.00 원")
col2.metric(label="일본JPY(100엔)", value="958.63 원", delta="-7.44 원")
col3.metric(label="유럽연합EUR", value="1,335.82 원", delta="11.44 원")
  • 컨테이너를 세 영역으로 나누어 각각 col1, col2, col3로 할당합니다.
  • 해당 영역에 맞는 metric을 할당해줍니다.

 

4. 자주 사용하는 위젯(Widgets)들

버튼

# 버튼 클릭
button = st.button('버튼을 눌러보세요')

if button:
    st.write(':blue[버튼]이 눌렸습니다 :sparkles:')

 

파일 다운로드 버튼

# 샘플 데이터 생성
dataframe = pd.DataFrame({
    'first column': [1, 2, 3, 4],
    'second column': [10, 20, 30, 40],
})

# 다운로드 버튼 연결
st.download_button(
    label='CSV로 다운로드',
    data=dataframe.to_csv(), # csv 형식으로 변환
    file_name='sample.csv', # 다운로드 받을 때 파일이름
    mime='text/csv' # mime 타입
)

 

체크 박스

agree = st.checkbox('동의 하십니까?')

if agree:
    st.write('동의 해주셔서 감사합니다 :100:')

 

라디오 선택 버튼

mbti = st.radio(
    '당신의 MBTI는 무엇입니까?',
    ('ISTJ', 'ENFP', '선택지 없음'))

# 선택지마다 수행할 동작 설정
if mbti == 'ISTJ':
    st.write('당신은 :blue[현실주의자] 이시네요')
elif mbti == 'ENFP':
    st.write('당신은 :green[활동가] 이시네요')
else:
    st.write("당신에 대해 :red[알고 싶어요]:grey_exclamation:")

 

선택 박스 - 단일 값 선택

mbti = st.selectbox(
    '당신의 MBTI는 무엇입니까?',
    ('ISTJ', 'ENFP', '선택지 없음'), 
    index=2 # 기본값 선택
)

if mbti == 'ISTJ':
    st.write('당신은 :blue[현실주의자] 이시네요')
elif mbti == 'ENFP':
    st.write('당신은 :green[활동가] 이시네요')
else:
    st.write("당신에 대해 :red[알고 싶어요]:grey_exclamation:")

 

다중 선택 박스 - 다중 값 선택

options = st.multiselect(
    '당신이 좋아하는 과일은 뭔가요?', # 라벨
    ['망고', '오렌지', '사과', '바나나'], # 선택지
    ['망고', '오렌지']) # 기본값

st.write(f'당신의 선택은: :red[{options}] 입니다.')

 

슬라이더

values = st.slider(
    '범위의 값을 다음과 같이 지정할 수 있어요:sparkles:',
    0.0, 100.0, (25.0, 75.0))
st.write('선택 범위:', values)

start_time = st.slider(
    "언제 약속을 잡는 것이 좋을까요?",
    min_value=dt(2020, 1, 1, 0, 0), # 년도, 월, 일, 시, 분
    max_value=dt(2020, 1, 7, 23, 0),
    value=dt(2020, 1, 3, 12, 0), # 기본값
    step=datetime.timedelta(hours=1), # 한번 칸을 움직일때마다 어느정도로 움직일지
    format="MM/DD/YY - HH:mm") # 출력되는 형식, HH : 오후 1시=13시
st.write("선택한 약속 시간:", start_time)

 

텍스트 입력

title = st.text_input(
    label='가고 싶은 여행지가 있나요?', 
    placeholder='여행지를 입력해 주세요'
)
st.write(f'당신이 선택한 여행지: :violet[{title}]')

 

숫자 입력

number = st.number_input(
    label='나이를 입력해 주세요.', 
    min_value=10, 
    max_value=100, 
    value=30,
    step=5
)
st.write('당신이 입력하신 나이는:  ', number)

 

 

5. 로또 생성기 웹사이트 만들기

import streamlit as st
import random
import datetime

# 타이틀 생성
st.title(':sparkles:로또 생성기:sparkles:')

# 로또 생성 
def generate_lotto():
    lotto = set() # 중복x

    while len(lotto) < 6:
        number = random.randint(1, 46)
        lotto.add(number)

    lotto = list(lotto)
    lotto.sort()
    return lotto

# st.subheader(f'행운의 번호: :green[{generate_lotto()}]')
# st.write(f"생성된 시각: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}")

button = st.button('로또를 생성해 주세요!')

if button:
    for i in range(1, 6):
        st.subheader(f'{i}. 행운의 번호: :green[{generate_lotto()}]')
    st.write(f"생성된 시각: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}")

 

 

6. 그래프, 차트 그리기

한글 폰트 설정

plt.rcParams['font.family'] = "NanumGothic"
plt.rcParams['axes.unicode_minus'] = False

 

bar 차트 그리기 - 스타일 투박

fig, ax = plt.subplots()
ax.bar(data['이름'], data['나이'])
st.pyplot(fig)

 

seaborn 그리기

barplot = sns.barplot(x='이름', y='나이', data=data, ax=ax, palette='Set2')
fig = barplot.get_figure()
st.pyplot(fig)

 

matplotlib doc 참고 - 더 복잡한 그래프 그리기

labels = ['G1', 'G2', 'G3', 'G4', 'G5']
men_means = [20, 35, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]
men_std = [2, 3, 4, 1, 2]
women_std = [3, 5, 2, 3, 3]
width = 0.35       # the width of the bars: can also be len(x) sequence

fig, ax = plt.subplots()

ax.bar(labels, men_means, width, yerr=men_std, label='Men')
ax.bar(labels, women_means, width, yerr=women_std, bottom=men_means,
       label='Women')

ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.legend()

st.pyplot(fig)

 

matplotlib doc 참고 - 바코드 생성

code = np.array([
    1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1,
    0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0,
    1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1,
    1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1])

pixel_per_bar = 4
dpi = 100

fig = plt.figure(figsize=(len(code) * pixel_per_bar / dpi, 2), dpi=dpi)
ax = fig.add_axes([0, 0, 1, 1])  # span the whole figure
ax.set_axis_off()
ax.imshow(code.reshape(1, -1), cmap='binary', aspect='auto',
          interpolation='nearest')

st.pyplot(fig)

 

 

7. 파일 업로드 기능 & 업로드된 파일 처리

파일 업로드 버튼

file = st.file_uploader("파일 선택(csv or excel)", type=['csv', 'xls', 'xlsx'])
  • type에 올릴 수 있는 파일의 확장자 넣어주기

Excel or CSV 확장자를 구분하여 출력하는 경우

if file is not None:
    ext = file.name.split('.')[-1] # 마지막 확장자만 가져오기
    if ext == 'csv':
        # 파일 읽기
        df = pd.read_csv(file)
        # 출력
        st.dataframe(df)
    elif 'xls' in ext:
        # 엑셀 로드
        df = pd.read_excel(file, engine='openpyxl')
        # 출력
        st.dataframe(df)

 

업로드 오류 방지

import streamlit as st
import pandas as pd
import time

# 파일 업로드 버튼 (업로드 기능)
file = st.file_uploader("파일 선택(csv or excel)", type=['csv', 'xls', 'xlsx'])

time.sleep(3) # 대기 시간

 

 

8. 주식 데이터 조회, 주가 차트 그리기

FinanceDataReader 설치

https://github.com/FinanceData/FinanceDataReader

pip install finance-datareader

 

날짜 선택

date = st.date_input(
    "조회 시작일을 선택해 주세요",
    datetime.datetime(2022, 1, 1)
)

 

종목 코드 입력

code = st.text_input(
    '종목코드', 
    value='',
    placeholder='종목코드를 입력해 주세요'
)

 

데이터 출력

if code and date:
    df = fdr.DataReader(code, date) # 데이터 가져오기
    data = df.sort_index(ascending=True).loc[:, 'Close'] # 인덱스 기준으로 정렬(날짜 기준), Close : 종가
    st.line_chart(data)

 

 

9. 사이드바, 탭 추가하기

사이드바

with st.sidebar:
    date = st.date_input(
        "조회 시작일을 선택해 주세요",
        datetime.datetime(2022, 1, 1)
    )

    code = st.text_input(
        '종목코드', 
        value='',
        placeholder='종목코드를 입력해 주세요'
    )

 

if code and date:
    df = fdr.DataReader(code, date)
    data = df.sort_index(ascending=True).loc[:, 'Close']

    tab1, tab2 = st.tabs(['차트', '데이터'])

    with tab1:    
        st.line_chart(data)

    with tab2:
        st.dataframe(df.sort_index(ascending=False))

    with st.expander('컬럼 설명'):
        st.markdown('''
        - Open: 시가
        - High: 고가
        - Low: 저가
        - Close: 종가
        - Adj Close: 수정 종가
        - Volumn: 거래량
        ''')
  • st.expander : 부가적인 설명

 


※ streamlit 배포 오류 참고

Python 3.9 이상에서는 backports.zoneinfo 대신 기본 제공 zoneinfo 모듈을 제공해서


1. requirements.txt에서 backports.zoneinfo를 제거하고

2. 아래 코드를 적용해 해결했다.

from zoneinfo import ZoneInfo

 

 

'Others' 카테고리의 다른 글

캐치카페 신촌점 후기  (0) 2023.04.06