Python

[Python] 빅데이터 분석 2 : 데이터 전처리(Data Preprocessing)

SangRok Jung 2022. 10. 11. 22:41
반응형

빅데이터 분석 절차

 


 

 

 

 

 

 

 

 

 

 

 

 

 

2. 데이터 전처리 (EDA : 탐색적 데이터 분석)


  • 컬럼명 해석.
    • 변수명.
    • 정의.

 

 

 

▷ 상위, 하위 5개의 컬럼 확인.

df_t.head(5)
df_t.tail(5)

 

 

▷ 데이터의 정보 확인.

df_t.dtypes
df_t.shape
df_t.info()
#  <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 891 entries, 0 to 890
# Data columns (total 15 columns):
#  #   Column       Non-Null Count  Dtype   
# ---  ------       --------------  -----   
#  0   survived     891 non-null    int64   
#  1   pclass       891 non-null    int64   
#  2   sex          891 non-null    object  
#  3   age          714 non-null    float64 
#  4   sibsp        891 non-null    int64   
#  5   parch        891 non-null    int64   
#  6   fare         891 non-null    float64 
#  7   embarked     889 non-null    object  
#  8   class        891 non-null    category
#  9   who          891 non-null    object  
#  10  adult_male   891 non-null    bool    
#  11  deck         203 non-null    category
#  12  embark_town  889 non-null    object  
#  13  alive        891 non-null    object  
#  14  alone        891 non-null    bool    
# dtypes: bool(2), category(2), float64(2), int64(4), object(5)
# memory usage: 80.7+ KB

 

 

 

 

▷ 데이터의 기본 통계 정보 확인.

describe()

df_t.describe()

 

 

 

 

 

생략된 정보 확인.

describe(include="all")

df_t.describe(include="all")

 

 

 

 

 

▷ age, fare의 분포 확인.

sns.distplot()

fig = plt.figure(figsize=(10, 5))

ax3 = fig.add_subplot(1, 2, 1)
ax4 = fig.add_subplot(1, 2, 2)

sns.distplot(df_t.age,  ax=ax3)
sns.distplot(df_t.fare,  ax=ax4)

 

 

 

 

▷ 범주형 데이터의 분포 확인.

sns.countplot()

fig = plt.figure(figsize=(10, 10))

ax1 = fig.add_subplot(2, 2, 1)
ax2 = fig.add_subplot(2, 2, 2)
ax3 = fig.add_subplot(2, 2, 3)
ax4 = fig.add_subplot(2, 2, 4)

sns.countplot(x = "survived", ax=ax1, palette='Set3', data=df_t)
sns.countplot(x = "pclass", ax=ax2, palette='Set3', data=df_t)
sns.countplot(x = "parch", ax=ax3, palette='Set3', data=df_t)
sns.countplot(x = "sibsp", ax=ax4, palette='Set3', data=df_t)

 

 

 

 

▷ 범주형 데이터(문자)의 분포 확인.

fig = plt.figure(figsize=(10, 10))

ax1 = fig.add_subplot(3, 2, 1)
ax2 = fig.add_subplot(3, 2, 2)
ax3 = fig.add_subplot(3, 2, 3)
ax4 = fig.add_subplot(3, 2, 4)
ax5 = fig.add_subplot(3, 2, 5)

sns.countplot(x = "sex", ax=ax1, palette='Set3', data=df_t, hue='class')
sns.countplot(x = "embarked", ax=ax2, palette='Set3', data=df_t)
sns.countplot(x = "who", ax=ax3, palette='Set3', data=df_t, hue='alive')
sns.countplot(x = "embark_town", ax=ax4, palette='Set3', data=df_t, hue='sex', dodge=False)
sns.countplot(x = "alive", ax=ax5, palette='Set3', data=df_t, hue='sex', dodge=True)

 

 

 

 

 

▷ 범주형 데이터(카테고리, 불리언)의 분포 확인.

fig = plt.figure(figsize=(10, 10))

ax1 = fig.add_subplot(3, 2, 1)
ax2 = fig.add_subplot(3, 2, 2)
ax3 = fig.add_subplot(3, 2, 3)
ax4 = fig.add_subplot(3, 2, 4)


sns.countplot(x = "class", ax=ax1, palette='Set3', data=df_t, hue='sex')
sns.countplot(x = "deck", ax=ax2, palette='Set3', data=df_t)
sns.countplot(x = "adult_male", ax=ax3, palette='Set1', data=df_t, hue='alive')
sns.countplot(x = "alone", ax=ax4, palette='Set3', data=df_t, hue='sex')

 

 

 

 

▷ 불필요한 변수 확인 및 제거.

  • drop()
  • pclass = class
  • alive = survived
  • embark_town = embarked
  • sex와 who, adult_male과 비슷.
  • alone과 sibsp, parch와 비슷.
df_t.drop(columns=['class', 'alive', 'embark_town', 'who', 'adult_male', 'alone'], inplace=True)

 

 

 

 

 


 

 

 

 

 

▷ 파생 변수 생성.

  • sibsp 컬럼과 parch 컬럼을 더해 famliy 컬럼 생성
df_t['family'] = df_t.sibsp + df_t.parch
  • sibsp, parch 컬럼 제거
df_t.drop(columns=['sibsp', 'parch'], inplace=True)

 

 

 

 


 

 

 

 

▷ 누락 데이터 확인.

df_t.isna().sum()

# survived      0
# pclass        0
# sex           0
# age         177
# fare          0
# embarked      2
# deck        688
# family        0
# dtype: int64

 

 

 

 

 

 


 

 

▷ 누락 데이터 처리

▶ 방법1

확률적으로 가장 많이 분포하고 있는 데이터로 채웁니다.

 

▷ embarked의 누락 데이터를 채웁니다.

  • embarked는 문자 범주형 데이터.
df1["embarked"].value_counts(dropna=False)

# S    646
# C    168
# Q     77
# Name: embarked, dtype: int64
df1.embarked.fillna('S', inplace=True)

 

 

 

 age의 누락 데이터를 채웁니다.

  • age는 연속형 숫자 데이터.

 

▷ 클래스별 age의 중간값을 찾는다.

df1.groupby('pclass').age.agg(['median'])

▷ 클래스와 성 별 age의 중간값을 찾는다.

df1.groupby(['pclass', 'sex']).age.agg(['median'])

▷ pclass와 sex별 age의 결측치를 채운다.

age_md = df1.groupby(['pclass', 'sex']).age.agg(['median'])
# 나이 중간값

df1.loc[(df1['sex'] == 'male') & (df1['pclass'] == 1) & (df1.age.isna()), "age"] = age_md.loc[1, 'male'][0]
df1.loc[(df1['sex'] == 'male') & (df1['pclass'] == 2) & (df1.age.isna()), "age"] = age_md.loc[2, 'male'][0]
df1.loc[(df1['sex'] == 'male') & (df1['pclass'] == 3) & (df1.age.isna()), "age"] = age_md.loc[3, 'male'][0]
df1.loc[(df1['sex'] == 'female') & (df1['pclass'] == 1) & (df1.age.isna()), "age"] = age_md.loc[1, 'female'][0]
df1.loc[(df1['sex'] == 'female') & (df1['pclass'] == 2) & (df1.age.isna()), "age"] = age_md.loc[2, 'female'][0]
df1.loc[(df1['sex'] == 'female') & (df1['pclass'] == 3) & (df1.age.isna()), "age"] = age_md.loc[3, 'female'][0]
sex_list = ['male', 'female']
pclass_list = [1, 2, 3]
a = []

for i in df1.groupby(['pclass', 'sex']).age.median() :#.loc[1, 'female']
    a.append(i)
    
for i in range(len(sex_list)):
    for j in range(len(pclass_list)):
        df1.loc[(df1.sex == sex_list[i]) & (df1.pclass == pclass_list[j]) & df1.age.isna(), 'age'] = a[3 * i + j]

 

▷ deck컬럼은 결측치가 많기 때문에 컬럼 자체를 삭제한다.

df1.drop(columns='deck', inplace=True)

결측치의 개수

 

 

 


 

▶ 방법2

fillna()로 간단하게 변경합니다.

▷ embarked 결측치 N으로 채우기.

df2.embarked.fillna('N', inplace=True)

 

 

▷ deck 결측치 N으로 채우기.

카테고리형은 추가할 수 없기 때문에 타입을 변경해주어야 한다.

df2.deck = df2.deck.astype('O')
df2.deck.fillna("N", inplace=True)

 

 

 

▷ age 결측치 평균값으로 채우기.

df2.age.fillna(df2['age'].mean(), inplace=True)

 

 

 

 

 

 

 


 

 

 

 

 

▶ 탐색적 데이터 분석

생존여부와 다른 컬럼들과 연관성 파악.

 

▷ survived와 가장 관계성이 높은 컬럼을 찾는다.

sns.heatmap()

plt.figure(figsize=(5, 5))
sns.heatmap(df1.corr(), annot=True, cmap="coolwarm", cbar=False)

히트맵으로 간단하게 관계성을 확인 할 수 있습니다.

 

 

▷ 연령별 컬럼을 생성하여 조건에 맞는 데이터를 입력합니다.

df1.loc[df1.age >= 50, "age_new"] = "old"
df1.loc[(df1.age < 50) & (df1.age>=10), "age_new"] = "young"
df1.loc[df1.age < 10, "age_new"] = "baby"

 

 

▷ 그래프를 생성합니다.

fig = plt.figure(figsize=(10, 10))

ax1 = fig.add_subplot(2, 2, 1)
ax2 = fig.add_subplot(2, 2, 2)

sns.countplot(x="age_new", hue="survived", data=df1, ax=ax1)
sns.violinplot(x="sex", y="age", hue="survived", data=df1, split=True, inner="quartile", ax=ax2)

 

→ 결론 : 여성과 아동의 생존률이 높았고 위급 상황에서 양보가 있었을것으로 예상. 

 

 

 


 

 

 

▶ 문자형 데이터를 숫자형 데이터로 변경

▷ Label Encoding 1.

▶ 데이터를 불러온다.

df_t = sns.load_dataset('titanic')
df_t.drop(columns=['class', 'alive', 'embark_town', 'who', 'adult_male', 'alone'], inplace=True)

 

 

 

▶ sex, embarked, deck의 변수를 숫자로 변경.

  • root.1 각 변수의 유니크 항목들을 확인한 후 임의의 숫자로 변경.
# age_new 컬럼 생성.
df_t.loc[df_t.age >= 50, "age_new"] = "old"
df_t.loc[(df_t.age < 50) & (df_t.age>=10), "age_new"] = "young"
df_t.loc[df_t.age < 10, "age_new"] = "baby"

for column in ['sex', 'embarked', 'age_new'] :
    datas = df_t[column].unique()
    for i, d in enumerate(datas) :
        df_t[column].replace(d, i, inplace=True)

 

 

 


 

 

▷ Label Encoding 2. (Scikit-learn)

▶ 데이터를 불러옵니다.

from sklearn.preprocessing import LabelEncoder

items = ['TESLA', '삼성', '카카오', '네이버', 'Apple', 'Benz', 'Benz', 'Apple']

lb = LabelEncoder()

 

 

▶ lb에 items의 유니크한 값을 오름차순하여 입력한다.

# items의 값의 유니크한 값이 오름차순으로 정렬된다.
lb.fit(items)

 

 

▶ 현재 lb의 숫자데이터 값을 확인한다.

labels = lb.transform(items)
print(labels)
[2 4 5 3 0 1 1 0]

 

 

 

▶ lb의 숫자 데이터를 확인한다.

# 숫자데이터를 문자데이터로 확인이 가능하다.
lb.inverse_transform([5])
# # array(['카카오'], dtype='<U5')

 

 

 

▶ lb의 현재 데이터를 확인한다.

for i, n in enumerate(lb.classes_) :
    print(i, n)

 

 

 

▶ Label encoding.

▷ root 1.

df1_sex_encoder = LabelEncoder()
df1_embarked_encoder = LabelEncoder()
df1_agenew_encoder = LabelEncoder()
df1_sex_encoder.fit(df_t['sex'])
df1_embarked_encoder.fit(df_t['embarked'])
df1_agenew_encoder.fit(df_t['age_new'])
df_t['sex'] = df1_sex_encoder.transform(df_t['sex'])
df_t['embarked'] = df1_embarked_encoder.transform(df_t['embarked'])
df_t['age_new'] = df1_agenew_encoder.transform(df_t['age_new'])

▷ root 2.

for i in ['sex', 'embarked', 'age_new']:
    globals()[f'df1_{i}_encoder'] = LabelEncoder()
    globals()[f'df1_{i}_encoder'].fit(df_t[i])
    df_t[i] = globals()[f'df1_{i}_encoder'].transform(df_t[i])

 

 

 

 

 

▶ Label Encoding VS One Hot Encoding

  • One Hot Encoding의 각 컬럼의 벡터는 1이기 때문에 데이터의 사이즈의 오해를 줄일 수 있다.

 

 

 

▶ df_t를 one-hot encoding으로 df_t2로 추가.

df_t2 = pd.get_dummies(df_t, prefix=['sex', 'embarked', 'deck'])

 

 

 

 

 

▶ 정규분포 그래프 시각화

 

 

 

▷ root.1

from scipy.stats import norm

fig = plt.figure(figsize=(10, 10))

ax1 = fig.add_subplot(2, 2, 1)

sns.lineplot(x='age' ,y=norm.pdf(df_t['age'], loc=df_t['age'].mean(), scale=2), data=df_t2, ax=ax1)

 

▷ root.2

df1_fs = (df_t2 - df_t2.mean())/df_t2.std()
print('mean: ')
print(df_t2.mean())
print('std : ')
print(df1_fs.std())
      
fig = plt.figure(figsize=(10, 10))
      
for no, i in enumerate(df_t2.columns):
    globals()[f'ax{no+1}'] = fig.add_subplot(3,3,no+1)
    
    sns.distplot(df1_fs[i], ax=globals()[f'ax{no+1}'])
    
plt.show()

 

 

 

▷ 컬럼의 변수가 모두 숫자형으로 변경된 상태이므로 상관관계 다시 확인.

sns.heatmap(df_t.corr(), annot=True, cmap='coolwarm', linewidths=0.2)

반응형