ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • R을 이용한 로또 1등 당첨번호 파헤치기
    잡다R 2019. 11. 18. 20:06

    " 로또에 당첨되고 싶어요 "

     로또에 당첨되고 싶은 염원을 담아서 첫 번째 잡다R 주제를 로또로 정해봤습니다. 짝짝짝 ^-^

     

     먼저 R을 이용해서 기본세팅을 해주겠습니다. 필요한 library를 불러오고 로또 홈페이지에서 로또데이터를 받아오도록 하겠습니다. 저는 881회까지의 데이터를 이용하고 있으니, 참고해주세요!

    library(readxl)
    library(dplyr)
    library(ggplot2)
    library(tidyr)
    # getwd()
    
    lotto_raw <- read_xls("lotto.xls", 
                          col_names = FALSE)
    
    lotto <- lotto_raw

     데이터를 살펴보고 싶다면 이렇게 살펴보아요.

    str(lotto)
    ## Classes 'tbl_df', 'tbl' and 'data.frame':    884 obs. of  20 variables:
    ##  $ ...1 : chr  "회차별 추첨결과" "년도" NA "2019" ...
    ##  $ ...2 : chr  NA "회차" NA "881" ...
    ##  $ ...3 : chr  NA "추첨일" NA "2019.10.19" ...
    ##  $ ...4 : chr  NA "1등" "당첨자수" "8" ...
    ##  $ ...5 : chr  NA NA "당첨금액" "2,503,212,282원" ...
    ##  $ ...6 : chr  NA "2등" "당첨자수" "67" ...
    ##  $ ...7 : chr  NA NA "당첨금액" "49,815,170원" ...
    ##  $ ...8 : chr  NA "3등" "당첨자수" "2189" ...
    ##  $ ...9 : chr  NA NA "당첨금액" "1,524,722원" ...
    ##  $ ...10: chr  NA "4등" "당첨자수" "116952" ...
    ##  $ ...11: chr  NA NA "당첨금액" "50,000원" ...
    ##  $ ...12: chr  NA "5등" "당첨자수" "1939849" ...
    ##  $ ...13: chr  NA NA "당첨금액" "5,000원" ...
    ##  $ ...14: chr  NA "당첨번호" "1" "4" ...
    ##  $ ...15: num  NA NA 2 18 17 4 6 17 16 22 ...
    ##  $ ...16: num  NA NA 3 20 19 10 11 18 21 30 ...
    ##  $ ...17: num  NA NA 4 26 23 14 16 22 26 34 ...
    ##  $ ...18: num  NA NA 5 27 24 15 25 23 34 39 ...
    ##  $ ...19: num  NA NA 6 32 45 35 31 43 42 44 ...
    ##  $ ...20: chr  NA NA "보너스" "9" ...
    head(lotto)
    ## # A tibble: 6 x 20
    ##   ...1   ...2  ...3   ...4  ...5  ...6  ...7  ...8  ...9  ...10 ...11 ...12
    ##   <chr>  <chr> <chr>  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
    ## 1 회차별 추~ <NA>  <NA>   <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA>  <NA> 
    ## 2 년도   회차  추첨일 1등   <NA>  2등   <NA>  3등   <NA>  4등   <NA>  5등  
    ## 3 <NA>   <NA>  <NA>   당첨자수~ 당첨금액~ 당첨자수~ 당첨금액~ 당첨자수~ 당첨금액~ 당첨자수~ 당첨금액~ 당첨자수~
    ## 4 2019   881   2019.~ 8     2,50~ 67    49,8~ 2189  1,52~ 1169~ 50,0~ 1939~
    ## 5 <NA>   880   2019.~ 7     2,83~ 52    63,6~ 2332  1,41~ 1149~ 50,0~ 1939~
    ## 6 <NA>   879   2019.~ 6     3,20~ 77    41,6~ 2594  1,23~ 1234~ 50,0~ 1952~
    ## # ... with 8 more variables: ...13 <chr>, ...14 <chr>, ...15 <dbl>,
    ## #   ...16 <dbl>, ...17 <dbl>, ...18 <dbl>, ...19 <dbl>, ...20 <chr>

     column 명이 이상한 형식으로 저장이 되어 있습니다. 가독성을 높여주기 위해 rename을 이용하여 이름을 부여해주겠습니다. 이름을 부여할 때, 데이터 가공에 필요한 1등 번호 6개의 숫자만 정리해주도록 하겠습니다.

    # column 명이 제대로 들어가 있지 않으므로 정리가 필요함
    lotto <- rename(lotto, 
                    "num1" = ...14,
                    "num2" = ...15,
                    "num3" = ...16,
                    "num4" = ...17,
                    "num5" = ...18,
                    "num6" = ...19)

     데이터에 필요없는 column을 정리해주도록 합니다. 아울러 1:3의 row는 데이터의 분류 및 속성을 나타내는 행으로 들어가있습니다. 프로그래밍에 쓸모없고 오류를 일으키는 부분이기 때문에 이 부분도 삭제해줍니다.

    # 불필요한 row와 column 제거
    lotto <- lotto[-c(1, 2, 3), -c(1:13, 20)]

     여기까지의 과정을 통해 다음과 같은 데이터를 저장했습니다.

    # 데이터 확인을 원한다면
    # lotto %>% View
    lotto %>% head
    ## # A tibble: 6 x 6
    ##   num1   num2  num3  num4  num5  num6
    ##   <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
    ## 1 4        18    20    26    27    32
    ## 2 7        17    19    23    24    45
    ## 3 1         4    10    14    15    35
    ## 4 2         6    11    16    25    31
    ## 5 5        17    18    22    23    43
    ## 6 5        16    21    26    34    42

     이제 위에서 가공한 lotto 데이터의 1등 당첨 번호의 빈도수를 정리해주는 과정을 진행하겠습니다. lotto 데이터를 보면 각 회차의 1등 당첨 번호가 병렬식으로, 한 행에 나타나있는 것을 확인할 수 있습니다. 총 1부터 45까지의 숫자가 매트릭스 안에 놓여져 있네요. 매트릭스 안에서 나타나는 각 번호의 빈도수를 카운트해주겠습니다. tidyr의 gather를 통해 롱포맷 후 45개의 번호를 카운트해겠습니다.

    joker <- lotto %>% 
      gather(key   = "number",
             value = "num_count",
             num1:num6) %>%
      count(num_count)

     설명은 다음과 같습니다. 조커를 하나 만들어주고, 조커에 1번부터 6번까지(이하 num1:num6)의 숫자를 key = "number", value = "num_count" 라는 모습으로 롱포맷해줍니다. 그러면 number에는 num1:num6가, num_count라는 column에는 num1:num6의 매트릭스가 한 줄로 나타납니다. 그렇다면 count를 이용하여 num_count의 빈도수를 구해주어 1부터 45까지의 번호들이 얼마나 1등 당첨 번호로 뽑혔는지 그 빈도수를 알 수 있을 거에요. 결과를 확인해보겠습니다.

    head(joker)
    ## # A tibble: 6 x 2
    ##   num_count     n
    ##   <chr>     <int>
    ## 1 1           124
    ## 2 10          125
    ## 3 11          122
    ## 4 12          126
    ## 5 13          124
    ## 6 14          124

     제대로 들어간 것 같습니다. 그러면 이제 조커를 이용하여 각 로또 번호의 빈도수를 확인해보겠습니다.

    joker %>% 
      ggplot(aes(x = num_count,
                 y = n,
                 fill = num_count)) +
      geom_bar(stat = "identity") +
      scale_x_discrete(limits = c(1:45)) +
      scale_fill_discrete(limits = c(1:45)) +
      labs(x = "전체 번호",
           y = "빈도수",
           title = "<그림 1> 각 로또 번호의 전체 빈도수") +
      theme(plot.title = element_text(size = 16),
            legend.position = "none")

     여기까지 881회 까지의 로또 전체 1등번호 빈도수를 확인해보았습니다. 얼핏 보았을 때 최대 빈도 번호와 최소 빈도 번호 사이에는 큰 차이가 있는 것 같습니다.

     

     그렇다면 다음으로 해볼 일은 아래와 같습니다.

     

    1. 빈도수가 높은 것들로만 번호를 짜는 전략이 과연 타당한가 알아봅니다.

      빈도수가 높은 숫자들의 조합으로만 이루어진 당첨번호가 있는지 살펴보도록 합니다.

    2. 전체 숫자의 빈도의 평균과 표준편차를 그려보도록 합니다.

      큰 의미는 없겠지만, 확률밀도함수와 정규분포는 학교에서 배운 내용이므로 충분히 써먹기로 합니다.



    1

     먼저 전체 번호를 정렬시켜 빈도가 높은 숫자들 중 n개를 뽑아 조합을 추려내야 합니다. n은 몇이어야 좋을까요? 우선 순서가 상관 없는 6개의 숫자를 선택할 것이므로 순열조합 중 조합을 생각해야 합니다. 조합 nC6에서, n == 8일 때 28, n == 9일 때 84가지 조합이 나오는 걸 확인할 수 있습니다. 적당히 숫자가 크고 한 번에 사용할 수 있는 정도의 조합으로는 8이 적당하다고 판단하고, 8개의 숫자를 가지고 조합을 만들어보겠습니다.

     

     먼저 최고 빈도수를 나타내는 8개의 숫자를 살펴보겠습니다.

    joker %>% 
      arrange(desc(n)) %>% head(8)
    ## # A tibble: 8 x 2
    ##   num_count     n
    ##   <chr>     <int>
    ## 1 34          139
    ## 2 43          130
    ## 3 27          129
    ## 4 40          129
    ## 5 17          128
    ## 6 12          126
    ## 7 20          126
    ## 8 10          125
    higherNumbers <- c(34, 43, 27, 40, 17, 12, 20, 10)

     8개의 최고 빈도 숫자들을 higherNumbers라는 변수에 담았습니다. 이제 filter를 이용해서 숫자가 higherNumbers에 속해 있는지 확인해보겠습니다.

    lotto %>%
      filter(num1 %in% higherNumbers) %>% head
    ## # A tibble: 6 x 6
    ##   num1   num2  num3  num4  num5  num6
    ##   <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
    ## 1 12       17    28    41    43    44
    ## 2 10       34    38    40    42    43
    ## 3 10       24    40    41    43    44
    ## 4 20       25    31    32    36    43
    ## 5 12       16    26    28    30    42
    ## 6 12       18    30    39    41    42

     이렇게 하면 num1이 higherNumbers에 속하는 개수가 86개임을 알 수 있습니다. &를 이용해서 나머지 5개 숫자에 대해서도 알아보겠습니다.

    lotto %>%
      filter(num1 %in% higherNumbers &
             num2 %in% higherNumbers &
             num3 %in% higherNumbers &
             num4 %in% higherNumbers &
             num5 %in% higherNumbers &
             num6 %in% higherNumbers )
    ## # A tibble: 0 x 6
    ## # ... with 6 variables: num1 <chr>, num2 <dbl>, num3 <dbl>, num4 <dbl>,
    ## #   num5 <dbl>, num6 <dbl>

     저런, 8개의 최고 빈도 숫자 조합으로만 이루어진 1등 당첨 조합이 없는 것으로 나타납니다. 당연히 8개의 숫자로만의 조합이 너무 적은 건 아닐까 하는 마음에 빈도가 높은 다른 숫자도 찾아봅니다.

    joker %>% 
      arrange(desc(n)) %>% head(13)
    ## # A tibble: 13 x 2
    ##    num_count     n
    ##    <chr>     <int>
    ##  1 34          139
    ##  2 43          130
    ##  3 27          129
    ##  4 40          129
    ##  5 17          128
    ##  6 12          126
    ##  7 20          126
    ##  8 10          125
    ##  9 1           124
    ## 10 13          124
    ## 11 14          124
    ## 12 18          124
    ## 13 33          124

     빈도수가 총 124회인 5개의 숫자, 1, 13, 14, 18, 33이 공동 9위인 것을 확인할 수 있었습니다. 이 숫자들을 전부 포함한 1등 조합을 찾아보겠습니다. 13C6 = 1,716 이라는 한 번에 시도하기에는 거의 불가능한 조합을 찾는 거지만 한번 시도해봅니다. 로또 1등의 희망을 품고서 말이죠.

    higherNumbers2 <- c(34, 43, 27, 40, 17, 12, 20, 10, 1, 13, 14, 18, 33)
    
    lotto %>% 
      filter(num1 %in% higherNumbers2 &
             num2 %in% higherNumbers2 &
             num3 %in% higherNumbers2 &
             num4 %in% higherNumbers2 &
             num5 %in% higherNumbers2 &
             num6 %in% higherNumbers2 )
    ## # A tibble: 1 x 6
    ##   num1   num2  num3  num4  num5  num6
    ##   <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
    ## 1 1        13    14    33    34    43

     이렇게 하니까 딱 하나의 조합만이 나오네요.. 빈도수가 높은 숫자 조합만으로 로또 당첨이 되기는 힘들어보입니다.


     하.지.만.


     고니는 한끗으로 5억을 태웠습니다. 똥패로도 게임은 뒤집어질 수가 있는 법. 역으로 빈도수가 가장 낮은 숫자로만 이루어진 1등 조합이 있을 수도 있습니다. 살펴보도록 하겠습니다.

    joker %>% 
      arrange(n) %>% head(15)
    ## # A tibble: 15 x 2
    ##    num_count     n
    ##    <chr>     <int>
    ##  1 9            90
    ##  2 22           97
    ##  3 23          102
    ##  4 32          102
    ##  5 41          106
    ##  6 29          107
    ##  7 28          108
    ##  8 35          108
    ##  9 30          109
    ## 10 6           111
    ## 11 42          112
    ## 12 16          113
    ## 13 3           114
    ## 14 38          114
    ## 15 44          115
    lowerNumbers <- c(9, 22, 23, 32, 41, 29, 28, 35, 30, 6, 42, 16, 3, 38, 44)
    
    lotto %>% 
      filter(lotto$num1 %in% lowerNumbers &
             lotto$num2 %in% lowerNumbers &
             lotto$num3 %in% lowerNumbers &
             lotto$num4 %in% lowerNumbers &
             lotto$num5 %in% lowerNumbers &
             lotto$num6 %in% lowerNumbers)
    ## # A tibble: 0 x 6
    ## # ... with 6 variables: num1 <chr>, num2 <dbl>, num3 <dbl>, num4 <dbl>,
    ## #   num5 <dbl>, num6 <dbl>

     빈도수가 가장 낮은 15개 숫자들로 이루어진 조합을 봤는데도 1등 번호 조합이 1도 없습니다.. 똥패는 똥패일 뿐이네요.


    2

     전체 숫자들의 출현 빈도에 대한 히스토그램과 확률밀도함수를 그려보겠습니다. 전체 빈도의 평균과 표준편차를 갖는 정규분포도 그려주기로 해요. 그전에 전체 빈도의 평균과 표준편차를 살펴보도록 합니다.

    mean(joker$n)
    ## [1] 117.4667
    sd(joker$n)
    ## [1] 9.34831

    다음으로 정규분포의 극단치를 보겠습니다.

    qnorm(.95, mean(joker$n), sd(joker$n))  # 상위 5%
    ## [1] 132.8433
    qnorm(.05, mean(joker$n), sd(joker$n))  # 하위 5%
    ## [1] 102.0901
    a <- joker %>% 
      arrange(desc(n))
    a[c(1, 42:45), ]
    ## # A tibble: 5 x 2
    ##   num_count     n
    ##   <chr>     <int>
    ## 1 34          139
    ## 2 23          102
    ## 3 32          102
    ## 4 22           97
    ## 5 9            90

     상위 5%에 해당하는 숫자는 단 1개인 반면에 하위 5%에 해당하는 숫자는 4개가 있는 것을 확인할 수 있었습니다. 특히 숫자 22와 9는 각각 97, 90번 뽑혔는데 이는 하위 2% 밖에 있는 수치임을 알 수 있죠.


     그렇다면 실제 확률밀도함수는 어떻게 그려질까요? 하위 극단치 값의 영향으로 최빈값이 더 왼쪽으로 치우쳐진 그림이 그려졌을 것이라고 예상할 수 있습니다. 예상이 맞을 지 빈도의 히스토그램, 확률밀도함수, 정규분포를 그려서 확인해보겠습니다.

    ggplot(joker, aes(x = n)) + 
      geom_histogram(aes(y = ..density..,
                         color = "히스토그램"),
                     bins = 40,
                     fill = "slategrey") +
      stat_density(aes(y = ..density..,
                       color = "확률밀도함수"),
                   fill = "springgreen2",
                   alpha = .2) +
      stat_function(fun  = dnorm,
                    args = list(mean = mean(joker$n),
                                sd   = sd(joker$n)),
                    xlim = c(90, 150),
                    geom  = "area",
                    fill  = "tomato",
                    alpha = .3,
                    aes(color = "정규분포")) +
      scale_color_manual("Graph", 
                         values = c("tomato", "springgreen2", "slategrey")) +
      guides(color = guide_legend(override.aes = list(fill = c("tomato", 
                                                               "springgreen2", 
                                                               "slategrey")))) +
      labs(x = "",
           y = "",
           title = "<그림 3> 전체 빈도") +
      theme(plot.title = element_text(size = 17))

     예상과는 다르게 확률밀도함수의 최빈값이 정규분포의 평균보다 크게 나타나네요.

    summary(joker)
    ##   num_count               n        
    ##  Length:45          Min.   : 90.0  
    ##  Class :character   1st Qu.:113.0  
    ##  Mode  :character   Median :119.0  
    ##                     Mean   :117.5  
    ##                     3rd Qu.:124.0  
    ##                     Max.   :139.0

     summary를 보면 joker의 중앙값이 평균보다 큽니다. 또 3사분위수 근처에서 빈도표가 밀집되어 있는 모습을 볼 수 있습니다. 극단치 값만 놓고 보았을 때는 하위 5%의 값이 상위 5%의 값보다 갖는 영향력이 훨씬 커보였습니다. 하지만 3사분위수 근처에서 빈도표가 몰려서 나타나는 모양이 이 영향력을 상쇄하고도 남을 정도의 힘을 가지고 있어서, 정규분포의 평균보다 확률밀도함수의 최빈값이 더 커지게 하는 요인으로 작용한 것처럼 보여집니다.


     그럼, 지금까지 로또에 대해서 살펴보았습니다.



     

     

     

     

     

Designed by Tistory.