import streamlit as st import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from scipy.stats import norm, skew import platform # 한글 폰트 설정 (다양한 OS 환경 지원) def set_korean_font(): if platform.system() == 'Windows': plt.rc('font', family='Malgun Gothic') elif platform.system() == 'Darwin': # Mac plt.rc('font', family='AppleGothic') else: # Linux # 나눔고딕 폰트가 설치되어 있어야 합니다. # 터미널에서 `sudo apt-get install -y fonts-nanum*` 실행 try: plt.rc('font', family='NanumGothic') except: st.warning("나눔고딕 폰트가 설치되어 있지 않아 한글이 깨질 수 있습니다. 'sudo apt-get install -y fonts-nanum*' 명령어로 폰트를 설치해주세요.") plt.rcParams['axes.unicode_minus'] = False # 마이너스 폰트 깨짐 방지 def analyze_scores(df): """데이터프레임을 받아 분석 결과를 표시하는 함수""" st.subheader("데이터 미리보기 (상위 5개)") st.dataframe(df.head()) # 분석할 점수 열 선택 score_column = st.selectbox("분석할 점수 열(column)을 선택하세요:", df.columns) if score_column: scores = df[score_column].dropna() if pd.api.types.is_numeric_dtype(scores): st.subheader(f"'{score_column}' 점수 분포 분석 결과") # 1. 기술 통계량 st.write("#### 📈 기술 통계량") st.table(scores.describe()) # 2. 분포 시각화 st.write("#### 🎨 점수 분포 시각화") fig, ax = plt.subplots(figsize=(10, 6)) sns.histplot(scores, kde=True, stat='density', label='학생 점수 분포', ax=ax) mu, std = norm.fit(scores) xmin, xmax = plt.xlim() x = np.linspace(xmin, xmax, 100) p = norm.pdf(x, mu, std) ax.plot(x, p, 'k', linewidth=2, label='정규분포 곡선') ax.set_title(f"'{score_column}' 점수 분포 (평균: {mu:.2f}, 표준편차: {std:.2f})") ax.set_xlabel('점수'); ax.set_ylabel('밀도'); ax.legend() st.pyplot(fig) # 3. 왜도(Skewness) 분석 st.write("#### 📐 왜도 (Skewness) 분석") skewness = skew(scores) st.metric(label="왜도 (Skewness)", value=f"{skewness:.4f}") if skewness > 0.5: st.info("꼬리가 오른쪽으로 긴 분포 (Positive Skew): 대부분의 학생들이 평균보다 낮은 점수에 몰려있고, 일부 고득점자들이 평균을 높이고 있습니다.") elif skewness < -0.5: st.info("꼬리가 왼쪽으로 긴 분포 (Negative Skew): 대부분의 학생들이 평균보다 높은 점수에 몰려있고, 일부 저득점자들이 평균을 낮추고 있습니다.") else: st.info("대칭에 가까운 분포: 점수가 평균을 중심으로 비교적 고르게 분포되어 있습니다.") else: st.error(f"오류: 선택하신 '{score_column}' 열은 숫자 데이터가 아닙니다. 숫자 형식의 열을 선택해주세요.") def main(): set_korean_font() st.title("학생 점수 분포 분석 도구 📊") st.write("CSV 파일을 직접 업로드하거나 Google Sheets URL을 붙여넣어 학생 점수 분포를 분석합니다.") st.write("---") st.sidebar.title("데이터 가져오기") source_option = st.sidebar.radio("데이터 소스를 선택하세요:", ("Google Sheets URL", "CSV 파일 업로드")) df = None if source_option == "Google Sheets URL": url = st.sidebar.text_input("웹에 게시된 Google Sheets CSV URL을 입력하세요.") if url: try: df = pd.read_csv(url) except Exception as e: st.error(f"URL로부터 데이터를 읽는 중 오류가 발생했습니다: {e}") st.warning("올바른 Google Sheets '웹 게시' CSV URL인지 확인해주세요.") elif source_option == "CSV 파일 업로드": uploaded_file = st.sidebar.file_uploader("CSV 파일을 업로드하세요.", type="csv") if uploaded_file: try: df = pd.read_csv(uploaded_file, encoding='utf-8-sig') except Exception as e: st.error(f"파일을 읽는 중 오류가 발생했습니다: {e}") if df is not None: analyze_scores(df) else: st.info("사이드바에서 데이터 소스를 선택하고 데이터를 불러와주세요.") if __name__ == '__main__': main()