使用有限数据每月对每个标记(或标记组)的页面视图进行近似?

时间:2009-09-10 19:01:37

标签: sql sql-server tsql

使用Stack Overflow公共数据转储,我创建了三个简单的表:

  • 问题(Question_Id,View_Count,Creation_Date)
  • 标签(Tag_Name)
  • QuestionTags(Question_Id,Tag_Name)

问题表包含数十万行,其中Creation_Date从一年前到今天。查看数据,有两个值得注意的趋势:

  • 按期间增加的问题数量 - 例如,本月提出的问题多于三个月前
  • 问题观点有长尾 - 通过查看基于周开放的观点,我们可以看到大多数问题的观点都发生在第一周;第二和第三的数量较少;并且在接下来的几周里有一条长而恒定的尾巴

如果这些因素都没有发挥作用,估计一个月内给定标签(或一组标签)的流量是相当简单的:

SELECT YEAR(Q.Creation_Date)
      ,MONTH(Q.Creation_Date)
      ,SUM( Q.View_Count / DATEDIFF(m,Q.Creation_Date,GETDATE()) )
  FROM Questions Q
       JOIN QuestionTags QT
         ON Q.Question_Id = QT.Question_Id
 WHERE QT.Tag_Name IN ('c#','.net', ... )
GROUP BY YEAR(Q.Creation_Date), MONTH(Q.Creation_Date)
ORDER BY 1,2

但由于上述因素(特别是长尾),我不确定如何近似观点。我的想法是创建一个函数,使用长尾公式,将根据当前视图数和周开放计算一个月的视图。

这是我想出来找到尾巴的原因:

DECLARE @SDTE DATETIME, @EDTE DATETIME
SELECT @SDTE = '2009-01-11' -- after new years holiday
      ,@EDTE = CAST( MAX([Creation_Date])  AS INT )
  FROM [Questions]


SELECT [DaysOpen_Count]
      ,AVG( [WView_Count] ) 
       FROM
  (
  SELECT QT.[Tag_Name], 
      Q.[View_Count],
      [DaysOpen_Count] = DATEDIFF(DAY, Q.[Creation_Date], @EDTE),
      [WView_Count] = CAST( Q.[View_Count] / ( DATEDIFF(DAY, Q.[Creation_Date], @EDTE) / 7.0 ) AS INT )
    FROM [Questions] Q
      INNER JOIN [QuestionTags] QT
        ON Q.[Question_Id] = QT.[Question_Id]
   WHERE [Tag_Name] IN ('c#','.net',...)
     AND [Creation_Date] < @EDTE
  ) Q
GROUP BY [DaysOpen_Count]
ORDER BY 1,2

我应该如何继续创建此SQL查询?

最终目标是一个存储的程序,它输入一个CSV字符串的标签,并吐出过去六个月对这些标签的页面浏览量。

更新在“获得”风滚草徽章之后,我认为是赏金的时候了!

3 个答案:

答案 0 :(得分:5)

您需要考虑一个指数视图衰减曲线,类似于此 - http://en.wikipedia.org/wiki/Exponential_decay

我们需要的是曲线下面积达到所需时间(以天为单位)。

如果你做数学,你会得到一个结果

Views = V/λ[1 - e^(-λt)]

t(创建日期 - 今天的日期 - 1)

V是我们的观看次数

λ可以是2ln2 / T或1.4 / T

T可以是5天或7天的主要生命时间。让我们看看它。

我们在这里做了很多假设,因为SO的动态性质。但我很肯定它会带来不错的结果。

您现在要做的就是替换相应的值并获取视图。

答案 1 :(得分:2)

我想到了这种估算尾巴的方法:

获取标签列表,     对于这些标签中的每个问题         在creation_date之后的第一个月给出View_Count的80%         在creation_date之后的第二个月给出View_Count的10%         在截至今天的剩余月份之间平均分配10%

当然80%,10%只是我的选择,它们可以根据实际数据更精确地计算。此外,第二个月10%可能会被淘汰。所有这些逻辑都在:CASE WHEN diff ...部分。

您获得估计的view_count / question / month

然后您所要做的就是每月总计view_count,如果您想要一个时间窗口,请在月份中添加条件

我创建了一个可以执行此操作的存储过程,但您必须首先创建一个临时表#tags(Tag_name),您可以在其中放置所需的标记。

CREATE PROCEDURE GetTagViews @startDate datetime, @endDate datetime
As

IF exists (SELECT null FROM sysobjects WHERE name = '#months' and type = 'U')
    DROP TABLE #MONTHS

CREATE TABLE #MONTHS
(
    month datetime
)

DECLARE @currMonth datetime
SELECT @currMonth = MIN(Creation_Date) FROM Questions

-- Populate #MONTHS with all the months from the oldest
-- question creation_date to Today
WHILE @currMonth < getdate()
BEGIN
    -- insert date starting at the beginning og the month
    INSERT INTO #MONTHS select @currMonth - day(@currMonth) + 1
    SELECT @currMonth = dateadd(m, 1, @currMonth) -- advance 1 month
END

SELECT YEAR(month) y, MONTH(month) m, SUM(curr_month_views) Views FROM (
SELECT Q1.month, Q1.diff, round(
CASE WHEN diff = dmin and diff = dmax THEN View_Count
     WHEN diff = dmin and diff < dmax THEN 0.8*View_Count
     WHEN diff = dmin+1 and diff < dmax THEN 0.1*View_Count
     WHEN diff = dmin+1 and diff = dmax THEN 0.2*View_Count
     WHEN diff >= dmin+2 THEN 0.1/(dmax - (dmin+2) + 1)*View_Count
     ELSE 0
END, 0) curr_month_views
FROM (
SELECT  Q.question_id, m.month, 
    DATEDIFF(m, Q.Creation_Date, m.month) diff, 
    Q.View_Count, dmin, dmax
FROM Questions Q,
     #MONTHS m, 
     (SELECT MIN(DATEDIFF(m, Q.Creation_Date, m.month)) [dmin], 
             MAX(DATEDIFF(m, Q.Creation_Date, m.month)) [dmax]
        FROM Questions Q,#MONTHS m
       WHERE DATEDIFF(m, Q.Creation_Date, m.month) >= 0) MINMAX
) Q1 join QuestionTags QT on Q1.question_id = QT.question_id 
     join #tags on #tags.Tag_Name = QT.Tag_Name
) b WHERE month >= @startDate - day(@startDate) + 1
      AND month <= @enddate - day(@enddate) + 1
GROUP BY Year(month), Month(month)
ORDER BY 1, 2

如果我使用以下数据运行此过程:

Question_Id View_Count  Creation_Date                  tag_name         
----------- ----------- ------------------------------ ----------
0           42          2009-09-10 00:00:00.000        sql
1           326         2008-08-04 00:00:00.000        sql
2           377         2008-08-04 00:00:00.000        sql
3           568         2008-08-03 00:00:00.000        sql
4           839         2008-08-01 00:00:00.000        sql
5           228         2009-03-01 00:00:00.000        sql
6           178         2009-03-11 00:00:00.000        sql
7           348         2009-08-11 00:00:00.000        c#

使用'sql'填充#tags

GetTagViews '20090501', '20091001'

    y           m           Views                                    
    ----------- ----------- ---------------
    2009        5           21.000000000000
    2009        6           21.000000000000
    2009        7           21.000000000000
    2009        8           21.000000000000
    2009        9           55.000000000000

使用'c#'填充#tags

GetTagViews '20090501', '20091001'

    y           m           Views                                    
    ----------- ----------- ---------------------------------------- 
    2009        5           .000000000000
    2009        6           .000000000000
    2009        7           .000000000000
    2009        8           278.000000000000
    2009        9           35.000000000000

使用'sql'和&amp ;;填充#tags 'c#'

GetTagViews '20090501', '20091001'

    y           m           Views                                    
    ----------- ----------- ----------------
    2009        5           21.000000000000
    2009        6           21.000000000000
    2009        7           21.000000000000
    2009        8           299.000000000000
    2009        9           90.000000000000

(你看到(sql,c#)的峰值与2009-08赛季的(sql)比较,这是由于那个月被问到c#问题。)

N.B。:如果你总结详细的观点并与给定问题的原始数据进行比较,估计的结果会导致一些观点的差异(~1)!

答案 2 :(得分:0)

要模拟长尾,只需引入一个常量。或者使用对数函数。

your_formula(delta_t) + C

1 / (1 + log(1 + delta_t))

(省略系数)