본문 바로가기

Computer Science

[Computer Science] 리팩토링과 최적화 -Refactoring, Data Optimization

리팩토링이란 소프트웨어를 보다 쉽게 이해할 수 있고, 적은 비용으로 수정할 수 있도록 겉으로 보이는 동작의 변화 없이 내부 구조를 변경하는 것.

Refactoring의 이유

  1. 코드를 정돈하여 중복된 부분을 제거
  2. 코드를 이해하면서 내부 구조를 바꾸는것으로 명확해져서 이해가 쉬워짐
  3. 코드의 이해가 쉬워 지면서 자동으로 버그를 알게 되어 Robust한 코드를 작성 하게 도와준다
  4. 코드디자인을 향상시키며,이는 곧 디자인이 나빠지는 것을 멈추게 하며, 소프트웨어를 보다 빨리 개발 할 수 있도록 도와준다. 
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Refactoring 해야하는 시기

  1. The Rule of Tree
    Don Roberts의 가이드 라인에 따르면, 처음 뭔가를 할때는 그냥 해라, 두번째 비슷하게 뭔가를 할때는 위축되더라도 어찌되었던 duplicate해라. 세번째 비슷하게 뭔가 할때는 리팩터링을 해라. 
  2. Comprehension Refactoring: Making Code Easier to Understand / Making It Easier to Add a Feature
    코드를 수정하려고 할때 그 코드나 자기 자신 혹은 누군가에 의해 쓰여졌더라도 그 코드가 어떻게 작동하는지 알아야한다. 직관적으로 이해하기 쉽게 리팩토링 되었는지 자문자답하면서 해보기.
  3. 버그를 수정해야 할 때 Refactoring을 하라
    버그 리포트를 받으면, 그것은 Refactoring이 필요하다는 신호인데, 왜냐하면 버그가 있었다는 것을 몰랐을 정도로 코드가 명확하지 않았다는 뜻이기 때문이다.
  4. 코드 검토(Code Review)를 할 때 Refactoring을 하라
    Refactoring은 다른 사람의 코드를 검토하는데 도움이 된다. 이렇게 함으로써, 코드가 어떻게 보일지 더욱 명확하게 알 수 있다.

 

 

ref.

https://ehsangazar.com/what-is-refactoring-d141abf596d1

 

What is Refactoring?

Refactoring is risky. It requires changes to the working code that can introduce subtle bugs. Refactoring, if not done properly, can set…

ehsangazar.com

https://starblood.tistory.com/entry/Refactoring-%EC%A0%95%EC%9D%98%EC%99%80-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0

 

Refactoring 정의와 필요한 이유

이번 글은 간단하게 Refactoring의 정의와 필요한 이유에 대해서 알아 볼까 한다. Refactoring의 정의 1. Refactoring(명사) - 소프트웨어를 보다 쉽게 이해할 수 있고, 적은 비용으로 수정할 수 있도록 겉

starblood.tistory.com

1. 데이터의 수집

import pandas as pd

# 타이타닉 데이터 셋을 불러온다
url = "https://biostat.app.vumc.org/wiki/pub/Main/DataSets/titanic3.xls" 
df = pd.read_excel(url)
orig_df = df
df.columns
Index(['pclass', 'survived', 'name', 'sex', 'age', 'sibsp', 'parch', 'ticket', 'fare', 'cabin', 'embarked', 'boat', 'body', 'home.dest'], dtype='object')

 

# 결측치 부분 마스크 처리 하기
mask = df.isnull().any(axis=1)
mask.head()
0 True
1 True
2 True
3 True
4 True
dtype: bool
# body 컬럼의 결측값
df[mask].body.head()

 

0 NaN
1 NaN
2 NaN
3 135.0
4 NaN
5 NaN
6 NaN
7 NaN
8 NaN
9 22.0
Name: body, dtype: float64
df.sex.value_counts(dropna=False)

 

male 843
female 466
Name: sex, dtype: int64
df.embarked.value_counts(dropna=False)
S 914
C 270
Q 123
NaN 2
Name: embarked, dtype: int64

2. 특징생성

name = df.name
name.head(3)
0 Allen, Miss. Elisabeth Walton
1 Allison, Master. Hudson Trevor
2 Allison, Miss. Helen Loraine
Name: name, dtype: object
df = df.drop(
    columns=["name",
             "ticket",
             "home.dest",
             "boat",
             "body",
             "cabin"]
    )
df.columns
Index(['pclass', 'survived', 'sex', 'age', 'sibsp', 'parch', 'fare', 'embarked'], dtype='object')
y = df.survived
X = df.drop(columns="survived")
print(X, y)
pclass sex age sibsp parch fare embarked
0 1 female 29.0000 0 0 211.3375 S
1 1 male 0.9167 1 2 151.5500 S
2 1 female 2.0000 1 2 151.5500 S
3 1 male 30.0000 1 2 151.5500 S
4 1 female 25.0000 1 2 151.5500 S
... ... ... ... ... ... ... ...
1304 3 female 14.5000 1 0 14.4542 C
1305 3 female NaN 1 0 14.4542 C
1306 3 male 26.5000 0 0 7.2250 C
1307 3 male 27.0000 0 0 7.2250 C
1308 3 male 29.0000 0 0 7.8750 S
[1309 rows x 7 columns]
0 1
1 1
2 0
3 0
4 0
..
1304 0
1305 0
1306 0
1307 0
1308 0
Name: survived, Length: 1309, dtype: int64

3. 샘플 데이터

from sklearn.model_selection import train_test_split
X_train1, X_test1, y_train1, y_test1 = train_test_split(X, y, test_size=0.3, random_state=42)
X_train1.shape, X_test1.shape, y_train1.shape, y_test1.shape
((916, 7), (393, 7), (916,), (393,))

4. 리팩터링

import numpy as np
from sklearn.experimental import enable_iterative_imputer
from sklearn.preprocessing import StandardScaler
from sklearn.impute import IterativeImputer

#특징 생성의 부분 리팩터링
def tweak_titanic(df):
  df = df.drop(
    columns=[
      "name",
      "ticket",
      "home.dest",
      "boat",
      "body",
      "cabin",
    ]
  ).pipe(pd.get_dummies, drop_first=True)
  return df

# 샘플데이터 생성 부분 리팩터링
def get_train_test_X_y(df, y_col, size=0.3, std_cols=None):
  y = df[y_col]
  X = df.drop(columns=y_col)
  X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=size, random_state=42
  )
  cols = X.columns
  num_cols = [
    "pclass",
    "age",
    "sibsp",
    "parch",
    "fare",
  ]

  fi = IterativeImputer()

  X_train.loc[:, num_cols] = fi.fit_transform(X_train[num_cols])
  X_test.loc[:, num_cols] = fi.transform(X_test[num_cols])

  if std_cols:
    std = StandardScaler()
    X_train.loc[:, std_cols] = std.fit_transform(X_train[std_cols])
    X_test.loc[:, std_cols] = std.transform(X_test[std_cols])

  return X_train, X_test, y_train, y_test
ti_df = tweak_titanic(orig_df)
std_cols = "pclass,age,sibsp,fare".split(",")

X_train, X_test, y_train, y_test = get_train_test_X_y(ti_df, "survived", std_cols=std_cols)

X_train.shape, X_test.shape, y_train.shape, y_test.shape
((916, 8), (393, 8), (916,), (393,))

 

5. 모델 만들기

from sklearn import ensemble

rf = ensemble.RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None, criterion='gini', max_depth=None, max_features='auto', max_leaf_nodes=None, max_samples=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, n_estimators=100, n_jobs=None, oob_score=False, random_state=42, verbose=0, warm_start=False)

6. 모델의 평가

rf.score(X_test, y_test)
0.7837150127226463
from sklearn import metrics
metrics.precision_score(y_test, rf.predict(X_test))

 

0.7916666666666666
for col, val in sorted(zip(X_train.columns, 
                           rf.feature_importances_), 
                       key=lambda x: x[1],
                       reverse=True)[:5]:
  print(f"{col:10}{val:10.3f}")
age 0.285
fare 0.262
sex_male 0.241
pclass 0.089
sibsp 0.050

7. 모델의 최적화

from sklearn import model_selection

rf4 = ensemble.RandomForestClassifier()
params = {
  "max_features": [0.4, "auto"],
  "n_estimators": [15, 200],
  "min_samples_leaf": [1, 0.1],
  "random_state": [42],
}

cv = model_selection.GridSearchCV(rf4, params, n_jobs=-1).fit(X_train, y_train)
print(cv.best_params_)
{'max_features': 0.4, 'min_samples_leaf': 1, 'n_estimators': 200, 'random_state': 42}
rf5 = ensemble.RandomForestClassifier(
  **{
  "max_features": "auto",
  "min_samples_leaf": 0.1,
  "n_estimators": 200,
  "random_state": 42,
  }
)

rf5.fit(X_train, y_train)
rf5.score(X_test, y_test)
0.7073791348600509

 

마무리

  • 중요한 것은 단순한 파이썬 코드작성이 아닌 특정 목적을 위한 복합적인 활용을 해야 한다는 것입니다.
  • 애플리케이션 개발을 위한 코드작성이 아닌 데이터를 잘 다루기 위한 코드개선을 생각하셔야 합니다.
  • 위의 내용은 메소드와 최적화에 집중한 코드를 담았습니다. 추가적인 학습을 원하시는 분들은 아래 사이트에서 학습을 진행하시길 바랍니다 :
  • 머신러닝을 위한 메소드
 

3장 분류 문제 둘러보기(타이타닉 데이터셋)

타이타닉 데이터셋으로 머신러닝의 전반적인 내용을 살펴봅니다.

deep-diver.github.io