数据产品开发前的必修课(四):躲开平均数骗局

浏览: 1331

Clipboard Image.png

平均数计算的目的,是用来判断一个序列的集中趋势,进而对数据总体有一个合理的认知。要求一个序列的平均数,主流算法其实有三种:算术平均数,中位数,众数。

算术平均数即大家用得最多的,几个数加总后除以个数;而中位数是序列中的数进行大小排序后,排名在中间的数(若序列中数值个数是偶数,则取排名在中间的两个数的算数平均数);而众数是指序列中出现频率最高的那个数。

三种算法在excel中对应的函数是average(),median()和mode()

三种算法各有特点,也各有使用条件。算数平均数的使用,有时侯会把你带坑里。我用最近遇到的一个案例,来说说为什么要使用中位数。

  • 用中位数来避免算数平均数陷阱

  • 中位数数的计算方式,我用下面一段python的代码表示,程序员们或许更好理解:

    def Median(a):
    a = sorted(a)
    l = len(a)
    l2 = l / 2
    return a[l2] if l % 2 else (a[l2] + a[l2 - 1]) / 2.0

    (文章末尾附录中我列出了sql语句计算中位数的方式)

    最近在做明道crm中的统计模块,计算订单承担周期时,遇到了算数平均数陷阱:

    比如,某个销售在6月份签了5单,每单的周期是order_period=[15,18,26,30,365]。其中有一个订单是去年注册的线索(小概率事件),因此周期的算数平均数90.8。之前成单周期水平都在一个月左右的人,在这个月突然变成了3个月,这个数值显然是不公平的。这样的巨幅变化,也会给销售决策造成干扰(对数据总体的认知被扭曲了)。因为365这个极大值,拉高了整个序列的算数平均数,使得数据失真。这正是算数平均数的弊端。

    取用中位数,便排除了特数值带来的影响。纵使有一单是去年的线索,这位顾问的成单周期是26,依然是正常水准。

  • 算数平均数与中位数的结合

  • 而算数平均数也有它的优势,它是汇总所有数值信息后,计算的集中趋势,只要不出现特殊值,它的精确度是超过中位数的。所以,算数平均数和中位数如何结合呢?

    crm成单周期的统计,又引发出另一个问题:从哪个层面来计算中位数?

    我们的计算其实有四级:

    公司销售部宏观的成单周期➡️每个城市分公司的成单周期➡️各个销售团队的成单周期➡️个人的成单周期。

    如果每一级都计算中位数,显然是过于粗放的。我采用的方式是在个人这一级取中位数,之后的所有层级都取算数平均数。

    但这还是有问题的:

    个人成单量比较少的情况下,计算中位数也会不合理。比如,某个团队三个人,每个人周期序列分别是[2,300,200],[15,3],[5,14,26],中位数分别是200,14,14,算团队算数平均数时,依然没有排除掉极值的影响。

    所以,最合理的方式也许是:

    人均订单数超过5单的团队,计算一级中位数;而人均订单数小于5的团队,计算两级中位数。

    当然,这里组合的方式就需要读者您根据自己业务情况来定了。

    众数目前我还没有用上,它一般会用在离散型序列且数值个数较多的时候。

    本周的内容简单实用,到此为止。欢迎大家关注我的“川术”公众号,获取企业内部运营数据分析的点滴知识。


    附录:

    sql语句中似乎没有直接计算中位数的方式,我这边从网上找了一段,个人读后感觉是靠谱的,也贴出来,供大家理解。概括的说,它是使用2个子查询来计算,1个子查询用来排序,1个子查询用于计算总数,然后根据总数的 奇/偶,来决定哪些行需要进行计算。

    SELECT
    data_with_rownumber.Name,
    AVG(data_with_rownumber.val) AS median
    FROM
    (
    SELECT
    ROW_NUMBER() OVER(PARTITION BY Name ORDER BY val) AS seq,
    Name,val
    FROM
    test_median
    ) data_with_rownumber
    JOIN
    (
    SELECT
    Name, COUNT(1) AS NumOfVal
    FROM
    test_median
    GROUP BY
    Name
    ) data_count
    ON
    (
    data_count.Name = data_with_rownumber.Name
    AND
    (
    (data_count.NumOfVal % 2 = 0 AND data_with_rownumber.seq
    IN (data_count.NumOfVal / 2, (data_count.NumOfVal / 2) + 1))
    OR
    (data_count.NumOfVal % 2 = 1
    AND
    data_with_rownumber.seq = 1 + data_count.NumOfVal / 2)
    )
    )
    GROUP BY
    data_with_rownumber.Name

    推荐 0
    本文由 胡晨川 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
    转载、引用前需联系作者,并署名作者且注明文章出处。
    本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

    0 个评论

    要回复文章请先登录注册