[빅데이터분석기사] 실기 대비 2일차 - 기출문제 2회
본 글은 개인적으로 공부하려고 작성한 내용입니다.
만일 저작권 및 다른 이유로 인해 문제가 발생할 소지가 있다면
pental@kakao.com 으로 메일 또는 해당 게시물에 댓글을 달아주시면 처리하도록 하겠습니다.
본 내용은 빅데이터분석기사-2차실기 / 장은진 교수님의 강의를 듣고 개인적으로 추가 공부가 필요한 내용에 대해서 정리한 글입니다.
작업형 1유형 문제 1번
| 주어진 Dataset에서 'CRIM' 값이 가장 큰 10개의 지역을 구하고 10개의 지역의 'CRIM' 값을 그 중 가장 작은 값으로 대체한 후, 'AGE' 컬럼 값이 80이상인 행의 'CRIM' 평균을 구하시오 (단, 출력 결과는 정수형으로 한다.) |
import pandas as pd
df = pd.read_csv("./bigdata_csvfile/boston_housing_data.csv")
df
데이터는 총 506개의 행, 14개의 열로 이루어져있다.
문제에서는 CRIM 값이 가장 큰 10개의 지역을 구하라고 했기에 sort_values 함수를 이용해서 내림차순 정렬을 진행했다. (내림차순의 경우 ascending = False 옵션을 통해 진행한다.)
df.sort_values("CRIM", ascending = False, inplace = True)
정렬 결과를 df 자체에 결과하기 위해서 inplace = True 옵션을 통해서 df에 바로 반영되도록 했다.
10개의 지역이 뽑혔기 때문에, 이 중 CRIM 값이 제일 작은것은 9번 인덱스에 있는 행일 것이다. 그에 따라 iloc[9]을 통해서 최소 CRIM을 따로 변수에 저장한다.
min_crim = df.iloc[9]['CRIM']
그리고 이 최소 CRIM 값을 가장 큰 10개의 지역에 모두 대체해야 하기 때문에 다음과 같이 iloc를 통해서 min_crim으로 대체했다. [:10, 0] 을 사용한 이유는 10개까지의 행에 대해서 0번 컬럼(CRIM)의 값을 min_crim 으로 대체한다는 것이다.
df.iloc[:10, 0] = min_crim
result = df.loc[df['AGE'] >= 80, 'CRIM'].mean()
print(int(result))
답 : 5
작업형 1유형 문제 2번
| 주어진 Dataset에서 첫번째 행 부터 순서대로 80% 까지의 데이터를 추출 후'total_bedrooms' 컬럼의 중앙값으로 해당 컬럼의 중앙값으로 행당 컬럼의 결측치를 대체하고 'total_bedrooms' 컬럼의 대치 전후의 표준편차 차이를 구하시오 |
df = pd.read_csv("./bigdata_csvfile/california_housing.csv")
df.head()
먼저 데이터를 불러오고 어떻게 생겼는지 확인한다. 일단 80%의 데이터를 추출해야하기 때문에 전체 행의 갯수에서 0.8을 통해서 자를 위치를 구했다.
len(df) * 0.8 # 16512.0
df = df.iloc[:16512, :]
df.iloc를 통해서 전체 열에 대해서 16512행까지의 결과를 df에 저장한다.
std1 = df['total_bedrooms'].std()
median = df['total_bedrooms'].median()
그리고나서 표준편차와 중앙값을 구하기 위해서 .std(), .median() 을 사용했다.
df['total_bedrooms'] = df['total_bedrooms'].fillna(median)
구한 median 값을 결측값에 대치하기 위해서 .fillna()를 통해서 결측값을 중앙값으로 모두 넣어주었다. 그리고 결측값을 처리 후 표준편차를 비교하기 위해서 동일하게 .std()를 사용해서 표준편차를 계산한다.
std2 = df['total_bedrooms'].std()
위에서 구해놓은 std1과 std2의 차를 계산한다.
print(std1 - std2)
답 : 1.975147291645726
작업형 1유형 문제 3번
| 주어진 데이터의 population 컬럼의 평균으로부터 1.5x표준편차를 벗어나는 영역을 이상치라 판단하고 이상치들의 합을 구하시오. (단, 출력 결과는 정수형태로 하고 이상치는 '평균 - 1.5x표준편차 미만' 또는 '평균 + 1.5x표준편차 초과' 로 연산한다.) |
데이터를 먼저 확인한다.
df = pd.read_csv("./bigdata_csvfile/california_housing.csv")
df.head()
std = df['population'].std()
mean = df['population'].mean()
print(std, mean) # np.float64(1132.462121765341), np.float64(1425.4767441860465)
표준편차를 계산하기 위해서 df[’population’].std()를 통해서 표준편차를 계산했고, 평균을 구하기 위해서는 .mean() 함수를 사용했다.
lower = mean - 1.5 * std
upper = mean + 1.5 * std
print(lower, upper) # -273.2164384619648 3124.169926834058
원래는 비슷한 유형으로 IQR 이상치 계산이 나오는걸로 기억하는데, 이 문제는 특이하게도 평균 - 1.5*표준편차 식으로 계산하라고 되어 있다. 따라서 lower와 upper 변수를 생성하여 이상치의 범위를 미리 계산해두었다.
cond1 = df['population'] < lower
cond2 = df['population'] > upper
계산 해둔 결과르 바탕으로 조건식을 세운다. 평균 +- 1.5 * 표준편차의 조건을 총 2개를 세웠다.
print(int(df[cond1 | cond2]['population'].sum())) # 5607295
위와 같이 `OR` 연산일 경우는 `|` 를 쓴다.
다른 방법으로 조건 계산하는 식이 있지만 나는 별로 좋아하는 방법은 아니다.
# 다른 방법 조건 계산
result = df['population'].loc[(df['population'] > upper) | (df['population'] < lower)].sum()
print(int(result)) # 5607295
답만 잘 나오면 장땡 아닌교~
답 : 5607295
작업형 2유형
|
df = pd.read_csv("./bigdata_csvfile/E-Commerce_Shipping.csv")
display(df.head())
이제 데이터를 읽어오는건 너무나 당연하게 쉬워진것 같다. 근데 구분자가 | ,- ,# 등으로 나오면 데이터 읽는 것부터 난이도 떡상~
print(df.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10999 entries, 0 to 10998
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ID 10999 non-null int64
1 Warehouse_block 10999 non-null object
2 Mode_of_Shipment 10999 non-null object
3 Customer_care_calls 10999 non-null int64
4 Customer_rating 10999 non-null int64
5 Cost_of_the_Product 10999 non-null int64
6 Prior_purchases 10999 non-null int64
7 Product_importance 10999 non-null object
8 Gender 10999 non-null object
9 Discount_offered 10999 non-null int64
10 Weight_in_gms 10999 non-null int64
11 Reached.on.Time_Y.N 10999 non-null int64
dtypes: int64(8), object(4)
memory usage: 1.0+ MB
먼저 10999개로 모두 동일 한것을 확인해보니 결측값은 없는것으로 확인된다. 단 4개의 컬럼에서 Dtype에 object로 되어 있는 것으로 보아 날리던지 One-Hot-Encoding을 하던지 고민해야하는 부분이다.
df.isnull().sum()
ID 0
Warehouse_block 0
Mode_of_Shipment 0
Customer_care_calls 0
Customer_rating 0
Cost_of_the_Product 0
Prior_purchases 0
Product_importance 0
Gender 0
Discount_offered 0
Weight_in_gms 0
Reached.on.Time_Y.N 0
dtype: int64
혹시라도 결측값이 있는지에 대해서 확인해보려고 `df.isnull().sum()`을 사용했으며 위 결과와 같이 결측값은 존재하지 않는다.
문제에서 분류라고 딱 줬는데, 이렇게 주는 경우는 드물다고 한다. 평가 지표를 바탕으로 분류인지 회귀인지 구별할 수 있다고 한다.
RMSE, MSE, F2 → 회귀 문제, ROC, ACC → 분류 문제
df['Reached.on.Time_Y.N'].value_counts()
Reached.on.Time_Y.N
1 6563
0 4436
Name: count, dtype: int64
일단 분류를 총 몇가지로 해야하는지 확인하기 위해서 `.value_counts()`를 사용하였다.
분류로 도출해야하는 종류는 0 또는 1로 딱 떨어지는 분류문제이다.
일단 위에서 확인했던 `object` 타입에 대해서는 라벨 인코딩을 진행해야한다. 그 이유는 다음과 같다.
columns = ['Warehouse_block', 'Mode_of_Shipment', 'Product_importance', 'Gender']
for col in columns :
print(df[col].value_counts())
Warehouse_block
F 3666
D 1834
A 1833
B 1833
C 1833
Name: count, dtype: int64
Mode_of_Shipment
Ship 7462
Flight 1777
Road 1760
Name: count, dtype: int64
Product_importance
low 5297
medium 4754
high 948
Name: count, dtype: int64
Gender
F 5545
M 5454
Name: count, dtype: int64
각각 단순히 2 ~ 5개 정도의 값만 반복되기 때문에 라벨인코딩을 진행하면 좋을것 같다고 판단해서 라벨인코딩을 진행한다.
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
columns = ['Warehouse_block', 'Mode_of_Shipment', 'Product_importance', 'Gender']
for col in columns :
le.fit(df[col])
df[col] = le.transform(df[col])
print(df.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10999 entries, 0 to 10998
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ID 10999 non-null int64
1 Warehouse_block 10999 non-null int64
2 Mode_of_Shipment 10999 non-null int64
3 Customer_care_calls 10999 non-null int64
4 Customer_rating 10999 non-null int64
5 Cost_of_the_Product 10999 non-null int64
6 Prior_purchases 10999 non-null int64
7 Product_importance 10999 non-null int64
8 Gender 10999 non-null int64
9 Discount_offered 10999 non-null int64
10 Weight_in_gms 10999 non-null int64
11 Reached.on.Time_Y.N 10999 non-null int64
dtypes: int64(12)
memory usage: 1.0 MB
`pd.get_dummies()`와 뭐가 다른지도 확인이 필요하다. (TODO)
일단 전처리가 완료 되었으면, 문제 지문과 같이 `train`과 `test` 데이터를 각각 10000, 999개로 나눈다.
train = df.iloc[0:10000]
test = df.iloc[10000:10999]
from sklearn.model_selection import train_test_split
X = train.drop(columns = ['ID', 'Reached.on.Time_Y.N'])
y = train['Reached.on.Time_Y.N']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 123)
또한 `train_test_split`을 통해서 `X_train`, `X_test`, `y_train`, `y_test`를 각각 나누어 주었다.
X의 경우는 독립변수로써, 우리가 구해야하는 `Reached.on.Time_Y.N`을 뺀 모든 컬럼이다. (ID는 학습에 도움이 되지 않으니 뺐다.)
y의 경우는 종속변수로써, 우리가 구해야하는 `Reached.on.Time_Y.N`만 가지고 있는다.
from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier(n_estimators = 170, max_depth = 30, random_state = 123)
rfc.fit(X_train, y_train)
pred = rfc.predict(X_test)
from sklearn.metrics import roc_auc_score, accuracy_score
roc = roc_auc_score(y_test, pred)
acc = accuracy_score(y_test, pred)
print(roc)
print(acc)
당연하게 `RandomForest`를 사용한다. (근데 오탈자 주의해야한다. 가끔씩 오탈자가 발생하는것 같다.) 이문제는 분류 문제이기 때문에 `RandomForestClassifier`을 사용했다.
또한 문제에서는 성능평가 지표로 `roc_auc_score`를 사용하라 했기에 `roc_auc_score`도 출력하고 정확도도 궁금하기 때문에 `accuracy_score`도 `import`해서 출력해봤다.
근데 지금 `n_estimators`가 170, `max_depth`가 30으로 줬을때 `roc`와 `acc`의 값는 각각 0.680, 0.681이다.
from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier(random_state = 123)
rfc.fit(X_train, y_train)
pred = rfc.predict(X_test)
from sklearn.metrics import roc_auc_score, accuracy_score
roc = roc_auc_score(y_test, pred)
acc = accuracy_score(y_test, pred)
print(roc)
print(acc)
랜덤포레스트에 `하이퍼파라미터` 튜닝을 하지 않고 그냥 돌렸을때의 `roc`와 `acc`의 값은 0.684, 0.683이다. (이러면 하이퍼파라미터 튜닝을 하지 않는게 더 좋은거 아닌가?)
여기서 마치는게 아니라 이게 테스트 데이터에 대해서도 예측을 진행하고 저장까지 해야한다.
test_data_X = test.drop(columns = ['ID', 'Reached.on.Time_Y.N'])
test_data_y = test['Reached.on.Time_Y.N']
pred2 = rfc.predict(test_data_X)
따라서 test데이터에서 위에서 진행했던 train_test_split 전 종속, 독립 변수를 나눈것처럼 test에서도 동일하게 ID와 Reach.on.Time_Y.N을 날려준다. 그리고 기존에 생성해준 모델을 이용해서 test_data_X를 예측한다. 이때 예측에 사용되는 독립변수는 ID, Reach.on.Time.Y_N을 제외한 모든 데이터이다.
result = pd.DataFrame(
{
'ID' : test['ID'],
'reached_ontime' : pred2
}
)
result.to_csv("2nd_test_type2.csv", index = False)
print(pd.read_csv('2nd_test_type2.csv').head())
ID reached_ontime
0 10001 1
1 10002 0
2 10003 0
3 10004 1
4 10005 0
결과를 저장할때, ID도 포함해서 CSV를 생성하라 했기 때문에 `test[’ID’]`를 그대로 가져와서 사용했다. 다른 문제에서도 몇번 보이던 패턴이었고, 대체적으로 `pred` 열로 생성하라 하는데 이 문제는 특이하게도 `reached_ontime`의 이름으로 열을 생성하라 했다.
또한 `result.csv` 가 아닌 `2nd_test_type2.csv`로 저장하는것도 조금 특이한 부분이라 생각된다.
즉, 문제를 제대로 확인해야 한다는 생각이 든다.
추가 공부해야하는 것
1. 이 문제를 풀면서 생각된 것은 원핫인코딩과 라벨인코딩이 뭐가 다른것인지?
2. sklearn.preprocessing.LabelEncoder과 pandas.get_dummies가 무엇이 다른지?
3. 문제를 제대로 읽고 회귀인지 분류인지 파악하기
4. RMSE, MSE, F1, ACC, ROC 를 통해서 회귀인지 분류인지 파악할수 있다.
5. 만약 그래도 모르겠다 싶으면 종속변수를 .value_counts를 통해서 1~4개 등으로 나타나는지 아니면 각각 다 다른값인지를 확인하면 회귀인지 분류인지 확인 할 수 있다.
6. 학습데이터로 예측만하고 그 데이터를 그대로 제출하지 말고 test 데이터에 대해서 예측하고 결과 저장하고 확인까지 하기