AIFFEL(220103)_다양한 데이터 전처리 기법

NODE : 10. 다양한 데이터 전처리 기법


학습목표

  1. 중복된 데이터를 찾아 제거할 수 있고, 결측치(missing data)를 제거하거나 채워 넣을 수 있다.
  2. 데이터를 정규화시킬 수 있다.
  3. 이상치(outlier)를 찾고, 이를 처리할 수 있다.
  4. 범주형 데이터를 원-핫 인코딩할 수 있다.
  5. 연속적인 데이터를 구간으로 나눠 범주형 데이터로 변환할 수 있다

전처리는 왜 중요할까?
아무 처리하지 않은 데이터와 가공한 데이터를 모델에 넣어봤을 때 성능 차이가 나게 된다. “데이터 분석의 8할은 데이터 전처리이다.”라는 말이 있다. 왜 데이터 분석에 있어 전처리는 중요한 걸까?
전처리에 따라서 데이터 분석의 질이 달라지기 때문이다. 전처리가 충분히 되어있지 않거나 잘못된 데이터를 사용한 경우 분석 결과의 신뢰도가 떨어지고, 예측 모델의 정확도도 떨어질 것이다. 반면 전처리가 잘 되어 있는 경우 데이터 분석의 질이 높아지고 예측 모델의 성능을 높일 수 있다.

📖 결측치(Missing Data)

현실에서 다룰 데이터는 결측치를 포함하고 있는 경우가 많다. 이미 결측치가 존재한다면 이를 처리해 주어야 한다.

결측치를 대체하는 방법은 다양한데 데이터마다 특성을 반영하여 해결해야 한다.

결측치를 처리하는 방법 두 가지

  • 결측치가 있는 데이터를 제거한다.
  • 결측치를 어떤 값으로 대체한다.

먼저 결측치 여부를 먼저 살펴본다.
전체 데이터 건수에서 각 컬럼별 값이 있는 데이터 수를 빼주면 컬럼별 결측치의 개수를 알 수 있다.

print('전체 데이터 건수:', len(trade))

print('컬럼별 결측치 개수')
len(trade) - trade.count()
# 출력결과
컬럼별 결측치 개수
기간        0
국가명       0
수출건수      3
수출금액      4
수입건수      3
수입금액      3
무역수지      4
기타사항    199
dtype: int64

'기타사항'은 전부 결측치라는 것을 알 수 있다. 이는 아무런 정보가 없는 컬럼이므로 삭제

trade = trade.drop('기타사항', axis=1)
trade.head()

DataFrame.isnull()은 데이터마다 결측치 여부를 True, False로 반환한다. DataFrame.any(axis=1)는 행마다 하나라도 True가 있으면 True, 그렇지 않으면 False를 반환한다.

두 메서드를 조합하여 결측치가 하나라도 있는 행을 찾아본다.

trade.isnull().any(axis=1)
# 실행결과
0      False
1      False
2      False
3      False
4      False
       ...  
194    False
195    False
196     True
197     True
198     True
Length: 199, dtype: bool

trade.isnull().any(axis=1)을 다시 DataFrame에 넣어 값이 True인 데이터만 추출

trade[trade.isnull().any(axis=1)]

index 191 데이터는 수출금액과 무역수지 컬럼이 빠져있고, index 196, 197, 198은 기간, 국가명을 제외하고 모두 결측치
이 경우 index 191 데이터는 삭제하기보다 특정 값으로 대체하는 것이 좋다. 반면 index 196, 197, 198은 제거하는 것이 바람직.

우선 '수출건수', '수출금액', '수입건수', '수입금액', '무역수지' 열이 모두 결측치인 index 196, 197, 198을 삭제

trade.dropna(how='all', subset=['수출건수', '수출금액', '수입건수', '수입금액', '무역수지'], inplace=True)
print("👽 It's okay, no biggie.")

DataFrame의 dropna는 결측치를 삭제해 주는 메서드이다. subset 옵션으로 특정 컬럼들을 선택. how 옵션으로 선택한 컬럼 전부가 결측치인 행을 삭제하겠다는 의미로 'all'을 선택('any': 하나라도 결측치인 경우). inplace 옵션으로 해당 DataFrame 내부에 바로 적용시킨다.

trade[trade.isnull().any(axis=1)]

index 191과 같이 수치형 데이터를 보완할 방법은 많다.

  • 특정 값을 지정해 줄 수 있다. 그러나 결측치가 많은 경우, 모두 같은 값으로 대체한다면 데이터의 분산이 실제보다 작아지는 문제가 생길 수 있다.

  • 평균, 중앙값 등으로 대체할 수 있다. 1번에서 특정 값으로 대체했을 때와 마찬가지로 결측치가 많은 경우 데이터의 분산이 실제보다 작아지는 문제가 발생할 수 있다.

  • 다른 데이터를 이용해 예측값으로 대체할 수 있다. 예를 들어 머신러닝 모델로 2020년 4월 미국의 예측값을 만들고, 이 값으로 결측치를 보완할 수 있다.

  • 시계열 특성을 가진 데이터의 경우 앞뒤 데이터를 통해 결측치를 대체할 수 있다. 예를 들어 기온을 측정하는 센서 데이터에서 결측치가 발생할 경우, 전후 데이터의 평균으로 보완할 수 있다.

index 191은 4번 방법을 통해 보완

DataFrame.loc[행 라벨, 열 라벨]을 입력하면 해당 라벨을 가진 데이터를 출력한다.

trade.loc[[188, 191, 194]]

index 191의 수출금액 컬럼값을 이전 달과 다음 달의 평균으로 채운다.

trade.loc[191, '수출금액'] = (trade.loc[188, '수출금액'] + trade.loc[194, '수출금액'] )/2
trade.loc[[191]]

ndex 191의 무역수지 컬럼은 수출금액과 수입금액의 차이를 이용

trade.loc[191, '무역수지'] = trade.loc[191, '수출금액'] - trade.loc[191, '수입금액'] 
trade.loc[[191]]

데이터가 범주형인 경우 결측치를 삭제하거나 대체하는 방법은 수치형일 때와 유사하다.

  • 특정 값을 지정해 줄 수 있다. 예를 들어 ‘기타’, ‘결측’과 같이 새로운 범주를 만들어 결측치를 채울 수 있다.

  • 최빈값 등으로 대체. 결측치가 많은 경우 최빈값이 지나치게 많아질 수 있으므로 결측치가 많을 때는 다른 방법을 사용한다.

  • 다른 데이터를 이용해 예측값으로 대체할 수 있다.

  • 시계열 특성을 가진 데이터의 경우 앞뒤 데이터를 통해 결측치를 대체할 수 있다. 예를 들어 특정인의 2019년 직업이 결측치이고, 2018년과 2020년 직업이 일치한다면 그 값으로 보완할 수 있다. 만약 다르다면 둘 중 하나로 보완하도록 한다.

📖 중복된 데이터

데이터를 수집하는 과정에서 중복된 데이터가 생길 수 있다. 같은 값을 가진 데이터 없이 행(row)별로 값이 유일해야 한다면 중복된 데이터를 제거해야 한다.

우선 중복된 데이터를 확인한다. DataFrame.duplicated()는 중복된 데이터 여부를 불리언 값으로 반환한다.

trade.duplicated()
# 실행결과
0      False
1      False
2      False
3      False
4      False
       ...  
191    False
192    False
193    False
194    False
195    False
Length: 196, dtype: bool
trade[trade.duplicated()]

trade[(trade['기간']=='2020년 03월')&(trade['국가명']=='중국')]

index 186, 187 중복

pandas에서는 * DataFrame.drop_duplicates를 통해 중복된 데이터를 손쉽게 삭제 가능하다.

trade.drop_duplicates(inplace=True)
print("👽 It's okay, no biggie.")

* DataFrame.drop_duplicates

다음과 같이 id와 name을 컬럼으로 갖는 df가 있다.

df = pd.DataFrame({'id':['001', '002', '003', '004', '002'], 
                   'name':['Park Yun', 'Kim Sung', 'Park Jin', 'Lee Han', 'Kim Min']})
df

id가 002인 데이터가 2개있다. id가 사람마다 unique 하다고 할 때, 둘 중 하나는 삭제 필요
index가 클수록 나중에 들어온 데이터이고, 사용자가 이름을 수정했을 때 업데이트가 되지 않고 삽입이 되어 생긴 문제라고 가정한다면 id가 중복된 경우 맨 나중에 들어온 값만 남겨야 한다.

DataFrame.drop_duplicatessubset, keep 옵션을 통해 손쉽게 중복을 제거할 수 있다.

df.drop_duplicates(subset=['id'], keep='last')

📖 이상치(Outlier)

이상치(Outlier) : 대부분 값의 범위에서 벗어나 극단적으로 크거나 작은 값
trade 데이터에서 큰 값을 가지는 이상치가 있다고 가정해 본다.
Min-Max Scaling 해보면 대부분의 값은 0에 가깝고 이상치만 1에 가까운 값을 가지게 될 것이다. 이렇게 몇 개의 이상치 때문에 대부분 값의 차이는 의미가 거의 없어지게 된다. 극단적인 값이 생기는 경우를 제외하고 데이터를 고려하고 싶은 경우 이상치를 제거하고 분석한다.

이상치를 어떻게 찾아내야 할까? 현실에서는 이상치를 찾는 것(anomaly detection) 자체가 큰 분야이다.

가장 먼저 생각해 볼 수 있는 간단하고 자주 사용되는 방법은 평균과 표준편차를 이용하는 z score방법이다.

평균을 빼주고 표준편차로 나눠 z score를 계산한다. 그리고 z score가 특정 기준을 넘어서는 데이터에 대해 이상치라고 판단한다. 기준을 작게 하면 이상치라고 판단하는 데이터가 많아지고, 기준을 크게 하면 이상치라고 판단하는 데이터가 적어진다.

이상치를 판단한 뒤에는

  • 가장 간단한 방법으로 이상치를 삭제할 수 있다. 이상치를 원래 데이터에서 삭제하고, 이상치끼리 따로 분석하는 방안도 있다.

  • 이상치를 다른 값으로 대체할 수 있다. 데이터가 적으면 이상치를 삭제하기보다 다른 값으로 대체하는 것이 나을 수 있다. 예를 들어 최댓값, 최솟값을 설정해 데이터의 범위를 제한할 수 있습니다.

  • 혹은 결측치와 마찬가지로 다른 데이터를 활용하여 예측 모델을 만들어 예측값을 활용할 수도 있다.

  • 아니면 binning을 통해 수치형 데이터를 범주형으로 바꿀 수도 있다.

(1) z-score method

이상치인 데이터의 인덱스를 리턴하는 outlier라는 함수를 만든다. 데이터프레임 df, 컬럼 col, 기준 z를 인풋으로 받는다.

  • abs(df[col] - np.mean(df[col])) : 데이터에서 평균을 빼준 것에 절대값을 취합니다.
  • abs(df[col] - np.mean(df[col]))/np.std(df[col]) : 위에 한 작업에 표준편차로 나눠줍니다.
  • df[abs(df[col] - np.mean(df[col]))/np.std(df[col])>z].index : 값이 z보다 큰 데이터의 인덱스를 추출합니다.
def outlier(df, col, z):
    return df[abs(df[col] - np.mean(df[col]))/np.std(df[col])>z].index
print("👽 It's okay, no biggie.")
trade.loc[outlier(trade, '무역수지', 1.5)]
trade.loc[outlier(trade, '무역수지', 2)]

trade.loc[outlier(trade, '무역수지', 3)]

무역수지의 이상치를 확인하는데 기준 되는 값이 클수록 이상치가 적어지는 것을 확인할 수 있다.

not_outlier라는 함수를 통해 무역수지가 이상치 값이 아닌 데이터만 추출한다.

def not_outlier(df, col, z):
    return df[abs(df[col] - np.mean(df[col]))/np.std(df[col]) <= z].index
print("👽 It's okay, no biggie.")
trade.loc[not_outlier(trade, '무역수지', 1.5)]

(2) IQR method

z-score 방법은 몇 가지 뚜렷한 한계점을 가지고 있다.
z-score 방법의 대안으로 사분위 범위수 IQR(Interquartile range) 로 이상치를 알아내는 방법을 알아본다.

아웃라이어가 포함된 임의의 데이터를 만든다.

np.random.seed(2020)
data = np.random.randn(100)  # 평균 0, 표준편차 1의 분포에서 100개의 숫자를 샘플링한 데이터 생성
data = np.concatenate((data, np.array([8, 10, -3, -5])))      # [8, 10, -3, -5])를 데이터 뒤에 추가함
data
# 실행결과
array([-1.76884571,  0.07555227, -1.1306297 , -0.65143017, -0.89311563,
       -1.27410098, -0.06115443,  0.06451384,  0.41011295, -0.57288249,
       -0.80133362,  1.31203519,  1.27469887, -1.2143576 ,  0.31371941,
       -1.44482142, -0.3689613 , -0.76922658,  0.3926161 ,  0.05729383,
        2.08997884,  0.04197131, -0.04834072, -0.51315392, -0.08458928,
       -1.21545008, -1.41293073, -1.48691055,  0.38222486,  0.937673  ,
        1.77267804,  0.87882801,  0.33171912, -0.30603567,  1.24026615,
       -0.21562684,  0.15592948,  0.09805553,  0.83209585,  2.04520542,
       -0.31681392, -1.31283291, -1.75445746,  0.10209408, -1.36150208,
        0.48178488, -0.20832874, -0.09186351,  0.70268816,  0.10365506,
        0.62123638,  0.95411497,  2.03781352, -0.48445122,  0.2071549 ,
        1.64424216, -0.4882074 , -0.01782826,  0.46891556,  0.27987266,
       -0.64664972, -0.54406002, -0.16008985,  0.03781172,  1.03383296,
       -1.23096117, -1.24673665,  0.29572055,  2.1409624 , -0.92020227,
       -0.06000238,  0.27978391, -1.53126966, -0.30293101, -0.14601413,
        0.27746159, -0.13952066,  0.69515966, -0.11338746, -1.233267  ,
       -0.79614131, -0.46739138,  0.65890607, -0.41063115,  0.17344356,
        0.28946174,  1.03451736,  1.22661712,  1.71998252,  0.40806834,
        0.32256894,  1.04722748, -1.8196003 , -0.42582157,  0.12454883,
        2.31256634, -0.96557586, -0.34627486,  0.96668378, -0.92550192,
        8.        , 10.        , -3.        , -5.        ])

아래 박스 플롯에서 박스를 벗어난 점들이 이상치

fig, ax = plt.subplots()
ax.boxplot(data)
plt.show()

사분위 범위수 IQR(Interquartile range)을 이용하여 이상치를 찾아낼 수 있다.

즉, IQR은 제 3사분위수에서 제 1사분위 값을 뺀 값으로 데이터의 중간 50%의 범위라고 생각하면 된다.
Q1−1.5∗IQR보다 왼쪽에 있거나, Q3+1.5*IQR 보다 오른쪽에 있는 경우 이상치라고 판단한다.

IQR을 구하기 위해 우선 제1사분위수와 제 3사분위수를 구한다.

Q3, Q1 = np.percentile(data, [75 ,25])
IQR = Q3 - Q1
IQR
# 실행결과
1.1644925829790964

IQR과 제 1사분위수, 제 3사분위수를 이용하여 이상치를 확인할 수 있다.

data[(Q1-1.5*IQR > data)|(Q3+1.5*IQR < data)]
# 실행결과
array([ 2.31256634,  8.        , 10.        , -3.        , -5.        ])

Three ways to detect outliers

📖 정규화(Normalization)

trade 데이터를 보면 수입건수, 수출건수와 수입금액, 수출금액, 무역수지는 단위가 다르다는 것을 알 수 있다.
이처럼 컬럼마다 스케일이 크게 차이가 나는 데이터를 입력하면 머신러닝 모델 학습에 문제가 발생할 수 있다.
그래서 일반적으로 컬럼 간에 범위가 크게 다를 경우 전처리 과정에서 데이터를 정규화한다.

정규화를 하는 방법은 다양하지만, 가장 잘 알려진 표준화(Standardization)와 Min-Max Scaling을 알본다.
Standardization 데이터의 평균은 0, 분산은 1로 변환

Min-Max Scaling 데이터의 최솟값은 0, 최댓값은 1로 변환

정규화 기법이 데이터의 분포를 어떻게 바꾸는지 살펴보기 위해 임의의 데이터를 생성하고, 각각의 기법으로 데이터를 정규화시켜준다.

# 정규분포를 따라 랜덤하게 데이터 x를 생성합니다. 
x = pd.DataFrame({'A': np.random.randn(100)*4+4,
                 'B': np.random.randn(100)-1})
x

# 데이터 x를 Standardization 기법으로 정규화합니다. 
x_standardization = (x - x.mean())/x.std()
x_standardization

# 데이터 x를 min-max scaling 기법으로 정규화합니다. 
x_min_max = (x-x.min())/(x.max()-x.min())
x_min_max

다음 이미지는 데이터를 Standardization 기법으로 정규화를 했을 때 분포가 어떻게 바뀌는지 보여준다. 즉, 각 컬럼의 평균은 0으로, 분산은 1로 데이터를 바꿔준다.

fig, axs = plt.subplots(1,2, figsize=(12, 4),
                        gridspec_kw={'width_ratios': [2, 1]})

axs[0].scatter(x['A'], x['B'])
axs[0].set_xlim(-5, 15)
axs[0].set_ylim(-5, 5)
axs[0].axvline(c='grey', lw=1)
axs[0].axhline(c='grey', lw=1)
axs[0].set_title('Original Data')

axs[1].scatter(x_standardization['A'], x_standardization['B'])
axs[1].set_xlim(-5, 5)
axs[1].set_ylim(-5, 5)
axs[1].axvline(c='grey', lw=1)
axs[1].axhline(c='grey', lw=1)
axs[1].set_title('Data after standardization')

plt.show()


다음 이미지는 동일한 데이터를 min-max scaling 기법으로 정규화를 했을 때 분포가 어떻게 바뀌는지 보여준다. 즉, 각 컬럼의 최솟값은 0, 최댓값은 1로 바꿔준다.

fig, axs = plt.subplots(1,2, figsize=(12, 4),
                        gridspec_kw={'width_ratios': [2, 1]})

axs[0].scatter(x['A'], x['B'])
axs[0].set_xlim(-5, 15)
axs[0].set_ylim(-5, 5)
axs[0].axvline(c='grey', lw=1)
axs[0].axhline(c='grey', lw=1)
axs[0].set_title('Original Data')

axs[1].scatter(x_min_max['A'], x_min_max['B'])
axs[1].set_xlim(-5, 5)
axs[1].set_ylim(-5, 5)
axs[1].axvline(c='grey', lw=1)
axs[1].axhline(c='grey', lw=1)
axs[1].set_title('Data after min-max scaling')

plt.show()

trade 데이터로 살펴본다.

Standardization
우선 정규화를 시켜야 할 수치형 컬럼들을 cols 변수에 담은 후, 데이터에서 평균을 빼고, 표준편차로 나눠준다.

# trade 데이터를 Standardization 기법으로 정규화합니다. 
cols = ['수출건수', '수출금액', '수입건수', '수입금액', '무역수지']
trade_Standardization= (trade[cols]-trade[cols].mean())/trade[cols].std()
trade_Standardization.head()

standardization 방법으로 정규화시킨 trade_Standardization을 확인해본다.(각 컬럼의 평균들을 보면 거의 0에 가깝고, 표준편차는 1에 가깝다.)

trade_Standardization.describe()

Min-Max Scaling
데이터에서 최솟값을 빼주고, '최댓값-최솟값'으로 나눠준다.

# trade 데이터를 min-max scaling 기법으로 정규화합니다. 
trade[cols] = (trade[cols]-trade[cols].min())/(trade[cols].max()-trade[cols].min())
trade.head()

Min-Max Scaling 방법으로 정규화시킨 후, 각 컬럼의 최솟값(min)은 0이고, 최댓값(max)은 1임을 확인할 수 있다.

trade.describe()

🔥주의!!🔥
train 데이터와 test 데이터가 나눠져 있는 경우 train 데이터를 정규화시켰던 기준 그대로 test 데이터도 정규화 시켜줘야 한다.

train = pd.DataFrame([[10, -10], [30, 10], [50, 0]])
test = pd.DataFrame([[0, 1], [10, 10]])
print("👽 It's okay, no biggie.")
train_min = train.min()
train_max = train.max()

train_min_max = (train - train_min)/(train_max - train_min)
test_min_max =  (test - train_min)/(train_max - train_min)    # test를 min-max scaling할 때도 train 정규화 기준으로 수행
print("💫 It's okay, no biggie...")
train_min_max

train_min_max

scikit-learn의 StandardScaler, MinMaxScaler를 사용하는 방법도 있다.

from sklearn.preprocessing import MinMaxScaler
train = [[10, -10], [30, 10], [50, 0]]
test = [[0, 1]]
scaler = MinMaxScaler()
print("👽 It's okay, no biggie.")
scaler.fit_transform(train)
# 실행결과
array([[0. , 0. ],
       [0.5, 1. ],
       [1. , 0.5]])
scaler.transform(test)
# 실행결과
array([[-0.25,  0.55]])

Normalizing Inputs

📖 원-핫 인코딩(One-Hot Encoding)

이제 범주형 데이터인 국가명 컬럼을 다뤄보도록 한다.
머신러닝이나 딥러닝 프레임워크에서 범주형을 지원하지 않는 경우 원-핫 인코딩을 해야 한다.
원-핫 인코딩(One-Hot Encoding) : 카테고리별 이진 특성을 만들어 해당하는 특성만 1, 나머지는 0으로 만드는 방법

pandas에서 get_dummies 함수를 통해 손쉽게 원-핫 인코딩을 할 수 있다.

#trade 데이터의 국가명 컬럼 원본
print(trade['국가명'].head())  

# get_dummies를 통해 국가명 원-핫 인코딩
country = pd.get_dummies(trade['국가명'])
country.head()
# 실행결과
0    중국
1    미국
2    일본
3    중국
4    미국
Name: 국가명, dtype: object


pd.concat 함수로 데이터프레임 trade와 country를 합쳐준다.

trade = pd.concat([trade, country], axis=1)
trade.head()

이제는 필요 없어진 국가명 컬럼을 삭제해 주고 나면 trade는 우리가 원하는 데이터프레임이 된다.

trade.drop(['국가명'], axis=1, inplace=True)
trade.head()

📖 구간화(Binning)

지금까지 trade 데이터를 다루면서 다양 전처리 기법을 배웠다. 이제 다른 전처리 기법을 배워보도록 한다.

salary에 소득 데이터가 있다.

salary = pd.Series([4300, 8370, 1750, 3830, 1840, 4220, 3020, 2290, 4740, 4600, 
                    2860, 3400, 4800, 4470, 2440, 4530, 4850, 4850, 4760, 4500, 
                    4640, 3000, 1880, 4880, 2240, 4750, 2750, 2810, 3100, 4290, 
                    1540, 2870, 1780, 4670, 4150, 2010, 3580, 1610, 2930, 4300, 
                    2740, 1680, 3490, 4350, 1680, 6420, 8740, 8980, 9080, 3990, 
                    4960, 3700, 9600, 9330, 5600, 4100, 1770, 8280, 3120, 1950, 
                    4210, 2020, 3820, 3170, 6330, 2570, 6940, 8610, 5060, 6370,
                    9080, 3760, 8060, 2500, 4660, 1770, 9220, 3380, 2490, 3450, 
                    1960, 7210, 5810, 9450, 8910, 3470, 7350, 8410, 7520, 9610, 
                    5150, 2630, 5610, 2750, 7050, 3350, 9450, 7140, 4170, 3090])
print("👽 Almost there..")

이 데이터를 구간별로 나누고자 한다. 이러한 기법을 구간화(Data binning 혹은 bucketing)이라고 부른다.

아래 히스토그램과 같이 연속적인 데이터를 구간을 나눠 분석할 때 사용하는 방법이다.

salary.hist()

pandas의 cutqcut을 이용해 수치형 데이터를 범주형 데이터로 변형시킨다.

cut을 사용하기 위해 구건을 정한다.

bins = [0, 2000, 4000, 6000, 8000, 10000]
print("👽 Almost there..")

cut 함수에 데이터와 구간을 입력하면 데이터를 구간별로 나눠준다.

ctg = pd.cut(salary, bins=bins)
ctg
# 실행결과
0      (4000, 6000]
1     (8000, 10000]
2         (0, 2000]
3      (2000, 4000]
4         (0, 2000]
          ...      
95     (2000, 4000]
96    (8000, 10000]
97     (6000, 8000]
98     (4000, 6000]
99     (2000, 4000]
Length: 100, dtype: category
Categories (5, interval[int64, right]): [(0, 2000] < (2000, 4000] < (4000, 6000] < (6000, 8000] < (8000, 10000]]

salary[0]는 4300으로 4000에서 6000 사이에 포함되었다는 것을 확인할 수 있다.

print('salary[0]:', salary[0])
print('salary[0]가 속한 카테고리:', ctg[0])
# 실행결과
salary[0]: 4300
salary[0]가 속한 카테고리: (4000, 6000]

구간별로 값이 몇 개가 속해 있는지 value_counts()로 확인해 본다.

ctg.value_counts().sort_index()
# 실행결과
(0, 2000]        12
(2000, 4000]     34
(4000, 6000]     29
(6000, 8000]      9
(8000, 10000]    16
dtype: int64

이렇게 특정 구간을 지정해 줘도 되고, 구간의 개수를 지정해 줄 수도 있다.
bins 옵션에 정수를 입력하면 데이터의 최솟값에서 최댓값을 균등하게 bins 개수만큼 나눠준다.

ctg = pd.cut(salary, bins=6)
ctg
# 실행결과
0      (4230.0, 5575.0]
1      (8265.0, 9610.0]
2     (1531.93, 2885.0]
3      (2885.0, 4230.0]
4     (1531.93, 2885.0]
            ...        
95     (2885.0, 4230.0]
96     (8265.0, 9610.0]
97     (6920.0, 8265.0]
98     (2885.0, 4230.0]
99     (2885.0, 4230.0]
Length: 100, dtype: category
Categories (6, interval[float64, right]): [(1531.93, 2885.0] < (2885.0, 4230.0] < (4230.0, 5575.0] < (5575.0, 6920.0] < (6920.0, 8265.0] < (8265.0, 9610.0]]
ctg.value_counts().sort_index()
# 실행결과
(1531.93, 2885.0]    27
(2885.0, 4230.0]     24
(4230.0, 5575.0]     21
(5575.0, 6920.0]      6
(6920.0, 8265.0]      7
(8265.0, 9610.0]     15
dtype: int64

qcut은 구간을 일정하게 나누는 것이 아니라 데이터의 분포를 비슷한 크기의 그룹으로 나눠준다.

ctg = pd.qcut(salary, q=5)
ctg
# 실행결과
0       (3544.0, 4648.0]
1       (7068.0, 9610.0]
2     (1539.999, 2618.0]
3       (3544.0, 4648.0]
4     (1539.999, 2618.0]
             ...        
95      (2618.0, 3544.0]
96      (7068.0, 9610.0]
97      (7068.0, 9610.0]
98      (3544.0, 4648.0]
99      (2618.0, 3544.0]
Length: 100, dtype: category
Categories (5, interval[float64, right]): [(1539.999, 2618.0] < (2618.0, 3544.0] < (3544.0, 4648.0] < (4648.0, 7068.0] < (7068.0, 9610.0]]
print(ctg.value_counts().sort_index())
print(".\n.\n🛸 Well done!")
# 실행결과
(1539.999, 2618.0]    20
(2618.0, 3544.0]      20
(3544.0, 4648.0]      20
(4648.0, 7068.0]      20
(7068.0, 9610.0]      20
dtype: int64
.
.
🛸 Well done!

좋은 웹페이지 즐겨찾기