티스토리 뷰

 

 

해당 프로젝트를 통해 학습하게 될 알고리즘 종류


 

[ ARIMA 모델 ]

- 전통적인 통계 추론 알고리즘

- 파라미터를 기반으로 어떤 패턴이 미래에 나타나게 될지 실제로 예측하는 알고리즘

 

[ Prophet 모델 ]

- 페이스북에서 개발

- 세부적인 것은 분석가에게 맡기는 알고리즘

- 이론적으로 방정식에 완벽히 맡기는 것이 아니라 분석가가 진행

→ 거기에 맞춰 좀 더 정규하게 찾아주는 알고리즘

 

[ LSTM, AutoEncoder ]

- 뉴럴 네트워크, 딥러닝 알고리즘

- ARIMA, Prophet과는 다르게 특별한 파라미터 결정 X

→ 학습하고 모델링 할 때 필요 X


 

시계열 데이터 

- 시간 정보가 포함되어 있는 데이터는 어떻게 핸들링하는지

- 어떻게 알고리즘에게 시간적 패턴도 학습시키고, 미래를 예측할 수 있을지

- 시간에 걸쳐 순차적으로 기록되고 배열된 데이터

 

판다스

- Python에서 데이터 분석을 위한 라이브러리

- 사용이 쉬운 데이터 구조와 여러 도구 제공

- 다른 라이브러리와의 연동으로 유용성 증가

- SQL과 비슷하게 관계형 데이터를 처리하는 데에 사용


 

데이터 재구조화

 

[ 판다스 ]

- Series와 DataFrame을 핸들링 하는 라이브러리

→ Series는 1차원 데이터 구조, 모든 타입의 데이터 할당 가능

→ DataFrame은 2차원 데이터 구조, 관계형 데이터베이스 테이블 구조와 비슷하며 Series의 집합

 

+ axis: 행 방향(axis = 0), 열 방향(axis=1)

+ index는 행 이름, column은 열 이름

+ missing value는 NaN으로 표기 (결측값)

 

df.head() # 데이터 상단 부분 출력
df.tail() # 데이터 하단 부분 출력
df.shape() # 행과 열 개수
df.describe() # 통계치 요약

df.columns # 컬럼 이름
df.index # 인덱스 이름
df.values # 데이터프레임 내부의 array 출력

df.T # Transpose, 데이터 전치 (column과 index가 서로 바뀜)
df.sort_values(by='기준', ascending=False) # 기준값 별로 내림차순 정렬

# 데이터프레임 접근
df['컬럼이름'] # 특정 컬럼 이름인 시리즈 접근
df.loc['인덱스 이름'] # index 이름 통해 접근
df.iloc['인덱스 위치'] # index 위치 통해 접근

# 데이터프레임 삭제
df.drop(['해당열'], axis=1) # 해당 컬럼 열 삭제
df.drop(['해당행'], axis=0) # 해당 컬럼 행 삭제

# 판다스 기초 인덱싱 (데이터프레임 계층 색인 index)
# DataFrame은 2개 이상의 색인(인덱스)을 지정 가능
df.swaplevel(0, 1, axis=0) # 인덱스 레벨 0과 1 바꿈
df.swaplevel(0, 1, axis=1) # 열 인덱스 레벨 0과 1 바꿈

df.unstack('연도') # 연도 인덱스를 컬럼으로 이동
# unstack(level): 로우 인덱스를 컬럼 인덱스로 옮길 때 사용
# DataFrame 결측치 확인
df.isna() # 결측치: True/False
df.isna().sum() # 결측치 True의 개수 합 출력

# 결측치 처리 (삭제)
df.dropna(axis=0) # 결측치 갖고 있는 행 모두 삭제
df.dropna(axis=1) # 결측치 갖고 있는 열 모두 삭제

"""
단일 대치법: 비어 있는 결측치를 특정한 값으로 채우는 방법
- 평균(mean): 연속성 변수에서 결측값을 제외한 평균으로 대치
- 중앙값(median): 연속성 변수에서 결측값을 제외한 중앙값으로 대치
- 최빈값(mode): 범주형에서 결측값 발생 시, 범주별로 빈도가 가장 큰 값으로 대치
"""
df.fillna(df.mean()) # 평균
df.fillna(df.median()) # 중앙값
df.fillna(df.mode()) # 최빈값

"""
보간법: 결측치 처리 시 사잇값 만드는 방법
- 선형 보간법: 특정 데이터를 지나는 선형 함수를 사용해 사잇값 보간
- 다항 보간법: 선형 보간법 일반화, 더 높은 차수의 다항식 함수를 사용해 보간
- "이렇지 않을까?" 하고 간접적으로 생각해 값을 채우는 방법
"""
df.interpolate(method='linear') # 선형
df.interpolate(method='spline') # 아래부터 다차원
df.interpolate(method='cubic')
df.interpolate(method='quadratic')
df.interpolate(method='polynomial')

 

# 시간마다 장비 기준으로 pivot table (Variable 안에 있는 변수들을 모두 컬럼으로 추출하기 위해)
df = df.pivot_table(index=['ds', 'src_nm'],
	columns=['variable'],
    aggfunc='sum')

"""
index: pivot_table을 적용한 후 행 이름으로 나타낼 정보가 담긴 컬럼
columns: pivot_table을 적용한 후 열에 나타날 정보가 담긴 컬럼
aggfunc: pivot_table 적용 시 중복된 데이터에 대해 어떤 연산을 적용할지 선택
"""

 


 

[프로젝트1]

데이터셋 전처리하기

 

"""
프로젝트 목표
1. 파이썬 데이터분석 라이브러리인 Pandas를 이용하여 주어진 데이터 이해
2. 결측치 처리를 한 후 MME 장비별 데이터를 저장하기

프로젝트 개요
: 본 프로젝트에서 주어진 데이터는 MME 장비 성능 상태를 파악할 수 있는 통계 데이터입니다. 이러한 성능 상태 데이터가 어떻게 구성되어 있는지 직접 확인해 보고, 추후 분석을 위해 기기별로 저장시켜 봅니다.
"""

# 데이터 읽기

# 라이브러리 설치
# : 본 프로젝트를 시행하기 앞서서 라이브러리 설치 및 버전을 맞추어 줍니다.
!pip install pickle5
!pip install missingno

# 라이브러리 불러오기
# : 프로젝트에 필요한 다양한 라이브러리, 파일을 불러옵니다
import pickle5 as pickle

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.stats as stats
import seaborn as sns
import sklearn
import missingno

pd.options.display.max_rows = 80
pd.options.display.max_columns = 80
sns.set_style("whitegrid")

assert float(pd.__version__[:3])>1 # pandas 버전 확인

%matplotlib inline

# 데이터 불러오기
# 본 실습에 사용하는 value 정보는 기기 신호의 세기에 해당하며, 이 값을 시계열 분석에 활용

"""
MME 데이터 분석
먼저, Python 라이브러리인 Pandas를 활용하여 xlsx 파일 안 첫 번째 sheet를 load하여 Pandas Dataframe 형태로 저장합니다.
"""
# 데이터 읽어오기
df = pd.read_excel('파일이름.xlsx',
					sheet_name=0,
                    )
df.head() # 앞 5행 보여주기

# excel 기본 정보 출력
df.info()

# ds 컬럼이 occr_dth, dt, hh의 결합인지 확인
df[['occr_dth', 'dt', 'hh', 'ds']]

df['ds'].unique()

# datetime인 ds 컬럼의 중복을 제외하고 처음과 끝을 확인
print('처음 날짜: ', list(df['ds'].unique())[0])
print('마지막 날짜: ', list(df['ds'].unique())[-1])

# tgt_nm 컬럼 확인
df[['tgt_nm']] # 연결장비명

# tgt_nm 컬럼 유일값 확인
df['tgt_nm'].unique() # 특별한 의미 없는 컬럼인 것 확인

# status 컬럼 유일값 확인
df['status'].unique()

# status 컬럼 분포 확인
df['status'].value_counts()

# src_nm 컬럼 유일값 확인
df['src_nm'].unique() # 장비명

# variable 컬럼 유일값 확인
df['variable'].unique()

# label이 src_nm과 variable의 결합인지 확인
df[['src_nm', 'variable', 'label']] # 붙인 값으로 재표현한 것 확인 (결합)

 

[TODO]

df dataframe을 묘사(describe)하는 코드를 작성하세요

(출력되는 값들은 소수점 셋째 자리까지 나오게 반올림하세요.)

# TODO
df.describe()

 

[TODO]

df dataframe의 카테고리 변수를 묘사(describe)하는 코드를 작성하세요

(출력되는 값들은 소수점 셋째 자리까지 나오게 반올림하세요)

# TODO: categorical data 분석
df.describe(include=['0'])

 

# 데이터 전처리

"""
데이터 삭제하기
앞서 확인한 정보로 데이터를 정제해 주도록 합니다
- label은 src_nm과 variable의 결합입니다
- ds 컬럼이 occr_dth, dt, hh의 결합입니다
- tgt_nm 컬럼은 단일 값이므로 영향을 미치지 못합니다 (ALL)
"""

# 불필요한 컬럼 삭제
drop_duplicate_list = ['occr_dth', 'dt', 'hh', 'tgt_nm','timeslot','status','label'] # 불피요한 컬럼 명명
df = df.drop(drop_duplicate_list, axis=1) # 불필요한 컬럼에 해당하는 값 삭제 (열)
df.head() # 잘 정제되었는지 확인

"""
기기별, 신호 종류에 따라 데이터 정제
1. 주어진 데이터에서 기기는 총 7개입니다
-> 'MME017', 'MME018', 'MME071', 'MME072', 'MME073', 'MME074','MME091', 'MME092', 'MME093'
2. variable은 26 종류로써 다음과 같이 세부 품목으로 이루어져 있습니다
->
comb_atch_att_45005	국내가입자 attach 시도호
comb_atch_att_etc	인바운드로머 attach 시도호
ue_dtch_att	가입자에 의한 detach 시도호
net_dtch_att	장비에 의한 detach 시도호
srmo_att	데이터 발신 시도
srmt_att	데이터 착신 시도
comb_tau_att	위치 등록 시도
prd_tau_att	주기적 위치등록 시도
pag_data_att	데이터 페이징 시도
pag_hdv_att	hdvoice 페이징 시도
pag_sms_att	sms 페이징 시도
pag_video_att	영상통화 페이징 시도
ddn_att	downlink data noti. 시도
comb_atch_succ_rate_45005	국내가입자 attach 성공율
comb_atch_succ_rate_etc	인바운드로머 attach 성공율
ue_dtch_succ_rate	가입자에 의한 detach 성공율
net_dtch_succ_rate	장비에 의한 detach 성공율
srmo_succ_rate	데이터 발신 성공율
srmt_succ_rate	데이터 착신 성공율
comb_tau_succ_rate	위치 등록 성공율
prd_tau_succ_rate	주기적 위치등록 성공율
pag_data_succ_rate	데이터 페이징 성공율
pag_hdv_succ_rate	hdvoice 페이징 성공율
pag_sms_succ_rate	sms 페이징 성공율
pag_video_succ_rate	영상통화 페이징 성공율
ddn_succ_rate	downlink data noti. 성공율

- pivot_table을 이용하여 데이터에서 시간과 기기를 인덱스로 설정하고 variable 각각의 변수별로 값을 나타내도록 정제합니다
"""

# variable 컬럼의 값을 직관적으로 알기 위해서 다음과 같은 딕셔너리 형태로 저장해줍니다
feature_dict = {
"comb_atch_att_45005":"국내가입자 attach 시도호",
"comb_atch_att_etc":"인바운드로머 attach 시도호",
"ue_dtch_att":"가입자에 의한 detach 시도호",
"net_dtch_att":"장비에 의한 detach 시도호",
"srmo_att":"데이터 발신 시도",
"srmt_att":"데이터 착신 시도",
"comb_tau_att":"위치 등록 시도",
"prd_tau_att":"주기적 위치등록 시도",
"pag_data_att":"데이터 페이징 시도",
"pag_hdv_att":"hdvoice 페이징 시도",
"pag_sms_att":"sms 페이징 시도",
"pag_video_att":"영상통화 페이징 시도",
"ddn_att":"downlink data noti. 시도",
"comb_atch_succ_rate_45005":"국내가입자 attach 성공율",
"comb_atch_succ_rate_etc":"인바운드로머 attach 성공율",
"ue_dtch_succ_rate":"가입자에 의한 detach 성공율",
"net_dtch_succ_rate":"장비에 의한 detach 성공율",
"srmo_succ_rate":"데이터 발신 성공율",
"srmt_succ_rate":"데이터 착신 성공율",
"comb_tau_succ_rate":"위치 등록 성공율",
"prd_tau_succ_rate":"주기적 위치등록 성공율",
"pag_data_succ_rate":"데이터 페이징 성공율",
"pag_hdv_succ_rate":"hdvoice 페이징 성공율",
"pag_sms_succ_rate":"sms 페이징 성공율",
"pag_video_succ_rate":"영상통화 페이징 성공율",
"ddn_succ_rate":"downlink data noti. 성공율",
}

len(feature_dict.keys())

att_features = list(feature_dict.keys())[:13] # 시도호 컬럼
rate_features = list(feature_dict.keys())[13:] # 성공률 컬럼

# 시도호 컬럼에 속하면 TYPE 시도호, 성공률 컬럼에 속하면 TYPE 성공률
for key in feature_dict:
    print('| TYPE:{:<6s} | KEY:{:<26s}| VALUE:{:<40s}' .format(
        '성공률' if key in rate_features else '시도호', key, feature_dict[key]))
        
pd.__version__ # magin method

# 기기 이름 확인 및 저장
devices = df['src_nm'].unique()

# pivot_table을 이용하여 데이터의 시간과 기기를 인덱스로 설정하고 variable 각각의 변수별로 값을 나타내도록 정제
# 시간과 기기를 인덱스로 설정하고 variable 각각의 변수별로 값을 나타냅니다
df = df.pivot_tabel(index=['ds', 'src_nm'], columns=['variable'], aggfunc='sum')
df = df.loc[(slice(None), slice(None)), 'value'] # value level 없애기
df

# 데이터 결측치 확인
# 다음 코드 실행을 통해 데이터의 결측치가 있는지 확인해 봅니다.
df.isna()

df.isna().sum() # 몇 개가 비어 있는지

"""
missingno 패키지를 활용하여 결측치를 시각화해 봅시다
- 여기서 하얀색은 결측치를 의미하며, 하얀색의 비중이 많을수록 해당 데이터에 결측치가 많다고 생각할 수 있습니다
"""

missingno.matrix(df)
df.isna().sum()

# 기기별로 데이터 결측치 확인
for device in devices:
	"""
    기기별로 for문을 돌고 df에서 기기를 선택하여 missingno 패키지를 사용하여 시각화
    """
    missingno.matrix(df.loc[(slice(None), device), :], # 시각화 할 데이터,
    figsize=(14, 4) # 시각화 템플릿 크기 설정
    )
    plt.title(f' Missing value in "{device}"', size=20) # 그래프 이름 설정
    plt.show()
    print('='*120) # 구분선

 

 

MultiIndex DataFrame 선택

 

특정 조건을 만족하는 데이터를 선택하는 방법입니다

MultiIndex를 가진 DataFrame에서 특정 수준의 인덱스를 기준으로 데이터를 선택하는 것은

단일 인덱스를 가진 DataFrame보다 조금 더 복잡할 수 있습니다

위 코드에서는 'loc' 인덱스를 사용하여 MultiIndex에서 데이터를 선택합니다

 

slice(None)

- 파이썬의 슬라이싱 문법을 사용하는 방법 중 하나로 ':'와 같은 의미입니다

- 여기서는 첫 번째 수준의 인덱스를 모두 선택합니다

 

device

- 'device'는 두 번째 수준의 인덱스에서 선택할 특정 값을 나타냅니다

 

:

- 모든 컬럼을 선택한다는 의미입니다

 

"""
기기별로 데이터에 존재하는 결측치 보간

일반적으로 데이터에 결측치가 존재할 경우 다음과 같은 세 가지 방법 중 하나를 사용합니다

1. 제거하기: 결측치 값의 비율이 적을 때 사용
- dropna()
2. 다른 값으로 대체하기: 가장 쉬운 방법으로 보통 평균으로 대체
- fillna()
3. interpolation 방법을 사용하기
- interpolation(method, limit_direction, axis)
-> method=linear (보간법 함수가 linear)
-> limit_direction='backward' # 뒤에서 채우기
-> limit_direction='forward' # 앞에서부터 채우기
-> limit_direction='both' # 양 옆에서 채우기
"""

# 1. 함수 설정
def interpolate_df(df):
	"""
    	Summary: pandas interpolation을 사용하여 열방향으로 결측치 보간
    """
    return df.interpolate(method='linear', limit_direction='both', axis=0)
    
# 2. 전체 결과 저장할 딕셔너리 만들기
total_df = {}

# 3. 기기에서 하나씩 불러서 결측치 채운 값 딕셔너리에 넣어주기
for device in devices:
	"""
    1. 데이터프레임(df)에서 기기를 선택
    2. 기기 존재하는 컬럼 삭제 droplevel(axis=0, level=1)
    - level: 제거할 단계
    - axis: 특정 레벨을 행/열 방향으로 제거할 옵션
    3. 보간 사용
    """
# total_df[device] = interpolate_df(
#	df.loc[(slice(None), device), :]).droplevel(axis=0, level=1)
	tmp = interpolate_df(df.loc[(slice(None), device), :]) 
    total_df[device] = pd.DataFrame(data=tmp.values, index=tmp.index.droplevel(1), columns=tmp.columns)
    # 딕셔너리로 전체 기기별 데이터프레임 저장
    
    total_df
    # 각 키(기기명)별로 데이터프레임 저장된 것 확인
    
    # 데이터 보간 확인
    # 앞서 결측치가 잘 보간되었는지 확인합니다
    # 기기별 결측치 확인
    for device in devices:
    	"""
        기기별로 for문을 돌고 df에서 기기를 선택하여 missingno 패키지를 사용하여 시각화
        """
        missingno.matrix(total_df[device], # 시각화 할 데이터
        				figsize=(14, 4) # 시각화 템플릿 크기 설정
                        )
		plt.title(f' Missing value in "{device"', size=20) # 그래프 이름 설정
        plt.show()
        print('='*120) # 구분선

 

[TODO]

전체 데이터에서 MME017 데이터에 대한 그래프 그리기

- 전체 데이터는 total_df로 dictionary 형태입니다

- 해당 기기 이름은 MME017입니다

- .plot()는 판다스의 그래프를 그려주는 내장 함수입니다

 

"""
데이터 시각화
결측치를 보간한 데이터를 시각화 해 보도록 합니다
"""

# 단순 그래프 그려보기
total_df['MME017'].plot(figsize=(12, 10), title='MME017', fontsize=10)
# 변수명 설명 = legend
plt.legend(loc='center left', bbox_to_anchor=(1.0, 0.6), fontsize=12)
plt.show()

def pplot(df, title='MME017'):
	"""
    Summary: 데이터를 시각화 해주는 함수입니다
    Args:
    	df는 데이터프레임
        title은 plot title
    """
    df.plot(figsize=(12, 10), # 전체 그래프 크기 조절
    		title=title # 전체 그래프 타이틀
            fontsize=12 # 전체 그래프 폰트 크기
            )
    plt.legend(loc='center left', # legend 위치 우측 센터
    		   bbox_to_anchor=(1.0, 0.5), # legend 위치 미세 조정
               fontsize=13 # legend 폰트 조절
               )
    plt.show()
    
for device in devices:
    """
    기기별로 pplot 함수를 사용하여 시각화
    """
    pplot(total_df[device], str(device))
    
"" 
전처리 끝난 데이터 저장하기
추후 데이터를 사용하기 위하여 전처리가 끝난 데이터를 pickle 패키지를 사용하여 저장하도록 합니다
"""
# save
# 통신 열어주고, wb 읽고쓰기 모드 선택, 저장할 이름 선택
with open('MME_data.p', 'wb') as fp:
	pickle.dump(total_df, fp, protocol=pickle.HIGHEST_PROTOCOL) # 저장