데이터프레임과 시리즈

 

데이터프레임이란?(Dataframe)

  • 데이터 분석에서 가장 중요한 데이터 구조
  • 관계형 데이터베이스의 테이블 또는 엑셀 시트와 같은 형태 (2차원 구조)
  • 변수들의 집합 → 각 열을 변수라고 부름

시리즈란?(Serise)

  • 하나의 정보에 대한 데이터들의 집합
  • 데이터 프레임에서 하나의 열을 떼어낸 것.(1차원)

 

<선언 및 확인>

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import koreanize_matplotlib # 한국어를 사용하게 해줌
import seaborn as sns

import joblib
import warnings

warnings.filterwarnings(action='ignore') # 경고 메시지 무시
%config InlineBackend.figure_format='retina' # 고화질의 결과물 출력
#딕셔너리 생성
dict1 = {'Name': ['Gildong', 'Sarang', 'Jiemae', 'Yeoin'],
        'Level': ['Gold', 'Bronze', 'Silver', 'Gold'],
        'Score': [56000, 23000, 44000, 52000]}
        
df = pd.DataFrame(dict1)

# 확인
print(df.head()) #head()안에 숫자가 없으면 기본으로 5가 들어감
display(df)

 

<데이터 읽어오기>

# 데이터 읽어오기
path ='https://raw.githubusercontent.com/DA4BAM/dataset/master/Attrition_simple2.CSV'
# path = 'airquality_simple.csv'
data = pd.read_csv(path)
# 상위 5개 확인
print(data.head(5))

 

자주사용하는 메서드( 배열명.메서드() )

  • 데이터명.head(): 상위 데이터 확인
  • 데이터명.tail(): 하위 데이터 확인
  • 데이터명.shape: 데이터프레임 크기
  • 데이터명.values: 값 정보 확인(저장하면 2차원 numpy 배열이 됨)
  • 데이터명.columns: 열 정보 확인
  • 데이터명.columns.values : array 형태로 확
  • 데이터명.dtypes: 열 자료형 확인
  • 데이터명.info(): 열에 대한 상세한 정보 확인
  • 데이터명.describe(): 기초통계정보 확인(count, mean, std, min, 25%, 50%, 75%, max)
    • 데이터명.describe().T - 전치행렬로 보기 편해진다.
  • 메이터명.corr() : 상관관계를 나타냄
    • numeric_only = True을 하면 수치형만 분석해줌
    • corr().style.background_gradient() 가독성을 높여줌
  • 데이터명.isna() : 결측치 확인
  • 데이터명.isna().sum() : NaN데이터가 있는지 확인 (값이 NaN인 데이터를 합쳐서 보여줌)
  • 데이터명[데이터명.isnull().any(axis=1)] : 결측치가 있는 행만 확인
  • 데이터명.copy()깊은복사 (원본을 헤치지 않을때 사용 -> 결과가 맘에들면 다시 원복)
  • 데이터명.dropna() : NaN인 애들을 없앰 axis를 지정
  • 데이터명.fillna() : Nan인 애들을 값을 지정해서 채움
  • 시리즈명.mode() : 최빈값 확인, 시리즈의 형태로 (index 최빈값) 형태
    • 시리즈명.fillna( 시리즈명.mode()[0] ) - 다음과 같은 형태를 활용해야 최빈값 지정 가능
    • mode()와 mode()[0]  각각 출력해보길 권장
  • 시리즈명.ffill() : 변수 NaN 값을 바로 앞의 값으로 채우기
  • 리즈명.bfill() : 변수 NaN 값을 바로 뒤의 값으로 채우기
  • 데이터명.drop_duplicates() : 중복된 행을 제거해 줌
    • inplace = True,

<단순 조회>

  • 데이터명[ '열 이름' ] - 시리즈로 조회
  • 데이터명[ [ '열 이름' ] ] - 데이터프레임으로 조회

 

DF.sort_values

<정렬해서 보기 / 중요!>

  • 데이터명.sort_values(by = [ '열1'], ascending = [ True ] ) 
    메소드로 특정 열을 기준으로 정렬한다.
  • 데이터명.sort_values(by = [ '열1', '열2' ],ascending = [ True, False ])
    다양한 값과 정렬방식 가능
  • 데이터명.ascending 옵션을 설정해 오름차순, 내림차순을 설정할 수 있다.
  • 데이터명.sort_values( [ '열1'], ascending = [ False ] )  by는 생략할 수 있다. 
    • ascending=True: 오름차순 정렬(기본값)
    • ascending=False: 내림차순 정렬

reset_index

  • 데이터명. reset_index(drop = True) - Index로 설정된 열을 DF내에서 삭제할 물어본다. True = 삭제, False = 유지
  • 데이터명. reset_index(inplace = True) - 원본 객체를 변경 하는지 물어본다. True = 적용, False = 미적용

 

<기본 집계>

  • 데이터명[ ' 이름' ].unique()
    메소드로 고유값을 확인하며, 결괏값은 배열 형태가 된다.
  • 데이터명[ ' 이름' ].value_counts()
    메소드로 고유값과 그 개수를 확인하며, 결괏값은 시리즈 형태가 된다.
  • .sum() 합 / .max() 최값 / .min() 최솟값 / .mean() 평균 / .median() 열 중앙값 
  • 데이터명.describe() / count, mean, std, min, 25%, 50%, 75%, max

 

LOC(isin / between)

<조건으로 조회 .loc / 매우중요!!!>

  • df.loc[조건] 형태로 조건을 지정해 조건에 만족하는 데이터만 조회할 수 있다.
  • 우선 조건이 제대로 판단이 되는지 확인한 후 그 조건을 대 괄호 안에 넣으면 된다.

 

  • 데이터명.loc[ 데이터 [ '열 이름' ] > 10 ]  #열 값이 10보다 큰 행 조회
  • 데이터명.loc[( 데이터 [ '열 이름1' ] > 10) & ( 데이터 [ '열 이름2' ] == 4) ] 
    # 열1 값이 10보다 크고 열2의 값이 4인 데이터 조회
  • 데이터명.loc[( 데이터 [ '열 이름1' ] > 10) | ( 데이터 [ '열 이름2' ] == 4) ] 
    # 열1 값이 10보다 크거나 열2의 값이 4인 데이터 조회
  • 데이터명.loc[( 데이터 [ '열 이름1' ].isin( [ 1, 4 ] ) ] # isin( [ 데이터 값 ] )
    안에 있는 데이터 값인 데이터 조회(↑  같은 의미)
  • 데이터명.loc[(배열명[ '열 이름1' ].between( 25, 32 ), inclusive = 'both' ]
    # 값1 ~ 값2 범위 안의 데이터 조회 inclusive 생략시 기본값 'both' ( 값1 <= 데이터 <= 값2 ) 'left', 'right', neither'

 

inclusive 생략시 기본값
'both'
값1 <= 데이터 <= 값2  'left' 값1 <= 데이터 < 값2 
'right' 값1 < 데이터 <= 값2  'neither'  값1 < 데이터 < 값2 

 

 

 

<조건에 만족하는 행의 일부열 조회 / 많이쓰임!>

  • 데이터명.loc[배열명[ '열 이름' ] >= 10000, [ '열 이름1' ] ] 
    # 조건에 맞는 하나의 열 조회
  • 데이터명.loc[배열명[ '열 이름' ] >= 10000, [ '열 이름1', '열 이름2', '열 이름3 ] ]  
    # 조건에 맞는 여 열 조회
  • ex1) data.loc[data['MonthlyIncome'] >= 10000, ['Age', 'MaritalStatus', 'TotalWorkingYears']]
  • ex2) data.loc[(data["Age"].between(50, 59)) & (data["MonthlyIncome" <10000),
    ['Age', 'MonthlyIncome', 'Gender', 'DistanceFromHome']]

 

<데이터프레임 집계>

  • 데이터명[ '열 이름' ].sum()
  • 데이터명[ '열 이름1', '열 이름2' ].mean()
  • 데이터명.groupby( 데이터 ['열 이름1'], as_index = True )[ '열 이름2' ].mean()
    # 열1을 기준으로한 열2의 평균(결과값)
    • DF.reset_index()를 통해 DF 구조를 맞춰줄 수 있음  
    • DF.columns.name=None : 열이름에 대한 이름을 제거합니다.  
  • as_index= 그 목록을 index로 쓸거야? 물어보는것
  • [ '열 이름2' ].mean() = 시리즈로 조회 / [ ['열 이름2'] ].mean() = 데이터프레임으로 조회
  • 데이터명.groupby( '열 이름1', as_index = True )[ ['열 이름2', '열 이름3', '열 이름4'] ].mean() #여러 열 집계
  • 여러개의 열을 집계할 경우 [  ] 리스트로 묶어주어야 된다.
  • 데이터명.groupby( '열 이름1', as_index = True )[ ['열 이름2'] ].agg( [ 'min', 'max', 'mean'] )
  • agg()를 활용해 여러 함수로 한꺼번에 집계

 

<데이테프레임 변경>

  • 열 이름 변경

DF.rename( columns = { 현재 열이름1 : 바꿀 열이름1, 현재열이름2 : 바꿀 열이름2 }, inplace = True)

# rename() 함수로 열 이름 변경
#inplace=True 변경할 colums를 저장 / False면 바꾼상태로 조회만 적용x
path = 'https://raw.githubusercontent.com/DA4BAM/dataset/master/Attrition_simple2.CSV'
data = pd.read_csv(path)  

data.rename(columns={'DistanceFromHome' : 'Distance', 
                    'EmployeeNumber' : 'EmpNo',
                    'JobSatisfaction' : 'JobSat',
                    'MonthlyIncome' : 'M_Income',
                    'PercentSalaryHike' : 'PctSalHike',
                    'TotalWorkingYears' : 'TotWY'}, inplace=True)
                    
# 확인
data.head()

 

  • 모든 열 이름 변경

DF.columns = [ 바꿀 이름1, 바꿀 이름2 ]

# 모든 열 이름 변경 / 열 수에 맞게 변경
data.columns = ['Attr','Age','Dist','EmpNo','Gen','JobSat','Marital','M_Income', 'OT', 'PctSalHike', 'TotWY']

# 확인
data.head()

 

 

<열 추가>

데이터명[ '새로운 열 이름' ] = 가공할 데이터

  • 올해 급여 : MonthlyIncome(M_Income)
  • 급여 인상율 : PercentSalaryHike(PctSalHike)
  • 작년 급여 * ( 1 + PctSalHike / 100 ) = M_Income
  • 작년 급여 = M_Income / (( 1 + PctSalHike / 100 )
# final_amt 열 추가
data['Income_LY'] = data['M_Income'] / (1+data['PctSalHike']/100 )
data['Income_LY'] = round(data['Income_LY'])
# 확인
data.head()

 

 

DF.drop

<열 삭제>

  • 데이터명.drop( '열 이름', axis= n , inplace = T/F) 
    메소드를 사용해 열을 삭제합니다.
    • DF = DF.drop( '열 이름', axis= n ) - 이 방식은 새로운 DF를 만듦으로 inplace를 사용하면 안된다.
    • DF.drop( columns = [ ], inplace = T ) - axis를 지정 안하고 columns지정해도 됨 (axis가 1일 경우)
  • axis=0: 행을 의미 / 삭제(기본 값)
  • axis=1: 열을 의미 / 삭제
  • inplace=True 
    옵션을 지정해야 실제로 반영이 됩니다. (False : 삭제한 것 처럼 보여줘(조회!))
  • 데이터명.dropna( axis= n , inplace = T) - 데이터 값이 NaN인 데이터를 삭제하고 적용
    • axis = 0 이라면 행을 날림
    • 시리즈를 사용해 특정 열만 NaN을 없앤다면
    • DF.열이름 = DF.열이름.dropna(axis = n)으로 사용
  • 조건에 맞을 경우에 그 행만 삭제하고 싶은 경우
    • index_to_drop = df[df[ '열 이름' ] 조건 ex) > 4000 ].index
    • df = df.drop(index_to_drop)
    • 또는 df = df[df[' 열 이름 '] != 기준] 등으로 조건으로 저장
# 잘못 되었을 경우 되돌리기 위해 data를 복사합니다.
data2 = data.copy()

# 열 하나 삭제
data2.drop('Income_LY', axis=1, inplace=False) # 실제로는 삭제안됨
data2.drop('Income_LY', axis=1, inplace=True) # 실제로 삭제됨

# 확인
data2.head()

# 열 두 개 삭제
data2.drop(['JobSat2','Diff_Income'], axis=1, inplace=True)

# 확인
data2.head()

 

DF.fillna

  • 데이터명.fillna( 채울 값 inplace = T/F)
    • 시리즈를 사용해 특정 열의 NaN만 채운다면
    • DF.열이름 = DF.열이름. fillna ( 채울 값 )으로 사용
    • method='bfill' : 변수 NaN 값을 바로 뒤의 값으로 채우기
    • method='ffill' : 변수 NaN 값을 바로 앞의 값으로 채우기

DF.ffill / DF.bfill

  • 이터명.ffill() / 데이터명.bfill() 
  • 시리즈의 경우
    • DF[열이름] = DF[열이름].ffill() : 변수 NaN 값을 바로 앞의 값으로 채우기
    • DF[열이름] =DF[열이름].dfill() : 변수 NaN 값을 바로 뒤의 값으로 채우기

DF.interpolate

  • 시리즈의 경우
    • DF[열이름] = DF[열이름].interpolate(method = 'linear' ) : 선형보간법으로 채우기 (중간값)
    • DF[열이름] = DF[열이름].interpolate(method = ' nearest ' ) : 가까운 값으로 채워줌

 

pd.cut

<값 변경>

  • 데이터명[ '열 이름' ] = 0 단순변경
  • 데이터명.loc[ 데이터명[ '열 이름' ]  < 조건문, '열 이름' ] = 0
  • 데이터명[ '열 이름' ]  = np.where( 데이터명[ '열 이름' ] > 40, 1, 데이터명[ '열 이름' ] )
    # 조건이 맞으면 1, 아닐시 다시 그 데이터 입력
  • 데이터명[ '열 이름' ]  = 데이터명[ '열 이름' ] .map({ '요소1' : 1, '요소2' : 0}) / 범주형 데이터에 사용 가능

 

<숫자형 -> 범주형 변수로 변환>

  • 새로운 범주 데이터명 = pd.cut( 데이터명[ '열 이름' ] , 3, labels = ['a','b','c'])
    pd.cut(숫자형변수, 분할방식, 범주값)

 

  • 내가 원하는 구간 자르기 : bins = []
    • ex) pd.cut(data2['Age'], bins =[0, 40, 50, 100] , labels = ['young','junior','senior'])

 

pd.concat / pd.merge

<데이터프레임 결합>

  • pd.concat() - 매핑 기준 : 인덱스(행), 칼럼이름(열) / 데이터프레임 구조에 맞게 함침
    • pd.concat([ 합칠 데이터1, 데이터2, 데이터3], axis = 0/1 , join ="조인 종류") 
      join 생략시 outer join 
    • 방향 axis = 0 - 세로()로 합치기(위, 아래로 붙여라) - 칼럼 이름 기준
    • 방향 axis = 1 - 가로()로 합치기(옆으로 붙여라) - 행 인덱스 기준
    • join = ‘outer’ : 모든 행과 열 합치기 (기본값) 
    • join = ‘inner’ : 같은 행과 열만 합치기
  • pd.merge() - 매핑 기준 : 특정 칼럼(key)의 값(열 값) 기준으로 결합 / 데이터 값 기준으로 합침
    • pd.merge( DF1, DF2, how = 'inner', on = '요소(공통된 열 이름)') /
      how 생락시 inner join, on 생략시 None으로 알아서 공통을 찾음
    • 옆으로만 병합 
    • how = 'inner' : 같은 값만
    • how = 'outer' : 모두
    • how = 'left' : 왼쪽 df는 모두, 오른쪽 df는 같은 값만
    • how = 'right' : 오른쪽 df는 모두, 왼쪽 df는 같은 값만

pd.get_dummies

  • pd.get_dummies( DF, columns = [ ] , dtype = int) - 가변수화
    • ★ drop_first=True  - 가변수화한 첫 열을 없앤다. -> 다중공선성 문제를 없앤다.

 

pd.pivot

  • 데이터명.pivot(index = , columns = , values = ) / 결합은 아니지만 데이터를 재구성 해줌
  • 원하는 데이터의 열을 index, columns, values로 지정하여 새로운 DF으로 생성

 

pd.melt

  • 데이터명.melt(id_vars = '', value_vars = '' )
    • id_vars - 기준되는 열을 지정
    • value_vars  - 값과 같이 행으로 들어갈 열
    • 두 값 모두 리스트로 여러개 지정 가능

 

<시계열 데이터>

  • 행과 행에 시간의 순서(흐름)가 있고 
  • 행과 행의 시간간격이 동일한 데이터

 

<날짜 요소 뽑기> 

  • 날짜 타입으로 변환한 후 .dt.날짜요소로 추출 가능
구분 air['Date'].dt air['Date'].dt.isocalendar().
day X
month Y
year year
주차 x week
요일 weekday : 0~6 day : 1~7

 

< 시간에 따른 흐름 추출하기 : Time Lag >

  • 데이터명[ '새로운 열' ]  = 데이터명[ '열 이름' ].shift(n)
    - 시계열 데이터에서 시간의 흐름 전후로 정보를 이동시킬 때 사용
    shift()의 default 값은 1, shift(2) 2행 밀기, shift(-1) 뒷쪽으로 밀기
  • 데이터명[ '새로운 열' ]  = 데이터명[ '열 이름' ].rolling(n, min_periods = 1).mean()
    - 시간의 흐름에 따라 일정 기간 동안 평균이동하면서 구하기
    - min_periods =1 -> 데이터가 하나라도 존재하면 조회함.
  • 데이터명[ '새로운 열' ]  = 데이터명[ '열 이름' ].diff(n) - 특정 시점 데이터, 이전시점 데이터와의 차이 구하기

 

MinMaxScaling

from sklearn.preprocessing import minmax_scale

minmax_scale(데이터) 를 통해 데이터를 스케일링 할 수 있다.

 

 

 

import 선언

# scikit-learn를 별칭(alias) sk로
import sklearn as sk

#Pandas를 사용할 수 있도록 별칭(alias)을 pd로
import pandas as pd

# 그래프를 그리기 위한 선언
# 필요하면 설치 !pip install seaborn
import seaborn as sns
import matplotlib.pyplot as plt

# split을 위한 선언
from sklearn.model_selection import train_test_split

# 스케일링을 위한 선언
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)


# numpy 선언
import numpy as np

#라벨 인코딩
from sklearn.preprocessing import LabelEncoder

#원한 인코딩의 경우에는 그냥 .get_dummies()
df = pd.get_dummies(df, columns=['범주형 열'], drop_first=True)

# import 
from sklearn.metrics import * # 모든 함수 로드

# 분류 모델 평가 지표
from sklearn.metrics import accuracy_score 		# 정확도
from sklearn.metrics import precision_score 	# 정밀도
from sklearn.metrics import recall_score 		# 재현율
from sklearn.metrics import f1_score 			# f1 score
from sklearn.metrics import confusion_matrix 	# 혼동행렬
from sklearn.metrics import accuracy_score 		# 모든 지표 한번에

# 회귀 모델 평가 지표
from sklearn.metrics import r2_score			# R2 결정계수
from sklearn.metrics import mean_squared_error	# MSE
from sklearn.metrics import mean_absolute_error	# MAE

# 머신러닝 모델 정리
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier # 분류
from sklearn.tree import DecisionTreeRegressor # 회귀
from sklearn.ensemble import RandomForestClassifier # 분류
from sklearn.ensemble import RandomForestRegressor # 회귀

!pip install xgboost
from xgboost import XGBClassifier # 분류
from xgboost import XGBRegressor # 회귀

!pip install lightbgm
from lightgbm import LGBMClassifier # 분류
from lightgbm import LGBMegressor # 회귀

 

원핫 인코딩 및 라벨링

#원핫 인코딩 예시
oh_cols = ['Address1', 'Address2']
df_preset = pd.get_dummies(df_del, columns = oh_cols, drop_first = True)

#라벨링 예시
from sklearn.preprocessing import LabelEncoder

# 객체 생성
le = LabelEncoder()

# 인코딩 적용
df['범주형 열'] = le.fit_transform(df['범주형 열'])

 

x, y 분리

from sklearn.model_selection import train_test_split
x = df_preset.drop(columns = ['y컬럼명'])
y = df_preset['y컬럼명']
X_train, X_valid, y_train, y_valid = train_test_split(x, y, test_size = 0.2, random_state = 42)

 

머신러닝 예제

!pip install lightgbm
from lightgbm import LGBMClassifier
lgbm = LGBMClassifier(n_estimators=3, random_state=42)  
lgbm.fit(X_train, y_train)

from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report

y_pred = lgbm.predict(X_valid)
cm = confusion_matrix(y_valid, y_pred)
sns.heatmap(cm, annot=True)

print(classification_report(y_valid, y_pred, zero_division=1))

 

딥러닝 예제

 

이진 분류

model = Sequential()
model.add(Dense(64, activation='relu', input_shape=(X_train.shape[1],)))
model.add(Dropout(0.2))
model.add(Dense(32, activation='relu'))
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='adam', 
              loss='binary_crossentropy', 
              metrics=['accuracy']) 

es = EarlyStopping(monitor='val_loss', patience=5) 

checkpoint_path = 'best_model.keras'
mc = ModelCheckpoint(checkpoint_path, monitor='val_loss', verbose=1, save_best_only=True)

history = model.fit(X_train, y_train, epochs=30, batch_size=16,
                   validation_data = (X_valid, y_valid),
                    callbacks=[es, mc]
                    )

 

다중 분류

n = x_train.shape[1]

clear_session()

model = Sequential([Input(shape=(n, )),
                   Dense(64, activation='relu'),
                   Dropout(0.2),
                   Dense(32, activation='relu'),
                   Dropout(0.2),
                   Dense(16, activation='relu'),
                   Dropout(0.2),
                   Dense(2, activation='softmax')])

model.compile(optimizer= Adam(learning_rate = 0.001), loss='categorical_crossentropy', metrics=['accuracy'])

# EarlyStop
min_de = 0.001
pat = 5
es = EarlyStopping(monitor = 'val_loss', min_delta = min_de, patience = pat)

# MCP
cp_path = 'best_model.keras'
mcp = ModelCheckpoint(cp_path, monitor='val_loss', verbose = 1, save_best_only=True)

history = model.fit(x_train, y_train, epochs = 10, batch_size=10, validation_split=0.2, callbacks = [es, mcp]).history

history = model.fit(x_train, y_train, epochs = 10, batch_size=10, validation_split=0.2).history

 

+ Recent posts