hossam 0.3.19__py3-none-any.whl → 0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hossam/__init__.py +19 -22
- hossam/data_loader.py +16 -10
- hossam/hs_classroom.py +69 -44
- hossam/hs_gis.py +10 -6
- hossam/hs_plot.py +153 -150
- hossam/hs_prep.py +95 -85
- hossam/hs_stats.py +426 -548
- hossam/hs_timeserise.py +161 -152
- hossam/hs_util.py +44 -17
- {hossam-0.3.19.dist-info → hossam-0.4.dist-info}/METADATA +6 -107
- hossam-0.4.dist-info/RECORD +16 -0
- hossam/mcp/__init__.py +0 -12
- hossam/mcp/hs_classroom.py +0 -22
- hossam/mcp/hs_gis.py +0 -30
- hossam/mcp/hs_plot.py +0 -53
- hossam/mcp/hs_prep.py +0 -61
- hossam/mcp/hs_stats.py +0 -25
- hossam/mcp/hs_timeserise.py +0 -22
- hossam/mcp/hs_util.py +0 -30
- hossam/mcp/loader.py +0 -29
- hossam/mcp/server.py +0 -675
- hossam-0.3.19.dist-info/RECORD +0 -27
- hossam-0.3.19.dist-info/entry_points.txt +0 -2
- {hossam-0.3.19.dist-info → hossam-0.4.dist-info}/WHEEL +0 -0
- {hossam-0.3.19.dist-info → hossam-0.4.dist-info}/licenses/LICENSE +0 -0
- {hossam-0.3.19.dist-info → hossam-0.4.dist-info}/top_level.txt +0 -0
hossam/__init__.py
CHANGED
|
@@ -1,33 +1,30 @@
|
|
|
1
|
+
# submodules
|
|
2
|
+
from . import hs_classroom
|
|
3
|
+
from . import hs_gis
|
|
4
|
+
from . import hs_plot
|
|
5
|
+
from . import hs_prep
|
|
6
|
+
from . import hs_stats
|
|
7
|
+
from . import hs_timeserise
|
|
8
|
+
from . import hs_util
|
|
1
9
|
from .data_loader import load_data, load_info
|
|
2
|
-
|
|
10
|
+
|
|
11
|
+
# py-modules
|
|
12
|
+
import sys
|
|
13
|
+
import warnings
|
|
3
14
|
from matplotlib import pyplot as plt
|
|
4
15
|
from matplotlib import font_manager as fm
|
|
5
16
|
from importlib.resources import files, as_file
|
|
6
17
|
from importlib.metadata import version
|
|
7
|
-
from types import SimpleNamespace
|
|
8
|
-
import sys
|
|
9
|
-
import warnings
|
|
10
18
|
|
|
11
19
|
try:
|
|
12
20
|
__version__ = version("hossam")
|
|
13
21
|
except Exception:
|
|
14
22
|
__version__ = "develop"
|
|
15
23
|
|
|
24
|
+
__all__ = ["load_data", "load_info", "hs_classroom", "hs_gis", "hs_plot", "hs_prep", "hs_stats", "hs_timeserise", "hs_util"]
|
|
16
25
|
|
|
17
|
-
hs_fig
|
|
18
|
-
|
|
19
|
-
width=800,
|
|
20
|
-
height=450,
|
|
21
|
-
font_size=9.5,
|
|
22
|
-
font_weight="normal",
|
|
23
|
-
frame_width=0.7,
|
|
24
|
-
line_width=1.5,
|
|
25
|
-
grid_alpha=0.3,
|
|
26
|
-
grid_width=0.5,
|
|
27
|
-
fill_alpha=0.3
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
__all__ = ["load_data", "load_info", "hs_classroom", "hs_gis", "hs_plot", "hs_prep", "hs_stats", "hs_timeserise", "hs_util", "hs_fig"]
|
|
26
|
+
# 내부 모듈에서 hs_fig를 사용할 때는 아래와 같이 import 하세요.
|
|
27
|
+
# from hossam import hs_fig
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
def _init_korean_font():
|
|
@@ -44,14 +41,14 @@ def _init_korean_font():
|
|
|
44
41
|
|
|
45
42
|
plt.rcParams.update({
|
|
46
43
|
"font.family": fname,
|
|
47
|
-
"font.size":
|
|
48
|
-
"font.weight":
|
|
44
|
+
"font.size": hs_plot.config.font_size,
|
|
45
|
+
"font.weight": hs_plot.config.font_weight,
|
|
49
46
|
"axes.unicode_minus": False,
|
|
50
47
|
"text.antialiased": True,
|
|
51
48
|
"lines.antialiased": True,
|
|
52
49
|
"patch.antialiased": True,
|
|
53
|
-
"figure.dpi":
|
|
54
|
-
"savefig.dpi":
|
|
50
|
+
"figure.dpi": hs_plot.config.dpi,
|
|
51
|
+
"savefig.dpi": hs_plot.config.dpi * 2,
|
|
55
52
|
"text.hinting": "auto",
|
|
56
53
|
"text.hinting_factor": 8,
|
|
57
54
|
"pdf.fonttype": 42,
|
hossam/data_loader.py
CHANGED
|
@@ -39,7 +39,8 @@ def __get_df(path: str, index_col=None) -> DataFrame:
|
|
|
39
39
|
hs_pretty_table(info)
|
|
40
40
|
print()
|
|
41
41
|
except Exception:
|
|
42
|
-
print(f"\033[91m[!] Cannot read metadata\033[0m")
|
|
42
|
+
#print(f"\033[91m[!] Cannot read metadata\033[0m")
|
|
43
|
+
pass
|
|
43
44
|
else:
|
|
44
45
|
df = read_excel(path, index_col=index_col)
|
|
45
46
|
|
|
@@ -51,7 +52,8 @@ def __get_df(path: str, index_col=None) -> DataFrame:
|
|
|
51
52
|
hs_pretty_table(info)
|
|
52
53
|
print()
|
|
53
54
|
except:
|
|
54
|
-
print(f"\033[91m[!] Cannot read metadata\033[0m")
|
|
55
|
+
#print(f"\033[91m[!] Cannot read metadata\033[0m")
|
|
56
|
+
pass
|
|
55
57
|
else:
|
|
56
58
|
df = read_csv(path, index_col=index_col)
|
|
57
59
|
|
|
@@ -105,10 +107,11 @@ def load_info(search: str = None, local: str = None) -> DataFrame:
|
|
|
105
107
|
DataFrame: name, chapter, desc, url 컬럼을 갖는 테이블
|
|
106
108
|
|
|
107
109
|
Examples:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
['name', 'chapter', 'desc', 'url']
|
|
110
|
+
```python
|
|
111
|
+
from hossam import *
|
|
112
|
+
info = load_info()
|
|
113
|
+
list(info.columns) #['name', 'chapter', 'desc', 'url']
|
|
114
|
+
```
|
|
112
115
|
"""
|
|
113
116
|
global BASE_URL
|
|
114
117
|
|
|
@@ -168,8 +171,10 @@ def load_data(key: str, local: str = None) -> Optional[DataFrame]:
|
|
|
168
171
|
DataFrame | None: 성공 시 데이터프레임, 실패 시 None
|
|
169
172
|
|
|
170
173
|
Examples:
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
```python
|
|
175
|
+
from hossam import *
|
|
176
|
+
df = load_data('AD_SALES') # 메타데이터에 해당 키가 있어야 함
|
|
177
|
+
```
|
|
173
178
|
"""
|
|
174
179
|
index = None
|
|
175
180
|
try:
|
|
@@ -181,8 +186,9 @@ def load_data(key: str, local: str = None) -> Optional[DataFrame]:
|
|
|
181
186
|
print(e)
|
|
182
187
|
return
|
|
183
188
|
|
|
184
|
-
print("\033[94m[data]\033[0m", url.replace("\\", "/"))
|
|
185
|
-
print("\033[94m[desc]\033[0m", desc)
|
|
189
|
+
#print("\033[94m[data]\033[0m", url.replace("\\", "/"))
|
|
190
|
+
#print("\033[94m[desc]\033[0m", desc)
|
|
191
|
+
print(f"\033[94m{desc}\033[0m")
|
|
186
192
|
|
|
187
193
|
df = None
|
|
188
194
|
|
hossam/hs_classroom.py
CHANGED
|
@@ -8,8 +8,8 @@ from kmodes.kmodes import KModes
|
|
|
8
8
|
from matplotlib import pyplot as plt
|
|
9
9
|
import seaborn as sns
|
|
10
10
|
from .hs_util import load_data, pretty_table
|
|
11
|
-
from . import hs_fig
|
|
12
11
|
from . import hs_plot
|
|
12
|
+
from .hs_plot import config
|
|
13
13
|
|
|
14
14
|
# ===================================================================
|
|
15
15
|
# 학생들을 관심사와 성적으로 균형잡힌 조로 편성한다
|
|
@@ -50,13 +50,16 @@ def cluster_students(
|
|
|
50
50
|
ValueError: 필수 컬럼이 없거나 입력값이 유효하지 않은 경우.
|
|
51
51
|
|
|
52
52
|
Examples:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
```python
|
|
54
|
+
df = read_csv('students.csv')
|
|
55
|
+
|
|
56
|
+
from hossam import *
|
|
57
|
+
result = hs_classroom.cluster_students(
|
|
58
|
+
df=df,
|
|
59
|
+
n_groups=5,
|
|
60
|
+
score_cols=['국어', '영어', '수학'],
|
|
61
|
+
interest_col='관심사')
|
|
62
|
+
```
|
|
60
63
|
"""
|
|
61
64
|
|
|
62
65
|
# 파일 경로인 경우 데이터프레임으로 로드
|
|
@@ -89,6 +92,11 @@ def cluster_students(
|
|
|
89
92
|
|
|
90
93
|
# ===== 1단계: 점수 기반 처리 =====
|
|
91
94
|
if score_cols is not None:
|
|
95
|
+
# 결측치는 0점으로 대체
|
|
96
|
+
for s in score_cols:
|
|
97
|
+
df[s] = df[s].fillna(0)
|
|
98
|
+
print(df)
|
|
99
|
+
|
|
92
100
|
# 총점/평균점수 계산
|
|
93
101
|
df['총점'] = df[score_cols].sum(axis=1)
|
|
94
102
|
df['평균점수'] = df[score_cols].mean(axis=1)
|
|
@@ -97,12 +105,22 @@ def cluster_students(
|
|
|
97
105
|
metric_col = '총점' if (score_metric or '').lower() != 'average' else '평균점수'
|
|
98
106
|
|
|
99
107
|
# 성적사분위 분류 (선택한 기준 사용)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
quantiles = [0, 0.25, 0.50, 0.75, 1.0]
|
|
109
|
+
n_bins = len(quantiles) - 1
|
|
110
|
+
labels = [f"Q{i+1}" for i in range(n_bins)]
|
|
111
|
+
try:
|
|
112
|
+
df['성적사분위'] = qcut(
|
|
113
|
+
df[metric_col],
|
|
114
|
+
q=quantiles,
|
|
115
|
+
labels=labels,
|
|
116
|
+
duplicates='drop' # 중복된 값 처리
|
|
117
|
+
)
|
|
118
|
+
except ValueError:
|
|
119
|
+
# 구간이 줄어든 경우, bins 개수에 맞게 labels 재생성
|
|
120
|
+
import pandas as pd
|
|
121
|
+
bins = pd.qcut(df[metric_col], q=quantiles, duplicates='drop').cat.categories
|
|
122
|
+
labels = [f"Q{i+1}" for i in range(len(bins))]
|
|
123
|
+
df['성적사분위'] = pd.qcut(df[metric_col], q=quantiles, labels=labels, duplicates='drop')
|
|
106
124
|
|
|
107
125
|
# 성적그룹 매핑
|
|
108
126
|
df['성적그룹'] = df['성적사분위'].map({
|
|
@@ -374,21 +392,24 @@ def _balance_group_sizes_only(
|
|
|
374
392
|
# ===================================================================
|
|
375
393
|
# 조 편성 결과의 인원, 관심사, 점수 분포를 시각화한다
|
|
376
394
|
# ===================================================================
|
|
377
|
-
def report_summary(df: DataFrame, width: int =
|
|
395
|
+
def report_summary(df: DataFrame, interest_col: str = None, width: int = config.width, height: int = config.height, dpi: int = config.dpi) -> None:
|
|
378
396
|
"""조 편성 결과의 요약 통계를 시각화합니다.
|
|
379
397
|
|
|
380
398
|
조별 인원 분포, 관심사 분포, 평균점수 분포를 나타냅니다.
|
|
381
399
|
|
|
382
400
|
Args:
|
|
383
|
-
df: cluster_students 함수의 반환 결과 데이터프레임.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
401
|
+
df (DataFrame): cluster_students 함수의 반환 결과 데이터프레임.
|
|
402
|
+
interest_col (str): 관심사 컬럼명
|
|
403
|
+
width (int): 그래프 넓이. 기본값: config.width
|
|
404
|
+
height (int): 그래프 높이. 기본값: config.height
|
|
405
|
+
dpi (int): 그래프 해상도. 기본값: config.dpi
|
|
387
406
|
|
|
388
407
|
Examples:
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
408
|
+
```python
|
|
409
|
+
from hossam import *
|
|
410
|
+
df_result = hs_classroom.cluster_students(df, n_groups=5, score_cols=['국어', '영어', '수학'])
|
|
411
|
+
hs_classroom.report_summary(df_result)
|
|
412
|
+
```
|
|
392
413
|
"""
|
|
393
414
|
|
|
394
415
|
if df is None or len(df) == 0:
|
|
@@ -405,7 +426,7 @@ def report_summary(df: DataFrame, width: int = hs_fig.width, height: int = hs_fi
|
|
|
405
426
|
# 필요한 컬럼 확인
|
|
406
427
|
has_score = '총점' in df.columns
|
|
407
428
|
has_avg = '평균점수' in df.columns
|
|
408
|
-
has_interest = '관심사' in df.columns
|
|
429
|
+
has_interest = interest_col and '관심사' in df.columns
|
|
409
430
|
|
|
410
431
|
# 혼합 타입 안전 정렬 라벨 준비
|
|
411
432
|
labels = df['조'].unique().tolist()
|
|
@@ -525,7 +546,7 @@ def report_summary(df: DataFrame, width: int = hs_fig.width, height: int = hs_fi
|
|
|
525
546
|
# ===================================================================
|
|
526
547
|
# 조별 점수 분포를 커널 밀도 추정(KDE) 그래프로 시각화한다
|
|
527
548
|
# ===================================================================
|
|
528
|
-
def report_kde(df: DataFrame, metric: str = 'average', width: int =
|
|
549
|
+
def report_kde(df: DataFrame, metric: str = 'average', width: int = config.width, height: int = config.height, dpi: int = config.dpi) -> None:
|
|
529
550
|
"""조별 점수 분포를 KDE(Kernel Density Estimation)로 시각화합니다.
|
|
530
551
|
|
|
531
552
|
각 조의 점수 분포를 커널 밀도 추정으로 표시하고 평균 및 95% 신뢰구간을 나타냅니다.
|
|
@@ -534,14 +555,16 @@ def report_kde(df: DataFrame, metric: str = 'average', width: int = hs_fig.width
|
|
|
534
555
|
df: cluster_students 함수의 반환 결과 데이터프레임.
|
|
535
556
|
metric: 점수 기준 선택 ('total' 또는 'average').
|
|
536
557
|
'total'이면 총점, 'average'이면 평균점수. 기본값: 'average'
|
|
537
|
-
width: 그래프 넓이. 기본값:
|
|
538
|
-
height: 그래프 높이. 기본값:
|
|
539
|
-
dpi: 그래프 해상도. 기본값:
|
|
558
|
+
width: 그래프 넓이. 기본값: config.width
|
|
559
|
+
height: 그래프 높이. 기본값: config.height
|
|
560
|
+
dpi: 그래프 해상도. 기본값: config.dpi
|
|
540
561
|
|
|
541
562
|
Examples:
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
563
|
+
```python
|
|
564
|
+
from hossam import *
|
|
565
|
+
df_result = hs_classroom.cluster_students(df, n_groups=5, score_cols=['국어', '영어', '수학'])
|
|
566
|
+
hs_classroom.report_kde(df_result, metric='average')
|
|
567
|
+
```
|
|
545
568
|
"""
|
|
546
569
|
if df is None or len(df) == 0:
|
|
547
570
|
print("데이터프레임이 비어있습니다")
|
|
@@ -611,10 +634,12 @@ def group_summary(df: DataFrame, name_col: str = '학생이름') -> DataFrame:
|
|
|
611
634
|
컬럼: '조', '학생', '총점평균', '평균점수평균'
|
|
612
635
|
|
|
613
636
|
Examples:
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
637
|
+
```python
|
|
638
|
+
from hossam import *
|
|
639
|
+
df_result = hs_classroom.cluster_students(df, n_groups=5, score_cols=['국어', '영어', '수학'])
|
|
640
|
+
summary = hs_classroom.group_summary(df_result, name_col='이름')
|
|
641
|
+
print(summary)
|
|
642
|
+
```
|
|
618
643
|
"""
|
|
619
644
|
|
|
620
645
|
if df is None or len(df) == 0:
|
|
@@ -698,15 +723,15 @@ def analyze_classroom(
|
|
|
698
723
|
조별 요약 정보 (group_summary의 결과).
|
|
699
724
|
|
|
700
725
|
Examples:
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
726
|
+
```python
|
|
727
|
+
from hossam import *
|
|
728
|
+
summary = hs_classroom.analyze_classroom(df='students.csv',
|
|
729
|
+
n_groups=5,
|
|
730
|
+
score_cols=['국어', '영어', '수학'],
|
|
731
|
+
interest_col='관심사',
|
|
732
|
+
name_col='이름')
|
|
733
|
+
print(summary)
|
|
734
|
+
```
|
|
710
735
|
"""
|
|
711
736
|
|
|
712
737
|
# 1. 조 편성
|
|
@@ -723,7 +748,7 @@ def analyze_classroom(
|
|
|
723
748
|
|
|
724
749
|
# 3. 요약 시각화
|
|
725
750
|
if show_summary:
|
|
726
|
-
report_summary(df_result)
|
|
751
|
+
report_summary(df_result, interest_col)
|
|
727
752
|
|
|
728
753
|
# 4. KDE 시각화
|
|
729
754
|
if show_kde:
|
hossam/hs_gis.py
CHANGED
|
@@ -101,10 +101,12 @@ def geocode(df: DataFrame, addr: str, key: str) -> DataFrame:
|
|
|
101
101
|
Exception: 지오코딩 과정에서 발생한 예외를 전파합니다.
|
|
102
102
|
|
|
103
103
|
Examples:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
```python
|
|
105
|
+
from hossam import *
|
|
106
|
+
result = hs_gis.geocode(df, addr="address", key="YOUR_VWORLD_KEY")
|
|
107
|
+
set(["latitude","longitude"]).issubset(result.columns)
|
|
108
|
+
# True
|
|
109
|
+
```
|
|
108
110
|
"""
|
|
109
111
|
data: DataFrame = df.copy()
|
|
110
112
|
size: int = len(data)
|
|
@@ -172,8 +174,10 @@ def load_shape(path: str, info: bool = True) -> GeoDataFrame:
|
|
|
172
174
|
FileNotFoundError: 파일이 존재하지 않는 경우.
|
|
173
175
|
|
|
174
176
|
Examples:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
```python
|
|
178
|
+
from hossam import *
|
|
179
|
+
gdf = hs_gis.load_shape("path/to/file.shp", info=False)
|
|
180
|
+
```
|
|
177
181
|
"""
|
|
178
182
|
if not os.path.exists(path):
|
|
179
183
|
raise FileNotFoundError("⚠️[FileNotFoundException] 주어진 파일을 찾을 수 없습니다.\n - %s" % path)
|