데이터 유형별로 여러 단계를 거치는 데이터 전처리와 모델의 학습, 신규 데이터에 대한 예측의 전체 기계학습 워크 플로우를 파이프라인으로 관리하면 워크 플로우 관리를 간소화하고 자동화(workflow automation) 하는데 매우 큰 도움이 됩니다. 

 

이번 포스팅에서는 Python의 scikit learn 모듈을 사용해서 숫자형과 범주형 변수가 섞여 있는 데이터셋의 데이터 전처리 및 선형회귀모형 모델을 학습하는 전체 파이프라인을 만드는 방법을 소개하겠습니다. 

 

(1) 숫자형 변수의 결측값 처리 및 표준화하는 데이터 전처리 파이프라인 만들기

(2) 범주형 변수의 원핫인코딩하는 데이터 전처리 파이프라인 만들기

(3) ColumnTransformer 클래스로 숫자형과 범주형 변수 전처리 파이프라인 합치기

(4) 숫자형 & 범주형 데이터 전처리와 선형회귀모형 학습하는 파이프라인 만들기

(5) 모델 학습

(6) 예측 및 모델 성능 평가

 

scikit-learn pipeline with numeric and categorical features and linear regression

 

 

먼저, 데이터 전처리 및 모델학습과 파이프라인을 구성하는데 필요한 Python modules 을 불러오겠습니다.  그리고 예제로 사용할 오픈 abalone.data 데이터셋을 가지고 pandas DataFrame을 만들어보겠습니다. 

 

## Importing modules
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import OneHotEncoder
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error


## Making a DataFrame by reading abalone data set from URL
url = "http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.data"
abalone = pd.read_csv(
    url, 
    sep=",", 
    names = ['sex', 'length', 'diameter', 'height', 
             'whole_weight', 'shucked_weight', 'viscera_weight', 
             'shell_weight', 'rings'], 
    header = None)
    
    
abalone.head()
# 	sex	length	diameter	height	whole_weight	shucked_weight	viscera_weight	shell_weight	rings
# 0	M	0.455	0.365	0.095	0.5140	0.2245	0.1010	0.150	15
# 1	M	0.350	0.265	0.090	0.2255	0.0995	0.0485	0.070	7
# 2	F	0.530	0.420	0.135	0.6770	0.2565	0.1415	0.210	9
# 3	M	0.440	0.365	0.125	0.5160	0.2155	0.1140	0.155	10
# 4	I	0.330	0.255	0.080	0.2050	0.0895	0.0395	0.055	7

 

 

위의 abalone 데이터셋에서 숫자형 변수(numeric variable)인 "length", "whole_weight" 와 범주형 변수(categorical variable)인 "sex" 의 3개 칼럼을 이용해서 "rings" 를 예측하는 선형회귀모형(linear regression)을 학습시키고, 예측을 해보겠습니다. 

 

 

(1) 숫자형 변수의 결측값 처리 및 표준화하는 데이터 전처리 파이프라인 만들기

 

숫자형 변수(numeric features)에 대해서는

  (1-1) scikit learn 모듈의 SimpleImputer() 클래스를 이용해서 결측값을 "중앙값(median)"으로 대체(imputation) 

  (1-2) scikit learn 모듈의 StandardScaler() 클래스를 이용해서 표준화(standardization) 

하는 단계를 거치는 파이프라인을 만들어보겠습니다. 

 

콤마로 써주는 부분은 각 단계(step)의 alias 이름이 되겠습니다.  아래의 (5)번에서 전체 파이프라인을 시각화했을 때 콤마로 부연설명해준 alias 이름을 도식화한 파이프라인의 윗부분에서 볼 수 있습니다. 

 

## (1) for numeric features
num_features = ["length", "whole_weight"]
num_transformer = Pipeline(
    steps = [("imputer", SimpleImputer(strategy="median")), ("scaler", StandardScaler())]
)

 

 

 

(2) 범주형 변수의 원핫인코딩하는 데이터 전처리 파이프라인 만들기

 

범주형 변수(categorical features)에 대해서는 scikit learn 모듈의 OneHotEncoder() 클래스를 사용해서 각 범주별로 칼럼을 만들어서 해당 범주에 속하면 '1' (hot), 해당 범주에 속하지 않으면 '0' (cold) 으로 인코딩을 해서 기계가 인식할 수 있도록 해주는 단계를 정의하겠습니다. 

(참고로, handle_unknown = "ignore" 옵션은 모델을 학습할 때 학습 데이터셋의 범주형 변수에는 없었던 범주가 예측에 사용하는 새로운 데이터셋에서 나타날 경우 무시하라는 의미입니다.)

 

## (2) Categorical features
cat_features = ["sex"]
cat_transformer = OneHotEncoder(handle_unknown="ignore")

 

 

 

(3) ColumnTransformer 클래스로 숫자형과 범주형 변수 전처리 파이프라인 합치기

 

위의 (1) 숫자형 데이터 전처리 단계와 (2) 범주형  데이터 전처리 단계를 정의하는 클래스와 파이프라인을 scikit learn 모듈의 ColumnTransformer() 클래스를 사용해서 숫자형 변수(num_features)와 범주형 변수(cat_features) 별로 매핑하여 하나의 데이터 전처리 파이프라인으로 합쳐보겠습니다. 

 

## (3) Use ColumnTransformer by selecting column by names
preprocessor = ColumnTransformer(
    transformers = [
        ("num", num_transformer, num_features), 
        ("cat", cat_transformer, cat_features)
    ]
)

 

 

 

(4) 숫자형 & 범주형 데이터 전처리와 선형회귀모형 학습하는 파이프라인 만들기

 

이번에는 위의 (3)번에서 숫자형과 범주형 데이터 전처리를 하나로 묶은 데이터 전처리 파이프라인에 (4) 선형회귀모형을 적합하는 클래스를 추가해서 파이프라인을 만들어보겠습니다. 

 

## (4) Append regressor to preprocessing pipeline.
lin_reg = Pipeline(
    steps = [("preporcessor", preprocessor), ("regressor", LinearRegression())]
)

 

 

(5) 모델 학습

 

모델을 학습할 때 사용할 training set (0.8) 과 모델의 성능을 평가할 때 사용할 test set (0.2) 를 0.8:0.2의 비율로 무작위 추출해서 분할해보겠습니다. 

 

## Split training(0.8) and test set(0.2) randomly
X = abalone[["length", "whole_weight", "sex"]]
y = abalone["rings"]

X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size=0.2, 
                                                    random_state=1004)

 

 

sklean 의 set_config(display="diagram") 을 설정해주면 위의 (1) ~ (4) 에서 정의해준 숫자형 & 범주형 변수 데이터 전처리와 선형회귀모형 적합의 전체 기계학습 워크플로우 파이프라인을 다이어그램으로 시각화해서 볼 수 있습니다. 

 

참고로, 파이프라인의 각 단계의 박스를 커서로 클릭하면 상세 옵션 설정 내용(예: SimpleImputer 클래스의 경우 strategy='median', OneHotEncoder 클래스의 handle_unknown='ignore' 등) 을 펼쳐서 볼 수 있습니다. 그리고 "num"과 "cat" 부분을 클릭하면 숫자형 변수와 범주형 변수 이름을 확인할 수 있습니다. 

 

이제 드디어 준비가 다 되었군요.  lin_reg.fit(X_train, y_train) 을 실행하면 (1)~(4)의 전체 워크플로우가 파이프라인을 따라서 순차적으로 실행이 됩니다.  코드가 참 깔끔해졌지요! 

 

## Display a diagram of Pipelines in Jupyter Notebook
from sklearn import set_config

set_config(display="diagram")

## Fit a Linear Regression model using pipelines
lin_reg.fit(X_train, y_train)

 

 

 

(6) 예측 및 모델 성능 평가

 

이제 따로 떼어놓았던 test set (0.2) 를 가지고 MAPE (Mean Absolute Percentage Error) 지표를 사용해서 모델의 성능을 평가해보겠습니다. 

 

lin_reg.predict(X_test) 를 실행하면 앞서 정의했던 (1) ~ (4) 의 숫자형 & 문자형 변수별 데이터 전처리와 모델 예측의 전체 워크 플로우의 파이프라인이 물 흐르듯이 자동으로 알아서 진행이 됩니다.  너무나 신기하고 편리하지요?!  

(파이프라인이 있으면 lin_reg.predict(X_test) 라는 코드 단 한줄이면 되는데요, 만약 위의 (1) ~ (4) 의 과정을 수작업으로 Test set 에 대해서 데이터 전처리해주는 코드를 다시 짠다고 생각을 해보세요. -_-;)

 

## Define UDF of MAPE(Mean Absolute Percentage Error)
## or sklearn.metrics.mean_absolute_percentage_error() class, which is new in version 0.24.
## : https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_absolute_percentage_error.html#sklearn.metrics.mean_absolute_percentage_error
def MAPE(y_test, y_pred): 
    y_test, y_pred = np.array(y_test), np.array(y_pred) 
    return np.mean(np.abs((y_test - y_pred) / y_test)) * 100
    

## Evaluate performance of a model
y_pred = lin_reg.predict(X_test)

print("MSE: %.2f" % MAPE(y_test, y_pred))
#[out] MSE: 18.68

 

 

[ Reference ]

- Column Transformer with Mixed Types
https://scikit-learn.org/stable/auto_examples/compose/plot_column_transformer_mixed_types.html
- sklearn SimpleImputer
https://scikit-learn.org/stable/modules/generated/sklearn.impute.SimpleImputer.html
- sklearn StandardScaler
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html
- sklearn OneHotEncoder
: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
- sklearn Linear Regression
https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html
- sklearn Pipeline
https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html
- sklearn train_test_split
https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html
- sklearn mean_squared_error
https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_squared_error.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 Python의 numpy 2D 행렬에서 행 기준, 열기준 백분율을 구하여 히트맵을 그리는 방법을 소개하겠습니다. 

 

(1) numpy 2D array 의 행 기준 백분율을 구해서 히트맵 그리기

     (Plotting the heatmap of percentages by row in numpy 2D array)

(2) numpy 2D array 의 열 기준 백분율을 구해서 히트맵 그리기 

     (Plotting the heatmap of percentages by column in numpy 2D array)

 

 

먼저, 예제로 사용할 numpy 2D array 를 0~4 사이의 정수 난수를 생성해서 만들어보겠습니다. 

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

## creating a sample dataset of numpy 2D array
np.random.seed(1004)
mat = np.random.randint(low=0, high=5, size=(5, 5))

print(mat)
# [[2 3 3 4 1]
#  [2 0 0 4 4]
#  [0 2 4 2 3]
#  [3 0 4 3 0]
#  [0 2 4 1 4]]

 

 

(1) numpy 2D array 의 행 기준 백분율을 구해서 히트맵 그리기

     (Plotting the heatmap of percentages by row in numpy 2D array)

 

 

numpy 2D array 에서 행 기준 백분율을 구하기 위해서는 행 기준의 합(summation by row, sum(axis=1, keepdims=True)) 이 필요합니다.  

 

이때 mat.sum(axis=1, keepdims=True) 처럼 keepdims=True 옵션을 표기해줌으로써 shape(5, 1) 의 각 행별 합계를 구할 수 있습니다. 만약 keepdims 옵션을 명기하지 않을 경우 디폴트로서 keepdims=False (by default) 가 적용이 되어서 shape(5,)  의 array([13, 10, 11, 10, 11]) 의 계산 결과가 나오며, 이걸로 나누어 주면 행 기준 백분율이 아니라 element-wise 나눗셈이 되어서 전혀 엉뚱한 계산결과가 나오므로 주의가 필요합니다. 

 

## (1) plotting heatmap of numpy percentages along axis 1 in 2D array

## row sums in matrix using 'keepdims=True' option
row_sum = mat.sum(axis=1, keepdims=True)

print(row_sum)
# [[13]
#  [10]
#  [11]
#  [10]
#  [11]]


## when 'keepdims=False' (by default)
mat.sum(axis=1)
# array([13, 10, 11, 10, 11])

 

 

 

위에서 구한 shape (5, 1) 의 행 기준 합계  row_sum 행렬로 원래의 2D array 행렬 mat  을 나누어주면, numpy 2D array에서 행 기준의 백분율을 구할 수 있습니다. np.round(x, decimals=2) 함수를 사용해서 백분율 계산 결과를 소수점 2째 자리까지만 나오도록 반올림을 해주었습니다. 

 

## calculating row percentage in matrix
mat_row_pct = mat / row_sum

## Round array elements to the given number of decimals
mat_row_pct = np.round(mat_row_pct, decimals=2)

print(mat_row_pct)
# [[0.15 0.23 0.23 0.31 0.08]
#  [0.2  0.   0.   0.4  0.4 ]
#  [0.   0.18 0.36 0.18 0.27]
#  [0.3  0.   0.4  0.3  0.  ]
#  [0.   0.18 0.36 0.09 0.36]]

 

 

 

이제 matplotlib 의 matshow() 함수와 colormap 을 사용해서, 위에서 구한 행 기준 백분율에 대해 히트맵을 그려보겠습니다. colormap 으로는 순차적으로 파란색이 진해지(sequential colormaps)는 camp='Blues' 를 사용하였습니다. 그리고 해석을 편리하게 할 수 있도록 plt.colorbar() 함수를 사용해서 칼라의 강도별 백분율을 legend colorbar 를 만들어주었습니다. 아래에 히트맵을 보면 한눈에 행 기준의 백분율이 어디 셀이 높고 어디 셀이 낮은지 한눈에 금방 알아볼 수 있습니다. 

 

## plotting the heatmap of matrix row percentage (axis=1) using colormaps
## ref: https://matplotlib.org/stable/tutorials/colors/colormaps.html

plt.rcParams['figure.figsize'] = (8, 8) # figure size
plt.matshow(mat_row_pct, cmap='Blues') # sequential colormaps
plt.title('Heatmap of row percentage in matrix', fontsize=16)
plt.colorbar(label='percentages along axis 1') # colorbar legend
plt.show()

heatmap of row percentage in numpy 2D array

 

 

 

 

(2) numpy 2D array 의 열 기준 백분율을 구해서 히트맵 그리기 

     (Plotting the heatmap of percentages by column in numpy 2D array)

 

이번에는 mat.sum(axis=0, keepdims=True)  로 numpy 2D array에서 열 기준으로 백분율(percentages by column in numpy 2D array)을 구해보겠습니다. 앞에서와는 다르게 열 기준 합을 구할 때는 axis = 0 을 사용하였습니다. 

(열 기준 백분율을 구할 때는 keepdims=False 로 해도 결과는 동일합니다.)

 

## (2) plotting heatmap of numpy percentages along axis 0 in 2D array
## row sums in matrix
print(mat)
# [[2 3 3 4 1]
#  [2 0 0 4 4]
#  [0 2 4 2 3]
#  [3 0 4 3 0]
#  [0 2 4 1 4]]

col_sum = mat.sum(axis=0, keepdims=True)

print(col_sum)
# [[ 7  7 15 14 12]]

 

 

 

이제 위에서 구한 열 기준 합으로 원래의 2D array 를 나누어주어서 열 기준 백분율을 구하겠습니다. 그리고 np.round(array, decimals=2) 함수를 사용해서 백분율 행렬의 원소 값을 소수점 2째 자리까지만 나오도록 반올림 해보겠습니다. 

 

## calculating row percentage in matrix
mat_col_pct = mat / col_sum

## Round array elements to the given number of decimals
mat_col_pct = np.round(mat_col_pct, decimals=2)

print(mat_col_pct)
# [[0.29 0.43 0.2  0.29 0.08]
#  [0.29 0.   0.   0.29 0.33]
#  [0.   0.29 0.27 0.14 0.25]
#  [0.43 0.   0.27 0.21 0.  ]
#  [0.   0.29 0.27 0.07 0.33]]

 

 

 

2D array 에서 열 기준 백분율이 준비가 되었으니 matplotlib의 plt.matshow() 함수에 순차적으로 빨간색의 강도가 진해지는 colormap 으로서 cmap='Reds' 를 사용하여 히트맵을 그려보겠습니다. 

 

## plotting the heatmap of matrix column percentage (axis=0) using colormaps
## ref: https://matplotlib.org/stable/tutorials/colors/colormaps.html
plt.rcParams['figure.figsize'] = (8, 8)
plt.matshow(mat_col_pct, cmap='Reds') # sequential colormaps
plt.title('Heatmap of row percentage in matrix', fontsize=16)
plt.colorbar(label='percentages along axis 0') # colorbar legend
plt.show()

colormap of percentages by column in numpy 2D array

 

 

[Reference]

* choosing colormaps in matplotlib: https://matplotlib.org/stable/tutorials/colors/colormaps.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 Python의 pandas DataFrame을 대사응로 matplotlib 그래프를 그렸을 때, X축 범주형 항목의 순서(changing the order of x-axis ticks in python matplotlib plot)를 변경하는 방법을 소개하겠습니다. 

 

(1) pandas DataFrame의 x 축으로 사용되는 범주형 값을 loc 를 사용해 재정렬하기

(2) index 가 재정렬된 pandas.DataFrame에 대해서 matplotlib 으로 그래프 그리기

 

changing the order of x-axis ticks in matplotlib plot

 

먼저, 예제로 사용할 간단한 pandas DataFrame을 만들어 보겠습니다. 요일(weekday dates) 별 값(value) 으로 구성된 DataFrame에 대해서 groupby() 연산자를 사용해서 요일별 값의 합을 집계(aggregation by weekday) 한 DataFrame을 만들었습니다. 

 

import pandas as pd
import matplotlib.pyplot as plt

## making a sample pandas DataFrmae
df = pd.DataFrame({
    'weekday': ['Monday', 'Saturday', 'Tuesday', 'Sunday', 'Wednesday', 'Thursday', 
                'Friday', 'Saturday', 'Sunday', 'Friday', 'Tuesday'], 
    'value': [2, 3, 4, 2, 6, 7, 5, 2, 1, 6, 4]
})

print(df)
#       weekday  value
# 0      Monday      2
# 1    Saturday      3
# 2     Tuesday      4
# 3      Sunday      2
# 4   Wednesday      6
# 5    Thursday      7
# 6      Friday      5
# 7    Saturday      2
# 8      Sunday      1
# 9      Friday      6
# 10    Tuesday      4


## aggregation of value by weekday --> this will be used for visualization
df_agg = df.groupby('weekday').sum('value')
print(df_agg)
#            value
# weekday         
# Friday        11
# Monday         2
# Saturday       5
# Sunday         3
# Thursday       7
# Tuesday        8
# Wednesday      6

 

 

 

(1) pandas DataFrame의 x 축으로 사용되는 범주형 값을 loc 를 사용해 재정렬하기

 

위에서 df.groupby('weekday').sum('value') 로 집계한 df_agg DataFrame의 결과를 보면, 알파벳(alphabet) 순서대로 요일의 순서("Friday", "Monday", "Satruday", "Sunday", "Thursday", "Tuesday", "Wednesday")가 정해져서 집계가 되었습니다. 이 DataFrame에 대해 matplotlib 으로 막대그래프를 그리면 아래와 같이 요일이 알파벳 순서대로 정렬이 된 상태에서 그래프가 그려집니다. 

 

plt.figure(figsize=(12, 8))
plt.bar(df_agg.index, df_agg.value)
plt.title("Bar plot without reordering x-axis label", fontsize=20)
plt.xlabel("Weekday", fontsize=18)
plt.xticks(fontsize=16)
plt.show()

matplotlib: before changing the order of x-axis label

 

 

(2) index 가 재정렬된 pandas.DataFrame에 대해서 matplotlib 으로 그래프 그리기
       (matplotlib 그래프의 x-axis 의 ticks 순서 바꾸기)

 

요일의 순서가 우리가 일상적으로 사용하는 순서와는 다르기 때문에 눈에 잘 안들어오고 거슬립니다. 이럴 때는 pandas DataFrame의 index 순서를 먼저 바꾸어주고, 순서가 재정렬된 후의 DataFrame에 대해서 matplotlib 으로 그래프를 그려주면 됩니다.

 

아래 예제에서는 요일(weekday)을 알파벳 순서가 아니라, 우리가 일상적으로 사용하는 ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday") 의 순서로 DataFrame의  index 를 loc 를 사용해서 바꾸어 준후에, matplotlib 막대그래프(bar graph)를 그려보았습니다. 

 

matplotlib의 X 축 레이블의 크기 

## changing the order of x-axis label using loc
weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
df_agg = df_agg.loc[weekday_order]

print(df_agg)
#            value
# weekday         
# Monday         2
# Tuesday        8
# Wednesday      6
# Thursday       7
# Friday        11
# Saturday       5
# Sunday         3


## box-plot after changing the order of x-axis ticks
plt.figure(figsize=(12, 8))
plt.bar(df_agg.index, df_agg.value)
plt.title("Bar plot after reordering x-axis label", fontsize=20)
plt.xlabel("Weekday", fontsize=18)
plt.xticks(fontsize=16)
plt.show()

matplotlib: after changing the order of x-axis label

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 Amazon AWS 의 SageMaker 에서 Python 객체(예: scikit-learn 으로 학습한 로지스틱 회귀모형)를 직렬화(serialization)하여 AWS S3에 저장하는 사용자 정의함수 코드를 소개하겠습니다. 

 

아래 예제 코드에서는 Python 객체를 직렬화하는데 six 모듈의 six.moves.cPickle 메소드를 사용하였으며, protocol 매개변수는 Python 버전에 따라서 Python 3.x 는 protocol=3, Python 2.x 는 protocol=2 를 입력해주면 됩니다. 

 

boto3 모듈로 특정 계정의 AWS S3에 접근해서 put_object() 메소드로 파일을 저장하는데요, 이때 put_object() 메소드에 Bucket (S3 내 버킷 이름), Key (폴더 이름, 키), Body (직렬화한 Python 객체) 매개변수 값을 지정해주면 됩니다.  

 

serialize a Python object using six module and save it to AWS S3 using boto3 module

 

Python 객체의 직렬화(serialization), 역직렬화(de-serialization)는 https://rfriend.tistory.com/525 를 참고하세요. 

 

 

[ Python object를 직렬화해서 AWS S3에 저장하는 사용자 지정 함수 ]

 

def store_object_to_S3(bucket_nm, folder_nm, key_nm, object_nm):
    import boto3
    
    # connect to specific account
    region = boto3.Session().region_name
    session = boto3.Session(region_name=region)
    s3 = session.client('s3')
    
    # serialization using pickle
    import six
    pickle = six.moves.cPickle
    
    try:
        serialized_obj = pickle.dumps(object_nm, protocol=3) # protocol=3 for Python 3.x
    except:
        serialized_obj = pickle.dumps(object_nm, protocol=2) # protocol=3 for Python 2.x
    
    print('[STATUS] serialized')
    
    # store a serialized object to S3 bucket
    key = "{}/{}".format(folder_nm, key_nm)
    s3.put_object(Bucket=bucket_nm, 
                  Key=key, 
                  Body=serialized_obj)
    
    print('[STATUS] saved to S3')

 

 

위에서 정의한 사용자 정의함수에 bucket_nm, folder_nm, key_nm, object_nm (직렬화하고자 하는 Python 객체, 예: 학습된 모델 객체) 을 순서대로 입력해주면 됩니다. 

 

# store the serialized best model to S3 bucket
store_object_to_S3(bucket_nm='my-bucket', 
                   folder_nm='best_model', 
                   key_nm='logitreg_model',
                   object_nm=logitreg_model)

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

탐색적 데이터 분석 및 데이터 전처리를 할 때 특정 행과 열을 기준으로 데이터를 선택(selection), 인덱싱(indexing), 슬라이싱(slicing)하는 하는 처리가 필요합니다. 이때 pandas 에서 사용할 수 있는 게 loc, iloc 인데요, 비슷해서 좀 헷갈리는 면이 있습니다. 

 

이번 포스팅에서는 Python pandas 의 DataFrame에서 데이터를 선택(selection), 인덱싱(indexing), 슬라이싱(slicing)할 때 사용하는 loc vs. iloc 를 비교해보겠습니다. 

 

- loc 는 레이블 기반 (label-based) 또는 블리언 배열(boolean array) 기반으로 데이터를 선택 

- iloc 는 정수 기반의 위치 (integer-based position) 또는 블리언 배열(boolean array) 기반으로 데이터를 선택

 

아래의 순서대로 loc vs. iloc 를 간단한 예를 들어 비교하면서 소개하겠습니다. 

(1) A Value

(2) A List or Array

(3) A Slice Object

(4) Conditions or Boolean Array

(5) A Callable Function

 

 

[ Pandas DataFrame의 데이터를 선택할 때 사용하는 loc vs. iloc ]

selecting data in pandas DataFrame using loc vs. iloc

 

 

먼저, index를 수기 입력해주고, 숫자형과 범주형 변수를 모두 가지는 간단한 예제 pandas DataFrame을 만들어보겠습니다. 

 

## creating a sample pandas DataFrame
import pandas as pd

df = pd.DataFrame({
    'name': ['park', 'choi', 'lee', 'kim', 'lim', 'yang'], 
    'gender': ['M', 'F', 'F', 'M', 'F', 'F'], 
    'age': [25, 29, 25, 31, 38, 30], 
    'grade': ['S', 'B', 'C', 'A', 'D', 'S'], 
    'tot_amt': [500, 210, 110, 430, 60, 680]}, 
    index=['c100', 'c101', 'c102', 'c103', 'c104', 'c105'])


print(df)
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500
# c101  choi      F   29     B      210
# c102   lee      F   25     C      110
# c103   kim      M   31     A      430
# c104   lim      F   38     D       60
# c105  yang      F   30     S      680

 

 

아래부터는 loc vs. iloc 코드 예시인데요, 모두 데이터를 선택하는 굉장히 간단한 코드들 이므로 설명은 생략합니다. 특이사항에 대해서만 간략히 부연설명 남겨요. 

 

(1) loc vs. iloc: A Value

 

한 개의 행 레이블(a single row label)로 loc 를 사용해서 값을 가져오면 pandas Series 를 반환하는 반면에, 리스트(list, [ ]) 안에 1개 행 레이블을 넣어서 loc 를 사용하면 pandas DataFrame을 반환합니다. 이것은 iloc 에 정수 위치를 넣어서 값을 가져올 때도 동일합니다. 

 

## (1) pandas.DataFrame.loc[]
## : Access a group of rows and colmns by label(s) or a boolean array

## (1-1) using a row label --> returns a pandas Series
df.loc['c100']
# name       park
# gender        M
# age          25
# grade         S
# tot_amt     500
# Name: c100, dtype: object



## (1-2) using a list of row label --> returns a pandas DataFrame
df.loc[['c100']]
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500




## (2) pandas.DataFrame.iloc[]
## : Purely integer-location based indexing for selection by position.

## (2-1) A scalar of integer --> returns pandas Series
df.iloc[0]
# name       park
# gender        M
# age          25
# grade         S
# tot_amt     500
# Name: c100, dtype: object


## (2-2) A list of integer --> returns pandas DataFrame
df.iloc[[0]]
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500

 

 

 

(2) loc vs. iloc: A List or Array

 

df.loc[['c100', 'c105']] 처럼 loc 에 레이블 리스트를 사용하면 해당 행의 다수의 열의 값을 가져오며, df.loc['c100', 'name'] 처럼 사용하면 레이블 기반의 해당 행(row)과 열(column)의 값을 가져옵니다. 

 

## (1) pandas.DataFrame.loc[]
## : Access a group of rows and colmns by label(s) or a boolean array

## (1-2) using multiple row labels
df.loc[['c100', 'c105']]
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500
# c105  yang      F   30     S      680


## using row and column label
df.loc['c100', 'name']
# 'park'



## (2) pandas.DataFrame.iloc[]
## : Purely integer-location based indexing for selection by position.

## (2-2) A list or array of integers
df.iloc[[0, 1, 3, 5]]
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500
# c101  choi      F   29     B      210
# c103   kim      M   31     A      430
# c105  yang      F   30     S      680

 

 

 

(3) loc vs. iloc: A Slice Object

 

loc 로 레이블 기반 슬라이싱을 하면 처음과 끝 레이블 값이 포함된 채로 값을 가져옵니다. 예를 들어, 아래의 df.loc['c100':'c103'] 은 'c100'과 'c103' 행도 모두 포함된 값을 가져왔습니다. 반면에 iloc 로 정수 위치 기반으로 슬라이싱을 하면 처음 정수 위치의 행은 가져오지만 마지막의 정수 위치의 행은 가져오지 않습니다. 

 

## (1) pandas.DataFrame.loc[]
## : Access a group of rows and colmns by label(s) or a boolean array

## (1-3) Slice with labels for row and single label for column.
df.loc['c100':'c103', 'name']
# c100    park
# c101    choi
# c102     lee
# c103     kim
# Name: name, dtype: object



## (2) pandas.DataFrame.iloc[]
## : Purely integer-location based indexing for selection by position.

## (2-3) slicing using integer
df.iloc[1:4]
#       name gender  age grade  tot_amt
# c101  choi      F   29     B      210
# c102   lee      F   25     C      110
# c103   kim      M   31     A      430

 

 

 

(4) loc vs. iloc: Conditions or Boolean Array

 

loc 의 레이블 기반 데이터 선택에는 조건문과 블리언 배열을 모두 사용할 수 있습니다. iloc 의 정수 기반 데이터 선택에는 블리언 배열을 사용할 수 있습니다. 

 

## (1) pandas.DataFrame.loc[]
## : Access a group of rows and colmns by label(s) or a boolean array

## (1-4-1) Conditional that returns a boolean Series

## Boolean list with the same length as the row axis
df['tot_amt'] > 400
# c100     True
# c101    False
# c102    False
# c103     True
# c104    False
# c105     True
# Name: tot_amt, dtype: bool


## Conditional that returns a boolean Series
df.loc[df['tot_amt'] > 400]
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500
# c103   kim      M   31     A      430
# c105  yang      F   30     S      680


## Conditional that returns a boolean Series with column labels specified
df.loc[df['tot_amt'] > 400, ['name']]
#       name
# c100  park
# c103   kim
# c105  yang


## (1-4-2) A boolean array
df.loc[[True, False, False, True, False, True]]
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500
# c103   kim      M   31     A      430
# c105  yang      F   30     S      680




## (2) pandas.DataFrame.iloc[]
## : Purely integer-location based indexing for selection by position.

## (2-4) With a boolean mask the same length as the index. 
## loc and iloc are interchangeable when labels are 0-based integers.
df.iloc[[True, False, False, True, False, True]]
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500
# c103   kim      M   31     A      430
# c105  yang      F   30     S      680

 

 

 

(5) loc vs. iloc: A Callable Function

 

lambda 익명함수를 사용하여 호출 가능한 함수를 loc, iloc 에 사용해서 값을 선택할 수 있습니다. 

(df.iloc[lambda df: df['tot_amt'] > 400] 은 왜그런지 모르겠는데 에러가 나네요. -_-;)

 

## (1) pandas.DataFrame.loc[]
## : Access a group of rows and colmns by label(s) or a boolean array

## (1-5) Callable that returns a boolean Series
df.loc[lambda df: df['tot_amt'] > 400]
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500
# c103   kim      M   31     A      430
# c105  yang      F   30     S      680



## (2) pandas.DataFrame.iloc[]
## : Purely integer-location based indexing for selection by position.

## (2-7) Callable that returns a boolean Series
df.iloc[lambda x: x.index == 'c100']
#       name gender  age grade  tot_amt
# c100  park      M   25     S      500

 

 

 

 

[ Reference ]

* pandas DataFrame에서 행, 열 데이터 선택: https://rfriend.tistory.com/282 
* pandas loc: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.loc.html   
* pandas iloc: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.iloc.html   

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 Python matplotlib 모듈을 사용해서 그래프를 그렸을 때 범례를 추가(adding a legend on the Axes)하는 4가지 방법을 소개하겠습니다. 그리고 범례의 위치, 범례 글자 크기, 범례 배경색, 범례 테두리 색, 범례 그림자 등을 설정하는 방법을 소개하겠습니다. 

 

(1) 범례 추가하기 (adding a legend on the Axes)

    : legend(), legend(handles, labels), legend(handles=handles), legend(labels)

(2) 범례 스타일 설정하기
    : location, fontsize, facecolor, edgecolor, shadow

 

 

(1) 범례 추가하기 (adding a legend on the Axes)

 

matplotlib 으로 그래프를 그린 후 범례를 추가하는 방법에는 (1-1) legend(), (1-2) line.set_label(), (1-3) legend(handles, labels), (1-4) legend(handles=handles) 의 4가지 방법이 있습니다. 이중에서 그래프를 그릴 때 label 을 입력해주고, 이어서 ax.legend() 나 plt.legend() 를 실행해서 자동으로 범례를 만드는 방법이 가장 쉽습니다. 차례대로 4가지 방법을 소개하겠습니다.  결과는 모두 동일하므로 범례가 추가된 그래프는 (1-1)번에만 소개합니다. 

 

먼저, 아래의 2개 방법은 자동으로 범례를 탐지해서 범례를 생성해주는 예제입니다. 

 

(1-1) ax.plot(label='text...') --> ax.legend() 또는 plt.legend()

 

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(1)
x = np.arange(100)
y1 = np.random.normal(0, 1, 100)
y2 = np.random.normal(0, 1, 100).cumsum()

## 1. Automatic detection of elements to be shown in the legend
## 1-1. label
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

## labels: A list of labels to show next to the artists. 
ax.plot(x, y1, 'b-', label='Company 1')
ax.plot(x, y2, 'r--', label='Company 2')
ax.legend()
plt.show()

matplotlib legend

 

 

(1-2) line.set_label('text...') --> ax.legend()

 

## 1-2. line.set_label()

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

line, = ax.plot(x, y1, 'b-')
line.set_label('Company 1')

line, = ax.plot(x, y2, 'r--')
line.set_label('Company 2')

ax.legend()
plt.show()

 

 

위의 두 방법은 범례를 자동 탐지해서 생성해줬다면, 아래의 방법부터는 명시적으로 범례의 handles (선 모양과 색깔 등) 와 labels (범례 이름)를 지정해줍니다.

 

(1-3) ax.legend(handles, labels)

 

## 2. Explicitly listing the artists and labels in the legend
## to pass an iterable of legend artists followed by an iterable of legend labels respectively
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

line1, = ax.plot(x, y1, 'b-')
line2, = ax.plot(x, y2, 'r--')

ax.legend([line1, line2], ['Company 1', 'Company 2'])
plt.show()

 

 

 

(1-4) ax.legend(handles=handles)

 

## 3. Explicitly listing the artists in the legend
## the labels are taken from the artists' label properties.
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

line1, = ax.plot(x, y1, 'b-', label='Company 1')
line2, = ax.plot(x, y2, 'r--', label='Company 2')

## handles: A list of Artists (lines, patches) to be added to the legend. 
ax.legend(handles=[line1, line2])
plt.show()

 

 

 

(2) 범례 스타일 설정하기
    : location, fontsize, facecolor, edgecolor, shadow

 

범례의 위치(location) 을 지정하려면 loc 매개변수에 location strings 인 'best', 'upper right', 'upper left', 'lower left', 'lower right', 'right', 'center left', 'center right', 'lower center', 'upper center', 'center' 중에서 하나를 선택해서 입력해주면 됩니다. 기본설정값은 loc='best' 로서 컴퓨터가 알아서 가장 적절한 여백을 가지는 위치에 범례를 생성해주는데요, 나름 똑똑하게 잘 위치를 선택해서 범례를 추가해줍니다. 

 

그밖에 범례의 폰트 크기(fontsize), 범례의 배경 색깔(facecolor, 기본설정값은 'white'), 범례의 테두리선 색깔(edgecolor), 범례 상자의 그림자 여부(shadow, 기본설정값은 'False') 등도 사용자가 직접 지정해줄 수 있습니다. (굳이 범례의 스타일을 꾸미는데 시간과 노력을 많이 쏟을 이유는 없을 것 같기는 합니다만....^^;)

 

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

ax.plot(x, y1, 'b-', label='Company 1')
ax.plot(x, y2, 'r--', label='Company 2')
ax.legend(
    ## location strings
    ## : 'best', 'upper right', 'upper left', 'lower left', 'lower right'
    ##   'right', 'center left', 'center right', 'lower center', 'upper center', 'center'
    loc='center left', # default: loc='best'
    ## fontsize
    ## # 'xx-small','x-small','small','medium','x-large','xx-large'
    fontsize='large', 
    facecolor='yellow', # background color
    edgecolor='black',
    shadow=True) # shadow behind the legend if True

ax.set_title('Adding a Legend with color and shadow', fontsize=18)
plt.show()

matplotlib adding a legend with color and shadow at center left location

 

 

[ Reference ]

* matplotlib.pyplot.legend() : https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.legend.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요. :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 Python matplotlib 모듈로 그래프를 그릴 때,  

 

(1) 눈금 설정하기 : set_xticks(), set_yticks()

(2) 눈금 이름 설정하기 : set_xticklabels(), set_yticklabels()

(3) 축 이름 설정하기 : set_xlabel(), set_ylabel()

(4) 제목 설정하기 : set_title()

 

하는 방법을 소개하겠습니다. 

 

 

먼저, numpy 모듈을 사용해서 표준정규분포 X~N(0, 1) 로부터 난수 100개를 생성해서 matplotlib의 기본 설정으로 선 그래프를 그려보겠습니다. matplotlib의 Figure 객체를 만들고, fig.add_subplot(1, 1, 1) 로 하위 플롯을 하나 만든 다음에 선 그래프를 그렸습니다. 

 

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

np.random.seed(1)
x=np.arange(100)
y=np.random.normal(0, 1, 100)

## plot with default setting
ax.plot(x, y)
plt.show()

 

 

(1) 눈금 설정하기 : set_xticks(), set_yticks()

 

X축의 눈금을 [0, 50, 100] 으로 설정하고, Y축의 눈금은 [-2, 0, 2] 로 설정해보겠습니다. 

 

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

ax.plot(x, y)

## setting xticks, yticks
ax.set_xticks([0, 50, 100])
ax.set_yticks([-2, 0, 2])

plt.show()

matplotlib.pyplot : ax.set_xticks(), ax.set_yticks()

 

 

 

(2) 눈금 이름 설정하기 : set_xticklabels(), set_yticklabels()

 

X축과 Y축에 눈금이름(xticklabel, yticklabel)을 설정해줄 수도 있습니다. 이때 set_xticks(), set_yticks() 메소드로 눈금의 위치를 먼저 설정해주고, 이어서 같은 개수의 눈금 이름을 가지고 set_xticklabels(), set_yticklabels() 메소드로 눈금 이름을 설정해주면 됩니다. 

 

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

ax.plot(x, y)

## setting xticks, yticks with labels
ax.set_xticks([0, 50, 100])
ax.set_yticks([-2, 0, 2])
ax.set_xticklabels(['start', 'middel', 'end'], fontsize=12)
ax.set_yticklabels(['low', 'zero', 'high'], fontsize=12)

plt.show()

 

 

 

(3) 축 이름 설정하기 : set_xlabel(), set_ylabel()

 

set_xlabel(), set_ylabel() 메소드로 축 이름(xlabel, ylabel)을 설정해 줄 때 fontsize 매개변수를 사용해서 글자 크기를 조정할 수 있습니다. 

 

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

ax.plot(x, y)

## setting xticks, yticks with labels
ax.set_xticks([0, 50, 100])
ax.set_yticks([-2, 0, 2])
ax.set_xticklabels(['start', 'middel', 'end'], fontsize=12)
ax.set_yticklabels(['low', 'zero', 'high'], fontsize=12)

## setting xlabel, ylabel
ax.set_xlabel('Steps', fontsize=16)
ax.set_ylabel('Value', fontsize=16)

plt.show()

 

 

 

(4) 제목 설정하기 : set_title()

 

제목의 텍스트, 폰트 크기(fontsize), 위치(loc={'center', 'left', 'right'}) 를 설정해 줄 수 있습니다. 

 

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)

ax.plot(x, y)

## setting xticks, yticks with labels
ax.set_xticks([0, 50, 100])
ax.set_yticks([-2, 0, 2])
ax.set_xticklabels(['start', 'middel', 'end'], fontsize=12)
ax.set_yticklabels(['low', 'zero', 'high'], fontsize=12)

## xlabel, ylabel
ax.set_xlabel('Steps', fontsize=16)
ax.set_ylabel('Value', fontsize=16)

# title
ax.set_title('Plo with ticks and labels', 
             fontsize=20, 
             loc='left') # 'center', 'right'

plt.show()

matplotlib: setting title, xlabel, xticks, xticklabels

 

 

 

[ Reference ]

* ax.set_xticks(): https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_xticks.html

* ax.set_xticklabels(): https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_xticklabels.html

* ax.set_xlabel(): https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_xlabel.html

* ax.set_title(): https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.axes.Axes.set_title.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

여러개의 그래프를 위/아래 또는 왼쪽/오른쪽으로 붙여서 시각화를 한 후에 비교를 해보고 싶을 때가 있습니다. 그리고 측정 단위가 서로 같을 경우에는 x축, y축을 여러개의 그래프와 공유하면서 동일한 scale의 눈금으로 시각화 함으로써 그래프 간 비교를 더 쉽고 정확하게 할 수 있습니다. 

 

이번 포스팅에서는

  (1) 여러개의 하위 플롯 그리기: plt.subplots(nrows=2, ncols=2)

  (2) X축과 Y축을 공유하기
        : plt.subplots(sharex=True. sharey=True)

  (3) 여러개의 하위 플롯들 간의 간격을 조절해서 붙이기

        : (3-1) plt.subplots_adjust(wspace=0, hspace=0)

        : (3-2) plt.tight_layout(h_pad=-1, w_pad=-1)

하는 방법을 소개하겠습니다. 

 

 

 

(1) 여러개의 하위 플롯 그릭: plt.subplots(nrows=2, ncols=2)

 

먼저, plt.subplots(nrows=2, ncols=2) 로 2개 행, 2개 열의 layout 을 가지는 하위 플롯을 그려보겠습니다. 평균=0, 표준편차=1을 가지는 표준정규분포로 부터 100개의 난수를 생성해서 파란색(color='b')의 약간 투명한(alpha=0.5) 히스토그램을 그려보겠습니다.

 

for loop 을 돌면서 2 x 2 의 레이아웃에 하위 플롯이 그려질때는 왼쪽 상단이 1번, 오른쪽 상단이 2번, 왼쪽 하단이 3번, 오른쪽 하단이 4번의 순서로 하위 그래프가 그려집니다.  

 

이렇게 하위 플롯이 그려질 때 자동으로 하위 플롯 간 여백(padding)을 두고 그래프가 그려지며, X축과 Y축은 공유되지 않고 각각 축을 가지고 그려집니다.  

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

## subplots with 2 by 2
## (1) there are paddings between subplots
## (2) x and y axes are not shared
fig, axes = plt.subplots(
    nrows=2, 
    ncols=2, 
    figsize=(10, 10))

for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color='b', 
            alpha=0.5)

subplots with padding and x, y axes

 

 

(2) X축과 Y축을 공유하기
      : plt.subplots(sharex=True. sharey=True)

(3) 여러개의 하위 플롯들 간의 간격을 조절해서 붙이기

      : (3-1) plt.subplots_adjust(wspace=0, hspace=0)

 

여러개의 하위 플롯 간에 X축과 Y축을 공유하려면 plt.subplots()의 옵션 중에서 sharex=True, sharey=True를 설정해주면 됩니다. 

 

그리고 하위 플롯들 간의 간격을 없애서 그래프를 서로 붙여서 그리고 싶다면 두가지 방법이 있습니다. 먼저, plt.subplots_adjust(wspace=0, hspace=0) 에서 wspace 는 폭의 간격(width space), hspace 는 높이의 간격(height space) 을 설정하는데 사용합니다. 

 

fig, axes = plt.subplots(
    nrows=2, ncols=2, 
    sharex=True, # sharing properties among x axes
    sharey=True, # sharing properties among y axes 
    figsize=(10, 10))

for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color='b', 
            alpha=0.5)
        
## adjust the subplot layout 
plt.subplots_adjust(
    wspace=0, # the width of the padding between subplots 
    hspace=0) # the height of the padding between subplots

matplotlib subplots adjust

 

 

아래 예제에서는 plt.subplots(sharex=False, sharey=True) 로 해서 X축은 공유하지 않고 Y축만 공유하도록 했습니다. 

그리고 plt.subplots_adjust(wspace=0, hspace=0.2) 로 해서 높이의 간격(height space)에만 0.2 만큼의 간격을 부여해주었습니다. 

 

fig, axes = plt.subplots(
    nrows=2, ncols=2, 
    sharex=False, # sharing properties among x axes
    sharey=True, # sharing properties among y axes 
    figsize=(10, 10))

for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color='b', 
            alpha=0.5)
        
## adjust the subplot layout 
plt.subplots_adjust(
    wspace=0, # the width of the padding between subplots 
    hspace=0.2) # the height of the padding between subplots

 

 

 

(3) 여러개의 하위 플롯들 간의 간격을 조절해서 붙이기

      : (3-2) plt.tight_layout(h_pad=-1, w_pad=-1)

 

하위 플롯 간 간격을 조절하는 두번째 방법으로는 plt.tight_layout(h_pad, w_pad) 을 사용하는 방법입니다. plt.tight_layout(h_pad=-1, w_pad=-1)로 설정해서 위의 2번에서 했던 것처럼 4개의 하위 플롯 간에 간격이 없이 모두 붙여서 그려보겠습니다. (참고: h_pad=0, w_pad=0 으로 설정하면 하위 플롯간에 약간의 간격이 있습니다.)

 

fig, axes = plt.subplots(
    nrows=2, ncols=2, 
    sharex=True, # sharing properties among x axes
    sharey=True, # sharing properties among y axes 
    figsize=(10, 10))

for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color='b', 
            alpha=0.5)

## adjusting the padding between and around subplots
plt.tight_layout(
    h_pad=-1, # padding height between edges of adjacent subplots
    w_pad=-1) # padding width between edges of adjacent subplots

matplotlib subplots tight_layout

 

 

X축은 공유하지 않고 Y축만 공유하며, plt.tight_layout(h_pad=3, w_pad=0) 으로 설정해서 높이 간격을 벌려보겠습니다. 그리고 하위 플롯이 그려지는 순서대로 'blue', 'red', 'yellow', 'black' 의 색깔을 입혀보겠습니다. 

 

fig, axes = plt.subplots(
    nrows=2, ncols=2, 
    sharex=False, # sharing properties among x axes
    sharey=True, # sharing properties among y axes 
    figsize=(10, 10))

color = ['blue', 'red', 'yellow', 'black']
k=0
for i in range(2):
    for j in range(2):
        axes[i, j].hist(
            np.random.normal(loc=0, scale=1, size=100), 
            color=color[k], 
            alpha=0.5)
        k +=1

## adjusting the padding between and around subplots
plt.tight_layout(
    h_pad=3, # padding height between edges of adjacent subplots
    w_pad=0) # padding width between edges of adjacent subplots

 

[ Reference ]

- plt.subplots(): https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots.html
- plt.subplots_adjust(): https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots_adjust.html
- plt.tight_layout(): https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.tight_layout.html

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 Python의 pandas DataFrame 에서 여러개 칼럼의 조건을 일부(any)만 또는 전부(all) 만족하는 행 가져오기하는 방법을 소개하겠습니다. 

 

pandas DataFrame의 any(), all() 메소드를 이용하면 매우 간단하게 조건을 만족하는 행을 색인해서 가져올 수 있습니다. 

 

(1) pandas DataFrame 의 칼럼 중에서 1개 이상의 칼럼이 True 인 행 가져오기: pd.DataFrame.any(axis=1)

(2) pandas DataFrame 의 칼럼 중에서 모든 칼럼이 True 인 행 가져오기: pd.DataFrame.all(axis=1)

 

 

pandas DataFrae any(axis=1), all(axis=1)

 

 

 

먼저, 예제로 사용할 간단한 pandas DataFrame 을 만들어 보겠습니다. 4개의 칼럼을 가지고 있고, 결측값도 하나 넣어습니다. 

 

import pandas as pd
import numpy as np

## creating a sample DataFrame with 4 columns
df = pd.DataFrame({
    'x1': [0, 1, 2, 3, 4], 
    'x2': [-2, -1, 0, 1, 3], 
    'x3': [-4, -3, -2, -1, -4], 
    'x4': [np.nan, 0, 2, 3, -10]
})

print(df)
#    x1  x2  x3    x4
# 0   0  -2  -4   NaN
# 1   1  -1  -3   0.0
# 2   2   0  -2   2.0
# 3   3   1  -1   3.0
# 4   4   3  -4 -10.0

 

 

 

(1) pandas DataFrame 의 칼럼 중에서 1개 이상의 칼럼이 True 인 행 가져오기: pd.DataFrame.any(axis=1)

 

아래처럼 np.abs(df) > 2 를 하면 모든 칼럼의 모든 행에 대해서 절대값(absolute value)이 2보다 큰지 아닌지 여부에 대해 True/False 블리언 값을 반환합니다. 

 

## returns boolean for all columns and rows
np.abs(df) > 2

# 	x1	   x2	   x3	   x4
# 0	False	False	True	False
# 1	False	False	True	False
# 2	False	False	False	False
# 3	True	False	False	True
# 4	True	True	True	True

 

 

이제 칼럼 4개 중에서 절대값(absolute value)이 2보다 큰 값이 단 하나라도 존재하는 행을 가져와 보겠습니다. 이때 '칼럼들에 대해 단 하나라도 존재하면'이라는 조건 판단은 pandas.DataFrame.any(axis=1) 메소드를 사용하면 편리합니다. 

 

any(axis =1) 에서 axis=1 을 설정해주면 칼럼 축을 기준으로 조건 만족여부를 평가합니다. 기본설정값이 axis=0 이므로 반드시 명시적으로 any(axis=1) 처럼 축을 지정해주어야 합니다. 

 

결측값이 있어도 다른 칼럼에서 조건을 만족하면 해당 행을 가져옵니다. 

 

## (1) DataFrame.any(axis=0, bool_only=None, skipna=True, level=None, **kwargs)
## pd.DataFrame.any(): Return whether any element is True, potentially over an axis.

df[(np.abs(df) > 2).any(axis=1)]
#    x1	x2	x3	x4
# 0	 0	-2	-4	NaN
# 1	 1	-1	-3	0.0
# 3	 3	1	-1	3.0
# 4	 4	3	-4	-10.0

 

 

 

pandas.DataFrame.any(axis=1) 메소드를 사용하지 않고, 아래처럼 블리언 값(True=1, False=0)을 칼럼 축으로 더해서(sum(axis=1)), 그 더한 값이 0보다 큰 행을 인덱싱해서 가져오는 방법을 써도 되긴 합니다. 

 

## or equivalantly
df[(np.abs(df) > 2).sum(axis=1) > 0]

#    x1	x2	x3	x4
# 0	 0	-2	-4	NaN
# 1	 1	-1	-3	0.0
# 3	 3	1	-1	3.0
# 4	 4	3	-4	-10.0

 

 

 

 

(2) pandas DataFrame 의 칼럼 중에서 모든 칼럼이 True 인 행 가져오기: pd.DataFrame.all(axis=1)

 

이번에는 pandas.DataFrame.all(axis=1)을 이용해서 DataFrame에 있는 4개의 모든 칼럼이 조건을 만족하는 행만 가져오기를 해보겠습니다. 

 

## DataFrame.all(axis=0, bool_only=None, skipna=True, level=None, **kwargs)
## Return whether all elements are True, potentially over an axis.

df[(np.abs(df) > 2).all(axis=1)]
#    x1	  x2	 x3	  x4
# 4	 4	3	-4	-10.0

 

 

 

아래는 pandas.DataFrame.all() 메소드를 사용하지 않고, 대신에 조건 만족여부에 대한 블리언 값을 칼럼 축으로 전부 더한 후, 이 블리언 합이 칼럼 개수와 동일한 행을 가져와본 것입니다. 위의 pandas.DataFrame.all(axis=1) 보다 코드가 좀더 길고 복잡합니다. 

 

## or, equivalently
df[(np.abs(df) > 2).sum(axis=1) == df.shape[1]]

#     x1	 x2	 x3	 x4
# 4	 4	3	-4	-10.0

 

[ Reference ]

* pandas.DataFrame.any() : https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.any.html  
* pandas.DataFrame.all(): https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.all.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

프로그래밍 코드를 짜다보면 수행 절차나 방법, 사용하는 메소드에 따라서 수행 시간이 차이가 나는 경우가 종종 있습니다. 그리고 성능이 중요해서 여러가지 방법을 테스트해보면서 가장 실행시간이 짧도록 튜닝하면서 최적화하기도 합니다. 

 

이번 포스팅에서는 Python에서 코드를 실행시켰을 때 소요된 시간을 측정하는 2가지 방법을 소개하겠습니다. 

 

(1) datetime.now() 메소드 이용해서 실행 시간 측정하기

(2) %timeit 로 실행 시간 측정하기

 

 

python : measuring the execution time of code snippets

 

먼저, 예제로 사용할 샘플 데이터셋으로서, 1억개의 값을 가지는 xarr, yarr 의 두개의 배열(array)를 만들어 보겠습니다. 그리고 배열 내 각 1억개의 값 별로 True/False 의 조건값을 가지는 cond 라는 배열도 난수를 생성시켜서 만들어보겠습니다. 

 

import numpy as np

## generating sample array with 100 million values
xarr = np.arange(100000000)
yarr = np.zeros(100000000)
cond = np.where(np.random.randn(100000000)>0, True, False)


cond[:10]
# [Out] array([False,  True,  True, False, False,  True,  True,  True,  
#              True, True])

 

 

위에서 만든 1억개의 원소를 가지는 배열을 가지고 조건값으로 True/False 블리언 값 여부에 따라서 True 조건값 이면 xarr 배열 내 값을 가지고, False 조건값이면 yarr 배열 내 값을 가지는 새로운 배열을 만들어보겠습니다. 이때 (1) List Comprehension 방법과, (2) NumPy의 Vectorized Operations 방법 간 수행 시간을 측정해서 어떤 방법이 더 빠른지 성능을 비교해보겠습니다.

(물론, Vectorized Operations이 for loop 순환문을 사용하는 List Comprehension보다 훨~씬 빠릅니다! 눈으로 직접 확인해 보겠습니다. )

 

## Let's compare the elapsed time between 2 methods 
## (list comprehension vs. vectorized operations)

## (1) List Comprehension
new_arr = [(x if c else y) for (x, y, c) in zip(xarr, yarr, cond)]

## (2) Vectorized Operations in NumPy 
new_arr = np.where(cond, xarr, yarr)

 

 

 

(1) datetime.now() 메소드 이용해서 실행 시간 측정하기

 

datetime 모듈은 날짜, 시간, 시간대(time zone) 등을 다루는데 사용하는 모듈입니다 datetime.now() 메소드는 현재의 로컬 날짜와 시간을 반환합니다. 실행 시간을 측정할 코드 바로 앞에 start_time = datetime.now() 로 시작 날짜/시간을 측정해놓고, 실행할 코드가 끝난 다음 줄에 time_elapsed = datetime.now() - start_time 으로 '끝난 날짜/시간'에서 '시작 날짜/시간'을 빼주면 '코드 실행 시간'을 계산할 수 있습니다. 

 

아래 결과를 비교해보면 알 수 있는 것처럼, for loop 순환문을 사용하는 List Comprehension 방법보다 NumPy의 Vectorized Operation이 약 38배 빠른 것으로 나오네요. 

 

## (1) -- measuring the elapsed time using datetime

## (a) List Comprehension
from datetime import datetime
start_time = datetime.now() 
list_comp_for_loop = [(x if c else y) for (x, y, c) in zip(xarr, yarr, cond)]
time_elapsed = datetime.now() - start_time 

print('Time elapsed (hh:mm:ss.ms) {}'.format(time_elapsed))
# Time elapsed (hh:mm:ss.ms) 0:00:17.753036

np.array(list_comp_for_loop)[:10]
# array([0., 1., 2., 0., 0., 5., 6., 7., 8., 9.])



## (b) Vectorized Operations in NumPy 
start_time = datetime.now() 
np_where_vectorization = np.where(cond, xarr, yarr)
time_elapsed = datetime.now() - start_time 

print('Time elapsed (hh:mm:ss.ms) {}'.format(time_elapsed))
# Time elapsed (hh:mm:ss.ms) 0:00:00.462215

np_where_vectorization[:10]
# array([0., 1., 2., 0., 0., 5., 6., 7., 8., 9.])

 

 

(2) %timeit 로 실행 시간 측정하기

 

다음으로 Python timeit 모듈을 사용해서 짧은 코드의 실행 시간을 측정해보겠습니다. timeit 모듈은 터미널의 command line 과 Python IDE 에서 호출 가능한 형태의 코드 둘 다 사용이 가능합니다. 

 

아래에는 Jupyter Notebook에서 %timeit [small code snippets] 로 코드 수행 시간을 측정해본 예인데요, 여러번 수행을 해서 평균 수행 시간과 표준편차를 보여주는 특징이 있습니다. 

 

## (2) measuring the elapsed time using timeit

## (a) List Comprehension
import timeit

%timeit list_comp_for_loop = [(x if c else y) for (x, y, c) in zip(xarr, yarr, cond)]
# 17.1 s ± 238 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)


## (b) Vectorized Operations in NumPy 
%timeit np_where_vectorization = np.where(cond, xarr, yarr)
# 468 ms ± 8.75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

 

 

[Reference]

* Python datetime: https://docs.python.org/3/library/datetime.html

* Python timeit: "measuring the execution time of small code snippets"
   : https://docs.python.org/3/library/timeit.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! 

 

반응형
Posted by Rfriend

댓글을 달아 주세요