最近不知道写什么了,基本python的各种功能百度一下,都能搜到一大把,最近itchat好像很火,不过对这个不是很感冒,等以后有兴趣或者用的上的时候研究研究
准备把统计方面的东西再看看,就写一些简单的统计算法吧,其实这些在python里面都有现成的,但是有句名言“不要只会用,还要知道原理”(是哪个名人说的?如果没有,那就算我说的了),所以写这些供学习之用。那么从头再来,循序渐进,先写这些,本来想写个对数函数的,结果发现真TM难,如果谁能写出来麻烦告知下,我也膜拜大神一下
1、算数平均数
2、算数平均回报
3、中位数
4、众数
5、极差
6、四分位数
7、几何平均数
8、几何平均回报
文/明道软件 胡晨川 个人公众号“川术”
9、对数(没写出来)
下面上代码,和往常一样,含说明,为了更容易理解,就不直接用函数了,尽量用最原始的语法写
import random
case_list = [] #准备个容器,放样例
#随机生成10个1到30的小数,不能重复,把10个小数放到容器里
while len(case_list) < 10:
rand_float = random.uniform(1,30)
if rand_float in case_list:
continue
case_list.append(rand_float)
case_list = [round(case,2) for case in case_list] #格式化一下,不然太长不好看,当然这个因需要而定,我为了显示好看,所以格式化保留两位小数
#我先执行了个结果
print case_list #结果是,[5.77, 27.21, 27.12, 12.65, 23.12, 25.56, 18.6, 23.75, 4.1, 25.13]
case_list[6] = 23.12 #为了后面众数,弄个相同值出来
print str(case_list) #单独打下面了,方便看
'[5.77, 27.21, 27.12, 12.65, 23.12, 25.56, 23.12, 23.75, 4.1, 25.13]'
case_list.sort() #排序,这个算法有很多,单独写,不在这里啰嗦
#先写个累加函数,为了方便,就不try了,默认输入的结果都是对的
def sum_fun(xlist):
n = 0
for x in xlist:
n += x
return n
#统计数据量函数
def len_fun(xlist):
n = 0
for x in xlist:
n += 1
return n
#累乘,和累加一样
def multiply_fun(xlist):
n = 1
for x in xlist:
n *= x
return n
#1、算数平均数:加和/个数
def sum_mean_fun(case_list):
sum_mean_num = sum_fun(case_list)/len_fun(case_list)
return sum_mean_num #结果是19.753
#2、算数平均数回报,平均回报率计算,
def sum_mean_rate(case_list):
'((case_list[1]-case_list[0])/case_list(0)+(case_list[2]-case_list[1])/case_list(1)+...(case_list[n]-case_list[n-1])/case_list(n-1))/len(case_list-1)'
n = 1
rate_return = [] #存放回报率
while n < len_fun(case_list):
rate = (case_list[n] - case_list[n-1])/case_list[n-1]
rate_return.append(rate)
n += 1
mean_rate_num = sum_fun(rate_return)/len_fun(rate_return)
return mean_rate_num
#3、中位数,list中间的数,如果count是基数index = len(list)/2,如果是偶数index1 = (len(list)/2 index2 = (len(list)/2)+1)
def median_fun(case_list):
if len_fun(case_list)%2 == 1:
median = case_list[(len_fun(case_list)+1)/2-1]
else:
median = (case_list[len_fun(case_list)/2-1] + case_list[len_fun(case_list)/2])/2
return median
#4、众数,存在最多的数
def modes_fun(case_list):
case_list_delre = list(set(case_list)) #去重,这个如果自己写的话可以用分治法,有兴趣的话可以自己写个
count_max = 0
for case_part in case_list_delre:
case_count = case_list.count(case_part)
if case_count > count_max:
count_max = case_count
max_return = case_part
if count_max == 1:
return None
mode = max_return
return mode
#5、极差,最大-最小,因为已经拍好序,所以index min - index max
def ext_minus_fun(case_list):
ext_minus_num = case_list[len_fun(case_list)-1] - case_list[0]
return ext_minus_num
#6、四分位数,箱图用的,可以避免极值的影响,分别是index1 = len(list)/4 index2 = 3*len(list)/4
def four_bit_fun(case_list):
Q1 = case_list[len_fun(case_list)/4]
Q2 = case_list[3*len_fun(case_list)/4]
return Q1,Q2
#7、几何平均数,和算数平均数不同,把所有都乘过^(1/len(list))
def geom_mean_fun(case_list):
geom_mean_num = multiply_fun(case_list) ** (1.0/len_fun(case_list))
return geom_mean_num
#8、几何平均回报,R = 回报,((1+R1)(1+R2)...(1+Rn))^1/(len(count(R))-1)
def geom_mean_rate(case_list):
n = 1
rate_return = [] #存放回报率
while n < len_fun(case_list):
rate = (case_list[n] - case_list[n-1])/case_list[n-1]
rate_return.append(rate)
n += 1
rate_return = [1+rate for rate in rate_return]
geom_mean_rate_num = multiply_fun(rate_return) ** (1.0/len_fun(rate_return)) - 1
return geom_mean_rate_num
# 验证
if __name__ == '__main__':
rand_list = create_rand_list(1,30,10)
rand_list.sort()
print rand_list
sum_mean_num = sum_mean_fun(rand_list)
print sum_mean_num
mean_rate_num = sum_mean_rate(rand_list)
print mean_rate_num
median_num = median_fun(rand_list)
print median_num
modes_num = modes_fun(rand_list)
print modes_num
ext_minus_num = ext_minus_fun(rand_list)
print ext_minus_num
four_bit_q1,four_bit_q2 = four_bit_fun(rand_list)
print four_bit_q1
print four_bit_q2
geom_mean_num = geom_mean_fun(rand_list)
print geom_mean_num
geom_mean_rate_num = geom_mean_rate(rand_list)
print geom_mean_rate_num
平均数计算的目的,是用来判断一个序列的集中趋势,进而对数据总体有一个合理的认知。要求一个序列的平均数,主流算法其实有三种:算术平均数,中位数,众数。
算术平均数即大家用得最多的,几个数加总后除以个数;而中位数是序列中的数进行大小排序后,排名在中间的数(若序列中数值个数是偶数,则取排名在中间的两个数的算数平均数);而众数是指序列中出现频率最高的那个数。
三种算法在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