Hệ thống tracking active, retention rate - Phần 2
White

Minh Hoang TO viết ngày 10/07/2016

Trước hết xin nhắc lại mục đích của bài viết này là đưa ra giải pháp build-from-scratch cho bài toán sau:

Tôi có một ứng dụng A, tôi đang liên kết với các đối tác {C_1, C_2, ..., C_n} để phát triển user cho sản phẩm. Vậy làm thế nào để tôi có thể xác định được độ hiệu quả cũng như chất lượng của tập user mỗi đối tác mang lại?

Phần trước của bài viết đã trình bày các nội dung chính sau:

  • Các metrics cần đo cho bài toán
  • Mô hình phân kênh và tracking data
  • Mô tả cách implement trên client/server

Phần tiếp theo của bài viết tập trung trình bày một số show-case trích xuất dữ liệu phức tạp hơn. Qua đó cho bạn đọc thấy được chỉ cần kết hợp mô hình dữ liệu tracking khá đơn giản với việc sử dụng Elastic Search query linh hoạt là có thể thu được hầu hết các metrics cần thiết cho bài toán chính mà bài viết nêu ra.

Tính active user trong khoảng thời gian [start, end] và [start-1, end-1]

Cặp giá trị này thường được dùng để thể hiện số liệu trong khoảng thời gian người dùng dashboard chọn và phần trăm tăng giảm so với kì trước.

Elastic Search cho phép trả về nhiều aggregrations trong cùng query nên để tính cặp giá trị này ta chỉ cần dùng query như dưới đây.

def active_user_in_range(start_time_epoch, end_time_epoch):
    q = {
        "query": {
            "filtered": {
                "filter": {
                    "bool": {
                        "must": [
                            {
                                "terms": {
                                    "action": ["FIRST_RUN", "START"]
                                }
                            },
                            {
                                "range": {
                                    "created": {
                                        "gte": (start_time_epoch - 86400000),
                                        "lte": (end_time_epoch)
                                    }
                                }
                            }
                        ]
                    }

                }
            }
        },

        "aggs": {
            "range_active_devices_current": {
                "filter": {
                    "range": {
                        "created": {
                            "gt": start_time_epoch
                        }
                    }
                },
                "aggs": {
                    "unique_devices": {
                        "cardinality": {"field": "device_id"}
                    }
                }
            },
            "range_active_devices_past": {
                "filter": {
                    "range": {
                        "created": {
                            "lt": end_time_epoch - 86400000
                        }
                    }
                },
                "aggs": {
                    "unique_devices": {
                        "cardinality": {"field": "device_id"}
                    }
                }
            }
        }
    }
...

Tính active user mỗi ngày trong các khoảng thời gian [start, end] và [start-1, end-1]

Ta có thể dùng Histogram Aggregation cho thống kê này

def au_histogram_in_range(start_time_epoch, end_time_epoch):
    past_start_time = start_time_epoch - 86400000
    past_end_time = end_time_epoch - 86400000
    q = {
        "query": {
            "filtered": {
                "filter": {
                    "bool": {
                        "must": [

                            {
                                "terms": {
                                    "action": ["FIRST_RUN", "START"]
                                }
                            },
                            {
                                "range": {
                                    "created": {"gte": past_start_time, "lte": end_time_epoch}
                                }
                            }
                        ]
                    }
                }
            }
        },
        "aggs": {
            "range_active_devices_current": {
                "filter": {
                    "range": {
                        "created": {
                            "gte": start_time_epoch
                        }
                    }
                },

                "aggs": {
                    "active_histogram": {
                        "histogram": {
                            "field": "created",
                            "interval": 86400000,
                            "min_doc_count": 0,
                            "extended_bounds": {
                                "min": start_time_epoch,
                                "max": end_time_epoch
                            }
                        },
                        "aggs": {
                            "unique": {
                                "cardinality": {
                                    "field": "device_id"
                                }
                            }
                        }
                    }
                }
            },
            "range_active_devices_past": {
                "filter": {
                    "range": {
                        "created": {
                            "lte": past_end_time,
                        }
                    }
                },
                "aggs": {
                    "active_histogram": {
                        "histogram": {
                            "field": "created",
                            "interval": 86400000,
                            "min_doc_count": 0,
                            "extended_bounds": {
                                "min": past_start_time,
                                "max": past_end_time,
                            }
                        },
                        "aggs": {
                            "unique": {
                                "cardinality": {
                                    "field": "device_id"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
  ...

Với dữ liệu trên, ta có thể vẽ biểu đồ thống kê active như dưới đây

alt text

Tính số lượng user mang lại bởi đối tác C_k, cài ứng dụng vào ngày X và vẫn active sau ngày X+Y

Đây là thông số cơ sở cho các thống kê về retention rate, nó cho ta biết chất lượng cài đặt mà đối tác C_k mang lại có thực sự tốt hay không.

Việc này tương đương với việc đếm các START event sau ngày X+Y mà có FIRST_RUN rơi vào ngày X và có thông số channel_id là C_k

def count_active_users(channel_id, product_id, install_date, elapsed_days):
    fr_lower = install_date
    fr_upper = install_date + 86400000
    c_lower = install_date + elapsed_days * 86400000
    c_upper = c_lower + 86400000
    q = {
        "query": {
            "filtered": {
                "filter": {
                    "bool": {
                        "must": [
                            {
                                "terms": {
                                    "action": [
                                        "START"
                                    ]
                                }
                            },
                            {
                                "range": {
                                    "first_run": {"gte": fr_lower, "lte": fr_upper}
                                }
                            },
                            {
                                "range": {
                                    "created": {"gte": c_lower, "lte": c_upper}
                                }
                            }
                        ]
                    }
                }
            }
        },
        "aggs": {
            "active_users": {
                "cardinality": {
                    "field": "device_id"
                }
            }
        },
        "_source": []
    }

    try:
        active_count = es.search(index=index_name, doc_type=doc_type, body=q, search_type='count')
        return active_count['aggregations']['active_users']['value']
    except Exception as e:
        print e
        return 0

Tại sao không dùng Firebase, Fabric, Parse luôn đi?

Firebase, Fabric, Parse đều có cung cấp tính năng tracking rồi, vậy tại sao không dùng luôn đi mà lại build from scratch làm gì?

Câu trả lời nằm ở chỗ các giải pháp cloud-based nói trên có rất nhiều hạn chế cho nhu cầu phân kênh khi số lượng đối tác của bạn rất lớn (thường là không có API hoàn chỉnh và có giới hạn về số lượng custom event).

Nếu bạn đang làm hệ thống quảng cáo (người viết bài này ko làm gì liên quan đến hệ thống quảng cáo cả :smile:), hay đang làm các ứng dụng lớn như trình duyệt Yolo Browser, UC Browser, phải làm việc với rất nhiều đối tác publishers thì việc tự xây dựng hệ thống tracking là không tránh được.

Lời kết

Bài viết đưa ra hướng implement cho một hệ thống tracking chạy được. Việc chạy như thế nào sẽ còn phụ thuộc rất nhiều vào quá trình implement cụ thể:smile::

  • Kiến trúc server-side, đặc biệt là phần thiết kế cài đặt Elastic Search
  • Khai thác sâu hơn chức năng indexing/search của Elastic Search
  • Tricks tối ưu phía client-side ...

Một trong những hạn chế rất lớn trong phần document trên trang chủ của Elastic Search đó là document viết các phần râu ria rất nhiều nhưng các ví dụ minh hoạ lại quá sơ sài. Theo kinh nghiệm của cá nhân người viết bài thì để khắc phục việc này ta có thể tra cứu phần REST API của Elastic Search

https://github.com/elastic/elasticsearch/tree/master/core/src/main/java/org/elasticsearch/rest

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Minh Hoang TO

4 bài viết.
5 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
10 0
Bài viết này trình bày một giải pháp buildfromscratch cho bài toán khá phổ biến sau Tôi có một ứng dụng A, tôi đang liên kết với các đối tác {C_1,...
Minh Hoang TO viết hơn 1 năm trước
10 0
White
6 1
Bài viết này trình bày cách thiết lập cơ chế build CI thông qua Jenkins cho các Android project (libraries hoặc applications) được quản lý bằng (Li...
Minh Hoang TO viết hơn 1 năm trước
6 1
White
1 0
Bài viết này trình bày cách xử lý khá đơn giản để đăng nhập vào tài khoản quản trị của TeamCity khi bạn vô tình quên password. Thay vì tìm hiểu cơ...
Minh Hoang TO viết hơn 1 năm trước
1 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
4 bài viết.
5 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!