확률(Probability) 실전예제

10년치 편의점 판매 데이터 분석하기

  • 1500 만건
  • 특정 상품을 정해 시간당 몇 개씩 팔리는 지 분석
  • 확률실험 관측값(팔린 갯수)를 확률변수로 사용

시간당 판매 갯수 확률변수로 분석하기

In [1]:
# 대량의 데이터 빠르게
library(data.table)
In [6]:
# 데이터 로드 - fread()로 data.frame 만들기
df <- fread('R-ggagi-data//example_conveniencestore.csv', encoding = 'UTF-8',
           data.table = F)
In [7]:
head(df)
A data.frame: 6 × 5
Datesellproductcompanypaymentmargin
<chr><chr><chr><chr><dbl>
12006-01-01 07:00:02보가리스웨트 620미리리터(P) 동화오츠카㈜ 체크카드0.11
22006-01-01 07:00:14막스 500미리리터(캔) 블랙맥주㈜ 현금 0.04
32006-01-01 07:00:42맛좋은우유 GT 1리터(팩) 서양유업㈜ 체크카드0.13
42006-01-01 07:00:48육개장사발탕면 86g(컵) ㈜낭심 신용카드0.28
52006-01-01 07:01:14제주도삼다수<U+00A0>2리터(P) ㈜낭심 신용카드0.22
62006-01-01 07:01:24경기도장수생막걸리 750미리리터(P)경기도탁주제조협회현금 0.30
In [35]:
# 표본 만들기 - sample()
S <- df[sample(nrow(df), 5000), ]
In [36]:
str(S)
'data.frame':	5000 obs. of  5 variables:
 $ Date       : chr  "2009-06-11 06:30:16" "2014-04-05 01:22:00" "2015-10-15 05:02:45" "2013-02-03 03:04:33" ...
 $ sellproduct: chr  "아이리시스<U+00A0>500미리리터(P)" "레잇비마일드 185미리리터(캔)" "가스큐팩 1.6리터(P)" "굿스맥주 355미리리터(캔)" ...
 $ company    : chr  "팔성음료㈜" "팔성음료㈜" "오비이락맥주㈜" "오비이락맥주㈜" ...
 $ payment    : chr  "교통카드" "체크카드" "체크카드" "체크카드" ...
 $ margin     : num  0.34 0.26 0.09 0.21 0.25 0.06 0.29 0.08 0.28 0.3 ...
In [37]:
head(S)
A data.frame: 6 × 5
Datesellproductcompanypaymentmargin
<chr><chr><chr><chr><dbl>
54275312009-06-11 06:30:16아이리시스<U+00A0>500미리리터(P)팔성음료㈜ 교통카드0.34
130306032014-04-05 01:22:00레잇비마일드 185미리리터(캔) 팔성음료㈜ 체크카드0.26
154397202015-10-15 05:02:45가스큐팩 1.6리터(P) 오비이락맥주㈜체크카드0.09
111870712013-02-03 03:04:33굿스맥주 355미리리터(캔) 오비이락맥주㈜체크카드0.21
53706422009-05-29 02:00:31바나나멋우유 240미리리터(P) ㈜싱글벙글 체크카드0.25
71080652010-07-05 11:37:41참이슬로 360미리리터(병) ㈜잔루 현금 0.06
In [38]:
# 분석 상품 선택 - 가장 많이 팔린 상품
sort(table(S$sellproduct)) # 가스큐팩 1.6리터(P)
         팔성사이다<U+00A0>1.5리터(P)               옥수수차 500미리리터(P) 
                                   33                                    36 
               육개장사발탕면 86g(컵)     TOP스위트블랙커피 275미리리터(캔) 
                                   38                                    41 
         굿이브닝케어 100미리리터(병)               메지밀B 190미리리터(병) 
                                   41                                    41 
                 가스 640미리리터(병)              구카카콜 250미리리터(캔) 
                                   43                                    45 
     팔성사이다<U+00A0>500미리리터(P)               맛좋은우유 GT 1리터(팩) 
                                   45                                    46 
 간다다프리미엄블렌드 275미리리터(캔)        카페라떼와일드 200미리리터(컵) 
                                   48                                    48 
글라소비타민물공급<U+00A0>500미리리터(P)            딸기속우유 310미리리터(팩) 
                                   49                                    49 
          보가리스웨트 620미리리터(P) 힘에이드마운틴블라스트 600미리리터(P) 
                                   49                                    49 
     아이리시스<U+00A0>500미리리터(P)          바리스타라떼 250미리리터(컵) 
                                   52                                    53 
                   세계콘 170미리리터            딸기짱우유 240미리리터(팩) 
                                   53                                    54 
     가스라이트큐팩<U+00A0>1.6리터(P)            아이리시스<U+00A0>2리터(P) 
                                   55                                    55 
             가스큐팩 1000미리리터(P)               비타528 100미리리터(병) 
                                   59                                    59 
           경기도우유 200미리리터(팩)      조르지아오리지널 240미리리터(캔) 
                                   60                                    60 
            새로운라면큰사발 114g(컵)      우유속에카푸치노 310미리리터(팩) 
                                   62                                    62 
                 막스 355미리리터(캔)     경기도장수생막걸리 750미리리터(P) 
                                   65                                    84 
        화이트피처 1.6리터(P)<U+00A0>                   구카카콜 1.5리터(P) 
                                   92                                    93 
          제주도사다수 500미리리터(P)               화이트 500미리리터((캔) 
                                   94                                    94 
                 경기도우유 1리터(팩)               구카카콜 500미리리터(P) 
                                   97                                    97 
       화이트 355미리리터(캔)<U+00A0>                  막스 500미리리터(캔) 
                                   97                                   103 
         레잇비마일드 185미리리터(캔)                   먹스피쳐 1.6리터(P) 
                                  109                                   111 
         제주도삼다수<U+00A0>2리터(P)            마지막처럼 360미리리터(병) 
                                  111                                   116 
              야명808 140미리리터(캔)              참이슬로 360미리리터(병) 
                                  122                                   140 
       핫개컨디션파워 100미리리터(병)           첨이슬fresh 360미리리터(병) 
                                  209                                   250 
             굿스맥주 500미리리터(캔)              굿스맥주 355미리리터(캔) 
                                  291                                   335 
          바나나멋우유 240미리리터(P)                   가스큐팩 1.6리터(P) 
                                  384                                   521 
In [39]:
# 가스큐팩 1.6리터(P) 의 시간당 판매 조사
# 날짜는 문자열 처리
library(stringr)
In [40]:
head(S$Date,3)
  1. '2009-06-11 06:30:16'
  2. '2014-04-05 01:22:00'
  3. '2015-10-15 05:02:45'
In [52]:
S$time <- str_split_fixed(S$Date, " ", 2)[,2]
S$hour <- str_split_fixed(S$time, ":", 3)[,1]
head(S, 5)
A tibble: 5 × 7
Datesellproductcompanypaymentmargintimehour
<chr><chr><chr><chr><dbl><chr><chr>
2009-06-11 06:30:16아이리시스<U+00A0>500미리리터(P)팔성음료㈜ 교통카드0.3406:30:1606
2014-04-05 01:22:00레잇비마일드 185미리리터(캔) 팔성음료㈜ 체크카드0.2601:22:0001
2015-10-15 05:02:45가스큐팩 1.6리터(P) 오비이락맥주㈜체크카드0.0905:02:4505
2013-02-03 03:04:33굿스맥주 355미리리터(캔) 오비이락맥주㈜체크카드0.2103:04:3303
2009-05-29 02:00:31바나나멋우유 240미리리터(P) ㈜싱글벙글 체크카드0.2502:00:3102
In [42]:
# 가스큐팩 1.6리터(P) 만 추출
library(dplyr)
In [43]:
# filter() : dplyr 에서 조건 주는 함수
S <- tbl_df(S)
S2 <- filter(S, sellproduct=='가스큐팩 1.6리터(P)')
In [44]:
# filter 사용 안 한다면 아래와 같이 가스큐팩 1.6리터(P) 만 추출 가능
S3 <- S[S$sellproduct == '가스큐팩 1.6리터(P)',]
head(S3)
S4 <- subset(S, subset = S$sellproduct == "가스큐팩 1.6리터(P)")
head(S4)
A tibble: 6 × 7
Datesellproductcompanypaymentmargintimehour
<chr><chr><chr><chr><dbl><chr><chr>
2015-10-15 05:02:45가스큐팩 1.6리터(P)오비이락맥주㈜체크카드0.0905:02:4505
2012-07-12 15:06:01가스큐팩 1.6리터(P)오비이락맥주㈜교통카드0.0915:06:0115
2006-11-22 17:44:58가스큐팩 1.6리터(P)오비이락맥주㈜신용카드0.0917:44:5817
2011-10-15 02:58:37가스큐팩 1.6리터(P)오비이락맥주㈜현금 0.0902:58:3702
2012-09-04 19:30:38가스큐팩 1.6리터(P)오비이락맥주㈜체크카드0.0919:30:3819
2012-10-11 14:02:25가스큐팩 1.6리터(P)오비이락맥주㈜교통카드0.0914:02:2514
A tibble: 6 × 7
Datesellproductcompanypaymentmargintimehour
<chr><chr><chr><chr><dbl><chr><chr>
2015-10-15 05:02:45가스큐팩 1.6리터(P)오비이락맥주㈜체크카드0.0905:02:4505
2012-07-12 15:06:01가스큐팩 1.6리터(P)오비이락맥주㈜교통카드0.0915:06:0115
2006-11-22 17:44:58가스큐팩 1.6리터(P)오비이락맥주㈜신용카드0.0917:44:5817
2011-10-15 02:58:37가스큐팩 1.6리터(P)오비이락맥주㈜현금 0.0902:58:3702
2012-09-04 19:30:38가스큐팩 1.6리터(P)오비이락맥주㈜체크카드0.0919:30:3819
2012-10-11 14:02:25가스큐팩 1.6리터(P)오비이락맥주㈜교통카드0.0914:02:2514
In [45]:
str(S2)
tibble [521 x 7] (S3: tbl_df/tbl/data.frame)
 $ Date       : chr [1:521] "2015-10-15 05:02:45" "2012-07-12 15:06:01" "2006-11-22 17:44:58" "2011-10-15 02:58:37" ...
 $ sellproduct: chr [1:521] "가스큐팩 1.6리터(P)" "가스큐팩 1.6리터(P)" "가스큐팩 1.6리터(P)" "가스큐팩 1.6리터(P)" ...
 $ company    : chr [1:521] "오비이락맥주㈜" "오비이락맥주㈜" "오비이락맥주㈜" "오비이락맥주㈜" ...
 $ payment    : chr [1:521] "체크카드" "교통카드" "신용카드" "현금" ...
 $ margin     : num [1:521] 0.09 0.09 0.09 0.09 0.09 0.09 0.09 0.09 0.09 0.09 ...
 $ time       : chr [1:521] "05:02:45" "15:06:01" "17:44:58" "02:58:37" ...
 $ hour       : chr [1:521] "05" "15" "17" "02" ...
In [46]:
# 시간대별 도수
freq <- table(S2$hour)
freq
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 
20 21 24 24 28 23 25 26 15 27 30 29 16 24 33 17 27 18 14 14 15 16 11 24 
In [47]:
# 확률변수 X 선택
rv <- as.vector(freq)
rv <- rv[!duplicated(rv)] # 중복값 제거
rv <- sort(rv)
rv
  1. 11
  2. 14
  3. 15
  4. 16
  5. 17
  6. 18
  7. 20
  8. 21
  9. 23
  10. 24
  11. 25
  12. 26
  13. 27
  14. 28
  15. 29
  16. 30
  17. 33
In [48]:
# 확률변수별 비율
prv <- prop.table(rv)
prv
  1. 0.0291777188328912
  2. 0.0371352785145889
  3. 0.0397877984084881
  4. 0.0424403183023873
  5. 0.0450928381962865
  6. 0.0477453580901857
  7. 0.0530503978779841
  8. 0.0557029177718833
  9. 0.0610079575596817
  10. 0.0636604774535809
  11. 0.0663129973474801
  12. 0.0689655172413793
  13. 0.0716180371352785
  14. 0.0742705570291777
  15. 0.0769230769230769
  16. 0.0795755968169761
  17. 0.0875331564986737
In [49]:
# 각각의 기댓값
exps <- rv*prv
exps
  1. 0.320954907161804
  2. 0.519893899204244
  3. 0.596816976127321
  4. 0.679045092838196
  5. 0.76657824933687
  6. 0.859416445623342
  7. 1.06100795755968
  8. 1.16976127320955
  9. 1.40318302387268
  10. 1.52785145888594
  11. 1.657824933687
  12. 1.79310344827586
  13. 1.93368700265252
  14. 2.07957559681698
  15. 2.23076923076923
  16. 2.38726790450928
  17. 2.88859416445623
In [50]:
# 확률변수의 평균은 확률변수*확률(비율)의 총합
sum(exps)
23.8753315649867

묶음행사 상품 판매 데이터 분석하기

  • 이벤트 상품 8개 중 6개를 골라 세트 구입
  • 중복해서 구입 가능 => 중복 조합
  • 6개 세트에서 마진이 좋은 상품도, 나쁜 상품도 있음
  • 마진의 기준 0.13
  • 6개 상품의 마진이 0.13인 상품갯수의 확률변수 X = {0,1,2,3,4,5,6}
  • 이때의 확률변수의 분포
In [1]:
# 이벤트 상품 목록 구하기 - 2000
eventlist <- c("구카카콜 250미리리터(캔)", "세계콘 170미리리터", 
               "딸기속우유 310미리리터(팩)", "조르지아오리지널 240미리리터(캔)", 
               "육개장사발탕면 86g(컵)", "카페라떼와일드 200미리리터(컵)", 
               "핫개컨디션파워 100미리리터(병)", "비타528 100미리리터(병)")
In [2]:
# 세트 판매 데이터 불러오기
event <- read.csv('R-ggagi-data//example_eventsale.csv', header=F)
event <- as.list(event) # 리스트 1개당 1세트로 처리
event <- lapply(event, as.character)
head(event, 3)
$V1
  1. '비타528 100미리리터(병)'
  2. '세계콘 170미리리터'
  3. '비타528 100미리리터(병)'
  4. '카페라떼와일드 200미리리터(컵)'
  5. '조르지아오리지널 240미리리터(캔)'
  6. '구카카콜 250미리리터(캔)'
$V2
  1. '구카카콜 250미리리터(캔)'
  2. '딸기속우유 310미리리터(팩)'
  3. '핫개컨디션파워 100미리리터(병)'
  4. '딸기속우유 310미리리터(팩)'
  5. '육개장사발탕면 86g(컵)'
  6. '카페라떼와일드 200미리리터(컵)'
$V3
  1. '구카카콜 250미리리터(캔)'
  2. '카페라떼와일드 200미리리터(컵)'
  3. '딸기속우유 310미리리터(팩)'
  4. '구카카콜 250미리리터(캔)'
  5. '비타528 100미리리터(병)'
  6. '딸기속우유 310미리리터(팩)'
In [4]:
length(event)
# 2000개 팔렸다~
2000
In [6]:
# 마진율 가져오기
# 데이터 적재

# fread() : data.table 패키지의 함수 (대용량 데이터 빠르게 읽어옴)
library(data.table)
DF <- fread('R-ggagi-data//example_conveniencestore.csv', encoding='UTF-8',
            data.table=F)
In [7]:
# 1000개 샘플링
S <- DF[sample(nrow(DF),1000),]
In [11]:
# 상품별 요약
library(dplyr)

S2 <- S %>% filter(sellproduct == eventlist[1] | sellproduct == eventlist[2]
                   | sellproduct == eventlist[3] | sellproduct == eventlist[4]
                   | sellproduct == eventlist[5] | sellproduct == eventlist[6] 
                   | sellproduct == eventlist[7] | sellproduct == eventlist[8] 
                  ) %>%

            group_by(sellproduct) %>%
            summarise(margin=min(margin)) %>%  # 상품별 마진은 동일 max()도 동일
           
            # mutate() : dplyr 패키지의 의 '열 추가' 함수
            # 마진이 0.13 보다 크면 참(1), 거짓(0)
            mutate(marginTF=ifelse(margin > 0.13, 1, 0))


S2
`summarise()` ungrouping output (override with `.groups` argument)

A tibble: 8 × 3
sellproductmarginmarginTF
<chr><dbl><dbl>
구카카콜 250미리리터(캔) 0.100
딸기속우유 310미리리터(팩) 0.110
비타528 100미리리터(병) 0.201
세계콘 170미리리터 0.151
육개장사발탕면 86g(컵) 0.281
조르지아오리지널 240미리리터(캔)0.271
카페라떼와일드 200미리리터(컵) 0.231
핫개컨디션파워 100미리리터(병) 0.301
In [13]:
# 경우의 수 만들기
# 8개 중 6개, 중복 가능
library('gtools')

com <- combinations(length(eventlist), 6, eventlist, repeats.allowed = T)

nrow(com)

# 1716 개의 중복조합이 가능함
1716
In [14]:
head(com, 3)
head(event, 3)
A matrix: 3 × 6 of type chr
구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)
구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)딸기속우유 310미리리터(팩)
구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)비타528 100미리리터(병)
$V1
  1. '비타528 100미리리터(병)'
  2. '세계콘 170미리리터'
  3. '비타528 100미리리터(병)'
  4. '카페라떼와일드 200미리리터(컵)'
  5. '조르지아오리지널 240미리리터(캔)'
  6. '구카카콜 250미리리터(캔)'
$V2
  1. '구카카콜 250미리리터(캔)'
  2. '딸기속우유 310미리리터(팩)'
  3. '핫개컨디션파워 100미리리터(병)'
  4. '딸기속우유 310미리리터(팩)'
  5. '육개장사발탕면 86g(컵)'
  6. '카페라떼와일드 200미리리터(컵)'
$V3
  1. '구카카콜 250미리리터(캔)'
  2. '카페라떼와일드 200미리리터(컵)'
  3. '딸기속우유 310미리리터(팩)'
  4. '구카카콜 250미리리터(캔)'
  5. '비타528 100미리리터(병)'
  6. '딸기속우유 310미리리터(팩)'
In [18]:
# 판매데이터와 사건 비교
# 236 첫번째 판매데이터로 236번째 조합
c <- NULL

for(j in 1: length(event)){
    for(i in 1: nrow(com)){
        if(all(event[[j]] %in% com [i,])){
            c <- c(c,i)
            break()
        }
    }
}

head(c, 10) # c 에는 몇번째 제품이 팔린 건지의 정보가 담겨있음
  1. 236
  2. 193
  3. 48
  4. 54
  5. 158
  6. 236
  7. 1088
  8. 246
  9. 310
  10. 491
In [20]:
# 각 eventlist의 marginTF 값 적용
com2 <- com
for(i in 1: length(eventlist)){
    com2[com == eventlist[i]] <- S2[S2$sellproduct == eventlist[i],]$marginTF
}

head(com2) # com 상품 코드표를 마진이 넘는지 안 넘는지 숫자로 표시
A matrix: 6 × 6 of type chr
000000
000000
000001
000001
000001
000001
In [24]:
# com2 int로 형변환
com3 <- apply(com2, 2, as.integer) # 1 : 행(row)단위 / 2: 열(col) 단위
head(com3)
A matrix: 6 × 6 of type int
000000
000000
000001
000001
000001
000001
In [25]:
head(com, 3)
head(com[com == eventlist[1]])
head(com2[com == eventlist[1]])
A matrix: 3 × 6 of type chr
구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)
구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)딸기속우유 310미리리터(팩)
구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)구카카콜 250미리리터(캔)비타528 100미리리터(병)
  1. '구카카콜 250미리리터(캔)'
  2. '구카카콜 250미리리터(캔)'
  3. '구카카콜 250미리리터(캔)'
  4. '구카카콜 250미리리터(캔)'
  5. '구카카콜 250미리리터(캔)'
  6. '구카카콜 250미리리터(캔)'
  1. '0'
  2. '0'
  3. '0'
  4. '0'
  5. '0'
  6. '0'
In [26]:
# 확률변수 X = {0,1,2,3,4,5,6}  (사건마다 마진이 있는 상품의 개수)
# 확률변수 X를 모두 더하기 rowSums
rv <- rowSums(com3) # 행의 값 더하기
In [27]:
head(rv) # 팔린 상품의 마진 TF
  1. 0
  2. 0
  3. 1
  4. 1
  5. 1
  6. 1
In [28]:
head(c) # 몇번째 상품이 팔린건지
  1. 236
  2. 193
  3. 48
  4. 54
  5. 158
  6. 236
In [29]:
# 확률마다 실제 판매된 제품 넣어서 도수 구하기
rv_table <- table(rv[c])
rv_table
  0   1   2   3   4   5   6 
  2  26 302 840 682 142   6 
In [30]:
addmargins(rv_table, margin = 1)
   0    1    2    3    4    5    6  Sum 
   2   26  302  840  682  142    6 2000 
In [31]:
# 상대적 판매 비율 => 확률
rv_prop_table <- prop.table(rv_table)
rv_prop_table
    0     1     2     3     4     5     6 
0.001 0.013 0.151 0.420 0.341 0.071 0.003 
In [37]:
# 시각화
library(ggplot2)
library(ggthemes)
Warning message:
"package 'ggthemes' was built under R version 4.0.2"
In [38]:
# 시각화해야 하니까 data.frame에 넣어주기
rv_df <- as.data.frame(rv_table)
rv_df
A data.frame: 7 × 2
Var1Freq
<fct><int>
0 2
1 26
2302
3840
4682
5142
6 6
In [39]:
ggplot(rv_df, aes(x=factor(Var1), y=Freq, fill=Var1)) + 
geom_bar(stat='identity') # 데이터는 각각 표시

게임 데이터 기하분포로 분석하기 - 기하분포

  • 기하분포: K번째 성공할 확률 분포
  • 게임밸런스 조절 등에 사용
In [40]:
library(data.table)
In [43]:
# 데이터 로드
DF <- fread('R-ggagi-data//example_gamedata.csv', data.table=T)
head(DF, 1)
tail(DF, 1)
A data.table: 1 × 4
Date2memberIDstagesuccess
<chr><int><chr><lgl>
2015-02-15 00:00:00376966stage_01TRUE
A data.table: 1 × 4
Date2memberIDstagesuccess
<chr><int><chr><lgl>
2015-12-31 23:00:00313777stage_08TRUE
In [44]:
# memberID 376966 자세히 보기
library(dplyr)
In [45]:
DF2 <- tbl_df(DF)
DF2 %>% filter(memberID == 376966)
Warning message:
"`tbl_df()` is deprecated as of dplyr 1.0.0.
Please use `tibble::as_tibble()` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated."
A tibble: 79 × 4
Date2memberIDstagesuccess
<chr><int><chr><lgl>
2015-02-15 00:00:00376966stage_01 TRUE
2015-02-17 19:55:05376966stage_02FALSE
2015-02-23 16:22:43376966stage_02 TRUE
2015-02-27 17:45:02376966stage_03FALSE
2015-03-02 16:35:11376966stage_03FALSE
2015-03-14 01:03:07376966stage_03 TRUE
2015-03-15 02:27:46376966stage_04FALSE
2015-03-16 20:56:15376966stage_04FALSE
2015-03-21 20:43:40376966stage_04FALSE
2015-03-27 20:39:32376966stage_04FALSE
2015-04-01 18:05:47376966stage_04FALSE
2015-04-02 11:42:09376966stage_04FALSE
2015-04-06 15:38:38376966stage_04 TRUE
2015-04-06 18:40:32376966stage_05FALSE
2015-04-13 20:51:45376966stage_05FALSE
2015-04-14 12:03:54376966stage_05FALSE
2015-04-18 22:34:56376966stage_05FALSE
2015-04-23 14:52:28376966stage_05FALSE
2015-04-29 02:17:15376966stage_05FALSE
2015-05-01 02:44:45376966stage_05FALSE
2015-05-08 18:02:48376966stage_05FALSE
2015-05-11 21:11:03376966stage_05FALSE
2015-05-12 19:17:20376966stage_05FALSE
2015-05-18 17:41:19376966stage_05FALSE
2015-05-23 13:18:11376966stage_05FALSE
2015-05-27 02:47:06376966stage_05FALSE
2015-05-28 17:28:18376966stage_05FALSE
2015-05-29 18:36:09376966stage_05 TRUE
2015-06-02 15:58:40376966stage_06FALSE
2015-06-04 11:10:49376966stage_06FALSE
2015-08-27 18:17:13376966stage_07FALSE
2015-09-04 15:43:18376966stage_07FALSE
2015-09-06 00:10:19376966stage_07FALSE
2015-09-08 22:36:30376966stage_07FALSE
2015-09-12 15:53:02376966stage_07FALSE
2015-09-22 20:29:17376966stage_07FALSE
2015-10-08 23:02:45376966stage_07FALSE
2015-10-11 20:47:07376966stage_07FALSE
2015-10-11 22:42:49376966stage_07FALSE
2015-10-13 01:53:08376966stage_07FALSE
2015-11-01 18:54:00376966stage_07FALSE
2015-11-02 20:48:09376966stage_07 TRUE
2015-11-08 05:26:15376966stage_08FALSE
2015-11-08 22:04:22376966stage_08FALSE
2015-11-10 18:28:01376966stage_08FALSE
2015-11-14 10:30:45376966stage_08FALSE
2015-11-16 01:46:49376966stage_08FALSE
2015-11-20 14:14:02376966stage_08FALSE
2015-11-22 10:46:00376966stage_08FALSE
2015-11-22 21:58:43376966stage_08FALSE
2015-11-23 15:17:33376966stage_08FALSE
2015-11-24 00:46:42376966stage_08FALSE
2015-11-30 08:21:37376966stage_08FALSE
2015-12-01 08:04:59376966stage_08FALSE
2015-12-07 11:41:08376966stage_08FALSE
2015-12-07 20:15:47376966stage_08FALSE
2015-12-11 06:22:44376966stage_08FALSE
2015-12-27 22:17:13376966stage_08FALSE
2015-12-28 15:53:05376966stage_08 TRUE
2015-12-31 13:10:58376966stage_08 TRUE
In [46]:
# memberID 376966 stage별 성공 / 실패
DF2 %>%
filter(memberID==376966) %>%
group_by(stage, success) %>%
select(success) %>%
summarise(count = n()) # 갯수 셀 때 쓰는 n()함수
Adding missing grouping variables: `stage`

`summarise()` regrouping output by 'stage' (override with `.groups` argument)

A grouped_df: 15 × 3
stagesuccesscount
<chr><lgl><int>
stage_01 TRUE 1
stage_02FALSE 1
stage_02 TRUE 1
stage_03FALSE 2
stage_03 TRUE 1
stage_04FALSE 6
stage_04 TRUE 1
stage_05FALSE14
stage_05 TRUE 1
stage_06FALSE13
stage_06 TRUE 3
stage_07FALSE16
stage_07 TRUE 1
stage_08FALSE16
stage_08 TRUE 2
In [47]:
# 모든 사람들의 성공 / 실패 구하기
SF <- DF2 %>%
group_by(stage, success) %>%
summarise(count=n())
SF
`summarise()` regrouping output by 'stage' (override with `.groups` argument)

A grouped_df: 16 × 3
stagesuccesscount
<chr><lgl><int>
stage_01FALSE 60
stage_01 TRUE 500007
stage_02FALSE 503610
stage_02 TRUE 500858
stage_03FALSE1071222
stage_03 TRUE 521440
stage_04FALSE2051331
stage_04 TRUE 721099
stage_05FALSE4263240
stage_05 TRUE 897753
stage_06FALSE6020643
stage_06 TRUE1426092
stage_07FALSE7945384
stage_07 TRUE 515538
stage_08FALSE7999460
stage_08 TRUE 999984
In [48]:
# 그래프 그리기
library(ggplot2)
library(ggthemes)
In [50]:
ggplot(SF, aes(x=factor(stage), y=count, colour=success, group=success)) + 
geom_line(size=0.7) + 
geom_point(size=4) +
ggtitle('A사의 B게임 스테이지별 성공여부') +
theme_wsj()
In [52]:
# 성공한 횟수만 따로 저장
SF2 <- SF %>% filter(success == T)
SF2
A grouped_df: 8 × 3
stagesuccesscount
<chr><lgl><int>
stage_01TRUE 500007
stage_02TRUE 500858
stage_03TRUE 521440
stage_04TRUE 721099
stage_05TRUE 897753
stage_06TRUE1426092
stage_07TRUE 515538
stage_08TRUE 999984
In [53]:
# 스테이지별 전체 횟수 구하기
SFT <- DF2 %>% group_by(stage) %>% summarise(total=n())
SFT
`summarise()` ungrouping output (override with `.groups` argument)

A tibble: 8 × 2
stagetotal
<chr><int>
stage_01 500067
stage_021004468
stage_031592662
stage_042772430
stage_055160993
stage_067446735
stage_078460922
stage_088999444
In [54]:
# 성공횟수와 전체 횟수 합치기
SF3 <- cbind(SF2, total=SFT$total)
SF3
A grouped_df: 8 × 4
stagesuccesscounttotal
<chr><lgl><int><int>
stage_01TRUE 500007 500067
stage_02TRUE 5008581004468
stage_03TRUE 5214401592662
stage_04TRUE 7210992772430
stage_05TRUE 8977535160993
stage_06TRUE14260927446735
stage_07TRUE 5155388460922
stage_08TRUE 9999848999444
In [55]:
# 스테이지마다 시도횟수, 성공횟수 -> 성공비율 구하기
SF4 <- SF3 %>% mutate(p = count/total)
SF4
A grouped_df: 8 × 5
stagesuccesscounttotalp
<chr><lgl><int><int><dbl>
stage_01TRUE 500007 5000670.99988002
stage_02TRUE 50085810044680.49863012
stage_03TRUE 52144015926620.32740155
stage_04TRUE 72109927724300.26009638
stage_05TRUE 89775351609930.17394966
stage_06TRUE142609274467350.19150567
stage_07TRUE 51553884609220.06093166
stage_08TRUE 99998489994440.11111620

베르누이 시행 확인 -> 이항분포, 기하분포

  • 성공/ 실패
  • 스테이지마다 같은 밸런스 => 성공확률 동일
  • 누군가 성공이 다른 사람에게 영향 없음 => 독립
  • 게임의 특성상 기하분포 적용
  • n 번째에 스테이지를 성공할 확률은?
  • 이번 스테이지는 5번만에 90% 유저가 성공하도록 하기
In [56]:
# stage_03일 때(성공확률 p = 0.32740155)의 기하분포 
x <- 1:15
y <- dgeom(x, 0.327)
plot(x,y,type='h')
In [58]:
# 기하분포 값을 자세히
round(y, 3) # 소숫점 세 자리 까지만 표시
  1. 0.22
  2. 0.148
  3. 0.1
  4. 0.067
  5. 0.045
  6. 0.03
  7. 0.02
  8. 0.014
  9. 0.009
  10. 0.006
  11. 0.004
  12. 0.003
  13. 0.002
  14. 0.001
  15. 0.001
In [59]:
# 한 번 실패 후 성공( 첫번째 값 )
0.327*(1-0.327)
0.220071
In [60]:
# 누적 기하분포 확률값
pgeom(5, 0.327) # 5 :실실실실성 했을 때의 확률값
pgeom(4, 0.327) # 4: 실실실성 했을 때의 확률값
0.907084025666639
0.861937631005407
In [61]:
# 6번 실패하고 성공할 확률 90% 만들기
# 0.9 될 때의 확률 p값 구하는 함수 만들기
findp <- function(n,p){
    for(i in 1:99){
        a <- pgeom(n, 0.01*i)
        if(a > p){
            print(paste0('n값이 ', n, '일 때'))
            print(paste0('p값 ', 0.01*0.1, '을 사용하면'))
            print(paste0('확률 ', a, '이 나옵니다'))
            break()
        }
    }
}
In [62]:
# 6번 만에 90%가 성공하는 p값 
findp(6, 0.9)

# -> 게임 밸런스를 0.327에서 0.29로 조정하면 6번만에 90%가 성공하게 만들 수 있음
[1] "n값이 6일 때"
[1] "p값 0.001을 사용하면"
[1] "확률 0.90904879841609이 나옵니다"
In [63]:
n = 6
for(i in 1:9){
    a <- pgeom(n, 0.01*i)
    print(paste0("n값이 ",n, "일 때"))
    print(paste0("p값 " , 0.01*i, "을 사용하면"))
    print(paste0("확률 ", a,"이 나옵니다"))
    
}
[1] "n값이 6일 때"
[1] "p값 0.01을 사용하면"
[1] "확률 0.06793465209301이 나옵니다"
[1] "n값이 6일 때"
[1] "p값 0.02을 사용하면"
[1] "확률 0.13187446675328이 나옵니다"
[1] "n값이 6일 때"
[1] "p값 0.03을 사용하면"
[1] "확률 0.19201715521887이 나옵니다"
[1] "n값이 6일 때"
[1] "p값 0.04을 사용하면"
[1] "확률 0.24855252189184이 나옵니다"
[1] "n값이 6일 때"
[1] "p값 0.05을 사용하면"
[1] "확률 0.30166270390625이 나옵니다"
[1] "n값이 6일 때"
[1] "p값 0.06을 사용하면"
[1] "확률 0.35152240580736이 나옵니다"
[1] "n값이 6일 때"
[1] "p값 0.07을 사용하면"
[1] "확률 0.39829912939243이 나옵니다"
[1] "n값이 6일 때"
[1] "p값 0.08을 사용하면"
[1] "확률 0.44215339876352이 나옵니다"
[1] "n값이 6일 때"
[1] "p값 0.09을 사용하면"
[1] "확률 0.48323898064269이 나옵니다"
In [ ]: