"Funnel analysis" 顺序很重要

"Funnel analysis" where order matters

漏斗分析通常是通过获取基础数据来完成的,然后在该数据中执行子查询以查看可以将多少数据存储到该部分中。一个好的用例是:

User signup (1,290) ==> Sign in (897) ==> User purchase (42)

我正在尝试分析各种日志以进行 sequence/anomaly 检测。与常见的漏斗分析不同,事件的顺序和时间戳非常重要。让我们以下面的例子为例,当用户打开文本编辑器然后发出网络请求时,我们试图标记可疑 activity(例如恶意软件安装)。日志可能如下所示:

[2019-07-14 17:54:04,251] generic.py:98@main [INFO] File opened
[2019-07-14 17:56:03,566] generic.py:98@main [INFO] Network request made
[2019-07-14 17:58:03,883] generic.py:98@main [INFO] File closed

我们会将此标记为可疑,因为网络请求是在文件打开后立即发出的。

然而,这并不可疑:

[2019-07-14 17:54:04,251] generic.py:98@main [INFO] File opened
[2019-07-14 17:58:05,883] generic.py:98@main [INFO] File closed
[2019-07-14 17:56:06,566] generic.py:98@main [INFO] Network request made

而且,我们还将设置一个“10 秒”的时间限制,从文件首次打开到发出网络请求。所以这也不会被标记为可疑:

[2019-07-14 17:54:04,251] generic.py:98@main [INFO] File opened
[2019-07-14 18:56:06,566] generic.py:98@main [INFO] Network request made
[2019-07-14 18:58:05,883] generic.py:98@main [INFO] File closed

目前,这是通过 for 循环和许多用于检查条件的内部对象来完成的。但是,我想知道是否可以直接在 SQL 中执行这种类型的 "error detection",因为它会快得多(如果可能的话),而且我可以轻松地使用以下形式的数据timestamp / action,例如:

session_id          timestamp                       action
123                 2019-07-14 17:54:04,251         file_opened
123                 2019-07-15 17:54:04,251         network_request
123                 2019-07-16 17:54:04,251         file_closed

那么,回到上面的问题:

SQL 是一种查询语言,不是为计算而设计的。此外,数据库也不是为计算而设计的。它专注于写入、读取、存储、一致性等,而不是计算。

所以在这种情况下,我认为 SQL 可以用于您的简单示例,但它不适合您的实际情况,因为它可能太复杂了。

回到计算,这取决于数据量。对于比较简单的任务,我们通常使用pandasscipynumpy作为工具。而对于一些庞大的任务,我们可能会使用 spark 作为计算引擎,并使用连接器将 spark 与数据库连接起来,然后使用 pyspark 来操作数据。

这里有一个 pandas 的例子,基于你给出的例子:

import pandas as pd
import io
import datetime

# Prepare data, you can assume that you have already got this
data = """
session_id;timestamp;action
123;2019-07-14 17:54:04,251;file_opened
123;2019-07-15 17:54:04,251;network_request
123;2019-07-16 17:54:04,251;file_closed
""".strip()
df = pd.read_csv(io.BytesIO(data.encode()), delimiter=";", parse_dates=["timestamp"])

df["action_before"] = df["action"].shift(1)
df["time_elapsed"] = df["timestamp"] - df["timestamp"].shift(1)

# This will give you an empty dataframe as the third condition is not satisfied.
result =  df[(df["action"] == "network_request") 
             & (df["action_before"] == "file_opened") 
             & (df["time_elapsed"] < datetime.timedelta(minutes=10))]