学习随记

hetal


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

假设检验的两类错误-功效分析和样本量预估

发表于 2020-07-18 | 分类于 统计 , 假设检验 | | 阅读次数:

一个对比实验,需要多大的样本量才能确保统计结果可信?做完实验后,如何判断其结果的显著性?而结果显著性的这个结论,又有多大的可信度?这涉及到假设检验的两类错误的理解,以及衍生出来的功效分析。

假设检验的两类错误

统计假设检验中,我们会先设定原假设($H_0$),并同时设立它的对面,称为备择假设($H_1$)。那么我们根据检验来判断是否拒绝H0,与实际的H0真假,对应有4个组合,其中判断错误的两个组合则分别是第一类和第二类错误。具体如下表:

拒绝$H_0$ 不拒绝$H_0$
$H_0$为真 第一类错误($\alpha$) 正确 ($1-\alpha$)
$H_0$为假 正确($power=1-\beta$) 第二类错误($\beta$)
  • 第一类错误
    • 原假设是真的,但我们拒绝了它(误认为它是假的)
    • 对应假设检验的显著性水平($\alpha$)。
    • 这也是常被人称道的P值的对比标准,例如P值小于0.05则认为通过假设检验,拒绝XX相等的原假设,结论是XX有显著差异
  • 第二类错误
    • 原假设是假的,但我们没有拒绝它(误认为它是真的)
    • 当原假设为假,我们正确拒绝原假设的概率就是 1-第二类错误概率,对应假设检验的功效($power=1-\beta$)。
    • power一般设置0.8以上,才认为拒绝$H_0$的决策,具有足够的功效(这个往往被人遗忘)

alpha和power的详解

举个实例来阐述alpha和power:一家公司更新了销售策略,希望知道单价是否有显著提升,原来的客单价30元/人,公司认为提升到32元/人的课单价才认可新策略。现在收集到了新策略下的n个样本,需要针对这批样本做假设检验,那么要检验的假设如下:

  • 原假设 $H_{0}:\mu=30$
  • 备择假设 $H_{1}: \mu=32$

根据原假设和备择假设,可以分别画出分布图如下:

显然,这个示例需要使用均值T检验,给定显著性水平后,就能根据分布推出临界值,即如果n个样本计算出的T统计量落在图中黑线右侧,则拒绝原假设,犯第一类错误的概率为红色阴影面积(alpha),而接受备择假设的正确率则为蓝色阴影面积(power)。

上述T检验对应alpha和power具体的计算公式如下(即只有分布均值$\mu$的差异,注意求$\beta$的是非中心化的t分布):

  • $ \alpha=P\left(\frac{\bar{X}-30}{s / \sqrt{n}}>t_{1-\alpha, n-1} ; \mu=30\right) $
  • $ 1-\beta=P\left(\frac{\bar{X}-30}{s / \sqrt{n}}>t_{1-\alpha, n-1} ; \mu=32\right) $

样本量的计算

铺垫这么多,终于能讲样本量的计算了。上一部分T检验的功效公式,可以看到,功效除了和样本分布、显著性水平有关,还和样本量$n$有关。因此实际假设检验中有4项相互依赖的指标:

  • 显著性水平,P(第一类错误)
  • 功效,1-P(第二类错误)
  • 样本大小,n
  • 效应值(例如上面效应值定义为$(32-30)/\sigma$)

以上4个指标确定其中三个即可计算出第四个。于是我们要预估假设检验所需的样本量,则需要先给定另外三项的取值,再代入公式计算即可。不同类型的假设检验都有不同的公式,此处不再赘述。

代码示例

R语言中,直接使用library(pwr)包即可实现各类检验的功效分析。以工作中最常见的两个检验(均值T检验和比例检验)为例,代码如下:

示例-均值T检验

以上面的为例,假设根据过去经验知道客单价的标注差为4,想要验证客单价从30提升到了32,在显著性水平为0.05,功效为0.9的标准下,对应需要样本量n:

1
2
library(pwr)
pwr.t.test(d=(32-30)/4,sig.level = 0.05,power = 0.9,type = 'one.sample',alternative = 'greater')

结果显示,要达到以上标准,至少需要36个样本(注意,效应值为d=(32-30)/4,由于只有一组数据,所以是单样本检验’one.sample’,由于只对客单价提高感兴趣,因此只需单边检验 alternative = greater)

1
2
3
4
5
6
7
# One-sample t test power calculation 
#
# n = 35.65269
# d = 0.5
# sig.level = 0.05
# power = 0.9
# alternative = greater

示例-比例检验

某公司想对比两个策略下(一个是对照组,一个是测试组)的转化率是否有显著差异,按照历史经验知道对照组的转化率是0.2,而测试组的目标转化率是提升到0.25),在显著性水平为0.05,功效为0.9的标准下,对应每个组需要样本量n:

1
pwr.2p.test(h=ES.h(0.25,0.2),sig.level = 0.05,power = 0.9,alternative = 'greater')

结果显示,要达到以上标准,至少需要1191个样本。(注意,效应值为ES.h(0.25,0.2))

1
2
3
4
5
6
7
8
9
# Difference of proportion power calculation for binomial distribution (arcsine transformation) 
#
# h = 0.1199023
# n = 1191.362
# sig.level = 0.05
# power = 0.9
# alternative = greater
#
# NOTE: same sample sizes

以上即为确定目标和检验标准之后需要的样本量预估。特别提醒,对效应值的预估是最重要的,alpha和power一般都设置0.05~0.1,0.8~0.9即可,而效应值代表了对该实验目标的界定,如果设定的目标虚高,例如上面的测试组转化率要达到0.5,那么对应每组样本量只需要41个,而实际上测试组样本显示只有0.3的转化率,那么在给定这些的情况下算出来power必然小于设定值:

1
2
# 样本显示两个组转率是0.3和0.2,因此效应值为h=ES.h(0.3,0.2)
pwr.2p.test(h=ES.h(0.3,0.2),sig.level = 0.05,n=41,alternative = 'greater')

结果显示,0.3和0.2比例下的power只有0.28,因此必须继续放量试验,扩大样本量从而提高power。

1
2
3
4
5
6
7
8
9
# Difference of proportion power calculation for binomial distribution (arcsine transformation) 
#
# h = 0.2319843
# n = 41
# sig.level = 0.05
# power = 0.2760888
# alternative = greater
#
# NOTE: same sample sizes

References

  • 卡巴科弗, 高涛, 肖楠, 陈钢. R语言实战 : R in action: data analysis and graphics with R[M]. 人民邮电出版社, 2013.
  • coursera的统计推断-第四周课程

认知提升——系列读书笔记

发表于 2020-06-21 | 分类于 认知合集 , 读书笔记 | | 阅读次数:

这里记录我的读书笔记,结合当前的状态和应用,总结出最有感触和最受用的内容。因此,这可能是一篇持续更新,永不完结的文章。当下的主题主要是如何快速、高效成长,在面对逆境和各种问题的心态,想成为领袖(or前言top)我缺什么,我该做什么,而比我们优秀的人是什么样的思维方式,我们成为他们后,可能又会缺少什么?

本人不太爱读书,但这些书籍,却于我有足够的吸引力,因为认知、软技能的提升具有极高的性价比,在这里,你总能找到一生都很受用的智慧。

《幸福的陷阱》

人们越是竭力追求幸福,就越是苦不堪言。本书不是教人如何追求幸福,而是将重点放在如何从根源上减少挣扎、回避及错失当下的情形。于我而言,当前阶段受益最大的其实是:当遇到有挑战的任务时,聚焦于自己能控制的部分,并且与真实的价值联结,告诉自己,我愿意。

成长

有挑战才有成长,我们常常有绕道而行的心理,只有联结了真实的价值,自己才有真实的意愿去努力,直面挑战。

  • 工作中的挑战,最通用的价值即为:成长。每个感到困难的地方,都是我们的非舒适区,越困难,则越能快速成长。

    • 更直接的说,成长即代表增加自己的核心价值,即代表未来生活和工作的更加的得心应手,代表更能活出生命的意义
  • 工作中/生活中必然有很多焦虑,也会很想逃离。但只需要和“成长“这一价值联结,便会真正愿意努力,愿意接纳这个过程中的各种不良情绪。

    • 不需要逼自己逃避焦虑,应该从自己能控制的部分开始思考和行动。比起控制焦虑,我们更容易掌控自己的注意力,从而最大化的做出有利的行动。
    • 我们无法控制他人的思想,也必然有很多其他因素无法全局把握,那么就从自己能做的开始努力,以最大的可能完成挑战。即使失败了,这个过程学到的经验依然是成长,成为了下次努力获得成功的铺垫
    • 真实的价值,是无法被抹去的。例如,成长无法被抹去,它是一种真实的价值。而赚到很多钱是一个结果,总有花完的一天,可以轻松被抹去,因此这不是真实的价值。

一些摘录

  • 我们花费大量的时间担忧未来、计划筹谋和做白日梦,同时花大量时间沉湎于过去。
  • 生而为人的一切皆可剥夺,唯独一种终极自由无法被夺取。那就是:
    • 在任何境况下,人都可以自主决定以何种态度去面对,并且选择属于自己的独特
  • 如果你将生活聚焦于实现目标,无论达成多少,你也是永不餍足。而如果将焦点放在价值上,情形就会很不同,因为:
    • 无论身处什么样的环境,价值总是触手可及。
  • 如果目标特别有挑战,让你感到轻松和自信几乎不可能,你会很自然地出现焦虑和自我怀疑的情绪。因此,在心理预演中:
    • 要聚焦于自己能直接控制的部分:你的行为。
    • 去想象你正在以发挥最大潜能的状态行动着,想象你正在说着和做着那些最有效的事。
    • 同时,也想象你正在给当时出现的各种想法和感觉创造空间,并继续采取有效行动

《逆商》

所有人都会遭遇逆境,成功与否在于一个人是否具有选择迎难而上并持续成长的能力。对应则需要高的逆商,逆商这本书说的不仅是如何攀越逆境,还告诉了我们如何生活的更好。

构成逆商的四个维度

逆商是由CORE四个维度构成的。CORE是Control(掌控感)、Ownership(担当力)、Reach(影响度)、Endurance(持续性)

LEAD工具

提升逆商的LEAD工具:

  • L= Listen,倾听自己的逆境反应?
    • 这是高逆商反应还是低逆商反应?
    • 在哪个维度得分最高或最低?
  • E= Explore,探究自己对结果的担当:
    • 我应该对结果的哪些部分担起责任?
    • 我不应该对哪些部分担责?
  • A= Analyze,分析证据:
    • 有什么证据可以表明我无法掌控?
    • 有什么证据可以表明此次困境一定会蔓延到生活的其他方面?
    • 有什么证据可以表明此次困境必然会持续过长时间。
  • D= Do,做点事情:
    • 我还需要什么信息?
    • 我可以做什么来获得对形势的一点点掌控感?
    • 我可以做什么来限制困境的影响范围?
    • 我可以做什么来限制当前困境的持续时间?

《终身成长》

本书向人们阐述了人类信念的力量。我们不一定能意识到这些信念的存在,但它们对我们想要什么以及能否成功达到目标至关重要。

用成长型思维模式看待问题

固定型思维模式会让你更关心别人如何看待你,而成长型思维模式让你更关心能否提高自己。我们要尽可能让自己多以成长型思维模式处理问题 。

  • 杰出的个人有着”一种能够准确评估自己能力和不足的独特才能“(而不是被别人和被特定的事件下定义)

  • 失败只是一个痛苦的经历,它不能对你下定义,它只是一个需要你面对和解决并能从中学习的问题。(并不代表之前的努力都是没用的,你学到的东西,你掌握的能力并不会因为一次失败,而凭空消失)

  • 技能和成就是通过投入和努力得来的(不要盲目膜拜天才、依赖天赋),成功源于尽自己最大的努力做事,来源于学习和自我提高,这也正是我们在不同冠军身上看到的。

  • 如何表扬和批评孩子

    • 表扬孩子时,不应该表扬他们的个性特质,而应该表扬他们的努力和成就
    • 建设性的批评孩子,帮助孩子理解如何去弥补自己的错误,而不是给孩子贴标签或者轻率的原谅

让自己拥有成长型思维模式

不要过分担心自己是不是聪明,不要过分在意失败,从现在开始好好学习和睡觉,好好生活。要成为一个拥有成长型思维模式的人,积极的讨论自己和其他人的努力、策略、挫折和学习情况,可以每天问自己:

- 今天学到了什么?
- 有没有犯错,从错误中学到了什么?
- 今天努力尝试做了什么?

《跃迁》

选择决定命运,认知决定选择,只有梯子搭对了,努力爬才有意义。本书讲述成为高手的方式,实现跨越式的成长。

认知方式

  • 调用知识而非记忆知识。
  • 未来世界的认知能力,是找到信息的搜索能力、运用信息的思考能力,以及从大量信息里抓取趋势的洞察能力

功利学习法

  • 学的更好,却学的更少
  • 最有效的知识:目标导向(能解决当下问题的)、有及时反馈(学了有地方用的)、最近发展区(难度适中的)

幂律分布

  • 发现世界的杠杆点,发现身边的高价值区
  • 对内,通过二八法则,持续放大系我校能;对外,移动到系统的头部,获得系统巨大推动力

头部效应

  • 站位比努力更重要
  • 找到自己的头部区:从价值,而非优势出发。不要因为容易去做一件事,而要因为有价值才做

联机学习

  • 成为知识的路由器,用答案去换答案
  • 先打磨第一个知识模块,再抛出去换回别人的知识模块,重复前两步积累到足够多的知识模块,最后整合出自己的体系,实现知识的跃迁
  • 举例:遇到问题,找人:“谁最有可能知道这个答案?在这之前自己要准备什么?”,带着找到的资料和准备好内容,找对方提出高质量的问题

知识IPO

  • 以提出问题为驱动、以解决问题为整合、用输出倒逼输入产品化
  • I:输入问题,以持续解决问题为目标
    • 真实、高价值、并且有可能被解决的问题
  • P:解决问题,以整合多学科知识为手段
    • 不是学习知识,而是解决问题(不要妄图看完所有相关资料,整个过程以解决问题为最高标准,持续问自己,这个只是对与解决问题有用吗?)
  • O:输出产品,通过咨询研发、授课整合和写作,让思想产品化
    • 把解决问题的结果传播出去,这个过程能够让多人知道你有解决这类问题的能力,能帮你找到下一轮更大的问题以及更大的价值,形成迭代,知识跃迁

破局思维

  • 很多问题的答案不在系统内,关注元素之间的关系,从时间维度理解回路、升维思考从而跳出层级,才能破局解决复杂问题
    • 回路:时间维度查看事物发展的脉络
    • 层级:空间维度理解真正的规律。上层决定下层,下层无解跃迁一层找到答案
  • 高手,思考深、见识广,能看到更大的系统

内在修炼

  • 真正的改变都是逆人性的
  • 现代高手的心智:
    • 面对世界,开放而专注,进入系统
    • 面对自己,迟钝而有趣,智慧而超然
    • 面对他人,善良而可激怒

人脉和弱关系

  • 推算每个人的一二三度人脉量:150个、2900个、20W个
  • 二三度人脉基本一个月不会见一次面,属于弱关系
  • 弱关系放大效应强关系更高:
    • 数量大
    • 价值清晰(三度人脉会完全模糊当事人的身份背景和利益关系,使得事情的价值更纯粹清晰)
    • 跨界,于认识多少人<认识多少种人<认识多少种牛人

《苏世明的25条工作和生活原则》

苏世民:“华尔街的新国王”“私募界的巴菲特”“美国房东”,他创立的黑石集团是全球私募股权资产管理公司和房地产管理公司的巨头。他的25条工作和生活原则,包含了其为人处世、公司管理、建立特色组织的经验与做法。以下摘录对于当前最受益的点:

  • 1.做大事和做小事的难易程度是一样的。所以要选择一个值得追求的宏伟目标,让回报与你的努力相匹配。
  • 4.人们总觉得最有意思的话题就是与自己相关的话题。所以,要善于分析他人的问题所在,并尝试提出办法来帮助他人。几乎所有的人都愿意接受新的想法(前提是想法经过深思熟虑)
  • 6.信息是最重要的商业资产。要始终对进入企业的新鲜事物保持开放的态度(新的人、经验,知识)
  • 9.再聪明的人也不能解决所有问题。聪明人组成的开诚布公的团队却可以无往而不利。
  • 10.处于困境中的人往往只关注自己的问题,而解决问题的途径通常在于你如何解决别人的问题。
  • 14.没有什么是一成不变的。不经常寻求自我重塑和自我改进的方法,就会被竞争对手打败。尤其是组织,因为组织比想象中更脆弱。(所有的事情一旦不再关注和改变,必然趋向熵增,出现问题)
  • 15.不要因为畏惧而不去争取自己想得到的东西。极少有人能在首次推介中完成销售,你需要能够一次又一次坚定地推销你的愿景。大多数人不喜欢改变,所以你需要说服他们为什么要接受改变。
  • 20.要在准备好时做出决定,而不是在压力之下。其他人催促你做出决策时,你可以说:“我需要更多的时间来考虑这个问题。我想清楚了再回复你。”即使是在最艰难、最令人不快的情况下,这种策略也非常有效。
  • 21.忧虑是一种积极的心理活动,可以开阔人的思路。如果能正确引导这一情绪,你就可以洞察任何形势下的负面风险,并采取行动规避这些风险。
  • 22.失败是一个组织最好的老师。开诚布公地客观谈论失败,分析问题所在,你就会从失败中学到关于决策和组织行为的新规则。如果评估得当,失败就有可能改变一个组织的进程,使其在未来更加成功。
  • 23.尽可能雇用10分人才,因为他们会积极主动地感知问题、设计解决方案,并朝着新方向开展业务。他们还会吸引和雇用其他10分人才。10分人才做什么事都会得心应手(也要让自己成为10分人才)
    1. 如果你认为一个人的本质是好的,就要随时为这个人提供帮助,即使其他人都离他而去。任何人都可能陷入困境。在别人需要的时候,一个偶然的善意行为就会改变他的生命轨迹,造就意想不到的友谊或忠诚。

《他人的力量》

人来需要连接关系,而且人类的系统一直都在搜索连接关系

人际关系的四个层次

人际关系有4个层次,而大部分人经常在前3个层析循环,第四层-真正的连接关系,才是健康的、能够调动你的心脏、思想、灵魂和热情的人际关系。

1、孤立状态,没有连接关系

  • 要么是给予受阻,要么是接受无能

2、坏的连接关系

  • 处于寻求认同模式
  • 一般而言,当有人需要认可的时候,也就没什么值得认可的地方

3、蛊惑人心的虚假“良好连接”

  • 沉溺于“自我治疗方式“,例如下属的彩虹屁、购物、游戏等成瘾用于”逃离现实缓解压力“

  • 很多事情值得去享受,但不能满足对”搜索连接“的需求

4、真正的连接关系

  • 能让你成为完整、真实的自我,双方知根知底互相理解、互相被需要(注意不是单方面输入也不是单方面输出)

《能力陷阱》

如何成为一名优秀的领导?那就是,学会”先行动后思考“。要实现往领导的转变,就应该通过动手去做,而不仅仅安于现状或者停留于思考。本书对于每一个想升职的人来说本书值得在不同阶段重复研读。对于还在基础岗位奋斗的我,以下仅列出目前助于我扩展思维方式的点:

如何改变——先行动后思考

在改变的过程中,我们会先看到结果,再根据结果进行思考,把外在经历内在化。

  • 我们要在行为上表现的想一个领导者,才会像领导者一样去思考。
  • 做创新型项目也是类似,我们需要先迈出一小步,再根据反馈进行下一步创新,不断迭代,实现改变

领导者真正需要做的事情

  • 一个领导者要跳出日常工作的限制,把时间花费在像他人解释改变的重要性上(即使改变的原因已经很明显了)
    • 管理需要我们高效高能的完成每日的目标和组织结构,而领导则是不停改变我们要做的事,以及思考我们该如何去做。
  • 想法+过程+你本人=领导公司成功转变:
    • 领导者想法的好坏并不是是否能得到他人支持的唯一因素,过程才是更为重要的因素,如何展现他们的想法,以及在这个过程中他们如何与听众进行交流才真正决定了人们是否愿意和他一起做事。

人际关系

积极的建立良好的(新的)人际关系网络,像领导一样行事并不只和你做的事有关,还与你所结交的人有关。我们需要一个多元的、广泛的、动态的以及跨域的人际关系网络。带有目的的人际关系更能

  • 使用如上的人际关系可以达成:
    • 感知发展趋势并寻找机会
    • 与各领域的领袖和人才建立联系
    • 跨域合作以创造更多价值
    • 避免群体思维
    • 提出突破性想法
    • 获得工作机会
  • 人际关系网络的优势:广泛性+连接性+动态性
    • 广泛性:与各行各业的人建立联系
    • 连接性:作为桥梁连接一些在其他方面没有关联的人和团队的能力
    • 动态性:随着你的进步,人际关系网络也在相应发展

反思一下自己的人际关系,缺少与公司外部的沟通,例如可以从与前同事的联系、同行/不同行的同学的联系开始?此外,帮助朋友建立更多的人际关系网络也有助于扩展自己的网络。

“真实性”陷阱

不要被”真实性”束缚,要愿意改变和模仿。

  • 装作是这样的人,最终就能成为这样的人
  • 没有什么是原创的,善于模仿优秀的人的思维和做事方式,最终成为自己的能力(取其精华)
  • 找到权威和亲和之间的平衡点
    • 领导者需要一些神秘感,你的下属希望你能融入他们,但他们也不希望他们的领导只是他们当中无区别的一员。因此有时很亲切,有时也需要有CEO的样子

《干法》

要想拥有一个充实的人生,一般只有两个选择:从事自己喜欢的工作,或者,让自己喜欢上工作。
日本的经营之圣,稻盛和夫指出,热爱是点燃工作激情的火把。无论什么工作,只要全力以赴去做就能产生很大的成就感和自信心,而且会产生向下一个目标挑战的积极性。成功的人往往都是那些沉醉于所做之事的人。

这本书写了稻盛和夫的工作理念和故事,与苏世明相比于我是两种感觉,稻盛和夫是一种仿佛只要努力踏实干事就能成功,或者吸引他人吸引机会从而实现目标。而苏世明是一种更加魔幻、更具有向上思考的想法和行动的干事方式。参考稻盛和夫的干法,会让让自己愿意坚持努力,充满精力和斗志,而结合苏世明的经验可以进一步获取不同维度的成功法则和思维方式。总之,两位都是改变自己国家/影响世界的圣人。具体如何,就看自己结合实践进行努力了。

比率p的假设检验

发表于 2020-01-04 | 分类于 统计 , 假设检验 | | 阅读次数:

总体比率的假设检验实际上是业界最常用也是最需要的检验,例如在ABtest中,检验两个实验的转化率是否有显著差异,则需要用到比率检验。

单总体比率的假设检验

前提条件:

  • 样本取自两点分布 $X \sim B(1,p)$
  • 样本量$n$很大,能够满足 $np>5$ 且 $n(1-p)>5$

记要检验的原假设为为 $H_0: p=p_0$,则样本比率 $\widetilde{\mathrm{p}}$服从方差为$p(1-p)/n$的正态分布,对应标准化的检验统计量近似服从$N(0,1)$ :

$$
\mathrm{u}=\frac{\sqrt{\mathrm{n}}\left(\widetilde{\mathrm{p}}-\mathrm{p}_{0}\right)}{\sqrt{\mathrm{p}_0\left(1-\mathrm{p}_0\right)}}
$$

实际上,当样本量很少时,需要采用精确的比率检验,即直接使用二项分布来检验,具体实现见下文的R代码。

两个总体比率的假设检验

检验前提条件:

  • 两总体互相独立
  • 变量都取自两点分布,即两总体服从二项分布
  • 两总体且每类的样本量满足大于5的要求,从而能用正态分布来近似

那么可知:

$$
\frac{\left(\widetilde{\mathrm{p}}_1-\widetilde{\mathrm{p}}_2\right)-\left(\mathrm{p}_1-\mathrm{p}_2\right)}{\frac{\mathrm{p}_1\left(1-\mathrm{p}_1\right)}{\mathrm{n}_1}+\frac{\mathrm{p}_2\left(1-\mathrm{p}_2\right)}{\mathrm{n}_2}} \approx \frac{\left(\widetilde{\mathrm{p}}_1-\widetilde{\mathrm{p}}_2\right)-\left(\mathrm{p}_1-\mathrm{p}_2\right)}{\frac{\mathbb{p}_1\left(1-\widetilde{\mathrm{p}}_1\right)}{\mathrm{n}_1}+\frac{\widetilde{\mathrm{p}}_2\left(1-\widetilde{\mathrm{p}}_2\right)}{\mathrm{n}_2}} \sim \mathrm{N}(0,1)
$$

因此对应的标准化检验统计量如下:

  • 1)当原假设为$H_0:p_1-p_2=0$时,最佳估计量为联合两组样本的比率$\hat{\mathrm{p}}=\frac{\mathrm{x}_1+\mathrm{x}_2}{\mathrm{n}_1+\mathrm{n}_2}=\frac{\mathrm{p}_1 \mathrm{n}_1+\mathrm{p}_2 \mathrm{n}_2}{\mathrm{n}_1+\mathrm{n}_2}$,于是检验统计量如下:

$$
z=\frac{\left(\widetilde{p}_1-\widetilde{p}_2\right)-0}{\frac{\hat{p}(1-\hat{p})}{n_1}+\frac{\hat{p}(1-\hat{p})}{n_2}}=\frac{\left(\tilde{p}_1-\tilde{p}_2\right)}{\hat{p}(1-\hat{p})\left(\frac{1}{n_1}+\frac{1}{n_2}\right)}
$$

  • 2)当原假设为$H_0:p_1-p_2=d_0(d_0 \neq 0)$时,直接用两个样本的比率$\widetilde{p}_1$和$\widetilde{p}_2$相应估计两个总体的的比率$p_1$和$p_2$,于是检验统计量入下:
    $$
    z=\frac{\left(\bar{p}_1-\bar{p}_2\right)-d_0}{\frac{\bar{p}_1\left(1-\bar{p}_1\right)}{n_1}+\frac{\bar{p}_2\left(1-\widetilde{p}_2\right)}{n_2}}
    $$

R语言实现

除了直接用代码写上面的公式,实际上可以直接使用现成的检验函数:chisq.test 和 prop.test,这两个函数默认都会加Yates 校正(修正小样本的影响,具体见文末),但要得到和上文公式一样的结果,则注意限制correct = F 。

  • 检验单总体比率是否等于特定的值:
    • prop.test,大样本才适用的近似检验
      • 不加Yates 校正和上文公式结果的平方一致(小样本一般都需要加Yates 校正)
    • binom.test,精确的比率检验,即直接使用二项分布检验是否是该比率
  • 检验两总体比率是否相等:
    • chisq.test,大样本才适应的近似检验
      • 不加Yates 校正和上文公式结果的平方一致(小样本一般都需要加Yates 校正)
      • 正态分布平方,服从自由度为1的卡方分布
      • 注意卡方检验的传参:是两个样本分别取0和1的数量(而不是取1的量和整体量n)
    • Fisher 精确检验,适用于小样本量(此处暂无示例)

单总体比率检验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
x=60
n=2000
p_real=x/n
p0=0.02
u=sqrt(n)*(p_real-p0)/sqrt(p0*(1-p0))
u^2
# 不加校正,得到和u^2一致的结果
prop.test(x, n,p0, correct = F)

# > u^2
# [1] 10.20408
# > prop.test(x, n,p0, correct = F)$statistic
# X-squared
# 10.20408

实际上单比率检验,推荐直接使用精确的二项分布的假设检验即可。可以看到p值远小于0.05,因此拒绝比率是 $p_0$ 的原假设,认为改总体比率不为$p_0$ 。

1
2
3
4
5
6
7
8
9
10
11
12
# > binom.test(x, n, p0)
# Exact binomial test
#
# data: x and n
# number of successes = 60, number of trials =
# 2000, p-value = 0.002974
# alternative hypothesis: true probability of success is not equal to 0.02
# 95 percent confidence interval:
# 0.02296955 0.03844886
# sample estimates:
# probability of success
# 0.03

两总体比率相等的假设检验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 检验的样本如下:
a=19;b=22;c=52;d=39
pA=a/(a+c);pB=b/(b+d)
nA=a+c;nB=b+d

# 借助chisq.test检验两总体比率是否一致
a1 <- rbind(c(nA*(1-pA),nA*pA), c(nB*(1-pB), nB*pB))
# 此处为了保持一致未加Yates 校正,但实际应用最好设置 correct=T
chisq.test(a1,correct=F)

# 直接计算统计量,和上面卡方检验的X-squared一致,都是 1.326693
p_fit=(a+b)/(a+b+c+d)
z2=(pA-pB)/sqrt((1/nA+1/nB)*p_fit*(1-p_fit))
z2^2
1
2
3
4
5
6
7
8
9
# > chisq.test(a1,correct=F)
#
# Pearson's Chi-squared test
#
# data: a1
# X-squared = 1.3267, df = 1, p-value = 0.2494
#
# > z2^2
# [1] 1.326693

可以看到p值大于0.05,认为无法拒绝原假设,即$p_1=p_2$

注意只是在这次样本里找不到拒绝的理由,不代表这两个比率就一定相等了

Yates 校正

小样本情况时Yates 校正后的卡方统计量可信度更高(大样本时对其影响很微弱),因此我们最常用加了Yates 校正后的比率检验,对应Yates 校正实际上是对卡方分布的统计量的修正,取平方之前将正偏差(观测-期望)减0.5,负偏差时加0.5,对应公式如下:

$$
\mathrm{X}^2((n_1-1)*(n_2-1)) = \sum_{i=1}^{n_1}\sum_{j=1}^{n_2} \frac{\left(\left|O_{ij}-E_{ij}\right|-0.5\right)^2}{\mathrm{E}_{\mathrm{ij}}}
$$

变量分箱

发表于 2020-01-04 | 分类于 特征处理 | | 阅读次数:

特征处理实际是整个建模过程中耗时最多,实际也是最重要最有意义的部分,所谓巧妇难为无米之炊。当我们有了米该如何淘呢?那么变量分箱则是实现’模型稳健性’+’业务实用性’的一大杀器。

我最喜欢的分箱方法:对x直接建决策树,参考其分割点,结合业务背景微调成为想要的cutoff。

示例R代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
library(partykit)
get_bins <- function(df,x,y,p=0.05){
print(paste('----- x: ',x,' ---------'))
df=as.data.frame(df)
ctree = ctree(formula(paste(y, "~", x)), data = df, na.action = na.exclude,
control = ctree_control(minbucket = ceiling(round(p * nrow(df)))))
bins = width(ctree)
if (bins < 2) {
return(list('ctree'="No significant splits",
'cutvct'="No significant splits"))
}
cutvct = data.frame(matrix(ncol = 0, nrow = 0))
n = length(ctree)
for (i in 1:n) {
cutvct = rbind(cutvct, ctree[i]$node$split$breaks)
}
cutvct = cutvct[order(cutvct[, 1]), ]
return(list('ctree'=ctree,'cutvct'=cutvct))
}

get_var_cut <- function(cut_name = 'eg',
the_breaks = c(-1,2,3),
dt=dat_model_data,
the_labels=NA){
# 根据传入的指标和分割点,给数据框新增cut后的一列,命名以_cut结尾
if(is.na(the_labels)){
the_labels <- paste0(the_breaks[-length(the_breaks)],'~',the_breaks[-1])
}
# 注意,cut是左开右闭类型( ]
re_cut <- cut(dt[[cut_name]],breaks = the_breaks,labels = the_labels)
dt[[paste0(cut_name,'_cut')]] <- re_cut
return(dt)
}

以上为工作中总结出的代码,屡试不爽,具体原理和和科学分析,且听下回分解…

异常处理-R与Python

发表于 2019-09-08 | 分类于 技术语言 | | 阅读次数:

本文将简单介绍R和Python中常用的异常处理方式。

R

R中常用try和tryCatch

try

如果只是为了某段代码不影响后续脚本的执行,最简单的办法就是直接在外层套上try()

1
2
3
4
# 如果该语句失败也没关系,那么可以直接用偷懒的写法:try
print('-- 开始 --')
try(o <- 'error'+2) # 会输出报错,但是程序不会停止,接下来的代码可以继续运行
print('-- 结束 --')
1
2
3
4
5
6
# [1] "-- 开始 --"
# Warning message:
# In strsplit(code, "\n", fixed = TRUE) :
# input string 1 is invalid in this locale
# Error in "error" + 2 : non-numeric argument to binary operator
# [1] "-- 结束 --"

trycatch

最常见的异常处理函数是tryCatch,能够让代码更加flexible

简单示例:

  • 'error'+2报错
  • 错误记录在e中 并打印
  • 报错后,对应变量 o 被赋值为3
1
2
3
tryCatch({o <- 'error'+2}, error = function(e) print(e), call = (o <- 3))
print(o)
# [1] 3

call的使用

为了防止一段代码出错,保证后续继续运行,并记录具体出错的位置,可以结合 call 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
error_ls <- c()
i <- 0
for(x in c(-2:2)){
i <- i+1
tryCatch(
expr={x1 <- ifelse(x>0, x, "make error") + 1
error_ls[i] <- 'success'
},
error = function(e){
cat('Error: ', e$message, 'index: ',i,'\n')},
call = (
# 如果报错,则对 x1 和 error_ls 重赋值
{x1 <- 100
error_ls[i] <- 'error'}))
cat('x1: ',x1,'\n')
}

x<=0时都会报错,并给x1重赋值为100,x>0时则正常运行

1
2
3
4
5
6
7
8
# Error:  non-numeric argument to binary operator index:  1 
# x1: 100
# Error: non-numeric argument to binary operator index: 2
# x1: 100
# Error: non-numeric argument to binary operator index: 3
# x1: 100
# x1: 2
# x1: 3

error_ls已记录对应每次循环的正误

1
print(error_ls)

注:call函数会优先执行expr的内容,如果执行失败,则执行call内部的语句,但若call内部出现其它赋值语句,则始终会被执行(会认为expr中没有相应返回),例如:

1
2
3
4
5
6
7
8
9
10
error_ls2 <- c()
i <- 0
for(x in c(-2:2)){
i <- i+1
tryCatch(
expr={1+1},
call = (
# 对expr中不包含的变量赋值,每次都会执行
{error_ls2[i] <- 'error'}))
}

即时没有任何异常,依然会执行error_ls2[i] <- 'error'

1
2
print(error_ls2)
# [1] "error" "error" "error" "error" "error"

包装成函数

包装成函数,不使用循环,转为在 data.table中快速运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 写一个会遇到错误的函数
process <- function(x){
# 显然,x<=0时,'make error'+1 字符串和数值不能相加,该函数会报错
ifelse(x>0, x, "make error") + 1
}

# 嵌套异常处理后的函数
process_tryCatch <- function(x){
tryCatch(expr=process(x),
error=function(e) {
# 如果报error,则输出对应报错,并直接返回 -1001
cat('Error: ', e$message, ". Input: ", x, "\n")
# 此处不再需要call,可以直接在error里写return
return(-1001)
}
)
}

构造个简单的data.table, 对a列的每个元素做process运算

1
2
3
4
5
6
library(data.table)
df = data.table(a=-2:2)
df[, new_a := process_tryCatch(a),a]
# Error: non-numeric argument to binary operator . Input: -2
# Error: non-numeric argument to binary operator . Input: -1
# Error: non-numeric argument to binary operator . Input: 0

对于>0的会正常返回,其他返回tryCatch设置的 -1001

1
2
3
4
5
6
7
df
# a new_a
# 1: -2 -1001
# 2: -1 -1001
# 3: 0 -1001
# 4: 1 2
# 5: 2 3

Python

try-except

try-except是python中常用异常处理方式,如果try语句块中发生异常,则执行except语句块(捕获异常并处理)。示例如下

1
2
3
4
5
6
import sys 
try:
re = "error" + 2
except Exception as e:
s = sys.exc_info()
print('Error: %s; line:[%s]' % (str(e), s[2].tb_lineno))
  • sys.exc_info() 记录错误出现的位置(问题回溯)
  • Exception as e 记录具体的报错信息。

以上示例运行结果如下:

1
# Error: cannot concatenate 'str' and 'int' objects; line:[3]

模型常用评估指标详解

发表于 2019-09-01 | 分类于 评估 | | 阅读次数:

简介

模型评估通常作为建模的最后一步,用于评估模型效果,判别该模型是否达到预期。但实际模型评估指标需要在建模的第一步确定,即确定目标函数。凡事都得有个目标,才知道努力的(拟合)方向,否则枉然。
连续值或者分类型的预测最常用的说法就是模型精度,但实际精度一词有多重含义,例如连续性预测模型常用的是MAPE/RMSE,而分类常用AUC/accuracy/recall/specify等等。本文将介绍常用的评估指标,并对应下各指标的不同称呼。

0-1分类预测的评估

一般的分类都是二元分类,而最常用的则是ROC曲线下方的面积AUC值了。首先需要知道混淆矩阵的构成,以及其衍生的一系列评估指标,取其不同阈值下的两个评估指标,即可构成ROC曲线,而曲线下方的面积,即为AUC值。

混淆矩阵

对于而分类问题,记1为正例,0为负例,预测和实际的值则会出现4中组合,例如,对某一样本,预测值为1(Positive),而实际值也为1,说明预测正确,即为 True Positive(真正例),反之如果实际值为0,则预测错误,即为 False Positive(假正例)。根据4中组合,得到混淆矩阵如下:

预测-1 预测-0
实际-1 True Positive(TP) False Negative(FN) Actual Positive (P=TP+FN)
实际-0 False Positive(FP) True Negative(TN) Actual Negative (N=FP+TN)
Predicted Positive (P’=TP+FP) Predicted Negative (N’=FN+TN) TP+FP+FN+TN

衍生的各种评估指标如下:

  • 准确率(accuracy)
    • $\frac{T P+T N}{TP+FP+FN+TN}$
  • 错误率
    • $\frac{FP+FN}{TP+FP+FN+TN}$
  • 敏感度(sensitivity)、真正例率、召回率(recall)
    • $\frac{TP}{TP+FN}$
  • 特效性(specificity)、真负例率
    • $\frac{TN}{FP+TN}$
  • 精度(precision)
    • $\frac{TP}{TP+FP}$
  • $F_1$、F分数(精度和召回率的调和均值)
    • $\frac{2 \times \text {precision} \times \text {recall}}{\text {precision}+r e c a l l} = \mathrm{F} 1=\frac{2 \mathrm{TP}}{2 \mathrm{TP}+\mathrm{FP}+\mathrm{FN}}$
    • 实际来自于: $\frac{2}{\mathrm{F} 1}=\frac{1}{\mathrm{P}}+\frac{1}{\mathrm{R}}$

ROC曲线

分类模型的原始预测值是0~1之间的连续型数值,可理解为因变量取1的概率,一般取0.5作为阈值,即小于0.5记为预测的0,大于等于0.5记为预测的1;而实际上根据不同情况可以取不同的阈值。

例如,银行预测可能发生贷款逾期的账目,由于整体逾期率非常低,例如样本中100个只有1个逾期,如果只看准确率,那么全预测0(即,不逾期)则可达到99%的准确率,显然不妥,因此此时会秉着宁可错杀一千不可放过一个的原则,会将阈值适当降低,例如降到0.1,若大于0.1则预测为1。(实际上此时主要的评估指标会选择recall)。

用不同的阀值,统计出每组不同阀值下的精确率和召回率,

  • 横坐标:假正率(FPR,即 1-specificity,1-真负例率)
    • FPR = FP /(FP + TN)
  • 纵坐标:真正例率(TPR, 即 recall)
    • TPR = TP /(TP + FN)

即可画出ROC曲线(受试者工作特征曲线,receiver operating characteristic curve),示例(图片来源于他处)如下

ROC曲线优势就是,当正负样本的分布发生变化时,其形状能够基本保持不变,因此其面积AUC值,可以说是极度适用于不平衡样本(例如以上的银行逾期数据,正负比极度不均衡)的建模评估了。AUC越大,模型分类效果越好,一般来说,AUC低于0.7的模型基本上是个废柴了。(若在正负样本比1:1情况下全预测为0或者1,AUC即为0.5)

R语言实现

可使用ROCR包计算AUC值,混淆矩阵自己写即可,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
library(ROCR)
get_confusion_stat <- function(pred,y_real,threshold=0.5){
# auc
tmp <- prediction(as.vector(pred),y_real)
auc <- unlist(slot(performance(tmp,'auc'),'y.values'))
# statistic
pred_new <- as.integer(pred>threshold)
tab <- table(pred_new,y_real)
if(nrow(tab)==1){
print('preds all zero !')
return(0)
}
TP <- tab[2,2]
TN <- tab[1,1]
FP <- tab[2,1]
FN <- tab[1,2]
accuracy <- round((TP+TN)/(TP+FN+FP+TN),4)
recall_sensitivity <- round(TP/(TP+FN),4)
precision <- round(TP/(TP+FP),4)
specificity <- round(TN/(TN+FP),4)
# 添加,预测的负例占比(业务解释:去除多少的样本,达到多少的recall)
neg_rate <- round((TN+FN)/(TP+TN+FP+FN),4)
re <- list('AUC' = auc,
'Confusion_Matrix'=tab,
'Statistics'=data.frame(value=c('accuracy'=accuracy,
'recall_sensitivity'=recall_sensitivity,
'precision'=precision,
'specificity'=specificity,
'neg_rate'=neg_rate)))
return(re)
}

引用上篇lasso-R示例博文的评估结果示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# $AUC
# [1] 0.8406198

# $Confusion_Matrix
# y_real
# pred_new 0 1
# 0 20 8
# 1 32 131

# $Statistics
# value
# accuracy 0.7906
# recall_sensitivity 0.9424
# precision 0.8037
# specificity 0.3846
# neg_rate 0.1466

连续值预测的评估

连续型变量的预测,通常使用MAPE和RMSE。对于回归问题的模型还经常使用拟合优度 $R^2$ 作为评估指标。

MAPE

MAPE(mean absolute percentage error)为平均百分比误差,预测连续型数据的准确率一般指1-MAPE,例如预测未来10个月的GDP数据,准确率达到98%,即代表该模型的预测 MAPE 为2%。
$$MAPE=\sum_{t=1}^{n}\left|\frac{\text {observed}_{t}-\text {predicted}_t}{\text {observed}_t}\right| \times \frac{100}{n}$$

MSE/RMSE

RMSE(root mean square error)为均方根误差,相应的MSE(mean square error)即为误差的平方和,两者含义一致,指标越小则模型效果越好。
$$RMSE=\sqrt{\frac{1}{N} \sum_{t=1}^{N}\left(\text {observed}_t-\text {predicted}_t\right)^{2}}$$

拟合优度

拟合优度(Goodness of Fit)是指回归直线对观测值的拟合程度。

R²/可决系数

度量拟合优度的统计量是可决系数(亦称确定系数)R²。R²的值越接近1,说明回归直线对观测值的拟合程度越好;反之,R²的值越小,说明回归直线对观测值的拟合程度越差。R²等于回归平方和( explained sum of squares)在总平方和( total sum of squares)中所占的比率,即回归方程所能解释的因变量变异性的百分比。

注意,回归问题才能用R²衡量

$$ R^{2} = \frac{S S_{\mathrm{reg}}}{S S_{\mathrm{tss}}}=1-\frac{S S_{\mathrm{rss}}}{S S_{\mathrm{tss}}} = 1- \frac{\sum_{i}\left(y_{i}-f_{i}\right)^{2}}{\sum_{i}\left(y_{i}-\bar{y}\right)^{2}} $$

由以上公式实际上可知,在带有截距项的线性最小二乘多元回归中(注意这个前提条件),$R^2$就是预测值和实际值相关系数的平方,即(注意,一定是线性回归模型才行):

$$ R^{2}=cor(y_{real},y_{fit})^{2} $$

调整的R²

在模型中增加多个变量(即使是无实际意义的变量)也能小幅度提高R平方的值,因此需要考虑模型的变量数作为相应惩罚,于是得到调整的R²如下:

$$ R_{adjusted}^{2} =1-\frac{S S_{\mathrm{rss}}/(n-p-1))}{S S_{\mathrm{tss}}/(n-1)}$$

R语言实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# MAPE
get_mape <- function(fit,y){
# 实际值为0的,直接不纳入计算
re <- round(mean(ifelse(y==0,NA,abs(y-fit)/y),na.rm=T)*100,1)
return(re)
}

# R方,可决系数,coefficient of determination
get_rsq <- function(preds,actual,p=1){
rss <- sum((actual - preds) ^ 2)
tss <- sum((actual - mean(actual)) ^ 2)
rsq <- round(1 - rss/tss,3)
n <- length(preds)
# 调整的R方 1-(1-rsq)*(n-1)/(n-1-1) 也一样
rsq_ad <- round(1 - (rss/(n-p-1))/(tss/(n-1)),3)
return(list('rsq'=rsq,
'rsq_ad'=rsq_ad))
}

lasso-R示例

发表于 2019-06-14 | 分类于 模型 , 回归 | | 阅读次数:

Lasso简介

LASSO(Least Absolute Shrinkage and Selection Operator)是线性回归的一种缩减方式,通过引入$L_1$惩罚项,实现变量选择和参数估计。

$$\sum_{i=1}^{N}\left(y_{i}-\beta_{0}+\sum_{j=1}^{p} x_{i j} \beta_{j}\right)^{2}+\lambda \sum_{j=1}^{p}\left|\beta_{j}\right|$$

R示例

简单的建模过程主要包括:

  • 数据切分、清洗
  • 建模,使用R的glmnet包即可实现lasso
  • 评估,分类常使用混淆矩阵、ROC(使用ROCR包),数值型预测常使用MAPE

以下用简单的数据集实现Lasso-LR:
这是由真实的医学数据抽样得到的一份demo数据,x1-x19分别代表不同的基因或者染色体表现数据,Y代表病人是否患有某种疾病。(由于数据敏感,因此都用$X_i$代替。)

1
2
3
rm(list=ls())
library(data.table)
dat_use <- fread('demo_data.csv',header = T)

1
2
3
4
5
6
7
8
head(dat_use)
# y Genotype Age Gender X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19
# 1: 1 0 19 0 2.09 1.85 1.93 5.54 5.08 6.20 8.82 8.19 8.09 4.77 4.53 4.48 3.50 3.23 3.22 0 0 0.46 0.64
# 2: 0 2 20 0 2.54 2.07 1.61 6.19 4.21 0.00 7.53 2.25 1.97 4.41 3.98 3.46 2.49 1.61 1.83 0 1 1.98 5.27
# 3: 1 1 32 1 2.17 2.22 1.95 6.67 5.39 0.00 7.89 5.89 3.00 4.49 2.88 0.03 2.87 2.45 1.23 0 1 1.28 2.00
# 4: 0 1 20 0 2.01 1.68 2.15 6.20 0.00 4.05 7.44 4.60 2.10 4.11 4.28 4.19 2.91 2.08 -0.96 1 0 6.20 2.83
# 5: 1 1 41 1 2.15 1.67 1.56 7.10 0.00 0.00 8.55 3.68 3.40 4.48 3.12 3.26 2.92 1.29 1.36 1 1 7.10 4.87
# 6: 1 1 47 1 1.93 1.62 2.63 6.43 5.21 5.76 8.13 7.95 6.96 4.36 4.47 4.27 2.96 2.52 2.38 0 0 1.22 0.17

数据预处理

首先将类别型变量转为哑变量,借助caret的dummyVars函数可以轻松获取哑变量

1
2
3
4
5
6
7
8
9
10
11
12
library(caret)
# Genotype 为数据中多类型的类别型变量,其他例如Age和Gender为二值0-1变量,无需再转换
# 首先将类别型变量转为facor类型
names_factor <- c('Genotype')
dat_use[, (names_factor) := lapply(.SD, as.factor), .SDcols = names_factor]
# 抽取出类别型变量对应的哑变量矩阵
dummyModel <- dummyVars(~ Genotype,
data = dat_use,
sep = ".")
dat_dummy <- predict(dummyModel, dat_use)
# 合并哑变量矩阵,并删除对应原始数据列
dat_model <- cbind(dat_use[,-c(names_factor),with=F],dat_dummy)

1
2
3
4
5
6
7
8
head(dat_dummy)
# Genotype.0 Genotype.1 Genotype.2
# 1 1 0 0
# 2 0 0 1
# 3 0 1 0
# 4 0 1 0
# 5 0 1 0
# 6 0 1 0

切分数据集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 设置随机数种子,便于结果重现
set.seed(1)
# split data
N <- nrow(dat_model)
test_index <- sample(N,0.3*N)
train_index <- c(1:N)[-test_index]
test_data <- dat_model[test_index,]
train_data <- dat_model[train_index,]
# 指定因变量列,
y_name <- 'y'
thedat <- na.omit(train_data)
y <- c(thedat[,y_name,with=F])[[1]]
x <- thedat[,c(y_name):=NULL]
x <- as.matrix(x)
# normalize(此步骤可省略,因为glmnet默认会标准化后建模,再返回变换后的真实系数)
# pp = preProcess(x,method = c("center", "scale"))
# x <- predict(pp, x)

建模

1
2
3
4
5
6
7
8
library(glmnet)
# fit the model
fit <- glmnet(x, y, alpha=1,family = 'binomial')
# 使用area under the ROC curve, CV 选择压缩参数lambda
# 再设置一次set.seed
set.seed(1)
fit_cv <- cv.glmnet(x, y, alpha=1, family = 'binomial', type.measure='auc')
plot(fit_cv)

可以看到压缩到5个变量时AUC最大,对应 $log(lambda)=-3.67$,抽取出对应5个变量的模型系数如下

1
2
3
4
5
6
7
8
9
10
11
12
# log(fit_cv$lambda.min)=-3.67
get_coe <- function(the_fit,the_lamb){
Coefficients <- coef(the_fit, s = the_lamb)
Active.Index <- which(Coefficients != 0)
Active.Coefficients <- Coefficients[Active.Index]
re <- data.frame(rownames(Coefficients)[Active.Index],Active.Coefficients)
re <- data.table('var_names'=rownames(Coefficients)[Active.Index],
'coef'=Active.Coefficients)
# 计算系数的指数次方,表示x每变动一个单位对y的影响倍数
re$expcoef <- exp(re$coef)
return(re[order(expcoef)])
}

1
2
3
4
5
6
7
8
get_coe(fit_cv,fit_cv$lambda.min)
# var_names coef expcoef
# 1: (Intercept) -1.12352837 0.3251306
# 2: X16 -0.20444072 0.8151031
# 3: Genotype.0 -0.15247051 0.8585842
# 4: Age 0.02762516 1.0280103
# 5: X14 0.15049722 1.1624121
# 6: X15 0.77624720 2.1733010

但医学上经常需要看每个变量在不同惩罚参数lambda下的压缩程度,从而结合实际背景进一步判别有效的影响因素,因此可借助以下图像进行分析,可以看到x15对y的影响最大(此去省略1000字)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
get_plot<- function(the_fit,the_fit_cv,the_lamb,toplot = seq(1,50,2)){
Coefficients <- coef(the_fit, s = the_lamb)
Active.Index <- which(Coefficients != 0)
coeall <- coef(the_fit, s = the_fit_cv$lambda[toplot])
coe <- coeall[Active.Index[-1],]
ylims=c(-max(abs(coe)),max(abs(coe)))
sp <- spline(log(the_fit_cv$lambda[toplot]),coe[1,],n=100)
plot(sp,type='l',col =1,lty=1,
ylim = ylims,ylab = 'Coefficient', xlab = 'log(lambda)')
abline(h=0)
for(i in c(2:nrow(coe))){
lines(spline(log(the_fit_cv$lambda[toplot]),coe[i,],n=1000),
col =i,lty=i)
}
legend("bottomright",legend=rownames(coe),col=c(1:nrow(coe)),
lty=c(1:nrow(coe)),
cex=0.5)
}
# 传入最优lambda-1,从而保留更多变量
get_plot(fit,fit_cv,exp(log(fit_cv$lambda.min)-1))

评估

对于分类模型,最常用的评估指标莫过于AUC值了,使用ROCR包可获取AUC值,为了多维度的评估,将混淆矩阵,和各种recall/accuracy指标加入其中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
library(ROCR)
get_confusion_stat <- function(pred,y_real,threshold=0.5){
# auc
tmp <- prediction(as.vector(pred),y_real)
auc <- unlist(slot(performance(tmp,'auc'),'y.values'))
# statistic
pred_new <- as.integer(pred>threshold)
tab <- table(pred_new,y_real)
if(nrow(tab)==1){
print('preds all zero !')
return(0)
}
TP <- tab[2,2]
TN <- tab[1,1]
FP <- tab[2,1]
FN <- tab[1,2]
accuracy <- round((TP+TN)/(TP+FN+FP+TN),4)
recall_sensitivity <- round(TP/(TP+FN),4)
precision <- round(TP/(TP+FP),4)
specificity <- round(TN/(TN+FP),4)
# 添加,预测的负例占比(业务解释:去除多少的样本,达到多少的recall)
neg_rate <- round((TN+FN)/(TP+TN+FP+FN),4)
re <- list('AUC' = auc,
'Confusion_Matrix'=tab,
'Statistics'=data.frame(value=c('accuracy'=accuracy,
'recall_sensitivity'=recall_sensitivity,
'precision'=precision,
'specificity'=specificity,
'neg_rate'=neg_rate)))
return(re)
}
# 结合数据预处理部分,得到模型在测试集上的表现
get_eval <- function(data,theta=0.5,the_fit=fit,the_lamb=fit_cv$lambda.min){
thedat_test <- na.omit(data)
y <- c(thedat_test[,y_name,with=F])[[1]]
x <- thedat_test[,-c(y_name),with=F]
x <- as.matrix(x)
pred <- predict(the_fit,newx=x,s=the_lamb,type = 'response')
print(get_confusion_stat(pred,y, theta))
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# get_eval(train_data) 篇幅原因不再展示训练集的拟合效果
get_eval(test_data)
# $AUC
# [1] 0.8406198

# $Confusion_Matrix
# y_real
# pred_new 0 1
# 0 20 8
# 1 32 131

# $Statistics
# value
# accuracy 0.7906
# recall_sensitivity 0.9424
# precision 0.8037
# specificity 0.3846
# neg_rate 0.1466

可以看到模型在测试集上的表选比较不错,AUC值达到0.84,不过在specificity上表现欠佳,说明在负样本上的召回较差(52个负样本,只预测对了20个)。

data.table小技巧

发表于 2019-05-23 | 分类于 技术语言 , 小技巧系列 | | 阅读次数:

data.table 作为R中数据处理的利器,本文记录常用的代码(持续更新)。

  • 操控数据
    • dt[i,j,k]
    • i:where 选择
    • j:条件,更新,计算;直接对j处的向量进行(自定义)函数操作得到新列
    • k:group by,聚合
  • 基础操作

    1
    2
    3
    4
    5
    6
    7
    # 根据(x,y)排序
    dt[i,j,k][order(x,y)]
    # 按照s_id和date_time排序的数据
    d1 <- dt[,.(t_id,date_time,s_id)][order(s_id,date_time)]
    # 取列和取行方式和data.frame基本通用,但如果直接传入 index向量/列名向量,需要加 with=FALSE
    idx = 1:5
    data[,idx,with=FALSE]
  • 加列

    1
    2
    3
    4
    # 多加一列,get_day是自定义对单个字符串操作的函数,所以还需要group by 这个向量,就可以对每个字符串操作了
    dat[,date_day3:=get_day(as.character(date_time)),date_time]
    # 生成新的汇总数据
    dat0108[m_type==0,.(t1=.N),mon]
  • 加行

    1
    re <- rbind(re,cbind('mon'='all',t(colSums(re[,2:4]))))
  • 设置列名:

    1
    setnames(DT, c("b","b") )
  • 一次加多列,赋值:

    1
    2
    dat[,`:=`(r_buy_call=a/b,
    x=1)]
  • 一次删除多列,根据列名:

    1
    2
    x <- x[,colnames(x)[grep('^y_',colnames(x))]:=NULL]
    re5 <- re5[,paste0('ft_cnt_',c(1:5)):=NULL]
  • 去重

    1
    2
    3
    4
    # 直接unique会默认选择之前的key,id进行去重,所以不行
    dim(unique(re1))
    # 需要加入变量的unique
    dim(unique(re1,by=c('id','call_time','call_bridge_duration')))
  • 一次计算多列

    1
    2
    3
    4
    5
    6
    # 写一个返回list的函数,即可返回多列
    myfun <- function (a) {
    re <- strsplit(a,',')[[1]]
    return(list(re[1],re[2],re[3],re[4]))
    }
    tmp1 <- dat[,c('num','A','B','C'):=myfun(V1),V1]
  • 一次改多列类型

    1
    2
    thecol = c('last6_ftclass','last6_paycnt')
    ftpay[, (thecol) := lapply(.SD, as.numeric), .SDcols = thecol]
  • 分组计算 (使用.SD)

    1
    2
    3
    4
    5
    6
    7
    # 按列计算
    data[,ldply(.SD,function…)]
    # 按行计算
    data[,apply(.SD,1,function…)]
    data[,sum(unlist(.SD)),by=1:nrow(data)]
    # 取每组的第一行
    tmp[order(subject_no, -time)][, head(.SD, 1), by = list(user_id, subject_no)]
  • 根据列名group by(传入的可以直接是字符串向量)

    1
    tmp <- re[,.N,by=c('x1','x2','x3')]
  • 返回值说明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    options(datatable.verbose = TRUE)
    # 不显示赋值的返回结果
    DT[ , 5] # data.table
    DT[2, 5] # data.table
    DT[,"region"] # data.table
    DT[["region"]] # vector
    DT$region # vector
    DT[,columnRed:columnViolet]
    # column name range to get data.table
    DT[,c("columnRed","columnOrange","columnYellow")]
    # column name to get data.table
    DT[,.(columnRed,columnOrange,columnYellow)]
    # column name to get data.table
    DT[, { x<-colA+10; x*x/2 }]
    # 对X进行计算
    DT[ , mycol, with = FALSE]
    # mycol = "x"
    NEWDT = DT[0]
  • 变换

    1
    dcast(tmp,user_id~status,value.var ='N')

自适应测评

发表于 2019-05-22 | 分类于 测评 | | 阅读次数:

目标

基于IRT理论,实现对题库参数的估计,根据新用户做题情况给出用户能力值估计及其标准差,再根据当前能力值给出最能够区分用户等级的下一道题,当能力值稳定在一定范围内即结束测试。实现根据学生能力动态出题的自适应测试。

基础知识

IRT-3PL

IRT理论即项目反应理论(Item Response Theory, IRT),又称题目反应理论、潜在特质理论(Item Response Theory)是一系列心理统计学模型的总称。在实际应用中,人们出于数值处理的简便,倾向于使用3参数Logistic模型(简称3PL模型,3-parameter Logistic model),3PL模型中,第$j$人做对第$i$题的概率如下:

$$ P_i(\theta _j)=c_i+\frac{(1-c_i)}{1+e^{-\alpha_i(\theta_j-\beta_i))}} $$

  • ${\theta_j}$,第$j$人的能力值
  • ${\alpha_i}$,第$i$题的区分度(item discrimination)
  • ${\beta_j}$,第$i$题的难度(item difficulty)
  • $c_i$,第$i$题的猜对概率(guessing parameter)

MLE

极大似然估计方法(Maximum Likelihood Estimate,MLE)也称为最大概似估计或最大似然估计,是建立在极大似然原理的基础上的一个统计方法。求极大似然函数估计值的一般步骤:1) 写出似然函数;2)对似然函数取对数;3) 求导数 4) 解似然方程得到参数估计

  • 本项目中,$u_{ij}$表示用户$j$是否做第$i$题的结果(0/1),则对应对数似然函数为:
    $$L=log(P(U|\theta)) \
    =log[\coprod_{j=1}^{J}\coprod_{i=1}^{N} P_{ij}^{u_{ij}}Q_{ij}^{1-u_{ij}} ] \
    =\sum_{j=1}^{J}\sum_{i=1}^{N}[u_{ij}logP_{ij}+(1-u_{ij})logQ_{ij}] $$

  • 结合3PL模型,可得用户$j$是否做第$i$题的对数似然值如下:
    $$ ell_{ij}=u_{ij}log[c_i+\frac{(1-c_i)}{1+e^{-z_i}}] + (1-u_{ij})log[(1-c_i)\frac{e^{-z_i}}{1+e^{-z_i}}]$$

    • 项目代码utl.clib.log_likelihood_2PL
      其中,$z_i=\alpha_i(\theta_j-\beta_i)$

EM

EM算法指的是最大期望算法(Expectation Maximization Algorithm,又译期望最大化算法),是一种迭代算法,用于含有隐变量(latent variable)的概率参数模型的最大似然估计或极大后验概率估计。

  • 输入:观测变量数据$Y$,隐变量数据$Z$,联合分布$P(Y,Z|\theta)$,条件分布$P(Z|Y,\theta)$;
  • 输出:模型参数$\theta$
    • (1) 选择参数的初值$\theta^{(0)}$,开始迭代;
      • 参数的初值可以任意选择,但EM算法对初值是比较敏感的
    • (2) E步:记$\theta^{(i)}$为第$i$次迭代参数${\theta}$的估计值,在第$i+1$次迭代的E步,计算
      $$ Q(\theta,\theta^{(i)})=E_Z[logP(Y,Z|\theta)|Y,\theta^{(i)}]=\sum_{Z}logP(Y,Z|\theta)P(Z|Y,\theta^{(i)}) $$
      这里,$P(Z|Y,\theta^{(i)}$是在给定观测数据$Y$和当前参数估计$\theta^{(i)}$下隐变量数据$Z$的条件概率分布
      • $Z$是为观测数据,$Y$是观测数据,$Q(\theta,\theta^{(i)})$中第一个变元表示 要极大化的参数,第2个变元表示参数当前的估计值。每次迭代式实际是求使得$Q$函数取最大值的$\theta$
    • (3) M步:求是$Q(\theta,\theta^{(i)})$极大化的$\theta$,确定第$i+1$次迭代的参数的估计值$\theta^{(i+1)}$
      $$\theta^{(i+1)}=argmax_{\theta}Q(\theta,\theta^{(i)})$$
    • (4) 重复第(2)步和第(3)步,直到收敛
      • 给出迭代的停止条件,一般是对较小的正数$\varepsilon _1,\varepsilon _2
        $,若满足
        $$\left | \theta^{(i+1)}- \theta^{(i)}\right | < \varepsilon _1
        或 \left | Q(\theta^{(i+1)},\theta^{(i)})-Q(\theta^{(i)},\theta^{(i)}) \right | < \varepsilon _2$$
        则停止迭代

参数估计

采用EM算法实现MLE,得到对$\Delta ,\pi$的估计

  • $\Delta$,题参数
    • 第$j$题的参数记为$\delta _j$,则 $\Delta = (\delta _1,…,\delta _N)$
  • $\pi$,能力分布
    • 假设所有用户一共可分为$K$个等级水平,其中第$k$级水平记为$q_k$,对应出现概率为$\pi_k$,则$\pi=(\pi_1,…,\pi_K)$
      此外,建模过程中需要用到的其他元素定义如下:
  • $n_k^{(s)}$,所用用户中$\theta$(能力)在$k$等级($q_k$)的人数
    • 代码中为self.item_expected_right_bytheta+self.item_expected_wrong_bytheta
    • 记$n$为$n_k$的集合向量
  • $r_{jk}^{(s)}$,能力$\theta$为$q_k$的并且做对第$j$题的人数
    • 代码中为self.item_expected_wrong_bytheta
    • 记$R$为$r_{jk}$的集合矩阵
      于是,完全数据下的对数似然函数为(具体推导过程见参考文献):
      $$log[L(R^{(s)},n^{(s)})|\Delta,\pi]=\sum_{j=1}^{J}l(\delta_j)+l(\pi)$$
      其中
      $$l(\delta_j)=\sum_{k=1}^{K}r_{jk}^{(s)}log[P(q_k|\delta_j)]+(n_{k}^{(s)}-r_{jk}^{(s)})log[1-P(q_k|\delta_j)] $$
      $$l(\pi)=\sum_{k=1}^{K}n_{k}^{(s)}log[\pi_k]$$
      因此,分开对$l(\delta_j)$和$l(\pi)$求导,从而能使整体对数似然函数达到最大,得到相应的参数估计。
      于是应用于本项目的EM算法实现如下:

E step

在第$(s-1)$次迭代已计算出$\pi^{(s)}$与$\delta^{(s)}$,则此次迭代的E step带入计算

  • (1)
    $$n_k^{(s)}=\sum_{i=1}^{N}f(q_k|y_i,\Delta^{(s)},\pi^{(s)})$$

  • (2)
    $$r_{jk}^{(s)}=\sum_{i=1}^{N}u_{ij}f(q_k|y_i,\Delta^{(s)},\pi^{(s)})$$
    即,在$n_k^{(s)}$的分子内乘以做第$j$题的结果(0/1)$u_{ij}$

M step

根据$l(\delta_j)$和$l(\pi)$分开估计$\pi^{(s+1)}$与$\delta^{(s+1)}$

  • (1) $$\pi_k^{(s+1)}=\frac{n_k^{(s)}}{N}$$
  • (2) 求解 $\frac{\partial l(\delta_j) }{\partial\delta_{tj}} = 0 $,得到$\delta_j^{(s)}$的估计值
    • 代码中主要采用L-BFGS-G(拟牛顿法)求解
    • from scipy.optimize import minimize , method='L-BFGS-B'

重复以上E和M step直到达到收敛条件,即可得到题库的参数以及用户能力的估计值。

CAT

计算机自适应考试(Computerized Adaptive Testing,简称CAT)是建构在20世纪50年代发展起来的现代测验理论——项目反应理论(简称IRT)基础上的一种考试方式。
它能根据考生答题的情况不断计算受试者的能力值及信息量,并实时地根据这些参数调整出题策略,最终给受试者一个恰当的评价。

  • step1: 首先从题库中随机调取学生初始等级下的一道题
  • step2: 根据学答题情况,以及相应题目的3个参数,估计出当前的能力值$\theta^{(s)}$
    • 项目代码solver.theta_estimator,目前只实现了MLE的$\theta$估计
  • step3: 根据当前能力值,计算可选题库中对于该能力值$\theta^{(s)}$下的信息量
    $$I_i(\theta)=\frac{[{P}’_i(\theta)]^2}{P_i(\theta)Q_i(\theta)} $$
    选择信息量最大的一道题给予学生,继续作答,同时可计算下一步的$\theta^{(s+1)}$估计量的标准差
    $$ SE = \frac{1}{\sqrt{\sum I_i(\theta)}} $$
    • 项目代码utl.clib.info_item
  • step4: 重复step2-step3,直到标准差在给定的阈值内,或者满足其他停止条件(例如最多只做30题)则结束测试,并给出最后一次估计的能力值$\theta$

Reference

  • IRT Parameter Estimation using the EM Algorithm
  • https://github.com/17zuoye/pyirt
  • https://github.com/xluo11/xxIRT
  • https://cran.r-project.org/web/packages/catR/index.html

基于竞技比赛的评分排序系统-Glicko

发表于 2019-05-22 | 分类于 模型 , 排序 | | 阅读次数:

背景介绍

参考各类竞技比赛的评分排序系统,我们在具体公司业务上也可以进行相应评分排序,从而得到最能体现用户偏好的商品评分系统。

  • 目标
    • 从用户角度出发,给出平台商品的真实排序
  • 数据
    • 需要数据类型:A和B比赛的胜负数据,即(A,B,1)代表A击败B
    • 例如在有A和B同时曝光可选时,用户选择了A,则代表A击败B一次
  • 方法
    • Elo rating system
    • Glicko rating system

Elo rating system

Elo等级分制度(Elo rating system)是指由匈牙利裔美国物理学家 Arpad Elo创建的一个衡量各类对弈活动水平的评价方法,是当今对弈水平评估的公认的权威方法。被广泛用于国际象棋、围棋、足球、篮球等运动。游戏界比较著名的应用有: WOW(魔兽世界)、DOTA、LOL。

计分方法

系统中有A和B两位选手,$R_A$ 和$R_B$分别代表A与B的当前等级分,之后A和B的进行一次竞技,结果记为$S_A$(如果A胜则$S_A=1$,负为0,平为0.5)。该次竞技后A的等级分更新如下:

$$ R_{A}^{‘} = R_A + K(S_A - E_A) $$

其中:

  • $E_A$,代表A相对于B的获胜概率(胜率期望值)
    • $E_A=\frac{1}{1+10^{(R_B-R_A)/400}}$
  • $K$ 为一个常数,代表理论上最多可以赢一个玩家的得分和失分
    • K/2就是相同rating的玩家其中一方胜利后所得的分数
    • 在大部分的游戏规则中,K=32; 国际象棋大师赛中,K=16
    • 竞技的选手水平越高K取值越小(避免少数比赛改变顶尖玩家排名)
  • Elo模型原先采用正态分布。但是实践显明棋手的表现并非呈正态分布,所以现在的等级分计分系统通常使用的是逻辑分布。
  • 公式中分母的400来由: 根据公式可以得出,当K值相同的情况下,越高的分母,越低的积分变化。总体来说400是一个平衡的、让多数玩家积分保持标准正态分布的值

实例说明

若当前A玩家积分为1500,B玩家积分为1600

  • 预估A玩家的胜负值: Ea = 1/(1+10^[(1600-1500)/400])≈ 0.36
  • 预估B玩家的胜负值: Eb = 1-Ea = 1-0.36 = 0.64
  • 假设A玩家获胜,实际胜负值为Sa = 1
    • A玩家最终得分为 :R’a = 1500 + 32*(1-0.36) = 1500+20.5 = 1520
    • A玩家赢20分,B玩家输20分。
  • 假设B玩家获胜,实际胜负值为Sa = 1
    • B队最终得分为 R’b = 1600 + 32*(1-0.64) = 1600 + 11.52 = 1612
    • B玩家赢12分,A玩家输12分。

Glicko rating system

Glicko是由Mark Glickman提出的方法,是对Elo评分系统的改进,加入了评分偏差项RD(ratings deviation)。

  • 考虑未进行对战的时间长度,时间越长则RD越大
  • 举例解释:一个很久未参与竞技比赛的选手A,其能力值很可能不再等于系统记录的$R_A$,如果A与能力值相同的B($R_A=R_B$)比赛,而选手B是近期一直活跃在系统的玩家,则明显能力值更加可信。
    • 若A击败B,则A加分应该更多,B减分应该更少
    • 对于A:其能力不确定,击败了系统中相同等级的对手,说明很可能A的能力早已高于系统的记录,因此需要‘快速加分’
    • 对于B:被一个不太确定能力的人击败,说明A很可能本来就比B强,因此被击败不应该扣除太多积分,因此需要‘减少降分’

计分方法

对于选手A,1)记其能力评分(rating)为$r$;2)偏差(可理解为:标准差)为$RD$,为了方便计算,记方差为$v$ ($v=RD^2$);3)第$j$次的比赛结果为$S_j$。则其进入系统后,随着比赛的增加,其得分相应计算如下:

注意:比赛以周期为单位更新选手评分,此处假设选手A在一个周期t+1内,参与了m次比赛

  • Step1:初始化评分
    • a) 无历史评分则,评分$r=1500$,方差$v=350^2$ (初始分可根据实际情况设置为其他数值)
    • b) 有历史评分则,评分 $r=r_{old}$,方差$v=min(v_{old}+c^2t,350^2) $
      • $c$,衡量时间因素的常量系数
      • $t$,本次比赛和上次比赛的时间间隔(几个时间周期)
  • Step2:更新评分$r_{new}$与方差$v_{new}$
    • 方差:
      $$ v_{new}=(\frac{1}{v}+d)^{-1} $$
    • 评分:
      $$ r_{new}=r+qv_{new}{\sum_{j=1}^{m}k_j(s_j-e_j)} $$
    • 其中:
      • 对比Elo的常量$K$,Glicko的系数是方差的函数:$k_j=\frac{1}{\sqrt{1+3q^2v_j/\pi^2}}$ (实际上要和Elo的$K$对等,还需要再乘$qv^*$)
      • 对比Elo的$E_A$,Glicko的获胜概率期望值加入$k_j$乘数,从而也和方差$v$有关:$e_j=\frac{1}{1+10^{-k_j(r-r_j)/400}}$
      • $q=ln(10)/400$
      • $d=q^2\sum_{j=1}^{m}k_j^2e_j(1-e_j)$

预测方法

现有选手A,其评分和方差为$(r_a,v_a)$,若A与$(r_b,v_b)$的选手B进行比赛,则可预测A的胜率为:
$$ e_{ab}=\frac{1}{1+10^{-k_{ab}(r_a-r_b)/400}} $$
其中,
$$ k_{ab}=\frac{1}{\sqrt{1+3q^2(v_a+v_b)/\pi^2}} $$

tips

  • 关于$RD$:随着选手的活跃,参赛次数的增加,偏差会越来越小,$RD$太小会导致选手即使提升了能力赢得比赛,也只能加极少的分数,一般应用中会设置$RD$的下限,例如 $ RD=max(30,RD)$
  • 置信区间:根据 quasi-Bayesian derivation of the Glicko system,一般可以认为95%的置信区间如下:
    $$(r-1.96RD,r+1.96RD)$$

工程实现

使用 Alec Stephenson 写的R包PlayerRatings

共有参数

  • data,数据,由四列组成:
    • 时间:第几个比赛周
    • 主场方方选手名称,比如说中国队和西班牙队这次在中国比赛,那么中国为主场方
    • 非主场方选手名称
    • 得分,胜=1,平=0.5,负=0
  • status,当前计分系统的状态数据框
    • 主要包括:选手名称(Player)、评分(Rating)、比赛总场次(Games)、胜场次(Win)、平场次(Draw)、负场次(Loss)、该队结束比赛后还剩的比赛周次(Lag)
    • 有‘RD’参数的系统,例如Glicko,还包括Deviation列
  • gamma,对主场方的偏置
    • 主要用于预测,例如在A的地区和B比赛,那么同等水平下会A的胜率应该更高
    • 建模函数中gamma默认为0,而预测函数(predict())中gamma默认为30

建模函数

  • elo(),实现Elo评分系统,主要参数:

    • data,status=NULL,gamma=0
    • init,初始分,默认init = 2200
    • kfac,K值,
      • 给定常数,默认kfac=27
      • 也可以设置不同规则下的K值,例如场次在30以上取26,否则取32(kfac=kgames)
  • glicko(),实现Glicko评分系统

    • data,status=NULL,gamma=0
    • init,初始分和标准差,默认init = c(2200,300)
    • cval,衡量时间因素的常量系数,默认cval = 15,(区别于Elo的重要参数)
  • steph(),实现作者基于 Glicko 改进的 Stephenson 评分系统

    • status = NULL, gamma = 0,
    • init,初始分和标准差,默认init = c(2200,300)
    • cval = 10, hval = 10, bval = 0, lambda = 2
      • 区别于其他系统的重要参数
      • 详细修改点和公式见Glicko rating system

hetal

记录数据科学中常用的模型+代码+技巧

10 日志
12 分类
17 标签
© 2020 hetal
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4