What types of user sessions lead to purchases?

Data processing series - R

Author

Duy Khanh

Published

February 14, 2024

Giới thiệu

Đối với xử lý dữ liệu, việc gộp dữ liệu đóng vai trò rất quan trọng khi chúng ta có từ hai bảng dữ liệu trở lên hoặc làm việc với cơ sở dữ liệu. Equi-join là loại gộp dữ liệu từ các bảng dữ liệu có (các) biến liên quan có dữ liệu giống nhau (còn được biết đến là các keys), vốn đã rất phổ biến và là tiêu chuẩn cần có của một người làm data hiện nay. Tuy nhiên, điều này ngược lại đối với non-equi joins, một loại gộp dữ liệu linh hoạt hơn nhưng lại ít được biết đến. Thông thường, một loại gộp dữ liệu sẽ bao gồm nhiều cách gộp dữ liệu khác nhau tùy vào mục đích gộp cụ thể. Bài viết này sẽ sử dụng cách gộp rolling joins trong non-equi joins và ứng dụng cách gộp này vào trong kinh doanh thông qua gói lệnh data.table trong ngôn ngữ R.

Tình huống

Một doanh nghiệp A sở hữu một trang web thương mại điện tử có sử dụng bên thứ ba để thực hiện thanh toán chẳng hạn như MoMo. Doanh nghiệp theo dõi các phiên đăng nhập của người dùng thông qua trang web và MoMo theo dõi việc thanh toán của khách hàng khi thực hiện mua hàng. Mục đích của doanh nghiệp muốn biết rằng phiên đăng nhập nào thì sẽ dẫn tới việc quyết định mua hàng.

Trước tiên tôi gọi các gói lệnh mà tôi cần sử dụng trong bài viết này.

Code
pacman::p_load(
   data.table, # xử lý data
   kableExtra # vẽ bảng
)

Bây giờ tôi sẽ tạo ra một 2 bảng dữ liệu mẫu. Trong đó, một bảng chứa dữ liệu về thông tin đa dạng kiểu khách hàng và thời điểm truy cập, bảng còn lại chứa thông tin và thời điểm thanh toán.

Một Suzy với nhiều lần truy cập trang web trước khi mua bất cứ thứ gì

Code
suzy_website <- data.table(name = rep('Indecisive Suzy', 5),
                           session_start_time = as.POSIXct(
                              c(
                                 '2024-01-01 11:01',
                                 '2024-01-02 8:59',
                                 '2024-01-05 18:18',
                                 '2024-01-07 19:03',
                                 '2024-01-08 19:01'
                              )
                           ))
suzy_momo <-
   data.table(name = 'Indecisive Suzy',
              purchase_time = as.POSIXct('2024-01-08 19:10'))

Một Adam “phóng khoáng” khi chỉ truy cập trang web một lần và mua nhiều lần

Code
adam_website <-
   data.table(name = 'Spendy Adam',
              session_start_time = as.POSIXct('2024-01-03 10:00'))
adam_momo <-
   data.table(name = rep('Spendy Adam', 2),
              purchase_time = as.POSIXct(c('2024-01-03 10:06', '2024-01-03 10:15')))

Một Tom thường đến trang web mua sắm và thỉnh thoảng mua hàng

Code
tom_website <- data.table(name = rep('Frequent Tom', 6),
                              session_start_time = as.POSIXct(
                                 c(
                                    '2024-01-02 13:09',
                                    '2024-01-03 19:22',
                                    '2024-01-08 8:44',
                                    '2024-01-08 20:22',
                                    '2024-01-10 17:36',
                                    '2024-01-15 16:56'
                                 )
                              ))
tom_momo <-
   data.table(name = rep('Frequent Tom', 3),
              purchase_time = as.POSIXct(c(
                 '2024-01-03 19:28', '2024-01-08 20:33', '2024-01-10 17:46'
              )))

Một Jane “vãng lai” ghé qua trang web vài lần nhưng không mua gì cả

Code
jane_website <-
   data.table(name = rep('Visitor Jane', 2),
              session_start_time = as.POSIXct(c('2024-01-01 9:10', '2024-01-09 2:15'))) 
jane_momo <- character(0) # has 0 rows, but the same column names/classes

Một Jessica thanh toán qua MoMo mà không truy cập trang web (ghé qua cửa hàng và thanh toán trực tuyến)

Code
jessica_website <- character(0)
jessica_momo <- data.table(name = 'Non-web Jessica', purchase_time = as.POSIXct('2023-12-02 17:58'))

Kết hợp tất cả lại ta có được 2 bảng dữ liệu như sau

Code
table_style <- 
   function(table, caption) {
      kbl(table, caption = caption,
          table.attr = 'data-quarto-disable-processing="true"') |> 
         kable_classic(
            bootstrap_options = c("condensed"),
            full_width = F,
            html_font = "Arial",
            font_size = 15
         ) |> 
         row_spec(0, bold = T)
   }



website <-
   rbindlist(list(
      adam_website,
      jane_website,
      # jessica_website,
      suzy_website,
      tom_website
   ))

momo <- rbindlist(list(adam_momo, jessica_momo, suzy_momo, tom_momo))

website |> table_style("Bảng theo dõi phiên đăng nhập")
Bảng theo dõi phiên đăng nhập
name session_start_time
Spendy Adam 2024-01-03 10:00:00
Visitor Jane 2024-01-01 09:10:00
Visitor Jane 2024-01-09 02:15:00
Indecisive Suzy 2024-01-01 11:01:00
Indecisive Suzy 2024-01-02 08:59:00
Indecisive Suzy 2024-01-05 18:18:00
Indecisive Suzy 2024-01-07 19:03:00
Indecisive Suzy 2024-01-08 19:01:00
Frequent Tom 2024-01-02 13:09:00
Frequent Tom 2024-01-03 19:22:00
Frequent Tom 2024-01-08 08:44:00
Frequent Tom 2024-01-08 20:22:00
Frequent Tom 2024-01-10 17:36:00
Frequent Tom 2024-01-15 16:56:00
Code
momo |> table_style("Bảng theo dõi việc thanh toán")
Bảng theo dõi việc thanh toán
name purchase_time
Spendy Adam 2024-01-03 10:06:00
Spendy Adam 2024-01-03 10:15:00
Non-web Jessica 2023-12-02 17:58:00
Indecisive Suzy 2024-01-08 19:10:00
Frequent Tom 2024-01-03 19:28:00
Frequent Tom 2024-01-08 20:33:00
Frequent Tom 2024-01-10 17:46:00

Trong trường hợp thực tế, tôi cần tạo các mã ID riêng biệt cho từng phiên đăng nhập và từng thời điểm thanh toán. Sau cùng, ta sẽ có 2 bảng dữ liệu như sau

Code
website[, session_id := .GRP, by = .(name, session_start_time)]

momo[, payment_id := .GRP, by = .(name, purchase_time)]

website |> table_style("Bảng theo dõi phiên đăng nhập")
Bảng theo dõi phiên đăng nhập
name session_start_time session_id
Spendy Adam 2024-01-03 10:00:00 1
Visitor Jane 2024-01-01 09:10:00 2
Visitor Jane 2024-01-09 02:15:00 3
Indecisive Suzy 2024-01-01 11:01:00 4
Indecisive Suzy 2024-01-02 08:59:00 5
Indecisive Suzy 2024-01-05 18:18:00 6
Indecisive Suzy 2024-01-07 19:03:00 7
Indecisive Suzy 2024-01-08 19:01:00 8
Frequent Tom 2024-01-02 13:09:00 9
Frequent Tom 2024-01-03 19:22:00 10
Frequent Tom 2024-01-08 08:44:00 11
Frequent Tom 2024-01-08 20:22:00 12
Frequent Tom 2024-01-10 17:36:00 13
Frequent Tom 2024-01-15 16:56:00 14
Code
momo |> table_style("Bảng theo dõi việc thanh toán")
Bảng theo dõi việc thanh toán
name purchase_time payment_id
Spendy Adam 2024-01-03 10:06:00 1
Spendy Adam 2024-01-03 10:15:00 2
Non-web Jessica 2023-12-02 17:58:00 3
Indecisive Suzy 2024-01-08 19:10:00 4
Frequent Tom 2024-01-03 19:28:00 5
Frequent Tom 2024-01-08 20:33:00 6
Frequent Tom 2024-01-10 17:46:00 7

Thực hiện và kết quả

Trước khi tiến hành gộp dữ liệu, tôi muốn tạo một biến thời điểm tương tự như cũ cho mỗi bảng dữ liệu để gộp bởi vì khi gộp một trong hai bảng sẽ mất đi một biến thời điểm và không dễ để xác định của đó là của bảng nào

Code
website[, join_time := session_start_time]
momo[, join_time := purchase_time]

website |> table_style("Bảng theo dõi phiên đăng nhập")
Bảng theo dõi phiên đăng nhập
name session_start_time session_id join_time
Spendy Adam 2024-01-03 10:00:00 1 2024-01-03 10:00:00
Visitor Jane 2024-01-01 09:10:00 2 2024-01-01 09:10:00
Visitor Jane 2024-01-09 02:15:00 3 2024-01-09 02:15:00
Indecisive Suzy 2024-01-01 11:01:00 4 2024-01-01 11:01:00
Indecisive Suzy 2024-01-02 08:59:00 5 2024-01-02 08:59:00
Indecisive Suzy 2024-01-05 18:18:00 6 2024-01-05 18:18:00
Indecisive Suzy 2024-01-07 19:03:00 7 2024-01-07 19:03:00
Indecisive Suzy 2024-01-08 19:01:00 8 2024-01-08 19:01:00
Frequent Tom 2024-01-02 13:09:00 9 2024-01-02 13:09:00
Frequent Tom 2024-01-03 19:22:00 10 2024-01-03 19:22:00
Frequent Tom 2024-01-08 08:44:00 11 2024-01-08 08:44:00
Frequent Tom 2024-01-08 20:22:00 12 2024-01-08 20:22:00
Frequent Tom 2024-01-10 17:36:00 13 2024-01-10 17:36:00
Frequent Tom 2024-01-15 16:56:00 14 2024-01-15 16:56:00
Code
momo |> table_style("Bảng theo dõi việc thanh toán")                                                                                                                                              
Bảng theo dõi việc thanh toán
name purchase_time payment_id join_time
Spendy Adam 2024-01-03 10:06:00 1 2024-01-03 10:06:00
Spendy Adam 2024-01-03 10:15:00 2 2024-01-03 10:15:00
Non-web Jessica 2023-12-02 17:58:00 3 2023-12-02 17:58:00
Indecisive Suzy 2024-01-08 19:10:00 4 2024-01-08 19:10:00
Frequent Tom 2024-01-03 19:28:00 5 2024-01-03 19:28:00
Frequent Tom 2024-01-08 20:33:00 6 2024-01-08 20:33:00
Frequent Tom 2024-01-10 17:46:00 7 2024-01-10 17:46:00

Gộp về phía trước (Rolling Forward)

Bây giờ ta sẽ trả lời câu hỏi “phiên đăng nhập nào được thực hiện trước thời điểm thanh toán?”

Code
website[momo, on = .(name, join_time), roll = T] |> # tương đương với website[momo, on = .(join_time), roll = Inf]
   table_style("")
name session_start_time session_id join_time purchase_time payment_id
Spendy Adam 2024-01-03 10:00:00 1 2024-01-03 10:06:00 2024-01-03 10:06:00 1
Spendy Adam 2024-01-03 10:00:00 1 2024-01-03 10:15:00 2024-01-03 10:15:00 2
Non-web Jessica NA NA 2023-12-02 17:58:00 2023-12-02 17:58:00 3
Indecisive Suzy 2024-01-08 19:01:00 8 2024-01-08 19:10:00 2024-01-08 19:10:00 4
Frequent Tom 2024-01-03 19:22:00 10 2024-01-03 19:28:00 2024-01-03 19:28:00 5
Frequent Tom 2024-01-08 20:22:00 12 2024-01-08 20:33:00 2024-01-08 20:33:00 6
Frequent Tom 2024-01-10 17:36:00 13 2024-01-10 17:46:00 2024-01-10 17:46:00 7

Trong bảng kết quả:

  • Mỗi lần thực hiện thanh toán đều được ghép với lần đăng nhập gần nhất trước đó
  • Thực hiện thanh toán nhưng không truy cập web trước đó vẫn xuất hiện ở bảng (Jessica)
  • Jane “vãng lai” không xuất hiện trong bảng bởi vì không thực hiện thanh toán nào
  • Một lần đăng nhập của Adam được ghép với cả hai lần thanh toán của anh ấy

Gộp về phía sau (Rolling Backward)

Giờ ta sẽ đổi vị trí của hai bảng và trả lời câu hỏi “phiên đăng nhập nào quyết định việc thanh toán?”

Code
momo[website, on = .(name, join_time), roll = -Inf] |> 
   table_style("")
name purchase_time payment_id join_time session_start_time session_id
Spendy Adam 2024-01-03 10:06:00 1 2024-01-03 10:00:00 2024-01-03 10:00:00 1
Visitor Jane NA NA 2024-01-01 09:10:00 2024-01-01 09:10:00 2
Visitor Jane NA NA 2024-01-09 02:15:00 2024-01-09 02:15:00 3
Indecisive Suzy 2024-01-08 19:10:00 4 2024-01-01 11:01:00 2024-01-01 11:01:00 4
Indecisive Suzy 2024-01-08 19:10:00 4 2024-01-02 08:59:00 2024-01-02 08:59:00 5
Indecisive Suzy 2024-01-08 19:10:00 4 2024-01-05 18:18:00 2024-01-05 18:18:00 6
Indecisive Suzy 2024-01-08 19:10:00 4 2024-01-07 19:03:00 2024-01-07 19:03:00 7
Indecisive Suzy 2024-01-08 19:10:00 4 2024-01-08 19:01:00 2024-01-08 19:01:00 8
Frequent Tom 2024-01-03 19:28:00 5 2024-01-02 13:09:00 2024-01-02 13:09:00 9
Frequent Tom 2024-01-03 19:28:00 5 2024-01-03 19:22:00 2024-01-03 19:22:00 10
Frequent Tom 2024-01-08 20:33:00 6 2024-01-08 08:44:00 2024-01-08 08:44:00 11
Frequent Tom 2024-01-08 20:33:00 6 2024-01-08 20:22:00 2024-01-08 20:22:00 12
Frequent Tom 2024-01-10 17:46:00 7 2024-01-10 17:36:00 2024-01-10 17:36:00 13
Frequent Tom NA NA 2024-01-15 16:56:00 2024-01-15 16:56:00 14

Trong bảng kết quả:

  • Mỗi lần thực hiện thanh toán đều được ghép với lần đăng nhập gần nhất trước đó
  • Jessica không xuất hiện vì không truy cập lần nào cả
  • Jane có truy cập nhưng không mua vẫn xuất hiện ở bảng kết quả
  • Adam chỉ xuất hiện một lần với thời điểm thanh toán gần với lần đăng nhập nhất
  • Tất cả các phiên đăng nhập của Suzy đều ứng với một lần mua hàng của duy nhất của cô ấy

Gộp có điều kiện

Nếu muốn thêm một điều kiện cho cách gộp ở trên: gộp thanh toán cho phiên đăng nhập với sao cho thời điểm thanh toán xuất hiện sau khi đăng nhập và cả 2 chỉ trong vòng 12 tiếng

Code
condition <- 60 * 60 * 12 # 12 tiếng = 60 giây * 60 phút * 12 giờ

momo[website, on = .(name, join_time),roll = -condition] |> 
   table_style("")
name purchase_time payment_id join_time session_start_time session_id
Spendy Adam 2024-01-03 10:06:00 1 2024-01-03 10:00:00 2024-01-03 10:00:00 1
Visitor Jane NA NA 2024-01-01 09:10:00 2024-01-01 09:10:00 2
Visitor Jane NA NA 2024-01-09 02:15:00 2024-01-09 02:15:00 3
Indecisive Suzy NA NA 2024-01-01 11:01:00 2024-01-01 11:01:00 4
Indecisive Suzy NA NA 2024-01-02 08:59:00 2024-01-02 08:59:00 5
Indecisive Suzy NA NA 2024-01-05 18:18:00 2024-01-05 18:18:00 6
Indecisive Suzy NA NA 2024-01-07 19:03:00 2024-01-07 19:03:00 7
Indecisive Suzy 2024-01-08 19:10:00 4 2024-01-08 19:01:00 2024-01-08 19:01:00 8
Frequent Tom NA NA 2024-01-02 13:09:00 2024-01-02 13:09:00 9
Frequent Tom 2024-01-03 19:28:00 5 2024-01-03 19:22:00 2024-01-03 19:22:00 10
Frequent Tom 2024-01-08 20:33:00 6 2024-01-08 08:44:00 2024-01-08 08:44:00 11
Frequent Tom 2024-01-08 20:33:00 6 2024-01-08 20:22:00 2024-01-08 20:22:00 12
Frequent Tom 2024-01-10 17:46:00 7 2024-01-10 17:36:00 2024-01-10 17:36:00 13
Frequent Tom NA NA 2024-01-15 16:56:00 2024-01-15 16:56:00 14