hossam 0.4.5__tar.gz → 0.4.6__tar.gz
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-0.4.5/hossam.egg-info → hossam-0.4.6}/PKG-INFO +1 -1
- {hossam-0.4.5 → hossam-0.4.6}/hossam/__init__.py +2 -1
- {hossam-0.4.5 → hossam-0.4.6}/hossam/hs_classroom.py +30 -30
- {hossam-0.4.5 → hossam-0.4.6}/hossam/hs_plot.py +137 -147
- {hossam-0.4.5 → hossam-0.4.6}/hossam/hs_prep.py +7 -1
- {hossam-0.4.5 → hossam-0.4.6}/hossam/hs_stats.py +1570 -1459
- {hossam-0.4.5 → hossam-0.4.6}/hossam/hs_timeserise.py +38 -39
- {hossam-0.4.5 → hossam-0.4.6}/hossam/hs_util.py +198 -1
- {hossam-0.4.5 → hossam-0.4.6/hossam.egg-info}/PKG-INFO +1 -1
- {hossam-0.4.5 → hossam-0.4.6}/hossam.egg-info/SOURCES.txt +0 -1
- {hossam-0.4.5 → hossam-0.4.6}/pyproject.toml +1 -1
- hossam-0.4.5/hossam/data_loader.py +0 -203
- {hossam-0.4.5 → hossam-0.4.6}/LICENSE +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/MANIFEST.in +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/README.md +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/hossam/NotoSansKR-Regular.ttf +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/hossam/hs_gis.py +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/hossam/leekh.png +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/hossam.egg-info/dependency_links.txt +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/hossam.egg-info/requires.txt +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/hossam.egg-info/top_level.txt +0 -0
- {hossam-0.4.5 → hossam-0.4.6}/setup.cfg +0 -0
|
@@ -6,7 +6,8 @@ from . import hs_prep
|
|
|
6
6
|
from . import hs_stats
|
|
7
7
|
from . import hs_timeserise
|
|
8
8
|
from . import hs_util
|
|
9
|
-
from .
|
|
9
|
+
from .hs_util import load_info
|
|
10
|
+
from .hs_util import _load_data_remote as load_data
|
|
10
11
|
|
|
11
12
|
# py-modules
|
|
12
13
|
import sys
|
|
@@ -15,10 +15,10 @@ from .hs_plot import config
|
|
|
15
15
|
# 학생들을 관심사와 성적으로 균형잡힌 조로 편성한다
|
|
16
16
|
# ===================================================================
|
|
17
17
|
def cluster_students(
|
|
18
|
-
df,
|
|
18
|
+
df: DataFrame | str,
|
|
19
19
|
n_groups: int,
|
|
20
|
-
score_cols: list = None,
|
|
21
|
-
interest_col: str = None,
|
|
20
|
+
score_cols: list | None = None,
|
|
21
|
+
interest_col: str | None = None,
|
|
22
22
|
max_iter: int = 200,
|
|
23
23
|
score_metric: str = 'total'
|
|
24
24
|
) -> DataFrame:
|
|
@@ -173,7 +173,7 @@ def cluster_students(
|
|
|
173
173
|
df_main,
|
|
174
174
|
actual_n_groups,
|
|
175
175
|
score_cols,
|
|
176
|
-
interest_col,
|
|
176
|
+
interest_col, # type: ignore
|
|
177
177
|
max_iter
|
|
178
178
|
)
|
|
179
179
|
else:
|
|
@@ -219,8 +219,8 @@ def cluster_students(
|
|
|
219
219
|
def _balance_groups(
|
|
220
220
|
df: DataFrame,
|
|
221
221
|
n_groups: int,
|
|
222
|
-
score_cols: list,
|
|
223
|
-
interest_col: str = None,
|
|
222
|
+
score_cols: list | None = None,
|
|
223
|
+
interest_col: str | None = None,
|
|
224
224
|
max_iter: int = 200
|
|
225
225
|
) -> DataFrame:
|
|
226
226
|
"""조 내 인원과 성적 균형을 조정하는 내부 함수.
|
|
@@ -281,7 +281,7 @@ def _balance_groups(
|
|
|
281
281
|
count = grade_counts.loc[g, grade]
|
|
282
282
|
min_g, max_g = grade_bounds[grade]
|
|
283
283
|
|
|
284
|
-
if count <= max_g:
|
|
284
|
+
if count <= max_g: # type: ignore
|
|
285
285
|
continue
|
|
286
286
|
|
|
287
287
|
donors = group[group['성적그룹'] == grade]
|
|
@@ -291,12 +291,12 @@ def _balance_groups(
|
|
|
291
291
|
if og == g:
|
|
292
292
|
continue
|
|
293
293
|
other_count = grade_counts.loc[og, grade]
|
|
294
|
-
if other_count >= min_g:
|
|
294
|
+
if other_count >= min_g: # type: ignore
|
|
295
295
|
continue
|
|
296
296
|
other_group = df[df['조'] == og]
|
|
297
297
|
|
|
298
298
|
og_interest = dominant_interest(other_group)
|
|
299
|
-
need_groups.append((min_g - other_count, og, og_interest))
|
|
299
|
+
need_groups.append((min_g - other_count, og, og_interest)) # type: ignore
|
|
300
300
|
|
|
301
301
|
need_groups.sort(reverse=True)
|
|
302
302
|
|
|
@@ -392,14 +392,14 @@ def _balance_group_sizes_only(
|
|
|
392
392
|
# ===================================================================
|
|
393
393
|
# 조 편성 결과의 인원, 관심사, 점수 분포를 시각화한다
|
|
394
394
|
# ===================================================================
|
|
395
|
-
def report_summary(df: DataFrame, interest_col: str = None, width: int = config.width, height: int = config.height, dpi: int = config.dpi) -> None:
|
|
395
|
+
def report_summary(df: DataFrame, interest_col: str | None = None, width: int = config.width, height: int = config.height, dpi: int = config.dpi) -> None:
|
|
396
396
|
"""조 편성 결과의 요약 통계를 시각화합니다.
|
|
397
397
|
|
|
398
398
|
조별 인원 분포, 관심사 분포, 평균점수 분포를 나타냅니다.
|
|
399
399
|
|
|
400
400
|
Args:
|
|
401
401
|
df (DataFrame): cluster_students 함수의 반환 결과 데이터프레임.
|
|
402
|
-
interest_col (str): 관심사 컬럼명
|
|
402
|
+
interest_col (str | None): 관심사 컬럼명
|
|
403
403
|
width (int): 그래프 넓이. 기본값: config.width
|
|
404
404
|
height (int): 그래프 높이. 기본값: config.height
|
|
405
405
|
dpi (int): 그래프 해상도. 기본값: config.dpi
|
|
@@ -540,24 +540,24 @@ def report_summary(df: DataFrame, interest_col: str = None, width: int = config.
|
|
|
540
540
|
plot_idx += 1
|
|
541
541
|
|
|
542
542
|
# hs_plot.finalize_plot을 사용하여 마무리
|
|
543
|
-
hs_plot.finalize_plot(axes, outparams=True, grid=False)
|
|
543
|
+
hs_plot.finalize_plot(axes, outparams=True, grid=False) # type: ignore
|
|
544
544
|
|
|
545
545
|
|
|
546
546
|
# ===================================================================
|
|
547
547
|
# 조별 점수 분포를 커널 밀도 추정(KDE) 그래프로 시각화한다
|
|
548
548
|
# ===================================================================
|
|
549
|
-
def report_kde(df: DataFrame, metric: str = 'average', width: int = config.width, height: int = config.height, dpi: int = config.dpi) -> None:
|
|
549
|
+
def report_kde(df: DataFrame | str, metric: str = 'average', width: int = config.width, height: int = config.height, dpi: int = config.dpi) -> None:
|
|
550
550
|
"""조별 점수 분포를 KDE(Kernel Density Estimation)로 시각화합니다.
|
|
551
551
|
|
|
552
552
|
각 조의 점수 분포를 커널 밀도 추정으로 표시하고 평균 및 95% 신뢰구간을 나타냅니다.
|
|
553
553
|
|
|
554
554
|
Args:
|
|
555
|
-
df: cluster_students 함수의 반환 결과 데이터프레임.
|
|
556
|
-
metric: 점수 기준 선택 ('total' 또는 'average').
|
|
555
|
+
df (DataFrame | str): cluster_students 함수의 반환 결과 데이터프레임.
|
|
556
|
+
metric (str): 점수 기준 선택 ('total' 또는 'average').
|
|
557
557
|
'total'이면 총점, 'average'이면 평균점수. 기본값: 'average'
|
|
558
|
-
width: 그래프 넓이. 기본값: config.width
|
|
559
|
-
height: 그래프 높이. 기본값: config.height
|
|
560
|
-
dpi: 그래프 해상도. 기본값: config.dpi
|
|
558
|
+
width (int): 그래프 넓이. 기본값: config.width
|
|
559
|
+
height (int): 그래프 높이. 기본값: config.height
|
|
560
|
+
dpi (int): 그래프 해상도. 기본값: config.dpi
|
|
561
561
|
|
|
562
562
|
Examples:
|
|
563
563
|
```python
|
|
@@ -570,17 +570,17 @@ def report_kde(df: DataFrame, metric: str = 'average', width: int = config.width
|
|
|
570
570
|
print("데이터프레임이 비어있습니다")
|
|
571
571
|
return
|
|
572
572
|
|
|
573
|
-
if '조' not in df.columns:
|
|
573
|
+
if '조' not in df.columns: # type: ignore
|
|
574
574
|
print("데이터프레임에 '조' 컬럼이 없습니다")
|
|
575
575
|
return
|
|
576
576
|
|
|
577
|
-
has_score = '총점' in df.columns
|
|
578
|
-
has_avg = '평균점수' in df.columns
|
|
577
|
+
has_score = '총점' in df.columns # type: ignore
|
|
578
|
+
has_avg = '평균점수' in df.columns # type: ignore
|
|
579
579
|
if not has_score:
|
|
580
580
|
print("점수 데이터가 없습니다")
|
|
581
581
|
return
|
|
582
582
|
|
|
583
|
-
labels = df['조'].unique().tolist()
|
|
583
|
+
labels = df['조'].unique().tolist() # type: ignore
|
|
584
584
|
def _sort_key(v):
|
|
585
585
|
try:
|
|
586
586
|
return (0, int(v))
|
|
@@ -596,18 +596,18 @@ def report_kde(df: DataFrame, metric: str = 'average', width: int = config.width
|
|
|
596
596
|
|
|
597
597
|
plot_idx = 0
|
|
598
598
|
metric_col = '평균점수' if (metric or '').lower() == 'average' else '총점'
|
|
599
|
-
if metric_col not in df.columns:
|
|
599
|
+
if metric_col not in df.columns: # type: ignore
|
|
600
600
|
print(f"'{metric_col}' 컬럼이 없습니다")
|
|
601
601
|
return
|
|
602
602
|
|
|
603
603
|
for group in ordered_labels:
|
|
604
|
-
group_df = df[df['조'] == group]
|
|
605
|
-
group_series = group_df[metric_col].dropna()
|
|
604
|
+
group_df = df[df['조'] == group] # type: ignore
|
|
605
|
+
group_series = group_df[metric_col].dropna() # type: ignore
|
|
606
606
|
n = group_series.size
|
|
607
607
|
if n == 0:
|
|
608
608
|
continue
|
|
609
609
|
|
|
610
|
-
hs_plot.kde_confidence_interval(data=group_df, xnames=metric_col, ax=axes[plot_idx], callback=lambda ax: ax.set_title(f"{group}조"))
|
|
610
|
+
hs_plot.kde_confidence_interval(data=group_df, xnames=metric_col, ax=axes[plot_idx], callback=lambda ax: ax.set_title(f"{group}조")) # type: ignore
|
|
611
611
|
|
|
612
612
|
plot_idx += 1
|
|
613
613
|
|
|
@@ -615,7 +615,7 @@ def report_kde(df: DataFrame, metric: str = 'average', width: int = config.width
|
|
|
615
615
|
for idx in range(plot_idx, len(axes)):
|
|
616
616
|
fig.delaxes(axes[idx])
|
|
617
617
|
|
|
618
|
-
hs_plot.finalize_plot(axes)
|
|
618
|
+
hs_plot.finalize_plot(axes) # type: ignore
|
|
619
619
|
|
|
620
620
|
|
|
621
621
|
# ===================================================================
|
|
@@ -690,10 +690,10 @@ def group_summary(df: DataFrame, name_col: str = '학생이름') -> DataFrame:
|
|
|
690
690
|
# 학생 조 편성부터 시각화까지의 전체 분석 프로세스를 일괄 실행한다
|
|
691
691
|
# ===================================================================
|
|
692
692
|
def analyze_classroom(
|
|
693
|
-
df,
|
|
693
|
+
df: DataFrame | str,
|
|
694
694
|
n_groups: int,
|
|
695
|
-
score_cols: list = None,
|
|
696
|
-
interest_col: str = None,
|
|
695
|
+
score_cols: list | None = None,
|
|
696
|
+
interest_col: str | None = None,
|
|
697
697
|
max_iter: int = 200,
|
|
698
698
|
score_metric: str = 'average',
|
|
699
699
|
name_col: str = '학생이름',
|
|
@@ -73,7 +73,7 @@ def get_default_ax(width: int = config.width, height: int = config.height, rows:
|
|
|
73
73
|
if is_array and (ws != None and hs != None):
|
|
74
74
|
fig.subplots_adjust(wspace=ws, hspace=hs)
|
|
75
75
|
|
|
76
|
-
if title and
|
|
76
|
+
if title and is_array:
|
|
77
77
|
fig.suptitle(title, fontsize=config.font_size * 1.5, fontweight='bold')
|
|
78
78
|
|
|
79
79
|
if flatten == True:
|
|
@@ -126,7 +126,7 @@ def create_figure(width: int = config.width, height: int = config.height, rows:
|
|
|
126
126
|
# ===================================================================
|
|
127
127
|
# 그래프의 그리드, 레이아웃을 정리하고 필요 시 저장 또는 표시한다
|
|
128
128
|
# ===================================================================
|
|
129
|
-
def finalize_plot(ax: Axes | np.ndarray, callback: Callable | None = None, outparams: bool = False, save_path: str | None = None, grid: bool = True, title: str | None = None) -> None:
|
|
129
|
+
def finalize_plot(ax: Axes | np.ndarray | list, callback: Callable | None = None, outparams: bool = False, save_path: str | None = None, grid: bool = True, title: str | None = None) -> None:
|
|
130
130
|
"""공통 후처리를 수행한다: 콜백 실행, 레이아웃 정리, 필요 시 표시/종료.
|
|
131
131
|
|
|
132
132
|
Args:
|
|
@@ -270,6 +270,10 @@ def boxplot(
|
|
|
270
270
|
yname: str | None = None,
|
|
271
271
|
title: str | None = None,
|
|
272
272
|
orient: str = "v",
|
|
273
|
+
stat_test: str | None = None,
|
|
274
|
+
stat_pairs: list[tuple] | None = None,
|
|
275
|
+
stat_text_format: str = "star",
|
|
276
|
+
stat_loc: str = "inside",
|
|
273
277
|
palette: str | None = None,
|
|
274
278
|
width: int = config.width,
|
|
275
279
|
height: int = config.height,
|
|
@@ -288,6 +292,10 @@ def boxplot(
|
|
|
288
292
|
yname (str|None): y축 값 컬럼명.
|
|
289
293
|
title (str|None): 그래프 제목.
|
|
290
294
|
orient (str): 'v' 또는 'h' 방향.
|
|
295
|
+
stat_test (str|None): 통계 검정 방법. None이면 검정 안함. xname과 yname이 모두 지정되어야 함.
|
|
296
|
+
stat_pairs (list[tuple]|None): 통계 검정할 그룹 쌍 목록.
|
|
297
|
+
stat_text_format (str): 통계 결과 표시 형식.
|
|
298
|
+
stat_loc (str): 통계 결과 위치.
|
|
291
299
|
palette (str|None): 팔레트 이름.
|
|
292
300
|
width (int): 캔버스 가로 픽셀.
|
|
293
301
|
height (int): 캔버스 세로 픽셀.
|
|
@@ -326,12 +334,67 @@ def boxplot(
|
|
|
326
334
|
|
|
327
335
|
boxplot_kwargs.update(params)
|
|
328
336
|
sb.boxplot(**boxplot_kwargs, linewidth=linewidth)
|
|
337
|
+
|
|
338
|
+
# 통계 검정 추가
|
|
339
|
+
if stat_test is not None:
|
|
340
|
+
if stat_pairs is None:
|
|
341
|
+
stat_pairs = [df[xname].dropna().unique().tolist()]
|
|
342
|
+
|
|
343
|
+
annotator = Annotator(ax, data=df, x=xname, y=yname, pairs=stat_pairs, orient=orient)
|
|
344
|
+
annotator.configure(test=stat_test, text_format=stat_text_format, loc=stat_loc)
|
|
345
|
+
annotator.apply_and_annotate()
|
|
329
346
|
else:
|
|
330
347
|
sb.boxplot(data=df, orient=orient, ax=ax, linewidth=linewidth, **params) # type: ignore
|
|
331
348
|
|
|
332
349
|
finalize_plot(ax, callback, outparams, save_path, True, title) # type: ignore
|
|
333
350
|
|
|
334
351
|
|
|
352
|
+
# ===================================================================
|
|
353
|
+
# 상자그림에 p-value 주석을 추가한다
|
|
354
|
+
# ===================================================================
|
|
355
|
+
def pvalue1_anotation(
|
|
356
|
+
data: DataFrame,
|
|
357
|
+
target: str,
|
|
358
|
+
hue: str,
|
|
359
|
+
title: str | None = None,
|
|
360
|
+
pairs: list | None = None,
|
|
361
|
+
test: str = "t-test_ind",
|
|
362
|
+
text_format: str = "star",
|
|
363
|
+
loc: str = "outside",
|
|
364
|
+
width: int = config.width,
|
|
365
|
+
height: int = config.height,
|
|
366
|
+
linewidth: float = config.line_width,
|
|
367
|
+
dpi: int = config.dpi,
|
|
368
|
+
save_path: str | None = None,
|
|
369
|
+
callback: Callable | None = None,
|
|
370
|
+
ax: Axes | None = None,
|
|
371
|
+
**params
|
|
372
|
+
) -> None:
|
|
373
|
+
"""
|
|
374
|
+
boxplot의 wrapper 함수로, 상자그림에 p-value 주석을 추가한다.
|
|
375
|
+
"""
|
|
376
|
+
boxplot(
|
|
377
|
+
data,
|
|
378
|
+
xname=hue,
|
|
379
|
+
yname=target,
|
|
380
|
+
title=title,
|
|
381
|
+
orient="v",
|
|
382
|
+
stat_test=test,
|
|
383
|
+
stat_pairs=pairs,
|
|
384
|
+
stat_text_format=text_format,
|
|
385
|
+
stat_loc=loc,
|
|
386
|
+
palette=None,
|
|
387
|
+
width=width,
|
|
388
|
+
height=height,
|
|
389
|
+
linewidth=linewidth,
|
|
390
|
+
dpi=dpi,
|
|
391
|
+
save_path=save_path,
|
|
392
|
+
callback=callback,
|
|
393
|
+
ax=ax,
|
|
394
|
+
**params
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
|
|
335
398
|
# ===================================================================
|
|
336
399
|
# 커널 밀도 추정(KDE) 그래프를 그린다
|
|
337
400
|
# ===================================================================
|
|
@@ -756,7 +819,12 @@ def regplot(
|
|
|
756
819
|
"data": df,
|
|
757
820
|
"x": xname,
|
|
758
821
|
"y": yname,
|
|
759
|
-
"scatter_kws": {
|
|
822
|
+
"scatter_kws": {
|
|
823
|
+
"s": 20,
|
|
824
|
+
"linewidths": 0.5,
|
|
825
|
+
"edgecolor": "w",
|
|
826
|
+
"color": scatter_color
|
|
827
|
+
},
|
|
760
828
|
"line_kws": {
|
|
761
829
|
"color": "red",
|
|
762
830
|
"linestyle": "--",
|
|
@@ -1088,7 +1156,7 @@ def barplot(
|
|
|
1088
1156
|
|
|
1089
1157
|
|
|
1090
1158
|
# ===================================================================
|
|
1091
|
-
#
|
|
1159
|
+
# boxen 플롯을 그린다
|
|
1092
1160
|
# ===================================================================
|
|
1093
1161
|
def boxenplot(
|
|
1094
1162
|
df: DataFrame,
|
|
@@ -1598,88 +1666,6 @@ def kde_confidence_interval(
|
|
|
1598
1666
|
finalize_plot(axes[0] if isinstance(axes, list) and len(axes) > 0 else ax, callback, outparams, save_path, True, title) # type: ignore
|
|
1599
1667
|
|
|
1600
1668
|
|
|
1601
|
-
# ===================================================================
|
|
1602
|
-
# 상자그림에 p-value 주석을 추가한다
|
|
1603
|
-
# ===================================================================
|
|
1604
|
-
def pvalue1_anotation(
|
|
1605
|
-
data: DataFrame,
|
|
1606
|
-
target: str,
|
|
1607
|
-
hue: str,
|
|
1608
|
-
title: str | None = None,
|
|
1609
|
-
pairs: list | None = None,
|
|
1610
|
-
test: str = "t-test_ind",
|
|
1611
|
-
text_format: str = "star",
|
|
1612
|
-
loc: str = "outside",
|
|
1613
|
-
width: int = config.width,
|
|
1614
|
-
height: int = config.height,
|
|
1615
|
-
linewidth: float = config.line_width,
|
|
1616
|
-
dpi: int = config.dpi,
|
|
1617
|
-
save_path: str | None = None,
|
|
1618
|
-
callback: Callable | None = None,
|
|
1619
|
-
ax: Axes | None = None,
|
|
1620
|
-
**params
|
|
1621
|
-
) -> None:
|
|
1622
|
-
"""statannotations를 이용해 상자그림에 p-value 주석을 추가한다.
|
|
1623
|
-
|
|
1624
|
-
Args:
|
|
1625
|
-
data (DataFrame): 시각화할 데이터.
|
|
1626
|
-
target (str): 값 컬럼명.
|
|
1627
|
-
hue (str): 그룹 컬럼명.
|
|
1628
|
-
title (str|None): 그래프 제목.
|
|
1629
|
-
pairs (list|None): 비교할 (group_a, group_b) 튜플 목록. None이면 hue 컬럼의 모든 고유값 조합을 자동 생성.
|
|
1630
|
-
test (str): 적용할 통계 검정 이름.
|
|
1631
|
-
text_format (str): 주석 형식('star' 등).
|
|
1632
|
-
loc (str): 주석 위치.
|
|
1633
|
-
width (int): 캔버스 가로 픽셀.
|
|
1634
|
-
height (int): 캔버스 세로 픽셀.
|
|
1635
|
-
linewidth (float): 선 굵기.
|
|
1636
|
-
dpi (int): 그림 크기 및 해상도.
|
|
1637
|
-
callback (Callable|None): Axes 후처리 콜백.
|
|
1638
|
-
ax (Axes|None): 외부에서 전달한 Axes.
|
|
1639
|
-
**params: seaborn boxplot 추가 인자.
|
|
1640
|
-
|
|
1641
|
-
Returns:
|
|
1642
|
-
None
|
|
1643
|
-
"""
|
|
1644
|
-
# pairs가 None이면 hue 컬럼의 고유값으로 모든 조합 생성
|
|
1645
|
-
if pairs is None:
|
|
1646
|
-
from itertools import combinations
|
|
1647
|
-
unique_values = sorted(data[hue].unique())
|
|
1648
|
-
pairs = list(combinations(unique_values, 2))
|
|
1649
|
-
|
|
1650
|
-
outparams = False
|
|
1651
|
-
|
|
1652
|
-
if ax is None:
|
|
1653
|
-
fig, ax = get_default_ax(width, height, 1, 1, dpi) # type: ignore
|
|
1654
|
-
outparams = True
|
|
1655
|
-
|
|
1656
|
-
# params에서 palette 추출 (있으면)
|
|
1657
|
-
palette_value = params.pop("palette", None)
|
|
1658
|
-
|
|
1659
|
-
# boxplot kwargs 구성
|
|
1660
|
-
boxplot_kwargs = {
|
|
1661
|
-
"data": data,
|
|
1662
|
-
"x": hue,
|
|
1663
|
-
"y": target,
|
|
1664
|
-
"linewidth": linewidth,
|
|
1665
|
-
"ax": ax,
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
# palette가 있으면 추가 (hue는 x에 이미 할당됨)
|
|
1669
|
-
if palette_value is not None:
|
|
1670
|
-
boxplot_kwargs["palette"] = palette_value
|
|
1671
|
-
|
|
1672
|
-
boxplot_kwargs.update(params)
|
|
1673
|
-
|
|
1674
|
-
sb.boxplot(**boxplot_kwargs)
|
|
1675
|
-
annotator = Annotator(ax, data=data, x=hue, y=target, pairs=pairs)
|
|
1676
|
-
annotator.configure(test=test, text_format=text_format, loc=loc)
|
|
1677
|
-
annotator.apply_and_annotate()
|
|
1678
|
-
|
|
1679
|
-
sb.despine()
|
|
1680
|
-
finalize_plot(ax, callback, outparams, save_path, True, title) # type: ignore
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
1669
|
|
|
1684
1670
|
# ===================================================================
|
|
1685
1671
|
# 잔차도 (선형회귀의 선형성 검정)
|
|
@@ -1741,7 +1727,7 @@ def ols_residplot(
|
|
|
1741
1727
|
outparams = True
|
|
1742
1728
|
|
|
1743
1729
|
# 산점도 seaborn으로 그리기
|
|
1744
|
-
sb.scatterplot(x=y_pred, y=resid, ax=ax, s=
|
|
1730
|
+
sb.scatterplot(x=y_pred, y=resid, ax=ax, s=20, edgecolor="white", **params)
|
|
1745
1731
|
|
|
1746
1732
|
# 기준선 (잔차 = 0)
|
|
1747
1733
|
ax.axhline(0, color="gray", linestyle="--", linewidth=linewidth*0.7) # type: ignore
|
|
@@ -1795,13 +1781,13 @@ def ols_residplot(
|
|
|
1795
1781
|
for i, c in enumerate(["red", "green", "blue"]):
|
|
1796
1782
|
ax.text( # type: ignore
|
|
1797
1783
|
s=f"{i+1} sqrt(MSE) = {mse_r[i]:.2f}% ({mse_r[i] - target[i]:.2f}%)",
|
|
1798
|
-
x=xmax + 0.
|
|
1784
|
+
x=xmax + 0.05,
|
|
1799
1785
|
y=(i + 1) * mse_sq,
|
|
1800
1786
|
color=c,
|
|
1801
1787
|
)
|
|
1802
1788
|
ax.text( # type: ignore
|
|
1803
1789
|
s=f"-{i+1} sqrt(MSE) = {mse_r[i]:.2f}% ({mse_r[i] - target[i]:.2f}%)",
|
|
1804
|
-
x=xmax + 0.
|
|
1790
|
+
x=xmax + 0.05,
|
|
1805
1791
|
y=-(i + 1) * mse_sq,
|
|
1806
1792
|
color=c,
|
|
1807
1793
|
)
|
|
@@ -2146,7 +2132,7 @@ def categorical_target_distribution(
|
|
|
2146
2132
|
plot_kwargs.update({"x": yname, "hue": col, "palette": palette, "fill": kde_fill, "common_norm": False, "linewidth": linewidth})
|
|
2147
2133
|
sb.kdeplot(**plot_kwargs)
|
|
2148
2134
|
else: # box
|
|
2149
|
-
plot_kwargs.update({"x": col, "y": yname, "palette": palette})
|
|
2135
|
+
plot_kwargs.update({"x": col, "y": yname, "hue": col, "palette": palette})
|
|
2150
2136
|
sb.boxplot(**plot_kwargs, linewidth=linewidth)
|
|
2151
2137
|
|
|
2152
2138
|
ax.set_title(f"{col} vs {yname}")
|
|
@@ -2419,8 +2405,7 @@ def radarplot(
|
|
|
2419
2405
|
# ===================================================================
|
|
2420
2406
|
def distribution_plot(
|
|
2421
2407
|
data: DataFrame,
|
|
2422
|
-
column: str,
|
|
2423
|
-
title: str | None = None,
|
|
2408
|
+
column: str | list[str],
|
|
2424
2409
|
clevel: float = 0.95,
|
|
2425
2410
|
orient: str = "h",
|
|
2426
2411
|
hue: str | None = None,
|
|
@@ -2441,7 +2426,6 @@ def distribution_plot(
|
|
|
2441
2426
|
Args:
|
|
2442
2427
|
data (DataFrame): 시각화할 데이터.
|
|
2443
2428
|
column (str): 분석할 컬럼명.
|
|
2444
|
-
title (str|None): 그래프 제목.
|
|
2445
2429
|
clevel (float): KDE 신뢰수준 (0~1). 기본값 0.95.
|
|
2446
2430
|
orient (str): Boxplot 방향 ('v' 또는 'h'). 기본값 'h'.
|
|
2447
2431
|
hue (str|None): 명목형 컬럼명. 지정하면 각 범주별로 행을 늘려 KDE와 boxplot을 그림.
|
|
@@ -2456,76 +2440,82 @@ def distribution_plot(
|
|
|
2456
2440
|
Returns:
|
|
2457
2441
|
None
|
|
2458
2442
|
"""
|
|
2459
|
-
if
|
|
2460
|
-
|
|
2461
|
-
fig, axes = get_default_ax(width, height, rows=1, cols=2, dpi=dpi)
|
|
2462
|
-
|
|
2463
|
-
kde_confidence_interval(
|
|
2464
|
-
data=data,
|
|
2465
|
-
xnames=column,
|
|
2466
|
-
clevel=clevel,
|
|
2467
|
-
linewidth=linewidth,
|
|
2468
|
-
ax=axes[0],
|
|
2469
|
-
)
|
|
2470
|
-
|
|
2471
|
-
if kind == "hist":
|
|
2472
|
-
histplot(
|
|
2473
|
-
df=data,
|
|
2474
|
-
xname=column,
|
|
2475
|
-
linewidth=linewidth,
|
|
2476
|
-
ax=axes[1]
|
|
2477
|
-
)
|
|
2478
|
-
else:
|
|
2479
|
-
boxplot(
|
|
2480
|
-
df=data[column], # type: ignore
|
|
2481
|
-
linewidth=linewidth,
|
|
2482
|
-
ax=axes[1]
|
|
2483
|
-
)
|
|
2484
|
-
|
|
2485
|
-
fig.suptitle(f"Distribution of {column}", fontsize=14, y=1.02)
|
|
2486
|
-
else:
|
|
2487
|
-
if hue not in data.columns:
|
|
2488
|
-
raise ValueError(f"hue column '{hue}' not found in DataFrame")
|
|
2489
|
-
|
|
2490
|
-
categories = list(pd.Series(data[hue].dropna().unique()).sort_values())
|
|
2491
|
-
n_cat = len(categories) if categories else 1
|
|
2443
|
+
if isinstance(column, str):
|
|
2444
|
+
column = [column]
|
|
2492
2445
|
|
|
2493
|
-
|
|
2494
|
-
|
|
2446
|
+
for c in column:
|
|
2447
|
+
title = f"Distribution Plot of {c}"
|
|
2495
2448
|
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2449
|
+
if hue is None:
|
|
2450
|
+
# 1행 2열 서브플롯 생성
|
|
2451
|
+
fig, axes = get_default_ax(width, height, rows=1, cols=2, dpi=dpi, title=title)
|
|
2499
2452
|
|
|
2500
2453
|
kde_confidence_interval(
|
|
2501
|
-
data=
|
|
2502
|
-
xnames=
|
|
2454
|
+
data=data,
|
|
2455
|
+
xnames=c,
|
|
2503
2456
|
clevel=clevel,
|
|
2504
2457
|
linewidth=linewidth,
|
|
2505
|
-
ax=
|
|
2458
|
+
ax=axes[0],
|
|
2506
2459
|
)
|
|
2507
|
-
left_ax.set_title(f"{hue} = {cat}")
|
|
2508
2460
|
|
|
2509
2461
|
if kind == "hist":
|
|
2510
2462
|
histplot(
|
|
2511
|
-
df=
|
|
2512
|
-
xname=
|
|
2463
|
+
df=data,
|
|
2464
|
+
xname=c,
|
|
2513
2465
|
linewidth=linewidth,
|
|
2514
|
-
ax=
|
|
2466
|
+
ax=axes[1]
|
|
2515
2467
|
)
|
|
2516
2468
|
else:
|
|
2517
2469
|
boxplot(
|
|
2518
|
-
df=
|
|
2470
|
+
df=data[column], # type: ignore
|
|
2519
2471
|
linewidth=linewidth,
|
|
2520
|
-
ax=
|
|
2472
|
+
ax=axes[1]
|
|
2521
2473
|
)
|
|
2522
2474
|
|
|
2523
|
-
|
|
2475
|
+
fig.suptitle(title, fontsize=14, y=1.02)
|
|
2476
|
+
else:
|
|
2477
|
+
if hue not in data.columns:
|
|
2478
|
+
raise ValueError(f"hue column '{hue}' not found in DataFrame")
|
|
2524
2479
|
|
|
2525
|
-
|
|
2480
|
+
categories = list(pd.Series(data[hue].dropna().unique()).sort_values())
|
|
2481
|
+
n_cat = len(categories) if categories else 1
|
|
2526
2482
|
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2483
|
+
fig, axes = get_default_ax(width, height, rows=n_cat, cols=2, dpi=dpi, title=title)
|
|
2484
|
+
axes_2d = np.atleast_2d(axes)
|
|
2485
|
+
|
|
2486
|
+
for idx, cat in enumerate(categories):
|
|
2487
|
+
subset = data[data[hue] == cat]
|
|
2488
|
+
left_ax, right_ax = axes_2d[idx, 0], axes_2d[idx, 1]
|
|
2489
|
+
|
|
2490
|
+
kde_confidence_interval(
|
|
2491
|
+
data=subset,
|
|
2492
|
+
xnames=c,
|
|
2493
|
+
clevel=clevel,
|
|
2494
|
+
linewidth=linewidth,
|
|
2495
|
+
ax=left_ax,
|
|
2496
|
+
)
|
|
2497
|
+
left_ax.set_title(f"{hue} = {cat}")
|
|
2498
|
+
|
|
2499
|
+
if kind == "hist":
|
|
2500
|
+
histplot(
|
|
2501
|
+
df=subset,
|
|
2502
|
+
xname=c,
|
|
2503
|
+
linewidth=linewidth,
|
|
2504
|
+
ax=right_ax,
|
|
2505
|
+
)
|
|
2506
|
+
else:
|
|
2507
|
+
boxplot(
|
|
2508
|
+
df=subset[c], # type: ignore
|
|
2509
|
+
linewidth=linewidth,
|
|
2510
|
+
ax=right_ax
|
|
2511
|
+
)
|
|
2512
|
+
|
|
2513
|
+
fig.suptitle(f"{title} by {hue}", fontsize=14, y=1.02)
|
|
2514
|
+
|
|
2515
|
+
plt.tight_layout()
|
|
2516
|
+
|
|
2517
|
+
if save_path:
|
|
2518
|
+
plt.savefig(save_path, bbox_inches='tight', dpi=dpi)
|
|
2519
|
+
plt.close()
|
|
2520
|
+
else:
|
|
2521
|
+
plt.show()
|
|
@@ -764,7 +764,7 @@ def bin_continuous(
|
|
|
764
764
|
# ===================================================================
|
|
765
765
|
# 지정된 변수에 로그 먼저 변환을 적용한다
|
|
766
766
|
# ===================================================================
|
|
767
|
-
def log_transform(data: DataFrame, *fields: str) -> DataFrame:
|
|
767
|
+
def log_transform(data: DataFrame, *fields: str, columns: list | None = None) -> DataFrame:
|
|
768
768
|
"""수치형 변수에 대해 로그 변환을 수행한다.
|
|
769
769
|
|
|
770
770
|
자연로그(ln)를 사용하여 변환하며, 0 또는 음수 값이 있을 경우
|
|
@@ -773,6 +773,7 @@ def log_transform(data: DataFrame, *fields: str) -> DataFrame:
|
|
|
773
773
|
Args:
|
|
774
774
|
data (DataFrame): 변환할 데이터프레임.
|
|
775
775
|
*fields (str): 변환할 컬럼명 목록. 지정하지 않으면 모든 수치형 컬럼을 처리.
|
|
776
|
+
columns (list, optional): 변환할 컬럼명 목록. fields와 중복 사용 불가.
|
|
776
777
|
|
|
777
778
|
Returns:
|
|
778
779
|
DataFrame: 로그 변환된 데이터프레임.
|
|
@@ -799,6 +800,11 @@ def log_transform(data: DataFrame, *fields: str) -> DataFrame:
|
|
|
799
800
|
"""
|
|
800
801
|
df = data.copy()
|
|
801
802
|
|
|
803
|
+
if columns is not None:
|
|
804
|
+
if fields:
|
|
805
|
+
raise ValueError("fields와 columns 인자는 중복 사용할 수 없습니다.")
|
|
806
|
+
fields = columns # type: ignore
|
|
807
|
+
|
|
802
808
|
# 대상 컬럼 결정
|
|
803
809
|
if not fields:
|
|
804
810
|
# 모든 수치형 컬럼 선택
|