이번 포스팅에서는 (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 사이만 계산하게끔 하고 싶습니다..ㅠ
    최종적인 아웃풋이 제품 별 평균 사용시간을 정의하는 것입니다.
    도움 부탁드립니다..ㅠ

  3. JHK 2019.12.02 01:40  댓글주소  수정/삭제  댓글쓰기

    이전의 질문에 대한 답변 감사했습니다!

    본문내용과는 좀 다르지만 전처리에 대한 질문을 하고싶어서요!

    데이터에서
    한id변수에 대해 기록된 정보가 여러개라서 예를 들어 id 변수에서 001이라는 아이디를 가진 사람의 정보가 약 200여개씩 존재합니다

    id
    001
    001
    001
    .
    .
    .
    이때 다른 것은 각 행마다 기록되는 시간이 다른데요, 그 기록된 시간마다 work라는 변수의 내용이 다릅니다.

    id work time
    001 작동준비 2018-10-23 03:22
    001 작동시작 2018-10-23 03:25
    001 ab투여 2018-10-23 03:43
    .
    .
    .

    이런식으로 구성되어 있는데요
    저는 work변수의 작동시작이라는 값이 해당하는 행에서 가지는 time변수의 시각을 기준으로 이전의 시각을 가지는 행들을 불러오고 싶습니다.

    어떻게 해야할지 잘 모르겠네요 ㅜㅜ
    time변수는 다루기 편하게 문자열과 공백을 지우고 대소비교가 가능하도록
    201810230322 이런식으로 바꾸어 놓은 상태입니다!

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

      제가 저녁에 답글 남길께요

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

      안녕하세요 JHK님,

      만약 id 별로 work 중에 '작동시작' 이 "단 한번만 존재"한다는 가정 하에

      (1) id별로 work = '작동시작' 인 행만을 가져와서 dataframe을 만들고 (df2)

      (2) 이를 id 키를 기준으로 원래의 dataframe(df)에 df2를 merge 한 후에

      (3) df2의 작동시작 시간(work_prepair_time) 보다 작은 time 인 경우만 가져와서 새로운 dataframe(df3)을 만드는 순서로 코드 작성해 보았습니다.

      (아래 코드에서 'w2' = '작동시작'으로 간주하시면 됩니다)

      > id <- c(rep('001', 3), rep('002', 4))
      > work <- c('w1', 'w2', 'w3', 'w0', 'w1', 'w2', 'w3')
      > time <- c(201810230322, 201810230325, 201810230343, 201810230222, 201810230322, 201810230325, 201810230343)
      >
      > df <- data.frame(id, work, time)
      > df
      id work time
      1 001 w1 201810230322
      2 001 w2 201810230325
      3 001 w3 201810230343
      4 002 w0 201810230222
      5 002 w1 201810230322
      6 002 w2 201810230325
      7 002 w3 201810230343
      >
      > work_prepair_time <- subset(df,
      + select = c('id', 'time'),
      + subset = (work == 'w2'))
      > names(work_prepair_time) <- c('id', 'work_prepair_time')
      > work_prepair_time
      id work_prepair_time
      2 001 201810230325
      6 002 201810230325
      >
      > df2 <- merge(df, work_prepair_time, by = 'id')
      > df2
      id work time work_prepair_time
      1 001 w1 201810230322 201810230325
      2 001 w2 201810230325 201810230325
      3 001 w3 201810230343 201810230325
      4 002 w0 201810230222 201810230325
      5 002 w1 201810230322 201810230325
      6 002 w2 201810230325 201810230325
      7 002 w3 201810230343 201810230325
      >
      > df3 <- subset(df2,
      + select = c('id', 'work', 'time'),
      + subset = (time < work_prepair_time))
      >
      > df3
      id work time
      1 001 w1 201810230322
      4 002 w0 201810230222
      5 002 w1 201810230322


      만약, 각 id 별로 work에서 '작동시작' 이 두번 이상 발생하는 경우도 있다면, 이럴 경우 처리하는 로직이 무엇이냐에 따라서 코드를 다르게 짜야 할텐데요, 혹시 이런 경우라면 댓글 남겨주세요.

    • JHK 2019.12.03 07:06  댓글주소  수정/삭제

      정말 친절한 설명 감사해요!ㅜㅜ!!
      막상 데이터분석을 해보니 전처리가 정말
      힘든 것 같아요! 감사합니다 :)

    • R Friend R_Friend 2019.12.03 09:28 신고  댓글주소  수정/삭제

      문제가 해결되었다니 기쁘네요.