[LG U+ 5기]/Project

[2월][3주차] 🏥의료데이터 분석 프로젝트

jjanggun0930 2025. 2. 17. 18:00

2025.02.17 월

HEART 심부전 데이터

심부전 발생했다고 가정한 데이터

 

[컬럼 설명]

더보기
  • ChestPainType : 가슴 통증 타입
    • TA : 심장에 의한 협심증 통증
    • ATA : 심장 관련 전형적이지 않은 통증
    • NAP : 심장 관련 아닌 가슴 통증
    • ASY : 무증상
    • RestingBP : 안정된 상태에서 측정된 혈압
  • RestingBP : 안정된 상태에서 측정된 혈압
  • Cholesterol : 혈액 내의 콜레스테롤 농도
  • FastingBS : 공복 상태의 혈당
    • 1 : 120mg/dl 보다 크면
    • 0 : 그 외 
  • RestingECG : 안정된 상태의 심전도
    • ST : 심전도에서 문제가 발견
    • LVH : 좌심실이 비정상적으로 커진 상태
  • MaxHR : 최대 심박수
  • ExerciseAngina : 운동시 가슴 통증 경험 여부
  • HeartDisease : 심장병 여부 
    • 1 : 심장병
    • 0 : 정상적인 상태
데이터 전처리 (결측치 / 이상치 처리)

총 918 => 916개 데이터로 처리 완료 (2개 행 삭제 / 나머지는 대체)

 

데이터셋 통계량 요약
# MaxHR 변수의 평균값과 중앙값
print(heart['MaxHR'].mean())    # 136.83078602620088
print(heart['MaxHR'].median())  # 138.0
'''
평균값과 중앙값이 비슷하다는 것
=> 데이터에 극단적인 이상치가 없음
=> MaxHR 변수의 값들이 큰 왜곡없이 고르게 분포
'''
# ChestPainType 열의 빈도수
heart['ChestPainType'].value_counts()
'''
ASY    496  <= 무증상
NAP    202  <= 비협심증 흉통
ATA    172  <= 비전형적인 협심증
TA      46  <= 전형적인 협심증 
=> 가장 흔한 가슴 통증은
=> 무증상이 다른 유형에 비해 훨씬 빈번하게 발생!!!!!
'''
# 'Age', 'MaxHR', 'Cholesterol' 통계량 요약 
heart[['Age','MaxHR','Cholesterol']].describe()
'''
              Age       MaxHR  Cholesterol
count  916.000000  916.000000   916.000000
mean    53.533843  136.830786   198.728166
std      9.425923   25.447917   109.466452
min     28.000000   60.000000     0.000000
25%     47.000000  120.000000   173.000000
50%     54.000000  138.000000   223.000000
75%     60.000000  156.000000   267.000000
max     77.000000  202.000000   603.000000

Age mean : 53.53 <= 대부분 50대 초반
Cholesterol std : 109 / mean : 198  <= 표준편차가 평균보다 작다(분포현황체크)
MaxHR max : 202 / min : 60  <= 심박수 수치가 매우 다양하게 분포
MaxHR median : 138 / mean : 136     <= 평균과 중앙값이 비슷하다
그룹별 집계로 변수별 상관관계 확인 
# 그룹별 집계 
# 1. HeartDisease
#    2. Cholesterol
#=> Age, MaxHR, Cholesterol => mean()
G = heart.groupby(['HeartDisease','ChestPainType'])[['Age','MaxHR','Cholesterol']].mean()
'''
                                  Age       MaxHR  Cholesterol
HeartDisease ChestPainType                                    
0.0          ASY            52.317308  138.548077   226.865385
             ATA            48.236486  152.621622   232.668919
             NAP            51.045802  150.641221   221.503817
             TA             54.692308  150.500000   222.730769
             
1.0          ASY            55.660714  125.806122   175.974490
             ATA            55.958333  137.500000   233.291667
             NAP            57.549296  129.394366   153.281690
             TA             55.000000  144.500000   186.700000  
=> 정상적인(심장병이 없는) 사람의 나이, 심박수, 콜레스테롤 수치가 
    각각 어떠한 흉통 유형에 따라 달라지는 지를 확인 할 수 있다!
예) 정상적인(심장병이 없는) 사람의 TA 유형과 심장병이 있는 사람의 TA 유형
나이 :        54.69 / 55.0
심박수 :      150.5 / 144.5  없 < 있
콜레스테롤 :  222.7 / 186.7  없 < 있
=> 심박수 / 콜레스테롤은 정상적인 사람이 더 높은 경향이 있다.  
'''
SG = heart.groupby(['Sex'])[['RestingBP']].mean()
'''
      RestingBP
Sex            
F    132.119792
M    132.421271
=> 차이가 거의 없음 : 성별은 혈압에 유의미한 영향을 미치지 않는다. 
                     성별 보다는 개인에 따른 맞춤형 관리가 필요해 보인다.
시각화

심부전 데이터셋에 사용할 컬러 팔레트

  • ChestPainType 빈도수 시각화 (with. PIE차트)

ASY 무통증이 54%로 가장 많은 비율을 차지한다. 다음으로 NAP 심장 관련이 아닌 통증이 22%로 뒤를 잇는다.

[코드 참고]

더보기
# 심부전 파이 차트  : 'ChestPainType' 변수 : 흉통 유형
# ratio : 'ChestPainType' 추출 <= 1차원으로 변경

# plt.pie()의 매개변수 설명
# labels : 부채꼴 조각 이름
# autopct : 부채꼴 안에 표시될 숫자 형식 지정
#   문자열에서 % 포맷팅으로 %0.f 형태로 사용하면 소수점 없이 정수
#   진짜 %를 표시하기 위해 %%로
# startangle : 부채꼴이 그려지는 시각 각도 설정, 90이면 12시 방향
# explode : 부채꼴이 파이 플롯의 중심에서 벗어나는 정도
# shadow : 그림자 효과

# plt.suptitle() : 전체 플롯의 제목
# plt.title() : 서브 플롯의 제목
# plt.show()

+ 인사이트 

  • ASY(무증상) : 심장병 여부를 판단할 때 흉통 유형이 중요하다. => 무증상 흉통을 고려!!
  • NAP, ATA(비전형적) : 다양한 흉통 유형이 심장병의 가능성이 높다. => 심장병 판단 시, 흉통 유형을 세밀하게 분석해야 한다!
  • TA : 전형적인 흉통 증상이 드물다. => 즉, 다른 유형의 흉통이 더 흔할 수 있다!!

  • 심장병과 흉통 유형의 관련성 (with.막대 그래프) 

[코드 참고]

더보기
# 심장병이 있을 때와 없을 때, 
# 항상 ASY[]인 무증상이 압도적인지 궁금!
# 'HeartDisease'여부에 따른 'ChestPainType'인 흉통 유형 막대 시각화
# HeartDisease 변수 : 심장병 여부
# ChestPainType 변수 : 흉통 유형

# plt.figure(figsize = ( , ))
# countplot() : 각 범주(심장병 여부)에 속하는 데이터(흉통 유형)의 개수를 막대 시각화
# countplot() 변수
# data : countplot에서 사용할 데이터 셋
# x = HeartDisease
# hue = ChestPainType 특정 열 데이터로 색상을 구분
# hue_order = 색상 순서를 수동 => 'ASY', 'NAP', 'ATA', 'TA'

# x축 눈금 설정 : HeartDisease ([0,1])
# plt.sticks([0,1],['~~','~~'])

# plt.tight_layout() : 막대그래프가 겹치지않도록 최조한의 여백을 만들어주는 역할

+ 인사이트

  • 심장병 O : ASY NAP ATA TA
    =>  심장병이 있는 사람은 주로 무증상 : 심장 질환과 흉통 유형간의 강한 관련성 
    =>  심장병이 심각한 단계에 이를 때까지 증상이 나타나지 않을 수 있다!
    =>  무증상 환자에 대한 모니터링 강화
    =>  조기 검진을 통해 조기 발견이 중요!!
  • 심장병 X : ATA NAP ASY TA
    =>  // 없는 사람은 주로 비전형적 통증 
    =>  비환자들에게도 심장 건강에 대한 경각심을 계속 일깨워 줘야 함!!!
    =>  비환자들에게도 심장 검진이 필요!!

  • 심장병에 따른 환자들의 나이별 숫자 (with.fill_between())

[코드 참고]

더보기
# 심부전 데이터 영역 그래프
# 나이에 따른 심부전 여부 수치화
# groupby('Age')['HeartDisease'].value_counts()
# unstack()
Heart_Age = heart.groupby('Age')['HeartDisease'].value_counts().unstack(level = 'HeartDisease')

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

# plt.fill_between() : x축을 기준으로 그래프 영역을 채우는 함수
# x : 곡선을 정의하는 노드의 x 좌표
# y1 : 첫 번째 곡선을 정의하는 노드의 y 좌표
# y2 : 두 번째 곡선을 정의하는 노드의 y 좌표
# alpha : 투명도
# label : 범례에 표시할 문자열 입력
plt.fill_between( x = Heart_Age[0].index,
                 y1 = 0, y2 = Heart_Age[0],
                 color = '#003399',
                 alpha = 1, label = 'Normal')
# x = Heart_Age[0].index : Heart_Age 데이터의 심장병에 없는 환자들의 나이
# y1 = 0 : y 좌표 0부터 그래프 영역을 채워야해서 y1 = 0으로 설정
# y2 = Heart_Age[0] : Heart_Age데이터의 심장병 없는 환자들의 나이별 숫자

# 심장병 있는 환자들의 나이별 숫자 시각화 
plt.fill_between( x = Heart_Age[1].index,
                 y1 = 0, y2 = Heart_Age[1],
                 color = '#0099ff',
                 alpha = 0.6, label = 'heart disease')

+ 인사이트

  • 50대 이후로 심장병 발병률이 높아 보인다. 
  • 50~70대는 특히, 심장병 검사를 자주 하여야 한다. 

  • 심장병 여부에 따른 안정된 상태의 심전도 / 운동 시 가슴 통증 여부 (with.swarmplot())

더보기
# 심부전 범주형 산점도 그래프 : sns.swarmplot()
# H_0 : 심장병이 없는 환자의 데이터 추출 HeartDisease == 0
# H_1 : 심장병이 있는 환자의 데이터 추출 HeartDisease == 1

# 그래프 객체 생성 (figure에 2개의 서브 플롯을 생성)

# axis1 = fig.add_subplot(1,2,1)    # 1행 2열짜리에서 1번째 위치
# axis2 = fig.add_subplot(1,2,2)    # 1행 2열짜리에서 2번째 위치


# RestingECG / ExerciseAngina
# 심장병이 없는 환자의 데이터에서 
# 나이별 안정된 상태에서 측정된 혈압 수치 시각화

# 심장병이 있는 환자의 데이터에서
# 나이별 안정된 상태에서 측정된 혈압 수치 시각화

+ 인사이트

 

  • ST 이상과 LVH 유형의 ECG는 심장 질환과 연관이 있는 경향이 있음.
  • 나이가 많을수록 심장 질환 발생 가능성이 높아지는 패턴을 보임.
  • Exercise Angina(운동 유발 협심증)를 가진 사람들은 심장 질환 그룹에서 더 높은 비율로 나타남.

  • 심장병 관련 논문 Title 워드클라우드

 

심부전 / 환자 가 가장 크다

 

더보기
# 심부전 워드 클라우드
# Pubmed 사이트의 심부전관련 논문의 제목 데이터 pubmed_title.csv
pubmed_title = pd.read_csv('./data/pubmed_title.csv')

from wordcloud import WordCloud
from PIL import Image

# text : 데이터프레임을 list로 1차 변환시키고 str(문자열)로 2차 변환
# mask : 단어를 그릴 위치 설정
# cmap : 컬러맵 생성
text = str(list(pubmed_title['Title']))
mask = np.array(Image.open('./data/image.jpg'))
#       np.array : 검은색 흰색 배열로 만들어서 구별할 수 있도록.
cmap = plt.matplotlib.colors.LinearSegmentedColormap.from_list("",
                                                               ['#000066','#003399','#00FFFF'])
wordcloud = WordCloud(background_color='white',
                      width = 2500,
                      height = 1400,
                      max_words = 170,
                      mask = mask,
                      colormap = cmap).generate(text)
plt.imshow(wordcloud)
plt.axis('off')
plt.suptitle('Heart Disease WordCloud', fontsize = 16)
plt.title('Pubmed site : Heart Failure', fontsize = 12)
plt.show()

+ 인사이트

  • 가장 큰 단어인 "heart failure" (심부전) / "patient" (환자) 이 핵심 주제임을 확인할 수 있음.
  • "patient" (환자), "ejection fraction" (박출률), "ventricular" (심실), "cardiac" (심장) 같은 단어들이 자주 등장 → 심부전 연구에서 중요한 핵심 개념들이 무엇인지 알 수 있음.
  • "management" (관리), "impact" (영향), "outcome" (결과) 등의 단어가 보임 → 치료 및 환자 관리와 관련된 연구가 많다는 것을 시사한다.