将用户事件分组到活动会话中



您负责实现一个新的分析"会话"视图。你会得到一组数据,其中包括个人网页访问量,以及由跟踪cookie生成的访问者ID,该cookie唯一标识每个访问者。根据这些数据,我们需要为每个访问者生成一个会话列表。

数据集如下所示:

"events": [
{
"url": "/pages/a-big-river",
"visitorId": "d1177368-2310-11e8-9e2a-9b860a0d9039",
"timestamp": 1512754583000
},
{
"url": "/pages/a-small-dog",
"visitorId": "d1177368-2310-11e8-9e2a-9b860a0d9039",
"timestamp": 1512754631000
},
{
"url": "/pages/a-big-talk",
"visitorId": "f877b96c-9969-4abc-bbe2-54b17d030f8b",
"timestamp": 1512709065294
},
{
"url": "/pages/a-sad-story",
"visitorId": "f877b96c-9969-4abc-bbe2-54b17d030f8b",
"timestamp": 1512711000000
},
{
"url": "/pages/a-big-river",
"visitorId": "d1177368-2310-11e8-9e2a-9b860a0d9039",
"timestamp": 1512754436000
},
{
"url": "/pages/a-sad-story",
"visitorId": "f877b96c-9969-4abc-bbe2-54b17d030f8b",
"timestamp": 1512709024000
}
]
}

给定这些输入数据,我们希望创建一组传入数据的会话。会话被定义为单个访问者的一组事件,每个连续事件之间的间隔不超过10分钟。访问者可以有多个会话。因此,给定上面的示例输入数据,我们期望输出如下:

{
"sessionsByUser": {
"f877b96c-9969-4abc-bbe2-54b17d030f8b": [
{
"duration": 41294,
"pages": [
"/pages/a-sad-story",
"/pages/a-big-talk"
],
"startTime": 1512709024000
},
{
"duration": 0,
"pages": [
"/pages/a-sad-story"
],
"startTime": 1512711000000
}
],
"d1177368-2310-11e8-9e2a-9b860a0d9039": [
{
"duration": 195000,
"pages": [
"/pages/a-big-river",
"/pages/a-big-river",
"/pages/a-small-dog"
],
"startTime": 1512754436000
}
]
}
}

票据

  • 时间戳以毫秒为单位
  • 事件可能不按时间顺序排列
  • sessionsByUser中的访问者可以按任何顺序排列
  • 对于每个访问者,会话将按时间顺序排列
  • 对于每个会话,URL应按时间顺序排序
  • 对于只有一个事件的会话,持续时间应为零
  • 会话中的每个事件(第一个事件除外(都必须发生在会话中的前一事件的10分钟内。这意味着第一次和最后一次之间可能有10分钟以上事件

注意:我不会向您展示如何制作所需的确切输出格式,但我会向您展示解决此问题的一般方法,希望您能找到如何根据自己的需求进行更改

您将希望从按用户分组事件开始:

events_by_user = events.group_by { |event| event[:visitorId] }

现在,对于每个用户,您需要按时间戳对其事件进行排序:

events_by_user.transform_values! do |events|
events.sort_by { |event| event[:timestamp] }
end

现在,您需要循环浏览每个用户的事件,并按顺序进行比较,根据时间戳相似性将它们分组:

session_length = 10 # seconds
sessions = {}
events_by_user.each do |visitor_id, events|
sessions[visitor_id] = []
events.each do |event|
if sessions[visitor_id].empty?
sessions[visitor_id].push([event])
else
last_session = sessions[visitor_id].last
last_timestamp = last_session.last[:timestamp]
if (event[:timestamp] - last_timestamp) <= session_length
last_session.push(event)
else
sessions[visitor_id].push([event])
end
end
end
end

现在sessions将包含这样的散列:

{
<visitor_id>: [
[<list of events in session 1>],
[<list of events in session 2>]
],
etc.
}

然后,您可以提取开始时间、总持续时间等

将"事件";属性"数组;访问者Id";第一您可以在JavaScript中使用

Array.prototype.reduce((:reduce((方法执行用户提供的"reducer"回调函数,按顺序,传入前面计算的返回值要素减速器在所有元件上运行的最终结果的值是单个值。因此,将{}设置为reducer函数的初始值,每次使用visitorId作为数组的键,该数组将保存访问者的事件,并将当前事件推送到数组中的相应位置。

a variable || []语句用于使"未定义"值为[],空数组。

现在按时间戳升序对刚才构建的事件数组进行排序。循环遍历它并成对比较时间戳(以前的和当前的数组元素(,如果差异低于给定的会话长度(例如10分钟(,则合并两个会话,并将其推送到以"visitorId"为关键字的数组中。使用变量跟踪要合并在一起的会话的索引。

let data = require('d:\dataset.json');
//Group by visitorId
let sessions = {
sessionsByUser: data.events.reduce(function (events, session) {
(events[session['visitorId']] = events[session['visitorId']] || []).push(session);
return events;
}, {})
};

//Sort events by timestamp ascending
for(let key in sessions.sessionsByUser){
let events = sessions.sessionsByUser[key];
events = events.sort((a, b) => {
return a.timestamp - b.timestamp;
});
}
let userSessions = {};
for(let key in sessions.sessionsByUser){
let events = sessions.sessionsByUser[key];
let lastIndex = 0;
for(let i = 0; i < events.length; i++)
{

if(i == 0) {
userSessions[key] = [{
duration: 0,
pages: [events[i].url],
startTime: events[i].timestamp

}]
}
else {
//Check difference (10 min)
if(events[i].timestamp - events[i-1].timestamp < 600000) {
let session = userSessions[key][lastIndex];
session.duration += (events[i].timestamp - events[i-1].timestamp);
session.pages.push(events[i].url);
}
else {
userSessions[key].push({
duration: 0,
pages: [events[i].url],
startTime: events[i].timestamp

});
lastIndex++;
}
}
}
}
let soln = {
sessionsByUser: userSessions
};
console.log(JSON.stringify(soln));
Run command in cmd: Node <filename>.js

更改数据集路径,导航到cmd中的文件目录。节点必须安装在系统上。

最新更新