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/hs_stats.py
CHANGED
|
@@ -35,6 +35,8 @@ from statsmodels.stats.diagnostic import linear_reset, het_breuschpagan, het_whi
|
|
|
35
35
|
from statsmodels.stats.outliers_influence import variance_inflation_factor
|
|
36
36
|
from statsmodels.stats.multitest import multipletests
|
|
37
37
|
from statsmodels.stats.stattools import durbin_watson
|
|
38
|
+
from statsmodels.regression.linear_model import RegressionResultsWrapper
|
|
39
|
+
from statsmodels.discrete.discrete_model import BinaryResults
|
|
38
40
|
|
|
39
41
|
from pingouin import anova, pairwise_tukey, welch_anova, pairwise_gameshowell
|
|
40
42
|
|
|
@@ -58,21 +60,22 @@ def missing_values(data: DataFrame, *fields: str):
|
|
|
58
60
|
- missing_rate (float): 전체 행에서 결측치의 비율(%)
|
|
59
61
|
|
|
60
62
|
Examples:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
```python
|
|
64
|
+
from hossam import *
|
|
65
|
+
from pandas import DataFrame
|
|
66
|
+
|
|
67
|
+
# 전체 컬럼에 대한 결측치 확인:
|
|
68
|
+
df = DataFrame({'x': [1, 2, None, 4], 'y': [10, None, None, 40]})
|
|
69
|
+
result = hs_stats.missing_values(df)
|
|
70
|
+
print(result)
|
|
71
|
+
|
|
72
|
+
# 특정 컬럼만 분석:
|
|
73
|
+
result = hs_stats.missing_values(df, 'x', 'y')
|
|
74
|
+
print(result)
|
|
75
|
+
```
|
|
73
76
|
"""
|
|
74
77
|
if not fields:
|
|
75
|
-
fields = data.columns
|
|
78
|
+
fields = tuple(data.columns)
|
|
76
79
|
|
|
77
80
|
result = []
|
|
78
81
|
for f in fields:
|
|
@@ -121,18 +124,20 @@ def outlier_table(data: DataFrame, *fields: str):
|
|
|
121
124
|
- outlier_rate (float): 이상치 비율(%)
|
|
122
125
|
|
|
123
126
|
Examples:
|
|
124
|
-
|
|
127
|
+
```python
|
|
128
|
+
from hossam import *
|
|
129
|
+
from pandas import DataFrame
|
|
125
130
|
|
|
126
|
-
|
|
127
|
-
>>> import pandas as pd
|
|
128
|
-
>>> df = pd.DataFrame({'x': [1, 2, 3, 100], 'y': [10, 20, 30, 40]})
|
|
129
|
-
>>> result = outlier_table(df)
|
|
130
|
-
>>> print(result)
|
|
131
|
+
df = DataFrame({'x': [1, 2, 3, 100], 'y': [10, 20, 30, 40]})
|
|
131
132
|
|
|
132
|
-
|
|
133
|
+
# 전체 숫자형 컬럼에 대한 이상치 경계 확인:
|
|
134
|
+
result = hs_stats.outlier_table(df)
|
|
135
|
+
print(result)
|
|
133
136
|
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
# 특정 컬럼만 분석:
|
|
138
|
+
result = hs_stats.outlier_table(df, 'x', 'y')
|
|
139
|
+
print(result[['q1', 'q3', 'up', 'down']])
|
|
140
|
+
```
|
|
136
141
|
|
|
137
142
|
Notes:
|
|
138
143
|
- DOWN 미만이거나 UP 초과인 값은 이상치(outlier)로 간주됩니다.
|
|
@@ -140,7 +145,7 @@ def outlier_table(data: DataFrame, *fields: str):
|
|
|
140
145
|
- Tukey의 1.5 * IQR 규칙을 사용합니다 (상자그림의 표준 방법).
|
|
141
146
|
"""
|
|
142
147
|
if not fields:
|
|
143
|
-
fields = data.columns
|
|
148
|
+
fields = tuple(data.columns)
|
|
144
149
|
|
|
145
150
|
result = []
|
|
146
151
|
for f in fields:
|
|
@@ -195,80 +200,160 @@ def outlier_table(data: DataFrame, *fields: str):
|
|
|
195
200
|
|
|
196
201
|
|
|
197
202
|
# ===================================================================
|
|
198
|
-
#
|
|
203
|
+
# 확장된 기술통계량 (Extended Descriptive Statistics)
|
|
199
204
|
# ===================================================================
|
|
200
|
-
def
|
|
201
|
-
"""데이터프레임의
|
|
205
|
+
def describe(data: DataFrame, *fields: str, columns: list | None = None):
|
|
206
|
+
"""데이터프레임의 연속형 변수의 단위 및 현실성을 평가하기 위해 확장된 기술통계량을 반환한다.
|
|
202
207
|
|
|
203
|
-
각
|
|
208
|
+
각 연속형(숫자형) 컬럼의 기술통계량(describe)을 구하고, 이에 사분위수 범위(IQR),
|
|
209
|
+
이상치 경계값(UP, DOWN), 왜도(skew), 이상치 개수 및 비율, 분포 특성, 로그변환 필요성을
|
|
210
|
+
추가하여 반환한다.
|
|
204
211
|
|
|
205
212
|
Args:
|
|
206
213
|
data (DataFrame): 분석 대상 데이터프레임.
|
|
207
|
-
*fields (str): 분석할 컬럼명 목록. 지정하지 않으면 모든
|
|
214
|
+
*fields (str): 분석할 컬럼명 목록. 지정하지 않으면 모든 숫자형 컬럼을 처리.
|
|
215
|
+
columns (list, optional): 반환할 통계량 컬럼 목록. None이면 모든 통계량 반환.
|
|
208
216
|
|
|
209
217
|
Returns:
|
|
210
|
-
DataFrame: 각
|
|
211
|
-
|
|
218
|
+
DataFrame: 각 필드별 확장된 기술통계량을 포함한 데이터프레임.
|
|
219
|
+
행은 다음과 같은 통계량을 포함:
|
|
212
220
|
|
|
213
|
-
- count (
|
|
214
|
-
-
|
|
221
|
+
- count (float): 비결측치의 수
|
|
222
|
+
- mean (float): 평균값
|
|
223
|
+
- std (float): 표준편차
|
|
224
|
+
- min (float): 최소값
|
|
225
|
+
- 25% (float): 제1사분위수 (Q1)
|
|
226
|
+
- 50% (float): 제2사분위수 (중앙값)
|
|
227
|
+
- 75% (float): 제3사분위수 (Q3)
|
|
228
|
+
- max (float): 최대값
|
|
229
|
+
- iqr (float): 사분위 범위 (Q3 - Q1)
|
|
230
|
+
- up (float): 이상치 상한 경계값 (Q3 + 1.5 * IQR)
|
|
231
|
+
- down (float): 이상치 하한 경계값 (Q1 - 1.5 * IQR)
|
|
232
|
+
- skew (float): 왜도
|
|
233
|
+
- outlier_count (int): 이상치 개수
|
|
234
|
+
- outlier_rate (float): 이상치 비율(%)
|
|
235
|
+
- dist (str): 분포 특성 ("극단 우측 꼬리", "거의 대칭" 등)
|
|
236
|
+
- log_need (str): 로그변환 필요성 ("높음", "중간", "낮음")
|
|
215
237
|
|
|
216
238
|
Examples:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
>>> result = category_table(df)
|
|
227
|
-
>>> print(result)
|
|
239
|
+
```python
|
|
240
|
+
from hossam import *
|
|
241
|
+
from pandas import DataFrame
|
|
242
|
+
|
|
243
|
+
df = DataFrame({
|
|
244
|
+
'x': [1, 2, 3, 4, 5, 100],
|
|
245
|
+
'y': [10, 20, 30, 40, 50, 60],
|
|
246
|
+
'z': ['a', 'b', 'c', 'd', 'e', 'f']
|
|
247
|
+
})
|
|
228
248
|
|
|
229
|
-
|
|
249
|
+
# 전체 숫자형 컬럼에 대한 확장된 기술통계:
|
|
250
|
+
result = hs_stats.describe(df)
|
|
251
|
+
print(result)
|
|
230
252
|
|
|
231
|
-
|
|
232
|
-
|
|
253
|
+
# 특정 컬럼만 분석:
|
|
254
|
+
result = hs_stats.describe(df, 'x', 'y')
|
|
255
|
+
print(result)
|
|
256
|
+
```
|
|
233
257
|
|
|
234
258
|
Notes:
|
|
235
|
-
-
|
|
236
|
-
-
|
|
237
|
-
-
|
|
259
|
+
- 숫자형이 아닌 컬럼은 자동으로 제외됩니다.
|
|
260
|
+
- 결과는 필드(컬럼)가 행으로, 통계량이 열로 구성됩니다.
|
|
261
|
+
- Tukey의 1.5 * IQR 규칙을 사용하여 이상치를 판정합니다.
|
|
262
|
+
- 분포 특성은 왜도 값으로 판정합니다.
|
|
263
|
+
- 로그변환 필요성은 왜도의 절댓값 크기로 판정합니다.
|
|
238
264
|
"""
|
|
239
265
|
if not fields:
|
|
240
|
-
|
|
241
|
-
fields = data.select_dtypes(include=['object', 'category', 'bool']).columns
|
|
266
|
+
fields = tuple(data.select_dtypes(include=['int', 'int32', 'int64', 'float', 'float32', 'float64']).columns)
|
|
242
267
|
|
|
243
|
-
|
|
268
|
+
# 기술통계량 구하기
|
|
269
|
+
desc = data[list(fields)].describe().T
|
|
270
|
+
# 각 컬럼별 결측치 수(null_count) 추가
|
|
271
|
+
null_counts = data[list(fields)].isnull().sum()
|
|
272
|
+
desc.insert(1, 'null_count', null_counts)
|
|
273
|
+
|
|
274
|
+
# 추가 통계량 계산
|
|
275
|
+
additional_stats = []
|
|
244
276
|
for f in fields:
|
|
245
|
-
#
|
|
246
|
-
if data[f].
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
277
|
+
# 숫자 타입이 아니라면 건너뜀
|
|
278
|
+
if data[f].dtype not in [
|
|
279
|
+
'int',
|
|
280
|
+
'int32',
|
|
281
|
+
'int64',
|
|
282
|
+
'float',
|
|
283
|
+
'float32',
|
|
284
|
+
'float64',
|
|
285
|
+
'int64',
|
|
286
|
+
'float64',
|
|
287
|
+
'float32'
|
|
253
288
|
]:
|
|
254
289
|
continue
|
|
255
290
|
|
|
256
|
-
#
|
|
257
|
-
|
|
291
|
+
# 사분위수
|
|
292
|
+
q1 = data[f].quantile(q=0.25)
|
|
293
|
+
q3 = data[f].quantile(q=0.75)
|
|
258
294
|
|
|
259
|
-
|
|
260
|
-
|
|
295
|
+
# 이상치 경계 (Tukey's fences)
|
|
296
|
+
iqr = q3 - q1
|
|
297
|
+
down = q1 - 1.5 * iqr
|
|
298
|
+
up = q3 + 1.5 * iqr
|
|
261
299
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
"category": category,
|
|
265
|
-
"count": count,
|
|
266
|
-
"rate": rate
|
|
267
|
-
}
|
|
300
|
+
# 왜도
|
|
301
|
+
skew = data[f].skew()
|
|
268
302
|
|
|
269
|
-
|
|
303
|
+
# 이상치 개수 및 비율
|
|
304
|
+
outlier_count = ((data[f] < down) | (data[f] > up)).sum()
|
|
305
|
+
outlier_rate = (outlier_count / len(data)) * 100
|
|
270
306
|
|
|
271
|
-
|
|
307
|
+
# 분포 특성 판정 (왜도 기준)
|
|
308
|
+
abs_skew = abs(skew)
|
|
309
|
+
if abs_skew < 0.5:
|
|
310
|
+
dist = "거의 대칭"
|
|
311
|
+
elif abs_skew < 1.0:
|
|
312
|
+
if skew > 0:
|
|
313
|
+
dist = "약한 우측 꼬리"
|
|
314
|
+
else:
|
|
315
|
+
dist = "약한 좌측 꼬리"
|
|
316
|
+
elif abs_skew < 2.0:
|
|
317
|
+
if skew > 0:
|
|
318
|
+
dist = "중간 우측 꼬리"
|
|
319
|
+
else:
|
|
320
|
+
dist = "중간 좌측 꼬리"
|
|
321
|
+
else:
|
|
322
|
+
if skew > 0:
|
|
323
|
+
dist = "극단 우측 꼬리"
|
|
324
|
+
else:
|
|
325
|
+
dist = "극단 좌측 꼬리"
|
|
326
|
+
|
|
327
|
+
# 로그변환 필요성 판정
|
|
328
|
+
if abs_skew < 0.5:
|
|
329
|
+
log_need = "낮음"
|
|
330
|
+
elif abs_skew < 1.0:
|
|
331
|
+
log_need = "중간"
|
|
332
|
+
else:
|
|
333
|
+
log_need = "높음"
|
|
334
|
+
|
|
335
|
+
additional_stats.append({
|
|
336
|
+
'field': f,
|
|
337
|
+
'iqr': iqr,
|
|
338
|
+
'up': up,
|
|
339
|
+
'down': down,
|
|
340
|
+
'outlier_count': outlier_count,
|
|
341
|
+
'outlier_rate': outlier_rate,
|
|
342
|
+
'skew': skew,
|
|
343
|
+
'dist': dist,
|
|
344
|
+
'log_need': log_need
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
additional_df = DataFrame(additional_stats).set_index('field')
|
|
348
|
+
|
|
349
|
+
# 결과 병합
|
|
350
|
+
result = concat([desc, additional_df], axis=1)
|
|
351
|
+
|
|
352
|
+
# columns 파라미터가 지정된 경우 해당 컬럼만 필터링
|
|
353
|
+
if columns is not None:
|
|
354
|
+
result = result[columns]
|
|
355
|
+
|
|
356
|
+
return result
|
|
272
357
|
|
|
273
358
|
|
|
274
359
|
# ===================================================================
|
|
@@ -284,7 +369,8 @@ def category_describe(data: DataFrame, *fields: str):
|
|
|
284
369
|
*fields (str): 분석할 컬럼명 목록. 지정하지 않으면 모든 명목형 컬럼을 처리.
|
|
285
370
|
|
|
286
371
|
Returns:
|
|
287
|
-
DataFrame: 각 컬럼별 최다/최소 범주 정보를 포함한
|
|
372
|
+
tuple[DataFrame, DataFrame]: 각 컬럼별 최다/최소 범주 정보를 포함한 데이터프레임과
|
|
373
|
+
각 범주별 빈도/비율 정보를 포함한 데이터프레임을 튜플로 반환.
|
|
288
374
|
다음 컬럼을 포함:
|
|
289
375
|
|
|
290
376
|
- 변수 (str): 컬럼명
|
|
@@ -294,22 +380,22 @@ def category_describe(data: DataFrame, *fields: str):
|
|
|
294
380
|
- 최소_비율(%) (float): 최소 범주의 비율
|
|
295
381
|
|
|
296
382
|
Examples:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
>>> result = category_describe(df)
|
|
307
|
-
>>> print(result)
|
|
383
|
+
```python
|
|
384
|
+
from hossam import *
|
|
385
|
+
from pandas import DataFrame
|
|
386
|
+
|
|
387
|
+
df = DataFrame({
|
|
388
|
+
'cut': ['Ideal', 'Premium', 'Good', 'Ideal', 'Premium'],
|
|
389
|
+
'color': ['E', 'F', 'G', 'E', 'F'],
|
|
390
|
+
'price': [100, 200, 150, 300, 120]
|
|
391
|
+
})
|
|
308
392
|
|
|
309
|
-
|
|
393
|
+
# 전체 명목형 컬럼에 대한 요약:
|
|
394
|
+
result, summary = hs_stats.category_describe(df)
|
|
310
395
|
|
|
311
|
-
|
|
312
|
-
|
|
396
|
+
# 특정 컬럼만 분석:
|
|
397
|
+
result, summary = hs_stats.category_describe(df, 'cut', 'color')
|
|
398
|
+
```
|
|
313
399
|
|
|
314
400
|
Notes:
|
|
315
401
|
- 숫자형 컬럼은 자동으로 제외됩니다.
|
|
@@ -320,6 +406,7 @@ def category_describe(data: DataFrame, *fields: str):
|
|
|
320
406
|
fields = data.select_dtypes(include=['object', 'category', 'bool']).columns
|
|
321
407
|
|
|
322
408
|
result = []
|
|
409
|
+
summary = []
|
|
323
410
|
for f in fields:
|
|
324
411
|
# 숫자형 컬럼은 건너뜀
|
|
325
412
|
if data[f].dtypes in [
|
|
@@ -335,40 +422,35 @@ def category_describe(data: DataFrame, *fields: str):
|
|
|
335
422
|
# 각 범주값의 빈도수 계산 (NaN 포함)
|
|
336
423
|
value_counts = data[f].value_counts(dropna=False)
|
|
337
424
|
|
|
425
|
+
# 범주별 빈도/비율 정보 추가 (category_table 기능)
|
|
426
|
+
for category, count in value_counts.items():
|
|
427
|
+
rate = (count / len(data)) * 100
|
|
428
|
+
result.append({
|
|
429
|
+
"변수": f,
|
|
430
|
+
"범주": category,
|
|
431
|
+
"빈도": count,
|
|
432
|
+
"비율(%)": round(rate, 2)
|
|
433
|
+
})
|
|
434
|
+
|
|
338
435
|
if len(value_counts) == 0:
|
|
339
436
|
continue
|
|
340
437
|
|
|
341
|
-
#
|
|
438
|
+
# 최다/최소 범주 정보 추가 (category_describe 기능)
|
|
342
439
|
max_category = value_counts.index[0]
|
|
343
440
|
max_count = value_counts.iloc[0]
|
|
344
441
|
max_rate = (max_count / len(data)) * 100
|
|
345
|
-
|
|
346
|
-
# 최소 범주 (마지막)
|
|
347
442
|
min_category = value_counts.index[-1]
|
|
348
443
|
min_count = value_counts.iloc[-1]
|
|
349
444
|
min_rate = (min_count / len(data)) * 100
|
|
350
|
-
|
|
351
|
-
iq = {
|
|
445
|
+
summary.append({
|
|
352
446
|
"변수": f,
|
|
353
447
|
"최다_범주": max_category,
|
|
354
448
|
"최다_비율(%)": round(max_rate, 2),
|
|
355
449
|
"최소_범주": min_category,
|
|
356
450
|
"최소_비율(%)": round(min_rate, 2)
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
result.append(iq)
|
|
360
|
-
|
|
361
|
-
return DataFrame(result)
|
|
451
|
+
})
|
|
362
452
|
|
|
363
|
-
|
|
364
|
-
# Backward-compatibility alias for categorical summary
|
|
365
|
-
# 기존 함수명(category_summary)을 계속 지원합니다.
|
|
366
|
-
def category_summary(data: DataFrame, *fields: str):
|
|
367
|
-
"""Deprecated alias for category_describe.
|
|
368
|
-
|
|
369
|
-
기존 코드 호환을 위해 유지됩니다. 내부적으로 category_describe를 호출합니다.
|
|
370
|
-
"""
|
|
371
|
-
return category_describe(data, *fields)
|
|
453
|
+
return DataFrame(result), DataFrame(summary).set_index("변수")
|
|
372
454
|
|
|
373
455
|
# ===================================================================
|
|
374
456
|
# 정규성 검정 (Normal Test)
|
|
@@ -403,19 +485,25 @@ def normal_test(data: DataFrame, columns: list | str | None = None, method: str
|
|
|
403
485
|
ValueError: 메서드가 "n" 또는 "s"가 아닐 경우.
|
|
404
486
|
|
|
405
487
|
Examples:
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
488
|
+
```python
|
|
489
|
+
from hossam import *
|
|
490
|
+
from pandas import DataFrame
|
|
491
|
+
import numpy as np
|
|
492
|
+
|
|
493
|
+
df = DataFrame({
|
|
494
|
+
'x': np.random.normal(0, 1, 100),
|
|
495
|
+
'y': np.random.exponential(2, 100)
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
# 모든 수치형 컬럼 검정
|
|
499
|
+
result = hs_stats.normal_test(df, method='n')
|
|
500
|
+
|
|
501
|
+
# 특정 컬럼만 검정 (리스트)
|
|
502
|
+
result = hs_stats.normal_test(df, columns=['x'], method='n')
|
|
503
|
+
|
|
504
|
+
# 특정 컬럼만 검정 (문자열)
|
|
505
|
+
result = hs_stats.normal_test(df, columns='x, y', method='n')
|
|
506
|
+
```
|
|
419
507
|
"""
|
|
420
508
|
if method not in ["n", "s"]:
|
|
421
509
|
raise ValueError(f"method는 'n' 또는 's'여야 합니다. 입력값: {method}")
|
|
@@ -515,22 +603,29 @@ def equal_var_test(data: DataFrame, columns: list | str | None = None, normal_di
|
|
|
515
603
|
ValueError: 수치형 컬럼이 2개 미만일 경우 (검정에 최소 2개 필요).
|
|
516
604
|
|
|
517
605
|
Examples:
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
606
|
+
```python
|
|
607
|
+
from hossam import *
|
|
608
|
+
from pandas import DataFrame
|
|
609
|
+
import numpy as np
|
|
610
|
+
|
|
611
|
+
df = DataFrame({
|
|
612
|
+
'x': np.random.normal(0, 1, 100),
|
|
613
|
+
'y': np.random.normal(0, 1, 100),
|
|
614
|
+
'z': np.random.normal(0, 2, 100)
|
|
615
|
+
})
|
|
616
|
+
|
|
617
|
+
# 모든 수치형 컬럼 자동 판별
|
|
618
|
+
result = hs_stats.equal_var_test(df)
|
|
619
|
+
|
|
620
|
+
# 특정 컬럼만 검정 (리스트)
|
|
621
|
+
result = hs_stats.equal_var_test(df, columns=['x', 'y'])
|
|
622
|
+
|
|
623
|
+
# 특정 컬럼만 검정 (문자열)
|
|
624
|
+
result = hs_stats.equal_var_test(df, columns='x, y')
|
|
625
|
+
|
|
626
|
+
# 명시적 지정
|
|
627
|
+
result = hs_stats.equal_var_test(df, normal_dist=True)
|
|
628
|
+
```
|
|
534
629
|
"""
|
|
535
630
|
# columns가 문자열인 경우 리스트로 변환
|
|
536
631
|
if isinstance(columns, str):
|
|
@@ -627,15 +722,19 @@ def ttest_1samp(data, mean_value: float = 0.0) -> DataFrame:
|
|
|
627
722
|
- interpretation (str): 검정 결과 해석 문자열
|
|
628
723
|
|
|
629
724
|
Examples:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
725
|
+
```python
|
|
726
|
+
from hossam import *
|
|
727
|
+
from pandas import Series
|
|
728
|
+
import numpy as np
|
|
729
|
+
|
|
730
|
+
# 리스트 데이터로 검정
|
|
731
|
+
data = [5.1, 4.9, 5.3, 5.0, 4.8]
|
|
732
|
+
result = hs_stats.ttest_1samp(data, mean_value=5.0)
|
|
733
|
+
|
|
734
|
+
# Series 데이터로 검정
|
|
735
|
+
s = Series(np.random.normal(5, 1, 100))
|
|
736
|
+
result = hs_stats.ttest_1samp(s, mean_value=5)
|
|
737
|
+
```
|
|
639
738
|
"""
|
|
640
739
|
# 데이터를 Series로 변환하고 이름 감지
|
|
641
740
|
if isinstance(data, Series):
|
|
@@ -724,17 +823,21 @@ def ttest_ind(x, y, equal_var: bool | None = None) -> DataFrame:
|
|
|
724
823
|
- interpretation (str): 검정 결과 해석
|
|
725
824
|
|
|
726
825
|
Examples:
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
826
|
+
```python
|
|
827
|
+
from hossam import *
|
|
828
|
+
from pandas import Series, DataFrame
|
|
829
|
+
import numpy as np
|
|
830
|
+
|
|
831
|
+
# 리스트로 검정
|
|
832
|
+
group1 = [5.1, 4.9, 5.3, 5.0, 4.8]
|
|
833
|
+
group2 = [5.5, 5.7, 5.4, 5.6, 5.8]
|
|
834
|
+
result = hs_stats.ttest_ind(group1, group2)
|
|
835
|
+
|
|
836
|
+
# Series로 검정
|
|
837
|
+
s1 = Series(np.random.normal(5, 1, 100))
|
|
838
|
+
s2 = Series(np.random.normal(5.5, 1, 100))
|
|
839
|
+
result = hs_stats.ttest_ind(s1, s2, equal_var=False)
|
|
840
|
+
```
|
|
738
841
|
"""
|
|
739
842
|
# 데이터를 Series로 변환
|
|
740
843
|
if isinstance(x, Series):
|
|
@@ -835,17 +938,21 @@ def ttest_rel(x, y, parametric: bool | None = None) -> DataFrame:
|
|
|
835
938
|
- interpretation (str): 검정 결과 해석
|
|
836
939
|
|
|
837
940
|
Examples:
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
941
|
+
```python
|
|
942
|
+
from hossam import *
|
|
943
|
+
from pandas import Series
|
|
944
|
+
import numpy as np
|
|
945
|
+
|
|
946
|
+
# 리스트로 검정
|
|
947
|
+
before = [5.1, 4.9, 5.3, 5.0, 4.8]
|
|
948
|
+
after = [5.5, 5.2, 5.7, 5.3, 5.1]
|
|
949
|
+
result = hs_stats.ttest_rel(before, after)
|
|
950
|
+
|
|
951
|
+
# Series로 검정
|
|
952
|
+
s1 = Series(np.random.normal(5, 1, 100))
|
|
953
|
+
s2 = Series(np.random.normal(5.3, 1, 100))
|
|
954
|
+
result = hs_stats.ttest_rel(s1, s2, parametric=False)
|
|
955
|
+
```
|
|
849
956
|
"""
|
|
850
957
|
# 데이터를 Series로 변환
|
|
851
958
|
if isinstance(x, Series):
|
|
@@ -958,11 +1065,11 @@ def vif_filter(
|
|
|
958
1065
|
DataFrame: VIF가 threshold 이하인 변수만 남은 데이터프레임 (원본 컬럼 순서 유지)
|
|
959
1066
|
|
|
960
1067
|
Examples:
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1068
|
+
```python
|
|
1069
|
+
# 기본 사용 예
|
|
1070
|
+
from hossam import *
|
|
1071
|
+
filtered = hs_stats.vif_filter(df, yname="target", ignore=["id"], threshold=10.0)
|
|
1072
|
+
```
|
|
966
1073
|
"""
|
|
967
1074
|
|
|
968
1075
|
df = data.copy()
|
|
@@ -1038,15 +1145,6 @@ def vif_filter(
|
|
|
1038
1145
|
|
|
1039
1146
|
return result
|
|
1040
1147
|
|
|
1041
|
-
# -------------------------------------------------------------------
|
|
1042
|
-
# Backward-compatibility alias for describe (typo support)
|
|
1043
|
-
# 오타(discribe)로 사용된 경우를 지원하여 혼란을 줄입니다.
|
|
1044
|
-
def discribe(data: DataFrame, *fields: str, columns: list = None):
|
|
1045
|
-
"""Deprecated alias for describe.
|
|
1046
|
-
|
|
1047
|
-
내부적으로 describe를 호출합니다.
|
|
1048
|
-
"""
|
|
1049
|
-
return describe(data, *fields, columns=columns)
|
|
1050
1148
|
|
|
1051
1149
|
|
|
1052
1150
|
# ===================================================================
|
|
@@ -1065,12 +1163,12 @@ def trend(x: any, y: any, degree: int = 1, value_count: int = 100) -> Tuple[np.n
|
|
|
1065
1163
|
tuple: (v_trend, t_trend)
|
|
1066
1164
|
|
|
1067
1165
|
Examples:
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1166
|
+
```python
|
|
1167
|
+
# 2차 다항 회귀 추세선
|
|
1168
|
+
from hossam import *
|
|
1169
|
+
vx, vy = hs_stats.trend(x, y, degree=2, value_count=200)
|
|
1170
|
+
print(len(vx), len(vy)) # 200, 200
|
|
1171
|
+
```
|
|
1074
1172
|
"""
|
|
1075
1173
|
# [ a, b, c ] ==> ax^2 + bx + c
|
|
1076
1174
|
x_arr = np.asarray(x)
|
|
@@ -1116,14 +1214,18 @@ def ols_report(fit, data, full=False, alpha=0.05):
|
|
|
1116
1214
|
- 회귀계수 표 (`rdf`, DataFrame)
|
|
1117
1215
|
|
|
1118
1216
|
Examples:
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1217
|
+
```python
|
|
1218
|
+
from hossam import *
|
|
1219
|
+
|
|
1220
|
+
df = hs_util.load_data("some_data.csv")
|
|
1221
|
+
fit = hs_stats.ols(df, yname="target")
|
|
1222
|
+
|
|
1223
|
+
# 전체 리포트
|
|
1224
|
+
pdf, rdf, result_report, model_report, variable_reports, eq = hs_stats.ols_report(fit, data, full=True)
|
|
1225
|
+
|
|
1226
|
+
# 간단한 버전 (성능지표, 회귀계수 테이블만)
|
|
1227
|
+
pdf, rdf = hs_stats.ols_report(fit, data)
|
|
1228
|
+
```
|
|
1127
1229
|
"""
|
|
1128
1230
|
|
|
1129
1231
|
# summary2() 결과에서 실제 회귀계수 DataFrame 추출
|
|
@@ -1270,7 +1372,7 @@ def ols_report(fit, data, full=False, alpha=0.05):
|
|
|
1270
1372
|
if full:
|
|
1271
1373
|
return pdf, rdf, result_report, model_report, variable_reports, equation_text
|
|
1272
1374
|
else:
|
|
1273
|
-
return pdf
|
|
1375
|
+
return pdf, rdf
|
|
1274
1376
|
|
|
1275
1377
|
|
|
1276
1378
|
# ===================================================================
|
|
@@ -1309,23 +1411,26 @@ def ols(df: DataFrame, yname: str, report=False):
|
|
|
1309
1411
|
- equation_text: 회귀식 문자열 (str)
|
|
1310
1412
|
|
|
1311
1413
|
Examples:
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1414
|
+
```python
|
|
1415
|
+
from hossam import *
|
|
1416
|
+
from pandas import DataFrame
|
|
1417
|
+
import numpy as np
|
|
1418
|
+
|
|
1419
|
+
df = DataFrame({
|
|
1420
|
+
'target': np.random.normal(100, 10, 100),
|
|
1421
|
+
'x1': np.random.normal(0, 1, 100),
|
|
1422
|
+
'x2': np.random.normal(0, 1, 100)
|
|
1423
|
+
})
|
|
1424
|
+
|
|
1425
|
+
# 적합 결과만 반환
|
|
1426
|
+
fit = hs_stats.ols(df, 'target')
|
|
1427
|
+
|
|
1428
|
+
# 요약 리포트 반환
|
|
1429
|
+
fit, pdf, rdf = hs_stats.ols(df, 'target', report=1)
|
|
1430
|
+
|
|
1431
|
+
# 풀 리포트 반환
|
|
1432
|
+
fit, pdf, rdf, result_report, model_report, var_reports, eq = hs_stats.ols(df, 'target', report=2)
|
|
1433
|
+
```
|
|
1329
1434
|
"""
|
|
1330
1435
|
x = df.drop(yname, axis=1)
|
|
1331
1436
|
y = df[yname]
|
|
@@ -1340,7 +1445,7 @@ def ols(df: DataFrame, yname: str, report=False):
|
|
|
1340
1445
|
return linear_fit
|
|
1341
1446
|
elif report == 1 or report == 'summary':
|
|
1342
1447
|
# 요약 리포트 (full=False)
|
|
1343
|
-
pdf, rdf
|
|
1448
|
+
pdf, rdf = ols_report(linear_fit, df, full=False, alpha=0.05)
|
|
1344
1449
|
return linear_fit, pdf, rdf
|
|
1345
1450
|
elif report == 2 or report == 'full' or report is True:
|
|
1346
1451
|
# 풀 리포트 (full=True)
|
|
@@ -1378,14 +1483,26 @@ def logit_report(fit, data, threshold=0.5, full=False, alpha=0.05):
|
|
|
1378
1483
|
- 회귀계수 표 (`rdf`, DataFrame)
|
|
1379
1484
|
|
|
1380
1485
|
Examples:
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1486
|
+
```python
|
|
1487
|
+
from hossam import *
|
|
1488
|
+
from pandas import DataFrame
|
|
1489
|
+
import numpy as np
|
|
1490
|
+
|
|
1491
|
+
df = DataFrame({
|
|
1492
|
+
'target': np.random.binomial(1, 0.5, 100),
|
|
1493
|
+
'x1': np.random.normal(0, 1, 100),
|
|
1494
|
+
'x2': np.random.normal(0, 1, 100)
|
|
1495
|
+
})
|
|
1496
|
+
|
|
1497
|
+
# 로지스틱 회귀 적합
|
|
1498
|
+
fit = hs_stats.logit(df, yname="target")
|
|
1499
|
+
|
|
1500
|
+
# 전체 리포트
|
|
1501
|
+
cdf, rdf, result_report, model_report, variable_reports, cm = hs_stats.logit_report(fit, df, full=True)
|
|
1502
|
+
|
|
1503
|
+
# 간단한 버전 (주요 테이블만)
|
|
1504
|
+
cdf, rdf = hs_stats.logit_report(fit, df)
|
|
1505
|
+
```
|
|
1389
1506
|
"""
|
|
1390
1507
|
|
|
1391
1508
|
# -----------------------------
|
|
@@ -1567,23 +1684,26 @@ def logit(df: DataFrame, yname: str, report=False):
|
|
|
1567
1684
|
- variable_reports: 변수별 보고 문장 리스트 (list[str])
|
|
1568
1685
|
|
|
1569
1686
|
Examples:
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1687
|
+
```python
|
|
1688
|
+
from hossam import *
|
|
1689
|
+
from pandas import DataFrame
|
|
1690
|
+
import numpy as np
|
|
1691
|
+
|
|
1692
|
+
df = DataFrame({
|
|
1693
|
+
'target': np.random.binomial(1, 0.5, 100),
|
|
1694
|
+
'x1': np.random.normal(0, 1, 100),
|
|
1695
|
+
'x2': np.random.normal(0, 1, 100)
|
|
1696
|
+
})
|
|
1697
|
+
|
|
1698
|
+
# 적합 결과만 반환
|
|
1699
|
+
fit = hs_stats.logit(df, 'target')
|
|
1700
|
+
|
|
1701
|
+
# 요약 리포트 반환
|
|
1702
|
+
fit, rdf, result_report, var_reports = hs_stats.logit(df, 'target', report='summary')
|
|
1703
|
+
|
|
1704
|
+
# 풀 리포트 반환
|
|
1705
|
+
fit, cdf, rdf, result_report, model_report, var_reports = hs_stats.logit(df, 'target', report='full')
|
|
1706
|
+
```
|
|
1587
1707
|
"""
|
|
1588
1708
|
x = df.drop(yname, axis=1)
|
|
1589
1709
|
y = df[yname]
|
|
@@ -1636,12 +1756,11 @@ def ols_linearity_test(fit, power: int = 2, alpha: float = 0.05) -> DataFrame:
|
|
|
1636
1756
|
- 해석: 선형성 판정 (문자열)
|
|
1637
1757
|
|
|
1638
1758
|
Examples:
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
>>> print(result)
|
|
1759
|
+
```python
|
|
1760
|
+
from hossam import *
|
|
1761
|
+
fit = hs_stats.logit(df, 'target')
|
|
1762
|
+
result = hs_stats.ols_linearity_test(fit)
|
|
1763
|
+
```
|
|
1645
1764
|
|
|
1646
1765
|
Notes:
|
|
1647
1766
|
- p-value > alpha: 선형성 가정을 만족 (귀무가설 채택)
|
|
@@ -1736,12 +1855,11 @@ def ols_normality_test(fit, alpha: float = 0.05) -> DataFrame:
|
|
|
1736
1855
|
- 해석: 정규성 판정 (문자열)
|
|
1737
1856
|
|
|
1738
1857
|
Examples:
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
>>> print(result)
|
|
1858
|
+
```python
|
|
1859
|
+
from hossam import *
|
|
1860
|
+
fit = hs_stats.logit(df, 'target')
|
|
1861
|
+
result = hs_stats.ols_normality_test(fit)
|
|
1862
|
+
```
|
|
1745
1863
|
|
|
1746
1864
|
Notes:
|
|
1747
1865
|
- Shapiro-Wilk: 샘플 크기가 작을 때 (< 5000) 강력한 검정
|
|
@@ -1831,12 +1949,11 @@ def ols_variance_test(fit, alpha: float = 0.05) -> DataFrame:
|
|
|
1831
1949
|
- 해석: 등분산성 판정 (문자열)
|
|
1832
1950
|
|
|
1833
1951
|
Examples:
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
>>> print(result)
|
|
1952
|
+
```python
|
|
1953
|
+
from hossam import *
|
|
1954
|
+
fit = hs_stats.logit(df, 'target')
|
|
1955
|
+
result = hs_stats.ols_variance_test(fit)
|
|
1956
|
+
```
|
|
1840
1957
|
|
|
1841
1958
|
Notes:
|
|
1842
1959
|
- Breusch-Pagan: 잔차 제곱과 독립변수의 선형관계 검정
|
|
@@ -1922,22 +2039,11 @@ def ols_independence_test(fit, alpha: float = 0.05) -> DataFrame:
|
|
|
1922
2039
|
- 해석: 검정 결과 해석
|
|
1923
2040
|
|
|
1924
2041
|
Examples:
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
>>> df = pd.DataFrame({
|
|
1931
|
-
... 'x': range(100),
|
|
1932
|
-
... 'y': [i + np.random.randn() for i in range(100)]
|
|
1933
|
-
... })
|
|
1934
|
-
>>> X = sm.add_constant(df['x'])
|
|
1935
|
-
>>> model = sm.OLS(df['y'], X)
|
|
1936
|
-
>>> fit = model.fit()
|
|
1937
|
-
>>>
|
|
1938
|
-
>>> # 독립성 검정
|
|
1939
|
-
>>> result = ols_independence_test(fit)
|
|
1940
|
-
>>> print(result)
|
|
2042
|
+
```python
|
|
2043
|
+
from hossam import *
|
|
2044
|
+
fit = hs_stats.logit(df, 'target')
|
|
2045
|
+
result = hs_stats.ols_independence_test(fit)
|
|
2046
|
+
```
|
|
1941
2047
|
|
|
1942
2048
|
Notes:
|
|
1943
2049
|
- Durbin-Watson 통계량 해석:
|
|
@@ -1975,92 +2081,6 @@ def ols_independence_test(fit, alpha: float = 0.05) -> DataFrame:
|
|
|
1975
2081
|
|
|
1976
2082
|
return result_df
|
|
1977
2083
|
|
|
1978
|
-
|
|
1979
|
-
# ===================================================================
|
|
1980
|
-
# 상관계수 히트맵
|
|
1981
|
-
# ===================================================================
|
|
1982
|
-
def corr(data: DataFrame, *fields: str) -> tuple[DataFrame, DataFrame]:
|
|
1983
|
-
"""데이터프레임의 연속형 변수들에 대한 상관계수 히트맵과 상관계수 종류를 반환한다.
|
|
1984
|
-
|
|
1985
|
-
정규성 검정을 통해 피어슨 또는 스피어만 상관계수를 자동 선택하여 계산한다.
|
|
1986
|
-
선택된 상관계수 종류를 별도의 데이터프레임으로 교차표(행렬) 형태로 반환한다.
|
|
1987
|
-
|
|
1988
|
-
Args:
|
|
1989
|
-
data (DataFrame): 분석 대상 데이터프레임.
|
|
1990
|
-
*fields (str): 분석할 컬럼명 목록. 지정하지 않으면 모든 숫자형 컬럼을 사용.
|
|
1991
|
-
|
|
1992
|
-
Returns:
|
|
1993
|
-
tuple[DataFrame, DataFrame]: 상관계수 행렬과 사용된 상관계수 종류 정보를 포함한 두 개의 데이터프레임.
|
|
1994
|
-
|
|
1995
|
-
- 첫 번째 DataFrame: 상관계수 행렬 (각 변수 쌍의 상관계수 값)
|
|
1996
|
-
- 두 번째 DataFrame: 상관계수 종류 (교차표 형태)
|
|
1997
|
-
- 행과 열: 변수명
|
|
1998
|
-
- 셀의 값: 각 변수 쌍에 사용된 상관계수 종류 ('Pearson' 또는 'Spearman')
|
|
1999
|
-
|
|
2000
|
-
Examples:
|
|
2001
|
-
>>> import pandas as pd
|
|
2002
|
-
>>> import numpy as np
|
|
2003
|
-
>>> df = pd.DataFrame({
|
|
2004
|
-
... 'x1': np.random.normal(0, 1, 100),
|
|
2005
|
-
... 'x2': np.random.normal(0, 1, 100),
|
|
2006
|
-
... 'x3': np.random.normal(0, 1, 100),
|
|
2007
|
-
... })
|
|
2008
|
-
>>> # 모든 연속형 변수에 대해 상관계수 계산
|
|
2009
|
-
>>> corr_matrix, corr_types = corr(df)
|
|
2010
|
-
>>> print(corr_matrix)
|
|
2011
|
-
>>> x1 x2 x3
|
|
2012
|
-
>>> x1 1.00 0.12 -0.05
|
|
2013
|
-
>>> x2 0.12 1.00 0.08
|
|
2014
|
-
>>> x3 -0.05 0.08 1.00
|
|
2015
|
-
>>> print(corr_types)
|
|
2016
|
-
>>> x1 x2 x3
|
|
2017
|
-
>>> x1 Pearson Pearson Pearson
|
|
2018
|
-
>>> x2 Pearson Pearson Pearson
|
|
2019
|
-
>>> x3 Pearson Pearson Pearson
|
|
2020
|
-
|
|
2021
|
-
>>> # 특정 컬럼만 분석
|
|
2022
|
-
>>> corr_matrix, corr_info = corr(df, 'x1', 'x2')
|
|
2023
|
-
>>> print(corr_matrix)
|
|
2024
|
-
"""
|
|
2025
|
-
# 분석 대상 컬럼 결정
|
|
2026
|
-
if fields:
|
|
2027
|
-
# 지정된 컬럼만 사용
|
|
2028
|
-
numeric_cols = list(fields)
|
|
2029
|
-
else:
|
|
2030
|
-
# 모든 숫자형 컬럼 선택
|
|
2031
|
-
numeric_cols = data.select_dtypes(include=[np.number]).columns.tolist()
|
|
2032
|
-
|
|
2033
|
-
# 분석 데이터 추출
|
|
2034
|
-
analysis_data = data[numeric_cols].copy()
|
|
2035
|
-
|
|
2036
|
-
# 샘플 크기에 따라 자동으로 shapiro 또는 normaltest 선택
|
|
2037
|
-
test_method = 's' if len(analysis_data) <= 5000 else 'n'
|
|
2038
|
-
normality_results = normal_test(analysis_data, columns=numeric_cols, method=test_method)
|
|
2039
|
-
|
|
2040
|
-
# 정규성 결과를 딕셔너리로 변환
|
|
2041
|
-
normality_info = dict(zip(normality_results['column'], normality_results['is_normal']))
|
|
2042
|
-
|
|
2043
|
-
# 상관계수 계산: 모든 변수가 정규분포를 따르면 Pearson, 하나라도 아니면 Spearman 사용
|
|
2044
|
-
all_normal = all(normality_info.values())
|
|
2045
|
-
if all_normal:
|
|
2046
|
-
# Pearson 상관계수
|
|
2047
|
-
corr_matrix = analysis_data.corr(method='pearson')
|
|
2048
|
-
selected_corr_type = 'Pearson'
|
|
2049
|
-
else:
|
|
2050
|
-
# Spearman 상관계수
|
|
2051
|
-
corr_matrix = analysis_data.corr(method='spearman')
|
|
2052
|
-
selected_corr_type = 'Spearman'
|
|
2053
|
-
|
|
2054
|
-
# 상관계수 정보 데이터프레임 생성 (교차표 형태 - 상관행렬과 동일한 구조)
|
|
2055
|
-
corr_info_df = DataFrame(
|
|
2056
|
-
selected_corr_type,
|
|
2057
|
-
index=numeric_cols,
|
|
2058
|
-
columns=numeric_cols
|
|
2059
|
-
)
|
|
2060
|
-
|
|
2061
|
-
return corr_matrix, corr_info_df
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
2084
|
# ===================================================================
|
|
2065
2085
|
# 쌍별 상관분석 (선형성/이상치 점검 후 Pearson/Spearman 자동 선택)
|
|
2066
2086
|
# ===================================================================
|
|
@@ -2099,13 +2119,16 @@ def corr_pairwise(
|
|
|
2099
2119
|
[1] corr_matrix: 상관계수 행렬 (행과 열에 변수명, 값에 상관계수)
|
|
2100
2120
|
|
|
2101
2121
|
Examples:
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2122
|
+
```python
|
|
2123
|
+
from hossam import *
|
|
2124
|
+
from pandas import DataFrame
|
|
2125
|
+
|
|
2126
|
+
df = DataFrame({'x1': [1,2,3,4,5], 'x2': [2,4,5,4,6], 'x3': [10,20,25,24,30]})
|
|
2127
|
+
# 전체 숫자형 컬럼에 대해 상관분석
|
|
2128
|
+
result_df, corr_matrix = hs_stats.corr_pairwise(df)
|
|
2129
|
+
# 특정 컬럼만 분석
|
|
2130
|
+
result_df, corr_matrix = hs_stats.corr_pairwise(df, fields=['x1', 'x2'])
|
|
2131
|
+
```
|
|
2109
2132
|
"""
|
|
2110
2133
|
|
|
2111
2134
|
# 0) 컬럼 선정 (숫자형만)
|
|
@@ -2289,17 +2312,22 @@ def oneway_anova(data: DataFrame, dv: str, between: str, alpha: float = 0.05) ->
|
|
|
2289
2312
|
- posthoc_report (str): 사후검정 유무와 유의한 쌍 정보를 요약한 보고 문장.
|
|
2290
2313
|
|
|
2291
2314
|
Examples:
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2315
|
+
```python
|
|
2316
|
+
from hossam import *
|
|
2317
|
+
from pandas import DataFrame
|
|
2318
|
+
|
|
2319
|
+
df = DataFrame({
|
|
2320
|
+
'score': [5.1, 4.9, 5.3, 5.0, 4.8, 5.5, 5.2, 5.7, 5.3, 5.1],
|
|
2321
|
+
'group': ['A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B']
|
|
2322
|
+
})
|
|
2323
|
+
|
|
2324
|
+
anova_df, anova_report, posthoc_df, posthoc_report = hs_stats.oneway_anova(df, dv='score', between='group')
|
|
2325
|
+
|
|
2326
|
+
# 사후검정결과는 ANOVA가 유의할 때만 생성됨
|
|
2327
|
+
if posthoc_df is not None:
|
|
2328
|
+
print(posthoc_report)
|
|
2329
|
+
print(posthoc_df.head())
|
|
2330
|
+
```
|
|
2303
2331
|
|
|
2304
2332
|
Raises:
|
|
2305
2333
|
ValueError: dv 또는 between 컬럼이 데이터프레임에 없을 경우.
|
|
@@ -2636,20 +2664,19 @@ def predict(fit, data: DataFrame | Series) -> DataFrame | Series | float:
|
|
|
2636
2664
|
Exception: 데이터와 모형의 특성 불일치로 인한 predict 실패.
|
|
2637
2665
|
|
|
2638
2666
|
Examples:
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
>>> pred = predict(fit_ols, df_new[['x1', 'x2']]) # DataFrame 반환
|
|
2645
|
-
|
|
2646
|
-
>>> # 로지스틱 회귀 (상수항 자동 추가)
|
|
2647
|
-
>>> fit_logit = sm.Logit(y_binary, X).fit()
|
|
2648
|
-
>>> pred_prob = predict(fit_logit, df_new[['x1', 'x2']]) # DataFrame 반환 (해석 포함)
|
|
2649
|
-
"""
|
|
2650
|
-
from statsmodels.regression.linear_model import RegressionResultsWrapper
|
|
2651
|
-
from statsmodels.discrete.discrete_model import BinaryResults
|
|
2667
|
+
```python
|
|
2668
|
+
from hossam import *
|
|
2669
|
+
|
|
2670
|
+
df = hs_util.load_data("some_data.csv")
|
|
2671
|
+
fit1 = hs_stats.ols(df, yname="target")
|
|
2652
2672
|
|
|
2673
|
+
pred = hs_stats.predict(fit1, df_new[['x1', 'x2']]) # DataFrame 반환
|
|
2674
|
+
|
|
2675
|
+
# 로지스틱 회귀 (상수항 자동 추가)
|
|
2676
|
+
fit2 = hs_stats.logit(df, yname="target")
|
|
2677
|
+
pred_prob = hs_stats.predict(fit2, df_new[['x1', 'x2']]) # DataFrame 반환 (해석 포함)
|
|
2678
|
+
```
|
|
2679
|
+
"""
|
|
2653
2680
|
# fit 객체의 타입 확인
|
|
2654
2681
|
fit_type = type(fit).__name__
|
|
2655
2682
|
|
|
@@ -2724,158 +2751,6 @@ def predict(fit, data: DataFrame | Series) -> DataFrame | Series | float:
|
|
|
2724
2751
|
)
|
|
2725
2752
|
|
|
2726
2753
|
|
|
2727
|
-
# ===================================================================
|
|
2728
|
-
# 확장된 기술통계량 (Extended Descriptive Statistics)
|
|
2729
|
-
# ===================================================================
|
|
2730
|
-
def describe(data: DataFrame, *fields: str, columns: list = None):
|
|
2731
|
-
"""데이터프레임의 연속형 변수에 대한 확장된 기술통계량을 반환한다.
|
|
2732
|
-
|
|
2733
|
-
각 연속형(숫자형) 컬럼의 기술통계량(describe)을 구하고, 이에 사분위수 범위(IQR),
|
|
2734
|
-
이상치 경계값(UP, DOWN), 왜도(skew), 이상치 개수 및 비율, 분포 특성, 로그변환 필요성을
|
|
2735
|
-
추가하여 반환한다.
|
|
2736
|
-
|
|
2737
|
-
Args:
|
|
2738
|
-
data (DataFrame): 분석 대상 데이터프레임.
|
|
2739
|
-
*fields (str): 분석할 컬럼명 목록. 지정하지 않으면 모든 숫자형 컬럼을 처리.
|
|
2740
|
-
columns (list, optional): 반환할 통계량 컬럼 목록. None이면 모든 통계량 반환.
|
|
2741
|
-
|
|
2742
|
-
Returns:
|
|
2743
|
-
DataFrame: 각 필드별 확장된 기술통계량을 포함한 데이터프레임.
|
|
2744
|
-
행은 다음과 같은 통계량을 포함:
|
|
2745
|
-
|
|
2746
|
-
- count (float): 비결측치의 수
|
|
2747
|
-
- mean (float): 평균값
|
|
2748
|
-
- std (float): 표준편차
|
|
2749
|
-
- min (float): 최소값
|
|
2750
|
-
- 25% (float): 제1사분위수 (Q1)
|
|
2751
|
-
- 50% (float): 제2사분위수 (중앙값)
|
|
2752
|
-
- 75% (float): 제3사분위수 (Q3)
|
|
2753
|
-
- max (float): 최대값
|
|
2754
|
-
- iqr (float): 사분위 범위 (Q3 - Q1)
|
|
2755
|
-
- up (float): 이상치 상한 경계값 (Q3 + 1.5 * IQR)
|
|
2756
|
-
- down (float): 이상치 하한 경계값 (Q1 - 1.5 * IQR)
|
|
2757
|
-
- skew (float): 왜도
|
|
2758
|
-
- outlier_count (int): 이상치 개수
|
|
2759
|
-
- outlier_rate (float): 이상치 비율(%)
|
|
2760
|
-
- dist (str): 분포 특성 ("극단 우측 꼬리", "거의 대칭" 등)
|
|
2761
|
-
- log_need (str): 로그변환 필요성 ("높음", "중간", "낮음")
|
|
2762
|
-
|
|
2763
|
-
Examples:
|
|
2764
|
-
전체 숫자형 컬럼에 대한 확장된 기술통계:
|
|
2765
|
-
|
|
2766
|
-
>>> from hossam import summary
|
|
2767
|
-
>>> import pandas as pd
|
|
2768
|
-
>>> df = pd.DataFrame({
|
|
2769
|
-
... 'x': [1, 2, 3, 4, 5, 100],
|
|
2770
|
-
... 'y': [10, 20, 30, 40, 50, 60],
|
|
2771
|
-
... 'z': ['a', 'b', 'c', 'd', 'e', 'f']
|
|
2772
|
-
... })
|
|
2773
|
-
>>> result = summary(df)
|
|
2774
|
-
>>> print(result)
|
|
2775
|
-
|
|
2776
|
-
특정 컬럼만 분석:
|
|
2777
|
-
|
|
2778
|
-
>>> result = summary(df, 'x', 'y')
|
|
2779
|
-
>>> print(result)
|
|
2780
|
-
|
|
2781
|
-
Notes:
|
|
2782
|
-
- 숫자형이 아닌 컬럼은 자동으로 제외됩니다.
|
|
2783
|
-
- 결과는 필드(컬럼)가 행으로, 통계량이 열로 구성됩니다.
|
|
2784
|
-
- Tukey의 1.5 * IQR 규칙을 사용하여 이상치를 판정합니다.
|
|
2785
|
-
- 분포 특성은 왜도 값으로 판정합니다.
|
|
2786
|
-
- 로그변환 필요성은 왜도의 절댓값 크기로 판정합니다.
|
|
2787
|
-
"""
|
|
2788
|
-
if not fields:
|
|
2789
|
-
fields = data.select_dtypes(include=['int', 'int32', 'int64', 'float', 'float32', 'float64']).columns
|
|
2790
|
-
|
|
2791
|
-
# 기술통계량 구하기
|
|
2792
|
-
desc = data[list(fields)].describe().T
|
|
2793
|
-
|
|
2794
|
-
# 추가 통계량 계산
|
|
2795
|
-
additional_stats = []
|
|
2796
|
-
for f in fields:
|
|
2797
|
-
# 숫자 타입이 아니라면 건너뜀
|
|
2798
|
-
if data[f].dtype not in [
|
|
2799
|
-
'int',
|
|
2800
|
-
'int32',
|
|
2801
|
-
'int64',
|
|
2802
|
-
'float',
|
|
2803
|
-
'float32',
|
|
2804
|
-
'float64',
|
|
2805
|
-
'int64',
|
|
2806
|
-
'float64',
|
|
2807
|
-
'float32'
|
|
2808
|
-
]:
|
|
2809
|
-
continue
|
|
2810
|
-
|
|
2811
|
-
# 사분위수
|
|
2812
|
-
q1 = data[f].quantile(q=0.25)
|
|
2813
|
-
q3 = data[f].quantile(q=0.75)
|
|
2814
|
-
|
|
2815
|
-
# 이상치 경계 (Tukey's fences)
|
|
2816
|
-
iqr = q3 - q1
|
|
2817
|
-
down = q1 - 1.5 * iqr
|
|
2818
|
-
up = q3 + 1.5 * iqr
|
|
2819
|
-
|
|
2820
|
-
# 왜도
|
|
2821
|
-
skew = data[f].skew()
|
|
2822
|
-
|
|
2823
|
-
# 이상치 개수 및 비율
|
|
2824
|
-
outlier_count = ((data[f] < down) | (data[f] > up)).sum()
|
|
2825
|
-
outlier_rate = (outlier_count / len(data)) * 100
|
|
2826
|
-
|
|
2827
|
-
# 분포 특성 판정 (왜도 기준)
|
|
2828
|
-
abs_skew = abs(skew)
|
|
2829
|
-
if abs_skew < 0.5:
|
|
2830
|
-
dist = "거의 대칭"
|
|
2831
|
-
elif abs_skew < 1.0:
|
|
2832
|
-
if skew > 0:
|
|
2833
|
-
dist = "약한 우측 꼬리"
|
|
2834
|
-
else:
|
|
2835
|
-
dist = "약한 좌측 꼬리"
|
|
2836
|
-
elif abs_skew < 2.0:
|
|
2837
|
-
if skew > 0:
|
|
2838
|
-
dist = "중간 우측 꼬리"
|
|
2839
|
-
else:
|
|
2840
|
-
dist = "중간 좌측 꼬리"
|
|
2841
|
-
else:
|
|
2842
|
-
if skew > 0:
|
|
2843
|
-
dist = "극단 우측 꼬리"
|
|
2844
|
-
else:
|
|
2845
|
-
dist = "극단 좌측 꼬리"
|
|
2846
|
-
|
|
2847
|
-
# 로그변환 필요성 판정
|
|
2848
|
-
if abs_skew < 0.5:
|
|
2849
|
-
log_need = "낮음"
|
|
2850
|
-
elif abs_skew < 1.0:
|
|
2851
|
-
log_need = "중간"
|
|
2852
|
-
else:
|
|
2853
|
-
log_need = "높음"
|
|
2854
|
-
|
|
2855
|
-
additional_stats.append({
|
|
2856
|
-
'field': f,
|
|
2857
|
-
'iqr': iqr,
|
|
2858
|
-
'up': up,
|
|
2859
|
-
'down': down,
|
|
2860
|
-
'outlier_count': outlier_count,
|
|
2861
|
-
'outlier_rate': outlier_rate,
|
|
2862
|
-
'skew': skew,
|
|
2863
|
-
'dist': dist,
|
|
2864
|
-
'log_need': log_need
|
|
2865
|
-
})
|
|
2866
|
-
|
|
2867
|
-
additional_df = DataFrame(additional_stats).set_index('field')
|
|
2868
|
-
|
|
2869
|
-
# 결과 병합
|
|
2870
|
-
result = concat([desc, additional_df], axis=1)
|
|
2871
|
-
|
|
2872
|
-
# columns 파라미터가 지정된 경우 해당 컬럼만 필터링
|
|
2873
|
-
if columns is not None:
|
|
2874
|
-
result = result[columns]
|
|
2875
|
-
|
|
2876
|
-
return result
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
2754
|
# ===================================================================
|
|
2880
2755
|
# 상관계수 및 효과크기 분석 (Correlation & Effect Size)
|
|
2881
2756
|
# ===================================================================
|
|
@@ -2902,13 +2777,16 @@ def corr_effect_size(data: DataFrame, dv: str, *fields: str, alpha: float = 0.05
|
|
|
2902
2777
|
- Effect_Size (str): 효과크기 분류 ('Large', 'Medium', 'Small', 'Negligible')
|
|
2903
2778
|
|
|
2904
2779
|
Examples:
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2780
|
+
```python
|
|
2781
|
+
from hossam import *
|
|
2782
|
+
from pandas import DataFrame
|
|
2783
|
+
|
|
2784
|
+
df = DataFrame({'age': [20, 30, 40, 50],
|
|
2785
|
+
'bmi': [22, 25, 28, 30],
|
|
2786
|
+
'charges': [1000, 2000, 3000, 4000]})
|
|
2787
|
+
|
|
2788
|
+
result = hs_stats.corr_effect_size(df, 'charges', 'age', 'bmi')
|
|
2789
|
+
```
|
|
2912
2790
|
"""
|
|
2913
2791
|
|
|
2914
2792
|
# fields가 지정되지 않으면 수치형 컬럼 중 dv 제외 모두 사용
|