패션 트렌트 분석 대시보드 스트림릿으로 만들기

Pearl · 바이브 코딩 · · 좋아요 0 · 답글 0

연재 Streamlit으로 첫 실용 웹앱 만들기 1/2편

스트림릿 쉐어 사이트를 통해 배포 완료

https://fashion-trend.streamlit.app/

결과와 배운 점

스트림릿 쉐어에서 배포를 하는 과정에서 에러가 발생했는데, 원인은 라이브러리 버젼 문제였고, 4개 라이브러리의 버젼을 삭제하고 다시 하라고 해서, 수정후 배포하는데 성공했다. 2번 정도 수정을 했고. 총 1시간 정도 소요.

파이썬 스트림릿 코드 (340줄)

import streamlit as st
import pandas as pd
import numpy as np
import random
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime

# 페이지 설정
st.set_page_config(page_title="패션 트렌드 분석 대시보드", layout="wide")

# CSS 스타일 추가
st.markdown("""
<style>
    .main-header {
        font-size: 2.5rem;
        color: #333;
        text-align: center;
        margin-bottom: 2rem;
        padding-bottom: 1rem;
        border-bottom: 2px solid #fafafa;
    }
    .sub-header {
        font-size: 1.5rem;
        color: #444;
        margin-top: 1rem;
    }
    .card {
        background-color: #f9f9f9;
        border-radius: 5px;
        padding: 1rem;
        box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    }
</style>
""", unsafe_allow_html=True)

# 제목
st.markdown("<h1 class='main-header'>패션 트렌드 분석 대시보드</h1>", unsafe_allow_html=True)

# 가상 데이터 생성
def generate_trend_keywords():
    seasons = ['SS22', 'FW22', 'SS23', 'FW23', 'SS24']
    keywords = {
        'SS22': {'미니멀': 95, '네온': 80, '오버사이즈': 75, '크롭': 70, '빈티지': 65, 
                '플리츠': 60, '스트라이프': 55, '체크': 50, '트위드': 45, '프릴': 40},
        'FW22': {'오버사이즈': 90, '레이어드': 85, '헤리티지': 80, '니트': 75, '빈티지': 70,
                '퍼': 65, '벨벳': 60, '체크': 55, '레더': 50, '메탈릭': 45},
        'SS23': {'컷아웃': 92, '와이드': 88, '비비드': 82, '크로셰': 78, '트랜스페런트': 72,
                '스트라이프': 68, '패치워크': 64, '플로럴': 60, '시스루': 56, '라인스톤': 52},
        'FW23': {'오버사이즈': 94, '빌드업': 86, '그런지': 84, '코듀로이': 76, '미니멀': 74,
                '체크': 66, '시어링': 64, '레트로': 58, '퀼팅': 54, '멀티포켓': 48},
        'SS24': {'바이오필릭': 96, '아르떼': 90, '소프트': 84, '투명': 82, '핸드메이드': 76,
                '코튼': 72, '메쉬': 68, '버클': 62, '프린지': 58, '리사이클': 56}
    }
    return seasons, keywords

def generate_color_data():
    seasons = ['SS22', 'FW22', 'SS23', 'FW23', 'SS24']
    colors = ['버터 옐로우', '스카이 블루', '세이지 그린', '라벤더', '테라코타', 
             '네이비', '에메랄드', '퍼플', '차콜', '올리브']
    
    data = []
    for season in seasons:
        for color in colors:
            popularity = random.randint(20, 100)
            data.append({'시즌': season, '색상': color, '인기도': popularity})
    
    return pd.DataFrame(data)

def generate_material_data():
    seasons = ['SS22', 'FW22', 'SS23', 'FW23', 'SS24']
    materials = ['코튼', '실크', '리넨', '데님', '울', '가죽', '벨벳', '트위드', '시폰', '저지']
    
    data = []
    for season in seasons:
        for material in materials:
            popularity = random.randint(15, 95)
            data.append({'시즌': season, '소재': material, '인기도': popularity})
    
    return pd.DataFrame(data)

def generate_pattern_data():
    seasons = ['SS22', 'FW22', 'SS23', 'FW23', 'SS24']
    patterns = ['스트라이프', '플로럴', '체크', '도트', '지오메트릭', 
               '타이다이', '카모', '애니멀', '페이즐리', '아가일']
    
    data = []
    for season in seasons:
        for pattern in patterns:
            popularity = random.randint(10, 90)
            data.append({'시즌': season, '패턴': pattern, '인기도': popularity})
    
    return pd.DataFrame(data)

def generate_time_series():
    # 트렌드 변화 추이 데이터 생성
    dates = pd.date_range(start='2022-01-01', end='2024-06-30', freq='MS')
    trends = ['미니멀', '오버사이즈', '빈티지', '네온', '크롭', '레이어드']
    
    data = []
    for trend in trends:
        base = random.randint(30, 70)
        for date in dates:
            # 랜덤한 변동성 추가
            value = base + random.randint(-15, 15) + random.randint(-10, 10) * np.sin(date.month/6*np.pi)
            value = max(5, min(value, 100))  # 5~100 사이로 제한
            
            # 특정 트렌드는 특정 시즌에 더 강하게 나타남
            if trend == '네온' and 3 <= date.month <= 8:  # 봄/여름에 강함
                value += 15
            if trend == '레이어드' and (date.month <= 2 or date.month >= 9):  # 가을/겨울에 강함
                value += 15
                
            # 데이터 포인트 추가
            data.append({'날짜': date, '트렌드': trend, '인기도': value})
    
    return pd.DataFrame(data)

# 데이터 생성
seasons, keywords = generate_trend_keywords()
color_df = generate_color_data()
material_df = generate_material_data()
pattern_df = generate_pattern_data()
trend_time_df = generate_time_series()

# 사이드바 설정
st.sidebar.title("분석 옵션")
selected_season = st.sidebar.selectbox("시즌 선택", seasons, index=len(seasons)-1)
chart_type = st.sidebar.radio("차트 유형", ["막대 차트", "레이더 차트", "히트맵"])
trend_categories = st.sidebar.multiselect("트렌드 추이 선택", 
                                         ['미니멀', '오버사이즈', '빈티지', '네온', '크롭', '레이어드'],
                                         default=['미니멀', '오버사이즈', '빈티지'])

# 대시보드 헤더 - 현재 선택된 시즌 표시
st.markdown(f"<h2 class='sub-header'>{selected_season} 시즌 트렌드 분석</h2>", unsafe_allow_html=True)
st.markdown("<hr>", unsafe_allow_html=True)

# 3개 열로 레이아웃 구성
col1, col2 = st.columns([3, 2])

with col1:
    # 트렌드 키워드 시각화
    st.markdown("<div class='card'>", unsafe_allow_html=True)
    st.subheader("트렌드 키워드")
    keywords_df = pd.DataFrame({
        '키워드': list(keywords[selected_season].keys()),
        '인기도': list(keywords[selected_season].values())
    }).sort_values('인기도', ascending=False)
    
    # 키워드 차트
    if chart_type == "막대 차트":
        fig = px.bar(keywords_df.head(10), x='키워드', y='인기도', color='키워드',
                    title=f"{selected_season} 인기 키워드 Top 10")
    elif chart_type == "레이더 차트":
        fig = go.Figure()
        fig.add_trace(go.Scatterpolar(
            r=keywords_df.head(8)['인기도'].tolist(),
            theta=keywords_df.head(8)['키워드'].tolist(),
            fill='toself',
            name=selected_season
        ))
        fig.update_layout(
            polar=dict(radialaxis=dict(visible=True, range=[0, 100])),
            showlegend=False,
            title=f"{selected_season} 인기 키워드 분포"
        )
    else:  # 히트맵
        # 시즌별 키워드 인기도 매트릭스 생성
        keyword_matrix = {}
        for season in seasons:
            keyword_matrix[season] = keywords[season]
        df_heatmap = pd.DataFrame(keyword_matrix).fillna(0)
        fig = px.imshow(df_heatmap, color_continuous_scale='Viridis',
                        title="시즌별 키워드 인기도 히트맵")
    
    st.plotly_chart(fig, use_container_width=True)
    st.markdown("</div>", unsafe_allow_html=True)
    
    # 트렌드 변화 추이 그래프
    st.markdown("<div class='card'>", unsafe_allow_html=True)
    st.subheader("트렌드 변화 추이")
    filtered_trends = trend_time_df[trend_time_df['트렌드'].isin(trend_categories)]
    fig = px.line(filtered_trends, x='날짜', y='인기도', color='트렌드', 
                 title="시간에 따른 트렌드 변화",
                 labels={'인기도': '인기도 지수', '날짜': '날짜'},
                 line_shape='spline')
    fig.update_layout(height=500)
    st.plotly_chart(fig, use_container_width=True)
    st.markdown("</div>", unsafe_allow_html=True)

with col2:
    # 인기 색상 차트
    st.markdown("<div class='card'>", unsafe_allow_html=True)
    st.subheader("인기 색상")
    season_colors = color_df[color_df['시즌'] == selected_season].sort_values('인기도', ascending=False).head(5)
    
    if chart_type == "막대 차트":
        fig = px.bar(season_colors, x='색상', y='인기도', color='색상',
                    title=f"{selected_season} 시즌 인기 색상 Top 5",
                    color_discrete_sequence=px.colors.qualitative.Pastel)
    elif chart_type == "레이더 차트":
        fig = go.Figure()
        fig.add_trace(go.Scatterpolar(
            r=season_colors['인기도'].tolist(),
            theta=season_colors['색상'].tolist(),
            fill='toself',
            name=selected_season
        ))
        fig.update_layout(
            polar=dict(radialaxis=dict(visible=True, range=[0, 100])),
            showlegend=False,
            title=f"{selected_season} 인기 색상 분포"
        )
    else:  # 히트맵
        pivot_colors = color_df.pivot(index='색상', columns='시즌', values='인기도')
        fig = px.imshow(pivot_colors, text_auto=True, aspect="auto", color_continuous_scale='Viridis',
                       title=f"시즌별 색상 인기도 히트맵")
    
    fig.update_layout(height=300)
    st.plotly_chart(fig, use_container_width=True)
    st.markdown("</div>", unsafe_allow_html=True)
    
    # 인기 소재 차트
    st.markdown("<div class='card'>", unsafe_allow_html=True)
    st.subheader("인기 소재")
    season_materials = material_df[material_df['시즌'] == selected_season].sort_values('인기도', ascending=False).head(5)
    
    if chart_type == "막대 차트":
        fig = px.bar(season_materials, x='소재', y='인기도', color='소재',
                    title=f"{selected_season} 시즌 인기 소재 Top 5")
    elif chart_type == "레이더 차트":
        fig = go.Figure()
        fig.add_trace(go.Scatterpolar(
            r=season_materials['인기도'].tolist(),
            theta=season_materials['소재'].tolist(),
            fill='toself',
            name=selected_season
        ))
        fig.update_layout(
            polar=dict(radialaxis=dict(visible=True, range=[0, 100])),
            showlegend=False,
            title=f"{selected_season} 인기 소재 분포"
        )
    else:  # 히트맵
        pivot_materials = material_df.pivot(index='소재', columns='시즌', values='인기도')
        fig = px.imshow(pivot_materials, text_auto=True, aspect="auto", color_continuous_scale='Viridis',
                       title=f"시즌별 소재 인기도 히트맵")
    
    fig.update_layout(height=300)
    st.plotly_chart(fig, use_container_width=True)
    st.markdown("</div>", unsafe_allow_html=True)
    
    # 인기 패턴 차트
    st.markdown("<div class='card'>", unsafe_allow_html=True)
    st.subheader("인기 패턴")
    season_patterns = pattern_df[pattern_df['시즌'] == selected_season].sort_values('인기도', ascending=False).head(5)
    
    if chart_type == "막대 차트":
        fig = px.bar(season_patterns, x='패턴', y='인기도', color='패턴',
                    title=f"{selected_season} 시즌 인기 패턴 Top 5")
    elif chart_type == "레이더 차트":
        fig = go.Figure()
        fig.add_trace(go.Scatterpolar(
            r=season_patterns['인기도'].tolist(),
            theta=season_patterns['패턴'].tolist(),
            fill='toself',
            name=selected_season
        ))
        fig.update_layout(
            polar=dict(radialaxis=dict(visible=True, range=[0, 100])),
            showlegend=False,
            title=f"{selected_season} 인기 패턴 분포"
        )
    else:  # 히트맵
        pivot_patterns = pattern_df.pivot(index='패턴', columns='시즌', values='인기도')
        fig = px.imshow(pivot_patterns, text_auto=True, aspect="auto", color_continuous_scale='Viridis',
                       title=f"시즌별 패턴 인기도 히트맵")
    
    fig.update_layout(height=300)
    st.plotly_chart(fig, use_container_width=True)
    st.markdown("</div>", unsafe_allow_html=True)

# 하단 정보
st.markdown("---")

# 시즌 요약 통계
st.subheader("시즌 통계 요약")
col1, col2, col3, col4 = st.columns(4)

with col1:
    st.metric("키워드 수", len(keywords[selected_season]))
with col2:
    avg_keyword = np.mean(list(keywords[selected_season].values()))
    st.metric("평균 키워드 인기도", f"{avg_keyword:.1f}")
with col3:
    top_color = season_colors.iloc[0]['색상']
    st.metric("인기 색상", top_color)
with col4:
    top_material = season_materials.iloc[0]['소재']
    st.metric("인기 소재", top_material)

# 앱 정보
st.caption("이 대시보드는 가상의 데이터를 사용하여 만들어졌습니다. 실제 패션 트렌드 데이터를 활용하면 보다 정확한 분석이 가능합니다.")
st.caption(f"마지막 업데이트: {datetime.now().strftime('%Y-%m-%d %H:%M')}")

# 앱 실행 방법 안내
with st.expander("앱 실행 방법"):
    st.code("""
    # 필요한 라이브러리 설치
    pip install streamlit pandas numpy plotly
    
    # 앱 실행
    streamlit run fashion_trend_dashboard.py
    """) 

태그: #스트림릿입코딩