看我这流放之路装备数据库和数据,6不6?

&p&工欲善其事,必先利其器。所以第一步,我们先下载第三方库。在这里,我用到的是pymysql库。&/p&&p&&br&&/p&&p&下载库:在命令行输入&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1pip install pymysql
&/code&&/pre&&/div&&p&&br&&/p&&p&下载后可检验一下是否成功下载。直接在命令行进入python然后导库即可&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1C:\Users\June&python
2Python 3.6.3 |Anaconda, Inc.| (default, Oct 15 :45) [MSC v.1900 64 bit (AMD64)] on win32
3Type &help&, &copyright&, &credits& or &license& for more information.
4&&& import pymysql
&/code&&/pre&&/div&&p&&br&&/p&&p&看到这个画面就说明下载成功了,接下来学习如何操作数据库了!!!&/p&&p&&br&&/p&&p&&b&连接数据库&/b& &/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1import pymysql
2# 连接数据库
3db = pymysql.connect(host='127.0.0.1',user='root',passwd='your password',db='news',port=3306,charset='utf8')
&/code&&/pre&&/div&&p&&br&&/p&&p&以上的参数是必填的&/p&&p&&br&&/p&&ul&&li&&b&host&/b&: 这个是ip地址,因为我这里是本地的,所以填127.0.0.1,也可以填localhost。&/li&&li&&b&user&/b&:用户名,如果你也是本地的,就填root好了&/li&&li&&b&passwd&/b&:这个是密码,填上你自己设的密码就可以了&/li&&li&&b&db&/b&:这个是数据库名,我这里选的是news数据库&/li&&li&&b&port&/b&:这个是端口,本地的一般都是3306&/li&&li&&b&charset&/b&:这个是编码方式,要和你数据库的编码方式一致,要不会连接失败&/li&&/ul&&p&&br&&/p&&p&连接上了,怎么验证呢?这里我们可以选择查一条数据&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1try:
2 db = pymysql.connect(host='127.0.0.1',user='root',passwd='your password',db='news',port=3306,charset='utf8')
3 # 检验数据库是否连接成功
4 cursor = db.cursor()
5 # 这个是执行sql语句,返回的是影响的条数
6 data = cursor.execute('SELECT * FROM `new`')
7 # 得到一条数据
8 one = cursor.fetchone()
9 print(data)
10 print(one)
11except pymysql.Error as e:
12 print(e)
13 print('操作数据库失败')
14finally:
15 # 如果连接成功就要关闭数据库
17 db.close()
&/code&&/pre&&/div&&p&&br&&/p&&p&代码解读:因为在连接数据库中,有时会发生连接失败等异常,所以这里就进行捕捉异常,这里的异常都是在 &b&pymsql.Error &/b&里面。上面的代码看不懂也没关系,因为我接下来会说,如果运行后有结果证明连接成功。&/p&&p&&br&&/p&&p&&b&在用完后,一定要记得关闭数据库连接,防止资源泄露问题。&/b&&/p&&p&&br&&/p&&p&&b&对数据进行查询&/b&&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1import pymysql
3 conn = pymysql.connect(host='127.0.0.1',user='root',passwd='password',db='news',charset='utf8',port=3306)
4 # 这个是光标,用来操作数据库语句
5 cursor = conn.cursor()
6 # 执行sql语句
7 cursor.execute('SELECT * FROM `new`')
8 print(cursor.fetchone())
9 # 关闭光标
10 cursor.close()
11except pymysql.Error as e:
12 print(e)
13 print('操作数据库失败')
14finally:
15 if conn:
16 conn.close()
&/code&&/pre&&/div&&p&&br&&/p&&p&代码解读:&/p&&ul&&li&&b&cursor()&/b&:这个是光标,用来执行mysql语句的,用完后也是需要关闭的&/li&&li&&b&excute()&/b&:这个是执行语句,执行参数的mysql语句&/li&&li&&b&fetchone()&/b&:这个是查看执行语句后的一条数据&/li&&li&&b&fetchall()&/b&:这个是查看所有数据&/li&&/ul&&p&&br&&/p&&p&在查询数据后,返回的是一整条数据,有没有可以按字典形式来查询的呢?来试试!&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1print(cursor.fetchone()['name'])
3Traceback (most recent call last):
4 File &E:/anaconda/python_project/mysql_test/test2.py&, line 8, in &module&
5 print(cursor.fetchone()['name'])
6TypeError: tuple indices must be integers or slices, not str
&/code&&/pre&&/div&&p&&br&&/p&&p&查了之后,编译器想都不想就给了我这个错误,说这是个元组,不能这样操作。&/p&&p&&br&&/p&&p&虽然python没有提供,但是我们可以手动转成字典来查询啊&/p&&p&cursor这里有个属性:&b&description&/b&。获取的是数据库每个栏位情况,如下:&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1print(cursor.description)
2# 下面是结果
3(('id', 3, None, 11, 11, 0, False), ('type', 253, None, 5, 5, 0, False), ('title', 253, None, 50, 50, 0, False), ('content', 253, None, , 0, False), ('view_count', 3, None, 11, 11, 0, False), ('release_time', 12, None, 19, 19, 0, False), ('author', 253, None, 20, 20, 0, True), ('from', 253, None, 20, 20, 0, True), ('is_valibale', 3, None, 11, 11, 0, False)
&/code&&/pre&&/div&&p&&br&&/p&&p&所以,我们利用这个属性手动生成字典&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1# 将一条数据转成字典方便查找
2new = dict(zip([x[0] for x in cursor.description],[x for x in cursor.fetchone()]))
3print(new)
4# 下面是结果
5{'id': 2, 'type': 'NBA', 'title': '考辛斯跟腱撕裂赛季报销 浓眉詹皇发声祝福', 'content': '他遭遇左脚跟腱撕裂,将缺席赛季剩下的比赛。这无疑对考辛斯和鹈鹕队都是一个重大的打击', 'view_count': 3560, 'release_time': datetime.datetime(, 12, 10), 'author': 'xiaoylin', 'from': '腾讯体育', 'is_valibale': 1}
&/code&&/pre&&/div&&p&&br&&/p&&p&这里利用zip函数和列表生成式来一行代码就生成成功了&/p&&p&&br&&/p&&p&用字典来查询,现在就可以了&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1print(new['title'])
2# 下面是结果
3考辛斯跟腱撕裂赛季报销 浓眉詹皇发声祝福
&/code&&/pre&&/div&&p&&br&&/p&&p&但是,上面的只是一条数据的,如果是多条的呢?再按上面的方法就行不通了。这时就需要用到map函数了&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1def new2dict(new):
2 return dict(zip([x[0] for x in cursor.description],[x for x in new]))
3news_list = list(map(new2dict,cursor.fetchall()))
4print(news_list)
5# 下面是结果
6[{'id': 2, 'type': 'NBA', 'title': '考辛斯跟腱撕裂赛季报销 浓眉詹皇发声祝福', 'content': '他遭遇左脚跟腱撕裂,将缺席赛季剩下的比赛。这无疑对考辛斯和鹈鹕队都是一个重大的打击', 'view_count': 3560, 'release_time': datetime.datetime(, 12, 10), 'author': 'xiaoylin', 'from': '腾讯体育', 'is_valibale': 1}, {'id': 3, 'type': 'NBA', 'title': '火箭挖21分大哈登得背锅 连遭浓眉大帽太尴尬', 'content': '火箭在客场以113-115惜败于鹈鹕,4连胜终结。詹姆斯-哈登出战34分钟16投5中,其中三分球9投只有1中,罚球14罚12中,拿到23分、11助攻、5篮板但也有4次失误,其在场正负值为尴尬的-12分', 'view_count': 7520, 'release_time': datetime.datetime(, 12, 5), 'author': 'youngcao', 'from': '腾讯体育','is_valibale': 1}, {'id': 4, 'type': '英超', 'title': '足总杯-曼联4-0英乙球队晋级 桑神首秀造两球', 'content': '2017-18赛季英格兰足总杯第4轮,曼联客场4比0击败英乙球队约维尔,顺利晋级下一轮。桑切斯迎来曼联首秀,并制造了两个入球', 'view_count': 6560, 'release_time': datetime.datetime(, 5, 49), 'author': 'ricazhang', 'from': '腾讯体育','is_valibale': 1}, {'id': 5, 'type': '英超', 'title': '这才配红魔7号!桑神首秀大腿级表演 回击嘘声质疑', 'content': '在今天凌晨对阵约维尔的首秀也值得期待。虽然在登场的72分钟时间里没有进球,但送出1次助攻且有有6次威胁传球的数据还是十分亮眼', 'view_count': 2760, 'release_time': datetime.datetime(, 6, 13), 'author': 'yaxinhao', 'from': '腾讯体育', 'is_valibale': 1}]
&/code&&/pre&&/div&&p&&br&&/p&&p&这里很巧妙的利用了map函数,因为多条数据就可以进行迭代了,需要操作每条数据,这样就可以想到map函数&/p&&p&&br&&/p&&p&&b&接下来我们再用面向对象的方法来用python进行查询数据库&/b&&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1import pymysql
2class MysqlSearch(object):
3 def get_conn(self):
4 '''连接mysql数据库'''
6 self.conn = pymysql.connect(host='127.0.0.1',user='root',passwd='your password',port=3306,charset='utf8',db='news')
7 except pymysql.Error as e:
8 print(e)
9 print('连接数据库失败')
11 def close_conn(self):
12 '''关闭数据库'''
14 if self.conn:
15 self.conn.close()
16 except pymysql.Error as e:
17 print(e)
18 print('关闭数据库失败')
20 def get_one(self):
21 '''查询一条数据'''
23 # 这个是连接数据库
24 self.get_conn()
25 # 查询语句
26 sql = 'SELECT * FROM `new` WHERE `type`=%s'
27 # 这个光标用来执行sql语句
28 cursor = self.conn.cursor()
29 cursor.execute(sql,('英超',))
30 new = cursor.fetchone()
31 # 返回一个字典,让用户可以按数据类型来获取数据
32 new_dict = dict(zip([x[0] for x in cursor.description],new))
33 # 关闭cursor
34 cursor.close()
35 self.close_conn()
36 return new_dict
37 except AttributeError as e:
38 print(e)
39 return None
40 def get_all(self):
41 '''获取所有结果'''
42 sql = 'SELECT * FROM `new` '
43 self.get_conn()
45 cursor = self.conn.cursor()
46 cursor.execute(sql)
47 news = cursor.fetchall()
48 # 将数据转为字典,让用户根据键来查数据
49 news_list =list(map(lambda x:dict(zip([x[0] for x in cursor.description],[d for d in x])),news))
50 # 这样也行,连续用两个列表生成式
51 news_list = [dict(zip([x[0] for x in cursor.description],row)) for row in news]
52 cursor.close()
53 self.close_conn()
54 return news_list
55 except AttributeError as e:
56 print(e)
57 return None
59def main():
60 # 获取一条数据
61 news = MysqlSearch()
62 new = news.get_one()
63 if new:
64 print(new)
66 print('操作失败')
68 # 获取多条数据
69 news = MysqlSearch()
70 rest = news.get_all()
71 if rest:
72 print(rest)
73 print(rest[7]['type'],rest[7]['title'])
74 print('类型:{0},标题:{1}'.format(rest[12]['type'],rest[12]['title']))
75 for row in rest:
76 print(row)
78 print('没有获取到数据')
80if __name__ == '__main__':
&/code&&/pre&&/div&&p&&br&&/p&&p&这样就可以通过实例的方法来进行查询数据库了&/p&&p&&br&&/p&&p&我们还可以根据页数来进行查询指定的数据数&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1 def get_more(self,page,page_size):
2 '''查多少页的多少条数据'''
3 offset = (page-1)*page_size
4 sql = 'SELECT * FROM `new` LIMIT %s,%s'
6 self.get_conn()
7 cursor = self.conn.cursor()
8 cursor.execute(sql,(offset,page_size,))
9 news = [dict(zip([x[0] for x in cursor.description],new)) for new in cursor.fetchall()]
10 cursor.close()
11 self.close_conn()
12 return news
13 except AttributeError as e:
14 print(e)
15 return None
17def main():
18 #获取某页的数据
19 news = MysqlSearch()
20 new = news.get_more(3,5)
21 if new:
22 for row in new:
23 print(row)
25 print('获取数据失败')
27if __name__ == '__main__':
&/code&&/pre&&/div&&p&&br&&/p&&p&利用的是mysql的&b&limit&/b&关键字,还有其他的,比如进行排序分组的感兴趣的可以自己尝试下&/p&&p&&br&&/p&&p&&b&增加数据到数据库&/b&&/p&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1 def add_one(self):
2 sql = 'INSERT INTO `new`(`title`,`content`,`type`,`view_count`,`release_time`) VALUE(%s,%s,%s,%s,%s)'
4 self.get_conn()
5 cursor = self.conn.cursor()
6 cursor.execute(sql, ('title', 'content', 'type', '1111', ''))
7 cursor.execute(sql, ('标题', '内容', '类型', '0000', ''))
8 # 一定需要提交事务,要不不会显示,只会占位在数据库
9 self.conn.commit()
10 return 1
11 except AttributeError as e:
12 print('Error:', e)
13 return 0
14 except TypeError as e:
15 print('Error:', e)
16 # 发生错误还提交就是把执行正确的语句提交上去
17 # self.conn.commit()
18 # 下面这个方法是发生异常就全部不能提交,但语句执行成功的就会占位
19 self.conn.rollback()
20 return 0
21 finally:
22 cursor.close()
23 self.close_conn()
25 def main():
26 news = OperateSQL()
27 if news.add_one():
28 print('增加数据成功')
30 print('发生异常,请检查!!!')
32 if __name__ == '__main__':
&/code&&/pre&&/div&&p&&br&&/p&&p&因为是增加数据,所以需要提交事务,这就需要用到&b&conn
.commit()&/b&来进行提交,在增加数据后,如果不提交,数据库就不会显示。&/p&&p&&br&&/p&&p&还有修改数据和删除数据就不贴出来了,只是把上面的sql变量的语句改成修改或者删除的语句就可以了,如果你还不会,建议练习下&/p&&p&&br&&/p&&p&&b&END&/b&&/p&&p&&br&&/p&&p&代码我放在github了,网站为&a href=&https://link.zhihu.com/?target=https%3A//github.com/SergioJune/gongzhonghao_code& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/SergioJune/g&/span&&span class=&invisible&&ongzhonghao_code&/span&&span class=&ellipsis&&&/span&&/a&,有兴趣的可以去看看,如果可以的话希望给个star哈!&/p&&p&&br&&/p&&p&这篇文章只适合入门的,如果需要学习更多的话可以去查看pymysql的文档&a href=&https://link.zhihu.com/?target=http%3A//pymysql.readthedocs.io/en/latest/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&pymysql.readthedocs.io/&/span&&span class=&invisible&&en/latest/&/span&&span class=&ellipsis&&&/span&&/a& 。&/p&&p&&br&&/p&&blockquote&作者:sergiojune
Python爱好者社区专栏作者&br&个人公众号:日常学python&br&专注python爬虫,数据可视化,数据分析,python前端技术&/blockquote&
工欲善其事,必先利其器。所以第一步,我们先下载第三方库。在这里,我用到的是pymysql库。 下载库:在命令行输入 1pip install pymysql
下载后可检验一下是否成功下载。直接在命令行进入python然后导库即可 1C:\Users\June&python
2Python 3.6.3 |Anacond…
&figure&&img src=&https://pic4.zhimg.com/v2-cd7ea20fc0c71b1b46fe_b.jpg& data-rawwidth=&832& data-rawheight=&471& class=&origin_image zh-lightbox-thumb& width=&832& data-original=&https://pic4.zhimg.com/v2-cd7ea20fc0c71b1b46fe_r.jpg&&&/figure&&p&在设计A/B测试时,总有一个无法避免的问题:实验需要收集多少样本才能使结果显著?虽然事实上所谓“显著性样本”这一名词并不存在,但是样本数量的确与实验结果是否显著密切相关。本文将对A/B测试中如何决定样本数量进行详细解释,并利用两独立样本比率检验(two independent sample evaluation)作为案例:&/p&&ul&&li&A/B测试中的假设检验。建议有统计基础的朋友跳过。&/li&&li&决定样本数量的因素:基准比率,可探测误差和容错率。&/li&&li&R语言实现。&/li&&/ul&&h2&&b&A/B测试中的假设检验&/b&&/h2&&p&在这个章节我们回顾一下如何进行假设检验。假设检验一般有四个步骤,我们以两独立样本比率检验(two independent sample evaluation)作为案例。假设微信想通过改变注册流程来提高新用户注册成功的比率。&/p&&p&&b&第一步:提出假设&/b&&/p&&p&一般而言有两种假设,分别是原假设(null hypothesis)和备择假设(alternative hypothesis)。原假设一般比较消极,即新注册流程与旧流程并无不同。相对应的备择假设为新注册流程与旧流程有差别。设立两种假设的逻辑是,除非在数据明显的反对原假设时,我们一般不拒绝原假设,也就是默认原假设为真。而这种“保护”会通过“容错率”来体现。我们会在后文中介绍。&/p&&p&&b&第二步:提取随机样本&/b&&/p&&p&在设置好两种注册流程之后,实验将随机选取一部分新用户提供新的流程,而剩下的一部分仍然使用老的流程。值得注意的是样本的选取一定要是随机的(random),而且每个用户必须持续的只接收到一种流程(persistent manner)。随机分配(random split)并不要求平均分配。&/p&&p&&b&第三步:计算检验统计量&/b&&/p&&p&在收集完数据后,我们需要计算检验统计量(test statistics)。检验统计量是一个用来衡量数据是否支持原假设的统计量。在这个案例中,检验统计量是&/p&&p&&img src=&https://www.zhihu.com/equation?tex=z+%3D+%5Cfrac%7Bp_t-p_c%7D%7B%5Csqrt%7Bp%281-p%29%5Cleft%28%5Cfrac%7B1%7D%7Bn_t%7D%2B%5Cfrac%7B1%7D%7Bn_c%7D%5Cright%29%7D%7D& alt=&z = \frac{p_t-p_c}{\sqrt{p(1-p)\left(\frac{1}{n_t}+\frac{1}{n_c}\right)}}& eeimg=&1&&&/p&&p&其中p_t和p_c分别代表新流程和旧流程的注册成功率,n_t和n_c分别代表两者的样本数量,p代表结合的注册成功率。直观上而言,检验统计量是取出两者成功率的差值,并通过标准差进行了标准化。如果这个检验统计量绝对值越大,就代表数据越反对原假设。&/p&&p&&b&第四步:决定是否拒绝原假设&/b&&/p&&p&此时我们需要通过检验统计量绝对值的大小来判断是否拒绝原假设。在原假设下,检验统计量应该服从某种标准的分布。例如在微信的例子中,检验统计量在原假设情况下近似服从标准正态分布。当检验统计量的数值极端到在原假设时,它和更极端的情况,发生的概率非常小时,我们就说样本明显的反对原假设,因此拒绝。我们也用p值来衡量样本是否支持原假设。详见:&a href=&https://www.zhihu.com/question//answer/& class=&internal&&姚岑卓:统计学假设检验中 p 值的含义具体是什么?&/a&。&/p&&p&&b&假设检验的容错率&/b&&/p&&p&在此,我们还要引入两个概念:&b&显著性&/b&(significance level)和&b&统计功效&/b&(statistical power)。由于在做统计检验时,小概率事件发生会被我们视为不正常而反对原假设。因此会出现两种错误:假阳性(false positive)和假阴性(false negative),分别代表原假设为真时拒绝了原假设和原假设为假时没有拒绝原假设。显著性控制着假阳性的概率,而统计功效表示真阴性(1-假阴性概率)。在统计学传统里,显著性常被取为5%而统计功效常被取为80%。这种选取也保证了我们更“保护”原假设,因为假阴性(20%=1-80%)的概率是假阳性(5%)的4倍。这种更在意假阳性的性质也与现实相符合:我们不希望改变产品到一个并不会提高用户体验的方向。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-cd7ea20fc0c71b1b46fe_b.jpg& data-size=&normal& data-rawwidth=&832& data-rawheight=&471& class=&origin_image zh-lightbox-thumb& width=&832& data-original=&https://pic4.zhimg.com/v2-cd7ea20fc0c71b1b46fe_r.jpg&&&figcaption&Image courtesy of Kim Larsen&/figcaption&&/figure&&h2&决定样本数量的因素:基准比率,可探测误差和容错率&/h2&&p&在复习完如何进行假设检验后,我们可以总结有三个因素影响着样本数量的决定:&/p&&ol&&li&&b&基准比率:在控制组下的表现情况&/b&——新用户在旧流程下的注册成功比率。当基准比率非常接近极端值(0或1)时,需要的样本量会少。而当基准比率非常居中(0.5)时,需要更多的样本。直观上而言,如果旧流程没有任何用户能够注册,新流程拥有一个可注册的新用户便可说明显著更好。&/li&&li&&b&可探测误差:预测的测量度提升&/b&——新流程与旧流程注册成功率的差别估计。当预测的差值越大,我们会需要更少的用户来进行检验。反之则需要更多。&/li&&li&&b&容错率:显著性和统计功效&/b&。当我们降低假设检验的容错率时,无论是假阳性或假阴性,我们都需要更多的样本来支持我们的结论。由于一般两者都因传统二为定值,一般不需要进行改变。&/li&&/ol&&p&通过梳理决定样本数量的因素,并且假设容错率都由统计传统设定,我们可以视假设检验样本数为基准比率和需要的可探测误差所决定。如下图所示,当样本数量为1,000时,统计功效达不到80%,只有当样本数量为5,000才能达到。因此此时需要样本数量为5,000。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-b3c310da93f0fcd5c0cc8d_b.jpg& data-size=&normal& data-rawwidth=&736& data-rawheight=&331& class=&origin_image zh-lightbox-thumb& width=&736& data-original=&https://pic4.zhimg.com/v2-b3c310da93f0fcd5c0cc8d_r.jpg&&&figcaption&Image courtesy of Kim Larsen&/figcaption&&/figure&&h2&R语言实现&/h2&&p&R里有power函数可以帮你计算需要的样本数量:&/p&&div class=&highlight&&&pre&&code class=&language-rconsole&&&span&&/span&&span class=&go&&power.prop.test(n = NULL, p1 = NULL, p2 = NULL, sig.level = 0.05,&/span&
&span class=&go&&
power = NULL, alternative = c(&two.sided&, &one.sided&),&/span&
&span class=&go&&
strict = FALSE)&/span&
&/code&&/pre&&/div&&p&其中我们我们并不需要输入n。回到微信的例子,如果现在的注册成功率是90% ,而预计的提升是1%。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&power.prop.test(p1=0.9, p2=0.91, power=0.8,
alternative=&two.sided&, sig.level=0.05)
Two-sample comparison of proportions power calculation
n = 13494.97
sig.level = 0.05
power = 0.8
alternative = two.sided
NOTE: n is number in *each* group
&/code&&/pre&&/div&&p&则需要的样本量是&b&每个流程&/b&大约13k的新用户。总共需要大约37k新用户。一般而言,对于样本数足够多的产品,如微信和FB,我们一般只控制足够的新流程用户数。一方面是因为就流程一定能达到所需的样本数量,另一方面是对于大型产品,一般有多个实验同时进行。因此需要在保证“足够”的流量的同时,保证其他实验也有足够的流量。&/p&&p&Reference:&/p&&p&[1] &a href=&https://link.zhihu.com/?target=https%3A//multithreaded.stitchfix.com/blog//significant-sample/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&So, You Need a Statistically Significant Sample?&/a&&/p&&p&[2] &a href=&https://link.zhihu.com/?target=https%3A//signalvnoise.com/posts/3004-ab-testing-tech-note-determining-sample-size& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&A/B Testing Tech Note: determining sample size&/a&&/p&
在设计A/B测试时,总有一个无法避免的问题:实验需要收集多少样本才能使结果显著?虽然事实上所谓“显著性样本”这一名词并不存在,但是样本数量的确与实验结果是否显著密切相关。本文将对A/B测试中如何决定样本数量进行详细解释,并利用两独立样本比率检验…
&figure&&img src=&https://pic3.zhimg.com/v2-8ed4b5bbe060d8f73d8ad20_b.jpg& data-rawwidth=&900& data-rawheight=&500& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic3.zhimg.com/v2-8ed4b5bbe060d8f73d8ad20_r.jpg&&&/figure&&p&本文继续给大家总结pandas相关知识,标题如下:&/p&&ol&&li&索引、选取、和过滤&/li&&li&算术运算与数据对齐&/li&&li&算术方法&/li&&li&ufuncs和统计函数&/li&&li&排序排名&/li&&li&唯一值、值计数、以及成员资格&/li&&li&缺失值处理&/li&&li&缺失值处理之过滤缺失值&/li&&li&缺失值处理之填充缺失值&/li&&/ol&&h2&索引、选取、和过滤&/h2&&p&Series的索引和numpy类似,并且Series的索引值不仅能为整数,还能利用索引对象与值的映射关系去选取元素,这些在上一篇介绍Series已经提到过了。但还有一点需要注意的是,numpy、列表以及元祖的切片操作是不包含末端的,而Series利用标签切片是包含末端的。(用整数索引和正常切片是一样的)&/p&&figure&&img src=&https://pic1.zhimg.com/v2-1d77ced92d63cdcd62603d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&169& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-1d77ced92d63cdcd62603d_r.jpg&&&/figure&&p&而对DataFrame的索引可以索引DataFrame的一个或多个列。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-ede683fd0b51a8f5f9664ddf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&156& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic4.zhimg.com/v2-ede683fd0b51a8f5f9664ddf_r.jpg&&&/figure&&p&同时,用轴标签去将DataFrame切片仍然是包括末端的。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-43bd5af5f96b2b5de8bd6d6ed150c5af_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&123& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic3.zhimg.com/v2-43bd5af5f96b2b5de8bd6d6ed150c5af_r.jpg&&&/figure&&p&但我们对DataFram使用布尔索引进行数据选取的时候&/p&&figure&&img src=&https://pic1.zhimg.com/v2-45bb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&174& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-45bb_r.jpg&&&/figure&&p&我们可以看到,如果某一行里存在符合条件的元素,那么这一行就会被选出来,但是作为结果的DataFrame不符合条件的元素会变成缺失值。但是如果我们目的是对这一些符合条件的元素操作,结果和numpy是一样的&/p&&figure&&img src=&https://pic1.zhimg.com/v2-19e09f5e27e49c57d509_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&200& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-19e09f5e27e49c57d509_r.jpg&&&/figure&&p&此外,&b&pandas&/b&还有层次化索引,它使你能在一个轴上拥有多个索引级别。以下是用层次索引的例子。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-54da9dae1d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&254& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-54da9dae1d_r.jpg&&&/figure&&p&我们可以在创建的时候传入一个二维的列表或者数组隐式的创建层次索引。我们还可以通过&/p&&p&&b&pd.MultiIndex&/b&显式的创建多层索引对象。例子如下。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-4c5b952c5ddddfc5cc476d1fc847f8e6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&118& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic3.zhimg.com/v2-4c5b952c5ddddfc5cc476d1fc847f8e6_r.jpg&&&/figure&&p&上面的例子的结果是相同的。如果索引为不同层次的标签的组合,你还可以使用&b&.from_product&/b&来创建多层索引,这样你就无须枚举所有的索引。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-024fe8b294d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&165& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-024fe8b294d_r.jpg&&&/figure&&p&我们可以通过.index查看层次索引的构成&/p&&figure&&img src=&https://pic3.zhimg.com/v2-e02b4fcce4f7c3c19b56_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&57& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic3.zhimg.com/v2-e02b4fcce4f7c3c19b56_r.jpg&&&/figure&&p&层次索引由两部分组成,levels描述索引所在的位置,&b&levels[ 0 ]&/b&对应最外层的索引的取值,&b&labels[ 0 ]&/b&对应位于最外层索引在不同位置的取值,如最外层索引的前三个索引标签都是a,因此&b&label[ 0 ]&/b&开头对应&b&3&/b&个&b&0&/b&,如此类推。&/p&&p&层次索引必须从外向内索引(左边为外),如果只想用内层索引,则外层索引必须为全切片。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-f6a644e0e9c675488ccd25fdfcfa5d25_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&250& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic4.zhimg.com/v2-f6a644e0e9c675488ccd25fdfcfa5d25_r.jpg&&&/figure&&h2&算术运算与数据对齐&/h2&&p&pandas最重要一个的功能是,它支持对不同索引对象进行算术运算。如果两个对象含有不同的索引,则结果的索引对象为两者的并集。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-d4caf1e486_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&147& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-d4caf1e486_r.jpg&&&/figure&&p&我们可以看到&b&s1 s2&/b&相加的结果,索引相同的元素相加,只有一边独有的索引为缺失值,并且结果的索引为&b&s1 s2&/b& 索引的并集。DataFrame也是如此。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-8dee07cddbbae860f9bd8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&204& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic4.zhimg.com/v2-8dee07cddbbae860f9bd8_r.jpg&&&/figure&&h2&算术方法&/h2&&p&如果两个对象含有不同的索引,那么它们之间运算的结果会出现缺失值。&b&pandas&/b&提供了&b&add、sub、div、mul&/b& 这些方法可以指定 &b&fill_value&/b& 参数,当在运算过程中存在不匹配的索引时,就会用&b&fill_value&/b& 填充使得索引匹配,结果不产生缺失值。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-584addbdca7c8aa233f10765_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&194& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-584addbdca7c8aa233f10765_r.jpg&&&/figure&&p&可以看到,结果中没有缺失值,其结果相当于将两个&b&DataFrame&/b&补全并把NaN值填充为指定值之后在相加。&/p&&p&有些时候我们会让&b&Series&/b&对象和&b&DataFrame&/b&对象运算,例如&/p&&figure&&img src=&https://pic1.zhimg.com/v2-ed8f6eaee5ab18ff54af_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&174& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-ed8f6eaee5ab18ff54af_r.jpg&&&/figure&&p&默认情况下,会把Series的索引与DataFrame的列索引匹配,然后沿着行一直传播。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-720b5e027c7cfcdbdc80d6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&173& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-720b5e027c7cfcdbdc80d6_r.jpg&&&/figure&&p&显然由于DataFrame的列索引和Series的索引没有交集,并且由于广播的效果,使得结果全为NaN,如果我们想在列上广播,必须使用算术方法,并指定轴。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-bceff91cf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&168& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-bceff91cf_r.jpg&&&/figure&&h2&ufuncs和统计函数&/h2&&p&&b&ufuncs&/b&同样可以用于操作&b&pandas&/b&对象,用法和&b&numpy&/b&一样,这里就重复介绍了。但是还有一种操作就是:将函数应用到各列或各行的一维数组上,DataFrame的apply方法可以实现此功能&/p&&figure&&img src=&https://pic1.zhimg.com/v2-881e73e46e0f5f5a6c2c3a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&134& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-881e73e46e0f5f5a6c2c3a_r.jpg&&&/figure&&p&其中&b&lambda&/b&表达式是匿名函数,它令代码更加简介,上面的例子把函数f应用到每一列,计算出每一列的极值。除了标量以外,&b&apply&/b&还可以返回&b&Series&/b& 。(返回的值Series与原来的列索引对应变成了DataFrame)&/p&&figure&&img src=&https://pic1.zhimg.com/v2-5c71feed9527ddb2dad25d10a758c48e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&150& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-5c71feed9527ddb2dad25d10a758c48e_r.jpg&&&/figure&&p&此外,Python元素级的方法同样可以使用,这时要借助&b&applymap&/b&,下面这个例子,把DataFrame的元素格式化为浮点数。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-aac1f876701e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&188& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic4.zhimg.com/v2-aac1f876701e_r.jpg&&&/figure&&p&之所以叫&b&applymap&/b&是因为&b&Series&/b&有一个用于应用到元素级函数的&b&map&/b&方法。&/p&&p&pandas 统计方法也与numpy,他们一般都有一下三个参数。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-fe0bb215ca5862d9dcdcb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&672& data-rawheight=&144& class=&origin_image zh-lightbox-thumb& width=&672& data-original=&https://pic3.zhimg.com/v2-fe0bb215ca5862d9dcdcb_r.jpg&&&/figure&&p&由上表我们可以知道,&b&pandas&/b&的统计函数会自动忽略缺失值,除非整个行或列都是缺失值,否则,结果不为缺失值。&/p&&p&pandas除了常见的求最大最小值(位置索引),累加和等等外,也有一些&b&numpy&/b&中没有的统计函数。如&b&count()&/b&用于计算缺失值的个数,还有用于计算百分数变化的&b&pct_change()&/b&等等,在这里就不一一描述,使用前看一下官方文档即可。&/p&&h2&排序排名&/h2&&p&pandas既可以对索引排序也能对数据排序,对索引排序的时候使用实例方法&b&.sort_index()&/b& ,他可以通过指定&b&axis&/b&来选择沿那一条轴进行排序(&b&0&/b&对应行,&b&1&/b&对应列)。排序默认为升序,若想按降序排序令&b&ascending = False &/b&即可。&/p&&p&若想对数据排序,则会用到&b&order&/b&方法,若想按照莫一列进行排序指定 &b&by&/b& = 对应的列索引即可。注意缺失值在排序时都会放在最后。&/p&&p&pandas 有一个类似argsort的方法,他返回各个元素的大小排名,但与argsort不同的时,rank方法存在破坏平级关系的规则,默认情况下两个具有相同值的排名为它们组的平均排名。例如&/p&&figure&&img src=&https://pic1.zhimg.com/v2-1d070aca3a62b2f7839dcd0fbf2fc9e8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&168& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-1d070aca3a62b2f7839dcd0fbf2fc9e8_r.jpg&&&/figure&&p&总共有7个数据,如果按顺序排两个7肯定占据第6,7位,因此他们的平均排名为6.5。当然我们可以指定&b&method&/b&参数打破这种平级关系,如果我们指定&b&method&/b&为&b&'first'&/b&,那么先出现的元素的排名会靠前。还有其他的选项分别为&b&'min','max'。&/b&&/p&&h2&唯一值、值计数、以及成员资格&/h2&&p&在面对大量数据的时候,我们要想方设法的从Series的取值中抽取信息。如果,我们想的得到Series的唯一值数组我们可以使用&b&unqiue&/b&函数&/p&&figure&&img src=&https://pic2.zhimg.com/v2-a64dad755b199ba79739_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&66& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-a64dad755b199ba79739_r.jpg&&&/figure&&p&注意&b&unique&/b&函数返回的是未排序序列。&/p&&p&我们也常常会用到value_count()方法,对某一列或某一行统计取值出现的频率并默认按降序排序返回。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-be3c53a1ce28b9f766a618_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&117& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic4.zhimg.com/v2-be3c53a1ce28b9f766a618_r.jpg&&&/figure&&p&索引标签为唯一值数组,并且按照降序排序,对应的值为标签的频率。&/p&&p&我们还有isin()方法用于判断矢量化集合的成员资格图,它可以返回与对象相同尺寸但取值为布尔值的对象,常用于数据选择&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-302e1da660aeea62f54baf79f26d89c1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&144& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic2.zhimg.com/v2-302e1da660aeea62f54baf79f26d89c1_r.jpg&&&/figure&&figure&&img src=&https://pic4.zhimg.com/v2-94c37f9befd368e7195e48_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&87& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic4.zhimg.com/v2-94c37f9befd368e7195e48_r.jpg&&&/figure&&h2&缺失值处理&/h2&&p&缺失数据在正常数据集是非常常见的,为了让数据集能够正常的使用,使用前都会对缺失值进行处理,pandas提供&b&isnull()&/b&方法返回一个含有布尔值的对象。&b&notnull&/b&是&b&isnull&/b&的否定式。&/p&&h2&缺失值处理之滤去缺失值&/h2&&p&我们可以使用&b&dropna&/b&方法去删除缺失值。但删除对于&b&DataFrame&/b&来说就复杂很多,你有可能只希望丢弃全为&b&NaN&/b&的行或列,或者只要有缺失值,你都要将某行或某列丢弃。&b&dropna&/b&默认丢弃含有缺失值的行,我们可以设置&b&axis&/b&使得我们可以丢弃含有缺失值的某列。如果我们只想丢弃全为&b&NaN&/b&的行,设置&b&how = ‘all’&/b&即可。&/p&&h2&缺失值处理之填充缺失值&/h2&&p&我们可以使用&b&fillna&/b&去填充缺失利用值而不是丢弃它,因为有些时候,数据是十分稀有宝贵的,要尽量重复利用,所以尽可能的去填充合理的值,例如(众数、均值等等)。&b&fillna&/b&可以接受标量也可以接受字典,通过字典(列索引为键),就可以实现对不同的列填充更合理的值。fillna默认返回新对象,令&b&inplace
= False&/b&可以让对象就地修改。&/p&&p&另外&b&fillna&/b&可以使用插值的方式填充数据,默认为'&b&ffill&/b&',这里的用法和&b&reindex&/b&的填充是一样的。&/p&&blockquote&本文首发于公众号「AI遇见机器学习」,更多干货可搜索[mltoai]或直接公众号名,欢迎关注!&/blockquote&
本文继续给大家总结pandas相关知识,标题如下:索引、选取、和过滤算术运算与数据对齐算术方法ufuncs和统计函数排序排名唯一值、值计数、以及成员资格缺失值处理缺失值处理之过滤缺失值缺失值处理之填充缺失值索引、选取、和过滤Series的索引和numpy类似,…
&p&为了设计出极佳用户体验的产品,找到更低成本、更高效的转化用户的空间,我们会通过用户行为,将用户的一切需求数据化、可视化,围绕用户需求进行价值挖掘。好的数据模型将比用户更加了解自己。&/p&&p&下面我们以白条产品为例,阐述如何通过关注&b&点击率、页面流失率、渠道转化率、K因子&/b&这四个方面的数据,分析用户行为并找到运营用户的方向。&/p&&h2&&b&点击率&/b&&/h2&&p&点击率 = 点击数 / 曝光数。点击率越高说明曝光出来的东西对用户吸引力越大。&/p&&p&拿白条某拉新流程举例:&/p&&p&在收银台页面,未选中白条支付之前,白条的优惠点是隐藏的,因此用户注意力会集中到银行卡支付中的“618单单减”优惠,“白条支付”移动端收银台的注意力热度就较低了。在移动端如此小的页面上如何突出重点抓住用户眼球极为重要。&/p&&p&白条支付的优惠点露出后,“白条支付”的点击率提升明显。从该渠道激活量绝对值来看,激活量提升了一倍左右。&/p&&p&&b&在用户碎片化的时间内给用户关键有效信息,提高用户点击率,给用户一键开通的极致体验,显得极为重要。&/b&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-677f7e5f67cd6aaf8f27dabafe7897d1_b.jpg& data-rawwidth=&826& data-rawheight=&636& class=&origin_image zh-lightbox-thumb& width=&826& data-original=&https://pic2.zhimg.com/v2-677f7e5f67cd6aaf8f27dabafe7897d1_r.jpg&&&/figure&&p&&br&&/p&&h2&&b&页面流失率&/b&&/h2&&p&页面流失率 = 页面未操作用户 / 页面访问用户。&/p&&p&&b&页面流失率一般是由于用户不理解页面内容、页面关键信息未突出、页面布局不合理、流程存在死点导致。&/b&&/p&&p&以白条某激活流程为例:&/p&&p&下图这个页面,UV到点击“立即激活”按钮的流失量较高。这个环节流失严重说明了一个问题:用户对页面概念的理解有歧义,同时这个页面有一定的用户教育成本。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-b3dcfeaa8e0940_b.jpg& data-rawwidth=&281& data-rawheight=&500& class=&content_image& width=&281&&&/figure&&p&我们做了用户回访后发现,看到这个页面后,用户第一理解是存入小金库的钱变成了“死钱”,不愿意继续操作。这个页面并没有很好的传达到存入京东小金库的钱“即取即到账”,同时还有收益这一利益点。&/p&&p&所以我们接下来需要考虑的运营方向就是:&b&如何包装通俗易懂的概念,突出相关利益点,让普通用户易于理解和接受,减少此页面流失率,提升用户体验,给用户简单有效的信息吸引用户继续走下去。 &/b&&/p&&p&以另一白条激活流程为例:&/p&&p&在某激活页面中,需要用户填写身份证及手机验证码。但出现用户身份证号、手机号被占用的问题时,页面未给出合理的处置方式,因此该类客诉较多。&/p&&p&这牵涉到实名与京东账户的一一对应及手机号与京东账号的一一对应问题,之前我们会通过客服工单方式解决,处理方案较为繁琐。为了更好的用户体验,目前我们在跟金融实名做换绑、解绑实名方案及账户体系方案。&/p&&p&由此可看出:&b&页面及流程设计要有闭环的概念&/b&。如果页面要进行手机号验证码验证,就要有“收不到验证码”的处理方案;页面要进行支付密码验证,就要有“忘记支付密码”的处理方案等。&/p&&p&优化后的页面如下图所示:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-a5cde661d34_b.jpg& data-rawwidth=&321& data-rawheight=&572& class=&content_image& width=&321&&&/figure&&p&&br&&/p&&h2&&b&渠道转化率&/b&&/h2&&p&渠道转化率 = 渠道成功量 / 渠道申请量。&/p&&p&目前平台合作将转化率和成本结合,衍生出CPM、CPC、CPS等模式。我们可以结合白条内部预授信情况,离线计算与合作平台的用户重合度及活跃用户重合度,从而选择合作模式。&/p&&ol&&li&如果活跃用户重合度不大,而用户重合度较大,可以采用CPC模式(按点击付费)合作。&/li&&li&如果活跃用户重合度不大,而用户重合度也不大,可以采用CPA模式(按激活量付费)合作。&/li&&/ol&&p&&b&如何结合平台做适当的激活流程改造,优化体验提升转化率是运营工作中极为重要的一部分。&/b&&/p&&p&当需要用户下载金融APP才可激活白条时,渠道转化率会比较低,用户体验较差。针对这个问题,我们正在开发不依托于金融APP的激活流程。同时针对CPC模式,我们优化了展示逻辑——预授信用户才展示激活入口。&/p&&p&另外,渠道转化率也跟具体流程有关。有时需要在&b&同一个渠道上针对不同用户做专属流程设计&/b&。&/p&&p&比如,通过量化不同群体的用户到达下一个页面的信息采集量、操作意愿、操作时长等,根据不同客群做类似Dijkstra(迪杰斯特拉)算法的分析,让不同客群花费最小的精力达到用户的预期。以某激活流程为例,针对用户群体做精准划分,做了专属的激活流程后,整体的激活转化率有所提升。&/p&&h2&K因子&/h2&&p&K因子指每位用户平均向多少用户发出邀请,发出的邀请又有多少有效的转化率,即每个用户带来的新用户量。&/p&&p&当然,K因子属于一个复合指标,里面包含了很多可以量化的单因子指标。当K因子足够大时,就会产生爆发效应,用户量会指数般增长,如雪球一样达到类似口碑的自传播效果。病毒营销即是迎合了用户特定场景下的思维方式和行为模式而获得极佳用户体验的呈现。&/p&&p&在我们以往做过的“邀请好友开白条”的活动中,就是由于K因子不够大,活动效果始终呈衰减效应,而且每个人邀请的转化人数也是大体上呈现二八效应。&/p&&p&通过分析得知,当活动中的邀请者有丰厚的邀请奖励的时候,用户发出的邀请人数越多;被邀请人有丰厚的奖励时,被邀请人的转化率也是增加的。因此,为了提高邀请效果和用户体验,我们需要进行邀请流程分析(对邀请人、被邀请人进行特性分析)和页面交互分析。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-082dc7e4db2de15ebd93_b.jpg& data-rawwidth=&430& data-rawheight=&384& class=&origin_image zh-lightbox-thumb& width=&430& data-original=&https://pic4.zhimg.com/v2-082dc7e4db2de15ebd93_r.jpg&&&/figure&&p&&br&&/p&&h2&&b&总结&/b&&/h2&&p&从不同的用户数据维度可以分析出用户体验问题。&/p&&p&其中最简单的方法就是从客诉规模情况找到重点体验问题。而未引起客诉的体验问题可以通过各种监控数据来发现。例如,页面停留时间、页面流失率等可以真实反馈用户可否真实获取页面上的关键信息。目前白条激活模块大大小小步骤均布置了转化率等监控,可以通过数据总结出需求,做页面及流程等的体验的优化。&/p&&p&总之,很多用户体验问题是可以量化的,如何通过关注科学的数据指标去衡量用户体验好坏是一件值得我们一直探索下去的事情。&/p&&p&&br&&/p&&p&&b&本文作者:京东金融公司产品经理 张清翔&/b&&/p&
为了设计出极佳用户体验的产品,找到更低成本、更高效的转化用户的空间,我们会通过用户行为,将用户的一切需求数据化、可视化,围绕用户需求进行价值挖掘。好的数据模型将比用户更加了解自己。下面我们以白条产品为例,阐述如何通过关注点击率、页面流失率…
&p&记录一下自己用Python做的一个智联招聘爬虫+数据分析和可视化项目。项目是保存在本地的个人博客系统上,觉得还是有必要转载出来,1来记录,2来娱乐下大家,大家看看就好^_^&/p&&p&文章在个人知乎专栏上,欢迎Python初学者关注,大家共同进步~&/p&&a href=&https://zhuanlan.zhihu.com/c_& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic4.zhimg.com/v2-3fab0055722ffbaaf16f7_ipico.jpg& data-image-width=&200& data-image-height=&200& class=&internal&&Java、Python和数据分析&/a&&hr&&h2&&b&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&智联Python相关职位的数据分析及可视化-Pandas&Matplotlib篇&/a&&/b&&/h2&&p&,by—&a href=&//link.zhihu.com/?target=http%3A//localhost%3A8080/Lyon/foreviewSingleBlog%3FId%3D12%23& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&阳光流淌&/a&&/p&&h2&上一篇,我用了Excel对爬虫采集到的智联招聘数据进行了数据分析及可视化,用到软件是Excel, 这一篇,我们打算完全用Python来做同样的事。用到的库有Pandas、Matplotlib。np、pd、plt分别是numpy、pandas、matplotlib.pyplot的常用缩写。&/h2&&p&Numpy(Numerical Python的简称)是Python科学计算的基础包。它提供了以下功能:&/p&&ol&&li&快速高效的多维数组对象ndarray。&/li&&li&用于对数组执行元素级计算以及直接对数组执行数学运算的函数。&/li&&li&用于读写硬盘上基于数组的数据集的工具。&/li&&li&线性代数运算、傅里叶变换,以及随机数生成。&/li&&li&用于将C、C++、Fortran代码集成到Python的工具。&/li&&/ol&&p&除了为Python提供快速的数组处理能力,Numpy在数据分析方面还有另外一个主要作用,即作为在算法之间传递数据的容器。对于数值型数据,Numpy数组在存储和处理数据时要比内置的Python数据结构高效的多。此外,由低级语言(比如C和Fortran)编写的库可以直接操作Numpy数组中的数据,无需进行任何数据复制工作。&br&&/p&&p&Pandas这个名字本身源于panel data(面板数据,这是计量经济学中关于多维结构化数据集的一个术语)以及Python data analysis。pandas提供了使我们能够快速便捷地处理结构化数据的大量数据结构和函数。Pandas中用的最多的是DataFrame,它是一个面向列的二维表结构,且含有行标和列标。pandas兼具numpy高性能的数组计算功能以及电子表格和关系型数据库(如SQL)灵活的数据处理功能。它提供了复杂精细的索引功能,以便更为便捷地完成重塑、切片和切块、聚合以及选取数据子集等操作。&/p&&p&Matplotlib是Python中常用的可视化绘图库,可以通过简单的几行代码生成直方图,功率谱,条形图,错误图,散点图等。Seaborn、ggplot、等诸多Python可视化库均是在此基础上开发的,所以学会matplotlib的基础操作还是很有必要的!它和Ipython结合的很好,提供了一种非常好用的交互式数据绘图环境。绘制的图表也是交互式的,你可以利用绘图窗口中的工具栏放大图表中的某个区域或对整个图表进行平移浏览。&/p&&h2&数据来源:&/h2&&p&Python爬虫爬取了智联招聘关键词:【Python】、全国30个主要城市的搜索结果,总职位条数:18326条(行),其中包括【职位月薪】、【公司链接】、【工作地点】、 【岗位职责描述】等14个字段列,和一个索引列【ZL_Job_id】共计15列。数据存储在本地MySql服务器上,从服务器上导出json格式的文件,再用Python进行数据读取分析和可视化。&/p&&h2&数据简单清洗:&/h2&&p&1.首先在终端中打开输入ipython --pylab。在Ipython的shell界面里导入常用的包numpy、pandas、matplotlib.pyplot。用pandas的read_json()方法读取json文件,并转化为用df命名的DataFrame格式文件。(DataFrame格式是Pandas中非常常用且重要的一种数据存储格式、类似于Mysql和Excel中的表。)&/p&&div class=&highlight&&&pre&&code class=&language-text&&import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_json('/Users/zhaoluyang/Desktop/Python_全国JSON.json')
#查看df的信息
df.columns
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-f679fb1c4acba7ebdfbd697a9fedcf10_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&884& data-rawheight=&789& class=&origin_image zh-lightbox-thumb& width=&884& data-original=&https://pic1.zhimg.com/v2-f679fb1c4acba7ebdfbd697a9fedcf10_r.jpg&&&/figure&&p&可以看到读取的df格式文件共有15列,18326行,pandas默认分配了索引值从0~18325。还有一点值得注意的:全部的15列都有18326个非空值,因为当初写爬虫代码时设置了, 如果是空值,譬如:有一条招聘信息其中【福利标签】空着没写,那么就用字符串代替,如“found no element”。&/p&&p&2.读取JSON文件时pandas默认分配了从0开始的索引,由于文件'ZL_Job_id'列中自带索引,故将其替换!替换后,用sort_index()给索引重新排列。&/p&&div class=&highlight&&&pre&&code class=&language-text&&df.index = df['ZL_Job_id']#索引列用'ZL_Job_id'列替换。
del(df['ZL_Job_id'])#删除原文件中'ZL_Job_id'列。
df_sort = df.sort_index()#给索引列重新排序。
df = df_sort
df[['工作地点','职位月薪']].head(10)
&/code&&/pre&&/div&&figure&&img src=&https://pic2.zhimg.com/v2-0b51bcac2a25a476cc795_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&492& data-rawheight=&286& class=&origin_image zh-lightbox-thumb& width=&492& data-original=&https://pic2.zhimg.com/v2-0b51bcac2a25a476cc795_r.jpg&&&/figure&&p&&br&&/p&&p&3.下面,将进行【职位月薪】列的分列操作,新增三列【bottom】、【top】、【average】分别存放最低月薪、最高月薪和平均月薪。 其中try语句执行的是绝大多数情况:职位月薪格式如:元/月,为此需要对【职位月薪】列用正则表达式逐个处理,并存放至三个新列中。 处理后bottom = 8000,top = 10000,average = 9000. 其中不同语句用于处理不同的情况,譬如【职位月薪】=‘面议’、‘found no element’等。对于字符形式的‘面议’、‘found no element’ 处理后保持原字符不变,即bottom = top = average = 职位月薪。&br&q1,q2,q3,q4用来统计各个语句执行次数.其中q1统计职位月薪形如‘元/月’的次数;q2统计形如月收入‘10000元/月以下’;q3代表其他情况如‘found no element’,‘面议’的次数;q4统计失败的特殊情况。&/p&&div class=&highlight&&&pre&&code class=&language-text&&import re
df['bottom'] = df['top'] = df['average'] = df['职位月薪']
pattern = re.compile('([0-9]+)')
q1=q2=q3=q4=0
for i in range(len(df['职位月薪'])):
item = df['职位月薪'].iloc[i].strip()
result = re.findall(pattern,item)
if result:
#此语句执行成功则表示result[0],result[1]都存在,即职位月薪形如‘元/月’
df['bottom'].iloc[i],df['top'].iloc[i] = result[0],result[1]
df['average'].iloc[i] = str((int(result[0])+int(result[1]))/2)
#此语句执行成功则表示result[0]存在,result[1]不存在,职位月薪形如‘10000元/月以下’
df['bottom'].iloc[i] = df['top'].iloc[i] = result[0]
df['average'].iloc[i] = str((int(result[0])+int(result[0]))/2)
#此语句执行成功则表示【职位月薪】中并无数字形式存在,可能是‘面议’、‘found no element’
df['bottom'].iloc[i] = df['top'].iloc[i] = df['average'].iloc[i] = item
except Exception as e:
print(q4,item,repr(e))
for i in range(100):#测试一下看看职位月薪和bottom、top是否对的上号
print(df.iloc[i][['职位月薪','bottom','top','average']])#或者df[['职位月薪','bottom','top','average']].iloc[i]也可
df[['职位月薪','bottom','top','average']].head(10)
&/code&&/pre&&/div&&figure&&img src=&https://pic4.zhimg.com/v2-f17c93681d3dfae5e4f3_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&595& data-rawheight=&516& class=&origin_image zh-lightbox-thumb& width=&595& data-original=&https://pic4.zhimg.com/v2-f17c93681d3dfae5e4f3_r.jpg&&&/figure&&p&经过检查,可以发现【职位月薪】和新增的bottom、top、average列是能对的上。其中形如‘元/月’的有16905条、形如‘10000元以下’的 有61条、'found no element'和'面议'加起来有1360条,总数18326条,可见是正确的。&/p&&p&4.进行【工作地点】列的处理,新增【工作城市】列,将工作地点中如‘苏州-姑苏区’、‘苏州-工业园区’等统统转化为‘苏州’存放在【工作城市】列。&/p&&div class=&highlight&&&pre&&code class=&language-text&&df['工作城市'] = df['工作地点']
pattern2 = re.compile('(.*?)(\-)')
df_city = df['工作地点'].copy()
for i in range(len(df_city)):
item = df_city.iloc[i].strip()
result = re.search(pattern2,item)
if result:
print(result.group(1).strip())
df_city.iloc[i] = result.group(1).strip()
print(item.strip())
df_city.iloc[i] = item.strip()
df['工作城市'] = df_city
df[['工作地点','工作城市']].head(20)
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-fbfd69ce7d8dd39ce8ecf23a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&448& data-rawheight=&451& class=&origin_image zh-lightbox-thumb& width=&448& data-original=&https://pic1.zhimg.com/v2-fbfd69ce7d8dd39ce8ecf23a_r.jpg&&&/figure&&p&检查一下,没有错误,可以进行下一步的操作了!&/p&&h2&数据分析和可视化&/h2&&p&
从可读性来看,应该是先进行数据清洗,然后进行分析及可视化,但是实际过程中,往往是交织在一起的, 所有下面让我们一步步来,完成所有的清洗、分析和可视化工作。除了具体的公司和职位名称以外,我们还比较关心几个关键词: 平均月薪、工作经验、工作城市、最低学历和岗位职责描述,这里岗位职责描述以后会用python分词做词云图,所以目前筛选出 【平均月薪】、【工作经验】、【工作城市】、【最低学历】这四个标签,这些标签可以两两组合产生各种数据。譬如我想知道各个城市的招聘数量分布情况, 会不会大部分的工作机会都集中在北上广深?是不是北上广深的平均工资也高于其他城市?我想知道Python这个关键词的18000多条招聘数据中 对学历的要求和对工作经验的要求,以及它们分别占比多少?我还想知道平均月薪和工作经验的关系?最低学历和平均月薪的关系? 和上一篇(Execel篇)类似,不同的是,这次我们完全用Python实现同样的操作。&/p&&h2&1.各个城市职位数量及分布&/h2&&p&根据猜想,北上广深,一定占据了Python这个关键词下大部分的工作机会,会不会符合28定律?20%的城市占据了80%的岗位? 有可能!我们先用df.工作城市.value_counts()看一下究竟有多少个城市,以及他们各自有多少条工作数据?&/p&&div class=&highlight&&&pre&&code class=&language-text&&df.工作城市.value_counts()#等价于df['工作城市'].value_counts()
#再用count()来看一下统计出来的城市数量
df.工作城市.value_counts().count()
type(df.工作城市.value_counts())#用type()查看下类型。
&/code&&/pre&&/div&&figure&&img src=&https://pic3.zhimg.com/v2-23cf8b0e216e0a9aa76b6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&448& data-rawheight=&578& class=&origin_image zh-lightbox-thumb& width=&448& data-original=&https://pic3.zhimg.com/v2-23cf8b0e216e0a9aa76b6_r.jpg&&&/figure&&p&可以看到,明明设置的是搜索30个城市,怎么变成了40?像延边、珲春、白山。。。。是什么鬼?想了一下,这些城市是搜索关键词城市‘吉林市’时,自动冒出来的;还有95个‘found no element’,是这些职位链接本身就没有填写工作城市,为了避免干扰,要把他们统统替换成空值。用df_工作城市 = df['工作城市'].replace()&/p&&div class=&highlight&&&pre&&code class=&language-text&&#将原来df['工作城市']列中选定的字段替换成空值nan
df_工作城市 = df['工作城市'].replace(['found no element','松原','辽源','珲春','白山','公主岭','白城','延边','四平','通化'],np.nan)
#查看替换后各个城市职位计数
df_工作城市.value_counts()
#查看替换后城市所包含的职位总数;查看替换后的城市数量,是否等于30.
df_工作城市
#将新的[df_工作城市]列添加到df表中,留作备用
df['df_工作城市'] = df_工作城市
&/code&&/pre&&/div&&figure&&img src=&https://pic4.zhimg.com/v2-aceb11ca737afb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&828& data-rawheight=&718& class=&origin_image zh-lightbox-thumb& width=&828& data-original=&https://pic4.zhimg.com/v2-aceb11ca737afb_r.jpg&&&/figure&&p&看了一下,没有问题,现在df_工作城市中筛选出了30个城市,合计18211条职位数据。 为了数据完整性,df表保持原样,我们用df_工作城市直接操作,进行下一步的可视化。先直接上代码和图,再一一解释下。&/p&&div class=&highlight&&&pre&&code class=&language-text&&fig1 = plt.figure(1,facecolor = 'black')#设置视图画布1
ax1 = fig1.add_subplot(2,1,1,facecolor='#4f4f4f',alpha=0.3)#在视图1中设置子图1,背景色灰色,透明度0.3(figure.add_subplot 和plt.suplot都行)
plt.tick_params(colors='white')#设置轴的颜色为白色
df_工作城市.value_counts().plot(kind='bar',rot=0,color='#ef9d9a')#画直方图图
#设置图标题,x和y轴标题
title = plt.title('城市——职位数分布图',fontsize=18,color='yellow')#设置标题
xlabel = plt.xlabel('城市',fontsize=14,color='yellow')#设置X轴轴标题
ylabel = plt.ylabel('职位数量',fontsize=14,color='yellow')#设置Y轴轴标题
#设置说明,位置在图的右上角
text1 = ax1.text(25,4500,'城市总数:30(个)',fontsize=12, color='cyan')#设置说明,位置在图的右上角
text2 = ax1.text(25,4000,'职位总数:18326(条)',fontsize=12, color='cyan')
text3 = ax1.text(25,3500,'有效职位:18211(条)',fontsize=12, color='red')
#添加每一个城市的坐标值
for i in range(len(list_1)):
ax1.text(i-0.3,list_1[i],str(list_1[i]),color='yellow')
#可以用plt.grid(True)添加栅格线
#可以用下面语句添加注释箭头。指向上海,xy为坐标值、xytext为注释坐标值,facecolor为箭头颜色。
#arrow = plt.annotate('职位数:3107', xy=(1,3107), xytext=(3, 4000),color='blue',arrowprops=dict(facecolor='blue', shrink=0.05))
ax2 = fig1.add_subplot(2,1,2)#设置子图2,是位于子图1下面的饼状图
#为了方便,显示前8个城市的城市名称和比例、其余的不显示,用空字符列表替代,为此需要构造列表label_list和一个空字符列表['']*23。
x = df_工作城市.value_counts().values#x是数值列表,pie图的比例根据数值占整体的比例而划分
label_list = []#label_list是构造的列表,装的是前8个城市的名称+职位占比。
for i in range(8):
t = df_工作城市.value_counts().values[i]/df_工作城市.value_counts().sum()*100
city = df_工作城市.value_counts().index[i]
percent = str('%.1f%%'%t)
label_list.append(city+percent)
#labels参数原本是与数值对应的标签列表,此处30个城市过多,所以只取了前8个城市显示。
#explode即饼图中分裂的效果explode=(0.1,1,1,。。)表示第一块图片显示为分裂效果
labels = label_list + ['']*22
explode = tuple([0.1]+[0]*29)
plt.pie(x,explode=explode,labels=labels,textprops={'color':'yellow'})
#可加参数autopct='%1.1f%%'来显示饼图中每一块的比例,但是此处30个城市,如果全显示的话会非常拥挤不美观,所以只能手动通过labels参数来构造。
#若要显示标准圆形,可以添加:plt.axis('equal')
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-131ba26ddec_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1266& data-rawheight=&737& class=&origin_image zh-lightbox-thumb& width=&1266& data-original=&https://pic1.zhimg.com/v2-131ba26ddec_r.jpg&&&/figure&&p&可以看见,这个曲线下降的弧度还是挺美的,北上深杭广5个城市占据了超过60%以上的职位数。其中北京当之无愧的占据了四分之一的Python工作数量,不愧为帝都。 上海以3107条职位排名第二,可见上海虽然经济超越北京,在互联网环境和工作机遇方面还需努力!深圳作为中国的科技中心,排名第三我是没疑问的,杭州竟然超过广州排名第四!不过也可以想到,阿里巴巴、百草味等等电商产业带动了整个杭州的互联网文化!&br&【北上深杭广】+成都、南京、郑州,这8个城市占据了全国30座城市中,近80%的工作机会!剩下的22个城市合起来只占据了20%,果然,是基本符合28定律的。。。&/p&&h2&2.工作经验-职位数量及分布&/h2&&p&Python虽然是一名比较老的语言,但是在人们的印象中火起来也就最近几年,Python相关的工作对于【工作经验】是怎样要求的呢?让我们来看看!&/p&&div class=&highlight&&&pre&&code class=&language-text&&df.工作经验.value_counts()#统计【工作经验】下各个字段的累计和
&/code&&/pre&&/div&&figure&&img src=&https://pic2.zhimg.com/v2-f7cd3ef51e5d6341a60dd_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&358& data-rawheight=&313& class=&content_image& width=&358&&&/figure&&p&&br&&/p&&p&可以看见出现了一些很数字少量的字段譬如“5年以上”,“2年以上”,“1-2年”,“1年以上”等,这些标签下职位的数量都在10以内,不太具备统计意义,所以我们作图的时候不想让他们出现,必须筛选掉。 下面我们还是通过同样的步骤来清除掉此类数据。&/p&&div class=&highlight&&&pre&&code class=&language-text&&= df['工作经验'].replace(['found no element','3年以上','1年以上','5年以上','2年以上','1-2年'],np.nan)
df_工作经验.value_counts()
df_工作经验.value_counts().sum()
&/code&&/pre&&/div&&figure&&img src=&https://pic2.zhimg.com/v2-da9ddb13ff92e87081aed5_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&814& data-rawheight=&353& class=&origin_image zh-lightbox-thumb& width=&814& data-original=&https://pic2.zhimg.com/v2-da9ddb13ff92e87081aed5_r.jpg&&&/figure&&p&&br&&/p&&p&现在,可以进行下一步可视化了,还是做2张图:直方图和饼图。通过这两张图可以直观地看到这么多职位中对不同工作经验的要求占比,好做到心里有数!&/p&&div class=&highlight&&&pre&&code class=&language-text&&fig2 = plt.figure(2,facecolor = 'black')
ax2_1 = fig2.add_subplot(2,1,1,facecolor='#4f4f4f',alpha=0.3)
plt.tick_params(colors='white')
df_工作经验.value_counts().plot(kind = 'bar',rot = 0,color='#7fc8ff')
title = plt.title('工作经验——职位数分布图',fontsize = 18,color = 'yellow')
xlabel = plt.xlabel('工作经验',fontsize = 14,color = 'yellow')
ylabel = plt.ylabel('职位数量',fontsize = 14,color = 'yellow')
plt.grid(True)
text1_ = ax2_1.text(5,5600,'城市总数:30(个)',fontsize=12, color='yellow')
text2 = ax2_1.text(5,4850,'职位总数:18326(条)',fontsize=12, color='yellow')
text3 = ax2_1.text(5,4100,'有效职位:18215(条)',fontsize=12, color='cyan')
&/code&&/pre&&/div&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&#设置子图2,是位于子图1下面的饼状图
ax2_2 = fig2.add_subplot(2,1,2)
#x是数值列表,pie图的比例根据数值占整体的比例而划分
x2 = df_工作经验.value_counts().values
labels = list(df_工作经验.value_counts().index[:5])+ ['']*2
explode = tuple([0.1,0.1,0.1,0.1,0.1,0.1,0.1])
plt.pie(x2,explode=explode,labels=labels,autopct='%1.1f%%',textprops={'color':'yellow'})
plt.axis('equal')#显示为等比例圆形
#设置图例,方位为右下角
legend = ax2_2.legend(loc='lower right',shadow=True,fontsize=12,edgecolor='cyan')
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-2c9de5eb33d9a655cb85c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1268& data-rawheight=&740& class=&origin_image zh-lightbox-thumb& width=&1268& data-original=&https://pic1.zhimg.com/v2-2c9de5eb33d9a655cb85c_r.jpg&&&/figure&&p&总共得到18215条职位。从直方图里可以明显看出工作机会集中在'不限'、'1-3年'、'3-5年', 其中工作经验要求3年以下的(【无经验】+【不限】+【1年以下】+【1-3年】)合计11501条职位,占比超过63%,看来即使是初入门者,大家的机会也还是有不少的! (PS:最后,在df表中添加一列'df_工作经验',以后筛选时就可以直接用了,df['df_工作经验']=df_工作经验)&/p&&p&&br&&/p&&h2&3.工作经验-平均月薪&/h2&&p&这个嘛,大家闭着眼都能想到!肯定是工作经验越久的拿钱越多了!再猜猜?无经验的和5-10年经验的收入差距有多大?这个,嘿嘿就不好猜了,让我们来看看吧!&/p&&p&1.第一步,要想统计工作经验和平均月薪的关系,那么我们先看看df中对应的列df.工作经验和df.average。之前我们构造了一列df_工作经验,把df.工作经验中几个样本容量小于10的值和‘found no element’全筛选掉了,故df_工作经验还能继续使用。现在,让我们看看df.average的信息。&/p&&div class=&highlight&&&pre&&code class=&language-text&&df.average.value_counts()
&/code&&/pre&&/div&&p&可以看到,其中有1265个值是‘面议’,有95个值是‘found no element’,这些值需要替换成空值,不然会影响下一步工资的计算。&/p&&div class=&highlight&&&pre&&code class=&language-text&&df_平均月薪 = df['average'].replace(['面议','found no element'],np.nan)
&/code&&/pre&&/div&&p&2.好了,第一步的简单数据清洗完成了,我们可以思考下一步了,现在我们想要得到的是不同工作经验字段下的平均月薪&/p&&p&A. 首先我需要把df_工作经验和df_平均月薪这两列元素放在一起,构造一个DataFrame用于存放df_工作经验和df_平均月薪这两列元素,且方便进一步的groupby操作。&br&B. 其次我需要把df_平均月薪列根据df_工作经验进行分组(用groupby),分组后我可以求得df_工作经验下各个字段的月薪的计数、最大值最小值、累加和、平均值等一系列数据。&br&C. 当然此处我只需要平均值。对分组后的grouped用mean()方法,就可以轻松统计分组内各项的平均值了。&/p&&div class=&highlight&&&pre&&code class=&language-text&&df3=pd.DataFrame(data={'工作经验':df['df_工作经验'],'平均月薪':df_平均月薪})
df3.info()
grouped3 = df3['平均月薪'].groupby(df3['工作经验'])
grouped3.mean()
&/code&&/pre&&/div&&figure&&img src=&https://pic3.zhimg.com/v2-b1fb6e906acb1b6c0105aea3cbfc01fa_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&566& data-rawheight=&349& class=&origin_image zh-lightbox-thumb& width=&566& data-original=&https://pic3.zhimg.com/v2-b1fb6e906acb1b6c0105aea3cbfc01fa_r.jpg&&&/figure&&p&&br&&/p&&p&在进行grouped3.mean()时,我们发现报错了:DataError: No numeric types to aggregate,看一下,原来df_平均月薪列里的值都是字符型str,并不是数值型的float,因为前面的步骤没有做好,留下了这个bug,无奈我们需要对值类型做个转换。&/p&&div class=&highlight&&&pre&&code class=&language-text&&#构造一个listi存放转化后float型的‘平均月薪’
pattern = re.compile('([0-9]+)')
listi = []
for i in range(len(df.average)):
item = df.average.iloc[i].strip()
result = re.findall(pattern,item)
if result:
listi.append(float(result[0]))
elif (item.strip()=='found no element' or item.strip()=='面议'):
listi.append(np.nan)
print(item)
except Exception as e:
print(item,type(item),repr(e))
#将df3.平均月薪列替换掉,同时给df新增一列'df_平均月薪'做备用。
df3['平均月薪'] = listi
df['df_平均月薪'] = df3['平均月薪']
#看看更新后的数据是否正确
df3['平均月薪'].value_counts()#统计每个月薪字段的个数
df3['平均月薪'][:10]#查看前10个值
type(df3['平均月薪'][1])#看看现在月薪的类型是不是浮点型
df3['平均月薪'].value_counts().sum()#看看月薪样本总数
df3['平均月薪'].mean()#看看这16966个月薪样本的平均值是多少?
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-46e8184cff84166bd14c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&457& data-rawheight=&438& class=&origin_image zh-lightbox-thumb& width=&457& data-original=&https://pic1.zhimg.com/v2-46e8184cff84166bd14c_r.jpg&&&/figure&&p&可以看到,替换后的df3['平均月薪']值从str变为了可以计算的float,月薪样本总数16966个,样本的平均月薪14197元。好,现在终于OK了,让我们再回到之前的步骤:&/p&&div class=&highlight&&&pre&&code class=&language-text&&grouped3 = df3['平均月薪'].groupby(df3['工作经验'])
grouped3.mean()
&/code&&/pre&&/div&&figure&&img src=&https://pic2.zhimg.com/v2-9dbc4da056a42ba96d731_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&583& data-rawheight=&252& class=&origin_image zh-lightbox-thumb& width=&583& data-original=&https://pic2.zhimg.com/v2-9dbc4da056a42ba96d731_r.jpg&&&/figure&&p&好了,完美,格式对了,数据有了,现在可以来画图了!但是再看看,还不是那么完美,数据大小排列很乱,而且小数点那么多。。。好吧,让我们再简单处理下&/p&&div class=&highlight&&&pre&&code class=&language-text&&#新增一个平均值,即所有非空df3['平均月薪']的平均值
s3 = pd.Series(data = {'平均值':df3['平均月薪'].mean()})
result3 = grouped3.mean().append(s3)
#sort_values()方法可以对值进行排序,默认按照升序,round(1)表示小数点后保留1位小数。
result3.sort_values(ascending=False).round(1)
&/code&&/pre&&/div&&figure&&img src=&https://pic2.zhimg.com/v2-b11bebd440a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&610& data-rawheight=&306& class=&origin_image zh-lightbox-thumb& width=&610& data-original=&https://pic2.zhimg.com/v2-b11bebd440a_r.jpg&&&/figure&&p&&br&&/p&&p&3.数据可视化&/p&&p&这次我们画一个躺倒的柱状图(barh),用ggplot的风格来画。&/p&&div class=&highlight&&&pre&&code class=&language-text&&matplotlib.style.use('ggplot')
fig3 = plt.figure(3,facecolor = 'black')
ax3 = fig3.add_subplot(1,1,1,facecolor='#4f4f4f',alpha=0.3)
result3.sort_values(ascending=False).round(1).plot(kind='barh',rot=0)
#设置标题、x轴、y轴的标签文本
title = plt.title('工作经验——平均月薪分布图',fontsize = 18,color = 'yellow')
xlabel= plt.xlabel('平均月薪',fontsize = 14,color = 'yellow')
ylabel = plt.ylabel('工作经验',fontsize = 14,color = 'yellow')
#添加值标签
list3 = result3.sort_values(ascending=False).values
for i in range(len(list3)):
ax3.text(list3[i],i,str(int(list3[i])),color='yellow')
#设置标识箭头
arrow = plt.annotate('Python平均月薪:14197元/月', xy=(), xytext=(),color='yellow',fontsize=16,arrowprops=dict(facecolor='cyan', shrink=0.05))
#设置图例注释(16966来源:df2['平均月薪'].value_counts().sum())
text= ax3.text(,'月薪样本数:16966(个)',fontsize=16, color='cyan')
#设置轴刻度文字颜色为白色
plt.tick_params(colors='white')
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-76e10a13ebc5d3da79940_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1311& data-rawheight=&797& class=&origin_image zh-lightbox-thumb& width=&1311& data-original=&https://pic1.zhimg.com/v2-76e10a13ebc5d3da79940_r.jpg&&&/figure&&p&&br&&/p&&p&通过图表,我们可以直观地看到,Python关键词下的职位月薪是随着工作经验增长而递增的(这不是说了一句废话么?!囧) 其中【无经验】的平均月薪最低,只有5842,相比之下【10年以上】经验的,平均月薪达到了恐怖的34890,约达到了【无经验】月薪的6倍之多!!! 【1年以下】的平均月薪7579,还勉强凑合,【1-3年】的已经破万了,达到了近12000元/月的水准。最后让我们看看平均值吧,由于‘被平均’的缘故,16966条月薪样本的均值是14197元,有没有让你满意呢?&br&&/p&&h2&4.工作城市-平均月薪&/h2&&p&对了,刚才说到北上广深占据了全国大部分的工作机会,那么北上广深的平均月薪如何呢?会不会也碾压小城市?让我们来看看! 和之前的套路一样,我们还是要构造一个DataFrame,包含两列,一列是【平均月薪】,一列是【工作城市】,然后对df4进行groupby操作,还是很简单的!不过,经过上次的教训,平均月薪一定要是数值型的,str型的计算不了。&/p&&div class=&highlight&&&pre&&code class=&language-text&&#此处df['df_工作城市']是之前经过筛选后的30个城市数据
df4=pd.DataFrame(data={'工作城市':df['df_工作城市'],'平均月薪':df['df_平均月薪']})
df4.info()
grouped4 = df4['平均月薪'].groupby(df4['工作城市'])
grouped4.mean()#查看对30个城市分组后,各个城市月薪的平均值
grouped4.count().sum()#查看对30个城市分组后筛选出的平均月薪样本数
#新增一个平均值,即所有非空df2['平均月薪']的平均值
s4 = pd.Series(data = {'平均值':df['df_平均月薪'].mean()})
result4 = grouped4.mean().append(s4)
#sort_values()方法可以对值进行排序,默认按照升序,round(1)表示小数点后保留1位小数。
result4.sort_values(ascending=False).round(1)
&/code&&/pre&&/div&&figure&&img src=&https://pic3.zhimg.com/v2-3b408c0a6be3e22aeb71a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&551& data-rawheight=&698& class=&origin_image zh-lightbox-thumb& width=&551& data-original=&https://pic3.zhimg.com/v2-3b408c0a6be3e22aeb71a_r.jpg&&&/figure&&figure&&img src=&https://pic1.zhimg.com/v2-bdadef5d979c4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&619& data-rawheight=&706& class=&origin_image zh-lightbox-thumb& width=&619& data-original=&https://pic1.zhimg.com/v2-bdadef5d979c4_r.jpg&&&/figure&&p&数据构造好了,进行下一步,可视化。&/p&&div class=&highlight&&&pre&&code class=&language-text&&#可以通过style.available查看可用的绘图风格,总有一款适合你
matplotlib.style.use('dark_background')
fig4 = plt.figure(4)
ax4 = fig4.add_subplot(1,1,1)#可选facecolor='#4f4f4f',alpha=0.3,设置子图,背景色灰色,透明度0.3
result4.sort_values(ascending=False).round(1).plot(kind='bar',rot=30)#可选color='#ef9d9a'
#设置图标题,x和y轴标题
title = plt.title(u'城市——平均月薪分布图',fontsize=18,color='yellow')#设置标题
xlabel = plt.xlabel(u'城市',fontsize=14,color='yellow')#设置X轴轴标题
ylabel = plt.ylabel(u'平均月薪',fontsize=14,color='yellow')#设置Y轴轴标题
#设置说明,位置在图的右上角
text1 = ax4.text(25,16250,u'城市总数:30(个)',fontsize=12, color='#FF00FF')#设置说明,位置在图的右上角
text2 = ax4.text(25,15100,u'月薪样本数:16946(条)',fontsize=12, color='#FF00FF')
#添加每一个城市的坐标值
list_4 = result4.sort_values(ascending=False).values
for i in range(len(list_4)):
ax4.text(i-0.5,list_4[i],int(list_4[i]),color='yellow')
#设置箭头注释
arrow = plt.annotate(u'全国月薪平均值:14197元/月', xy=(4.5,14197), xytext=(7,15000),color='#9B30FF',fontsize=14,arrowprops=dict(facecolor='#FF00FF', shrink=0.05))
#设置轴刻度文字颜色为粉色
plt.tick_params(colors='pink')
&/code&&/pre&&/div&&figure&&img src=&https://pic4.zhimg.com/v2-5b97b12128fea0cdb97f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1310& data-rawheight=&820& class=&origin_image zh-lightbox-thumb& width=&1310& data-orig}

我要回帖

更多关于 流放之路装备数据库 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信