Spaces:
Running
Running
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 | |
import os | |
import matplotlib.font_manager as fm # [์์ 1] ๋๋ฝ๋์๋ ํฐํธ ๊ด๋ฆฌ์ ๋ชจ๋ import | |
# --- set_korean_font ํจ์๋ฅผ ์๋ ์ฝ๋๋ก ๊ต์ฒดํด์ฃผ์ธ์ --- | |
def set_korean_font(): | |
""" | |
Hugging Face Space ๋๋ Streamlit Cloud ํ๊ฒฝ์์ ํ๊ธ ํฐํธ๋ฅผ ์์ ์ ์ผ๋ก ๋ก๋ํฉ๋๋ค. | |
1. ์ง์ ๋ ๊ฒฝ๋ก์ ํฐํธ ํ์ผ์ด ์๋์ง ํ์ธํฉ๋๋ค. | |
2. ํ์ผ์ด ์๋ค๋ฉด ๊ฒฝ๊ณ ๋ฅผ ํ์ํฉ๋๋ค. | |
3. ํ์ผ์ด ์๋ค๋ฉด ํด๋น ํฐํธ๋ฅผ matplotlib์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ค์ ํฉ๋๋ค. | |
4. ํน์ ๋ชจ๋ฅผ ์บ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์บ์๋ฅผ ์ฌ์์ฑํ๋ ์ต์ ์ ๊ณ ๋ คํ ์ ์์ต๋๋ค. | |
""" | |
# ํฐํธ ํ์ผ ์ด๋ฆ ์ง์ | |
font_filename = "NanumGaRamYeonGgoc.ttf" | |
# ์์คํ ํ๊ฒฝ์ ๋ฐ๋ผ ํฐํธ ๊ฒฝ๋ก ํ์ | |
# (Hugging Face, Streamlit Cloud์ ๊ฐ์ ๋ฆฌ๋ ์ค ๊ธฐ๋ฐ ํ๊ฒฝ์ ๊ฐ์ ) | |
font_path = None | |
if os.path.exists(font_path): | |
try: | |
# ํฐํธ ํ๋กํผํฐ๋ฅผ ์ค์ ํ๊ณ matplotlib์ rcParams์ ๋ฑ๋ก | |
font_prop = fm.FontProperties(fname=font_path) | |
fm.fontManager.addfont(font_path) # ์บ์์ ํฐํธ ์ถ๊ฐ | |
plt.rc('font', family=font_prop.get_name()) | |
# ์ฑ๊ณต ๋ฉ์์ง ํ์ (Streamlit ๋ ๋๋ง ๊ณผ์ ์์ ์ค๋ฅ ๋ฐฉ์ง) | |
try: | |
st.sidebar.success(f"'{font_prop.get_name()}' ํฐํธ ๋ก๋ฉ ์ฑ๊ณต!") | |
except Exception: | |
pass | |
except Exception as e: | |
try: | |
st.sidebar.error(f"ํฐํธ ๋ก๋ฉ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {e}") | |
except Exception: | |
pass | |
else: | |
# ํฐํธ ํ์ผ์ด ์์ ๊ฒฝ์ฐ ๊ฒฝ๊ณ ๋ฉ์์ง | |
try: | |
st.sidebar.warning(f"ํฐํธ ํ์ผ({font_filename})์ ์ฐพ์ ์ ์์ต๋๋ค. ์ฑ์ ๋ฃจํธ ๋๋ ํ ๋ฆฌ์ ํฐํธ ํ์ผ์ ์ ๋ก๋ํ์ธ์.") | |
# matplotlib์ ํฐํธ ์บ์๋ฅผ ๊ฐ์ ๋ก ๋ค์ ๋น๋ (๋๋ฒ๊น ์ฉ) | |
# st.sidebar.info("Matplotlib ํฐํธ ์บ์๋ฅผ ์ฌ๋น๋ํฉ๋๋ค...") | |
# fm._rebuild() | |
# st.sidebar.info("์บ์ ์ฌ๋น๋ ์๋ฃ. ์ฑ์ ์๋ก๊ณ ์นจํ์ธ์.") | |
except Exception: | |
pass | |
# ๋ง์ด๋์ค ๋ถํธ๊ฐ ๊นจ์ง๋ ํ์ ๋ฐฉ์ง | |
plt.rcParams['axes.unicode_minus'] = False | |
# --- ์ ์ ๋ถ์ ํจ์ --- | |
def analyze_scores(df): | |
"""๋ฐ์ดํฐํ๋ ์์ ๋ฐ์ ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ํ๋ ํจ์""" | |
st.subheader("๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ (์์ 5๊ฐ)") | |
st.dataframe(df.head()) | |
# ์ซ์ ํ์์ ์ด๋ง ์ ํ์ง๋ก ์ ๊ณตํ์ฌ ์ค๋ฅ ๋ฐฉ์ง | |
numeric_columns = df.select_dtypes(include=np.number).columns.tolist() | |
if not numeric_columns: | |
st.error("๋ฐ์ดํฐ์์ ๋ถ์ ๊ฐ๋ฅํ ์ซ์ ํ์์ ์ด์ ์ฐพ์ ์ ์์ต๋๋ค.") | |
return | |
score_column = st.selectbox("๋ถ์ํ ์ ์ ์ด(column)์ ์ ํํ์ธ์:", numeric_columns) | |
if score_column: | |
scores = df[score_column].dropna() | |
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("๋์นญ์ ๊ฐ๊น์ด ๋ถํฌ: ์ ์๊ฐ ํ๊ท ์ ์ค์ฌ์ผ๋ก ๋น๊ต์ ๊ณ ๋ฅด๊ฒ ๋ถํฌ๋์ด ์์ต๋๋ค.") | |
# --- ๋ฉ์ธ ์คํ ํจ์ --- | |
def main(): | |
st.set_page_config(layout="wide") # ํ์ด์ง ๋ ์ด์์์ ๋๊ฒ ์ค์ | |
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": | |
sample_url = "https://docs.google.com/spreadsheets/d/e/2PACX-1vQ2Z8kzJq2sM7w2_9gXo-jZ-mO5o-BvC-w5p2nJ6oJ7oJ9xL-w3kZ9j5Z3kX7vN1aQ4mB1cW8jB7fR/pub?gid=0&single=true&output=csv" | |
url = st.sidebar.text_input("์น์ ๊ฒ์๋ Google Sheets CSV URL", value=sample_url) | |
if url: | |
try: | |
df = pd.read_csv(url) | |
except Exception as e: | |
st.error(f"URL๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {e}") | |
st.warning("์ฌ๋ฐ๋ฅธ Google Sheets '์น ๊ฒ์' CSV URL์ธ์ง ํ์ธํด์ฃผ์ธ์.") | |
# [์์ 2] elif์ ๋ค์ฌ์ฐ๊ธฐ๋ฅผ ์์ ํ์ฌ if์ ๊ฐ์ ๋ ๋ฒจ๋ก ๋ง์ถค | |
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() |