MIMIC_preprocessing์ ๋๊ท๋ชจ ์ ์๊ฑด๊ฐ๊ธฐ๋ก(EHR) ๋ฐ์ดํฐ, ํนํ MIMIC-III/IV ๋ฐ์ดํฐ์ ์ ํจ์จ์ ์ผ๋ก ์ ์ฒ๋ฆฌํ๊ณ ๋ถ์ํ๊ธฐ ์ํด ์ค๊ณ๋ Python ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
EHR ๋ฐ์ดํฐ๋ ๋ฐฉ๋ํ ์์ ์๊ณ์ด ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๊ณ ์์ด ์ผ๋ฐ์ ์ธ Pandas ์ฐ์ฐ๋ง์ผ๋ก๋ ์ฒ๋ฆฌ ์๋๊ฐ ๋งค์ฐ ๋๋ฆฝ๋๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ์(ICUSTAY_ID) ๋จ์์ ๋ณ๋ ฌ ์ฒ๋ฆฌ์ ์๊ณ์ด ๋ฐ์ดํฐ ์ ๋ ฌ(Alignment) ๊ธฐ๋ฅ์ ์ ๊ณตํ์ฌ ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ์ ๊ฐ์ํํฉ๋๋ค.
์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ ํต์ฌ ์ ์ฒ๋ฆฌ ํ๋ฆ์ ์ง์ํฉ๋๋ค:
- ํ์ ํํฐ๋ง:
ICUSTAY_ID๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ํจํ ํ์ ์ ๋ณ (์: 48์๊ฐ ์ด์ ์ ์ ํ์ ํํฐ๋งcheck_48h). - ์๊ณ์ด ์ ๋ ฌ (Time Alignment): ๋ถ๊ท์นํ ์๊ณ์ด ๋ฐ์ดํฐ๋ฅผ 1์๊ฐ, 4์๊ฐ, 24์๊ฐ ๋ฑ ํน์ ๊ฐ๊ฒฉ(
intv_h)์ผ๋ก ์ ๋ ฌํ๊ณ ์ง๊ณ(Aggregation)ํฉ๋๋ค (process_interval_shift_alignment). - ๋ฐ์ดํฐ ํด๋ ์ง: ์ฐ๊ด ์๋ ์ปฌ๋ผ ์ ๊ฑฐ, ์ด์์น ์ฒ๋ฆฌ ๋ฐ ๋จ์(UOM) ๋งคํ์ ์ํํฉ๋๋ค.
- ๋ณ๋ ฌ ๊ฐ์ํ:
@ParallelEHR๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ํตํด ๋ฉํฐ์ฝ์ด๋ฅผ ํ์ฉํ ๊ณ ์ ์ฐ์ฐ์ ์ํํฉ๋๋ค.
PyPI๋ฅผ ํตํด ์์ฝ๊ฒ ์ค์นํ ์ ์์ต๋๋ค.
pip install openmimic@ParallelEHR์ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ผ๋ก, Pandas DataFrame ์ฐ์ฐ์ CPU ์ฝ์ด ์์ ๋ง์ถฐ ์๋์ผ๋ก ๋ณ๋ ฌํํด์ฃผ๋ ๋ฐ์ฝ๋ ์ดํฐ์
๋๋ค.
MIMIC๊ณผ ๊ฐ์ ์๋ฃ ๋ฐ์ดํฐ๋ ๋ณดํต ์๋ฐฑ๋ง ํ(Row)์ ๊ฐ์ง์ง๋ง, ๋ถ์ ๋จ์๋ ํ์(ICUSTAY_ID)๋ณ๋ก ๋
๋ฆฝ์ ์ธ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์ผ๋ฐ์ ์ธ df.groupby().apply()๋ ๋จ์ผ ์ฝ์ด๋ง ์ฌ์ฉํ๋ฏ๋ก ๋งค์ฐ ๋๋ฆฝ๋๋ค.
@ParallelEHR์:
- ์ง์ ๋ ์ปฌ๋ผ(์:
ICUSTAY_ID)์ ๊ธฐ์ค์ผ๋ก ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ CPU ์ฝ์ด ์๋งํผ ๊ทธ๋ฃน์ผ๋ก ๋ถํ ํฉ๋๋ค. - ๊ฐ ๊ทธ๋ฃน์ ๋ณ๋์ ํ๋ก์ธ์ค(Process)์ ํ ๋นํ์ฌ ๋์์ ์คํํฉ๋๋ค.
- ์์
์ด ์๋ฃ๋๋ฉด ๊ฒฐ๊ณผ๋ฅผ ์๋์ผ๋ก ๋ค์ ํ๋์ DataFrame์ผ๋ก ํฉ์ณ(
pd.concat) ๋ฐํํฉ๋๋ค. cloudpickle์ ์ฌ์ฉํ์ฌ ๋ณต์กํ ํจ์๋ ์์กด์ฑ๋ ๋ฌธ์ ์์ด ์ง๋ ฌํํ์ฌ ์ฒ๋ฆฌํฉ๋๋ค.
ํจ์๋ฅผ ์ ์ํ ๋ @ParallelEHR("๊ธฐ์ค_์ปฌ๋ผ๋ช
")์ ๋ถ์ฌ์ฃผ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
์ฃผ์: ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์ ์ฉ๋ ํจ์๋ ์ฒซ ๋ฒ์งธ ์ธ์ ํน์ *args ์ค ํ๋๋ก ๋ฐ๋์ DataFrame์ ๋ฐ์์ผ ํ๋ฉฐ, ํด๋น DataFrame์๋ ๊ธฐ์ค_์ปฌ๋ผ๋ช
์ด ์กด์ฌํด์ผ ํฉ๋๋ค.
import pandas as pd
import numpy as np
from openmimic.utils import ParallelEHR
# ์์: ๋์ฉ๋ ์ฐจํธ ์ด๋ฒคํธ ๋ฐ์ดํฐ
# ์ค์ ๋ก๋ pd.read_csv('chartevents.csv') ๋ฑ์ ์ฌ์ฉ
data = {
'ICUSTAY_ID': np.random.randint(200000, 200100, 100000),
'CHARTTIME': pd.date_range(start='1/1/2022', periods=100000, freq='T'),
'VALUENUM': np.random.randn(100000)
}
df_huge = pd.DataFrame(data)
# ---------------------------------------------------------
# @ParallelEHR ์ฌ์ฉ ์์
# ---------------------------------------------------------
@ParallelEHR(column_name="ICUSTAY_ID")
def complex_feature_engineering(df, param1, param2):
"""
๊ฐ ํ์ ๊ทธ๋ฃน(Chunk)๋ณ๋ก ์คํ๋ ํจ์์
๋๋ค.
๋ง์น ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ฏ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๋ฉ๋๋ค.
"""
# ์: ํ์๋ณ๋ก ๋ณต์กํ ์ด๋ ํ๊ท ์ด๋ ์ฐ์ฐ์ ์ํ
# ์ฌ๊ธฐ๋ก ๋ค์ด์ค๋ df๋ ์ ์ฒด ๋ฐ์ดํฐ๊ฐ ์๋๋ผ,
# ์๋์ผ๋ก ๋ถํ ๋ ํ์๋ค์ ๋ถ๋ถ ๋ฐ์ดํฐ(subset)์
๋๋ค.
# 1. ์์ ์ฐ์ฐ: ๊ฐ์ด ํน์ ๋ฒ์์ธ ๊ฒฝ์ฐ ํํฐ๋ง
df['processed_value'] = df['VALUENUM'] * param1 + param2
# 2. ํ์๋ณ ๊ทธ๋ฃน ์ฐ์ฐ
df['rolling_mean'] = df.groupby('ICUSTAY_ID')['VALUENUM'].transform(lambda x: x.rolling(5).mean())
return df
# ์คํ (๋ด๋ถ์ ์ผ๋ก ๋ณ๋ ฌ ์ฒ๋ฆฌ ์ํ)
if __name__ == '__main__':
# ์๋์ผ๋ก CPU ์ฝ์ด๋ฅผ ๊ฐ์งํ์ฌ ๋ณ๋ ฌ๋ก ์์
ํ ๊ฒฐ๊ณผ ๋ฐํ
result_df = complex_feature_engineering(df_huge, param1=1.5, param2=10)
print(result_df.head())
print(f"์ฒ๋ฆฌ๋ ๋ฐ์ดํฐ ํฌ๊ธฐ: {result_df.shape}")@ParallelEHR์ ๋ด๋ถ์ ์ผ๋กmp.cpu_count() * 0.8๋งํผ์ ์ฝ์ด๋ฅผ ์ฌ์ฉํ๋๋ก ์ค์ ๋์ด ์์ด ์์คํ ๊ณผ๋ถํ๋ฅผ ๋ฐฉ์งํฉ๋๋ค.- ๋ฐ์ดํฐํ๋ ์์ ์ชผ๊ฐ๊ณ ํฉ์น๋ ์ค๋ฒํค๋๊ฐ ์์ผ๋ฏ๋ก, ๋๋ฌด ๊ฐ๋จํ ์ฐ์ฐ๋ณด๋ค๋ ์ฐ์ฐ ๋น์ฉ์ด ๋์ ์์ ์ด๋ ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ์ฌ์ฉํ ๋ ๊ฐ์ฅ ํจ๊ณผ์ ์ ๋๋ค.
check_48h(df): ํ์์ ICU ์ฌ์ ๊ธฐ๊ฐ์ด 48์๊ฐ ์ด์์ธ์ง ๊ฒ์ฆํฉ๋๋ค.process_interval_shift_alignment(df, item_interval_info): ๋ค์ํ ์๊ฐ ๊ฐ๊ฒฉ(1์๊ฐ, 4์๊ฐ ๋ฑ)์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฌํ๊ณ ๊ฒฐํฉํฉ๋๋ค.print_completion: ํจ์ ์คํ ์๊ฐ์ ์ธก์ ํ์ฌ ์ถ๋ ฅํด์ฃผ๋ ๋ฐ์ฝ๋ ์ดํฐ์ ๋๋ค.
MIT license