[데이터 분석] 분석 기법 및 활용
데이터 분석 기법에 대해 알아보자정형 데이터 분석 / 비정형 데이터 분석으로 구분되며, 각각의 기법이 특정한 데이터 유형과 분석 목적에 따라 활용된다! [정형 데이터 분석]- 탐색적 데이터
claremont.tistory.com
ㅁEDA(탐색적 데이터 분석): 모델을 만들기 전에 데이터를 파악하고 이해하기 위해 통계적 기법과 시각화 등을 통해 데이터를 분석하는 과정
(raw 데이터 파일)
[변수 설명] - 수치형 변수 4가지
이름 | 의미 | 활용 | 해석 |
MolWt(Molecular Weight) | 분자의 전체 질량 | 약물의 흡수 분포, 대사 등에 영향을 미침 | ◦ 너무 무거운 분자는 세포막 투과력이 낮음. ◦ Lipinski’s Rule에 따르면 500 g/mol 이하가 경구 약물에 적합. |
clogp(Calculated LogP) | 지질/수분 분배계수의 로그값 (logP), 소수성(Hydrophobicity)측정 | 약물이 지질막(세포막)을 통과할 수 있는지 평가할 때 사용됨. |
• 값이 클수록 지질에 잘 녹고 작을수록, 수용성이 높음 |
sa_score(Synthetic Accessibility Score) | 합성 가능성 점수 (Synthetic Accessibillity) | 실제 실험실에서 약물을 합성할 수 있는지를 판단할 때 사용. |
◦ 점수가 낮을수록 합성이 쉽고, 높을수록 합성이 어려움. ◦ 일반적으로 1(쉬움) ~ 10(어려움) 사이의 값을 가짐. |
qed(Quantitative Estimate of Drug-likeness) | 약물 적합성에 대한 종합 점수 | 여러 물리화학적 특성을 통합하여 후보 약물의 이상적 특성 여부를 판단. |
• 0.9 이상: 매우 우수한 약물 후보 • 0.5 이상: 일반적인 수준 • 0.3 이하: 적합하지 안흘 가능성 높음 |
[종속변수 value-count]
앞서 언급했듯이, 데이터셋의 종속변수 label은 화합물의 독성 여부를 나타내며, 0은 독성이 있는 화합물, 1은 독성이 없는 화합물을 의미한다.
Count Plot을 통해 label의 분포를 확인한 결과, 두 클래스 간의 샘플 수는 약간의 차이는 있으나 전반적으로 균형 잡힌 분포를 보였다.
이는 클래스 불균형 문제가 존재하지 않음을 의미하며, 모델 학습 시 특정 클래스에 편향되거나, 성능 지표(정확도, 정밀도 등)가 왜곡될 가능성이 낮다. 따라서 별도의 리샘플링 기법(SMOTE, 언더샘플링 등)을 적용하지 않고도, 모델이 양 클래스 모두에 대해 균형 잡힌 학습과 일반화 성능을 낼 수 있다 볼 수 있다.
SMILES 문자열 변수
[ rdkit ] - 화학정보학 오픈소스 파이썬 라이브러리
from rdkit import Chem
from rdkit.Chem import Descriptors
# SMILES로 분자 생성
mol = Chem.MolFromSmiles('CC(=O)OC1=CC=CC=C1C(=O)O') # 아스피린
# 분자량 계산
print(Descriptors.MolWt(mol)) # → 180.16
# 지용성(LogP)
print(Descriptors.MolLogP(mol)) # → 1.19
이렇게 분자식 구조를 입력하면 분자량 계산과 지용성 계산에 쉽게 활용할 수 있다
Bit 서브구조 추출
서브 구조로 SMILE CODE를 쪼갤 수 있으므로, 독성이 있는 물질을 대상으로 가장 많이 등장한 서브 구조를 추출.
추출한 것 중 TOP 8 리스트를 만들어서(독성 물질에서 많이 등장한 서브구조 추출) 해당 값들이 몇 번 등장하는지 추출!
예시) 아래는 train 데이터의 Top 8 리스트 서브 구조를 시각화 한 자료. 첫 번째 row의 SMILES 값은 ‘Nc1ccncc1’으로, 이를 구성하는 서브 구조는 다음과 같다. 아래 리스트에 3번 등장하므로 값이 3이 되는 것이다.
- 서브 구조
- bit 147: atom index: 0, radius: 0
- bit 184: atom index: 3, radius: 2
- bit 356: atom index: 5, radius: 2
- bit 378: atom index: 1, radius: 0
- bit 383: atom index: 4, radius: 0
- bit 433: atom index: 4, radius: 2
- bit 439: atom index: 0, radius: 1
- atom index: 2, radius: 2
- bit 579: atom index: 6, radius: 2
- bit 726: atom index: 4, radius: 1
- bit 780: atom index: 6, radius: 1
- bit 842: atom index: 1, radius: 2
- bit 849: atom index: 5, radius: 1
- bit 888: atom index: 6, radius: 0
# 1. 독성 있는 데이터만 필터링
toxic_df = df[df['label'] == 1]
# 2. 각 분자의 fingerprint 추출하여 bit 카운트
bit_counter = Counter()
bit_mapping = {}
for smi in toxic_df['SMILES']:
mol = Chem.MolFromSmiles(smi)
bitInfo = {}
fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=1024, bitInfo=bitInfo)
bit_counter.update(fp.GetOnBits())
for bit in fp.GetOnBits():
if bit in bit_mapping:
continue
if bit in bitInfo:
bit_mapping[bit] = (mol, bitInfo[bit][0]) # 중심 원자, 반경
# 3. 상위 8개 자주 등장한 bit 추출
top_bits = [bit for bit, _ in bit_counter.most_common(8)]
# 4. 각 SMILES가 top bit를 몇 개 포함하는지 세기
def count_top_bits(smi, top_bits):
mol = Chem.MolFromSmiles(smi)
if mol is None:
return 0
bitInfo = {}
fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=1024, bitInfo=bitInfo)
return sum(1 for bit in top_bits if bit in fp.GetOnBits())
# 5. 결과 컬럼 추가
df['cnt_top_bit'] = df['SMILES'].apply(lambda x: count_top_bits(x, top_bits))
Finger Print
1024 비트 변수 생성 : ECFP, FCFP, PTFP
1024개씩 인덱스를 끊어서 3개의 변수로 정리
# 열 인덱스 기준으로 합산
ecfp = df.iloc[:, 1:1025].astype(str).agg(''.join, axis=1)
fcfp = df.iloc[:, 1025:2049].astype(str).agg(''.join, axis=1)
ptfp = df.iloc[:, 2049:3073].astype(str).agg(''.join, axis=1)
파생 변수 추가 : PCA 차원 축소
1024차원을 2차원으로 축소해서 시각화
PCA로 압축된 값을 파생변수로 추가
from sklearn.decomposition import PCA
# 예: 1024차원 fingerprint matrix (예: ecfp_matrix)
# → 여기선 2차원 축소를 가정
pca = PCA(n_components=2)
pca_result = pca.fit_transform(ecfp_matrix)
# 설명된 분산 비율 (각 성분의 중요도)
weights = pca.explained_variance_ratio_
# 가중 합산: 각 성분에 가중치 곱해서 1개 값으로 합치기
pca_weighted_feature = (
pca_result[:, 0] * weights[0] +
pca_result[:, 1] * weights[1]
)
# 결과를 DataFrame에 추가
df['ecfp_pca_feature'] = pca_weighted_feature
FCFP와 PTFP도 마찬가지로 위와 동일한 코드로 파생변수 추가
+ 파생 변수 추가 : fingerprint별 활성 비트 수
PTFP는 ECFP, FCFP보다 더 많은 비트가 활성화되었고 이는 잠재적으로 더 많은 구조적 또는 물리화학적 정보를 담고 있을 수 있음
→ fingerprint별 활성 비트 수 파생 변수 추가하기로 결정
# 각 행(샘플)별로 1의 개수를 세어 파생 변수로 추가
df['ecfp_bit_count'] = ecfp_matrix.sum(axis=1)
df['fcfp_bit_count'] = fcfp_matrix.sum(axis=1)
df['ptfp_bit_count'] = ptfp_matrix.sum(axis=1)
[수치형 변수 분석(왜도, 이상치 등)]
1. MolWt
fig, axes = plt.subplots(1, 2, figsize=(10, 3))
# Boxplot
sns.boxplot(data=df, x='label', y='MolWt', ax=axes[0])
axes[0].set_title('Boxplot')
# KDEplot
sns.kdeplot(data=df, x='MolWt', hue='label', fill=True, common_norm=False, ax=axes[1], palette='Set2')
axes[1].set_title('KDEplot')
plt.tight_layout()
plt.show()
# IQR 계산
Q1 = df['MolWt'].quantile(0.25)
Q3 = df['MolWt'].quantile(0.75)
IQR = Q3 - Q1
# 이상치 조건
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 이상치 필터링
outliers = df[(df['MolWt'] < lower_bound) | (df['MolWt'] > upper_bound)]
# 이상치 개수 출력
print("이상치 개수:", len(outliers))
# 결과값
이상치 개수: 130
[MolWt 시각화 해석]
1. Boxplot 해석 두 클래스(label=0: Toxic, label=1: Non-toxic)의 중앙값은 유사하지만, Toxic(0)은 상대적으로 낮은 clogP 값을 더 많이 포함하고 있음 이상치(outlier)는 양쪽 모두 존재하나, Toxic 클래스에서 낮은 값 쪽 이상치가 더 많음
2. KDEplot 해석 Toxic(0) 클래스는 2~4 범위에 밀집 Non-toxic(1) 클래스는 4~6 이상으로 더 오른쪽으로 분포 두 클래스 간 분포의 밀도와 중심 위치가 명확하게 다름 → clogP가 높을수록 Non-toxic일 가능성 높음 → clogP가 낮을수록 Toxic일 가능성 높음
2. clogp
(코드 플로우 위와 동일)
(코드 플로우 위와 동일)
[clogp 시각화 해석]
1. Boxplot 해석 두 클래스(label=0: Toxic, label=1: Non-toxic)의 중앙값은 유사하지만, Toxic(0) 쪽은 낮은 값 구간(0~2)에 이상치가 더 많이 분포함 전체적으로 Non-toxic(1) 클래스의 clogP 값은 조금 더 분산 폭이 좁고 높은 위치에 있음
2. KDEplot 해석 Toxic(0) 클래스는 clogP가 2~4 사이에 분포 밀도가 높음 Non-toxic(1) 클래스는 4~6 사이에 더 뚜렷하게 집중되어 있음 분포의 중심값 위치가 명확히 다르며, → clogP 값이 클수록 Non-toxic일 가능성이 높아지는 경향 확인됨
3. sa_score
(코드 플로우 위와 동일)
(코드 플로우 위와 동일)
[sa_score 시각화 해석]
1. Boxplot 해석 - 두 집단(label=0, 1)의 sa_score 중앙값은 비슷하지만, 독성 없는 화합물은 약간 더 낮은 중앙값을 가진다. - 두 그룹 모두 이상치가 존재한다 특히 label = 1 쪽에서 극단적으로 낮은 sa_score가 소수 나타난다. - 전반적으로 sa_score가 2~4 사이에 집중된 분포를 보인다.
2. KDEplot 해석 - 독성 없는 그룹의 분포가 조금 더 왼쪽으로 이동되어 있다. -> 합성하기 쉬운 화합물이 상대적으로 많다 - 독성 있는 그룹은 분포가 더 넓다. -> 구조가 복잡하고 합성이 어려운 경우도 존재함을 암시한다.
4. qed
(코드 플로우 위와 동일)
(코드 플로우 위와 동일)
[qed 시각화 해석]
1. Boxplot 해석 - 독성 없는 화합물(label=1)이 더 높은 중앙값을 보인다. -> 약물 적합성이 평균적으로 더 우수함
2. KDEplot 해석 - label=1의 분포는 우측(0.6)이상 에 더 많은 밀도가 몰려 있다. - label=0은 상대적으로 좌측(0.3-0.5)에 집중되어 있다 -> qed 분포자체가 독성 여부에 따라 뚜렷한 차이를 보인다.
변수 간 상관관계 및 다중공선성
1. 상관관계(Correlation): 두 변수 간의 선형적 관계를 나타내는 통계적 척도
피어슨 상관계수 (-1 ~ +1) 사용. 수치형 변수 간의 상관관계 파악
if target_var in df.columns:
print(f"\n[6. {target_var} 변수와의 상관관계 분석]")
try:
# 상관계수 계산
cols_for_corr = numerical_vars + [target_var]
correlation_matrix = df[cols_for_corr].corr()
correlation_with_target = correlation_matrix[target_var].sort_values(ascending=False)
print("\n상관계수 (vs {}) :".format(target_var))
print(correlation_with_target)
# 히트맵 시각화
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt=".2f")
plt.title(f'Correlation Heatmap including {target_var}')
plt.show() # 플롯을 셀 출력으로 표시
except Exception as e:
print(f"\n - 상관관계 계산 중 오류 발생 (데이터 타입 확인 필요): {e}")
else:
print(f"\n[6. '{target_var}' 컬럼을 찾을 수 없어 상관관계 분석을 생략합니다.]")
2. 다중공선성(Multicollinearity): 독립변수들 간의 강한 상관관계 존재하는 것(한 독립변수가 다른 독립변수들의 선형 조합으로 설명 가능)
(발생하는 문제점)
- 모델 계수의 불안정성
- 변수 영향력 해석의 어려움
- 모델 성능 저하 가능성
분산 팽창 지수(VIF) 계산
from statsmodels.stats.outliers_influence import variance_inflation_factor
import pandas as pd
print("\n[다중공선성 확인 (VIF)]")
X = df[numerical_vars].dropna()
vif_data = pd.DataFrame()
vif_data["feature"] = X.columns
vif_data["VIF"] = [variance_inflation_factor(X.values, i) for i in range(len(X.columns))]
print(vif_data)
feature VIF
0 MolWt 34.436728
1 clogp 9.965039
2 sa_score 27.374073
3 qed 6.304276
--- VIF 해석 ---
VIF = 1: 다중공선성 없음
1 < VIF < 5: 약한 다중공선성 의심
5 <= VIF < 10: 다소 높은 다중공선성 의심
VIF >= 10: 높은 다중공선성, 조치 고려
MolWt와 sa_score의 다중공선성이 의심되기 때문에 분석 모델 구축 시 유의할 필요가 있음.
전처리 과정에서 손을 보거나, 심한 경우에는 피처값을 제거하는 경우도 고려!
'데이터 분석' 카테고리의 다른 글
[데이터 분석] 회귀분석(Regression Analysis) (0) | 2025.02.12 |
---|---|
[데이터 분석] 통계 용어 정리(feat. 퍼짐) (0) | 2025.02.11 |
[데이터 분석] 이상치 처리(Outlier Handling) (0) | 2025.02.11 |
[데이터 분석] 결측치 처리(Missing Value Handling) (0) | 2025.02.11 |
[데이터 분석] 데이터 구조 유형(R, Python) (0) | 2025.02.11 |