JUNGU commited on
Commit
a63a0bc
·
verified ·
1 Parent(s): a748e57

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +86 -80
src/streamlit_app.py CHANGED
@@ -6,91 +6,97 @@ import seaborn as sns
6
  from scipy.stats import norm, skew
7
  import platform
8
 
9
- # 한글 폰트 설정 (Windows, Mac, Linux 환경에 맞게 자동 설정)
10
- if platform.system() == 'Windows':
11
- plt.rc('font', family='Malgun Gothic')
12
- elif platform.system() == 'Darwin': # Mac
13
- plt.rc('font', family='AppleGothic')
14
- else: # Linux
15
- # 나눔고딕 폰트가 설치되어 있어야 합니다.
16
- # sudo apt-get install fonts-nanum*
17
- plt.rc('font', family='NanumGothic')
18
-
19
- plt.rcParams['axes.unicode_minus'] = False # 마이너스 폰트 깨짐 방지
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  def main():
22
- """
23
- 스트림릿을 이용한 학생 점수 분포 분석 애플리케이션
24
- """
25
  st.title("학생 점수 분포 분석 도구 📊")
26
- st.write("CSV 파일을 업로드하여 학생들의 점수 분포를 확인하고, 정규분포와의 차이 왜도(skewness)를 분석합니다.")
27
  st.write("---")
28
 
29
- # 파일 업로드 위젯
30
- uploaded_file = st.file_uploader("점수 데이터가 포함된 CSV 파일을 업로드하세요.", type="csv")
31
-
32
- if uploaded_file is not None:
33
- try:
34
- # utf-8-sig 인코딩으로 CSV 파일 읽기
35
- df = pd.read_csv(uploaded_file, encoding='utf-8-sig')
36
-
37
- st.subheader("업로드된 데이터 미리보기")
38
- st.dataframe(df.head())
39
-
40
- # 분석할 점수 선택
41
- score_column = st.selectbox("분석할 점수 열(column)을 선택하세요:", df.columns)
42
-
43
- if score_column:
44
- # 선택된 열의 데이터 추출 (결측치 제거)
45
- scores = df[score_column].dropna()
46
-
47
- if pd.api.types.is_numeric_dtype(scores):
48
- st.subheader(f"'{score_column}' 점수 분포 분석 결과")
49
-
50
- # 1. 기술 통계량 표시
51
- st.write("#### 📈 기술 통계량")
52
- st.table(scores.describe())
53
-
54
- # 2. 분포 시각화 정규분포 비교
55
- st.write("#### 🎨 점수 분포 시각화")
56
- fig, ax = plt.subplots(figsize=(10, 6))
57
-
58
- # 히스토그램 및 KDE 플롯
59
- sns.histplot(scores, kde=True, stat='density', label='학생 점수 분포', ax=ax)
60
-
61
- # 정규분포 곡선 추가
62
- mu, std = norm.fit(scores)
63
- xmin, xmax = plt.xlim()
64
- x = np.linspace(xmin, xmax, 100)
65
- p = norm.pdf(x, mu, std)
66
- ax.plot(x, p, 'k', linewidth=2, label='정규분포 곡선')
67
-
68
- title = f"'{score_column}' 점수 분포 (평균: {mu:.2f}, 표준편차: {std:.2f})"
69
- ax.set_title(title)
70
- ax.set_xlabel('점수')
71
- ax.set_ylabel('밀도')
72
- ax.legend()
73
- st.pyplot(fig)
74
-
75
- # 3. 왜도(Skewness) 계산 및 해석
76
- st.write("#### 📐 왜도 (Skewness) 분석")
77
- skewness = skew(scores)
78
- st.metric(label="왜도 (Skewness)", value=f"{skewness:.4f}")
79
-
80
- if skewness > 0.5:
81
- st.info("꼬리가 오른쪽으로 긴 분포 (Positive Skew): 대부분의 학생들이 평균보다 낮은 점수에 몰려있고, 일부 학생들이 매우 높은 점수를 받았습니다.")
82
- elif skewness < -0.5:
83
- st.info("꼬리가 왼쪽으로 긴 분포 (Negative Skew): 대부분의 학생들이 평균보다 높은 점수에 몰려있고, 일부 학생들이 매우 낮은 점수를 받았습니다.")
84
- else:
85
- st.info("대칭에 가까운 분포: 점수가 평균을 중심으로 비교적 고르게 분포되어 있습니다.")
86
-
87
- else:
88
- st.error(f"오류: 선택하신 '{score_column}' 열은 숫자 데이터가 아닙니다. 숫자 데이터로 구성된 열을 선택해주세요.")
89
-
90
- except Exception as e:
91
- st.error(f"파일을 읽는 도중 오류가 발생했습니다: {e}")
92
- st.warning("CSV 파일이 'utf-8-sig' 또는 'utf-8' 인코딩 형식인지 확인해주세요.")
93
-
94
 
95
  if __name__ == '__main__':
96
  main()
 
6
  from scipy.stats import norm, skew
7
  import platform
8
 
9
+ # 한글 폰트 설정 (다양한 OS 환경 지원)
10
+ def set_korean_font():
11
+ if platform.system() == 'Windows':
12
+ plt.rc('font', family='Malgun Gothic')
13
+ elif platform.system() == 'Darwin': # Mac
14
+ plt.rc('font', family='AppleGothic')
15
+ else: # Linux
16
+ # 나눔고딕 폰트가 설치되어 있어야 합니다.
17
+ # 터미널에서 `sudo apt-get install -y fonts-nanum*` 실행
18
+ try:
19
+ plt.rc('font', family='NanumGothic')
20
+ except:
21
+ st.warning("나눔고딕 폰트가 설치되어 있지 않아 한글이 깨질 수 있습니다. 'sudo apt-get install -y fonts-nanum*' 명령어로 폰트를 설치해주세요.")
22
+ plt.rcParams['axes.unicode_minus'] = False # 마이너스 폰트 깨짐 방지
23
+
24
+ def analyze_scores(df):
25
+ """데이터프레임을 받아 분석 결과를 표시하는 함수"""
26
+ st.subheader("데이터 미리보기 (상위 5개)")
27
+ st.dataframe(df.head())
28
+
29
+ # 분석할 점수 열 선택
30
+ score_column = st.selectbox("분석할 점수 열(column)을 선택하세요:", df.columns)
31
+
32
+ if score_column:
33
+ scores = df[score_column].dropna()
34
+
35
+ if pd.api.types.is_numeric_dtype(scores):
36
+ st.subheader(f"'{score_column}' 점수 분포 분석 결과")
37
+
38
+ # 1. 기술 통계량
39
+ st.write("#### 📈 기술 통계량")
40
+ st.table(scores.describe())
41
+
42
+ # 2. 분포 시각화
43
+ st.write("#### 🎨 점수 분포 시각화")
44
+ fig, ax = plt.subplots(figsize=(10, 6))
45
+ sns.histplot(scores, kde=True, stat='density', label='학생 점수 분포', ax=ax)
46
+ mu, std = norm.fit(scores)
47
+ xmin, xmax = plt.xlim()
48
+ x = np.linspace(xmin, xmax, 100)
49
+ p = norm.pdf(x, mu, std)
50
+ ax.plot(x, p, 'k', linewidth=2, label='정규분포 곡선')
51
+ ax.set_title(f"'{score_column}' 점수 분포 (평균: {mu:.2f}, 표준편차: {std:.2f})")
52
+ ax.set_xlabel('점수'); ax.set_ylabel('밀도'); ax.legend()
53
+ st.pyplot(fig)
54
+
55
+ # 3. 왜도(Skewness) 분석
56
+ st.write("#### 📐 왜도 (Skewness) 분석")
57
+ skewness = skew(scores)
58
+ st.metric(label="왜도 (Skewness)", value=f"{skewness:.4f}")
59
+ if skewness > 0.5:
60
+ st.info("꼬리가 오른쪽으로 긴 분포 (Positive Skew): 대부분의 학생들이 평균보다 낮은 점수에 몰려있고, 일부 고득점자들이 평균을 높이고 있습니다.")
61
+ elif skewness < -0.5:
62
+ st.info("꼬리가 왼쪽으로 긴 분포 (Negative Skew): 대부분의 학생들이 평균보다 높은 점수에 몰려있고, 일부 저득점자들이 평균을 낮추고 있습니다.")
63
+ else:
64
+ st.info("대칭에 가까운 분포: 점수가 평균을 중심으로 비교적 고르게 분포되어 있습니다.")
65
+ else:
66
+ st.error(f"오류: 선택하신 '{score_column}' 열은 숫자 데이터가 아닙니다. 숫자 형식의 열을 선택해주세요.")
67
 
68
  def main():
69
+ set_korean_font()
 
 
70
  st.title("학생 점수 분포 분석 도구 📊")
71
+ st.write("CSV 파일을 직접 업로드하거나 Google Sheets URL을 붙여넣어 학생 점수 분포를 분석합니다.")
72
  st.write("---")
73
 
74
+ st.sidebar.title("데이터 가져오기")
75
+ source_option = st.sidebar.radio("데이터 소스를 선택하세요:", ("Google Sheets URL", "CSV 파일 업로드"))
76
+
77
+ df = None
78
+
79
+ if source_option == "Google Sheets URL":
80
+ url = st.sidebar.text_input("웹에 게시된 Google Sheets CSV URL을 입력하세요.")
81
+ if url:
82
+ try:
83
+ df = pd.read_csv(url)
84
+ except Exception as e:
85
+ st.error(f"URL로부터 데이터를 읽는 오류가 발생했습니다: {e}")
86
+ st.warning("올바른 Google Sheets '웹 게시' CSV URL인지 확인해주세요.")
87
+
88
+ elif source_option == "CSV 파일 업로드":
89
+ uploaded_file = st.sidebar.file_uploader("CSV 파일을 업로드하세요.", type="csv")
90
+ if uploaded_file:
91
+ try:
92
+ df = pd.read_csv(uploaded_file, encoding='utf-8-sig')
93
+ except Exception as e:
94
+ st.error(f"파일을 읽는 중 오류가 발생했습니다: {e}")
95
+
96
+ if df is not None:
97
+ analyze_scores(df)
98
+ else:
99
+ st.info("사이드바에서 데이터 소스를 선택하고 데이터를 불러와주세요.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  if __name__ == '__main__':
102
  main()