데이터분석/Python

[streamlit] Streamlit 튜토리얼 - 앱 만들기

psystat 2024. 8. 15. 16:37

https://docs.streamlit.io/get-started/tutorials/create-an-app

 

Streamlit Docs

Join the community Streamlit is more than just a way to make data apps, it's also a community of creators that share their apps and ideas and help each other make their work better. Please come join us on the community forum. We love to hear your questions

docs.streamlit.io

여기까지 읽으셨다면 Streamlit을 설치하고 기본 개념과 고급 개념의 기본 사항을 살펴봤을 가능성이 높습니다. 그렇지 않다면 지금이 바로 시작하기 좋은 시기입니다.

스트림릿 사용법을 배우는 가장 쉬운 방법은 직접 사용해 보는 것입니다. 이 가이드를 읽으면서 각 방법을 테스트해 보세요. 앱이 실행되는 동안 스크립트에 새 요소를 추가하고 저장할 때마다 스트림릿의 UI는 앱을 다시 실행하고 변경 사항을 확인할 것인지 묻습니다. 따라서 코드를 작성하고, 저장하고, 결과물을 검토하고, 다시 작성하는 등 결과에 만족할 때까지 빠른 대화형 루프에서 작업할 수 있습니다. 목표는 Streamlit을 사용하여 데이터 또는 모델에 대한 대화형 앱을 만들고 그 과정에서 Streamlit을 사용하여 코드를 검토, 디버그, 완성 및 공유하는 것입니다.

이 가이드에서는 Streamlit의 핵심 기능을 사용하여 대화형 앱을 만들고, 뉴욕시의 픽업 및 하차에 대한 공개 Uber 데이터 세트를 살펴봅니다. 이 과정을 마치면 데이터를 가져와 캐시하고, 차트를 그리고, 지도에 정보를 표시하고, 슬라이더와 같은 대화형 위젯을 사용하여 결과를 필터링하는 방법을 알게 될 것입니다.

1. Create your first app

Streamlit은 단순히 데이터 앱을 만드는 방법을 넘어, 앱과 아이디어를 공유하고 서로의 작업을 개선하도록 돕는 크리에이터들의 커뮤니티이기도 합니다. 커뮤니티 포럼에 참여해 주세요. 여러분의 질문과 아이디어를 듣고 버그 해결에 도움을 드리고 싶으니 지금 바로 방문해 주세요!

1. 첫 번째 단계는 새 Python 스크립트를 만드는 것입니다. uber_pickups.py라고 부르겠습니다.

2. 즐겨 사용하는 IDE 또는 텍스트 편집기에서 uber_pickups.py를 열고 다음 줄을 추가합니다:

import streamlit as st
import pandas as pd
import numpy as np

3. 모든 좋은 앱에는 제목이 있으므로 제목을 추가해 보겠습니다:

st.title('Uber pickups in NYC')

4. 이제 명령줄에서 Streamlit을 실행할 차례입니다:

streamlit run uber_pickups.py

<Tip>

streamlit run에 URL을 전달할 수도 있다는 사실을 알고 계셨나요? 이 기능은 GitHub Gist와 함께 사용하면 매우 유용합니다. 예를 들어

streamlit run https://raw.githubusercontent.com/streamlit/demo-uber-nyc-pickups/master/streamlit_app.py

5. 평소와 같이 앱이 브라우저의 새 탭에서 자동으로 열립니다.

2. Fetch some data

이제 앱이 생겼으니 다음으로 해야 할 일은 뉴욕시에서 픽업 및 하차에 대한 Uber 데이터 세트를 가져오는 것입니다.

1. 먼저 데이터를 로드하는 함수를 작성해 보겠습니다. 이 코드를 스크립트에 추가합니다:

DATE_COLUMN = 'date/time'
DATA_URL = ('https://s3-us-west-2.amazonaws.com/'
         'streamlit-demo-data/uber-raw-data-sep14.csv.gz')

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

load_data는 일부 데이터를 다운로드하여 판다스 데이터 프레임에 넣고 날짜 열을 텍스트에서 날짜/시간으로 변환하는 평범한 함수라는 것을 알 수 있습니다. 이 함수는 데이터 프레임에 로드할 행의 수를 지정하는 단일 매개변수(nrows)를 받습니다.

2. 이제 함수를 테스트하고 출력을 검토해 보겠습니다. 함수 아래에 다음 줄을 추가합니다:

# Create a text element and let the reader know the data is loading.
data_load_state = st.text('Loading data...')
# Load 10,000 rows of data into the dataframe.
data = load_data(10000)
# Notify the reader that the data was successfully loaded.
data_load_state.text('Loading data...done!')

앱 오른쪽 상단에 앱을 다시 실행할지 묻는 몇 개의 버튼이 표시됩니다. 항상 재실행을 선택하면 저장할 때마다 변경 사항이 자동으로 표시됩니다.

데이터를 다운로드하고 데이터 프레임에 10,000줄을 로드하는 데 시간이 오래 걸리는 것으로 나타났습니다. 날짜 열을 날짜/시간으로 변환하는 것도 쉬운 일이 아닙니다. 앱이 업데이트될 때마다 데이터를 다시 로드하고 싶지 않다면 다행히 Streamlit을 사용하면 데이터를 캐시할 수 있습니다.

3. Effortless caching

1. load_data 선언 앞에 @st.cache_data를 추가해 보세요:

@st.cache_data
def load_data(nrows):

2. 그런 다음 스크립트를 저장하면 스트림릿이 자동으로 앱을 다시 실행합니다. st.cache_data로 스크립트를 실행하는 것은 이번이 처음이므로 아무 변화가 없습니다. 캐싱의 성능을 확인할 수 있도록 파일을 조금 더 조정해 보겠습니다.

3. data_load_state.text('데이터 로드 중...완료!') 줄을 이 줄로 바꿉니다:

data_load_state.text("Done! (using st.cache_data)")

4. 이제 저장합니다. 추가한 줄이 바로 어떻게 표시되는지 보셨나요? 잠시 한 발짝 물러서서 보면 정말 놀라운 일입니다. 무대 뒤에서 마법 같은 일이 일어나고 있으며, 이를 활성화하는 데 코드 한 줄만 있으면 됩니다.

4. How's it work?

잠시 시간을 내어 @st.cache_data가 실제로 어떻게 작동하는지 살펴보겠습니다.

함수에 Streamlit의 캐시 어노테이션을 표시하면 함수가 호출될 때마다 두 가지를 확인해야 한다고 Streamlit에 알려줍니다:

1. 함수 호출에 사용한 입력 매개변수입니다.

2. 함수 내부의 코드입니다.

스트림릿이 이 두 항목을 처음 본 경우, 이 정확한 값과 이 정확한 조합으로 두 항목을 모두 본 경우, 함수를 실행하고 결과를 로컬 캐시에 저장합니다. 다음에 함수가 호출될 때 두 값이 변경되지 않았다면 Streamlit은 함수 실행을 건너뛸 수 있다는 것을 알고 있습니다. 대신 로컬 캐시에서 출력을 읽어와서 마법처럼 호출자에게 전달합니다.

"하지만 잠깐만요, 이건 너무 좋은 것 같지만 사실이라고 하기에는 너무 좋은데요. 이 모든 멋진 소스의 한계는 무엇일까요?"

몇 가지가 있습니다:

1. Streamlit은 현재 작업 디렉터리 내의 변경 사항만 확인합니다. 파이썬 라이브러리를 업그레이드하는 경우, 해당 라이브러리가 작업 디렉터리 내에 설치된 경우에만 Streamlit의 캐시가 이를 감지합니다.

2. 함수가 결정론적이지 않거나(즉, 출력이 난수에 의존하는 경우) 시간에 따라 변하는 외부 소스(예: 실시간 주식 시장 시세 서비스)에서 데이터를 가져오는 경우 캐시된 값은 아무 의미가 없습니다.

3. 마지막으로, 캐시된 값은 참조로 저장되므로 st.cache_data로 캐시된 함수의 출력에 변형을 가하지 않도록 해야 합니다.

이러한 제한 사항을 염두에 두는 것이 중요하지만, 의외로 많은 경우 문제가 되지 않는 경향이 있습니다. 그럴 때 이 캐시는 정말 혁신적입니다.

이제 Streamlit의 캐싱이 어떻게 작동하는지 알았으니, Uber 픽업 데이터로 돌아가 보겠습니다.

5. Inspect the raw data

작업을 시작하기 전에 항상 작업 중인 원시 데이터를 살펴보는 것이 좋습니다. 앱에 원시 데이터의 하위 헤더와 인쇄물을 추가해 보겠습니다:

st.subheader('Raw data')
st.write(data)

기본 개념 가이드에서 st.write는 사용자가 전달하는 거의 모든 것을 렌더링한다는 것을 배웠습니다. 이 경우 데이터 프레임을 전달하면 대화형 테이블로 렌더링됩니다. 

st.write는 입력의 데이터 유형에 따라 올바른 작업을 수행하려고 시도합니다. 예상한 대로 작동하지 않는 경우 st.dataframe과 같은 특수 명령을 대신 사용할 수 있습니다. 전체 목록은 API 참조를 참조하세요.

6. Draw a histogram

이제 데이터 집합을 살펴보고 사용 가능한 항목을 관찰했으니 한 걸음 더 나아가 히스토그램을 그려서 뉴욕시에서 Uber가 가장 바쁜 시간대가 언제인지 확인해 보겠습니다.

1. 먼저 원시 데이터 섹션 바로 아래에 하위 헤더를 추가해 보겠습니다:

st.subheader('Number of pickups by hour')

2. NumPy를 사용하여 픽업 시간을 시간 단위로 세분화한 히스토그램을 생성합니다:

hist_values = np.histogram(
    data[DATE_COLUMN].dt.hour, bins=24, range=(0,24))[0]

3. 이제 Streamlit의 st.bar_chart() 메서드를 사용하여 이 히스토그램을 그려보겠습니다.

st.bar_chart(hist_values)

4. 스크립트를 저장합니다. 이 히스토그램이 앱에 바로 표시됩니다. 간단히 검토한 결과, 가장 바쁜 시간은 17:00(오후 5시)인 것으로 보입니다.

이 다이어그램을 그리기 위해 Streamlit의 기본 bar_chart() 메서드를 사용했지만, Streamlit은 Altair, Bokeh, Plotly, Matplotlib 등과 같은 더 복잡한 차트 라이브러리를 지원한다는 점을 알아두는 것이 중요합니다. 전체 목록은 지원되는 차트 라이브러리를 참조하세요.

7. Plot data on a map

Uber의 데이터 세트에 히스토그램을 사용하면 가장 붐비는 시간대를 파악하는 데 도움이 되었지만, 도시 전체에서 어디에서 차량 서비스가 집중되는지 파악하려면 어떻게 해야 할까요? 막대 차트를 사용하여 이 데이터를 표시할 수는 있지만, 도시의 위도 및 경도 좌표를 잘 알고 있지 않으면 해석하기가 쉽지 않습니다. 픽업 집중도를 표시하기 위해 Streamlit st.map() 함수를 사용하여 뉴욕시 지도 위에 데이터를 오버레이해 보겠습니다.

1. 섹션의 하위 헤더를 추가합니다:

st.subheader('Map of all pickups')

2. st.map() 함수를 사용하여 데이터를 플로팅합니다:

st.map(data)

3. 스크립트를 저장합니다. 지도는 완전한 대화형입니다. 이동하거나 확대하여 사용해 보세요.

히스토그램을 그린 후 Uber 픽업이 가장 붐비는 시간이 17시라는 것을 확인했습니다. 17:00에 픽업이 집중되는 시간을 표시하도록 지도를 다시 그려 보겠습니다.

1. 다음 코드 스니펫을 찾습니다:

st.subheader('Map of all pickups')
st.map(data)

2. 다음 코드로 교체합니다:

hour_to_filter = 17
filtered_data = data[data[DATE_COLUMN].dt.hour == hour_to_filter]
st.subheader(f'Map of all pickups at {hour_to_filter}:00')
st.map(filtered_data)

이 지도를 그리기 위해 Streamlit에 내장된 st.map 함수를 사용했지만, 복잡한 지도 데이터를 시각화하고 싶다면 st.pydeck_chart를 살펴보는 것을 추천합니다.

8. Filter results with a slider

지난 섹션에서 지도를 그릴 때 결과 필터링에 사용되는 시간을 스크립트에 하드코딩했는데, 독자가 실시간으로 데이터를 동적으로 필터링할 수 있도록 하려면 어떻게 해야 할까요? Streamlit의 위젯을 사용하면 가능합니다. st.slider() 메서드를 사용하여 앱에 슬라이더를 추가해 보겠습니다.

1. 이 줄을 찾습니다:

st.subheader('Raw data')
st.write(data)

2. 이 줄을 다음 코드로 바꿉니다:

if st.checkbox('Show raw data'):
    st.subheader('Raw data')
    st.write(data)

여러분만의 아이디어가 있을 것입니다. 이 튜토리얼을 마치면 Streamlit이 노출하는 모든 위젯을 API 레퍼런스에서 확인하세요.