분류 전체보기 (321)

반응형


Python PDPbox 패키지 설치시 문제 해결


문제


 PDPbox 패키지를 설치하던 도중 아래 에러가 발생하면서 설치가 완료되지 않음


/usr/bin/ld: cannot find -lpython3.5m

 collect2: error: ld returned 1 exit status

 error: command 'gcc' failed with exit status 1


해결


sudo apt install python3.5-dev



구글링해도 안나와서 올립니다.


도움을 얻은 페이지

https://github.com/giampaolo/psutil/issues/1143

반응형
반응형


생존분석 - 모수적 모형



Cox의 비례위험 모형


일반적으로 생존분석에 사용되는 모델인 Cox의 비례위험모형의 경우 준모수적 (Semi-parametric) 방법입니다. 이 방법은 생존함수 (Survival function)을 추정하기 위해 Hazard function에 대한 모델링을 합니다. 시간에 따라 변하는 Baseline hazard가 존재하고, Covariate 들의 effect에 따라 hazard가 달라진다고 모델링합니다. 그리고 hazard의 effect인 Hazard ratio가 시간에 따라 동일하다는 비례위험을 가정하여 Hazard function과 Survival function 을 추정하게 됩니다. 이 방법의 장점은 Survival function에 분포에 대해서 가정을하지 않기 때문에 강건하며, Covariate를 보정할 수 있다는 것입니다. 또한 Baseline hazard function이 어떤 함수인지를 알 필요가 없이 계수 추정을 통해 Hazard ratio만 구할 수 있다면 Covariate의 effect를 파악할 수 있습니다. 


모수적 모형


반면 모수적 모형은 Hazard function과 Survival function의 모양에 대해 Cox 보다 더 강한 '가정'을 합니다. 이 가정은 Hazard function과 Survival function이 어떤 분포를 따를 것이라는 믿음에서 출발합니다. 가장 많이 사용되는 모수적 모델은 Weibull 모형입니다. 


Weibull 모형의 Hazard function과 Survival function 은 다음과 같이 정의됩니다.


$$ h(t) = \lambda p t^{p-1} $$

$$ S(t) = exp(-\lambda t^p) $$ 


h(t)와 S(t)에 정의에 따라 h(t)가 저렇게 정의되면 S(t)가 저렇게 계산되는 것을 확인할 수 있습니다.


$$ \lambda = exp(\beta_0 + \beta_1 TRT) $$ 


만약 Treatment 변수에 대해 Weibull model을 만들 경우, 이 때 위와 같이 식을 놓고, beta0, beta1, p 3개의 계수를 추정하게 됩니다. 


또는 Hazard를 기준으로 계수를 추정하는 것이 아니라 Survival time을 기준으로 추정할 수 있는데, 이 경우가 모수적 방법에서 많이 사용되는 방법입니다. 이를 Acceleration Failure Time Model이라고 합니다. 


$$ S(t) = exp(-\lambda t^p) $$

$$ t = [-lnS(t)]^{1/p} \times \frac{1}{\lambda^{\frac{1}{p}}} $$

$$ \frac{1}{\lambda^{\frac{1}{p}}} = exp(\alpha_0 + \alpha_1 TRT) $$


AFT 모델에서는 위와 같이 놓고 alpha0, alpha1, p를 추정하게 됩니다. 


이렇게 두 가지 방법으로 구한 계수 beta와 alpha의 의미는 다른데, 만약 p가 고정되있다고 한다면, beta는 exponential을 취하면 Hazard ratio를 뜻하며, alpha는 exponential을 취하면 Accerleration factor가 됩니다. Acceleration factor에 대해서는 본 포스팅에서는 다루지 않을 예정이므로 자료를 참고 바랍니다. 


Strata 별로 p가 동일하다고 가정한 경우, Weibull model은 Proportional Hazard 가정과 Acceleration Failure Time 가정을 만족합니다. (https://en.wikipedia.org/wiki/Accelerated_failure_time_model)


PBC 데이터를 통한 Weibull model의 구축


pbc.dat

library(survival)

data <- read.table("pbc.dat")

# Column rename
colnames(data) <- c('id', 'futime', 'status', 'drug', 'age', 'sex', 'ascites', 'hepato', 'spiders', 'edema', 'bili', 'chol', 'albumin', 'copper', 'alk_phos', 'sgot', 'trig', 
                   'platelet', 'protime', 'stage')

head(data)
data <- data[data$drug != '.', ]
data$drug <- as.character(data$drug)
data$drug[(data$drug == 2)] = 0
data$drug <- factor(data$drug)


Drug = 0 플라시보, Drug = 1 페니실린에 대해 Death에 대한 Time to event 분석을 수행합니다.


Weibull Model Fitting

library(survival)
library(eha)

# AFT  
swei = survreg(s~drug, dist = "weibull",data = data) 

# PH  
sweiph = phreg(s~drug, dist = "weibull",data = data)

summary(swei) 
summary(sweiph)
Call:
survreg(formula = s ~ drug, data = data, dist = "weibull")
              Value Std. Error     z      p
(Intercept)  8.3167     0.1094 75.99 <2e-16
drug1       -0.0438     0.1431 -0.31  0.759
Log(scale)  -0.1532     0.0729 -2.10  0.036

Scale= 0.858 

Weibull distribution
Loglik(model)= -1348.2   Loglik(intercept only)= -1348.3
	Chisq= 0.09 on 1 degrees of freedom, p= 0.76 
Number of Newton-Raphson Iterations: 5 
n= 312 
Call:
phreg(formula = s ~ drug, data = data, dist = "weibull")

Covariate          W.mean      Coef Exp(Coef)  se(Coef)    Wald p
drug 
               0    0.491     0         1           (reference)
               1    0.509     0.051     1.052     0.167     0.759 

log(scale)                    8.317               0.109     0.000 
log(shape)                    0.153               0.073     0.036 

Events                    144 
Total time at risk        625985 
Max. log. likelihood      -1348.2 
LR test statistic         0.09 
Degrees of freedom        1 
Overall p-value           0.759387


Median Survival Time Comparison

predict(swei,newdata=data.frame(drug='0'),type=c("quantile"),se.fit=FALSE,p=0.5) predict(swei,newdata=data.frame(drug='1'),type=c("quantile"),se.fit=FALSE,p=0.5)

1: 2987.74956079141
1: 2859.63735172644



Weibull Model을 통해 구한 Survival Function과 Kaplen-Meier estimation의 비교

data$event_all=ifelse(data$status==0,0,1)
data$futime = ifelse(data$futime==0,0.1,data$futime)
s = Surv(data$futime,data$event_all)
sKM <- survfit(s ~ drug, data=data)
plot(sKM)
lines(predict(swei,newdata=list(drug='0'),type="quantile", p=seq(.01,.99,by=.01)),seq(.99,.01,by=-.01),col='red')
lines(predict(swei,newdata=list(drug='1'),type="quantile", p=seq(.01,.99,by=.01)),seq(.99,.01,by=-.01),col='red')

위 그림에서 검은색 직선이 Kaplen-Meier Survival function estimation이며, 붉은색과 파란색이 Weibull 모델로 추정한 Survival function 입니다. Weibull 모델의 S(t)의 모양에 대한 가정이 맞다면 두 직선은 가까울 것입니다. 그리고 위 직선들은 p가 같다는 가정하에 구해진 직선들입니다. 즉, PH, AFT 가정을 한 후에 그려진 직선입니다. 그렇기 때문에 Weibull 모형의 Survival function에 대한 가정, 그리고, PH, AFT 가정을 해도 될지에 대해서 체크를 해보아야합니다.



Weibull 가정의 체크 


(https://stat.ethz.ch/education/semesters/ss2011/seminar/contents/handout_9.pdf)


Weibull 모형에서 가정을 체크하는 방법은 X 축을 log(t), Y 축을 log(-log(S(t)) 로 그래프를 그려보는 것입니다. 이 때, S(t)는 Kaplen-Meier 방법으로 구한 Survival function 입니다. 이 때, 그룹간의 그래프의 모양을 보고 Weibull 생존모형 가정이 맞는지, 또한 두 그룹에서 PH, AFT 가정이 맞는지를 체크할 수 있습니다. 


먼저, 두 그룹의 직선이 평행하게 나오면 Weibull 가정을 만족합니다. 그렇지 않으면 Weibull 가정이 만족되지 못하며 AFT 가정도 만족되지 못합니다. 또한 두 그룹의 기울기의 비가 일정하면 PH 가정을 만족합니다. 


sKM <- survfit(s~ drug, data=data)
kmfit.TRT1 = summary(sKM)
kmfit.TRT2 = data.frame(kmfit.TRT1$strata, kmfit.TRT1$time, kmfit.TRT1$surv)
names(kmfit.TRT2) = c("TRT","time","survival")
TRT0=kmfit.TRT2[kmfit.TRT2$TRT=="drug=0", ]
TRT1=kmfit.TRT2[kmfit.TRT2$TRT=="drug=1", ]
plot(log(TRT0$time), log(-log(TRT0$survival)), pch=16, xlab="time in days using log-arithmic scale", ylab="log-log survival",type="p", xlim=c(3,10), ylim=c(-6,1), font=2, lwd=2, font.lab=2, col="Black",main="log-log curves by treatment")
par(new=T)
plot(log(TRT1$time), log(-log(TRT1$survival)), pch=3, col="grey50", ylab="",xlab="",  xlim=c(3,10), ylim=c(-6,1), font=2, lwd=2, font.lab=2)


이 그래프의 경우 우선 그룹에서 거의 직선이라는 것을 확인할 수 있지만, 두 그래프가 평행하지는 않습니다. (교차가 일어납니다.) 그렇기 때문에 Weibull 가정을 만족하지만 PH 가정과 AFT 가정을 만족하지 않으며, 두 그룹에서 p가 다르다는 알 수 있습니다.



반응형
반응형


의생명 분야에서의 신경망 모델 (Deep learning in biomedicine)

본 포스팅에서는 Nation biotechnology에서 publish된 논문을 기반으로 하여 의생명 분야에서 적용되고 있는 신경망 모델 (Deep learning) 에 대해서 살펴보려고합니다. (https://www.nature.com/articles/nbt.4233)

자율 주행차, 게임, 음성 인식, 텍스트 인식 등 Deep learning은 인공지능 연구자들과 일반 대중들의 마음을 사로잡고 있습니다. 최근에는 Deep learning은 임상 의사들의 관심도 끌고 있습니다. 지금까지의 많은 분야에서의 AI를 통한 모델링의 목표는 인간 레벨의 인공지능 (human-level AI)였습니다. 이미 사람이 잘 하는 것들을 높은 정확도로 모방하는 것입니다. 

이러한 분야들과 의생명 분야 (Biomedical) 데이터의 차이점은 의생명 분야의 데이터는 사람도 이해하기 힘들다는 것입니다. 예를 들어, genome 데이터는 사람이 맨눈으로 보고 바로 이해하기 어렵습니다. Biomedical 분야에 AI를 적용하는 것의 한가지 목표는 이렇게 사람이 쉽게 하기 힘든 분야에서 AI가 추론을 통해 사람에게 통찰력을 전해줄 수 있을까 하는 것입니다. 

딥러닝과 Biomedical 분야의 간략한 역사


실제로 이러한 것들을 실현 가능하도록하는 기회가 만들어지고 있습니다. 큰 규모의 Biomedical dataset들이 수집되고 있기 때문입니다. 예를 들어, NGS 데이터를 들 수 있습니다. Biomedical 분야에 Deep learning을 적용한다는 것은, 다양한 분야에서 각광 받고 있는 AI의 방법론인 Deep learning 라는 도구를 이용해서 이 데이터들로부터 유용한 가치를 이끌어내고, 과학적인 발견을 하고자 하는 것입니다. 


NGS (Next-generation sequencing) 데이터의 증가




머신러닝과 Deep learning의 핵심 아이디어를 간단하게 이야기해보겠습니다. 간단한 모델, 예를 들어, Linear regression과 다르게 Deep learning은 조금 더 복잡하고 유연한 모델링을 할 수 있습니다. Label을 Input feature로 바로 mapping 하는 것이 아니라 intermediate variable을 만들고, 이 intermediate variable의 function으로 output을 예측하는 모델을 만드는 것입니다. Deep learning의 강점은 이러한 Feature (독립변수)와 Label(종속변수) 사이의 어떠한 복잡한 함수관계도 유연하게 모델링할 수 있다는 것이며, 이론적으로도 이것이 가능하다는 것입니다 (Universal approximation theorem). 1980-90년대쯤 이러한 복잡한 모델링이 대한 이론적이 이미 정립이 되어있었지만, 이를 실현 가능한 하드웨어 기술이 부족해 실현할 수 없었습니다. 현 시대에서는 이러한 복잡한 모델에서 계수를 찾는 것이 계산적으로 가능합니다. 이는 이러한 Deep learning model의 수많은 계수 추정을 효율적으로 할 수 있는 다양한 알고리즘 및 라이브러리들 (Pytorch, tensorflow) 의 등장과 GPU 등의 발전으로 인한 컴퓨팅 파워의 발전의 결과입니다.  


Shallow model vs. Deep model.
 


우선 Shallow model과 Deep model의 차이에 대해 간단하게 설명을 하고 넘어가려고 합니다. Linear regression의 경우, Input feature와 Label 사이의 Linear 한 관계를 가정합니다. 그래서 예측값은 Input feature와 Weight의 linear combination으로 만들어 집니다. (여기에 bias를 더해준 값이 됩니다.) 

하지만 딥러닝의 한 종류인 Multi-layer perceptron의 경우, Layer라는 개념을 도입해서 output을 바로 예측하지 않고, Intermediate variable들을 만듭니다. Deep learning 이란 이러한 Layer 구조를 도입하여 Input feature와 Label 사이의 복잡한 함수관계를 모델링 하는 것을 말합니다. 이러한 구조를 도입하면, X와 Y의 관계가 linear한 관계가 아닌 경우, 혹은 다양한 형태의 Interaction이 존재하는 경우에, Y를 Linear regression 보다 더 잘 예측할 수 있게 됩니다. 즉, Deep model은 bias를 줄인다고 할 수 있습니다. 반면, Overfitting 등의 문제로 variance가 커질 수 있다는 것이 Deep model의 단점이기도 합니다. 


Genome sequencing 데이터에 대한 신경망 모델의 응용


다음으로는 Biomedical 데이터의 한 종류라고 할 수 있는 Genome sequencing 데이터에 어떻게 Deep learning이 어떻게 적용되는지를 알아보겠습니다. 예를 들어, motif detection (transcription factor binding site detection) 같은 분야를 예로 들어보면, 이 분야에는 Bioinformatics 분야에서 전통적으로 자주 사용되었던 Position weight matrix라는 방법이 있습니다. 어떤 문제에 대해 이러한 DNA Sequence들이 알려져 있을 때, 이것에 기반하여 아래와 같은 matrix를 만듭니다. 

Position weight matrix (position probability matrix)


이 Matrix는 해당 위치에서 어떤 sequence가 발견된 확률을 나타내는 matrix입니다. 이것을 기반으로 새로운 sequence가 들어왔을 때, 어떤 score를 내주고 이 score를 기반으로 sequence에 존재하는 어떤 pattern을 detection 할 수 있습니다. 이것은 Sequence와 어떤 pattern을 직접적으로 mapping 시킨 것으로 볼 수 있습니다. 반면 Deep learning에서의 방법은 layer를 더 만들어, Sequence와 어떤 pattern 사이에 존재할 수 있는 복잡한 관계를 모델링할 수 있습니다. 


이 그림은 CAACTT 라고 하는 sequence pattern을 찾는 CNN Model을 나타낸 것입니다. 물론 실제 상황에서는 CAACTT라고 하는 Pattern을 우리가 알 수는 없습니다. Deep learning에서는 수많은 데이터를 주고, 그 속에서 CAACTT라고 하는 Pattern을 딥러닝이 직접 학습하도록 하는 것입니다. 그리고 딥러닝의 강점이 바로 이런 것입니다. CNN에 익숙하신 분들은 잘 아시겠지만, 이 것은 1-D Convolution에 Same padding을 적용한 것으로 볼 수 있습니다. 그림에 나와있듯, Filter size는 3으로 잡고, 총 Input sequence로는 18의 길이를 갖는 sequence를 넣어준 것입니다. 이것은 일렬로 죽 늘어선 1차원 공간상의 이미지로 볼 수 있습니다. 2차원 이미지에는 W-H-(RGB) 3개의 차원이 있다면, Genome data의 경우 W-(ATCG) 2개의 차원이 있는 것입니다. 

딥러닝을 통해 Sequencing 데이터에 무슨 일을 할 수 있는가?

다음으로는 이러한 Deep learning이 Biomedical 분야에 어떻게 적용되고 있는지를 설명하였습니다. genetic data에 어떻게 되고 있는지를 먼저 예로 들었습니다. 현재, Genotype-Phenotype 관계를 규명하는데 GWAS (Genome-wise association study) 라고 하는 도구가 사용됩니다. 수많은 샘플을 보아 variant랑 phenotype의 association을 통계적으로 보는 것입니다. GWAS를 통해 variant를 찾는 것에 추가적으로 variant의 function을 연구하는 것도 한 가지 주제입니다. 왜냐하면, GWAS를 통해 찾아낸 variant (SNPs)는 그것이 질병과 인과관계를 갖는다고 보기 어렵기 때문입니다. GWAS의 경우 Mendelian disease와는 다르게 LD, 작은 effect size, regulatory network의 복잡한 구조 등으로 인해 causal variant를 찾기가 힘듭니다. 몇몇 coding 지역에 위치한 causal variant는 코돈을 통해서 그 변이의 effect를 예측할 수 있지만, non-coding variant 같은 경우, 해석이 매우 어렵습니다. Deep learning은 이 분야에서 적합합니다. 바로 transcription, splicing, regulation 등의 Molecular phenotype과 genetic variant의 관계를 보는 것입니다. 

신경망을 통한 Molecular phenotype 예측 소프트웨어, 논문

1. SPIDEX
    DNA sequence → percent-spliced-in of cassette exons across tissues
2. DeepBind
    DNA and RNA sequence → transcription factor and RNA-binding protein binding
3. Basset
    DNA sequence → DNase hypersensitivity
4. DeepSEA and DanQ
    DNA sequence → transcription factor binding
5. TITER
    DNA sequence → translation initiation sites

이것들이 현재 논문으로 나와 있거나 소프트웨어로서 구현된 variant를 통해 molecular phenotype을 예측하는 구현체들입니다. 이러한 non-coding variant로부터 molecular phenotype을 예측하는 일에는 주로 CHIP-seq이나 DNase-seq 데이터를 트레이닝 데이터로 이용하며, DNA sequence로부터, transcription factor binding이나, DNase hypersensitivity (이를 chromatin feature 라고도 합니다.) 같은 것들을 예측합니다. 


DeepSEA (Deep learning based sequence analyzer)

DeepSEA는 Deep learning based sequence analyzer의 약자인데, Genome Sequence를 input으로 받아들여, variant의 effect를 chromatin에 미치는 영향을 바탕으로 예측합니다. ENCODE, Roadmap epigenomics 와 같은 genome의 function을 찾아내려는 목적을 갖고 수집된 데이터들, Chip-seq 또는 DNase-seq 데이터를 학습해, variant가 chromatin feature 에 미치는 영향을 학습한 후, 최종적으로 학습된 모델에 wild-type과 variant가 있는 두 개의 input을 주고, output을 뽑아내서 이 output의 비율로 그 variant의 effect를 예측하는 방식입니다. 


DeepSEA의 데이터 구축 (Ground truth) 및 딥러닝 모델 구조

DeepSEA의 데이터 구축 과정 및 모델 아키텍쳐입니다. 데이터 구축 과정에서는 총 919개의 chromatin feature를 학습하도록 Ground truth가 마련되습니다. 시퀀스를 통해 이것이 chromatin feature (예를 들어, Dnase hypersensitive site) 인가? 하는 것이 바로  Y, 즉 예측하고자 하는 것입니다. X로는 whole genome을 200bp 로 나눈 후, 이 시퀀스 중 절반 이상이 919개의 chromatin feature의 peak region에 포함되면 1 아니면 0으로 코딩되었습니다. 모델로는 Convolutional neural network를 사용하였고, Regularization을 위해 L2, L1 regularization, Dropout을 사용하였습니다. 



모델 구축


DeepSEA의 목적은 총 2개로 나눌 수 있습니다.


1. 해당 Variant가 Chromatin feature에 미치는 영향 파악, 이를 통해 variant의 각각의 chromatin feature (919개) 에 대한 기능을 파악할 수 있습니다.

2. 해당 Variant의 overall한 functional prediction 


1을 위해서 variant가 없는 sequence (1000bp 단위) 와 variant가 있는 sequence를 모델에 넣어서 log(P(reference)/P(alternative))를 통해 해당 chromatin에 variant가 미치는 영향을 파악합니다. 2를 위해서는 총919개의 chromatin feature에 대한 예측값과 함께 Evolutionary conservation score를 이용합니다. 저는 이 부분이 DeepSEA 가 대단한 부분이라고 생각합니다. 기존에 알려진 과학적 지식을 Deep learning 모델에 통합하여 더욱 잘 functional score를 예측하는 것이죠. 일종의 앙상블 모델이라고 볼 수 있습니다. 이를 통해 딥러닝 모델로부터 발생할 수 있는 오버피팅을 방지하고 더욱 robust한 모델이 될 수 있습니다. 



모델 테스트


위 링크에 방문하면 실제로 웹으로 구현된 DeepSEA 를 이용해볼 수 있습니다. variant를 나타내는 VCF file을 인풋으로 넣어주면, VCF file에 있는 각각의 variant에 대해, 919개의 chromatin feature에 대한 영향과, overall한 functional prediction score를 구합니다. 

신경망을 통한 Phenotype 을 예측하는 것이 가능한가?

Sequence를 통해 바로 phenotype을 예측할 수 있지 않겠냐 라는 의문이 들 수 있습니다. 물론, 최종적으로 나아가야할 목표는 sequence를 통해 phenotype 보는 것입니다. 하지만 아직까지 딥 러닝이 그 정도의 수준은 아닌듯 합니다. Molecular phenotype은, genotype에서 phenotype으로 이어지는 복잡한 메커니즘 중 최하단에 있다고 말할 수 있는 것이고, 아직까지 그것조차 제대로 해결되어지고 있지 않습니다. 그 다음으로 해결할 과제는 network-level gene interaction, physiological process 등 여러가지 해결해야할 과제들이 남아있습니다. 하지만 본 논문에서는 궁극적인 목표는 딥러닝에 genotype-phenotype 데이터를 학습시키고, 여기에 여러가지 생물학적 지식, 실험 데이터를 합쳐서 바로 phenotype을 예측하는 것이라고 주장하고 있습니다. 저는 DeepSEA에서의 사례처럼 Evolutionary conservation score 처럼, 학습된 딥러닝 모델에 이러한 explicit한 "지식" 들을 어떻게 통합하냐가 하나의 해결해야할 과제로 보입니다.


Medical Image에서의 신경망의 응용

다음으로는 medical image 분야에서의 딥러닝의 응용입니다. 이 분야는 딥러닝이 가장 직접적으로 응용이되고 있는 분야이고, 실제로 임상에서 활용이 되고 있는 분야이기도 합니다. Medical image의 특징은, Multi-modal, 즉 MRI, X-ray, CT 등 다양한 방법으로 이미지가 얻어지고, 같은 MRI 데이터라도 세팅값, 기기 종류에 따라 intensity가 모두 다릅니다. 또한, CT 같은 경우 3D 이미지 이기 때문에 다루기 까다롭습니다. 하지만 그럼에도 불구하고 딥러닝이 가장 성공을 한 분야이기도 합니다.

신경망을 Medical Image에 적용할 때 발생하는 이슈들

1. 이미지에 대한 높은 수준의 해석은 Automation이 힘듭니다. 이는 사람에 있어서도 사람마다 주관적인 기준이 있기 때문에 Intra-class variation이 크기 때문입니다. 

2. 또한 딥러닝 자체가 블랙박스의 성격을 띈다는 것입니다. 특히, 이미지 진단의 경우에는 이해 관계가 매우 크기 때문에, 딥 러닝의 결과를 어떻게 설명하느냐가 매우 중요합니다. 이 논문에서는 딥러닝이 그렇게 예측한 이유, 그 지역을 highlighting을 해주는 등의 전략이 필요하다고 언급하였습니다.

3. 세 번째는 사람의 예측값을 Ground truth로 해서 트레이닝 했을 때, 사람의 성능을 뛰어넘기 힘들다는 것입니다. 물론, 딥러닝은 계속 같은 값을 내기 때문에 Reliability는 좋습니다. 다만, 그 트레이닝 데이터가 한 사람의 기준에만 맞춘 경우, 다른 데이터를 대상으로 했을 때, bias가 생길 수 있다는 것입니다. 그러므로 Multi-expert consensus가 중요하다고 할 수 있습니다. 

국내 Medical Image 응용

국내에서는 2018년도에 Vuno에서 최초로 식약처에서 의료기기 사용허가를 받았습니다. 성장기 소아에서 X-ray 영상에서 인공지능을 통해 골연령을 자동으로 측정해주는 Vunomed-Boneage 소프트 웨어를 통해서입니다. Vuno에서는 이 방법의 효율성을 보이기 위해 임상시험 논문을 냈습니다. 결과에 따르면 AI가 2명의 의사들보다 consensus와의 concordance가 더 좋았고, 특히 의사가 AI 를 보조적으로 활용했을 때, 그렇지 않았을 때보다 concordance가 증가하는 결과를 보였습니다. (이 부분에 대해 궁금하신 분들은 최윤섭님의 유투브를 참고하시기 바랍니다. https://www.youtube.com/watch?v=wqXzmChH3N0&t=349s)




딥러닝과 실제 세계의 괴리

다음으로는 Deep learning이 실제 현실에 사용되었을 때 발생할 수 있는 문제점에 대한 것입니다. 

1. 가장 중요한 것은 반드시 Deep learning의 성능이 보장 되어야한다는 것입니다. 
    • 이를 위해서는 Performance 측정을 해야합니다. C.V 나 hold out validation 같은 방법을 통해 충분한 validation이 이루어져야 합니다.
    • 두 번째는 Deep learning을 Overfitting이 큰 문제이기 때문에, 모델의 불확실성이 어느정도인지를 보여주어야 한다는 것입니다. 통계적인 모델처럼 딥러닝은 결과의 신뢰구간을 통계적인 방법으로 얻어낼 수 없기 때문에 부트스트랩이나 베이지안 방법 등을 통해 모델의 신뢰구간을 추정할 수 있습니다. 

2. 또한 딥러닝의 문제점은 딥러닝이 목표하는 바와 실제 목표하는 바가 다를 수 있다는 것입니다. 이것이 Target mismatchloss function mismatch 입니다. 예를 들어, 임상에서는 종양의 크기가 일정한 임계치를 넘는 것이 중요한데, 딥러닝 모델을 트레이닝 할 때는 Intersect over union을 최소화 하기 위해 트레이닝을 보통 이용하곤 합니다. 이 경우에 딥 러닝 모델은 실제로 원하는 결과에서 bias가 생길 수 있습니다.

3. 다음으로는 오로지 딥러닝은 현재 이용가능한 데이터에 기반하여 예측 모형을 만드는 것이기 때문에 selection biasconfounding 이 생길 수 있고, 이 경우에 Causality 는 추론하는것이 매우 힘들다는 것입니다. 예를 들어, 여자라는 단어는 인문학, 남자라는 단어는 이공계와 연관시키거나, 흑인을 백인보다 더 위험하다고 학습할 수 있습니다. 


신경망 모델이 신뢰를 얻기 위해서는?

1. Performance를 보장해야합니다.
    • Stakeholder가 원하는 메트릭을 제공해야 한다.
    • Performance를 보장하기 위해서는 데이터 가공, 모델 선택, 오버피팅, 아웃라이어 제거, 혼란 변수등을 잘 해야한다는 것입니다.
    • 원하는 메트릭이 Stakeholder 마다 다르기 때문에 여러가지 메트릭에 대해 성능을 테스트해서 로버스트 한지를 봐야합니다.


2. Stakeholder가 그것을 사용할만한 Rationale이 있어야합니다.

  • 이들은 Small-test를 한다거나 직관, 사고실험 등으로 신뢰할만한지를 판단하기 때문에 이런것들에 도움을 주어야 합니다.
  • 가장 좋은 Rationale은 인과적인 설명입니다. 그래서 causal relationship 에 대한 설명이 반드시 이루어져야 합니다.

결론

1. 의생명 분야는 복잡하기 때문에 정확하게 이해하기 힘들고, 그렇기 때문에 AI의 서포트가 필요합니다.

2. 딥러닝은 수많은 데이터셋을 포함하여 복잡한 모델링을 하는데 유망한 방법이고, 그렇기 때문에 Deep learning 은 의생명 분야에서 중요한 역할을 할 것입니다. 


반응형
반응형


Time-dependent variable의 정의 


Definition : 생존 분석에 있어서 시간에 따라 변수의 값이 바뀌는 변수를 time-dependent variable이라고한다. 


예 : 흡연 여부, 직장에서의 지위 등


Extended cox model : Time-dependent variable을 생존분석에서 사용하는 방법 


개념 : 만약 시간에 따라 변수의 값이 변화하는 변수가 있다면 extended cox model을 수행하여야한다. 


- 기존 데이터의 형태


ID
clinic
event
time
prison
dose
111428050
211275155
311262055
411183030
511259165
611714055

- Extended cox model을 위한 데이터의 형태


ID
clinic
prison
dose
ID2
start
time
event
110501070
1105017130
11050113170
11050117190
11050119260
11050126290

모든 time에 대해 데이터를 쪼개서 start와 time으로 정의한다. 하나의 row를 시간에 따라 쪼개므로 한 사람당 여러개의 row가 생기게 된다. 만약 해당 시간 내에서 event가 발생한 경우, event는 1이된다.


library(data.table)
library(survival)
data <- read.csv("addict.csv")

Extended cox model을 위한 데이터의 형태 만드는 법

data.cp <- survSplit(data, cut=data$time[data$event==1], end="time", event="event", start="start", id="ID2")

만약 아래와 같이 logdose라는 time-dependent variable이 있다고 하면 

data.cp$logdose <- data.cp$dose * log(data.cp$time)

Extended cox model 은 아래와 같이 수행할 수 있다.

Y = Surv(data.cp$start, data.cp$time, data.cp$event) coxph(Y ~ clinic + logdose +prison+ cluster(ID2), data=data.cp)


Time-dependent variable의 활용 1 : PH (proportional hazard) Assumption check


개념 : PH Assumption에 위반이 되는 것이 의심되는 변수가 있을 때, 이 변수에 대한 time-dependent variable을 정의함으로서 PH 가정을 체크할 수 있다. 


ID
clinic
event
time
prison
dose
111428050
211275155
311262055
411183030
511259165
6117140

55

만약에 위와 같은 데이터에 대해서 dose에 대해 PH Assumption 가정이 위반된다고 해보자. 즉, 이말은 시간에 따라 dose의 hazard ratio가 달라진다는 것이다. 이를 테스트하기위해 dose*g(t)라는 새로운 변수를 정의하고 이 변수의 계수가 유의한지 확인하는 방법을 통해 해당 변수에 대한 PH Assumption을 체크할 수가 있다. 


Y = Surv(data.cp$start, data.cp$time, data.cp$event)
coxph(Y ~ clinic + dose + logdose +prison+ cluster(ID2), data=data.cp)
Call:
coxph(formula = Y ~ clinic + dose + logdose + prison + cluster(ID2), 
    data = data.cp)

            coef exp(coef) se(coef) robust se     z       p
clinic  -1.01988   0.36064  0.21542   0.23637 -4.31 1.6e-05
dose    -0.08262   0.92070  0.03598   0.02960 -2.79  0.0053
logdose  0.00862   1.00865  0.00645   0.00525  1.64  0.1007
prison   0.34063   1.40584  0.16747   0.15972  2.13  0.0329

Likelihood ratio test=66.35  on 4 df, p=1e-13
n= 18708, number of events= 150 
   (1379 observations deleted due to missingness)


위 결과에서 logdose의 p-value > 0.05이므로, dose가 PH Assumption을 만족한다고 결론내릴 수 있다. 위 방법은 Wald test를 한 것이며, 다른 방법으로는 Likelihood ratio test를 통해 p-value를 구할 수도 있다. 


Likelihood ratio test

Y = Surv(data.cp$start, data.cp$time, data.cp$event)
mod1 <- coxph(Y ~ clinic + dose + logdose +prison+ cluster(ID2), data=data.cp)
mod2 <- coxph(Y ~ clinic + dose +prison+ cluster(ID2), data=data.cp)

print(mod1$loglik[2])
print(mod2$loglik[2])

LRT <- (-2) * (mod2$loglik[2]-mod1$loglik[2])
pvalue <- 1 - pchisq(LRT, 2) 

이는 logdose를 포함한 full model과 logdose를 포함하지 않은 restricted model의 likelihood ratio를 구하고 이것이 카이제곱 분포를 따른다는 것을 이용한 검정이다. p-value를 계산한 결과 0.4 정도로 위의 wald test와 마찬가지로 logdose는 의 계수는 유의하지 않았다. 


따라서 dose에 대해 PH Assumption이 어긋나지 않는다고 결론 내릴 수 있다.



Time-dependent variable의 활용 2 : PH Assumption 만족 안하는 경우, 시간에 따라 달라지는 Hazard ratio 모델링


개념 : 어떤 변수에 대하여 heavy side function을 이용한 time-dependent variable을 정의함으로서 시간에 따라 달라지는 hazard ratio를 모델링할 수 있다. 



heavy side function은 어떤 값을 기준으로 그 이상일 때는 1, 미만일 때는 0을 돌려주는 함수를 말한다. 


예제 : 만약 clinic에 대해 시간에 따라 달라지는 HR을 모델링 하고 싶다고 해보자. 


fit <- survfit(Surv(data$time, data$event)~data$clinic)
plot(fit)

이것이 clinic=1과 clinic=2일 때 각각 그린 카플란 마이어 생존함수 추정값이다. 위 그림에서 hazard ratio가 시간에 따라 바뀐다는 것이 의심스러울 수 있다. 이 때, 어떤 시점을 기준으로 heavy side function을 이용한 time-dependent variable을 정의함으로써 이를 모델링할 수 있다. 


365를 기준으로 데이터를 쪼갠다. 

data.cp365 <- survSplit(data, cut=365, end="time", event="event", start="start", id="ID2")
ID
clinic
prison
dose
ID2
start
time
event
11050103650
1105013654281
21155202751
31055302621
41030401831
51165502591
data.cp365$hv1 <- data.cp365$clinic * (data.cp365$start < 365)
data.cp365$hv2 <- data.cp365$clinic * (data.cp365$start >= 365)

위와 같이 두 개의 heavy side function을 원래 clinic의 값에 곱해준 것을 두 개의 변수로 정의한다.  


ID
clinic
prison
dose
ID2
start
time
event
hv1
hv2
1105010365010
110501365428101
2115520275110
3105530262110
4103040183110
5116550259110
6105560365010
610556365714101

Extended cox model

Y365 <- Surv(data.cp365$start, data.cp365$time, data.cp365$event)
coxph(Y365 ~ dose + hv2 + hv1 + prison + cluster(ID2), data=data.cp365)


Call:
coxph(formula = Y365 ~ dose + hv2 + hv1 + prison + cluster(ID2), 
    data = data.cp365)

           coef exp(coef) se(coef) robust se     z       p
dose   -0.03548   0.96514  0.00643   0.00652 -5.44 5.3e-08
hv2    -1.83052   0.16033  0.38595   0.39838 -4.59 4.3e-06
hv1    -0.45937   0.63168  0.25529   0.25998 -1.77   0.077
prison  0.37795   1.45929  0.16841   0.16765  2.25   0.024

Likelihood ratio test=74.25  on 4 df, p=3e-15
n= 360, number of events= 150 


이 때 결과를 보면 hv1의 HR은 0.63, hv2의 HR은 0.16이다. 이 말은 clinic 1과 clinic2의 hazard ratio가 365미만에서는 0.63이었는데, 365 이상에서는 0.16이라고 해석할 수 있다. 즉, 두 clinic에서 보이는 생존률에 대한 경향성이 시간이 지날 수록 강해지고 있는 것이다. 이는 위의 카플란 마이서 생존곡선에서도 확인할 수 있다. 


반응형
반응형


Cox 비례위험 모형의 가정 체크


개념 : Cox 비례위험 모형은 "비례위험 가정" 을한다. 


비례위험가정은 시간에 따라 어떤 변수에서의 Hazard ratio가 일정해야한다는 것이다.

비례위험가정이 어긋나면, Stratified cox regression 또는 time-dependant variable 정의를 통한 extended cox regression을 수행할 수 있다. 


비례위험 가정의 체크는 아래방식으로 할 수 있다. 


1. Log-log plot

2. Observed-expected plot

3. Goodness of fit test


본 포스팅에서는 R의 survival, survminer 패키지를 통한 실습을 통해 cox 비례 위험 모형의 가정을 체크하는 방법을 알아본다. 


addict.csv


library(data.table)
library(survival)
data <- read.csv("addict.csv")

head(data)
ID
clinic
event
time
prison
dose
111428050
211275155
311262055
411183030
511259165

먼저 데이터의 형태는 위와 같이 생겼다. 


관심변수 : clinic 에 대하여 Kaplan-Meier 플롯 그리기

# Treatment 별로 그룹지어서 KM Estimation
fit = survfit(y~clinic, data=data)
par(mai=c(1,1,1,1))
plot(fit,main="KM Curve", xlab="Time(Week)", ylab=expression(paste(hat(S),"(t)")), col=c("blue", "red"))


우리는 이것이 비례위험 가정을 만족하는지를 확인하고 싶다. 비례위험 가정을 만족하려면 모든 timepoints에서 Hazard ratio가 같아야한다. hazard란 f(t)/S(t) (참고 : https://3months.tistory.com/349?category=743476) 로 위 그림만 봐서는 가정을 만족하는지 한 눈에 알 수 없다.  


로그로그 플롯


개념 : 로그로그 플롯을 그리고 그룹간에 평행하게 나오면 비례위험가정을 만족한다. 

plot(fit, fun="cloglog", lty=1:2, col=c("Black", "Grey50"), lwd=2, font=2, ylim=c(-3,2), font.lab=2, main="log-log KM curves by Rx", ylab="log-log survival", xlab="time in weeks using logarithmic scale")
legend("bottomright",lty=1:2,legend=c("Group1","Group2"),bty="n", text.font=2, lwd=2, col=c("Black", "Grey50"))


관찰값-기댓갓 플롯


개념 : Kaplen-Meier 생존 함수와 Cox regression을 통해 구한 생존함수를 동시에 그려본다.


이 때, Expected plot은 Cox 비례위험 모형에서 나오는 생존함수로 관찰한 생존함수와 모양의 차이가 많이나면 비례위험 가정을 만족하지 않는 것이다. 

kmfit <- survfit(y~clinic, data=data)
#observed
plot(kmfit, lty="dashed", col=c("Black", "Grey50"), lwd=2, font=2, font.lab=2, main="Observed Versus Expected Plots by Clinic", ylab="Survival probability", xlab="Survival time")
par(new=T)
#expected
exp <- coxph(y~clinic, data=data)
new_df <- data.frame(clinic=c(1,2))
kmfit.exp <- survfit(exp, newdata=new_df)
plot(kmfit.exp, lty="solid", col=c("Black", "Grey50"), lwd=2, font=2, font.lab=2)



Goodness of fit test


개념 : Goodness of fit test(GoF Test) 는 관찰값-기댓값의 차이를 통계적으로 검정하는 방법이다. (카이스퀘어 분포 이용)

# Gooodness of fit test
mod1 <- coxph(y~clinic, data=data)
cox.zph(mod1, transform=rank)

plot(cox.zph(mod1, transform=rank), se=F, var="clinic")
abline(h=0, lty=3)
plot(cox.zph(mod1, transform=rank), se=F, var="prison")
abline(h=0, lty=3)


        rho chisq       p
clinic -0.255  10.5 0.00118

p-value가 0.001이므로 clinic 변수는 비례위험 가정을 만족하지 않는다. 이는 그림을 통해서도 확인할 수 있다. 위 그림에서 직선이 평행하지를 확인하면되는데, 직선은 추정된 beta값을 의미하는데 직선이 평행하지 않고 어떠한 경향성이 보이면 beta 값이 시간에 따라 바뀐다는 의미로 비례위험 가정에 어긋난다. 




참고 : https://www.r-bloggers.com/cox-model-assumptions/

반응형
반응형


Stratified Cox 비례위험모형


개념 : 비례위험 가정을 만족하지 않는 변수의 경우, 층화를 하여서 분석하여야함


비례 위험 가정이란 ? => Hazard ratio가 시간에 따라 모두 같아야한다는 것


기본적인 Cox 비례위험모형의 식은 다음과 같이 쓸 수 있다. 두 가지 변수 dose와 Sex 가 있다고 해보자. (각각 연속형, 범주형 변수이다.)


$$ h(t, X) = h_0(t)exp(\beta_1 dose + \beta_2 Sex) $$


예를 들어, 성별 (Sex)에 대해서 비례위험 가정이 만족하지 않는다고 해보자. 


$$ h_{1} (t, X) = h_{1}(t)exp(\beta_1 dose) $$

$$ h_{2} (t, X) = h_{2}(t)exp(\beta_1 dose) $$


그러면 위와 같이 Sex에 따라 따로 따로 모형을 fitting 시키는 것이 Stratified Cox regression의 기본 개념이라고 할 수 있다. 또한 두 식의 dose에 대한 beta 값이 같다는 것을 확인할 수 있다. Sex에 대해 층화를 한 후, 공통 beta 값을 추정하는 것이라고 할 수 있다. baseline hazard 인 h(t) 값만 두 식에서 다르게 추정한다. 이는 Sex의 hazard ratio를 추정하는 것이 아니라 비모수적 방법으로 각각의 Sex의 h(t)를 추정하는 것으로 이해할 수 있다. 


R에서의 구현 (survival 라이브러리)


coxph(Surv(time, event) ~ dose + strata(sex), data=data)


Stratified cox 모형의 한 가지 이슈는 층화한 변수가 다른 변수와 교호작용이 있는 경우이다. 이 경우에는 두식에서 공통된 beta를 추정하는 것이 아니라, 각각의 beta를 추정하는 것이 좋다. 이를 Interaction model이라고 한다. 


$$ h_{1} (t, X) = h_{1}(t)exp(\beta_{1} dose) $$

$$ h_{2} (t, X) = h_{2}(t)exp(\beta_{2} dose) $$


Interaction model은 R에서 다음과 같이 각각의 strata 별로 모델을 따로 만들어주면 된다. 
coxph(Surv(time, event) ~ dose, data=data[data$sex== 0,])
coxph(Surv(time, event) ~ dose, data=data[data$sex== 1,])

또는 아래와 같은 방식으로 표현할 수 있다.


$$ h_g (t, X) = h_g (t)exp(\beta_{1} dose + \beta_2(Sex*dose)) $$


위 식에서 (Sex=0 or 1) 이고, Sex=1일 때, beta2 값이 beta1 값과 더해져 dose의 beta가 된다. 이렇게 식을 쓰면 beta2를 이용하여 교호작용이 유의한지에 대한 검정을 할 수 있다. 교호작용을 검정하는 방법에는 두 가지 방법이 있다.


1. Wald test : 교호작용에 대한 beta값이 통계적으로 유의한지 검정

2. Likelihood ratio test : 두 모델의 likelihood ratio를 이용해 likelihood의 증가값이 통계적으로 유의한지 검정


결론적으로 Stratified cox regression 의 장점과 단점은 아래와 같이 요약해볼 수 있다.


장점 : PH 가정이 맞지 않는 변수에 대하여 층화 (Stratification)를 수행하여 분석하기 때문에, 다른 계수 추정을 더욱 신뢰성 있게 할 수 있다. 

단점 : 층화를 한 변수에 대해서는 HR을 추정할 수 없다. 층화한 변수와 다른 변수와의 교호작용을 고려하여야한다. 교호작용이 있는 경우 추정된 계수값은 바이어스가 생긴다. 







반응형
반응형

Competing Risk를 고려한 생존 분석


Competing risk 분석은 생존분석에서 관심 event 외의 다른 event가 있을 때, 분석하는 방법입니다. 예를 들어, 특정 약이 폐암으로 인한 사망을 줄이는가에 대해 관심을 갖고 연구하려고 하는데 심혈관 질환, 뇌혈관 질환 등으로 사망한 경우 Competing risk 분석을 통해 이 약이 폐암으로 인한 사망은 줄여도, 다른 사망에 대해서는 반대의 효과를 줄 수 있기 때문에 이를 확인할 필요가 있습니다.


Competing risk는 기본적인 생존분석 모형의 확장된 버전입니다. 데이터의 형태는 기본 생존 분석의 [time to event, event( censoring, event 를 나타내는 변수)]를 따르지만, vent가 censoring, event 0/1 로 나누어진 것이 아니라, 3개 이상의 범주형 변수를 갖게 됩니다. 


R의 경우 timereg, cmprsk 를 이용하여 competing risk 분석을 수행할 수 있습니다. 


먼저 데이터를 읽어옵니다. 

fol <- read.csv("data.csv", header=TRUE)

age
path1
hgb
ldh
clinstg
blktxcat
relsite
ch
rt
survtime
stat
dftime
dfcens
resp
stnum
56NH04140NA21BY0.698152010.2381930181CR1
36NH02130NA21DY14.5023956112.4188911701CR2
39NH02140NA23YY4.914442210.0027378511NR3
37NH03140NA11Y15.6851472115.6851471591CR4
61NH04110NA22Y0.235455210.0027378511NR5
69NH02120NA11Y8.418891218.4188911701CR6

이 데이터에는 1,2 두 가지의 사망원인이 있습니다. 


cause
  0   1   2 
193 272  76 



1. 관심 사건 외 다른 이벤트들을 censoring으로 취급하여 분석

library(data.table)
fol <- setDT(fol)

fol$y1 = fol$cause
fol$y2 = fol$cause
fol[cause==2, y1:=0]
fol[cause==1, y2:=0]
fol[y2==2 , y2:=1]

원래는 0이 censoring 인데, 관심요인이 1일 때는 2를 censoring, 관심요인이 2일 때는 1을 censoring으로 바꾸어주는 코드입니다. 이 때, data.table으로 dataframe으로 변환한 후, := 키워드를 이용하였습니다.


① Cause = 1에 대한 Model (Cause=2 를 Censoring 취급)

=> age, hgb, clinstg를 보정해서 blktxcat의 effect 파악

library(survival)

cause1 <- Surv(fol$dftime, fol$y1)
cox1 <- coxph(cause1 ~ age+hgb+blktxcat+clinstg, data=fol)


Call:
coxph(formula = y0 ~ age + hgb + blktxcat + clinstg, data = fol)

  n= 541, number of events= 272 

             coef exp(coef) se(coef)     z Pr(>|z|)    
age      0.023784  1.024069 0.004733 5.025 5.02e-07 ***
hgb      0.003078  1.003083 0.004075 0.755 0.449974    
blktxcat 0.207383  1.230454 0.057329 3.617 0.000298 ***
clinstg  0.329831  1.390733 0.138171 2.387 0.016981 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

         exp(coef) exp(-coef) lower .95 upper .95
age          1.024     0.9765    1.0146     1.034
hgb          1.003     0.9969    0.9951     1.011
blktxcat     1.230     0.8127    1.0997     1.377
clinstg      1.391     0.7190    1.0608     1.823

Concordance= 0.623  (se = 0.019 )
Rsquare= 0.082   (max possible= 0.997 )
Likelihood ratio test= 46.23  on 4 df,   p=2e-09
Wald test            = 45.16  on 4 df,   p=4e-09
Score (logrank) test = 45.84  on 4 df,   p=3e-09



② Cause = 2에 대한 Model (Cause=1 를 Censoring 취급)

=> age, hgb, clinstg를 보정해서 blktxcat의 effect 파악

cause2 <- Surv(fol$dftime, fol$y2)

cox2 <- coxph(cause2 ~ age+hgb+blktxcat+clinstg, data=fol)

summary(cox2)
Call:
coxph(formula = cause2 ~ age + hgb + blktxcat + clinstg, data = fol)

  n= 541, number of events= 76 

             coef exp(coef) se(coef)     z Pr(>|z|)    
age      0.087125  1.091033 0.011440 7.616 2.62e-14 ***
hgb      0.005945  1.005962 0.008452 0.703    0.482    
blktxcat 0.164626  1.178952 0.109133 1.508    0.131    
clinstg  0.353410  1.423915 0.285801 1.237    0.216    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

         exp(coef) exp(-coef) lower .95 upper .95
age          1.091     0.9166    1.0668     1.116
hgb          1.006     0.9941    0.9894     1.023
blktxcat     1.179     0.8482    0.9519     1.460
clinstg      1.424     0.7023    0.8132     2.493

Concordance= 0.764  (se = 0.039 )
Rsquare= 0.131   (max possible= 0.759 )
Likelihood ratio test= 75.68  on 4 df,   p=1e-15
Wald test            = 62.84  on 4 df,   p=7e-13
Score (logrank) test = 67.68  on 4 df,   p=7e-14



2. Cumulative incidence curve를 통한 분석


Cumulative incidence curve의 정의 


개념 : Cumulative incidence curve란 competing risk를 고려한 1-생존함수 의 추정값 이다. 


$$ CIC_c(t) = P(T_c \lt t) $$


위 식에서 CIC란 어떤 사람의 c라는 event의 발생 시각인 Tc가 t보다 작을 확률입니다. 만약 competing risk가 없다면 이것은  간단하게 계산됩니다. 일반적으로 competring risk 가 없는 상황에서는 이것은 1 - (Kaplen-Meier 생존함수)로 추정하게 됩니다. 하지만 competing risk가 있는 경우, 다른 원인으로도 사람이 죽을 수 있기 때문에 Kaplen-Meier 방법을 그대로 사용하면 특정 시점 t 이전에 event 가 발생했을 확률이 과대추정됩니다. 


CIC 방법에서는 proportion of dying을 아래와 같은 방식으로 추정하게 됩니다. 


$$ CIC_c(t) = \int_{0}^{t} h_c(u)S(u) du $$


이 방식을 직관적으로 이해하면 Incidence를 모든 t에 대해서 더해준다고 이해할 수 있는데 incidence는 특정 시점에서의 cause-specific hazard와 생존함수를 곱해준 값입니다. 이 때, 생존함수는 인구집단에서의 생존함수입니다. 만약 이 생존 함수를 해당 cause에 대한 생존함수를 사용한다면 1-KM과 같은 값이 나오게 됩니다. 하지만 인구집단에서의 생존함수를 사용하여 competing risk를 고려하는 것으로 이해할 수 있습니다. 


Cumulative incidence curve는 cmprsk 라는 라이브러리를 통해 분석할 수 있습니다. Cumulative incidence는 cause-specific hazard function으로 계산되며, proportion of dying을 계산해줍니다. 이 때, 관심 요인 blktxcat을 지정해줍니다. blktxcat을 약의 종류라고 생각을하고 0,1,2,3 총 4개의 약의 종류가 있는 것입니다. 이 4개의 약이 1,2 두 가지 사망 원인에 어떻게 작용하는지를 아래 표를 통해 확인할 수 있습니다=> 각각의 strata 별로 proportion of dying의 예측값을 보여주고 있습니다. 


예를 들어, 25달이 지난 후, 사망원인 1을 보면, 0의 약을 쓴 사람은 52%가 죽은 반면, 3 약을 쓴 사람은 66%가 죽었습니다.  

library(cmprsk)
CIC <- cuminc(fol$dftime, fol$cause, fol$blktxcat)
CIC
Tests:
       stat           pv df
1 17.189363 0.0006461085  3
2  4.596607 0.2038333549  3
Estimates and Variances:
$est
             5         10         15        20        25        30
0 1 0.28456914 0.40301775 0.47654623 0.5235252 0.5235252 0.5235252
1 1 0.36620667 0.44808409 0.47719815 0.5096313 0.5541112        NA
2 1 0.42528724 0.54412907 0.59678622 0.5967862 0.5967862        NA
3 1 0.50604626 0.63733974 0.66670849 0.6667085 0.6667085        NA
0 2 0.02540834 0.08048914 0.15465458 0.1546546 0.1546546 0.2619280
1 2 0.11604642 0.16329519 0.18920323 0.2309031 0.2679696        NA
2 2 0.05245607 0.07941283 0.13288099 0.2003667 0.2409361        NA
3 2 0.03960396 0.06897272 0.09834147 0.1374998 0.2353957        NA

$var
               5           10          15          20          25          30
0 1 0.0010438691 0.0013684404 0.001665324 0.002070841 0.002070841 0.002070841
1 1 0.0022693095 0.0025757378 0.002723652 0.003459139 0.004840443          NA
2 1 0.0018165618 0.0021166396 0.002288543 0.002288543 0.002288543          NA
3 1 0.0025253539 0.0027149325 0.002701947 0.002701947 0.002701947          NA
0 2 0.0001266488 0.0004372677 0.001007219 0.001007219 0.001007219 0.006742183
1 2 0.0010075372 0.0014431850 0.001683726 0.002346285 0.003481490          NA
2 2 0.0003774854 0.0005948500 0.001226766 0.002385591 0.003610060          NA
3 2 0.0003819491 0.0007880136 0.001162941 0.001791598 0.011245328          NA


plot(CIC, xlab="month", ylab="proportion dying")



위와 같이 그림도 그릴 수 있습니다. 위쪽에 위치한 4개의 그래프가 사망원인 1에 대한 4가지 약의 효과 그래프이고, 아래쪽에 있는 4개의 그래프가 사망원인 2에 대한 것입니다. 전반적으로 사망원인 1로 죽는 사람이 많다는 것을 확인할 수 있습니다. 또한 약의 경우 사망원인 1에 대해 어느 정도 연관성이 있는 것으로 보입니다. 각 4개의 약에 대해 proportion of dying에 차이를 보여줍니다. 하지만 사망원인 2에 대해서는 약과 별로 상관 없는 것을 확인할 수 있습니다. 



3. Conditional probability function

library(Cprob)
CPC <- cpf(Hist(dftime, cause)~blktxcat, data=fol, failcode=1)
CPC
plot(CPC, xlab="month", ylab="proportion dying")

Conditional probability function도 마찬가지로 그릴 수 있습니다. 이 때, failcode로 관심 event를 지정해줍니다. 


4. Proportional-odds Model for the Conditional Probability Function

CPC_reg=cpfpo(Hist(dftime, cause)~blktxcat+age, data=fol, failcode=1, tis=seq(0, 70, 5)) CPC_reg

Warning message:

“glm.fit: algorithm did not converge”

Test for non-significant effect 

                  stat       pvalue

(Intercept) -26.769122 0.000000e+00

blktxcat      3.952040 7.748788e-05

age           5.041975 4.607511e-07


 Test for constant fit 

                   coef     exp.coef     SE.coef       stat     pvalue

(Intercept) -9.51827834 7.349609e-05 0.355569317 -24.964365 0.00000000

blktxcat     0.27674405 1.318829e+00 0.070025626   1.790873 0.07331363

age          0.02991248 1.030364e+00 0.005932691  10.309364 0.00000000


Conditional probability function의 proportional odds 모델을 이용하면 regression을 통해 각 factor들의 odds ratio를 구할 수 있습니다. 



data.csv



데이터 출처


Thomas H. Scheike, Mei-Jie Zhang

Title: Analyzing Competing Risk Data Using the R timereg Package


Journal of statistical software

https://www.jstatsoft.org/article/view/v038i02

반응형
반응형


생존분석은 왜 하는가?

생존분석은 Censoring을 고려하여 Time to event에 대해 분석하기 위해 수행한다. 어떤 약의 효과를 판단한다고 해보자. 만약에 암에 걸린 사람을 대상으로 A라는 약을 처방하였는데, 이 약의 효과를 판단하기 위해서 여러가지 디자인을 해볼 수 있다. 

생존분석의 접근법은 약을 처방 받은 그룹 X, 처방받지 않은 그룹 Y에 대해서 생존 시간을 비교하고, 이 생존시간의 차이가 유의한지를 확인한다. 그럼 T-test로 할 수 있다고 생각할 수 있지만, 생존분석에서는 Censoring을 고려한다. 이것이 무슨말이냐하면 중도탈락한 데이터라도 그 데이터가 있었던 시점까지의 정보는 활용한다는 것이다. 생존분석은 중도절단된 자료의 부분적 정보를 최대한 이용한다.

즉, 기존 통계 모형을 쓰지 않고 생존분석을 쓰는 이유는 다음과 같이 두 가지로 정리할 수 있다.

1. Time to event를 알고 싶고,

2. Censoring 데이터를 고려하고 싶을 때


이는 linear regression, t-test, logistic regression 등의 다른 통계적 방법으로는 해결할 수 없다.

또한 생존분석의 목적은 다음과 같이 정리할 수 있다.

1. 어떤 사람의 Time to event를 예측 하고 싶을 때
2. 둘 이상의 그룹 간의 Time to event (생존예후) 를 비교하고 싶을 때
3. 변수들이 Event (생존)에 미치는 영향 파악 및 비교

Censoring이란 무엇인가?

Censoring은 생존분석에서 중요한 개념으로 두 종류로 나누어볼 수 있다.

1. left censoring : 관찰 기간보다 event가 발생 시각빠른 경우를 말한다.  

● 만약, 나무늘보가 언제 나무에서 내려오는지를 관찰하려고 한다. 근데 나무늘보는 항상 사람이 잠드는 새벽시간에만 땅으로 내려온다고 해보자 (예를 들어, 5am 정도). 그리고 사람은 아침 9시에 일어나서 나무늘보를 관찰한다. 그렇다면 사람은 평생 나무늘보다 땅으로 내려오는 것을 관찰할 수 없을지도 모른다. 이는 관찰기간 (9am~) 보다 event의 발생시각(5am) 이 전에 있기 때문이다. 
● 언제 담배를 처음 피었는가?
● 치매의 발생 (언제 정확히 발생했는지 알기 힘들다.)

2. right censoring :   관찰기간보다 event 발생 시각느린 경우를 말한다. 

● 가장 일반적인 censoring의 의미이다. censoring하면 보통 right censoring을 의미하는 경우가 많다. 
● 연구자는 어떤 연구 참여자를 평생 관찰할 수 없다. 예를 들어, 흡연자와 비흡연자의 폐암 발생까지의 시간을 비교하는 연구를 해본다고 해보자. 흡연자가 생각보다 건강해서 100살 넘도록 살수도 있을 것이다. 하지만 100년 넘도록 폐암의 발생여부를 주기적으로 관찰하는 것은 매우 어렵다. 따라서 적절한 시점에서 연구 종료를 선언하고, 지금까지 수집된 정보를 바탕으로 흡연자와 비흡연자를 비교하는 것이 적절할 것이다. 또는 중간에 폐암이 아닌 다른 원인으로 사망한다거나, 더 이상의 연구 참여를 하지 않겠다고 통보할 수도 있다. 이러한 경우를 right censoring 이라 한다. 
● 즉, right censoring이 발생하는 경우는 다음과 같은 상황을 예로 들어볼 수 있다. -> 스터디의 종료, 관심 event가 아닌 이유로 사망, 중도탈락
● 실제 분석 시, right censoring 이 있는 경우, 그 사람의 정보가 어떤 특정시점 t까지는 available 한 것을 이용하게 된다. 이러한 right censoring을 활용하는 것이 생존분석의 강점이라고 할 수 있다.

생존분석 관련 함수

생존분석 함수는 개인의 생존시간 T가 확률변수 (random variable) 라고 생각했을 때 이와 관련된 함수를 의미한다.

Survival function
S(t) = P(T > t) : 특정 시점 t에서 살아 있을 확률을 나타내는 함수이다. 즉, 이것은 event time T가 t보다 클 확률이다. 

F(t) : 특정시점 t까지 event가 발생했을 확률을 말한다. 이는 1-S(t)이다. f(t)의 cdf (cumulative density function)이다.
f(t) : 특정 시점 t에서 event가 발생할 확률을 나타내는 함수이다. (이것은 probability density function 이다.) 그리고 f(t)는 F(t)의 t에 대한 미분이다.

Hazard function
h(t) : t까지 살았을 때, 직후에 바로 event가 일어날 조건부 확률을 나타낸다.

h(t) = f(t)/S(t) 로 나타내어 지는데, 아래 식을 통해 확인해 보자.



생존분석 관련 함수의 정리


$$ S(t) = P(T> t) $$

$$ F(t) = 1-S(t) = P(T \leq t) $$

$$ f(t) = F'(t) $$

$$ h(t) = \lim_{\Delta{t}\to\ 0}P(t\leq T<t+\Delta{t} | T > t) = \frac{f(t)}{S(t)} $$

why? 직접 f(t)/S(t) 를 계산해보면 조건부 확률의 계산 공식을 통해 양변이 같다는 것을 알 수 있다.


$$ f(t) = \lim_{\Delta{t}\to\ 0}P(t<T<t+\Delta{t}) $$

$$ \frac{f(t)}{S(t)} =  \frac{\lim_{\Delta{t}\to\ 0}P(t<T<t+\Delta{t})}{P(T>t)} = \lim_{\Delta{t}\to\ 0}P(t\leq T<t+\Delta{t} | T > t) $$

$$ S(t) = e^{-\int_{0}^{t}h(u)du} $$

why? h(t) 라는 것은 1-S(t)를 미분한 것을 다시 S(t) 로 나눈 것이다. 이를 만족하는 S(t)는 위의 S(t) 밖에 없다. (if and only if 이다.)



카플란마이어 estimation


Censoring이 있는 데이터에서 생존함수를 추정하는 비모수적인 방법이다 만약 censoring이 아예 없다면, 생존함수는 그 시점에서 살아있는 사람을 보면 된다.  하지만 right-censoring이 있는 경우 해당시점에서 살아있는 사람은 censoring 된 사람을 제외한 사람일 것이고, 이 경우에 살아있는 사람만 계산하게 되면  생존 함수가 잘못 추정되게 된다.  따라서 censoring이 있을 때, 그 사람이 t시점까지 살았다는것을 활용하여 각 시점에서 survival rate을 구하여 계속 곱하면서 survival function을 추정한다.




http://sphweb.bumc.bu.edu/otlt/MPH-Modules/BS/BS704_Survival/BS704_Survival_print.html


반응형
반응형

7v로 알아보는 빅데이터의 정의 및 데이터셋과의 차이점


Big data is a term used to refer to data sets that are too large or complex for traditional data-processing application software to adequately deal with.


Wikipedia의 정의에 따르면 빅데이터는 전통적인 데이터 프로세싱 소프트웨어로 다룰 수 없는 큰 규모의 데이터셋을 말합니다. 



데이터를 저장하는 방식은 과거 추억의 아날로그 저장형태 (비디오 테이프, 레코드판, 카세트 테이프, 인쇄 책 등)으로부터 디지털 저장형태(하드디스크, CD, SSD 등)로 변화하였습니다. 인간은 기하급수적으로 발전한 데이터 저장 기술을 통해 엄청난 양의 데이터를 생성하고 이를 저장할 수 있는 기술을 갖게 되었습니다. 2010년대 들어서는 빅데이터라는 말이 심심찮게 들려왔습니다. 그리고 오늘날, 빅데이터라는 말을 한 번도 들어보지 않은 사람들이 없을 정도로 미디어에서 빅데이터라는 말은 일반적인 용어가 되어가고 있습니다. 그렇다면 도대체 빅데이터란 어떻게 정의될까요? 그 이름에서처럼 단순히 큰 데이터를 빅데이터라고 하는걸까요? 빅데이터와 비슷하면서 과거부터 써왔던 용어는 데이터셋이라고 볼 수 있습니다. 그렇다면 그냥 데이터셋과 빅데이터의 차이점은 무엇일까요?


출처 - https://www.ibmbigdatahub.com/infographic/four-vs-big-data


빅데이터는 원래 3v (volume, variety, velocity) 로 정의되었었습니다.  여기에 veracity, value가 추가되면서 보통 5v로 많이 불립니다. 하지만 여기에 validity와 volatility를 추가하여 7v로 부르기도 합니다. 이것은 정의하기 나름이기 때문에 정답이란 없다고 볼 수 있습니다. 이번 포스팅에서는 이 7v로 빅데이터의 개념을 설명하고, 일반적인 데이터셋과 무엇이 다른지를 풀이해보는 시간을 가져보겠습니다.



빅 데이터의 정의 (7v)


1. Volume

빅데이터란 우선, 그 이름에서도 알 수 있듯, 양이 큰 데이터를 말합니다. 과거에는 사람이 데이터를 만들었지만, 현시대에서는 데이터가 기계로부터 자동으로 생성되어져 나옵니다. 그렇기 때문에 데이터의 크기가 커집니다. 과거에는 사람들이 병원에 오면, 이를 병원 직원이 병원의 데이터 시스템에 입력을 해주었습니다. 현재는 이 뿐 아니라 개인의 생체 검사 정보, 의학 영상 정보, 또한 개인의 유전체 정보까지 수많은 종류의 데이터가 여러 플랫폼에 저장되고 있습니다. 이는 예를 들어, 과거의 텍스트 데이터로부터 다양한 멀티미디어 데이터(ex. SNS 로부터 수집되는 수많은 사진, 동영상 등의 정보)로 시대가 변화하였음을 의미합니다.


2. Variety

빅데이터란 그 종류가 매우 다양합니다. 이는 다양한 Source로 부터 정형(Structured), 비정형(Structured) 데이터가 수집되는 것을 의미합니다. 예를 들어, 은행의 거래 시스템 정보는 정형화된 데이터입니다. 누가 누구한테 얼마를 보냈냐는 정보가 정형화된 형테로 데이터베이스에 들어있습니다. 하지만, 빅데이터라는 것은 이 뿐아니라 예를 들어, 의사의 손글씨 데이터, 사람의 음성 데이터 등 정형화 되지 않은 데이터까지 포함하는 의미합니다. 이러한 것들을 가공, 분석하기 위해서는 특별한 기술이 필요합니다.  


3. Velocity

빅데이터는 데이터가 수집되는 속도가 매우 빠릅니다. 데이터가 수집의 원천은 비지니스, 기계, 네트워크, 소셜미디어, 모바일 기기 등 다양해지고 있으며, 이러한 데이터들의 흐름은 그 양이 크고, 연속적입니다. 이러한 real-time 데이터가 비지니스 혹은 연구자들이 의사결정 (decision making)할 때 도움이 되고 있습니다. 


4. Veracity

Volume이나 Velocity가 있더라도, 그것이 빅데이터라고 불릴 수 없는 것은 아닙니다. 주로 진실성이라고 번역하는 Veracity는 빅데이터셋이 얼마나 신뢰할 수 있는지를 의미합니다. 만약 빅데이터에 수많은 노이즈와 바이어스가 있어, 이를 적절히 처리할 수 없을 때에는 이를 통해 유용한 가치를 만들어낼 수 없을지도 모릅니다. 빅데이터는 Veracity를 확보하는 것이 중요하지만, 빅데이터에서의 노이즈와 오류는 종종 피할 수 없는 것이기도 합니다. 따라서 필요없는 정보를 삭제  (processing 또는 cleaning) 해야할 수도 있습니다. 이것이 빅데이터를 다루는 사람이 도메인 지식을 가지거나, 관련 도메인 지식을 갖은 사람들과 협업이 필요한 이유라고 할 수 있습니다.  


5. Validity

Validity의 개념은 그 데이터의 정확성을 의미합니다. 데이터가 타당한지 정확한지 여부는 어떠한 결정을 내리는데 중요합니다. Veracity와 Validity는 비슷한 개념이나, 데이터에 Veracity가 없다면, 노이즈와 바이어스로 인해 잘못된 결론을 이끌어낼 수 있으며, Validity가 없다면 데이터는 규모가 크더라고 쓸모가 없어집니다. (참고) 예를 들어, 개와 고양이 사진 DB을 예로 들어보면, 개와 고양이 사진에 기술적 결함으로 생겨난 인공적 노이즈가 많다면, Veracity가 없는 것입니다. 하지만, 개와 고양이의 Labeling이 잘못된 데이터라면 Validity가 없는 것입니다. 


6. Volatility

Volatility란 휘발성으로 번역되며, 데이터가 얼마나 오래 저장될 수 있고, 타당하여 오랫동안 쓰일 수 있을지에 관한 것입니다. 아무리 데이터의 양이 많고 깔끔하게 정리되어있더라도 몇 년만 지나면 의미가 없어지는 유형의 데이터이거나, 데이터의 양이 가진 자원에 비해 너무나도 커서 이를 오래 저장할 수 없는 환경을 마련하는 것이 힘들다면 빅데이터로서의 활용성을 점검해보아야할 것입니다. 빅데이터는 단기적으로 활용하기 보다는 장기적인 관점에서 유용한 가치를 창출할 수 있어야합니다.


7. Value

빅데이터는 결국 비즈니스나 연구에 사용되며 유용한 가치를 이끌어낼 수 있어야 그 의미가 있습니다. 마지막 Value 는 이러한 빅데이터의 가치를 의미합니다. 최근 많은 빅데이터 관련 initiative가 나오고 있는데, 빅데이터를 설계하고 그것을 수집하기 전에 그 데이터를 활용하여 무엇을 할 수 있을지에 대한 고민이 먼저 필요할 것입니다. 


2016년에 언급된 빅데이터 정의

A 2016 definition states that "Big data represents the information assets characterized by such a high volume, velocity and variety to require specific technology and analytical methods for its transformation into value".

빅데이터란 Volume, Velocity, Variety로 특징지을 수 있는 정보 자원이며, 이를 활용하여 기술 및 분석 방법에서의 가치를 얻을 수 있어야합니다. 


2018년 언급된 빅데이터 정의

A 2018 definition states "Big data is where parallel computing tools are needed to handle data", and notes, "This represents a distinct and clearly defined change in the computer science used, via parallel programming theories, and losses of some of the guarantees and capabilities made by Codd’s relational model."

빅데이터는 그것을 다루기 위해 병렬 컴퓨팅 툴이 필요할 정도의 데이터를 말합니다.


빅데이터의 정의는 시대에 따라달리지고, 또 이를 해석하는 분야, 사람에 따라 달라질 수 있습니다. 하지만 공통적으로 많은 사람들의 동의하는 정의가 바로 위의 정의들이라고 볼 수 있습니다. 이러한 관점에서 빅데이터와 기존 데이터셋의 차이점은 바로 다음과 같이 정리해볼 수 있습니다.


1. 빅데이터는 다양한 소스로부터 수집된 데이터를 이르지만 이 중, 비정형화된 데이터(Unstructured data)에 더 초점을 맞춥니다.

2. 빅데이터는 병렬 컴퓨터의 필요할 정도의 큰 데이터셋을 말합니다. 

3. 빅데이터는 비지니스 혹은 연구에서 유용한 가치를 창출하여야 합니다.

4. 빅데이터는 타당성 (Validity), 신뢰성(Veracity)이 확보되어야합니다. 하지만 이것은 힘들 수도 있습니다.

5. 빅데이터는 오랫동안 저장되어 가치를 창출할 수 있어야하며, 단기간 활용보다는 장기적 활용에 초점을 맞춥니다.  


반응형
반응형



Python 중고급 속성 정리 (3) 가변길이 인수목록 받기(*args, **kargs)


이번 포스팅에서는 알아두면 정말 유용한 가변길이 인수목록 받기를 정리해보겠습니다. 파이썬 코드를 보다보면 가끔 *args, **kargs를 보실 수 있습니다. 이것은 함수의 인자(parameter 또는 arguments)가 가변 길이일 때 사용합니다. args, kargs는 원하는 이름대로 쓸 수 있고, *, **는 각각 non-keworded arguments, keworded arguments를 뜻합니다. 


예를 들어 아래 파이썬 코드를 보시면 쉽게 이해하실 수 있습니다. 


*args


def test_var_args(f_arg, *args):
    print("first normal arg:", f_arg)
    for arg in args:
        print("another arg through *argv:", arg)

test_var_args('yasoob', 'python', 'eggs', 'test')

first normal arg: yasoob

another arg through *argv: python

another arg through *argv: eggs

another arg through *argv: test


f_arg를 통해 하나의 인자를 전달 받고, *args를 통해 가변길이 인자를 전달 받습니다. 전달받은 인자들은 함수내에서 list처럼 다룰 수 있습니다. 


또한 아래 코드처럼, 인자들을 미리 정의한 후 전달할 수도 있습니다. 이를 통해 파라미터 정의부함수 실행부를 분리할 수 있다는 장점이 있습니다. 조금 더 깔끔하게 파이썬 코드를 관리할 수 있겠죠.


param = ['yasoob', 'python', 'eggs', 'test']
test_var_args('hi', *param)


*kargs


다음으로는 **, 별표가 2개 붙은 keworded argments 사용법입니다. **kwargs로 전달받은 인자는 함수 내에서 dictionary 처럼 다룰 수 있습니다.

def greet_me(**kwargs):
    print(kwargs.items())
    for key, value in kwargs.items():
        print("{0} = {1}".format(key, value))
        
greet_me(name="yasoob", school="snu")

dict_items([('name', 'yasoob'), ('school', 'snu')])

name = yasoob

school = snu


kwargs = {"name": 'yasoob', 'school' :"snu"}
greet_me(**kwargs)

이런식으로 dict로 미리 정의한 후에 ** 를 통해 함수의 인자로 넘길 수 있습니다. 매우 유용하죠.



Decorator와 결합하여 사용하기


*args와 **kargs는 decorator와 결합하여 사용하면 더 유용하게 사용할 수 있습니다. 예를 들어, 함수를 실행할 때, 그 함수의 이름을 출력하고 함수를 실행하는 decorator를 만든다고 해봅시다. 근데, decorator 함수에서는 함수의 인자를 지정해줘야하기 때문에 decoration하는 함수의 인자의 수가 모두 같아야합니다. 이럴 때, *args와 **kargs를 이용하면 decoration 하는 함수의 인자의 길이에 상관없이 decorator를 만들 수 있습니다. 


logging_decorator는 함수를 실행할 때, 그 함수의 이름을 출력하는 기능을 갖는 decorator입니다. 이 decorator 함수에 *args, **kargs를 지정하고 이것을 그대로 원래 함수로 넘겨주면 인자의 길이에 상관없이 decorator를 구현할 수 있습니다.


Decorator와 *args, **kargs 사용 예제


이 예제에서 squared_number와 add_number는 인자의 수가 다른데 이를 *args를 이용하여 해결하였습니다. 만약 *args를 활용하지 않는다면, 각각에 대하여 decorator를 만들어야하죠. 또한 마지막 함수(add_numbers) 의 경우 함수 안에 keword를 지정하고 있습니다. 또 함수 실행 시 **kargs 를 통하여 인자를 넘기는데, 이 때 decorator에서 **kargs를 정의해 놓으면 keworded arguments 들을 그대로 decoration 함수를 전달할 수 있기 때문에 가변길이 인자를 처리할 수 있게 됩니다.

from functools import wraps

def logging_decorator(f) : 
    @wraps(f)
    def wrapper_function(*args, **kargs) : 
        print(f.__name__ + " was called") 
        print("결과 : ", f(*args, **kargs))
    return wrapper_function

@logging_decorator
def square_number(x) :
    return x**2

square_number(2)

@logging_decorator
def add_number(x,y,z=0) :
    return x+y

add_number(2,3)

@logging_decorator
def add_numbers(x=3, y=4) : 
    return x+y

add_numbers()

kwargs = {"x": 3, "y": 5}

add_numbers(**kwargs)

square_number was called

결과 :  4

add_number was called

결과 :  5

add_numbers was called

결과 :  7

add_numbers was called

결과 :  8

반응형
반응형