TIL

TIL - 실전 프로젝트

pys6341 2025. 3. 7. 20:47

대시보드 

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

# Streamlit 대시보드 UI 설정
st.markdown("<h2 style='text-align: center;'>✨ Chill & NASA RUL 예측 서비스 ✨</h2>", unsafe_allow_html=True)

# 파일 업로드 기능
uploaded_file = st.file_uploader("📂 CSV 파일을 업로드하세요", type=["csv"])

if uploaded_file is not None:
    # 데이터 로드
    df = pd.read_csv(uploaded_file)
    
    # 온도 및 컷오프 전압 선택
    available_temps = df["ambient_temperature"].unique()
    available_cutoffs = df["discharge_voltage"].unique()
    
    col1, col2 = st.columns(2)
    with col1:
        selected_temp = st.selectbox("🌡️ 온도를 선택하세요 (°C)", sorted(available_temps))
    with col2:
        selected_cutoff = st.selectbox("🔋 컷오프 전압을 선택하세요 (V)", sorted(available_cutoffs))
    
    # 선택된 조건에 맞는 데이터 필터링
    filtered_df = df[(df["ambient_temperature"] == selected_temp) & 
                     (df["discharge_voltage"] == selected_cutoff)]
    
    if filtered_df.empty:
        st.warning("⚠️ 선택한 조건에 해당하는 데이터가 없습니다. 다른 값을 선택해 주세요.")
    else:
        # Cycle을 인덱스로 설정
        filtered_df.set_index("Cycle", inplace=True)
        soh_series = filtered_df["SOH"].dropna()

        # SOH 최대값 및 80% 임계값 계산
        soh_max = soh_series.max()
        soh_threshold = soh_max * 0.8

        # SOH 80% 이하가 되는 첫 번째 싸이클 찾기 (EOL)
        below_threshold_cycles = soh_series[soh_series.index > soh_series.idxmax()]
        below_threshold_cycles = below_threshold_cycles[below_threshold_cycles <= soh_threshold]
        
        EOL_cycle = None
        if not below_threshold_cycles.empty:
            EOL_cycle = int(below_threshold_cycles.index[0])
        
        # 현재 싸이클을 데이터의 마지막 싸이클로 설정
        current_cycle = int(soh_series.index[-1])
        
        # 현재 싸이클과 RUL 계산
        RUL = EOL_cycle - current_cycle if EOL_cycle is not None else None
        
        # 🔹 현재 사이클 & 예상 RUL 표시
        st.subheader("🔢 배터리 상태")
        col3, col4 = st.columns(2)
        with col3:
            st.info(f"🔄 현재 사이클: **{current_cycle} 회**")
        with col4:
            if RUL is not None:
                st.success(f"📌 예상 RUL: **{RUL} 회**")
            else:
                st.error("⚠️ RUL을 계산할 수 없습니다.")

        if RUL is not None:
            # 🔹 1사이클 사용 가능 시간 (초)
            CYCLE_DURATION = 10496  
            
            # 🔹 사용자 입력 (하루 평균 사용 시간)
            daily_usage = st.number_input("⏳ 하루 평균 사용 시간 (초)", min_value=1, value=36000, step=1)

            # 🔹 남은 사용 가능 일수 계산
            if daily_usage > 0:
                remaining_days = (RUL * CYCLE_DURATION) / daily_usage

                # 🔹 연간 교체 개수 계산
                annual_replacements = 365 / remaining_days

                # 🔹 연간 교체 비용 계산 (배터리 가격 5,000원)
                BATTERY_PRICE = 5000  
                annual_cost = annual_replacements * BATTERY_PRICE

                # 🔹 남은 사용 가능 일수 & 연간 교체 비용 나란히 출력
                st.subheader("📅 배터리 사용 예측")
                col5, col6 = st.columns(2)
                with col5:
                    st.success(f"📅 남은 사용 가능 일수: **{remaining_days:.2f} 일**")
                with col6:
                    st.warning(f"💵 예상 연간 교체 비용: **{annual_cost:,.0f} 원**")

                # 🔹 예상 배터리 교체 날짜 계산 (오늘 날짜 기준)
                expected_replacement_date = datetime.date.today() + datetime.timedelta(days=remaining_days)

                # 🔹 FullCalendar 스타일 달력 추가 (예상 교체일 이벤트 추가)
                st.subheader("📆 예상 배터리 교체일 (캘린더)")

                calendar_html = f"""
                <html>
                <head>
                <link href='https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.css' rel='stylesheet' />
                <script src='https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.js'></script>
                <script>
                document.addEventListener('DOMContentLoaded', function() {{
                    var calendarEl = document.getElementById('calendar');
                    var calendar = new FullCalendar.Calendar(calendarEl, {{
                        initialView: 'dayGridMonth',
                        initialDate: '{expected_replacement_date.strftime('%Y-%m-%d')}',
                        events: [
                            {{
                                title: '🔋 예상 교체일',
                                start: '{expected_replacement_date.strftime('%Y-%m-%d')}',
                                color: '#FF5733'
                            }}
                        ]
                    }});
                    calendar.render();
                }});
                </script>
                </head>
                <body>
                <div id='calendar'></div>
                </body>
                </html>
                """

                # 🔹 Streamlit에서 캘린더 표시
                st.components.v1.html(calendar_html, height=600)

                # 🔹 예상 교체일 출력
                st.info(f"📌 예상 배터리 교체일: **{expected_replacement_date.strftime('%Y-%m-%d')}**")

            else:
                st.error("⚠️ 하루 평균 사용 시간은 0보다 커야 합니다!")

'TIL' 카테고리의 다른 글

TIL - 실무에 쓰는 머신러닝 기초 1-2  (0) 2025.03.14
TIL - 실무에 쓰는 머신러닝 기초 1-1  (0) 2025.03.12
TIL - 실전 프로젝트  (0) 2025.03.06
TIL - 실전 프로젝트  (0) 2025.03.05
TIL - 실전 프로젝트  (0) 2025.03.04