Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

여유로움

[11.27 토] 빅데이터 분석기사 실기 - Pandas (30~35강) 본문

셀프스터디/빅데이터 분석기사

[11.27 토] 빅데이터 분석기사 실기 - Pandas (30~35강)

티로즈 2021. 11. 27. 14:54

1. 통계 분석 개요

  • 모집단(population): 연구 대상 데이터 전체 집합
  • 모수(parameter) : 모집단의 특성을 나타내는 수치
  • 표본(sample) : 모집단에서 추출한 일부 데이터
  • 통계량(statistic) : 표분의 특성을 나타내는 수치
# [1] 평균, 분산, 표준편차 구하기
# Delta Degrees of Freedom (ddof=1) : pandas - 표본이 기준, 모분산/모표준편차는 ddof=0으로 주어야 함

s = pd.Series([12345678910])
print('표본평균:', s.mean())
print('표본분산:', s.var())
print('표본표준편차:', s.std())
print('모분산:', s.var(ddof=0))
print('모표준편차:', s.std(ddof=0))
# [2] numpy의 var()와 pandas의 var()가 다른 이유는?
# ddof=0, numpy - 모집단
s = np.array([12345678910])
print('평균:', s.mean())
print('표본분산:', s.var(ddof=1))
print('표본표준편차:', s.std(ddof=1))
print('모분산:', s.var())
print('모표준편차:', s.std())

 

2. 표본추출(Sampling)

# [1] tips 데이터를 가져오기 한다
tips = sns.load_dataset("tips")
tips.head(3)

# [2] 단순 무작위 추출 - 비율 사용
# 0.02(2%) 추출 : 244 * 0.02 = 4.88 -> 5개
tips.sample(frac=0.02, random_state=1)

# [3] 단순 무작위 추출 - 정수 사용
# 5개 sample 추출
tips.sample(n=5, random_state=1)

# [4] 계통 추출 - 1부터 시작해 50 마다 1개 추출
# tips의 iloc을 사용해 특정 번호 list => [1, 51, 101, 151, 201] 로 indexing
tips.iloc[range(1len(tips), 50),:]

# [5] 층화 추출 - 성별을 기준으로 성별 비율에 맞춰 10개 데이터 추출
# [5-1] 성별 비율 구하기(rate), 추출할 sample 개수 구하기(sample_n)
sample_n = 10
temp = tips['sex'].value_counts(normalize=True).to_frame()
temp.columns = ['rate']
temp['sample_n'] = round(sample_n * temp['rate'], 0).astype('int')
temp

# [5-2] Female, Male로 데이터 분리 및 sample개수 만큼의 임의 표본 추출
Female = tips.loc[tips['sex'] == 'Female', :]
Male = tips.loc[tips['sex'] == 'Male', :]
df1 = Female.sample(n=temp.loc['Female''sample_n'], random_state=1)
df2 = Male.sample(n=temp.loc['Male''sample_n'], random_state=1)
df = df1.append(df2)  # pd.concat([df1, df2])
df = df.sample(frac=1, random_state=123#전체 데이터를 섞을 때 사용
df

 

3. 이상치

  • 일반적인 값들과 많이 떨어진 위치의 데이터
  • 평균은 이상치에 영향을 크게 받지만, 중앙값은 영향을 거의 받지 않음
# [1] 이상치와 평균, 중앙값의 관계
import pandas as pd
data1 = [10,20,30,40,50]
data2 = [10,20,30,40,200]
a = pd.Series(data1)
b = pd.Series(data2)
print('평균  ', a.mean(), b.mean())
print('중앙값', a.median(), b.median())

# [2] seaborn의 tips 데이터셋 가져오기
tips = sns.load_dataset("tips")

# [3] tips의 'total_bill'의 이상치를 그래프로 확인하기
tips['total_bill'].plot.box(figsize=(3,4))
plt.show()

# [4] DataFrame의 plot을 사용하여 연속형 데이터에 대해 모두 boxplot 그리기
tips.plot(kind='box', subplots=True, figsize=(9,4))
plt.show()

# [5] ESD(Extream Studentized Diviate)를 이용한 방법
# 평균으로 부터 3 표준편차 떨어진 값을 이상치로 판단
# tip에 대한 이상치 구하기, 소수점 아래 2째자리까지 표기
s = tips['tip']
s_mean, s_std = s.mean(), s.std()
e_lower = round(s_mean - 3 * s_std, 2#평균-3*표준편차
e_upper = round(s_mean + 3 * s_std, 2#평균+3*표준편차
print(f'Lower: {e_lower}, Upper: {e_upper}')

s = tips['tip']
s_mean, s_std = s.mean(), s.std()
e_lower = round(s_mean-3*s_std, 2)
e_upper = round(s_mean+3*std,2)
print(f'Lower: {e_lower}, Uppser:{e_upper}')

# [6] 사분위수를 이용한 방법
# Q1 - 1.5*IQR 미만, Q3 + 1.5*IQR 초과 를 이상치로 판단 (IQR = Q3 - Q1)
# tip에 대한 이상치 구하기, 소수점 아래 2째자리까지 표기
s = tips['tip']
Q1, Q3 = s.quantile([0.250.75])
IQR = Q3 - Q1
q_lower = Q1 - 1.5 * IQR 
q_upper = Q3 + 1.5 * IQR
print(f'Lower: {q_lower}, Upper: {q_upper}')

 

4. 이상치 처리

# [7] 이상치 제거 - 정상범주에 있는 데이터를 indexing 하는 방법으로 처리 (q_lower, q_upper 사이값이 정상)

tips2 = tips[(tips['tip'] > q_lower) & (tips['tip'] < q_upper)] #q_lower보다 크고, q_upper보다 작은 데이터
print(tips.shape, tips2.shape)

# [8] 이상치 데이터 모음
tips_outlier = tips[(tips['tip'] <= q_lower) | (tips['tip'] >= q_upper)]
tips_outlier

# [9] 이상값 대체
# q_upper 보다 큰 데이터는 q_upper, q_lower 보다 작은 데이터는 q_lower로 변경
# tips3 데이터프레임에서 'tip' 값이 q_upper 보다 큰 것의 'tip'값을 q_upper 값으로 변경
# tips3 데이터프레임에서 'tip' 값이 q_lower 보다 작은 것의 'tip'값을 q_lower 값으로 변경
tips3 = tips.copy()
# code here
tips3.loc[tips3['tip'] > q_upper, 'tip'] = q_upper
tips3.loc[tips3['tip'] < q_lower, 'tip'] = q_lower
tips3.loc[(tips3['tip'] > q_upper) | (tips3['tip'] < q_lower)]

tips3['tip'].plot.box()
plt.show()

 

5. 데이터 변환(Transform)

  • 이상치를 완화하거나, 정규분포가 되도록 하기 위해 사용함
  • numpy의 log1p, sqrt, expm1, power 등의 함수 사용
    • log1p, sqrt 는 큰 값을 작게 만들어 주며, 오른쪽 꼬리가 긴 분포를 정규분포로 변환하는데 사용, 큰 이상치를 작게 만들 수 있음
    • expm1, power는 작은 값을 크게 만들어 주며, 왼쪽 꼬리가 긴 분포를 정규분포로 변환하는데 사용함
# [0] 데이터 가져오기
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")

# [1] tips에서 tip의 분포 확인
s = tips['tip']
s.plot.hist(bins=20)
plt.show() #오른쪽으로 꼬리가 길 때 => log, sqrt 함수 이용

# [5] tip의 분포를 오른쪽 꼬리가 긴 것에 대해서 짧게 만들기 (로그)
s = np.log1p(tips['tip']) #데이터가 0을 포함하고 있을 것 같을 때 log1p 사용
s.plot.hist(bins=20)
plt.show()

# [6] tip의 분포를 오른쪽 꼬리가 긴 것에 대해서 짧게 만들기  (제곱근)
s = np.sqrt(tips['tip'])
s.plot.hist(bins=20)
plt.show()

# [7] tips['tip'] 원본 -> log1p -> expm1 = 원본 
a = tips['tip']
b = np.log1p(a)
c = np.expm1(b)
print(a[:3], b[:3], c[:3], sep='\n\n')

 

6. 데이터 스케일링(Scaling)

  • min-max normalization : 값의 범위를 [0, 1]로 변환
    • (xi - x.min()) / (x.max() - x.min())
  • standardization : 특성의 값이 표준정규분포를 갖도록 변환 (평균 0, 표준편차 1)
    • (xi - x.mean()) / x.std()
# [1] Scaling을 위한 함수 구현
def minmaxScale(x):
    return (x - x.min()) / (x.max() - x.min())  

def standardScale(x):
    return (x - x.mean()) / x.std()

# [2-1] minmaxScale 함수를 사용한 'tip' 컬럼의 스케일링
x = minmaxScale(tips['tip'])
print(x.min(), x.max())

# [2-2] standardScale 함수를 사용한 'tip' 컬럼의 스케일링
x = standardScale(tips['tip'])
print(x.mean(), x.std())

# [3] sklearn 라이브러리의 스케일러(MinMaxScaler)를 사용한 스케일링
# 스케일러의 fit_transform() 사용시 2차원의 데이터를 전달해야 함 
# (DataFrame도 2차원), 결과는 ndarray로 반환 됨
from sklearn.preprocessing import MinMaxScaler, StandardScaler
x = MinMaxScaler().fit_transform(tips[['tip']])
print(x.min(), x.max())

# [5] sklearn 라이브러리의 스케일러(StandardScaler)를 사용한 스케일링
from sklearn.preprocessing import StandardScaler
x = StandardScaler().fit_transform(tips[['tip']])
print(x.mean(), x.std())

# [6] scipy.stats의 zscore 함수를 사용한 스케일링
from scipy.stats import zscore
x = zscore(tips['tip'])     
print(x.mean(), x.std())

 

7. Encoding(범주형->수치형)

  • Label Encoding : 값의 일련번호로 변경 #순서가 있는 경우
    • '여성', '남성', '아이' : 0, 1, 2
    • '월', '화', ... '일' : 0, 1, ...6
    • category 타입의 cat.codes
    • Series.replace()를 사용 함
  • One Hot Encoding : 범주의 개수 만큼의 feature를 만들어냄 #순서가 없는 경우
    • pd.get_dummies(Series/DataFrame)
import pandas as pd
df = pd.DataFrame({'A':['월''화''수''화''수''금''월'],
                   'B':['여자''남자''여자''남자''아이''남자''아이']})

# [1] 요일에 대해서 Label Encoding 합니다.  (cat.codes 사용)
weekdays = '월 화 수 목 금 토 일'.split()
#print(weekdays)
df['A_LE'] = pd.Categorical(df['A'], weekdays, ordered=True)
df['A_LE'] = df['A_LE'].cat.codes

# [2] '남자', '여자', '아이'에 대해서 Label Encoding 합니다 (replace 사용)
#df['B'].replace(['남자', '여자', '아이'], [0, 1, 2]) #dictionary나 list 사용 가능
v = df['B'].unique() #어떤 범주가 있는 지 저장
df['B_LE'] = df['B'].replace(v, range(len(v)))

# [3] df의 'A' 컬럼을 One Hot Encoding 합니다.
a = pd.get_dummies(df['A'])

# [6] df의 모든 범주형 변수를 OneHotEncoding 합니다
df3 = pd.get_dummies(df)

 

8. Binning (수치형->범주형)

  • 연속형 변수를 구간을 이용하여 범주화 하는 과정
  • 정보가 압축되고 단순해짐 정확도는 떨어짐
  • 이상치 해결 방법 중 한 가지로 사용하거나, 오버피팅(overfitting) 방지 기법으로 사용
  • (3, 6] : 3초과 6포함, right=True
  • [3, 6) : 3이상 6포함하지 않음, right=False
  • pd.cut()
# [0] 데이터 준비
data = [012345678910]
df = pd.DataFrame(data, columns=['data'])
df['data'].to_list()

# [1] data를 사용하여 0~3, 4~6, 7~10 에 대한 binning을 하여 보도록 한다
# 범주의 레이블은 ['A', 'B', 'C']를 사용한다

# (Min-1, 3], (3, 6], (6, Max] 로 범주를 만들어 result_A 컬럼으로 추가합니다.
Min, Max = df['data'].min(), df['data'].max()
df['result_A'] = pd.cut(df['data'], [Min-136, Max], labels=['A''B''C'], right=True)

# [Min, 3), [3, 6), [6, Max+1) 로 범주를 만들어 result_B 컬럼으로 추가합니다.
df['result_B'] = pd.cut(df['data'], [Min, 36, Max+1], labels=range(3), right=False)

# [2] result_A에 대해 LabelEncoding을 하여 result_C를 컬럼으로 추가합니다.
df['result_C'] = df['result_A'].cat.codes #카테고리 타입으로 되어 있는 경우, cat.codes 바로 사용 가능
비율을 사용하여 구간 나누기
  • pd.qcut(데이터, 구간)
  • 구간은 0 ~ 1 사이의 숫자 사용
# [3] 비율을 사용하여 구간나누기
df = pd.DataFrame([01231011121320304050], columns=['data'])
pd.qcut(df['data'], [00.250.50.751], labels=range(4))

# [4] 다음 데이터를 binning 하여보세요 (bins 컬럼 추가)
# 사용구간 : [10, 40), [40, 50), [50, 60), [60, 70), [70, 80), [80, 101)
# 사용레이블 : '10-40미만', '40-50미만', '50-60미만', '60-70미만', '70-80미만', '80-100' 
data = [55.683.343.458.131.655.660.764.6,
        73.355.664.352.822.746.371.453.8,
        64.567.971.480.059.540.577.158.6,
        65.452.466.791.341.372.161.978.4,
        63.641.065.281.354.819.650.053.1,
        41.256.5]
df = pd.DataFrame(data, columns=['data'])        
df['bins'] = pd.cut(df['data'], [104050607080101], 
                    labels=['10-40미만''40-50미만''50-60미만''60-70미만''70-80미만''80-100'], 
                    right=False)
df.head()

# [5] bins 컬럼에 대해 LabelEncoding을 하여 bin_n 컬럼을 추가합니다.
df['bin_n'] = df['bins'].cat.codes
df.head()

# [6] 다음 나이 데이터를 binning 하여보세요
bin_labels = 'Baby Child Teenager Student Young_Adult Adult Elderly'.split()   # 7개 label
data = [42114016355811322762115267423315603636]
df = pd.DataFrame(data, columns=['age'])

# (-1, 5], (5, 12], (12, 18], (18, 25], (25, 35], (35, 60], (60, df['age'].max()] 로
# 범주를 만들어 age_cat 컬럼으로 추가합니다.
Max = df['age'].max()
df['age_cat'] = pd.cut(df['age'], [-151218253560, Max], labels=bin_labels, right=True)
df['age_LE'] = df['age_cat'].cat.codes
df.head()

 

출처: 유튜브 강의 EduAtoZ