hossam 0.3.15__py3-none-any.whl → 0.3.17__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 CHANGED
@@ -5,6 +5,7 @@ from matplotlib import font_manager as fm
5
5
  from importlib.resources import files, as_file
6
6
  from importlib.metadata import version
7
7
  from types import SimpleNamespace
8
+ import sys
8
9
  import warnings
9
10
 
10
11
  try:
@@ -15,14 +16,14 @@ except Exception:
15
16
 
16
17
  hs_fig = SimpleNamespace(
17
18
  dpi=200,
18
- width=600,
19
- height=320,
20
- font_size=6,
19
+ width=800,
20
+ height=480,
21
+ font_size=9.5,
21
22
  font_weight="light",
22
- frame_width=0.4,
23
- line_width=1,
23
+ frame_width=0.5,
24
+ line_width=1.5,
24
25
  grid_alpha=0.3,
25
- grid_width=0.4,
26
+ grid_width=0.5,
26
27
  fill_alpha=0.3
27
28
  )
28
29
 
@@ -56,9 +57,10 @@ def _init_korean_font():
56
57
  "pdf.fonttype": 42,
57
58
  "ps.fonttype": 42,
58
59
  })
59
- print(
60
- "\n✅ 시각화를 위한 한글 글꼴(NotoSansKR-Regular)이 자동 적용되었습니다."
61
- )
60
+ if sys.stdout.isatty():
61
+ print(
62
+ "\n✅ 시각화를 위한 한글 글꼴(NotoSansKR-Regular)이 자동 적용되었습니다."
63
+ )
62
64
  return
63
65
  except Exception as e:
64
66
  warnings.warn(f"\n한글 폰트 초기화: 패키지 폰트 사용 실패 ({e}).")
@@ -76,8 +78,10 @@ def _init():
76
78
  f"🔖 Version: {__version__}",
77
79
  ]
78
80
 
79
- for msg in messages:
80
- print(f"{msg}")
81
+ # MCP/stdio 환경에서는 배너를 출력하지 않음 (stdout TTY일 때만 출력)
82
+ if sys.stdout.isatty():
83
+ for msg in messages:
84
+ print(f"{msg}")
81
85
 
82
86
  _init_korean_font()
83
87
 
hossam/hs_plot.py CHANGED
@@ -56,6 +56,9 @@ def get_default_ax(width: int = hs_fig.width, height: int = hs_fig.height, rows:
56
56
  rows (int): 서브플롯 행 개수.
57
57
  cols (int): 서브플롯 열 개수.
58
58
  dpi (int): 해상도(DPI).
59
+ flatten (bool): Axes 배열을 1차원 리스트로 평탄화할지 여부.
60
+ ws (int|None): 서브플롯 가로 간격(`wspace`). rows/cols가 1보다 클 때만 적용.
61
+ hs (int|None): 서브플롯 세로 간격(`hspace`). rows/cols가 1보다 클 때만 적용.
59
62
 
60
63
  Returns:
61
64
  tuple[Figure, Axes]: 생성된 matplotlib Figure와 Axes 객체.
@@ -100,7 +103,7 @@ def finalize_plot(ax: Axes, callback: any = None, outparams: bool = False, save_
100
103
  callback (Callable|None): 추가 설정을 위한 사용자 콜백.
101
104
  outparams (bool): 내부에서 생성한 Figure인 경우 True.
102
105
  save_path (str|None): 이미지 저장 경로. None이 아니면 해당 경로로 저장.
103
- grid (bool): 그리드 표시 여부.
106
+ grid (bool): 그리드 표시 여부. 기본값은 True입니다.
104
107
 
105
108
  Returns:
106
109
  None
@@ -166,6 +169,7 @@ def lineplot(
166
169
  height (int): 캔버스 세로 픽셀.
167
170
  linewidth (float): 선 굵기.
168
171
  dpi (int): 해상도.
172
+ save_path (str|None): 이미지 저장 경로. None이면 화면에 표시.
169
173
  callback (Callable|None): Axes 후처리 콜백.
170
174
  ax (Axes|None): 외부에서 전달한 Axes.
171
175
  **params: seaborn lineplot 추가 인자.
@@ -230,6 +234,7 @@ def boxplot(
230
234
  height (int): 캔버스 세로 픽셀.
231
235
  linewidth (float): 선 굵기.
232
236
  dpi (int): 그림 크기 및 해상도.
237
+ save_path (str|None): 이미지 저장 경로. None이면 화면에 표시.
233
238
  callback (Callable|None): Axes 후처리 콜백.
234
239
  ax (Axes|None): 외부에서 전달한 Axes.
235
240
  **params: seaborn boxplot 추가 인자.
@@ -1847,6 +1852,7 @@ def distribution_by_class(
1847
1852
  linewidth=linewidth,
1848
1853
  dpi=dpi,
1849
1854
  callback=callback,
1855
+ save_path=save_path
1850
1856
  )
1851
1857
  elif type == "hist":
1852
1858
  histplot(
@@ -1861,6 +1867,7 @@ def distribution_by_class(
1861
1867
  linewidth=linewidth,
1862
1868
  dpi=dpi,
1863
1869
  callback=callback,
1870
+ save_path=save_path
1864
1871
  )
1865
1872
  elif type == "histkde":
1866
1873
  histplot(
@@ -1875,6 +1882,7 @@ def distribution_by_class(
1875
1882
  linewidth=linewidth,
1876
1883
  dpi=dpi,
1877
1884
  callback=callback,
1885
+ save_path=save_path
1878
1886
  )
1879
1887
 
1880
1888
 
@@ -1892,6 +1900,7 @@ def scatter_by_class(
1892
1900
  height: int = hs_fig.height,
1893
1901
  linewidth: float = hs_fig.line_width,
1894
1902
  dpi: int = hs_fig.dpi,
1903
+ save_path: str = None,
1895
1904
  callback: any = None,
1896
1905
  ) -> None:
1897
1906
  """종속변수(y)와 각 연속형 독립변수(x) 간 산점도/볼록껍질을 그린다.
@@ -1939,10 +1948,14 @@ def scatter_by_class(
1939
1948
 
1940
1949
  if outline:
1941
1950
  for v in group:
1942
- convex_hull(data, v[0], v[1], hue, palette, width, height, linewidth, dpi, callback)
1951
+ convex_hull(data=data, xname=v[0], yname=v[1], hue=hue, palette=palette,
1952
+ width=width, height=height, linewidth=linewidth, dpi=dpi, callback=callback,
1953
+ save_path=save_path)
1943
1954
  else:
1944
1955
  for v in group:
1945
- scatterplot(data, v[0], v[1], hue, palette, width, height, linewidth, dpi, callback)
1956
+ scatterplot(data=data, xname=v[0], yname=v[1], hue=hue, palette=palette,
1957
+ width=width, height=height, linewidth=linewidth, dpi=dpi, callback=callback,
1958
+ save_path=save_path)
1946
1959
 
1947
1960
 
1948
1961
  # ===================================================================
@@ -1951,7 +1964,7 @@ def scatter_by_class(
1951
1964
  def categorical_target_distribution(
1952
1965
  data: DataFrame,
1953
1966
  yname: str,
1954
- xnames: list | str | None = None,
1967
+ hue: list | str | None = None,
1955
1968
  kind: str = "box",
1956
1969
  kde_fill: bool = True,
1957
1970
  palette: str | None = None,
@@ -1960,6 +1973,7 @@ def categorical_target_distribution(
1960
1973
  linewidth: float = hs_fig.line_width,
1961
1974
  dpi: int = hs_fig.dpi,
1962
1975
  cols: int = 2,
1976
+ save_path: str = None,
1963
1977
  callback: any = None,
1964
1978
  ) -> None:
1965
1979
  """명목형 변수별로 종속변수 분포 차이를 시각화한다.
@@ -1967,7 +1981,7 @@ def categorical_target_distribution(
1967
1981
  Args:
1968
1982
  data (DataFrame): 시각화할 데이터.
1969
1983
  yname (str): 종속변수 컬럼명(연속형 추천).
1970
- xnames (list|str|None): 명목형 독립변수 목록. None이면 자동 탐지.
1984
+ hue (list|str|None): 명목형 독립변수 목록. None이면 자동 탐지.
1971
1985
  kind (str): 'box', 'violin', 'kde'.
1972
1986
  kde_fill (bool): kind='kde'일 때 영역 채우기 여부.
1973
1987
  palette (str|None): 팔레트 이름.
@@ -1983,13 +1997,13 @@ def categorical_target_distribution(
1983
1997
  """
1984
1998
 
1985
1999
  # 명목형 컬럼 후보: object, category, bool
1986
- if xnames is None:
2000
+ if hue is None:
1987
2001
  cat_cols = data.select_dtypes(include=["object", "category", "bool", "boolean"]).columns
1988
2002
  target_cols = [c for c in cat_cols if c != yname]
1989
- elif isinstance(xnames, str):
1990
- target_cols = [xnames]
2003
+ elif isinstance(hue, str):
2004
+ target_cols = [hue]
1991
2005
  else:
1992
- target_cols = list(xnames)
2006
+ target_cols = list(hue)
1993
2007
 
1994
2008
  if len(target_cols) == 0:
1995
2009
  return
@@ -2030,7 +2044,7 @@ def categorical_target_distribution(
2030
2044
 
2031
2045
 
2032
2046
  # ===================================================================
2033
- #
2047
+ # ROC 커브를 시각화 한다.
2034
2048
  # ===================================================================
2035
2049
  def roc_curve_plot(
2036
2050
  fit,
@@ -2100,14 +2114,13 @@ def roc_curve_plot(
2100
2114
 
2101
2115
 
2102
2116
  # ===================================================================
2103
- #
2117
+ # 혼동행렬 시각화
2104
2118
  # ===================================================================
2105
2119
  def confusion_matrix_plot(
2106
2120
  fit,
2107
2121
  threshold: float = 0.5,
2108
2122
  width: int = hs_fig.width,
2109
2123
  height: int = hs_fig.height,
2110
- linewidth: float = hs_fig.line_width,
2111
2124
  dpi: int = hs_fig.dpi,
2112
2125
  save_path: str = None,
2113
2126
  callback: any = None,
@@ -2120,7 +2133,6 @@ def confusion_matrix_plot(
2120
2133
  threshold (float): 예측 확률을 이진 분류로 변환할 임계값. 기본값 0.5.
2121
2134
  width (int): 캔버스 가로 픽셀.
2122
2135
  height (int): 캔버스 세로 픽셀.
2123
- linewidth (float): 선 굵기 (현재 사용되지 않음).
2124
2136
  dpi (int): 해상도.
2125
2137
  callback (Callable|None): Axes 후처리 콜백.
2126
2138
  ax (Axes|None): 외부에서 전달한 Axes. None이면 새로 생성.
@@ -2152,7 +2164,7 @@ def confusion_matrix_plot(
2152
2164
 
2153
2165
 
2154
2166
  # ===================================================================
2155
- #
2167
+ # 레이더 차트(방사형 차트)
2156
2168
  # ===================================================================
2157
2169
  def radarplot(
2158
2170
  df: DataFrame,
@@ -2279,3 +2291,118 @@ def radarplot(
2279
2291
  ax.set_title('Radar Chart', pad=20)
2280
2292
 
2281
2293
  finalize_plot(ax, callback, outparams, save_path)
2294
+
2295
+
2296
+ # ===================================================================
2297
+ # 연속형 데이터 분포 시각화 (KDE + Boxplot)
2298
+ # ===================================================================
2299
+ def distribution_plot(
2300
+ data: DataFrame,
2301
+ column: str,
2302
+ clevel: float = 0.95,
2303
+ orient: str = "h",
2304
+ hue: str | None = None,
2305
+ kind: str = "boxplot",
2306
+ width: int = hs_fig.width,
2307
+ height: int = hs_fig.height,
2308
+ linewidth: float = hs_fig.line_width,
2309
+ dpi: int = hs_fig.dpi,
2310
+ save_path: str = None,
2311
+ callback: any = None,
2312
+ ) -> None:
2313
+ """연속형 데이터의 분포를 KDE와 Boxplot으로 시각화한다.
2314
+
2315
+ 1행 2열의 서브플롯을 생성하여:
2316
+ - 왼쪽: KDE with 신뢰구간
2317
+ - 오른쪽: Boxplot
2318
+
2319
+ Args:
2320
+ data (DataFrame): 시각화할 데이터.
2321
+ column (str): 분석할 컬럼명.
2322
+ clevel (float): KDE 신뢰수준 (0~1). 기본값 0.95.
2323
+ orient (str): Boxplot 방향 ('v' 또는 'h'). 기본값 'h'.
2324
+ hue (str|None): 명목형 컬럼명. 지정하면 각 범주별로 행을 늘려 KDE와 boxplot을 그림.
2325
+ kind (str): 두 번째 그래프의 유형 (boxplot, hist). 기본값 "boxplot".
2326
+ width (int): 캔버스 가로 픽셀.
2327
+ height (int): 캔버스 세로 픽셀.
2328
+ linewidth (float): 선 굵기.
2329
+ dpi (int): 그림 크기 및 해상도.
2330
+ save_path (str|None): 저장 경로.
2331
+ callback (Callable|None): Axes 후처리 콜백.
2332
+
2333
+ Returns:
2334
+ None
2335
+ """
2336
+ if hue is None:
2337
+ # 1행 2열 서브플롯 생성
2338
+ fig, axes = get_default_ax(width, height, rows=1, cols=2, dpi=dpi)
2339
+
2340
+ kde_confidence_interval(
2341
+ data=data,
2342
+ xnames=column,
2343
+ clevel=clevel,
2344
+ linewidth=linewidth,
2345
+ ax=axes[0],
2346
+ )
2347
+
2348
+ if kind == "hist":
2349
+ histplot(
2350
+ df=data,
2351
+ xname=column,
2352
+ linewidth=linewidth,
2353
+ ax=axes[1]
2354
+ )
2355
+ else:
2356
+ boxplot(
2357
+ df=data[column],
2358
+ linewidth=linewidth,
2359
+ ax=axes[1]
2360
+ )
2361
+
2362
+ fig.suptitle(f"Distribution of {column}", fontsize=14, y=1.02)
2363
+ else:
2364
+ if hue not in data.columns:
2365
+ raise ValueError(f"hue column '{hue}' not found in DataFrame")
2366
+
2367
+ categories = list(pd.Series(data[hue].dropna().unique()).sort_values())
2368
+ n_cat = len(categories) if categories else 1
2369
+
2370
+ fig, axes = get_default_ax(width, height, rows=n_cat, cols=2, dpi=dpi)
2371
+ axes_2d = np.atleast_2d(axes)
2372
+
2373
+ for idx, cat in enumerate(categories):
2374
+ subset = data[data[hue] == cat]
2375
+ left_ax, right_ax = axes_2d[idx, 0], axes_2d[idx, 1]
2376
+
2377
+ kde_confidence_interval(
2378
+ data=subset,
2379
+ xnames=column,
2380
+ clevel=clevel,
2381
+ linewidth=linewidth,
2382
+ ax=left_ax,
2383
+ )
2384
+ left_ax.set_title(f"{hue} = {cat}")
2385
+
2386
+ if kind == "hist":
2387
+ histplot(
2388
+ df=subset,
2389
+ xname=column,
2390
+ linewidth=linewidth,
2391
+ ax=right_ax,
2392
+ )
2393
+ else:
2394
+ boxplot(
2395
+ df=subset[column],
2396
+ linewidth=linewidth,
2397
+ ax=right_ax
2398
+ )
2399
+
2400
+ fig.suptitle(f"Distribution of {column} by {hue}", fontsize=14, y=1.02)
2401
+
2402
+ plt.tight_layout()
2403
+
2404
+ if save_path:
2405
+ plt.savefig(save_path, bbox_inches='tight', dpi=dpi)
2406
+ plt.close()
2407
+ else:
2408
+ plt.show()