제 1 유형


데이터셋 : https://raw.githubusercontent.com/YoungjinBD/dataset/main/HR-Employee-Attrition.csv

주어지는 데이터는 IBM의 근무자에 대한 직무정보와 퇴사여부(Attrition)를 정리한 것이다.

주요 컬럼 설명
Attrition Yes : 퇴사, No : 퇴사하지 않음
  1. 데이터에서 ’Attrition’은 퇴사여부에 대한 종속변수에 해당한다. 종속변수의 값을 다음과 같이 수치로 변환해 새 컬럼으로 추가하고 각 범주별 레코드 수를 계산하시오.
    (Yes → 1, No → 0)

  2. 데이터셋의 데이터타입별 컬럼 개수를 계산하고, 범주형 변수 중 유일한 값을 1개만 가지고 있는 컬럼(변수)을 찾아내어 그 변수를 데이터에서 제거하시오.

  3. 원래 데이터에서 숫자형인 컬럼(변수)만 추출하여 새로운 데이터프레임을 생성하고, 각 변수 간 상관계수(피어슨 상관계수)를 구하고 상관계수가 0.9 이상인 두 개의 컬럼을 찾아내어 그 변수 중 하나를 제거하시오.

# 필요한 파이썬 라이브러리 불러오기
import numpy as np
import pandas as pd
import seaborn as sns

from scipy.stats import pearsonr
from itertools import combinations
from statsmodels.stats.proportion import proportion_confint

# csv 파일이 위치한 디렉토리 입력
df = pd.read_csv('https://raw.githubusercontent.com/YoungjinBD/dataset/main/HR-Employee-Attrition.csv')

df
##       Age Attrition  ... YearsSinceLastPromotion  YearsWithCurrManager
## 0      41       Yes  ...                       0                     5
## 1      49        No  ...                       1                     7
## 2      37       Yes  ...                       0                     0
## 3      33        No  ...                       3                     0
## 4      27        No  ...                       2                     2
## ...   ...       ...  ...                     ...                   ...
## 1465   36        No  ...                       0                     3
## 1466   39        No  ...                       1                     7
## 1467   27        No  ...                       0                     3
## 1468   49        No  ...                       0                     8
## 1469   34        No  ...                       1                     2
## 
## [1470 rows x 35 columns]
# 레이블 인코딩
from sklearn.preprocessing import LabelEncoder
df["Attrition_numerical"] = LabelEncoder().fit_transform(df["Attrition"])
df["Attrition_numerical"].value_counts()
## Attrition_numerical
## 0    1233
## 1     237
## Name: count, dtype: int64
df.info()
## <class 'pandas.core.frame.DataFrame'>
## RangeIndex: 1470 entries, 0 to 1469
## Data columns (total 36 columns):
##  #   Column                    Non-Null Count  Dtype 
## ---  ------                    --------------  ----- 
##  0   Age                       1470 non-null   int64 
##  1   Attrition                 1470 non-null   object
##  2   BusinessTravel            1470 non-null   object
##  3   DailyRate                 1470 non-null   int64 
##  4   Department                1470 non-null   object
##  5   DistanceFromHome          1470 non-null   int64 
##  6   Education                 1470 non-null   int64 
##  7   EducationField            1470 non-null   object
##  8   EmployeeCount             1470 non-null   int64 
##  9   EmployeeNumber            1470 non-null   int64 
##  10  EnvironmentSatisfaction   1470 non-null   int64 
##  11  Gender                    1470 non-null   object
##  12  HourlyRate                1470 non-null   int64 
##  13  JobInvolvement            1470 non-null   int64 
##  14  JobLevel                  1470 non-null   int64 
##  15  JobRole                   1470 non-null   object
##  16  JobSatisfaction           1470 non-null   int64 
##  17  MaritalStatus             1470 non-null   object
##  18  MonthlyIncome             1470 non-null   int64 
##  19  MonthlyRate               1470 non-null   int64 
##  20  NumCompaniesWorked        1470 non-null   int64 
##  21  Over18                    1470 non-null   object
##  22  OverTime                  1470 non-null   object
##  23  PercentSalaryHike         1470 non-null   int64 
##  24  PerformanceRating         1470 non-null   int64 
##  25  RelationshipSatisfaction  1470 non-null   int64 
##  26  StandardHours             1470 non-null   int64 
##  27  StockOptionLevel          1470 non-null   int64 
##  28  TotalWorkingYears         1470 non-null   int64 
##  29  TrainingTimesLastYear     1470 non-null   int64 
##  30  WorkLifeBalance           1470 non-null   int64 
##  31  YearsAtCompany            1470 non-null   int64 
##  32  YearsInCurrentRole        1470 non-null   int64 
##  33  YearsSinceLastPromotion   1470 non-null   int64 
##  34  YearsWithCurrManager      1470 non-null   int64 
##  35  Attrition_numerical       1470 non-null   int32 
## dtypes: int32(1), int64(26), object(9)
## memory usage: 407.8+ KB
  • 데이터프레임 df에서 범주형 변수(문자형 데이터 및 category 타입 데이터)를 선택하고, 각 변수의 중복 제거된 고유값 개수를 계산하여 정렬합니다.
cat_feat = df.select_dtypes('object','category').columns.values
df_cat = df[cat_feat].copy()
df_cat.nunique().sort_values()
## Over18            1
## Attrition         2
## Gender            2
## OverTime          2
## BusinessTravel    3
## Department        3
## MaritalStatus     3
## EducationField    6
## JobRole           9
## dtype: int64
# 'Over18' 컬럼이 데이터프레임에 없을 경우 에러를 발생시키지 않고 무시합니다.
# errors='ignore'은 코드 실행 중 예상치 못한 에러를 방지합니다.
df_cat = df_cat.drop(['Over18'], axis=1, errors='ignore') 
df_cat
##      Attrition     BusinessTravel  ... MaritalStatus OverTime
## 0          Yes      Travel_Rarely  ...        Single      Yes
## 1           No  Travel_Frequently  ...       Married       No
## 2          Yes      Travel_Rarely  ...        Single      Yes
## 3           No  Travel_Frequently  ...       Married      Yes
## 4           No      Travel_Rarely  ...       Married       No
## ...        ...                ...  ...           ...      ...
## 1465        No  Travel_Frequently  ...       Married       No
## 1466        No      Travel_Rarely  ...       Married       No
## 1467        No      Travel_Rarely  ...       Married      Yes
## 1468        No  Travel_Frequently  ...       Married       No
## 1469        No      Travel_Rarely  ...       Married       No
## 
## [1470 rows x 8 columns]
# 'Over18' 컬럼이 데이터프레임에 없을 경우 에러를 발생시키지 않고 무시합니다.
# errors='ignore'은 코드 실행 중 예상치 못한 에러를 방지합니다.
df_cat = df_cat.drop(['Over18'], axis=1, errors='ignore') 
df_cat
##      Attrition     BusinessTravel  ... MaritalStatus OverTime
## 0          Yes      Travel_Rarely  ...        Single      Yes
## 1           No  Travel_Frequently  ...       Married       No
## 2          Yes      Travel_Rarely  ...        Single      Yes
## 3           No  Travel_Frequently  ...       Married      Yes
## 4           No      Travel_Rarely  ...       Married       No
## ...        ...                ...  ...           ...      ...
## 1465        No  Travel_Frequently  ...       Married       No
## 1466        No      Travel_Rarely  ...       Married       No
## 1467        No      Travel_Rarely  ...       Married      Yes
## 1468        No  Travel_Frequently  ...       Married       No
## 1469        No      Travel_Rarely  ...       Married       No
## 
## [1470 rows x 8 columns]
df = pd.read_csv('https://raw.githubusercontent.com/YoungjinBD/dataset/main/HR-Employee-Attrition.csv')

# 수치형 컬럼만으로 구성된 데이터셋 만들기
num_feat = df.select_dtypes('number').columns.values
df_num = df[num_feat].copy()

# 각 컬럼 간 상관계수 구하고 상관계수 차원 계산 
corr = df_num.corr(method="pearson")
corr.shape
## (26, 26)
# 상관계수가 0.9보다 큰 2개의 변수와 상관계수를 출력
for i in range(0, 24) :
    for j in range(i+1, 25) :
        if (corr.iloc[i, j] >= 0.9) :
            print(i, j, corr.iloc[i, j])
## 9 11 0.9502999134798473
corr.info()
## <class 'pandas.core.frame.DataFrame'>
## Index: 26 entries, Age to YearsWithCurrManager
## Data columns (total 26 columns):
##  #   Column                    Non-Null Count  Dtype  
## ---  ------                    --------------  -----  
##  0   Age                       24 non-null     float64
##  1   DailyRate                 24 non-null     float64
##  2   DistanceFromHome          24 non-null     float64
##  3   Education                 24 non-null     float64
##  4   EmployeeCount             0 non-null      float64
##  5   EmployeeNumber            24 non-null     float64
##  6   EnvironmentSatisfaction   24 non-null     float64
##  7   HourlyRate                24 non-null     float64
##  8   JobInvolvement            24 non-null     float64
##  9   JobLevel                  24 non-null     float64
##  10  JobSatisfaction           24 non-null     float64
##  11  MonthlyIncome             24 non-null     float64
##  12  MonthlyRate               24 non-null     float64
##  13  NumCompaniesWorked        24 non-null     float64
##  14  PercentSalaryHike         24 non-null     float64
##  15  PerformanceRating         24 non-null     float64
##  16  RelationshipSatisfaction  24 non-null     float64
##  17  StandardHours             0 non-null      float64
##  18  StockOptionLevel          24 non-null     float64
##  19  TotalWorkingYears         24 non-null     float64
##  20  TrainingTimesLastYear     24 non-null     float64
##  21  WorkLifeBalance           24 non-null     float64
##  22  YearsAtCompany            24 non-null     float64
##  23  YearsInCurrentRole        24 non-null     float64
##  24  YearsSinceLastPromotion   24 non-null     float64
##  25  YearsWithCurrManager      24 non-null     float64
## dtypes: float64(26)
## memory usage: 6.5+ KB
# 0.9 이상인 것 중에서 1개 제거(JobLevel)
df_num = df_num.drop(["JobLevel"], axis=1, errors='ignore')
df_num.info()
## <class 'pandas.core.frame.DataFrame'>
## RangeIndex: 1470 entries, 0 to 1469
## Data columns (total 25 columns):
##  #   Column                    Non-Null Count  Dtype
## ---  ------                    --------------  -----
##  0   Age                       1470 non-null   int64
##  1   DailyRate                 1470 non-null   int64
##  2   DistanceFromHome          1470 non-null   int64
##  3   Education                 1470 non-null   int64
##  4   EmployeeCount             1470 non-null   int64
##  5   EmployeeNumber            1470 non-null   int64
##  6   EnvironmentSatisfaction   1470 non-null   int64
##  7   HourlyRate                1470 non-null   int64
##  8   JobInvolvement            1470 non-null   int64
##  9   JobSatisfaction           1470 non-null   int64
##  10  MonthlyIncome             1470 non-null   int64
##  11  MonthlyRate               1470 non-null   int64
##  12  NumCompaniesWorked        1470 non-null   int64
##  13  PercentSalaryHike         1470 non-null   int64
##  14  PerformanceRating         1470 non-null   int64
##  15  RelationshipSatisfaction  1470 non-null   int64
##  16  StandardHours             1470 non-null   int64
##  17  StockOptionLevel          1470 non-null   int64
##  18  TotalWorkingYears         1470 non-null   int64
##  19  TrainingTimesLastYear     1470 non-null   int64
##  20  WorkLifeBalance           1470 non-null   int64
##  21  YearsAtCompany            1470 non-null   int64
##  22  YearsInCurrentRole        1470 non-null   int64
##  23  YearsSinceLastPromotion   1470 non-null   int64
##  24  YearsWithCurrManager      1470 non-null   int64
## dtypes: int64(25)
## memory usage: 287.2 KB

제 2 유형


데이터셋 : https://raw.githubusercontent.com/YoungjinBD/dataset/main/Parkinsons.csv

환자들의 뇌를 촬영한 사진의 상태를 기록한 자료에 각 환자의 상태를 status(1: 파킨슨병 진단, 0: 파킨슨병 아님)로 추가한 테이블이다.

데이터셋을 이용하여 파킨슨병을 예측하는 모델을 로지스틱 회귀모형을 적용하여 생성하고, 이때 파킨슨병을 예측하는데 영향을 미치는 변수를 중요한 순서대로 3개 선정하시오.

이 모델에서 파킨슨병으로 진단하는 기준(threshold, 또는 cutoff)을 0.5로 했을 때와 0.8로 했을 때의 F1-스코어를 비교하고 해석하시오.
(단, 다음 조건을 지켜서 물음에 답하시오.)


분석 조건

  • 필요 없는 컬럼인 ‘name’ 제거
  • 로지스틱 회귀를 위해 상수항 추가
  • 트레이닝셋과 테스트셋 비율을 9:1
  • 모델의 최적화 방법론으로 ‘lbfgs’ 사용
  • 데이터 정규화는 min-max 스케일러 사용
  • Status는 카테고리 타입으로 변환
  • 모델은 로지스틱 회귀분석 사용
# 구글 코랩 환경을 기준으로 하고 있습니다.

# 데이터 처리
import numpy as np
import pandas as pd

# 머신러닝 알고리즘 및 평가
import statsmodels.api as sm
from sklearn.model_selection import train_test_split
import sklearn.preprocessing as preprocessing
from sklearn import metrics
from sklearn.metrics import f1_score

dat = pd.read_csv("https://raw.githubusercontent.com/YoungjinBD/dataset/main/Parkinsons.csv")

dat.head(10)
##              name  MDVP:Fo(Hz)  MDVP:Fhi(Hz)  ...   spread2        D2       PPE
## 0  phon_R01_S01_1      119.992       157.302  ...  0.266482  2.301442  0.284654
## 1  phon_R01_S01_2      122.400       148.650  ...  0.335590  2.486855  0.368674
## 2  phon_R01_S01_3      116.682       131.111  ...  0.311173  2.342259  0.332634
## 3  phon_R01_S01_4      116.676       137.871  ...  0.334147  2.405554  0.368975
## 4  phon_R01_S01_5      116.014       141.781  ...  0.234513  2.332180  0.410335
## 5  phon_R01_S01_6      120.552       131.162  ...  0.299111  2.187560  0.357775
## 6  phon_R01_S02_1      120.267       137.244  ...  0.257682  1.854785  0.211756
## 7  phon_R01_S02_2      107.332       113.840  ...  0.183721  2.064693  0.163755
## 8  phon_R01_S02_3       95.730       132.068  ...  0.327769  2.322511  0.231571
## 9  phon_R01_S02_4       95.056       120.103  ...  0.325996  2.432792  0.271362
## 
## [10 rows x 24 columns]
# 데이터프레임의 전처리 및 상수 항 추가 과정을 수행

from sklearn.preprocessing import minmax_scale
import statsmodels.api as sm
import pandas as pd

# 'name' 변수 제거
dat_processing = dat.drop(columns=['name'], errors='ignore')

# 정규화 (Min-Max Scaling) 및 상수 열 추가
dat_processed = pd.DataFrame(
    minmax_scale(dat_processing), columns=dat_processing.columns
)
dat_processed = sm.add_constant(dat_processed, has_constant='add')

# 결과 확인
print(dat_processed.head(10))
##    const  MDVP:Fo(Hz)  MDVP:Fhi(Hz)  ...   spread2        D2       PPE
## 0    1.0     0.184308      0.112592  ...  0.585765  0.390661  0.497310
## 1    1.0     0.198327      0.094930  ...  0.741337  0.473145  0.671326
## 2    1.0     0.165039      0.059128  ...  0.686371  0.408819  0.596682
## 3    1.0     0.165004      0.072927  ...  0.738089  0.436977  0.671949
## 4    1.0     0.161150      0.080909  ...  0.513798  0.404336  0.757611
## 5    1.0     0.187568      0.059232  ...  0.659218  0.339999  0.648753
## 6    1.0     0.185909      0.071647  ...  0.565955  0.191959  0.346328
## 7    1.0     0.110606      0.023873  ...  0.399458  0.285340  0.246912
## 8    1.0     0.043063      0.061082  ...  0.723731  0.400034  0.387368
## 9    1.0     0.039139      0.036658  ...  0.719740  0.449094  0.469780
## 
## [10 rows x 24 columns]
# 데이터프레임의 모든 컬럼 중 'status' 컬럼을 제외한 나머지 컬럼 이름을 리스트로 저장
feature_columns = list(dat_processed.columns.difference(["status"]))

# 타깃(target) 변수인 'status' 컬럼을 범주형 데이터(category)로 변환하여 y에 저장.
X = dat_processed[feature_columns]
y = dat_processed['status'].astype('category')    # 질환여부: 1 or 0

# train_test_split 함수를 이용하여 학습 데이터와 검증 데이터로 9:1로 나누어 데이터를 구분
train_x, test_x, train_y, test_y = train_test_split(X, y, stratify=y, test_size=0.1, random_state=2017010500)
train_x.shape, test_x.shape, train_y.shape, test_y.shape
## ((175, 23), (20, 23), (175,), (20,))
import statsmodels.api as sm

# 로지스틱 회귀 모델 생성 및 학습
logit_model = sm.Logit(train_y, train_x)
results = logit_model.fit(method='bfgs', maxiter=1000)
## Optimization terminated successfully.
##          Current function value: 0.224375
##          Iterations: 315
##          Function evaluations: 316
##          Gradient evaluations: 316
# 결과 요약 출력
print(results.summary())
##                            Logit Regression Results                           
## ==============================================================================
## Dep. Variable:                 status   No. Observations:                  175
## Model:                          Logit   Df Residuals:                      152
## Method:                           MLE   Df Model:                           22
## Date:                    토, 01 2 2025   Pseudo R-squ.:                  0.5976
## Time:                        13:50:56   Log-Likelihood:                -39.266
## converged:                       True   LL-Null:                       -97.576
## Covariance Type:            nonrobust   LLR p-value:                 7.140e-15
## ====================================================================================
##                        coef    std err          z      P>|z|      [0.025      0.975]
## ------------------------------------------------------------------------------------
## D2                   2.8218      3.448      0.818      0.413      -3.936       9.580
## DFA                  1.4404      2.320      0.621      0.535      -3.107       5.987
## HNR                  3.0901      6.002      0.515      0.607      -8.674      14.854
## Jitter:DDP          45.6740   2956.389      0.015      0.988   -5748.741    5840.089
## MDVP:APQ            31.0449     53.564      0.580      0.562     -73.939     136.029
## MDVP:Fhi(Hz)        -1.2380      1.961     -0.631      0.528      -5.082       2.606
## MDVP:Flo(Hz)         1.0740      2.186      0.491      0.623      -3.211       5.359
## MDVP:Fo(Hz)         -4.5819      3.936     -1.164      0.244     -12.297       3.133
## MDVP:Jitter(%)     -39.0447     40.449     -0.965      0.334    -118.323      40.233
## MDVP:Jitter(Abs)   -14.3703     23.721     -0.606      0.545     -60.862      32.122
## MDVP:PPQ           -55.8013     38.159     -1.462      0.144    -130.592      18.989
## MDVP:RAP            45.8953   2955.587      0.016      0.988   -5746.948    5838.739
## MDVP:Shimmer       -40.6224    119.684     -0.339      0.734    -275.199     193.954
## MDVP:Shimmer(dB)    74.3763     53.297      1.395      0.163     -30.085     178.837
## NHR                  9.2313     17.548      0.526      0.599     -25.163      43.625
## PPE                 18.3958     13.277      1.386      0.166      -7.627      44.418
## RPDE                -1.9728      2.192     -0.900      0.368      -6.270       2.324
## Shimmer:APQ3       -10.7878   6499.355     -0.002      0.999   -1.27e+04    1.27e+04
## Shimmer:APQ5         5.6487     36.853      0.153      0.878     -66.582      77.880
## Shimmer:DDA        -12.3804   6500.456     -0.002      0.998   -1.28e+04    1.27e+04
## const               -6.7595      5.692     -1.187      0.235     -17.916       4.397
## spread1              0.4163     10.613      0.039      0.969     -20.384      21.217
## spread2              4.8739      2.833      1.721      0.085      -0.678      10.426
## ====================================================================================
## 
## Possibly complete quasi-separation: A fraction 0.17 of observations can be
## perfectly predicted. This might indicate that there is complete
## quasi-separation. In this case some parameters will not be identified.
  • 결과 해석

    • 반복 횟수(Iterations: 315), 함수 평가(Function evaluations: 316), 그리고 기울기 평가(Gradient evaluations: 316)를 통해 최적화를 수행했습니다.

    • Current function value: 0.224375

      • 로그 가능도 함수(Log-Likelihood)의 값입니다. 값이 낮을수록 모델의 적합도가 높습니다.
    • Pseudo R-squared: 0.5976

      • 모형의 설명력을 나타내는 지표로, 일반적인 결정 계수(R²)의 대안입니다.

      • 값이 1에 가까울수록 종속 변수를 잘 설명한다고 볼 수 있습니다. 여기서는 약 59.76%의 설명력을 가집니다.

    • Log-Likelihood: -39.266

      • 로그 가능도의 값으로, 모델의 성능을 나타냅니다. 값이 높을수록 데이터에 더 잘 맞습니다.
    • Covariance Type: nonrobust

      • 공분산 추정 방식이 “nonrobust”(기본값)임을 나타냅니다. 추가적으로 견고한 방식(robust covariance)을 사용할 수 있습니다.
    • 회귀 계수 (coef)

      • 의미: 각 독립 변수(feature)가 종속 변수(status)에 미치는 영향을 나타냅니다.

        • 양수: 변수 값이 증가하면 결과(status)가 1일 가능성이 증가.

        • 음수: 변수 값이 증가하면 결과(status)가 0일 가능성이 증가.

    • P>|z| (p-value)

      • 의미:

        • 각 변수의 계수가 통계적으로 유의미한지 나타냅니다.

        • 일반적으로 P>|z| < 0.05일 때 유의미하다고 판단합니다.

        • 결과 해석:

        • 대부분의 변수에서 P값이 0.05보다 크므로, 통계적으로 유의미하지 않은 변수가 많습니다.

        • HNR은 경계선에서 유의미할 가능성이 있으므로 주목할 수 있습니다.

    • “Possibly complete quasi-separation”:

      • 데이터가 특정 변수 조합에 따라 완벽하게 분리(quasi-separation)되었을 가능성을 시사합니다.

      • 이는 일부 관측치가 완벽히 예측 가능하며, 특정 계수를 신뢰할 수 없음을 의미합니다.

      • 이로 인해 일부 계수가 추정되지 않을 수 있습니다.

    • 개선 방안

      • 변수 선택: 유의미하지 않은 변수를 제거하거나 변수 선택 기법(예: Lasso)을 적용해 모델을 단순화할 수 있습니다.

      • 데이터 처리:

      • 변수 간 다중공선성(multicollinearity)을 점검하여 이를 해결.

      • 상관관계가 높은 변수를 제거하거나 결합하여 새로운 변수를 생성.

      • 모델 변경: 로지스틱 회귀 외에 다른 비선형 모델(예: Random Forest, Gradient Boosting)을 고려해 성능을 비교.

    • 결론

      • 현재 모델은 Pseudo R-squared = 0.5976으로 적절한 성능을 보이지만, 다수의 독립 변수가 통계적으로 유의미하지 않아 추가적인 변형이 필요합니다.

      • 특히, 경고 메시지를 고려해 데이터 구조를 분석하고 적절한 전처리를 수행하는 것이 중요합니다.

      • 데이터를 사용해 문제를 풀려고 했는데, 불필요한 정보를 너무 많이 넣어서 방해받는 상황입니다. 불필요한 정보를 정리하거나 더 똑똑한 방법(새로운 모델)을 사용하면 결과가 더 좋아질 겁니다!

# 로지스틱 회귀 결과를 사용해 예측값을 생성하고, 이를 기반으로 모델 성능(F1 점수)을 계산
from sklearn.metrics import f1_score

# cut_off 함수 정의
def cut_off(y, threshold=0.5):
    y = y.copy()
    y[y >= threshold] = 1
    y[y < threshold] = 0
    return y.astype(int)

# 테스트 데이터 예측 확률 및 이진화
test_y_pred_prob = results.predict(test_x)  # 예측 확률 생성
test_y_pred = cut_off(test_y_pred_prob, threshold=0.8)  # 80% 기준 이진화

# F1 점수 계산
f1 = f1_score(test_y, test_y_pred)
print(f"F1 Score: {f1}")
## F1 Score: 0.7857142857142857
  • F1 점수 0.7857은 모델이 정밀도와 재현율 간 균형 잡힌 성능을 보이고 있음을 의미합니다.

  • Threshold 값 0.8로 설정해 “1”로 예측하는 기준을 높였으며, 이는 정밀도를 증가시키고 재현율은 다소 낮췄을 가능성이 있습니다.

  • 성능을 향상시키기 위해 데이터 전처리, 하이퍼파라미터 튜닝, 또는 Threshold 조정을 고려할 수 있습니다.


제 3 유형


데이터셋 : https://raw.githubusercontent.com/YoungjinBD/dataset/main/HR-Employee-Attrition.csv

결혼유무(미혼, 결혼, 이혼)에 따라 초과근로 여부에 차이가 있는지 카이제곱 검정을 이용하여 확인하시오.

  1. 주어진 데이터로 결혼유무와 초과근로 간의 분할표를 만들고, 결혼한 집단의 초과근로자 수와 초과근로 하지 않은 자 수의 차이를 정수로 계산하시오.

  2. 가설검정을 위한 검정통계량을 소수점 둘째자리까지 반올림하여 구하시오.

  3. 통계량에 대한 p-value 값을 소수점 넷째자리까지 반올림하여 구하고, 유의수준 0.05 내에서 결과를 논의하시오. (채택과 기각 중 선택)

import pandas as pd
import scipy.stats as stats

# csv 파일 위치 입력
df = pd.read_csv('https://raw.githubusercontent.com/YoungjinBD/dataset/main/HR-Employee-Attrition.csv')

df[['MaritalStatus','OverTime']]
##      MaritalStatus OverTime
## 0           Single      Yes
## 1          Married       No
## 2           Single      Yes
## 3          Married      Yes
## 4          Married       No
## ...            ...      ...
## 1465       Married       No
## 1466       Married       No
## 1467       Married      Yes
## 1468       Married       No
## 1469       Married       No
## 
## [1470 rows x 2 columns]
# 결혼 유무와 초과근로 간의 분할표
table = pd.crosstab(df['OverTime'], df['MaritalStatus'])
print(table)
## MaritalStatus  Divorced  Married  Single
## OverTime                                
## No                  228      487     339
## Yes                  99      186     131
  • 초과근무 여부(OverTime)가 “Yes” 또는 “No”인 그룹의 결혼 상태 데이터를 분리합니다.

  • 초과근무 여부에 따른 “기혼(Married)” 상태의 인원 수를 계산합니다.

# X1: 초과근로자에 대한 결혼 여부
# X2: 초과근로 하지 않은 자의 결혼 여부
X1 = table.loc['Yes', :]
X2 = table.loc['No', :]
print(X1)
## MaritalStatus
## Divorced     99
## Married     186
## Single      131
## Name: Yes, dtype: int64
print(X2)
## MaritalStatus
## Divorced    228
## Married     487
## Single      339
## Name: No, dtype: int64
  • 두 그룹의 “기혼” 상태 인원 수 차이를 계산하고 출력합니다.
X1_Married = X1['Married']
X2_Married = X2['Married']
print(int(abs(X1_Married - X2_Married)))
## 301
  • 결과적으로 초과근무 여부에 따라 기혼 상태 인원 수의 차이는 301명입니다.

  • 카이제곱 독립성 검정 (Chi-squared Test for Independence).

    • 귀무가설 (H₀): 두 변수는 독립적이다.

    • 대립가설 (H₁): 두 변수는 독립적이지 않다.

result = stats.chi2_contingency([X1, X2])
print(round(result.statistic, 2))
## 0.82
p_value = result.pvalue
print(round(p_value, 4))
## 0.6647
if p_value < 0.05:
    print('기각')
else:
    print('채택')
## 채택
  • 결과: p-value가 0.6647로 귀무가설을 채택하므로, 초과근무 여부와 결혼 상태는 독립적이다.