AI/광인사플젝

[머신러닝]3.치매예측 인공지능 - EDA

살랑춤춰요 2024. 8. 1. 12:18

2.인공지능 치매예측 부분엔 전체적인 코드를 작성했습니다.

해당 페이지부턴 자세한 접근방법과 코드설명을 첨부하겠습니다.

 

#1. 데이터프레임 만들기

from scipy.signal import butter,filtfilt
import csv, os         ; from glob import glob
import matplotlib.pyplot as plt
import numpy as np     ; import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from tqdm.notebook import tqdm  ; import seaborn as sns
from skimage.restoration import denoise_wavelet
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler, \
QuantileTransformer, PowerTransformer, Normalizer

import warnings
warnings.filterwarnings('ignore')
plt.rcParams["font.family"] = 'NanumGothic'
plt.rcParams['axes.unicode_minus'] = False
 
train_test_set = sorted(glob('GNB/100_new/*.csv'))
validation_set = sorted(glob('GNB/35_new/*.csv'))
 
df_98_ = pd.DataFrame()
category = 0
for i, file in enumerate(train_test_set) :

    user_df = pd.read_csv(file)
    user_df = user_df[user_df.columns[:22]][:]

    lst_730   = user_df.columns[1:8].tolist()
    lst_850   = user_df.columns[8:15].tolist()
    lst_back  = user_df.columns[15:22].tolist()

    for idx in range(7) :

#         user_df[lst_730[idx]] = user_df[lst_730[idx]] - np.mean(user_df[lst_730[idx]]) #  DC OFFSET 제거
#         user_df[lst_850[idx]] = user_df[lst_850[idx]] - np.mean(user_df[lst_850[idx]]) #  DC OFFSET 제거

        # 98명 데이터에 730,850 각각 +1300, 35명 데이터 각각 200씩 더해주면 정확도 올라감
        user_df[lst_730[idx]] = user_df[lst_730[idx]]  # + user_df[lst_back[idx]] #+ 300
        user_df[lst_850[idx]] = user_df[lst_850[idx]]  # + user_df[lst_back[idx]] #+ 300

    user_df = user_df[user_df.columns[:15]][:]
    uid  = int(file.split('/')[-1].split('\\')[1].split(".")[0])
    user_df.insert(0,'uid', uid)
    if i < 55:
        category = 'CN'
    elif  55<=i<85:
        category = 'MCI'
    elif  85<=i<99:
        category = "AD"
    user_df['Category'] = category

    df_98_ = pd.concat([df_98_,user_df])

uid_lst = list(set(df_98_.uid.unique().tolist()))

df_98 = pd.DataFrame()

for uid in uid_lst :

    df   = df_98_[df_98_.uid==uid]
    df_98 = pd.concat([df_98,df])

df_98 = df_98.reset_index(drop = True)

del df_98_


print("\n데이터 바로 확인 특정 유저 특정 채널 불러오기")
uids = 15

plt.title("98 Raw Data - User name : {} -  730nm,850nm - 4Channel".format(uids),fontsize=20)
df_98[df_98.uid==uids].A_730_3.plot(figsize=(12,5))
df_98[df_98.uid==uids].A_850_3.plot(figsize=(12,5))

 

 

주어진 데이터는 여러개의 csv 파일이 존재합니다. 해당 파일을 한개로 묶어 데이터프레임을 만들어주는 과정입니다. 데이터 프레임을 만들고 plot 해주면 해당 이미지를 얻을 수 있습니다.

 

#2. 이상치 확인하기

df_98[df_98.columns[2:]].boxplot(figsize=(15,7))

 

컬럼별 이상치값을 파악했습니다. 생각보다 이상치가 많이 분포되어있다는 점을 확인했습니다.

박스 위 아래 검은점은 사분위수의 통계적 관점에서의 이상치입니다.

 

sns.boxplot(data=df_98, y='A_730_0', x='Category')
sns.boxplot(data=df_98, y='A_730_1', x='Category')
sns.boxplot(data=df_98, y='A_730_2', x='Category')
sns.boxplot(data=df_98, y='A_730_3', x='Category')
sns.boxplot(data=df_98, y='A_730_4', x='Category')
sns.boxplot(data=df_98, y='A_730_5', x='Category')
sns.boxplot(data=df_98, y='A_730_6', x='Category')

카테고리별로 이상치값을 뽑아봤습니다.

이상하죠? 건강할수록(CN : 정상, MCI : 경도인지장애, AD : 알츠하이머) 이상치가 많다...? 

 

def outlier_iqr(data, column):
    # lower, upper 글로벌 변수 선언하기
    global lower, upper

    # 4분위수 기준 지정하기
    q25, q75 = np.quantile(data[column], 0.25), np.quantile(data[column], 0.75)

    # IQR 계산하기
    iqr = q75 - q25

    # outlier cutoff 계산하기
    cut_off = iqr * 1.5

    # lower와 upper bound 값 구하기
    lower, upper = q25 - cut_off, q75 + cut_off

    print('IQR은',iqr, '이다.')
    print('lower bound 값은', lower, '이다.')
    print('upper bound 값은', upper, '이다.')

    # 1사 분위와 4사 분위에 속해있는 데이터 각각 저장하기
    data1 = data[data[column] > upper]
    data2 = data[data[column] < lower]

    # 이상치 총 개수 구하기
    print('총 이상치 건수는', data1.shape[0] + data2.shape[0], '이다.')
    return data1,data2

 

사분위수를 통한 이상치를 추출하고 시각화하기 위해서 사용한 코드입니다. 이 코드는 [python] 탭에도 저장되어있는 코드로 자주 사용하는 코드입니다.

 

columns = 'A_850_2'          # 채널(컬럼)

outlier_1, outlier_2 = outlier_iqr(df_98,columns)   # 98명의 유저들의 특정 컬럼(채널)의 전체 데이터 분포를 활용한 이상치 탐지

plt.figure(figsize=(10,5))

sns.distplot(df_98[columns], kde=False)


# 이상치 영역 박스 그리기

plt.axvspan(xmin=lower, xmax=df_98[columns].min(), alpha=0.2, color='red')

plt.axvspan(xmin=upper, xmax=df_98[columns].max(), alpha=0.2, color='red')

 

columns = 'A_850_1'
outlier_1, outlier_2 = outlier_iqr(df_98,columns)

len(outlier_1), len(outlier_2)


print("\n 이상치가 존재하는 유저 목록 : {}".format(outlier_1.uid.unique().tolist()))

print("\n 80번 유저의 이상치 구간 (실선 제외) , 이상치 건수 : {}".format(len(outlier_1[outlier_1.uid==80])),
     "(주황색)")


df_98[df_98.uid==80].A_850_1.plot()
outlier_1[outlier_1.uid==80].A_850_1.plot()

 

이상치가 존재하는 유저의 목록을 뽑아봐도 

    if i < 55:
        category = 'CN'
    elif  55<=i<85:
        category = 'MCI'
    elif  85<=i<99:
        category = "AD"

대부분 CN과 MCI 에 분포되어있음이 확인됩니다.

 

<결론>

1. IQR 로 구한 통계학적 이상치값이 없어져야할 값이 아닌 단순히 신호값이 높은것들의 값이였고, CN, MCI, AD 를 구별짓는 한개의 척도로 받아들어야 하구나! 를 예상 할 수 있습니다.

 

2. 그렇다면 이 아날로그 신호를 디지털로 해석되면 어떨까?

- 높은신호값은 디지털관점으로 모두 1로 보고 낮은 신호값을 0으로 본다면? CN(정상)에 가까운 사람일수록 1인 신호가 많고 AD(알츠하이머)에 가까운 사람일수록 0인 신호가 많겠구나! 를 예상할 수 있습니다.