1、上午去面试了个phd,压力大啊,还需要不断学习,不断交流,增加自己的知识,增强自己的交流和表达能力,take more practise
2、我上午还干了什么?拉黑了某个城市,这个倒是很快!然后尝试分析organic模型对于爱尚是否有区隔度,写了下SQL,了解库表结构,抓逾期等信息
3、下午继续到晚上继续干爱尚建模的事情,接触了一些新东西,也温故了旧知识,小结吸收一下
开始发现SQL的效率出现了极大的问题,尝试去优化,我才发现我以前的写法有一个很大的问题,我昨天才发现,子查询的join方式效率太低了,-- 3C放款客户 SELECT l.loan_number,m.grade,m.description,i.days_in_default FROM (SELECT * FROM alchemist.t_loans WHERE product_no = '3C') l -- 获取审批信息 JOIN underwriting.uw_model m ON m.uw_model_id = (SELECT uw_model_id FROM underwriting.uw_model WHERE loan_number = l.loan_number AND grade IS NOT NULL ORDER BY uw_model_id DESC LIMIT 1) -- 获取逾期信息 JOIN alchemist.t_installments i ON l.loan_number = i.loan_number AND i.installment_number = 1我以前join的时候喜欢写类似这种子查询条件,去join某个分组(上例中loan_number为一个分组)中最大或最小的那个,但这种情况下,运行机制是如何的呢?为什么会这么慢呢?因为子查询的where拿每一个loan_number去扫全表,比如5000个loan_number扫5000次表,表容量100万,这样就非常慢了,而先不管去找到分组中最大的直接join起来,后续再做处理就是一种非常高效的做法,join的后台机制,100万的表可能只扫一次,具体机制不了解,非常高效join中有一些筛选条件,一种方式是写在最后SELECT l.loan_number,m.grade,m.description,i.days_in_default FROM alchemist.t_loans l -- 获取审批信息 JOIN underwriting.uw_model m ON m.uw_model_id = (SELECT uw_model_id FROM underwriting.uw_model WHERE loan_number = l.loan_number AND grade IS NOT NULL ORDER BY uw_model_id DESC LIMIT 1) -- 获取逾期信息 JOIN alchemist.t_installments i ON l.loan_number = i.loan_number AND i.installment_number = 1 WHERE l.product_no = '3C' AND l.funding_success_date >= '2017-02-01' AND l.funding_success_date < '2017-03-01'另一种方式是join之前就筛选出来,临时表的写法SELECT l.loan_number,m.grade,m.description,i.days_in_default FROM (SELECT * FROM alchemist.t_loans WHERE product_no = '3C' AND funding_success_date >= '2017-02-01' AND funding_success_date < '2017-03-01') l -- 获取审批信息 JOIN underwriting.uw_model m ON m.uw_model_id = (SELECT uw_model_id FROM underwriting.uw_model WHERE loan_number = l.loan_number AND grade IS NOT NULL ORDER BY uw_model_id DESC LIMIT 1) -- 获取逾期信息 JOIN alchemist.t_installments i ON l.loan_number = i.loan_number AND i.installment_number = 1昨天跑的结果是前者更快,我还有点纳闷,前者花了2600秒,后者花了9000+秒(后来我又跑了一次前者是2768s,后者是2048s,如果想得到结论可能需要多跑几次,有时候可能存在一定的不稳定),(随着数据表越来越大,写SQL的时候就要追求速度了,而不是追求一些花式技巧),后来在辉哥的启发下,我发现还可以把这些where筛选信息写到on中,可能以前写join on受到定势思维的影响,一直是写两张表的关联关系,其实也可以把单张的表筛选条件写到on后面,比如这样SELECT l.loan_number,m.uw_model_id,m.grade,m.description,i.days_in_default FROM alchemist.t_loans l -- 获取审批信息 JOIN underwriting.uw_model m ON l.loan_number = m.loan_number AND l.product_no = '3C' AND m.grade IS NOT NULL -- 获取逾期信息 JOIN alchemist.t_installments i ON l.loan_number = i.loan_number AND i.installment_number = 1 AND i.original_due_date < '2017-04-23' ORDER BY l.loan_number,uw_model_id DESC 前面说到为了追求效率,先用join on的方式把表连起来再说,后面再进行筛选,在mysql中,我由于没有create临时表的权限,采用的传统方式是group by,然后再MAX id或time的方式找到最新的一条,但是一旦结果集是由很多join产生的,这个时候产生结果集那一坨逻辑由于不能建成临时表,所有再用group by有两个缺点,第一、结构非常臃肿,内外select几乎是相同的语句 第二、如果改动内逻辑,外逻辑跟着做相同的修改非常蛋疼,但是昨天辉哥教了我一种新的写法,同样可以解决这个问题,这个写法感觉上有点儿类似于所谓的存储过程SELECT * ,if(days_in_default > 10,1,0) AS default_flag FROM ( SELECT a.* ,@rownum := @rownum + 1 AS rownum ,if(@loan_number = a.loan_number,@rank := @rank + 1,@rank := 1) as rank ,@loan_number := a.loan_number FROM ( SELECT l.loan_number,m.uw_model_id,m.grade,m.description,i.days_in_default FROM alchemist.t_loans l -- 获取审批信息 JOIN underwriting.uw_model m ON l.loan_number = m.loan_number AND l.product_no = '3C' AND m.grade IS NOT NULL -- 获取逾期信息 JOIN alchemist.t_installments i ON l.loan_number = i.loan_number AND i.installment_number = 1 AND i.original_due_date < '2017-04-23' ORDER BY l.loan_number,uw_model_id DESC ) a ,(SELECT @rownum := 0,@loan_number := null,@rank := 0) b ) t WHERE t.rank = 1大概的思路就是,要设置几个变量一个是大序号rownum,一个是分组内序号rank,最后一个就是分组变量,上面的例子中是loan_number。结构是这样的,子表a中是自己的结果集(未按分组group by取最新),b表用于设置要用到的3个变量,然后select a.*后是迭代逻辑,可以看成记录是逐条在处理,@标记的变量在逐条处理过程中一直在存储,@loan_number的赋值就是可以看成是和select的顺序一致的,a中order by控制好,第一排序第二排序,最后再包一层选择rank=1的,这种方法好处就是结构比较清晰,其实子表b和a.*后面的select内容是固定的,包一层也是固定的,坏处就是会引入另外几个变量形成的字段,最后如果不想让这几个字段出现在结果集中就只能用select手工列出自己希望的字段,但总的说来这个方法不错。新技能get!然后分析了目前的qh模型,爱尚商品贷在各个grade上的表现,拉个列联表,没什么新东西,发现表现还不错 然后拉出了同盾分和算话分,以及最后前海分,作分析,发现算话最烂,最后决定用同盾和前海建模,分析的时候直接是透视表,分区只能采用等步长的分区方法,但是大概可以看出有无趋势,一个好的变量应该在随意的分bin方法中都能透露出一个比较好的趋势,考虑建模了,遇到的第一个问题就是as这群人qh的空值率居然达到了40%多,我考虑是否采用woe进行编码,但是考虑了下,还是首先用数值进行尝试,用一个近似的值去填补了缺失值,然后就是工具选择的问题,我还是考虑用sklearn,sklearn的算法调用都很简单,from sklearn.linear_model.logistic import LogisticRegression,sklearn的参数有很多我不太了解,我观察到L1和L2要任选一种,默认采用的L2,全部用默认参数,fit出来,查看系数用的lr.coef_,然后w0截距是在另外一个成员变量中,叫lr.intercept_,sigmoid函数用的标准的,并没有把负号拿到Z里面去,1/(1+exp(-Z)),得到系数过后,就可以回到excel中算出所有的prob,这里就涉及到一个问题,excel等分组方法,这里等分有两种思路,第一种就是把临界值划分到某个组里面,如果临界值很多的情况下,有时候每组的容量会很不一致,另一种分组方法就是按照排序强行等分,我查了下excel的等分方式,都比较麻烦,我觉得这种事情还是应该python来完成,我昨天是人工完成的,所以这里是一个痛点,下一步要想办法解决,开始我采用的是第一种方案,大致看了下和单独用qh没差,但是我思考了下不能这样比,两个策略应该用于相同的数据,然后就是肉眼可见的好,fit出来的模型,我本身又是用的比较有区隔力的变量,从经验上分析,fit出的模型无论如何是有提高的,对于划分出来的5个组,我复习了一下entropy的计算,entropy是对无序的度量,熵越大越无序,可以理解成两种好坏越均匀,熵越小,越有序,好坏尽可能分开,从熵的角度也算出来fit出来的模型更佳,等分写if公式,用excel来做都不效率,后面全部自动化用python来弄,透视表展示似乎还是可以用excel,差不多以上 最后回到家中,听了我堂客朋友的一个故事,同样的年龄已经年薪百万,总行空降到省分行做公司部老总,人与人3、5年就拉开差距了,通过我自己的观察,和老丈人提到的一些事情,我所在的阶级,努力可以改变很多事情,事业都是拿时间去堆出来的,应征了一句话 天道酬勤!不努力只配吃屎!加油!