이번 포스팅에서는 장비별로 On, Off 상태 변화 간의 가동 시간(run time)을 구하고, 이렇게 구한 장비별 가동 시간의 평균을 집계해보겠습니다. 




먼저, 장비(device), 날짜/시간(time), status('ON', 'OFF') 의 3개 변수로 구성된 간단한 예제 DataFrame을 만들어보겠습니다. 



> #===========================

> # time difference by device

> #===========================

> device <- c('A', 'A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'C')

> time <- c('2018-07-01 10:20:23', '2018-07-01 10:30:55', '2018-07-01 11:11:01',

+           '2018-07-01 11:51:41', '2018-07-02 07:11:02', '2018-07-02 09:00:33',

+           '2018-07-02 09:20:24', '2018-07-02 12:12:21', '2018-07-02 14:01:09',

+           '2018-07-02 18:11:41', '2018-07-02 19:21:51', '2018-07-02 20:30:00')

> status <- c('ON', 'ON', 'ON', 'OFF', 'ON', 'ON', 'OFF', 'ON', 'OFF', 'ON', 'ON', 'OFF')

> df <- data.frame(device, time, status)


> df

   device                time status

1       A 2018-07-01 10:20:23     ON   <-- start

2       A 2018-07-01 10:30:55     ON

3       A 2018-07-01 11:11:01     ON

4       A 2018-07-01 11:51:41    OFF    <-- end


5       B 2018-07-02 07:11:02     ON    <-- start

6       B 2018-07-02 09:00:33     ON

7       B 2018-07-02 09:20:24    OFF    <-- end


8       C 2018-07-02 12:12:21     ON    <-- start

9       C 2018-07-02 14:01:09    OFF     <-- end

10      C 2018-07-02 18:11:41     ON    <-- start

11      C 2018-07-02 19:21:51     ON

12      C 2018-07-02 20:30:00    OFF     <-- end




다음으로 장비(device)와 날짜/시간(time)을 기준으로 정렬(sort)을 하겠습니다. 



> # sort df by device and time in ascending order

> df <- df[order(device, time),]

> df

   device                time status

1       A 2018-07-01 10:20:23     ON

2       A 2018-07-01 10:30:55     ON

3       A 2018-07-01 11:11:01     ON

4       A 2018-07-01 11:51:41    OFF

5       B 2018-07-02 07:11:02     ON

6       B 2018-07-02 09:00:33     ON

7       B 2018-07-02 09:20:24    OFF

8       C 2018-07-02 12:12:21     ON

9       C 2018-07-02 14:01:09    OFF

10      C 2018-07-02 18:11:41     ON

11      C 2018-07-02 19:21:51     ON

12      C 2018-07-02 20:30:00    OFF




각 장비(device) 별로 'ON' 이후에 다음 'OFF' 까지의 사이 중간에 끼어있는 'ON'은 필요가 없으므로 삭제를 해서 새로운 'df_2' 이름의 DataFrame을 만들어보겠습니다. 


[ 전처리 후의 Output Image ]


   device                time status

1       A 2018-07-01 10:20:23     ON   <-- start

2       A 2018-07-01 10:30:55     ON

3       A 2018-07-01 11:11:01     ON

4       A 2018-07-01 11:51:41    OFF    <-- end


5       B 2018-07-02 07:11:02     ON    <-- start

6       B 2018-07-02 09:00:33     ON

7       B 2018-07-02 09:20:24    OFF    <-- end


8       C 2018-07-02 12:12:21     ON    <-- start

9       C 2018-07-02 14:01:09    OFF     <-- end

10      C 2018-07-02 18:11:41     ON    <-- start

11      C 2018-07-02 19:21:51     ON

12      C 2018-07-02 20:30:00    OFF     <-- end

 





> # set of device

> device_set <- as.character(unique(df$device))

> # blank DataFrame to store the preprocessed dataset

> df_2 <- data.frame()

> for (i in 1:length(device_set)){

+   # split dataframe by device

+   device_i <- device_set[i]

+   df_i <- df[df$device == device_i, ]

+   

+   # add the first time of df_i to df2

+   df_2 <- rbind(df_2, df_i[1,])

+   

+   # add the time if the device is turned off or turned on again

+   for (j in 1:(nrow(df_i)-1)){

+     if((df_i$status[j] == 'ON' & df_i$status[j+1] == 'OFF') | 

+        (df_i$status[j] == 'OFF' & df_i$status[j+1] == 'ON')){

+       df_2 <- rbind(df_2, df_i[j+1,])

+     }

+   }

+ }

> df_2

   device                time status

1       A 2018-07-01 10:20:23     ON

4       A 2018-07-01 11:51:41    OFF

5       B 2018-07-02 07:11:02     ON

7       B 2018-07-02 09:20:24    OFF

8       C 2018-07-02 12:12:21     ON

9       C 2018-07-02 14:01:09    OFF

10      C 2018-07-02 18:11:41     ON

12      C 2018-07-02 20:30:00    OFF





lag() window function을 사용해서 장비, 날짜/시간, 상태를 한칸씩 밑으로 내리고(Lag), 장비 이름과 상태를 기준으로 필요한 행만 남기고 나머지는 삭제하겠습니다. 



> # lag window function

> library(dplyr)

> df_2_lag <- mutate(df_2, 

+                    device_lag = lag(device, 1), 

+                    time_lag = lag(time, 1), 

+                    status_lag = lag(status, 1))

> # filtering

> df_2_lag_filtered <- df_2_lag %>% 

 filter(device == device_lag & status == 'OFF')

> df_2_lag_filtered

  device                time status device_lag            time_lag status_lag

1      A 2018-07-01 11:51:41    OFF          A 2018-07-01 10:20:23         ON

2      B 2018-07-02 09:20:24    OFF          B 2018-07-02 07:11:02         ON

3      C 2018-07-02 14:01:09    OFF          C 2018-07-02 12:12:21         ON

4      C 2018-07-02 20:30:00    OFF          C 2018-07-02 18:11:41         ON




이제 장비(device)별로 'ON' 이후 다음번 'OFF' 까지의 가동 시간(run_time)을 difftime() 함수를 사용해서 구해보겠습니다. 이때 strptime() 함수를 사용해서 문자열 값을 '년/월/일/시간/분/초("%Y-%m-%d %H:%M:%S")' 형태로 만들어준 후에 difftime() 함수를 사용할 수 있습니다.  이렇 구한 가동시간은 '시간(hour)' 단위 값입니다. (분으로 환산하려면 곱하기 60, 초로 환산하려면 곱하기 60*60 을 해주면 됩니다) 



> # calculate the run_time

> df_2_lag_filtered$run_time <- as.numeric(difftime(strptime(df_2_lag_filtered$time, "%Y-%m-%d %H:%M:%S"),

+                                                   strptime(df_2_lag_filtered$time_lag, "%Y-%m-%d %H:%M:%S")))

> df_2_lag_filtered

  device                time status device_lag            time_lag status_lag run_time

1      A 2018-07-01 11:51:41    OFF          A 2018-07-01 10:20:23         ON 1.521667

2      B 2018-07-02 09:20:24    OFF          B 2018-07-02 07:11:02         ON 2.156111

3      C 2018-07-02 14:01:09    OFF          C 2018-07-02 12:12:21         ON 1.813333

4      C 2018-07-02 20:30:00    OFF          C 2018-07-02 18:11:41         ON 2.305278

 




마지막으로, 각 장비(device)별 가동시간(run_time)의 평균을 구해보겠습니다. 



> # average of run_time by device

> run_time_by_device <- df_2_lag_filtered %>% 

+   group_by(device) %>% 

+   summarise(run_time_avg = mean(run_time))

> run_time_by_device

# A tibble: 3 x 2

  device run_time_avg

  <fct>         <dbl>

1 A              1.52

2 B              2.16

3 C              2.06




많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요.



Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 최고 2019.10.25 10:36  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 감사드립니다~

  2. 2019.11.06 15:47  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  3. 2019.11.19 22:47  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.11.19 23:01 신고  댓글주소  수정/삭제

      암명하세요 r린이님,

      교호작용항은 두 변수를 곱하는 방식으로 표현하시면 됩니다.

      가령, y~ be5a0 + beta1*x1 + beta2*x2 + beta3*x1*x2 (<== 교호작용항) 이런식으로요.

  4. 2019.11.19 23:13  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  5. 2019.11.19 23:17  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.11.19 23:22 신고  댓글주소  수정/삭제

      위의 댓글에 남겨주신 코드 중에

      %*% 를 * 로 수정해서 해보실래요?

      %*% 은 내적(dot product)을 할 때 사용하는 operator 여서 scalar (한개의 값, dot)을 반환합니다. (https://rfriend.tistory.com/145 )

      그냥 element-wise multiplication을 하시려면 * 만 사용하시면 됩니다.

      %*%를 사용해서 두 변수를 곱했기 때문에 값이 한개의 scalar값으로 반환되면서 벡터 길이가 다르다는 에러메시지가 난거 같습니다.

  6. 2019.11.19 23:30  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  7. r린이 2019.11.19 23:41  댓글주소  수정/삭제  댓글쓰기

    감사합니다. 좋은 밤 되세요!

이번 포스팅에서는 STEFANY COXE, STEPHEN G. WEST, AND LEONA S. AIKEN, 2009, "The Analysis of Count Data: A Gentle Introduction to Poisson Regression and Its Alternatives" 논문을 요약해서 한글로 번역한 내용을 소개하겠습니다. 


빈도(count data) 데이터에 대한 분석을 할 때 어떤 분포를 가정하고, 어떤 회귀모형을 적합하는 것이 좋을지 분석 방향을 잡는데 도움이 될 것입니다. 






  왜 포아송 회귀모형이 필요한가(Why Poisson Regression)?


특정 시간 동안 발생한 사건의 건수에 대한 도수 자료(count data, 음수가 아닌 정수)를 목표변수, 특히 평균이 10 미만일 경우, 최소제곱법(Ordinary Least Squares) 회귀모형을 적합하면 표준 오차와 유의수준이 편향되는 문제가 발생한다. OLS 회귀모형은 오차의 조건부 정규성(conditional normality), 등분산성(homoscedasiticity), 독립성(independence)을 가정한다. 하지만 평균이 작은 도수 데이터는 0 이상의 정수만 있고, 작은 계급에 많은 관측치가 몰려있으며, 우측으로 길게 치우친 분포를 띠어 회귀모형의 가정을 위배하고 음수값도 결과값으로 반환하는 문제가 있다. 종속변수가 도수 데이터이면서 오차가 정규분포, 등분산이 아닌 경우 포아송 회귀모형을 사용한다.

 


  • 포아송 회귀모형은 무엇인가? (Overview of Poisson Regression)

포아송 회귀모형은 일반화선형모형(Generalized Linear Model, GLM)의 한 종류로서,

-       Random component: Poisson Distribution, 


-       Systematic component: Mixed linear relationship

-       Link function: Log Link (ln)

 

표현 예

해석  이며, X1을 한 단위 증가시켰을 때 효과를 보면   이므로, 다른 변수들을 통제했을 때 X1이 한단위 증가하면 도수의 추정치 만큼 승법적으로 변화(multiplicative changes)한다.


선형회귀모형이 최소제곱법(OLS)로 모수를 추정한다면, 포아송 회귀모형은 최대가능도추정(MLE, Maximum Likelihood Estimation)을 통해 모수를 추정한다.


선형회귀모형은 모델에 의해 설명되는 잔차 제곱합 SS (Sum of Squares)를 총 잔차제곱합(TSS, Total Sum of Squares)로 나눈 값인  R^2로 모델 적합도(Goodness of Fit of the model) 평가하지만, 포아송 회귀모형은 설명변수의 추가에 따른 이탈도의 감소 비율(proportional reduction in deviance)로 표현되는 pseudo-R^2를 사용하여 완전모델(perfect model)에 얼마나 가깝게 적합이 되었는지를 평가한다



선형회귀모형은 F-test를 통해 모델의 유의성을 검정하지만, 포아송 회귀모형은 base modelcomplete model 간의 이탈도 차이에 대한 Chi-square test를 통해 모델 유의성을 검정한다 

선형회귀모형은 모형 타당성 평가 (Assessing model adequacy)를 위해 잔차도(residual plot)를 그려서 확인해보는 반면, 포아송 회귀모형은 이탈도 잔차도(deviance residuals vs. predicted values)를 그려보거나 실측치와 예측치를 비교해보는 것이다.

 

* [참고] 포아송 분포 : https://rfriend.tistory.com/101




  과대산포(Over-dispersion) 문제는 무엇이며어떻게 해결할 수 있나?


포아송분포는 평균과 분산이 동일(equidispersion)하다고 가정하므로, 분산이 평균보다 큰 과대산포 (Overdispersion) 시에는 포아송 회귀모형을 사용할 수 없다. 과대산포 시에 적용할 수 있는 대안 모델로는 과대산포 포아송 회귀(Overdispersed Poisson Regression), 음이항회귀(Negative Binomial Regression)가 있다.


-   Overdispersed Poisson Regression: 과대산포를 모델링하기 위해 두번째 모수인 조건부 분산 overdispersion scaling parameter 를 추정하며, 과대산포 포아송 회귀모형의 오차 분포는  를 가진다.


-   Negative Binomial Regression Models: 음이항회귀모형은 평균에는 영향이 없으면서 과대산포를 유발하는, 설명이 되지 않는 추가적인 가변성이 있다고 가정한다. 음이항 회귀모형은 똑같은 설명변수 값을 가지는 관측치가 다른 평균 모수를 가지고 포아송 회귀모형에 적합될 수 있도록 해준다. 오차 함수는 포아송 분포와 감마분포의 혼합 분포이다.


과대산포 검정은 가능도비 검정(Likelihood ratio test) 또는 score 검정을 사용한다.

모델 선택을 위해서는 AIC(Akaike Information Criterion), BIC(Bayesian Information Criterion) 을 사용하며, AIC, BIC의 값이 작은 모델을 선택하는데, 샘플 크기가 작을 때는 간단한 모델을 선택하고, 샘플 크기가 클 때는 좀더 복잡한 모델을 선택한다.

 



  과대영(Excess Zeros) 문제는 무엇이며어떻게 해결할 수 있나?


특정 평균을 모수로 가지는 포아송 분포에서 나타나는 ‘0’보다 많은 ‘0’을 가지는 샘플의 경우 과대영(excess zeros)이 발생했다고 말한다. 과대영은 (a) 특정 행동을 나타내지 않은 그룹의 포함 (: 비흡연자 그룹), (b) 특정 행동을 나타내지만 샘플링 계획에서 제외 (: 병원 입원한 사람만 연구) 하는 경우에 발생할 수 있다. 과대영일 경우 ZIP 회귀모형을 대안으로 사용할 수 있다.


-   Zero-Inflated Poisson(ZIP) Regression: 두 부분으로 나뉘는데, (1) 로지스틱 회귀모형을 사용하여 항상 ‘0’인 집단(structural zeros) 또는 항상 ‘0’은 아니지만 조사 시점에 ‘0’이라고 응답한 집단에 속할 확률을 구하고, (2) 포아송 회귀 또는 음이항 회귀모형으로 structural zeros를 포함하지 않은 나머지 관측치를 추정하는 모델을 적합한다.





  음이항분포(Negative Binomial Distribution) 


* Reference : https://en.wikipedia.org/wiki/Negative_binomial_distribution


 
확률 이론과 통계에서, 음 이항 분포는 특정 (무작위) 실패 횟수 (r로 표시)가 발생하기 전에 독립적이고 동일하게 분산 된 베르누이 시행 순서에서 성공 횟수의 이산 확률 분포이다. 예를 들어 1을 실패로, 1이 아닌 모든 것을 성공으로 정의하고 1이 세 번째로 나타날 때까지 반복적으로 던지면 (r = 3 번의 실패), 출현한 1이 아닌 수(non-1)의 확률 분포는 음 이항 분포가 된다.


 파스칼 분포 (Blaise Pascal 이후) Polya 분포 (George Pólya의 경우)는 음 이항 분포의 특수한 경우이다. 엔지니어, 기후 학자 및 다른 사람들 사이의 협약은 정수 값 정지 시간 매개 변수 r의 경우 "음 이항" 또는 "파스칼"을 사용하고, 실수 값의 경우에는 "Polya"를 사용한다. 토네이도 발생과 같은 "전염성 있는" 이산 이벤트가 발생하는 경우 Polya 분포를 사용하여 Poisson과 달리 평균 및 분산을 다르게 하여 Poisson 분포보다 더 정확한 모델을 제공 할 수 있다. "전염성 있는" 사건은 양의 공분산으로 인해 사건이 독립적이었던 경우보다 양의 상관관계가 있어 Poisson 분포에 의한 분산보다 더 큰 분산을 일으킨다. Poisson 분포는 평균과 분산이 동일하다고 가정하기 때문에 평균보다 분산이 큰 분포(Overdispersed)의 경우 적절하지 않으며, 2개의 모수를 가져서 과대산포분포를 적합할 수 있는 음 이항 분포를 사용할 수 있다.


 일련의 독립적 인 베르누이 재판이 있다고 가정하자. 따라서 각 임상 시험은 "성공" "실패"라는 두 가지 결과를 나타낸다. 각 시험에서 성공 확률은 p이고 실패 확률은 (1 - p)이다. 미리 정의 된 실패 횟수 r이 발생할 때까지의 서열을 관찰한다. 그러면 우리가 본 성공의 무작위 수 X는 음 이항 (또는 파스칼) 분포를 가질 것이다





 R을 활용한 Poisson model, Negative binomial model, Zero-Inflated model


Poisson model, Negative binomial model, Zero-Inflated model 의 유형, 분포, 모수 추정 방법, R패키지와 함수를 표로 정리하면 아래 [1]을 참고하세요. 


[1] Overview of count regression models


유형

분포

추정

방법

Description

R package & function

GLM

Poisson

ML

Poisson Regression: classical GLM, estimated by maximum likelihood(ML)

{stats} 패키지의 glm()

NB

ML

NB Regression: exteded GLM, estimated by ML including additional shape parameter

{stats} 패키지의 glm(), {MASS} 패키지의 glm.nb() 함수

Zero-augmented

Poisson

ML

Zero-Inflated Poission(ZIP)

{pscl} 패키지의 zeroinfl() 함수

 



각 회귀모형별로 R 패키지와 함수의 활용 방법은 아래와 같다.


-       (1) R을 활용한 Poisson model : glm() in the {stats} package



glm(formula, data, subset, na.action, weights, offset,

    family = poisson, start = NULL, control = glm.control(...),

    model = TRUE, y = TRUE, x = FALSE, ...)


 



-       (2) R을 활용한 Negative binomial model
   :
glm() in the {stats} package



glm(formula, data, subset, na.action, weights, offset,

    family = negative.binomial, start = NULL, control = glm.control(...),

    model = TRUE, y = TRUE, x = FALSE, ...)


 


   : glm.nb() in the {MASS} package



glm.nb(formula, data, weights, subset, na.action,

       start = NULL, etastart, mustart,

       control = glm.control(...), method = "glm.fit",

       model = TRUE, x = FALSE, y = TRUE, contrasts = NULL, ...,

       init.theta, link = log)


 



-       (3) R을 활용한 Zero-inflated regression model: zeroinfl() in the {pscl} package



zeroinfl(formula, data, subset, na.action, weights, offset,

dist = "poisson", link = "logit", control = zeroinfl.control(...),

model = TRUE, y = TRUE, x = FALSE, ...)




많은 도움이 되었기를 바랍니다. 



Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 2019.10.27 15:26  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.10.27 16:45 신고  댓글주소  수정/삭제

      안녕하세요 Mini님,

      GAM은 GLM을 포괄하는, smoother 조정을 통해 좀더 flexible하고 nonlinear한 적합이 가능한 반면, 말씀하신대로 non-parametric approach로서 RR, CI 등은 알 수 없습니다.

      만약 RR, CI 등을 구해야 하는 상황이라면 parametric approach (모델 기반)인 GLM을 사용하시는게 맞을거 같습니다.

  2. mini 2019.10.27 21:09  댓글주소  수정/삭제  댓글쓰기

    답변 너무 감사합니다.
    exp()로 RR은 구했는데요. 95% 신뢰구간을 구하는 방법을 모르겠습니다.
    아래와 같은 방법으로 구하는 것이 맞을까요?? 구글링을 많이 해봤는데, 딱 맞는 것은 못찾아 문의 드리게 되었습니다.

    > exp(-0.0041)
    [1] 0.9959084
    > 0.9959084-(1.96*0.0007)
    [1] 0.9945364
    > 0.9959084+(1.96*0.0007)
    [1] 0.9972804

  3. 2019.10.27 21:53  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  4. Mini 2019.11.01 09:03  댓글주소  수정/삭제  댓글쓰기

    Rr과 신뢰구간은 모두 구했습니다~
    Exp(coef(모델))
    EXP(confint(모델))로요
    도움 감사합니다.
    그려진 플롯에서 변곡점을 찾아야되는데요.
    아무리 인터넷을 찾아봐도
    gam에서 적용할만한 내용이 안나오네요.
    실례가 안된다면 여쭤봐도 될까요?

  5. mini 2019.11.02 18:21  댓글주소  수정/삭제  댓글쓰기

    감사합니다.
    GAm에서는 안되는 것인지, 제가 못하는 것인지 확실치 않은데요. 안되네요.
    그래도, 도움 주셔서 너무 감사합니다. !

R의 다양한 패키지들을 사용하다 보면 함수를 사용한 후의 산출물 결과가 자신이 생각했던 결과와 다를 경우 R 패키지 함수의 소스 코드가 어떻게 되어있는 것인지 궁금할 때가 있습니다. 


이번 포스팅에서는 R 패키지에 있는 함수, 메소드의 소스 코드를 들여다볼 수 있는 2가지 방법을 소개하겠습니다. 


(1) getAnywhere(function_name)

(2) package_name:::function_name





가령, 두 벡터의 차집합(difference of sets)을 구할 때 사용하는 함수가 base 패키지의 setdiff() 함수입니다. 


> x <- c(1, 1, 2, 3, 4, 5)

> y <- c(4, 5, 6, 7)

> setdiff(x, y)

[1] 1 2 3




위에서 예로 든 base 패키지의 setdiff() 함수에 대한 소스 코드를 위의 2가지 방법을 사용해서 살펴보겠습니다. 



 (1) getAnywhere(function_name)


R package 의 이름을 몰라도 함수, 메소드의 이름만 알고 있으면 R package 이름과 namespace, 그리고 소스 코드를 친절하게 다 볼 수 있으므로 매우 편리합니다. 



> getAnywhere(setdiff)

A single object matching ‘setdiff’ was found

It was found in the following places

  package:base

  namespace:base

with value


function (x, y) 

{

    x <- as.vector(x)

    y <- as.vector(y)

    unique(if (length(x) || length(y)) 

        x[match(x, y, 0L) == 0L]

    else x)

}

<bytecode: 0x000000000ff1e8d0>

<environment: namespace:base>

 




 (2) R_package_name:::function_name


R package 이름을 알고 있다면 package_name:::function_name 의 형식으로 ':::' 를 사용해서 함수의 소스 코드를 들여다볼 수 있습니다. 위 (1)번의 getAnywhere() 함수가 기억이 잘 안날 때 쉽게 ':::' 를 사용해서 소스 코드 볼 때 편리합니다. 



> base:::setdiff

function (x, y) 

{

    x <- as.vector(x)

    y <- as.vector(y)

    unique(if (length(x) || length(y)) 

        x[match(x, y, 0L) == 0L]

    else x)

}

<bytecode: 0x000000000ff1e8d0>

<environment: namespace:base>




많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 (1) 시계열 정수의 순차 3개 묶음 패턴 별 개수를 구하고, (2) 각 패턴별로 발생 빈도 기준으로 내림차순 정렬하는 방법을 소개하겠습니다. 



먼저 S = {1, 2, 3, 4, 5} 의 정수 집합에서 복원추출(replacement) 하여 무작위 샘플 1,000개를 생성해보겠습니다. 



> #----------------------------------

> # counting and sorting by patterns

> #----------------------------------

> set.seed(123) # for reproducibility

> sample <- sample(x=1:5, size=1000, replace=TRUE)

> sample

   [1] 2 4 3 5 5 1 3 5 3 3 5 3 4 3 1 5 2 1 2 5 5 4 4 5 4 4 3 3 2 1 5 5 4 4 1 3 4 2 2 2 1 3 3 2 1 1 2

  [48] 3 2 5 1 3 4 1 3 2 1 4 5 2 4 1 2 2 5 3 5 5 4 3 4 4 4 1 3 2 2 4 2 1 2 4 3 4 1 3 5 5 5 1 1 4 2 4

  [95] 2 1 4 1 3 3 3 2 3 5 3 5 5 4 3 1 5 2 1 5 4 1 3 5 3 3 4 2 2 2 2 5 1 1 1 4 4 5 4 4 3 4 5 4 5 3 2

 [142] 3 1 1 5 2 2 1 2 4 5 3 2 2 1 2 3 2 3 2 3 2 4 2 2 3 4 2 3 2 4 1 5 4 4 4 2 3 5 3 5 2 4 2 3 3 2 3

 [189] 5 5 2 2 5 4 5 3 3 4 1 3 2 5 4 3 3 5 2 2 1 1 3 2 2 4 1 4 2 3 5 5 2 5 4 4 1 2 3 3 4 5 4 3 3 1 2

 [236] 2 1 5 1 5 3 4 1 4 2 4 2 5 5 4 2 2 3 2 3 4 1 3 3 5 5 5 4 5 3 3 2 2 1 3 5 1 1 1 4 4 5 3 1 4 4 1

 [283] 2 2 1 2 1 2 1 4 2 1 1 5 4 5 5 1 1 4 4 1 4 4 4 3 1 1 3 3 2 3 4 1 2 5 5 2 2 5 5 2 1 4 1 1 5 1 2

 [330] 5 4 2 4 5 2 3 4 4 4 5 3 1 3 2 3 2 5 2 2 4 1 5 3 1 4 5 4 3 2 5 1 1 5 2 2 4 1 1 3 3 3 2 5 3 3 5

 [377] 4 2 2 5 3 4 2 4 3 5 1 2 1 2 5 2 4 4 1 2 2 1 1 5 5 1 5 3 2 3 4 1 2 4 2 5 2 3 2 1 5 2 3 1 3 4 3

 [424] 2 5 4 2 2 1 5 5 3 2 5 2 4 1 3 1 1 2 3 4 1 5 4 4 1 2 4 3 2 2 1 2 5 5 4 1 1 5 3 3 1 3 2 2 4 2 5

 [471] 1 5 3 4 4 3 4 1 2 4 2 5 2 4 5 1 1 4 3 5 5 1 3 4 1 5 2 1 4 4 2 2 2 1 2 1 3 3 5 2 3 1 5 2 4 1 3

 [518] 4 1 5 2 2 2 1 3 4 5 1 5 2 5 4 3 5 1 4 2 5 3 2 5 1 5 2 4 5 1 5 5 2 2 2 3 5 3 3 4 3 3 1 1 5 4 2

 [565] 4 4 2 5 3 5 3 5 3 3 5 4 3 1 3 3 5 5 5 2 5 1 2 4 5 3 3 1 5 1 1 5 3 2 5 1 2 4 2 2 1 5 1 5 2 2 4

 [612] 1 1 5 3 4 5 4 5 1 5 5 4 2 1 2 2 5 2 2 4 5 3 4 1 2 5 4 5 3 3 4 5 2 1 4 4 1 2 4 4 5 3 3 4 5 5 1

 [659] 3 2 5 5 1 4 2 1 5 1 4 1 1 3 1 2 3 3 5 2 3 3 4 4 5 5 5 3 3 1 3 2 1 4 3 1 2 3 1 1 3 2 5 2 1 5 2

 [706] 4 3 2 1 3 4 3 4 4 3 2 1 1 5 1 3 4 4 5 4 4 5 4 4 1 3 5 4 5 1 2 5 5 2 1 5 1 1 3 2 3 4 1 4 5 2 2

 [753] 1 1 3 3 3 5 1 2 3 2 3 3 3 1 1 3 4 4 5 5 4 5 1 2 2 3 4 5 2 5 1 4 5 3 2 1 2 5 5 3 2 3 5 3 1 2 2

 [800] 2 3 2 1 1 2 5 3 3 4 4 1 4 2 5 4 1 2 1 3 3 4 2 4 5 4 1 3 1 2 4 1 4 3 4 4 3 4 4 2 1 5 4 4 1 3 2

 [847] 5 1 2 1 2 4 5 4 3 3 5 1 1 5 3 1 4 3 4 2 1 1 3 3 1 1 4 3 5 4 1 4 5 4 1 5 4 1 4 5 3 2 2 2 3 4 4

 [894] 3 5 4 1 2 1 4 5 3 5 3 4 3 4 5 5 1 1 1 5 3 2 5 3 3 2 2 5 1 2 5 4 4 2 3 1 4 5 2 4 3 5 1 3 5 5 1

 [941] 5 3 2 3 1 1 3 2 3 2 3 1 5 1 2 5 4 1 3 3 3 3 4 1 5 3 3 1 4 3 4 1 5 2 2 4 5 1 2 3 3 4 5 4 5 4 1

 [988] 1 1 3 1 2 3 3 3 5 4 2 4 1

 




다음으로 순차적으로 3개의 정수를 묶음으로 하여 1개씩 이동하면서 패턴을 정의하고(패턴1 {X1, X2, X3}, 패턴2 {X2, X3, X4}, ..., 패턴n-2 {Xn-2, Xn-1, X}, 위의 정수 난수 샘플을 가지고 예를 들면, 첫번째 3개 묶음의 패턴은 '2_4_3', 두번째 3개 묶음의 패턴은 '4_3_5', 세번째 3개 묶음의 패턴은 '3_5_5', ... ), 각 패턴별로 발생 빈도를 세어보겠습니다


먼저 비어있는 pattern_list 라는 이름의 리스트(list) 를 만들어놓구요, 


for loop 반복문을 사용하여 '패턴1' {X1, X2, X3}, '패턴2' {X2, X3, X4}, ..., '패턴n-2' {Xn-2, Xn-1, X} 을 생성합니다. 


if else 조건문을 사용하여, 만약 3개 정수 묶음의 패턴이 pattern_list의 키(key)에 이미 들어있는 것이면 +1 을 추가해주구요, 그렇지 않다면 (처음 발생하는 패턴이라면) pattern_list의 키(key)에 새로 발생한 패턴을 키로 추가해주고 값(value)으로 1을 부여해줍니다. 



> # blank list to store the patterns' count

> pattern_list <- {}

> # count per patterns

> for (i in 1:(length(sample)-2)){

+   

+   pattern <- paste(sample[i], sample[i+1], sample[i+2], sep="_")

+   

+   if (pattern %in% names(pattern_list)) {

+     pattern_list[[pattern]] <- pattern_list[[pattern]] + 1

+   } else {

+     pattern_list[[pattern]] <- 1

+   }

+ }

> pattern_list

2_4_3 4_3_5 3_5_5 5_5_1 5_1_3 1_3_5 3_5_3 5_3_3 3_3_5 5_3_4 3_4_3 4_3_1 3_1_5 1_5_2 5_2_1 2_1_2 

    6     7    10     9     6     6    10    15    11     7     5     5     5    13     7    12 

1_2_5 2_5_5 5_5_4 5_4_4 4_4_5 4_5_4 4_4_3 4_3_3 3_3_2 3_2_1 2_1_5 1_5_5 4_4_1 4_1_3 1_3_4 3_4_2 

   11     8     9    11     9    13     7     5     8     9    10     5    11    14     9     6 

4_2_2 2_2_2 2_2_1 2_1_3 1_3_3 2_1_1 1_1_2 1_2_3 2_3_2 3_2_5 2_5_1 3_4_1 1_3_2 2_1_4 1_4_5 4_5_2 

    8     8    14     6    11     8     3     9    11    13    10    15    12     8     8     6 

5_2_4 2_4_1 4_1_2 1_2_2 2_2_5 2_5_3 5_3_5 5_4_3 4_3_4 3_4_4 4_4_4 3_2_2 2_2_4 2_4_2 4_2_1 1_2_4 

    9    10    12     7     7     7     6     8    10    11     4     8     8     9     7    10 

5_5_5 5_1_1 1_1_4 1_4_2 4_2_4 1_4_1 3_3_3 3_2_3 2_3_5 1_5_4 5_4_1 3_3_4 1_1_1 1_4_4 3_4_5 5_4_5 

    4    10     6     7     7     3     7    15     6     7    10    11     4     7     9     9 

4_5_3 5_3_2 2_3_1 3_1_1 1_1_5 5_2_2 2_4_5 3_2_4 2_2_3 2_3_4 4_2_3 4_1_5 4_4_2 3_5_2 2_3_3 5_5_2 

   13    11     7     8    12    12     9     2     6     9     5     8     5     4     7     7 

2_5_4 1_1_3 4_1_4 5_2_5 3_3_1 3_1_2 1_5_1 5_1_5 1_5_3 4_2_5 5_4_2 3_5_1 5_3_1 3_1_4 1_2_1 4_5_5 

   10    11     8     4     8     6     8     9    11     7     7     6     5     5     7     5 

4_1_1 5_1_2 5_2_3 3_1_3 2_5_2 4_3_2 3_5_4 2_4_4 5_5_3 1_3_1 4_5_1 1_4_3 5_1_4 

    6    11     5     5     7     5     6     3     3     4     7     6     4 

 




이제 발생 빈도를 기준으로 패턴 리스트를 내림차순 정렬(sort in descending order)을 해보겠습니다. 

'5_3_3', '3_4_1', '3_2-3' 패턴이 총 15번 발생해서 공동 1등을 하였네요. 



> # sorting pattern_list in descending order

> sort(pattern_list, decreasing = TRUE)

5_3_3  3_4_1  3_2_3  4_1_3 2_2_1 1_5_2 4_5_4 3_2_5 4_5_3 2_1_2 1_3_2 4_1_2 1_1_5 5_2_2 3_3_5 1_2_5 

   15     15      15     14    14    13    13    13    13    12    12    12    12    12    11    11 

5_4_4 4_4_1 1_3_3 2_3_2 3_4_4 3_3_4 5_3_2 1_1_3 1_5_3 5_1_2 3_5_5 3_5_3 2_1_5 2_5_1 2_4_1 4_3_4 

   11    11    11    11    11    11    11    11    11    11    10    10    10    10    10    10 

1_2_4 5_1_1 5_4_1 2_5_4 5_5_1 5_5_4 4_4_5 3_2_1 1_3_4 1_2_3 5_2_4 2_4_2 3_4_5 5_4_5 2_4_5 2_3_4 

   10    10    10    10     9     9     9     9     9     9     9     9     9     9     9     9 

5_1_5 2_5_5 3_3_2 4_2_2 2_2_2 2_1_1 2_1_4 1_4_5 5_4_3 3_2_2 2_2_4 3_1_1 4_1_5 4_1_4 3_3_1 1_5_1 

    9     8     8     8     8     8     8     8     8     8     8     8     8     8     8     8 

4_3_5 5_3_4 5_2_1 4_4_3 1_2_2 2_2_5 2_5_3 4_2_1 1_4_2 4_2_4 3_3_3 1_5_4 1_4_4 2_3_1 2_3_3 5_5_2 

    7     7     7     7     7     7     7     7     7     7     7     7     7     7     7     7 

4_2_5 5_4_2 1_2_1 2_5_2 4_5_1 2_4_3 5_1_3 1_3_5 3_4_2 2_1_3 4_5_2 5_3_5 1_1_4 2_3_5 2_2_3 3_1_2 

    7     7     7     7     7     6     6     6     6     6     6     6     6     6     6     6 

3_5_1 4_1_1 3_5_4 1_4_3 3_4_3 4_3_1 3_1_5 4_3_3 1_5_5 4_2_3 4_4_2 5_3_1 3_1_4 4_5_5 5_2_3 3_1_3 

    6     6     6     6     5     5     5     5     5     5     5     5     5     5     5     5 

4_3_2 4_4_4 5_5_5 1_1_1 3_5_2 5_2_5 1_3_1 5_1_4 1_1_2 1_4_1 2_4_4 5_5_3 3_2_4 

    5     4     4     4     4     4     4     4     3     3     3     3     2

 




위의 패턴별 발생 빈도수 기준으로 정렬된 결과를 DataFrame으로 변환해서 상위 10개 패턴을 프린트해보겠습니다. 



> # convert a list to DataFrame

> cnt_per_pattern_sorted <- sort(pattern_list, decreasing = TRUE)

> pattern <- names(cnt_per_pattern_sorted)

> df <- data.frame(pattern, cnt_per_pattern_sorted)

> rownames(df) <- NULL

> # display top 10 patterns

> df[1:10,]

   pattern     cnt_per_pattern_sorted

1    5_3_3                     15

2    3_4_1                     15

3    3_2_3                     15

4    4_1_3                     14

5    2_2_1                     14

6    1_5_2                     13

7    4_5_4                     13

8    3_2_5                     13

9    4_5_3                     13

10   2_1_2                     12

 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. ^^



Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 최성원 2019.10.16 14:15  댓글주소  수정/삭제  댓글쓰기

    항상 감사드립니다. 질문이 있습니다!
    수집된 패턴들을 가지고 다른 분석을 하고 싶은데요, 시계열 군집분석이나 다변량 군집분석이요...
    그래서 데이터 타입을 "list" 형태로 변환해봤는데 에러가 나더라구요.
    제가 데이터 타입에 대한 깊은 이해가... 부족해서 그런것 같은데
    질문은...
    위의 방법으로 패턴 수집했을때 데이터가 어떤 형태로 저장되는 건가요?
    그리고 리스트나 데이터 프레임의 형태로 저장은 어떻게 하나요?

    • R Friend R_Friend 2019.10.16 14:22 신고  댓글주소  수정/삭제

      안녕하세요.

      현재 분석 결과는 리스트로 저장을 해둔 상태입니다.

      만약 패턴별 발생빈도 리스트를 데이터프레임으로 변경을 하고 싶으시면,

      df <- data.frame(matrix(unlist(pattern_list), nrow=length(pattern_list), byrow=T), stringAsFactors=FALSE)

      로 하시면 됩니다. (만약 character 를 factor type으로 해서 분석할 경우라면 stringAsFactors 옵션을 빼주면 됩니다)

      ps. 제가 이번주는 너무 바빠서 더이상은 못도와드리겠스니다. 죄송합니다. 이해해주세요. ㅜ.ㅜ

    • 최성원 2019.10.16 16:28  댓글주소  수정/삭제

      지금도 너무 감사드려요 ㅠㅠ

    • R Friend R_Friend 2019.10.16 16:29 신고  댓글주소  수정/삭제

      네, 이해해주셔서 고맙습니다. 아무쪼록 순탄하게 잘 해결하시기를 바래요.

    • R Friend R_Friend 2019.10.17 23:50 신고  댓글주소  수정/삭제

      본문 하단에 DataFrame으로 변환하는 코드도 update 했습니다. ^^

  2. 질문드립니다. 2019.10.24 10:16  댓글주소  수정/삭제  댓글쓰기

    매번 친절하게 알려주셔서 감사드립니다^^

    데이터 프레임에 제품 On/off 시간에 대해서 수집하고 있는데
    제품을 동작하고 끄기까지의 사용시간을 계산하고 싶은데 너무 초보라
    이해도가 부족해서 코딩을 하기가 힘들어서 문의드립니다..
    아래처럼 시작 시간 정지 시간 별로 시간 데이터가 입력이 되는 구조입니다.

    DEVICE | time | state | sum |
    ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
    A |2018-07-01 10:20:23| ON | |
    A |2018-07-01 10:30:55| ON | |
    A |2018-07-01 11:11:01| ON | |
    A |2018-07-01 11:51:41| OFF | |
    B |2018-07-02 07:11:02| ON | |
    B |2018-07-02 09:00:33| ON | |
    B |2018-07-02 09:20:24| OFF | |
    C |2018-07-02 12:12:21| ON | |
    C |2018-07-02 14:01:09| OFF | |
    C |2018-07-02 18:11:41| ON | |
    C |2018-07-02 19:21:51| OFF | |

    DEVICE 별로 시작과 정지 사이의 시간을 계산해서 합한 후에 열을 추가해서 기입하고
    A 제품처럼 ON과 OFF 사이에 ON 이 여러개 들어있으면 이것들을 제거하고 처음ON과
    마지막 OFF 사이만 계산하게끔 하고 싶습니다..ㅠ
    최종적인 아웃풋이 제품 별 평균 사용시간을 정의하는 것입니다.
    도움 부탁드립니다..ㅠ

이번 포스팅에서는 


(1) R ggplot2를 이용하여 커널 밀도 곡선(Kernel Density Curve)을 그리기

(2) 커밀 밀도 곡선의 최대 피크값의 좌표 구하기 
   (X, Y coordinates of the peak value in kernel density curve)

(3) 커널 밀도 곡선의 최대 피크값 위치에 수직선을 추가하는 방법 (adding a vertical line at peak value in kernel density plot) 을 소개하겠습니다. 




  (1) R ggplot2로 커널 밀도 곡선 그리기 (Kernel Density Curve by R ggplot2)


예제로 사용할 데이터는 MASS 패키지에 내장되어 있는 Cars93 데이터프레임입니다. 자동차의 가격(Price 변수)에 대해서 R ggplot2로 커널 밀도 곡선 (not frequency, but density) 을 그려보겠습니다. 


> library(MASS)

> library(ggplot2)

> ggplot(Cars93, aes(x=Price)) + 

+   geom_density(fill = 'yellow') + 

+   geom_line(stat = "density") + 

+   expand_limits(y = 0) + 

+   ggtitle("Kernel Density Curve") 






(2) 커널 밀도 곡선의 최대 피크값의 좌표 구하기 

     (X, Y coordinates of the peak value in kernel density curve)


density(df$var) 함수를 사용하면 x 와 y 값의 요약통계량 (최소, Q1, 중앙값, Q3, 최대값)을 확인할 수 있습니다. y 값의 최대값(Max)은 5.025e-02 값이네요. 


 

> density(Cars93$Price)


Call:

density.default(x = Cars93$Price)


Data: Cars93$Price (93 obs.); Bandwidth 'bw' = 3.011


       x                y            

 Min.   :-1.634   Min.   :1.608e-05  

 1st Qu.:16.508   1st Qu.:9.773e-04  

 Median :34.650   Median :5.329e-03  

 Mean   :34.650   Mean   :1.377e-02  

 3rd Qu.:52.792   3rd Qu.:2.078e-02  

 Max.   :70.934   Max.   :5.025e-02




이중에서 y 값 peak 값의 x 좌표를 알고 싶으므로 (a) which.max(density(Cars93$Price)$y) 로 y값의 최대 peak 값을 가지는 데이터의 index를 찾고 (127번째 데이터), 이 index를 이용해서 x 값의 좌표(16.25942)를 indexing 해올 수 있습니다. 



> which.max(density(Cars93$Price)$y)

[1] 127

> x_coord_of_y_max = density(Cars93$Price)$x[127]

> x_coord_of_y_max

[1] 16.25942

 


즉, y (Price) 최대 피크값의 좌표는 (x, y) 는 (16.25922, 0.05025) 가 되겠습니다. 




 (3) 커널 밀도 곡선의 최대 피크값 위치에 수직선을 추가하기

     (adding a vertical line at peak value in kernel density plot)


geom_vline() 으로 (2)번에서 구한 y 피크값의 x 좌표를 입력해주면 커널밀도곡선의 최대 피크값 위치에 수직선(vertical line)을 추가할 수 있습니다. 



> ggplot(Cars93, aes(x=Price)) + 

+   geom_density(fill = 'yellow') + 

+   geom_line(stat = "density") + 

+   expand_limits(y = 0) + 

+   ggtitle("Kernel Density Curve w/ Vertical Line at Peak Point") +

+   geom_vline(xintercept = x_coord_of_y_max, color='blue', size=2, linetype="dotted")


 





  (4) 여러개 그룹의 커널 밀도 곡선을 겹쳐 그린 경우 최대 peak 값 찾고 수직선 그리기


위의 예는 1개의 데이터셋에 대해 1개의 커널 밀도 곡선을 그린 후 최대 peak 값을 찾는 것이었습니다. 이제부터는 여러개의 하위 그룹으로 나뉘어진 데이터셋으로 여러개의 커널 밀도 곡선을 겹쳐서 그린 경우에 최대 peak 값을 찾고, 그 위치에 빨간색으로 수직선을 추가해보겠습니다. 


예제로 Cars93 데이터프레임에서 차종(Type) 별 가격(Price)의 커널 밀도 곡선을 겹쳐서 그려보겠습니다. 


> ggplot(Cars93, aes(x=Price, colour = Type)) + 

+   geom_density(fill = NA) + 

+   geom_line(stat = "density") + 

+   expand_limits(y = 0) + 

+   ggtitle("Kernel Density Curve by Car Types")





차종(Type) 중에서 Van 의 커널 밀도 곡선이 최대 Peak 값에 해당하므로 --> 차종 중에서 Van 의 데이터만 가져와서 y 최대값과 이에 해당하는 관측치의 index 위치를 찾아보겠습니다. 



> density(Cars93[Cars93$Type=='Van', ]$Price)


Call:

density.default(x = Cars93[Cars93$Type == "Van", ]$Price)


Data: Cars93[Cars93$Type == "Van", ]$Price (9 obs.); Bandwidth 'bw' = 0.303


       x               y            

 Min.   :15.39   Min.   :0.0000072  

 1st Qu.:17.45   1st Qu.:0.0040424  

 Median :19.50   Median :0.0495649  

 Mean   :19.50   Mean   :0.1215343  

 3rd Qu.:21.55   3rd Qu.:0.1807290  

 Max.   :23.61   Max.   :0.5321582  

> which.max(density(Cars93[Cars93$Type=='Van', 'Price'])$y)

[1] 238

> x_coord_of_y_max = density(Cars93[Cars93$Type=='Van','Price'])$x[238]

> x_coord_of_y_max

[1] 19.20249

 


밀도 y의 최대값(max)은 0.532 이며, 이때의 관측치의 index는 238번째 값 이고, 관측치 238번째 값의 x값의 좌표는 19.20 입니다. 


따라서, 밀도 y 의 최대 peak 값의 좌표는 (x, y) = (19.20, 0.53) 이네요. 



마지막으로, geom_vline() 을 사용해서 밀도 최대 peak 값에 해당하는 x=19.20 을 기준으로 빨간색 점선 수직선(vertical line)을 추가해보겠습니다. 



> ggplot(Cars93, aes(x=Price, colour = Type)) + 

+   geom_density(fill = NA) + 

+   geom_line(stat = "density") + 

+   expand_limits(y = 0) + 

+   ggtitle("Kernel Density Curve w/ Vertical Line at Peak Point") +

+   theme_bw() +

+   geom_vline(xintercept = x_coord_of_y_max, color='red', size=1, linetype="dashed")






R ggplot2로 히스토그램, 커널밀도곡선을 그리는 다양한 방법은 https://rfriend.tistory.com/67 를 참고하세요. 


많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. ^^


Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 스물스물 2019.10.04 14:36  댓글주소  수정/삭제  댓글쓰기

    올려주신 답변 보고 작성해주신 글 확인하였습니다. 감사합니다.

    추가적으로 질문을 드리면, 올려주신 글에서는 Cars93$Price에 대한 density plot을 그리고 그에 해당하는 x,y 좌표값을 구하는 방식 & 그래프에 최대 peak 를 나타내는 방법인것 같습니다.

    만약, density plot을 여려개를 겹쳐 그려서, 예를 들면, 내장되어 있는(Cars93$price)에는 num[1:93]까지의 데이터를 이루고 있지만, 만약 price1, pirce2, price3...이렇게 여러개의 데이터를 하나로 이루어서 density plot을 그렸다면, 그림자체도 여러개의 그래프가 겹쳐서 나타나게 될 것이고, 그에 따른 최대 peak값을 따로 구해야할것 같은데,
    개인적으로는, 전체 합쳐진 데이터내에서 최대 peak값을 구하고 싶은데 이럴 때에는 어떻게 해야 할까요?

이번 포스팅에서는 R의 DataFrame에서 특정 조건에 해당하는 값의 행과, 해당 행의 앞, 뒤 2개 행(above and below 2 rows) 을 동시에 제거하는 방법을 소개하겠습니다. 




예를 들어서, 아래와 같이 var1, var2의 두 개의 변수를 가지는 df라는 이름의 DataFrame이 있다고 했을 때, var2의 값 중 음수(-)인 값을 가지는 행과, 해당 행의 위, 아래 2개 행을 같이 제거(remove, filter) 해서 df2 라는 이름의 새로운 DataFrame을 만들어보겠습니다. 



> var1 <- c(1:12)

> var2 <- c(100, -200, 101, 1102, 50, 300, 100, 400, -100, 82, 90, 80)

> df <- data.frame(var1, var2)

> df

   var1 var2

1     1  100

2     2 -200

3     3  101

4     4 1102

5     5   50

6     6  300

7     7  100

8     8  400

9     9 -100

10   10   82

11   11   90

12   12   80

 



먼저, 칼럼 var2 에서 음수(-)인 값의 조건을 만족하는 값의 행의 위치를 indexing 한 negative_idx 벡터를 만들어보겠습니다. 



> # condition: if var2 value is negative, then TRUE

> negative_idx <- df$var2 < 0

> negative_idx

 [1] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE




다음으로 칼럼 var2의 값이 음수(-)인 값의 위치를 기준으로 해당 값의 행과 앞, 뒤 2개행까지는 제거(remove, filter)하고, 그 외의 값은 유지(keep_idx = TRUE) 하는 keep_idx 라는 벡터를 for loop 반복문과 if 조건문을 사용해서 만들어보겠습니다. 



> keep_idx <- c(rep(TRUE, length(df$var2)))

> keep_idx

 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

> for (i in 1:length(negative_idx)) {

 if (negative_idx[i] == TRUE) {

+     keep_idx[i-2] = FALSE

+     keep_idx[i-1] = FALSE

+     keep_idx[i] = FALSE

+     keep_idx[i+1] = FALSE

+     keep_idx[i+2] = FALSE

 }

+ }

> keep_idx

 [1] FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE

 



이제 df라는 DataFrame에서 위에서 구한 keep_idx = TRUE 인 행만 indexing 해서 (즉, keep_idx = FALSE 인 행은 제거) 새로운 df2 라는 DataFrame을 만들어보겠습니다. 



> # subset only rows with keep_idx = TRUE

> df_filstered <- df[keep_idx, ]

> df_filstered

   var1 var2

5     5   50

6     6  300

12   12   80




위는 예는 조건문과 반복문을 사용해서 indexing 해오는 방법이었구요, windows 함수인 lag(), lead()를 사용해서도 동일한 기능을 수행하는 프로그램을 짤 수도 있습니다. 다만, 이번 포스팅에서 소개한 코드가 좀더 범용적이고 코드도 짧기 때문에 lag(), lead() 함수를 사용한 방법은 추가 설명은 하지 않겠습니다. 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요.



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 수식 표기(mathematical notation)를 영어로 어떻게 읽는지 정리한 표를 소개하겠습니다. 

통계, 기계학습, 딥러닝 공부할 때 수식이 나올 때면 '이걸 어떻게 읽어야 하나?'하고 궁금했던 적이 있을 것입니다. 한글로 된 책을 읽거나 강의를 들을 때면 '저렇게 읽는게 맞나? 저렇게 읽은 것을 듣고서 거꾸로 수식으로 정확하게 옮길 수 있나?' 하는 의문을 가진 적도 있을 것이구요. 

수식을 영어로 읽는 법을 알고 있으면 많은 도움이 되겠다는 생각에 한번은 정리를 해봐야지...하고 미루고 있던 차에요,  'Deep Learning' (by Ian Goodfellow, Yoshua Bengio, and Aaron Courville, The MIT Press) 책에서 깔끔하게 정리된 자료를 찾았습니다. 


1. 수와 배열 (Numbers and Arrays)


2. 집합과 그래프 (Sets and Graphs)


3. 인덱싱 (Indexing)


4. 선형대수 연산 (Linear Algebra Operations)


5. 미적분 (Calculus)


6. 확률과 정보 이론 (Probability and Information Theory)


7. 함수 (Functions)


8. 데이터셋과 분포 (Datasets and Distributions)

* source:  'Deep Learning' (by Ian Goodfellow, Yoshua Bengio, and Aaron Courville, The MIT Press)

Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 R Shiny를 사용하여

 

- (1) 두 개의 독립 표본 집단 간 평균 차이 t-검정 (independent two-sample t-test) 

- (2) 신뢰수준 별 신뢰구간 (confidence interval by confidence level)

을 구하는 웹 애플리케이션을 만들어보겠습니다. 

 

예제로 사용한 데이터는 MASS 패키지의 Cars93 데이터프레임이며, 'Origin'  변수의 두 개 집단(USA, Non-USA) 별로 Price, RPM, Length, Width 변수 간의 t-test 를 해보겠습니다. 

 

왼쪽 사이드바 패널에는 t-검정을 할 변수를 선택할 수 있는 콤보 박스와, 신뢰수준(confidence level)을 지정할 수 있는 슬라이드 입력(default = 0.95) 화면을 만들었습니다. 

그리고 메인 패널에는 변수 이름, 신뢰수준, t-검정 결과를 텍스트로 보여줍니다. 

 

R Shiny - independent two-sample t-test

 

library(shiny)

# Define UI for application that does t-test

ui <- fluidPage(

   

   # Application title

   titlePanel("Independent two sample t-test"),

   

   # Select a variable to do t-test by Origin (USA vs. non-USA groups)

   sidebarPanel(

     selectInput("var_x", 

                 label = "Select a Variable to do t-test", 

                 choices = list("Price"="Price", 

                                "RPM"="RPM", 

                                "Length"="Length", 

                                "Width"="Width"), 

                 selected = "Price"), 

     

     sliderInput("conf_level", "Confidence Level:", 

                  min = 0.05, max = 0.99, value = 0.95)

     ),

   

   # Show a t-test result

   mainPanel(

     h5("Variable is:"),

     verbatimTextOutput("var_x_name"), 

     h5("Confidence Level is:"),

     verbatimTextOutput("conf_level"), 

     h4("t-test Result by Origin (USA vs. non-USA independent two samples)"), 

     verbatimTextOutput("t_test")

   )

)

# Define server logic required to do t-test

server <- function(input, output) {

  

  library(MASS)

  cars93_sub <- Cars93[, c('Origin', 'Price', 'RPM', 'Length', 'Width')]

  

  output$var_x_name <- renderText({

    as.character(input$var_x)

  })

  

  output$conf_level <- renderText({

    as.character(input$conf_level)

  })

  

  output$t_test <- renderPrint({

    # independent two-sample t-test

    t.test(cars93_sub[,input$var_x] ~ Origin, 

           data = cars93_sub, 

           alternative = c("two.sided"), 

           var.equal = FALSE, 

           conf.level = input$conf_level)

  })

}

 

# Run the application 

shinyApp(ui = ui, server = server)

 

많은 도움이 되었기를 바랍니다. 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 꾸리꾸리 2019.09.10 18:17  댓글주소  수정/삭제  댓글쓰기

    최근 shiny 를 공부하고 있는데 많은 도움이 되었습니다.
    통계 관련해서는 자세히 모르지만, 통계분석도 시각화가 가능한가요?

이번 포스팅에서는 R Shiny를 사용하여 interactive하게 편하고 쉽게 두 연속형 변수 간 관계를 알아볼 수 있도록 

(1) 상관계수 (Correlation Coefficient)를 구하고, 

(2) 산점도 (Scatter Plot)을 그린 후, 

(3) 선형 회귀선 (Linear Regression Line) 을 겹쳐서 그려주는 

웹 애플리케이션을 만들어보겠습니다. 

 

 

R Shiny - Correlation Coefficient, Scatter Plot, Linear Regression Line

 

예제로 사용할 데이터는 MASS 패키지에 내장되어 있는 Cars93 데이터프레임으로서, Price, MPG.city, MPG.highway, EngineSize, Horsepower, RPM 의 6개 연속형 변수를 선별하였습니다. 

 

왼쪽 사이드바 패널에는 산점도의 X축, Y축에 해당하는 연속형 변수를 콤보박스로 선택할 수 있도록 하였으며, 상관계수와 선형회귀모형을 적합할 지 여부를 선택할 수 있는 체크박스도 추가하였습니다. 

 

오른쪽 본문의 메인 패널에는 두 연속형 변수에 대한 산점도 그래프에 선형 회귀선을 겹쳐서 그려서 보여주고, 상관계수와 선형회귀 적합 결과를 텍스트로 볼 수 있도록 화면을 구성하였습니다. 

 

library(shiny)

# Define UI for application that analyze correlation coefficients and regression

ui <- fluidPage(

   

   # Application title

   titlePanel("Correlation Coefficients and Regression"),

   

   # Select 2 Variables, X and Y 

   sidebarPanel(

     selectInput("var_x", 

                 label = "Select a Variable of X", 

                 choices = list("Price"="Price", 

                                "MPG.city"="MPG.city", 

                                "MPG.highway"="MPG.highway", 

                                "EngineSize"="EngineSize", 

                                "Horsepower"="Horsepower", 

                                "RPM"="RPM"), 

                 selected = "RPM"),

     

     selectInput("var_y", 

                 label = "Select a Variable of Y", 

                 choices = list("Price"="Price", 

                                "MPG.city"="MPG.city", 

                                "MPG.highway"="MPG.highway", 

                                "EngineSize"="EngineSize", 

                                "Horsepower"="Horsepower", 

                                "RPM"="RPM"), 

                 selected = "MPG.highway"), 

     

     checkboxInput(inputId = "corr_checked", 

                   label = strong("Correlation Coefficients"), 

                   value = TRUE), 

     

     checkboxInput(inputId = "reg_checked", 

                   label = strong("Simple Regression"), 

                   value = TRUE)

      ),

      # Show a plot of the generated distribution

      mainPanel(

        h4("Scatter Plot"), 

        plotOutput("scatterPlot"), 

        

        h4("Correlation Coefficent"), 

        verbatimTextOutput("corr_coef"), 

        

        h4("Simple Regression"), 

        verbatimTextOutput("reg_fit")

      )

   )

# Define server logic required to analyze correlation coefficients and regression

server <- function(input, output) {

  library(MASS)

  scatter <- Cars93[,c("Price", "MPG.city", "MPG.highway", "EngineSize", "Horsepower", "RPM")]

  

  # scatter plot

  output$scatterPlot <- renderPlot({

    var_name_x <- as.character(input$var_x)

    var_name_y <- as.character(input$var_y)

    

    plot(scatter[, input$var_x],

         scatter[, input$var_y],

         xlab = var_name_x,

         ylab = var_name_y,

         main = "Scatter Plot")

    

    # add linear regression line

    fit <- lm(scatter[, input$var_y] ~ scatter[, input$var_x])

    abline(fit)

    })

  

  # correlation coefficient

  output$corr_coef <- renderText({

    if(input$corr_checked){

      cor(scatter[, input$var_x], 

          scatter[, input$var_y])

      }

    })

  

  # simple regression

  output$reg_fit <- renderPrint({

    if(input$reg_checked){

      fit <- lm(scatter[, input$var_y] ~ scatter[, input$var_x])

      names(fit$coefficients) <- c("Intercept", input$var_x)

      summary(fit)$coefficients

    }

  })

}

# Run the application 

shinyApp(ui = ui, server = server)

 

많은 도움이 되었기를 바랍니다. 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

R Shiny를 이용하면 탐색적 데이터 분석(Exploratory Data Analysis)을 할 때 요약 통계량, 그래프를 변수, 기간, 통계량, 그래프 종류 등을 자유롭게 조건 선택하고 조합하여 편리하게 사용할 수 있습니다. 

 

이번 포스팅에서는 R Shiny로 DataFrame 의

(1) 특정 연속형 변수를 선택하고 

(2) 분석하고 싶은 요약 통계량을 선택했을 때 

(3) 요약 통계량 결과를 interactive하게 볼 수 있는 웹 애플리케이션을 만들어보겠습니다. 

 

Output Image는 아래와 같습니다. 

 

[ R Shiny 를 활용한 연속형 변수 요약 통계량 조회 웹 애플리케이션 화면 ]

R Shiny - Summary Statistics

 

먼저 R Studio 에서 'File > New File > Shiny Web App' 을 선택해주세요. 

 

 

다음으로, (a) 애플리케이션의 이름, (b) 애플리케이션 유형 (간단한 것이면 하나의 파일에 UI와 Server를 함께 만드는게 편하구요, 복잡한 것이면 ui.R, server.R 의 두 개의 파일로 분리하는 것이 편리함), (c) 애플리케이션 저장 경로 (위치)를 지정해주고, Create 해줍니다. 

 

 

이제 R Shiny 코드를 짜볼 텐데요,

먼저 UI 부분에서는 (a) 왼쪽 사이드바에서 분석 대상이 되는 변수를 콤보 박스로 선택하는 화면, (b) 왼쪽 사이드바에서 요약 통계량을 선택하는 체크 박스 화면, (c) 중앙 메인 패널에서 보여줄 제목과 요약 통계량 테이블을 지정해줍니다. 

 

그리고 Server 부분에서는 (a) MASS 패키지의 Cars93 데이터 프레임으로 부터 일부 6개의 연속형 변수 데이터 읽어오기, (b) 요약 통계량 계산하기 (단, 결측값은 제외하기), (c) 요약 통계량들을 하나의 데이터 프레임으로 만들기, (d) 데이터 프레임 테이블을 렌더링 해줍니다. 

 

#------------------------------------------------------------------ 
# Summary Statistics of a Continuous Variable using RShiny 
#------------------------------------------------------------------

library(shiny)

# Define UI for application that calculate summary statistics
ui <- fluidPage(
   
   # Application title
   titlePanel("Descriptive Statistics of Automobiles"),
   
   # select Input from lists 
   sidebarPanel(
     selectInput("var", "Select a Variable to Analyze", 
                 choice = c("Price"=1, 
                            "MPG.city"=2, 
                            "MPG.highway"=3, 
                            "EngineSize"=4, 
                            "Horsepower"=5, 
                            "RPM"=6), 
                 selectize = F), 
     
     checkboxGroupInput("checked", "Check summary statistics", 
                        list(mean = "mean", 
                             std_dev = "std_dev", 
                             median = "median", 
                             min = "min", 
                             max = "max", 
                             obs_num = "obs_num", 
                             quartile_1 = "quartile_1", 
                             quartile_3 = "quartile_3"), 
                        selected = "mean")
   ), 
   mainPanel(
     h4("Summary Statistics of a variable"),
     tableOutput("desc_statistics")
   )
)

# Define server logic required to calculate summary statistics
server <- function(input, output) {
  # read dataset
  library(MASS)
  descriptive <- Cars93[,c("Price", "MPG.city", "MPG.highway", "EngineSize", "Horsepower", "RPM")]

  output$desc_statistics <- renderTable({
    # summary statistics
    variable <- colnames(descriptive)[as.numeric(input$var)]
    mean <- mean(descriptive[, as.numeric(input$var)], na.rm=TRUE)
    std_dev <- sd(descriptive[, as.numeric(input$var)], na.rm=TRUE)
    median <- median(descriptive[, as.numeric(input$var)], na.rm=TRUE)
    min <- min(descriptive[, as.numeric(input$var)], na.rm=TRUE)
    max <- max(descriptive[, as.numeric(input$var)], na.rm=TRUE)
    obs_num <- nrow(descriptive)
    quartile_1 <- quantile(descriptive[, as.numeric(input$var)], 0.25, na.rm=TRUE)
    quartile_3 <- quantile(descriptive[, as.numeric(input$var)], 0.75, na.rm=TRUE)
    
    # make a DataFrame
    desc_summary <- data.frame(variable, 
                               mean, std_dev, 
                               median, min, max, 
                               obs_num, 
                               quartile_1, quartile_3)
    
    # render Table
    desc_summary[, c("variable", input$checked), drop = FALSE]
  })
}

 


# Run the application 
shinyApp(ui = ui, server = server)

 

 

마지막으로, shinyApp(ui = ui, server = server) 로 UI와 Server를 지정해주고, 저장한 후에, RStudio 우측 상단의 'Run App' 단추를 눌러서 실행시켜주면 됩니다. 

 

많은 도움이 되었기를 바랍니다. 

 

Posted by R Friend R_Friend

댓글을 달아 주세요