파이썬으로 데이터를 다룰 때 가장 큰 불편한 점을 하나 꼽자면 R의 dplyr
과 같은 통일된(?) 패키지가 없다는 것이다. 좀 복잡한 인덱싱, aggragation, pivot table 등을 구현하는 함수나 메서드가 같은 부모(?)에서 나오지 않았다는게 나한테는 큰 어려움이다. 매번 구글링하는 것도 귀찮고, 그냥 내가 정리해볼란다.
중구난방으로 정리할 수는 없으니, 아래의 머신러닝 워크플로우에 따라 정리해보곘다.
출처: https://wikidocs.net/31947
패키지 관련 (Python Packages)
대부분의 패키지는 다른 여러 패키지를 상속받아 뼈대로 쓴다. 이 때 부모 클래스의 어떤 시기(버전)을 쓰는지가 패키지마다 달라서 문제가 되곤한다. 특히 numpy
에서 이런 에러가 자주 뜨는데, 아래 두 개의 명령문으로 커버할 수 있다.
$ pip install --user numpy --upgrade # numpy를 최신 버전으로 업데이트할 때
$ pip install --user numpy==1.16.5 # 구버전의 numpy가 필요할 때
Downgrade든 upgrade든 끝나면 np.__version__
으로 numpy 버전을 확인해주자.
수집 (Data Load)
점검 및 탐색 (Exploratory Data Analysis)
- Pandas Profiling
pr = df.profile_report() pr.to_file('./pr_report.html')
- Data Type
type(data)
- Select Columns with a Prefix(Suffix) using Pandas filter
df.filter(regex = '^start', axis = 1) # prefix df.filter(regex = 'end$', axis = 1) # suffix df.loc[:, df.columns.str.startswith('start')] df.loc[:, df.columns.str.endswith('end')]
- Dataframe Sampling
df.sample(n, random_state, replace, weights = 'col_name', # 특정 열의 값을 가중치로 매겨 sampling함 axis = 1) # or 'column', 열 단위로 sampling하는 것인데 잘 안함... axis = 0 은 index 기준으로 default 값이다. df.fraction(frac, random_state, replace, weights = 'col_name', axis = 1) df['col1'].sample(n) # Series 반환
- Unique
df1.col1.unique() set(df1.col1.unique()) - set(df2.col1.unique()) # 고유값의 차집합
- Sort
# 데이터프레임 정렬 df.sort_values(by = None, axis = 0, # axis = 1 이면 열을 정렬하느건데 보통 알파벳 순으로 정렬한다 ascending = True, inplace = False, kind = 'quicksort', na_position = 'last') # 결측값을 'first'에 또는 'last'에 위치할 것인지 # 튜플 정렬 sorted(tuple, key = lambda key1: key1[0], # 튜플 원소의 몇 번째 값으로 정렬할 것인지 reverse = True) # 리스트 정렬 sorted(list, reverse = True) list.sort(reverse = True)
- Round
round(x, n) import math math.ceil(x) math.floor(x) # -inf로 향하는 내림 math.trunc(x) # 0으로 향하는 내림
- Correlation
df.corr(method = 'pearson)
전처리 및 정제 (Data Preprocessing)
- Convert column type
dat['col1'] = dat.col1.astype('str') dat.col1 = dat.col1.astype('str)
- Reshape
pd.melt(data, # id_vars 에 해당하는 열만 남기고 나머지 열은 varaible로 나머지 열의 값은 value로 melting id_vars, # 어떤 열(들)을 남길 것인지 var_name, # variable의 이름을 지정해줌 value_name) # value의 이름을 지정해줌 pd.pivot_table(data, # 긴 테이블을 옆으로 늘릴 때 주로 씀 index, # 어떤 열(들)을 남길 것인지 columns, # 어떤 열(들)을 옆으로 늘릴 것인지 value, # 어떤 열을 늘린 열들의 값으로 할 것인지 aggfunc = np.sum, np.mean, # 중복되는 값을 어떻게 처리할 것인지 margins = True, False) # 행, 열 합계 제시할지 # `melt()`는 id_vars가 column으로 들어가고, `pivot_table()`은 동일한 개념인 index가 index 그 자체가 된다. # 범주형 변수들의 도수분포표 구하는 함수 pd.crosstab(index, # 행 쪽으로 grouping variable, multi-index: [id1, id2] columns, # 열 쪽으로 grouping할 variable, multi-level: [col1, col2] rownames, # 행 이름 부여 colnames, # 열 이름 부여 margins = True, # 행 합, 열 합 추가 normalize = True) # count가 아닌 proportion을 계산 # 옆으로 뚱뚱한 테이블을 아래로 길게 쌓는 것. 대신 index 차원으로 작업함 dat.stack(level = -1, # 기본적으로 df의 row index와 col index의 level은 1개인 경우가 많다. 그래서 stack을 하게되면 multi (row) index가 됨 dropna = True) # df를 stack할 때 결측값 제거 # stack 된 df가 있으면 dat_stacked['ind1']['ind1_1'] # 아래로 기다란 테이블을 옆으로 뚱뚱하게 만드는 것 dat.unstack(level = -1, # multi (row) index가 있을 때 가장 바깥쪽이 가장 낮은 level에 해당한다. 중간은 0 level, 가장 바깥쪽은 1 level에 대응되는 것 같은데... fill_value = None) dat_unstck_df = dat_unstacked.reset_index() # unstack하면 series가 반환되는데 이를 다시 df로 바꿔준다. dat_unstck_df.rename(columns = {'level_0':'col1', 'level_1':'col2'}, inplace = True) # df로 바꿔도 여전히 row, col index는 남아있기 때문에 이름을 지정해야한다.
- Row names to columns
df.index.name = 'name_I_want' df.reset_index(inplace=True) # or df.rename_axis("name_I_want").reset_index()
- Matching
pd.concat([df1, df2], ignore_index = True, # axis=0인 경우 그냥 붙이면 각 df의 행 인덱스까지 가져온다는 문제를 방지 axis = 1, # 열 기준으로 붙임. axis = 0은 행 기준이고 default 값임 join = 'inner') # 교집합만을 붙이는 것. default는 outer다 sr1 = pd.Series(['cont1', 'cont2', 'cont3'], name = 'cont', # series가 df에 붙을 때 열 이름 지정 index = [3, 4, 5]) # 매칭시키려는 인덱스도 정의할 수 있음 pd.concat([df1, sr1], axis = 1) # 열 기준으로 시리즈를 데이터프레임에 붙임
당연히 series끼리도 붙일 수 있다. 열방향으로 concat하면 데이터프레임이, 행방향으로 하면 시리즈 객체가 반환된다.
pd.merge(df_left, df_right, how = 'inner', # outer, left, right on = None, # 공통 열이름을 기준으로 inner join함. left_on = None, right_on = None)
JSON to pandas dataframe
JSON은 JavaScript Object Notation의 약자인데, JavaScript의 객체 형식을 기반으로 구조화된 데이터를 표현하는 인코딩 기법이다. 일종의 파일 형식으로 알고 있어도 무방하다. 데이터를 저장하는 방식의 단순성, 구조성(?)이 좋아 서버와 웹 응용 프로그램 간 데이터 공유에 많이 사용된다. JSON은 여러 목록과 그에 대응하는 dictionary의 조합으로 볼 수 있다. 그래서 JSON 파일에서 데이터를 추출하고 이를 Pandas dataframe으로 저장하는 것이 쉽다.
import json from pandas import json_normalize data = ''' { "Results": [ { "id": "1", "Name": "Jay" }, { "id": "2", "Name": "Mark" }, { "id": "3", "Name": "Jack" } ], "status": ["ok"] } ''' info = json.loads(data) df1 = json_normalize(info['Results']) df2 = pd.read_json(data, orient ='index')
- Dataframe rename
df.columns = ['col1', 'col2'] df.rename(columns = {'old_col' : 'new_col'}, inplace = True) df.index = ['row1', 'row2'] df.rename(index = {'old_row' : 'new_row'}, inplace = True)
- Label encoding
from sklearn.preprocessing import LabelEncoder le = LabelEncoder() le.fit(df.col) le.classes_ # 인코딩 확인 df.col = le.transform(df.col) le.inverse_transform(df.col) # 역 인코딩 확인
- Min, max index
np.min(x) # 최솟값 np.argmin(x) # 최솟값의 인덱스 np.max(x) np.argmax(x)
- Numpy where (ifelse in R)
np.where(X > 10) # 인덱스 출력 x[np.where(x > 10)] # 인덱싱 np.where(x > 10, 1, 0) # array 반환
- Iterating over rows, columns in dataframe
for i, j in df.iterrows(): print(i, j) # 각각 row index, row value를 key-value pair 형태로 반환 for i, j in df.iteritems(): print(i, j) # 각각 feature name, feature value를 key-value pair 형태로 반환
- Appending df in a for loop
appended_data = [] for infile in glob.glob("*.xlsx"): data = pandas.read_excel(infile) # store DataFrame in list appended_data.append(data) # see pd.concat documentation for more info appended_data = pd.concat(appended_data) # write DataFrame to an excel sheet appended_data.to_excel('appended.xlsx')
- Transpose
arr.T np.transpose(arr, (2, 1, 0)) # 3차원 이상 자료에 대해서 shape(2, 3, 4) => shape(4, 3, 2)로 바꿀 수 있음 np.swapaxes(arr, 0, 1) # 2차원 자료에 대해선 0, 1, 3차원의 경우 0, 2로 하면 처음과 끝의 shape이 바뀜
- Inner product
np.dot(arr1, arr2)
모델링 및 훈련 (Modelling)
- OLS formula
from statsmodels.formula.api import ols all_cols = '+'.join(df.columns) # y가 있는지 확인 my_formula = 'y~' + all_cols # R에서는 'y~.'으로 간단하게 되는데..불편하네 ols(my_formula, data = df)
- VIF
from statsmodels.stats.outliers_influence import variance_inflation_factor model = ols(formula, data) res = model.fit() # res.summary() pd.DataFrame({'Features': column, 'VIF': variance_inflation_factor(model.exog, i)} for i, column in enumerate(model.exog_names) if column != 'Intercept')
평가 (Evaluation)
배포 (Export)
전처리한 데이터나 오래 걸려 얻은 모델을 밖으로 빼내어 저장할 수 있다. 매번 코드를 다시 처음부터 돌리는건 비효율적이니 이 방법을 습관화해야 한다.
- list to file
import pickle with open('./dat_pkl.ob', 'wb') as fp: pickle.dump(dat, fp) with open ('./dat_pkl.ob', 'rb') as fp: dat = pickle.load(fp)
- JSON export
import json with open("json1.json", "w") as json_tmp: # json1.json 이름의 파일을 쓰기 모드("w")로 열고 이걸 json_tmp로 객체화한 뒤, json.dump(data, json_tmp) # json.dump로 직렬화(?)해서 내보내려는 data를 직렬화된 데이터가 쓰여질 파일 json_tmp에 쓰기를 해줌