文章目录
- 第1章 Python基础
- 第2章 金融数据挖掘之爬虫技术基础
- 第3章 金融数据挖掘案例实战1
- 第4章 数据库详解及实战
- 第5章 数据清洗优化及数据评分系统搭建
- 第6章、数据分析利器—NumPy与pandas库
- 第7章 数据可视化与数据相关性分析
- 第8章 金融数据爬虫之爬虫技术进阶
- 第9章 金融数据挖掘案例实战2
- 第10章 通过PDF文本解析上市公司理财公告
- 第11章 邮件提醒系统搭建
- 第12章 基于评级报告的投资决策分析
- 第13章 用Python生成Word文档
- 第14章 基于股票信息及其衍生变量的数据分析
- 第16章 器学习之客户违约预测模型搭建
第1章 Python基础
1、变量、行、缩进与注释
(1)变量
关于变量的命名,尽量用字母a
,b
,c
,a_1
,b_1
等,或者自己创建的字符,不要用系统自带的函数(保留字KEYWORD)来命名。比如说不要用print
来命名,写成print = 1
,这样程序就会凌乱了
(2)行
在Python中,一般来说,很少用逗号或者分号
代码都是一行一行写的,每写完一句,在句尾按Enter键,就可以换行。
(3)缩进
缩进快捷键是Tab键
,按住Shift + Tab
键的话就可以撤销原来的缩进
python严格要求缩进,在if,for,while等语句中都会用到缩进(4个空格)
(4)注释
Python不运行注释,注释在程序中大多是提示解释的作用。
2、数据类型:数字、字符串、字典、列表等
(1)数字int()与字符串str()
使用单或双引号中写出的数字被认为是字符串; 单写的数值会被认为数字,可以使用type()
显示数据类型
注:不同的数据类型是不能相互运算的
TypeError: unsupported operand type(s) for +:‘int’ and ‘str’
数据类型可以改变,如使用int()
把字符串变成数字,使用str()
把数字变成字符串。
列表和字典
(1)列表list()
列表里的元素可以是字符串,也可以是数字,甚至可以是嵌套列表
(a)统计列表的元素个数:函数len()
(b)调取一个列表元素的方法 :[序号],注意到元素序号从0开始算起
(c)选取多个列表元素的方法:[序号1:序号2]
(d)列表增加元素的办法:列表.append( )
(e)列表与字符串之间的转换方法:
- 如果想连接列表里的元素变成一个元素可以使用
",".join(list1)
- 如果想把字符串分开列表:
a.split(" ")
(2)字典dict()
在字典中,每个元素都有两部分(区别于列表中一个元素只有一个部分),前一个部分,称之为键,后一部分称其为值,中间用冒号相连。(键值对)
- (a) 使用键来获取值
dct[key]
- (b) 使用for循环来获取字典里的全部取值
class1 = {'丁一': 85, '王二麻子': 95, '张三': 75, '李四': 65, '赵五': 55}
for i in class1:# 这个i代表的是字典中的键,也就是丁一、王二麻子等
print(i + ':' + str(class1[i])) # 注意要str把85等数字转换成字符串,才能进行字符串拼接
3、 关于Python语言中的各种运算符
(1)算术运算符 (= + − × ÷)
字符串的拼接
(2)比较运算符 (<,>,==)
(3)逻辑运算符 (<,>,==)
4、try\except异常处理语句
try:
主代码
except:
如果主代码出错了,那么执行该代码块
try:
print(1+'a')
except:
print('主代码运行失败')
5、常用函数
(1)len( )
函数
length 的缩写len( ): 意思是长度。
a = '123华小智abcd'
print(len(a))
10
(2)replace()
函数
replace:意思是代替,主要功能是替换你想替换的内容
a ='<em>阿里巴巴</em>电商脱贫成“教材” 累计培训逾万名县域干部'
a = a.replace('<em>','')
a = a.replace('</em>','')
print(a)
阿里巴巴电商脱贫成“教材” 累计培训逾万名县域干部
(3)strip()
函数
strip:意思是剥去一层
删除空白符(包括’换行符\n’和空字符串’ ')
a =' 华能信托2018年上半年行业综合排名位列第5 '
a = a.strip()
print(a)
华能信托2018年上半年行业综合排名位列第5
(4)split()
函数
split():意思是分割字符串
a = '2018年12月12日 08:07'
a = a.split(' ')[0]
print(a)
# 注意,split分割完是一个列表
2018年12月12日
(5)try/except
函数
try/except:意思是试用/除了
避免程序因为哪一步程序出了错而整个程序终止
第2章 金融数据挖掘之爬虫技术基础
1 查看网页源代码的两种方式
(1)F12
浏览器F12的运用,以及如何查看网页源代码
- 选择按钮
- Elements元素按钮
(2)查看网页源代码
2 在Python代码里输入网址
网页识别的是英文字符(复制网址显示),中文需要转码才可以识别,例如:
https://www.baidu.com/s?tn=news&rtt=1&bsst=1&cl=2&wd=阿里巴巴
https://www.baidu.com/s?tn=news&rtt=1&bsst=1&cl=2&wd=%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4
3 网页结构的各种标签
(1)标题<h>
标签
标题是通过<h1> - <h6>
标签来定义的,一般格式为<h1>标题内容</h1>
,其中h1
的字号最大,h6
的字号最小
(2) 段落<p>
标签
段落是通过标签 <p>
来定义的,一般格式为:<p>段落内容</p>
(3) 链接<a>
标签
链接是通过标签 <a>
来定义的,一般格式为:<a href="链接地址">文本内容</a>
如果想在一个新的标签页里打开百度首页,而不是把原网页覆盖的话,只要在“链接地址”的后面加上target=_blank
即可
(4) 其他常用标签
定义表格的标签<table>
定义序号的<li>
标签
定义图片的<img>
标签
定义样式的<script>
标签
4 正则表达式中的贪婪匹配和非贪婪匹配
findall方法
import re
content = 'Hello 123 world 456 华小智Python基础教学135'
result = re.findall('\d\d\d',content) #返回得到所有的数字
print(result)
# 注意获取到的是一个列表
print(result[0])
print(result[1])
print(result[2])
# 更简单的遍历方法,其中len表示列表长度,range(n)表示0到n-1
for i in range(len(result)):
print(result[i])
非贪婪匹配之(.*?)
与.*?
(.*?)的作用就是找到想要的内容,同时不确定它的长度以及格式,但知道它在哪两块内容中间
(.*?)
是用来获取文本A与文本B之间的内容.*?
的作用是表示文本C和文本D之间的内容
# 非贪婪匹配之.*? 实战演练
import re
res = '<h3 class="c-title"><a href="网址" data-click="{一堆英文}"><em>阿里巴巴</em>代码竞赛现全球首位AI评委 能为代码质量打分</a>'
p_title = '<h3 class="c-title">.*?>(.*?)</a>'
title = re.findall(p_title, res)
print(title)
自动考虑换行之修饰符re.S 介绍
import re
res = '''<h3 class="c-title">
<a href="https://baijiahao.baidu.com/s?id=1631161702623128831&wfr=spider&for=pc"
data-click="{
一堆我们不关心的英文
}"
target="_blank"
>
<em>阿里巴巴</em>代码竞赛现全球首位AI评委 能为代码质量打分
</a>
'''
p_href = '<h3 class="c-title">.*?<a href="(.*?)"'
p_title = '<h3 class="c-title">.*?>(.*?)</a>'
href = re.findall(p_href, res, re.S)
title = re.findall(p_title, res, re.S)
print(href)
print(title)
# 清除换行符号
for i in range(len(title)):
title[i] = title[i].strip()
print(title)
小知识点补充
(1)sub()方法:substitute替换
这个方法不仅会去掉<em></em>
,还会去掉其它的<>
里的内容
# 1 re.sub()方法实现批量替换
# 1.1 传统方法-replace()函数
title = ['<em>阿里巴巴</em>代码竞赛现全球首位AI评委 能为代码质量打分']
title[0] = title[0].replace('<em>','')
title[0] = title[0].replace('</em>','')
print(title[0])
# 1.2 re.sub()方法
import re
title = ['<em>阿里巴巴</em>代码竞赛现全球首位AI评委 能为代码质量打分']
title[0] = re.sub('<.*?>', '', title[0])
print(title[0])
# 2 中括号[ ]的用法:使在中括号里的内容不再有特殊含义
company = '*华能信托'
company1 = re.sub('[*]', '', company)
print(company1)
(2)中括号[ ]的用法
5 requests库及其爬虫代码
import requests
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'}
url = 'https://www.baidu.com/s?tn=news&rtt=1&bsst=1&cl=2&wd=阿里巴巴'
res = requests.get(url, headers=headers).text
print(res)
第3章 金融数据挖掘案例实战1
1 数据清洗函数的用法
for i in range(len(title)): # range(len(title)),这里因为知道len(title) = 10,所以也可以写成for i in range(10)
title[i] = title[i].strip() # strip()函数用来取消字符串两端的换行或者空格,不过这里好像不太需要了
title[i] = re.sub('<.*?>', '', title[i]) # 核心,用re.sub()函数来替换不重要的内容
print(str(i + 1) + '.' + title[i])
print(href[i])
2 写模式w
和追加模式a
写入方式 | 含义 |
---|---|
w | 每次新写入数据,都会把原来的数据清除 |
a | 不清除原来数据,在原数据之后写入新内容 |
3 open()函数中“r”的作用
字母前加r表示raw string,也叫原始字符串常量。一般用在一下两个方面:
- 1、正则表达式用于处理正则表达式时,规避反斜杠的转义
- 2、系统路径如下面的路径,使用r就防止了\t的转义
4 代码间隔一定时间运行time.sleep()
import time
time.sleep(10800) #每隔三个小时运行一次
5 一次性爬取多页
(1) 爬取一家公司的多页代码
def baidu(page):
num = page * 10
url = 'https://www.baidu.com/s?tn=news&rtt=4&bsst=1&cl=2&wd=阿里巴巴&pn=' + str(num)
res = requests.get(url, headers=headers).text
6 requests.get里timeout
参数的作用
res = requests.get(url, headers=headers, timeout=10).text
连接服务器,当闲置时间大于接10,则关闭连接。
第4章 数据库详解及实战
1 连接MySQL服务器数据库
- host
- port
- charset
# 连接数据库
import pymysql #引入Pymysql库
db = pymysql.connect(host='localhost', port=3306, user='root',
password='', database='pachong', charset='utf8')
#host:本地主机,port:端口,User:用户名,password:登录密码
#Database:数据库名称,Charset:编码方式
2 数据表基本操作
其中我们最常用到的就是上面的5个功能:浏览、结构、SQL、插入、搜索功能
查(select)
SELECT * FROM `数据表名` WHERE `元素` LIKE `内容`
SELECT * FROM `test` WHERE `company` LIKE `阿里巴巴`
Select*
和Select
的区别
SELECT * From `test` WHERE 1
SELECT `Company` `Title` `href` `Date` `Source` From `test` WHERE 1
select可以选择感兴趣的列
如果有多个查找条件,则可以用AND
来连接多个查找条件
SELECT * FROM `test` WHERE `title` LIKE `标题1` AND `href` = `链接1`
增(insert)
INSERT INTO `数据表名` (`元素1`,`元素2`,`元素3`,`元素4`,`元素5`) VALUES (`内容1`,`内容2`,`内容3`,`内容4`,`内容5`)
如果想多加内容并可以使用一个逗号,
INSERT INTO `test` (`company`,`title`,`href`,`date`,`source`) VALUES (`内容1`,`内容2`,`内容3`,`内容4`,`内容5`),VALUES (`内容1`,`内容2`,`内容3`,`内容4`,`内容5`)
改(update)和删(delete)
DELETE FROM `数据表名` WHERE `元素` = `内容`
DELETE FROM `test` WHERE `company` = `百度`
# 插入数据
cur = db.cursor() # 获取会话指针,用来调用SQL语句,模拟执行SQL语句的固定写法
sql = 'INSERT INTO test(company, title, href, source, date) VALUES (%s, %s, %s, %s, %s)' # 编写SQL语句
cur.execute(sql, (company, title, href, source, date)) # 执行SQL语句
db.commit() # 当改变表结构后,更新数据表的操作
cur.close() # 关闭会话指针
db.close() # 关闭数据库链接
3 在数据库中查找并提取数据
# 1.根据1个条件查找并提取
import pymysql
db = pymysql.connect(host='localhost', port=3306, user='root', password='', database='pachong', charset='utf8')
company = '阿里巴巴'
cur = db.cursor() # 获取会话指针,用来调用SQL语句
sql = 'SELECT * FROM test WHERE company = %s' # 编写SQL语句
cur.execute(sql,company) # 执行SQL语句
data = cur.fetchall() # 提取所有数据,并赋值给data变量
print(data)
db.commit() # 这个其实可以不写,因为没有改变表结构
cur.close() # 关闭会话指针
db.close() # 关闭数据库链接
# 2.根据2个条件查找并提取
import pymysql
db = pymysql.connect(host='localhost', port=3306, user='root', password='',
database='pachong', charset='utf8')
company = '阿里巴巴'
title = '标题1'
cur = db.cursor() # 获取会话指针,用来调用SQL语句
sql = 'SELECT * FROM test WHERE company = %s AND title = %s' # 编写SQL语句
cur.execute(sql, (company, title)) # 执行SQL语句
data = cur.fetchall() # 提取所有数据,并赋值给data变量
print(data)
db.commit() # 这个其实可以不写,因为没有改变表结构
cur.close() # 关闭会话指针
db.close() # 关闭数据库链接
第5章 数据清洗优化及数据评分系统搭建
1 数据清洗和日期格式统一
# 3.数据清洗(参考3.1节)
for i in range(len(title)):
title[i] = title[i].strip() # strip()函数用来取消字符串两端的换行或者空格,不过这里好像不太需要了
title[i] = re.sub('<.*?>', '', title[i]) # 核心,用re.sub()函数来替换不重要的内容
# 统一日期格式(参考5.1节)
date[i] = date[i].split(' ')[0]
date[i] = re.sub('年', '-', date[i])
date[i] = re.sub('月', '-', date[i])
date[i] = re.sub('日', '', date[i])
if ('小时' in date[i]) or ('分钟' in date[i]):
date[i] = time.strftime("%Y-%m-%d")
else:
date[i] = date[i]
# 4.正文爬取及数据深度清洗(参考5.1和5.2和5.3节)
for i in range(len(title)):
# 获取新闻正文
try:
article = requests.get(href[i], headers=headers, timeout=10).text
except:
article = '单个新闻爬取失败'
# 数据深度清洗
company_re = company[0] + '.{0,5}' + company[-1] # 匹配“公司名称第一个字 + 0到5个任意字符 + 公司最后一个字”这样的匹配规则
# 根据新闻正文内容进行数据深度过滤
if len(re.findall(company_re, article)) < 1: #检验公司名称是否在正文里
title[i] = ''
source[i] = ''
href[i] = ''
date[i] = ''
while '' in title:
title.remove('')
while '' in href:
href.remove('')
while '' in date:
date.remove('')
while '' in source:
source.remove('')
2 数据乱码:encode
函数和decode
函数的作用和用法
Python获取的源代码的编码方式为ISO-8859-1
import requests
# 0.通过如下代码,会发现获取的网页源代码出现乱码
url = 'https://www.baidu.com'
res = requests.get(url).text
print(res)
# 1.编码分析
# 1.1 查看Python获得的网页源代码的编码方式,其编码方式为ISO-8859-1
url = 'https://www.baidu.com'
code = requests.get(url).encoding
print('通过Python获得的网页源代码的编码方式为:' + code)
# 1.2 查看网页实际的编码方式,通过F12查看,展开最上方的head标签(head标签里主要用来存储编码方式、网站标题等信息),
# 其中<meta charset="编码方式">中存储着网页实际的编码方式,可以看到网页实际的编码方法为utf-8
# 2.重新编码及解码
url = 'https://www.baidu.com'
res = requests.get(url).text
res = res.encode('ISO-8859-1').decode('utf-8')
print(res) # 此时便已经可以解决数据乱码问题了
# 注意,如果有的网站的实际编码方式为gbk,则在decode解码的时候需要把utf-8换成gbk
# 3.数据乱码万金油的解决办法
import requests
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
url = 'https://www.baidu.com'
res = requests.get(url).text
# res = requests.get(url, headers=headers).text # 这里加headers能获取更多的网页源代码
try:
res = res.encode('ISO-8859-1').decode('utf-8') # 方法3
except:
try:
res = res.encode('ISO-8859-1').decode('gbk') # 方法2
except:
res = res # 方法1
print(res) # 可以在源代码里搜索“百度”检验爬取成功
3 舆情数据评分系统版本
# 舆情评分版本4
score = []
keywords = ['违约', '诉讼', '兑付', '阿里', '百度', '京东', '互联网'] # 这个关键词列表可以自己定义,这里只是为了演示
for i in range(len(title)):
num = 0
# 获取新闻正文
try:
article = requests.get(href[i], headers=headers, timeout=10).text
except:
article = '爬取失败'
# 解决可能存在的乱码问题
try:
article = article.encode('ISO-8859-1').decode('utf-8') # 方法3
except:
try:
article = article.encode('ISO-8859-1').decode('gbk') # 方法2
except:
article = article # 方法1
# 只筛选真正的正文内容,旁边的滚动新闻之类的内容忽略
p_article = '<p>(.*?)</p>' # 有的时候p标签里还有class等无关内容,所以更严谨的写法是<p.*?>(.*?)</p>
article_main = re.findall(p_article, article) # 获取<p>标签里的正文信息
article = ''.join(article_main) # 将列表转换成为字符串
for k in keywords:
if (k in article) or (k in title[i]):
num -= 5
score.append(num)
第6章、数据分析利器—NumPy与pandas库
numpy(Numeriacl Python)
import numpy as np
1 列表和数组的索引机制
(1)相似点
(a)创建与类型
import numpy as np
a = [1, 2, 3, 4]
b = np.array([1, 2, 3, 4])
print(a)
print(b)
print(type(a))
print(type(b))
[1, 2, 3, 4] # 列表的展现形式
[1 2 3 4] # 数组的展现形式
<class 'list'> # a的类别为列表
<class 'numpy.ndarray'> # b的类别为数组
(b)索引
通过列表索引和数组索引来访问列表和数组中的元素,代码如下:
print(a[1])
print(b[1])
print(a[0:2])
print(b[0:2])
2
2
[1, 2] #列表打印有,
[1 2] #数组打印无,
两种方法打印的结果很相似
(2)差异点
(a)数学运算差异
numpy库能非常好地支持数学运算
# 数组的优势1-数组方便数学运算
c = a * 2
d = b * 2
print(c)
print(d)
[1, 2, 3, 4, 1, 2, 3, 4] # 相当于[1, 2, 3, 4]+[1, 2, 3, 4]是拼接
[2 4 6 8] # 数组相加是对应元素相加
(b)数据存储差异
列表储存的是一维数据,而数组则可以存储多维数据
# 数组的优势2-数组可以是多维的
e = [[1, 2], [3, 4], [5, 6]] # 列表里的元素为小列表
f = np.array([[1, 2], [3, 4], [5, 6]]) # 创建二维数组的一种方式
print(e)
print(f)
[[1, 2], [3, 4], [5, 6]] # e为一维数据
[[1 2]
[3 4]
[5 6]] # f为多维数据
2 创建列表与数组
通过np.array(列表)
方式创建列表
import numpy as np
# 创建一维数组
a = np.array([1, 2, 3, 4])
# 创建二维数组
b = np.array([[1, 2], [3, 4], [5, 6]])
(2)使用np.arange()
arange([start,] stop[, step,], dtype=None, *, like=None)
# 其他创建数组的方式
# 1.通过np.arange()函数创建
# 一个参数 参数值为终点,起点取默认值0,步长取默认值1
x = np.arange(5)
# 两个参数 第一个参数为起点,第二个参数为终点,步长取默认值1,左闭右开
y = np.arange(5,10)
# 三个参数 第一个参数为起点,第二个参数为终点,第三个参数为步长,左闭右开
z = np.arange(5, 10, 0.5)
print(x)
print(y)
print(z)
[0 1 2 3 4]
[5 6 7 8 9]
[5. 5.5 6. 6.5 7. 7.5 8. 8.5 9. 9.5]
可使用.reshape()
改变np.arange(行,列)
的形状
# 3.通过np.arange()函数和reshape方法产生二维数组
d = np.arange(12).reshape(3,4)
print(d)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
(3)调用np.random
模块
np.random.randn(n)
创建标准正态分布的n个随机数一维数组
# 2.通过np.random.randn()创建随机一维数组
c = np.random.randn(3)
print(c)
[2.84368813e-01 5.51043126e-01 1.32707728e-04]
- 通过
np.random.randint
创建随机整数(起始数,终止数,(行,列))
# 4.创建一个随机二维数组
e = np.random.randint(0, 10, (4, 4))
print(e)
[[9 2 1 9]
[6 1 7 3]
[2 0 7 1]
[8 5 1 2]]
3 读取编辑Excel\CSV文件
通过pandas以Excel和CSV文件讲解文件读取和写入
(1)文件读取
# 1.文件读取
# 1.1 读取Excel文件
import pandas as pd
data = pd.read_excel('data.xlsx',sheet_name=0, encoding='utf-8') #data为DataFrame结构,设置可以是相对路径或绝对路径
print(data)
read_excel
的相关参数
- sheet_name: 指定sheet表,可输入sheet名称,也可为数字(默认为0,即第1个sheet)
- encoding: 指定文件编码方式,一般是utf-8或gbk
index_col: 设置某一列为行索引。
# 1.2 读取CSV文件,CSV文件类似Excel文件,不过专门用来存储数据的,所占空间更小
data = pd.read_csv('data.csv',delimiter=',',encoding='utf-8')
print(data)
read_csv
的相关参数
- delimiter: CSV文件中的分隔符号,默认为逗号
- encoding: 指定文件编码方式,一般是utf-8或gbk
- index_col: 设置某一列为行索
(2)文件写入
# 2.文件写入
# 2.1 写入到一个Excel当中
data = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['A列', 'B列']) #先生成一个DataFrame
data.to_excel('E:/data_0.xlsx') #将DataFrame导入到Excel当中
# 忽略索引信息的写法
data.to_excel('E:/data_1.xlsx', columns=['A列'], index=False)
to_excel
的常见参数
- sheet_name: 数据表名
- index: True or False,默认为True,保存索引信息,第一列为索引值,选择False则忽略索引信息
- columns: 选择需要的列
- encoding: 编码方式
# 2.2 写入到一个CSV文件当中
data = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['A列', 'B列']) #先生成一个DataFrame
data.to_csv('E:/data_0.csv', columns=['A列'], index=False) #将DataFrame导入到CSV文件当中
to_csv
也可设置 index、columns、encoding等参数
4 data.iloc读取DataFrame数据
import pandas as pd
data = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=['r1', 'r2', 'r3'], columns=['c1', 'c2', 'c3'])
c1 c2 c3
r1 1 2 3
r2 4 5 6
r3 7 8 9
# 2.2 pandas官方推荐的iloc写法,根据行索引的序号来选取
b = data.iloc[1:3]
print(b)
c1 c2 c3
r2 4 5 6
r3 7 8 9
# 如果要选取单行的话,就得用iloc了
c = data.iloc[-1]
print(c)
c1 7
c2 8
c3 9
5 数据表拼接和删除
数据表合并与重塑有三大方法:merge
, concat
和append
(1) merge函数
- 可通过
on
参数指定按哪一列合并 - how参数有
inner
、outer
、left
、right
- 如想根据行索引合并,可设置
left_index
和right_index
参数
# 1 merge函数
# 1.1 最简单的用法,直接选取相同的列名(“公司”这一列)进行合并,而且默认选取的是两种表共有的列内容(万科、阿里)
df3 = pd.merge(df1, df2)
print(df3)
# 1.2 默认的合并其实是取交集(inner连接),也即取两表共有的内容,如果想取并集(outer连接),也即选取两表所有的内容,可以设置how参数
df3 = pd.merge(df1, df2, how='outer')
print(df3)
# 1.3 如果想保留左表全部内容,而对右表不太在意的话,可以将how参数设置为left:
df3 = pd.merge(df1, df2, how='left')
print(df3)
# 1.4 如果想根据行索引进行合并,可以通过设置left_index和right_index参数,代码如下:
df3 = pd.merge(df1, df2, left_index=True, right_index=True)
print(df3)
(2) concat函数
concat方法是全连接方式,它不需要对齐,而是直接合并。
所以concat没有how
和on
参数,而是通过axis
指定连接轴向,默认axis=0
表示横向连接
# 2 concat函数
'''concat方法是一种全连接(UNION ALL)方式,它不需要对齐,而是直接进行合并(即它不需要两表的某些列或者索引相同,只是把数据整合到一起)。
所以concat没有"how"和"on"参数,而是通过“axis”指定连接的轴向。'''
# 2.1 默认情况下,axis=0,按行方向进行连接。
df3 = pd.concat([df1, df2]) # 或者写成df3 = pd.concat([df1, df2], axis=0)
print(df3)
# 2.2 如果想按列方向进行连接,可以设置axis参数为1。
df3 = pd.concat([df1, df2], axis=1)
print(df3)
(3) append函数
- append函数是concat函数的简化版:
df3 = df1.append(df2)
效果和pd.concat([df1,df2])
类似 - append函数也和列表的
append()
相似
# 3 append函数
'''append函数可以说concat函数的简化版,效果和pd.concat([df1,df2]) 类似'''
# 3.1 常规用法
df3 = df1.append(df2)
print(df3)
# 3.2 append()函数还有个常用的功能,和列表.append()一样,可用来新增元素,代码如下:
df3 = df1.append({'公司': '腾讯', '分数': '90'}, ignore_index=True)
6 访问数据库:db.cursor()
和db.commit()
import pymysql
db = pymysql.connect(host='localhost', port=3306, user='root', password='', database='pachong', charset='utf8')
cur = db.cursor() # 获取会话指针,用来调用SQL语句
db.commit() # 更新表单,如果对数据表没有修改,可以不写这行
cur.close() # 关闭会话指针
db.close() # 关闭数据库连接
第7章 数据可视化与数据相关性分析
1 Tushare库调用财经数据
(1) 获得日线行情数据
数据参数说明:
- Open: 开盘价 ;
- Close: 收盘价;
- High: 最高价;
- Low: 最低价;
- Volume: 成交量;
- price_change: 价格变化量(=今日收盘价 - 昨日收盘价)
- p_change: 价格涨跌幅(=price_change/昨日收盘价)
- Ma5: 5日均线价格;
- v_ma5: 5日均线成交量
ts.get_hist_data()
函数最多只能调取当天往前3年的数据;
如果想调取超过3年的日线级别数据,得用ts.get_k_data()
函数,它只获取股价的基本数据。
# 1 获得日线行情数据
import tushare as ts
df = ts.get_hist_data('000002', start='2019-01-01', end='2020-01-31')
print(df)
(2) 获得分钟级别的数据
可使用ktype='分钟'
函数,如把分钟改成5,可调用5分钟级别的数据。还能设置为每15,30或60分钟级别数据。一旦要求获取分钟级别数据,再写起始日期和结束日期就没有效果了。
# 2 获得分钟级别的数据
df = ts.get_hist_data('000002', ktype='5')
print(df)
如想获取当时的股价信息, 需使用ts.get_realtime_quotes()
函数
# 3 获得实时行情数据
df = ts.get_realtime_quotes('000002')
print(df)
# 如果觉得列数过多,可以通过DataFrame选取列的方法选取相应的列,代码如下
df = df[['code', 'name', 'price', 'bid', 'ask', 'volume', 'amount', 'time']]
print(df)
# 获得多个股票代码的实时数据
df = ts.get_realtime_quotes(['000002', '000980', '000981'])
print(df)
(3) 获得分笔数据
ts.get_tick_data
可获得历史分笔数据,分笔数据也即每笔成交的信息
# 4 获得分笔数据
df = ts.get_tick_data('000002', date='2018-12-12', src='tt')
print(df)
# 获取当日分笔信息
df = ts.get_today_ticks('000002') # 注意在非交易日无法用该代码
print(df)
(4) 获得指数信息
可以用ts.get_index( )
获取指数信息
# 5 获得指数信息
df = ts.get_index()
print(df)
2 对于金融数据可视化的操作
- 条形图函数plt.bar( )
- 设置图例函数plt.gcf( )
- plt.gcf().autofmt_datex()
- 设置中文字体函数plt.rcParams[ ]
- plt.rcParams[‘figure.figsize’] = (8,6)
- plt.rcParams[‘font.sans-serif’] = ‘SimHei’ ‘NSimSun’
- plt.raParams[‘axes.unicode_minus’] = False
- 设置x轴刻度plt.xticks()
- plt.xticks(rotation=45)
# 导入模块
import numpy as np
import matplotlib.pyplot as plt
(1) 折线图 plt.plot()
# 1 折线图
# 1.1 折线图1
x = [1, 2, 3]
y = [2, 4, 6]
# color设置颜色,linewidth设置线宽,单位像素,linestyle默认为实线,“--”表示虚线
plt.plot(x, y, color='red', linewidth=3, linestyle='--')
# 绘制并展示图形
plt.show() # 运行后得关掉弹出窗才能看到下一个图
# 1.1 折线图2-多条线
# 第一条线:y = x*2
x1 = np.array([1, 2, 3])
y1 = x1*2
# color设置颜色,linewidth设置线宽,单位像素,linestyle默认为实线,“--”表示虚线
plt.plot(x1, y1, color='red', linewidth=3, linestyle='--')
# 第二条线:y = x + 1
y2 = x1 + 1
plt.plot(x1, y2) # 使用默认参数画图
plt.show()
(2) 柱状图 plt.bar()
# 2 柱状图
x = [1, 2, 3, 4, 5]
y = [5, 4, 3, 2, 1]
plt.bar(x, y)
plt.show()
(3) 添加文字说明
如下代码可添加标题plt.title()
和 xplt.xlabel()
, y轴plt.ylabel()
:
# 3 添加文字说明
x = [1, 2, 3]
y = [2, 4, 6]
plt.plot(x, y)
plt.title('TITLE') # 添加标题
plt.xlabel('X') # 添加X轴
plt.ylabel('Y') # 添加Y轴
plt.show() # 显示图片
(4) 添加图例plt.legend()
可通过 plt.legend()
来添加图例:
如果想把图例锁定在左上角,只需在括号里写:loc =‘upper left’
想改图例的位置需要改单引号里面的字:
锁定在右上角为upper right
锁定在左下角为lower left
锁定在右下角为lower right
# 4 添加图例
# 第一条线, 设定标签lable为y = x*2
x1 = np.array([1, 2, 3])
y1 = x1*2
plt.plot(x1, y1, color='red', linestyle='--', label='y = x*2')
# 第二条线, 设定标签lable为y = x + 1
y2 = x1 + 1
plt.plot(x1,y2,label='y = x + 1')
plt.legend(loc='upper left')# 图例位置设置为左上角
plt.show()
(5) 设置双坐标轴plt.twinx()
如何画出两条y坐标轴,要两条线之间的代码加上一行:plt.twinx()
# 5 设置双坐标轴
# 第一条线, 设定标签lable为y = x
x1 = np.array([10, 20, 30])
y1 = x1
plt.plot(x1, y1, color='red', linestyle='--', label='y = x')
plt.legend(loc='upper left') # 该图图例设置在左上角
plt.twinx() # 设置双坐标轴
# 第二条线, 设定标签lable为y = x^2
y2 = x1*x1
plt.plot(x1, y2, label='y = x^2')
plt.legend(loc='upper right') # 改图图例设置在右上角
plt.show()
(6) 设置图片大小
如果要改变默认图片大小,可通过如下代码设置图片大小:
plt.rcParams['figure.figsize'] = (8,6)
(7) 中文显示问题
在使用 matplotlib画图时,默认情况下不支持中文显示,通过如下代码可解决该问题。
其中由于更改了字体导致显示不出负号,需将配署文件中 axes. unicode_minus
设为False
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['NSimSun'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #解决符号'-'显示为方块的问题
(8) 日期字串比较长
- 旋转X轴。如要旋转45度,代码如下:
plt.xticks(rotation=45)
- 使用python自动旋转,代码如下:
plt.gcf().autofmt_datex()
3 相关性分析
皮尔逊线性相关系数r
(Pearson correlation coefficient),公式如下:
r
=
C
O
V
(
X
,
Y
)
D
(
X
)
D
(
Y
)
r=\frac{COV(X,Y)}{\sqrt{D(X)}\sqrt{D(Y)}}
r=D(X)D(Y)COV(X,Y)
其中
D
(
X
)
D(X)
D(X)和
D
(
Y
)
D(Y)
D(Y)分别为变量
X
X
X和
Y
Y
Y的方差,
C
O
V
(
X
,
Y
)
COV(X,Y)
COV(X,Y)为变量
X
X
X和
Y
Y
Y的协方差
用Python自带的 scipy
库运算皮尔逊相关系数:
from scipy.stats import pearsonr
corr = pearsonr(X,Y)
print('相关系数r值为' + str(corr[0]) + ',显著性水平P值为' + str(corr[1]))
该方法返回一个列表两个数值:相关系数r值和显著水平P值。P<0.05时表示相关显著。
第8章 金融数据爬虫之爬虫技术进阶
1 使用IP代理的作用
有的网站对IP有监控:
如IP在短时间内访问该网站次数太多,该IP会被冻结,网络被该网站列入“黑名单”。网页会跳出:
- “您的IP访问频率太高”的提示
- 被要求输入验证码,访问失败。
遇到IP地址反爬的网址,即通过固定IP地址访问次数过多就会拒绝访问的网址,设置proxies参数,即可正常访问。
IP代理原理
- IP类似于id, 就是所用网络的身份证号码
- IP代理就是IP伪装,把本机的IP伪装成其他的IP地址。
- IP代理商有海量IP地址,这些海量IP地址被称为IP代理池。
- 在IP代理池里提取IP地址, 把提取的IP地址写到Python程序里 (自己的IP伪装成别的IP),从而躲过某些网站对固定IP访问次数的限制
import requests # 讯代理官网:http://www.xdaili.cn/;教程见书第8章
proxy = requests.get('http://api.xdaili.cn/xdaili-api//greatRecharge/getGreatIp?spiderId=b030195e2075469299bca6b661c913ff&orderno=YZ201810262456rdpAb0&returnType=1&count=1').text
proxy = proxy.strip() # 这一步非常重要,因为要把看不见的换行符等给清除掉
proxies = {"http": "http://"+proxy, "https": "https://"+proxy}
url = 'https://httpbin.org/get'
res = requests.get(url, proxies=proxies).text
print(res)
2 查找元素的方法:Xpath法和css_selector法
(1) xpath法
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.baidu.com/")
browser.find_element_by_xpath('//*[@id="kw"]').send_keys('python')
browser.find_element_by_xpath('//*[@id="su"]').click()
(2) css_selector法
# 3.css_selector方法来定位元素
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.baidu.com/")
browser.find_element_by_css_selector('#kw').send_keys('python')
browser.find_element_by_css_selector('#su').click()
3 Selenium库的作用
用常规代码res = requests.get(url).text
的方式来访问新浪财经的上证指数。发现在源代码里并不能搜索到刚刚看到的数据。
因为用F12
看到的是网站已经渲染后的信息, 一个快速验证的办法就是在网页上右击,选择查看网页源代码, 发现没有F12渲染过的信息。
selenium库是一个不错的解决办法,利用它可快速获取到网页真正的源码以及模拟人在浏览器上的操作
4 Selenium库的用法
(1) 访问及关闭页面 + 网页最大化
# 1.打开及关闭网页+网页最大化
from selenium import webdriver #引入selenium库里的webdriver功能
browser = webdriver.Chrome() #声明用的模拟器是谷歌浏览器
browser.maximize_window() #窗口最大化
# browser.get("https://www.baidu.com/")
browser.get("http://hrnext.cn/7fV6n1") #通过brower.get(‘网址’)方法访问网址
browser.quit() #关闭模拟浏览器,代码如下
(2) 查找元素模拟鼠标点击和键盘输入
(3) 获取网页真正的源代码
# 4.browser.page_source方法来获取模拟键盘鼠标点击,百度搜索python后的网页源代码
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get("https://www.baidu.com/")
browser.find_element_by_xpath('//*[@id="kw"]').send_keys('python')
browser.find_element_by_xpath('//*[@id="su"]').click()
time.sleep(3) # 因为是点击按钮后跳转,所以最好休息3秒钟再进行源代码获取,如果是直接访问网站,则通常不需要等待。
data = browser.page_source
print(data)
(4) Chrome Headless无界面浏览器设置
有时不需要弹出一个模拟浏览器,就用Chrome Headless方法,把浏览器
转到后台运行而不显示,具体只要把代码前面部分改成如下:
# 6.Chrome Headless无界面浏览器设置
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
browser = webdriver.Chrome(options=chrome_options) # 参数名由chrome_options改成了options
browser.get("http://finance.sina.com.cn/realstock/company/sh000001/nc.shtml")
data = browser.page_source
print(data)
第9章 金融数据挖掘案例实战2
1 基本的Python数据清洗函数
标题里有一些<em>
类似的符号,可用re.sub()
的方法替代。
新闻日期里有一些不需要的内容,它们中间以空格分隔,所以可利用split()
函数来分割。
for i in range(len(title)):
title[i] = re.sub(r'<.*?>', '', title[i])
href[i] = 'http://www.cninfo.com.cn' + href[i]
href[i] = re.sub('amp;', '', href[i])
date[i] = date[i].strip() # 清除空格和换行符
date[i] = date[i].split(' ')[0] # 只取“年月日”信息,不用“时分秒”信息
print(str(i + 1) + '.' + title[i] + ' - ' + date[i])
print(href[i])
2 基本的例子
第10章 通过PDF文本解析上市公司理财公告
1 用于解析PDF文本的的Python库
深入分析PDF正文内容,需要用到PDF文本解析技术。
Python中有多个库可用于解析PDF文本,如:
- pdfplumber库
- pdfminer库
- tabula库
pdfplumber库是目前使用最方便的库,而且pdfplumber库不仅可以解析文字,还可以解析PDF文件中的表格。
2 用pdfplumber库提取文本内容的程序
(1)pdfplumber库提取文本内容
通过pdfplumber库的extract_text()
方法可以解析PDF文件第一页的文本内容,代码如下
# 1.解析第一页的文本信息
import pdfplumber
pdf = pdfplumber.open('E://公司A理财公告.PDF') # 打开PDF文件
pages = pdf.pages # 通过pages属性获取所有页的信息,此时pages是一个列表
page = pages[0] # 获取第一页内容
text = page.extract_text() # 通过
print(text) # 打印第一页内容
pdf.close() # 关闭PDF文件
如果想解析每一页的内容,可通过for循环
语句实现,代码如下:
# 2.解析全部页数的文本信息
import pdfplumber
pdf = pdfplumber.open('E://公司A理财公告.PDF')
pages = pdf.pages
text_all = []
for page in pages: # 遍历pages中每一页的信息
text = page.extract_text() # 提取当页的文本内容
text_all.append(text) # 通过列表.append()方法汇总每一页内容
text_all = ''.join(text_all) # 把列表转换成字符串
print(text_all) # 打印全部文本内容
pdf.close()
(2)pdfplumber库提取表格内容
通过pdfplumber库的extract_tables()
方法可以提取页面中的表格,
代码如下:
# 3.解析表格内容
import pdfplumber
import pandas as pd
pdf = pdfplumber.open('E://公司A理财公告.PDF')
pages = pdf.pages
page = pages[3] # 因为表格在第四页,所以提取第四页,也即pages[3]
tables = page.extract_tables() # 通过extract_tables方法获取该页所有表格
table = tables[0] # 因为第四页只有一个表格,所以通过tables[0]提取
# 替换原来table中的换行符
for i in range(len(table)): # 遍历大列表中的每一个子列表
for j in range(len(table[i])): # 遍历子列表中的每一个元素
table[i][j] = table[i][j].replace('\n', '') # 替换换行符
pd.set_option('display.max_columns', None) # 显示全部列
df = pd.DataFrame(table[1:], columns=table[0]) # 得到的table是嵌套列表类型,转化成DataFrame更加方便查看和分析
print(df)
pdf.close()
通过extract_tables()
返回的tables是该页的全部表格列表。因为第四页只有一个表格,所以可以通过tables[0]
来提取第一个也即唯一的表格。
3 遍历文件夹里所有的PDF文件
(1) 遍历文件夹里所有的PDF文件
首先需要遍历文件夹里所有的PDF文件, 然后才能进行批量的文本解析
# 1.遍历文件夹中的所有PDF文件
import os
file_dir = 'E://演示文件夹' # 也可以改成你自己需要遍历的文件夹,这里用的相对路径
file_list = []
for files in os.walk(file_dir): # 遍历该文件夹及其里面的所有子文件夹
for file in files[2]:
if os.path.splitext(file)[1] == '.pdf' or os.path.splitext(file)[1] == '.PDF':
file_list.append(file_dir + '\\' + file)
print(file_list)
第一行引用os
库,为之后使用os.walk
功能遍历文件夹做准备;
第二行是写入要遍历的文件夹路径;
第三行是遍历该母文件夹下面的所有子文件夹及子文件夹里的所有文件信息如果没有子文件夹,那么就只循环一遍,获取到该母文件夹下所有文件信息。
第四行是把文件信息打印输出。其中files[2]表示母文件夹和子文件夹里各文件信息,一般写files[2]来获取文件信息。
- 如果写files[1]则表示各个子文件夹信息,
- 如果写files[0]则表示母文件夹信息
假设目标演示文件夹构造如图:
for files in os.walk(file_dir):
print(files)
('.\\演示文件夹', ['子文件夹'], ['信托投资公告B.PDF', '信托购买报告A.PDF', '正业科技:关于控股股东、实际控制人获得上市莞企发展投资及东莞信托资金支持的公告.PDF'])
('.\\演示文件夹\\子文件夹', [], ['ST云维:委托理财公告.PDF'])
因而选取files[2]
进行文件名的遍历
(2)批量解析每一个PDF文件
# 2.PDF文本解析和内容筛选
pdf_all = []
for i in range(len(file_list)):
pdf = pdfplumber.open(file_list[i])
pages = pdf.pages
text_all = []
for page in pages: # 遍历pages中每一页的信息
text = page.extract_text() # 提取当页的文本内容
text_all.append(text) # 通过列表.append()方法汇总每一页内容
text_all = ''.join(text_all) # 把列表转换成字符串
print(text_all) # 打印全部文本内容
pdf.close()
# 通过正文进行筛选
if ('自有' in text_all) or ('议案' in text_all) or ('理财' in text_all) or ('现金管理' in text_all):
pdf_all.append(file_list[i])
print(pdf_all) # 打印筛选后的PDF列表
(3)将合格的PDF文件自动归档
# 3.筛选后文件的移动
for pdf_i in pdf_all:
newpath = 'E:\\筛选后的文件夹\\' + pdf_i.split('\\')[-1] # 这边这个移动到的文件夹一定要提前就创建好!
os.rename(pdf_i, newpath) # 执行移动操作
4 使用apply()函数默认axis参数
axis{0 or ‘index’, 1 or ‘columns’},
default 0 Axis along which the function is applied:
0 or ‘index’: apply function to each column.
1 or ‘columns’: apply function to each row.
5 案例
# 1.遍历文件夹中的所有PDF文件
file_dir = '.\\演示文件夹' # 也可以改成你自己需要遍历的文件夹,这里用的相对路径
file_list = []
for files in os.walk(file_dir): # 遍历该文件夹及其里面的所有子文件夹
for file in files[2]:
if os.path.splitext(file)[1] == '.pdf' or os.path.splitext(file)[1] == '.PDF':
file_list.append(file_dir + '\\' + file)
print(file_list)
['.\\演示文件夹\\ST云维:委托理财公告.PDF', '.\\演示文件夹\\信托投资公告B.PDF', '.\\演示文件夹\\信托购买报告A.PDF', '.\\演示文件夹\\正业科技:关于控股股东、实际控制人获得上市莞企发展投资及东莞信托资金支持的公告.PDF']
# 2.PDF文本解析和内容筛选
pdf_all = []
for i in range(len(file_list)):
pdf = pdfplumber.open(file_list[i])
pages = pdf.pages
text_all = []
for page in pages: # 遍历pages中每一页的信息
text = page.extract_text() # 提取当页的文本内容
text_all.append(text) # 通过列表.append()方法汇总每一页内容
text_all = ''.join(text_all) # 把列表转换成字符串
#print(text_all) # 打印全部文本内容
pdf.close()
# 通过正文进行筛选
if ('自有' in text_all) or ('议案' in text_all) or ('理财' in text_all) or ('现金管理' in text_all):
pdf_all.append(file_list[i])
print(pdf_all) # 打印筛选后的PDF列表
['.\\演示文件夹\\ST云维:委托理财公告.PDF', '.\\演示文件夹\\信托投资公告B.PDF', '.\\演示文件夹\\信托购买报告A.PDF']
# 3.筛选后文件的移动
for pdf_i in pdf_all:
newpath = '.\\演示文件夹\\子文件夹\\' + pdf_i.split('\\')[-1] # 这边这个移动到的文件夹一定要提前就创建好!
os.rename(pdf_i, newpath) # 执行移动操作
print('PDF文本解析及筛选完毕!')
第11章 邮件提醒系统搭建
1 通过腾讯QQ和163邮箱发送邮件
import smtplib # 引入两个控制邮箱发送邮件的库
from email.mime.text import MIMEText
user = '你自己的qq号@qq.com' # 发件人邮箱
pwd = '你自己的SMTP授权码' # 邮箱的SMTP密码,看书第11章,申请很方便
to = '你自己设置的收件人邮箱' # 可以设置多个收件人,英文逗号隔开,如:'***@qq.com, ***@163.com'
# 1.邮件正文内容
msg = MIMEText('测试邮件正文内容')
# 2.设置邮件主题、发件人、收件人
msg['Subject'] = '测试邮件主题!' # 邮件的标题
msg['From'] = user # 设置发件人
msg['To'] = to # 设置收件人
# 3.发送邮件
s = smtplib.SMTP_SSL('smtp.qq.com', 465) # 选择qq邮箱服务,默认端口为465
s.login(user, pwd) # 登录qq邮箱
s.send_message(msg) # 发送邮件
s.quit() # 退出邮箱服务
print('Success!')
import smtplib
from email.mime.text import MIMEText
user = '你自己的163邮箱@163.com' # 发件人,这里为163邮箱了
pwd = 'huaxiaozhi123' # 163邮箱的SMTP授权码
to = '收件人邮箱' # 可以设置多个收件人,英文逗号隔开,如:'***@qq.com, ***@163.com'
# 1.邮件正文内容
msg = MIMEText('测试邮件正文内容')
# 2.设置邮件主题、发件人、收件人
msg['Subject'] = '测试邮件主题!'
msg['From'] = user
msg['To'] = to
# 3.发送邮件
s = smtplib.SMTP_SSL('smtp.163.com', 465) # 选择163邮箱服务,默认端口为465
s.login(user, pwd) # 登录163邮箱
s.send_message(msg) # 发送邮件
s.quit()
print('Success!')
2 利用Python发送多家上市公司数据报告邮件
import smtplib
from email.mime.text import MIMEText
user = '你自己的qq号@qq.com'
pwd = '你自己的SMTP授权码'
to = '你自己设置的收件人邮箱' # 可以设置多个收件人,英文逗号隔开,如:'***@qq.com, ***@163.com'
# 1.连接数据库 提取所有今天的"阿里巴巴"的新闻信息
import pymysql
import time
db = pymysql.connect(host='localhost', port=3306, user='root', password='', database='pachong', charset='utf8')
company = '阿里巴巴'
today = time.strftime("%Y-%m-%d") # 这边采用标准格式的日期格式
cur = db.cursor() # 获取会话指针,用来调用SQL语句
sql = 'SELECT * FROM test WHERE company = %s AND date = %s'
cur.execute(sql, (company,today))
data = cur.fetchall() # 提取所有数据,并赋值给data变量
print(data)
db.commit() # 这个其实可以不写,因为没有改变表结构
cur.close() # 关闭会话指针
db.close() # 关闭数据库链接
# 2.利用从数据库里提取的内容编写邮件正文内容
mail_msg = []
mail_msg.append('<p style="margin:0 auto">尊敬的小主,您好,以下是今天的舆情监控报告,望查阅:</p>') # style="margin:0 auto"用来调节行间距
mail_msg.append('<p style="margin:0 auto"><b>一、阿里巴巴舆情报告</b></p>') # 加上<b>表示加粗
for i in range(len(data)):
href = '<p style="margin:0 auto"><a href="' + data[i][2] + '">' + str(i+1) + '.' + data[i][1] + '</a></p>'
mail_msg.append(href)
mail_msg.append('<br>') # <br>表示换行
mail_msg.append('<p style="margin:0 auto">祝好</p>')
mail_msg.append('<p style="margin:0 auto">华小智</p>')
mail_msg = '\n'.join(mail_msg)
print(mail_msg)
# 3.添加正文内容
msg = MIMEText(mail_msg, 'html', 'utf-8')
# 4.设置邮件主题、发件人、收件人
msg["Subject"] = "华小智舆情监控报告"
msg["From"] = user
msg["To"] = to
# 5.发送邮件
s = smtplib.SMTP_SSL('smtp.qq.com', 465) # 选择qq邮箱服务,默认端口为465
s.login(user, pwd) # 登录qq邮箱
s.send_message(msg) # 发送邮件
s.quit() # 退出邮箱服务
print('Success!')
第12章 基于评级报告的投资决策分析
1 获取新浪财经数据中心提供的大宗交易网络表格数据
import pandas as pd
url = 'http://vip.stock.finance.sina.com.cn/q/go.php/vInvestConsult/kind/dzjy/index.phtml' # 新浪财经数据中心提供股票大宗交易的在线表格
table = pd.read_html(url)[0] # 通过pd.read_html(url)获取的是一个列表,所以仍需通过[0]的方式提取列表的第一个元素
print(table)
table.to_excel('E:\\大宗交易表.xlsx') # 如果想忽略行索引的话,可以设置index参数为False
print('获取表格成功!')
该网站上虽只有一张表格,所以仍需通过[0]提取列表的第一个元素。如网站有多个表格,则按序号提取所需表格。
2 命令pd.read_html()
解析函数
利用pandas库获取在线表格,代码如下:
import pandas as pd
table = pd.read_html(url)
- url为网址链接;
- pd.read_html()函数可获得该网站上的所有表格;并自动解析成DataFrame二维表格。
3 data.groupby()
方法
# 1.先创建一个DataFrame
import pandas as pd
data = pd.DataFrame([['丁一', '阿里', 0.4, 0.2],
['王二', '百度', 0.2, 0.6],
['王二', '腾讯', 0.4, 0.8],
['张三', '京东', 0.1, 0.4],
['张三', '拼多多', 0.1, 0.2]],
columns=['分析师', '股票名称', '30天收益率', '90天收益率'])
print(data)
分析师 股票名称 30天收益率 90天收益率
0 丁一 阿里 0.4 0.2
1 王二 百度 0.2 0.6
2 王二 腾讯 0.4 0.8
3 张三 京东 0.1 0.4
4 张三 拼多多 0.1 0.2
# 2.根据分析师进行分组,并计算平均30天收益率均值
means = data.groupby('分析师')[['30天收益率']].mean()
print(means)
30天收益率
分析师
丁一 0.4
张三 0.1
王二 0.3
# 3.根据分析师及股票名称进行多重分组,并计算平均30天收益率均值
means = data.groupby(['分析师', '股票名称'])[['30天收益率']].mean()
print(means)
30天收益率
分析师 股票名称
丁一 阿里 0.4
张三 京东 0.1
拼多多 0.1
王二 百度 0.2
腾讯 0.4
# 4.根据分析师进行分组,并计算其预测次数
count = data.groupby('分析师')[['30天收益率']].count()
print(count)
30天收益率
分析师
丁一 1
张三 2
王二 2
4 pd.read_excel()
函数
# 1.文件读取
# 1.1 读取Excel文件
import pandas as pd
data = pd.read_excel('data.xlsx',sheet_name=0, encoding='utf-8') #data为DataFrame结构,设置可以是相对路径或绝对路径
print(data)
read_excel
的相关参数
- sheet_name: 指定sheet表,可输入sheet名称,也可为数字(默认为0,即第1个sheet)
- encoding: 指定文件编码方式,一般是utf-8或gbk
index_col: 设置某一列为行索引。
5 获取Tushare库获取股票数据资料代码
6 使用Matplotlib库画图时支持显示中文
import matplotlib.pyploy as plt
plt.rcParams['font.sans.serif'] = ['NSimSun']
plt.rcParams['font.sans-serif'] = ['KaiTi']
7 判断股票一字涨停
有的股票可能在分析报告出来的第二天出现一字涨停(即开盘即涨停,且当天一直涨停,形成一个一字的形状,这样涨停的股票是买不到的)。
这种走势一般出现在股票刚上市时,或出现重大消息的时候,虽然并不常见,但是也要考虑,如果分析师推荐了这种类型的股票则要将其剔除。
对这样的股票,有一个简单的判断逻辑,就是最低价等于最高价且价格变化与10%(涨停板)相差不超过0.1,代码如下:
ts_result.iloc[-1]['low'] == ts_result.iloc[-1]['high'] and abs(ts_result.iloc[-1]['p_change'] -10)
#p_change 价格涨跌幅(=price_change/昨日收盘价)
8 对和讯研报网,利用Selenium库来访问网站并获取网页源码
# 它可能会弹出一个Warning警告,警告不是报错,不用在意
import pandas as pd
from selenium import webdriver
# 设置为无界面模式,从而让模拟浏览器不弹出来,使得程序在后台运行
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
browser = webdriver.chrome(options=chrome_options)
import re
data_all = pd.DataFrame() # 创建一个空列表用来汇总所有表格信息
for pg in range(1, 2): # 可以将页码调大,比如2019-04-30该天,网上一共有176页,这里可以将这个2改成176
url = 'http://yanbao.stock.hexun.com/ybsj5_' + str(pg) + 'shtml'
browser.get(url) # 通过Selenium访问网站
data = browser.page_source # 获取网页源代码
table = pd.read_html(data)[0] # 通过pandas库提取表格
print(table) # 2020-09完善
# 如果上面打印的table里的表头有问题,则需要使用如下代码将第一行为表头,然后从第二行开始取数
# table.columns = table.iloc[0] # 将原来的第一行内容设置为表头
# table = table.iloc[1:] # 改变表格结构,从第二行开始选取
# 添加股票代码信息
p_code = '<a href="yb_(.*?).shtml'
code = re.findall(p_code, data)
table['股票代码'] = code
# 通过concat()函数纵向拼接成一个总的DataFrame
data_all = pd.concat([data_all, table], ignore_index=True)
print(data_all)
print('分析师评级报告获取成功')
data_all.to_excel('E://分析师评级报告.xlsx')
应用:评估券商分析师预测准确度
(1)数据预处理
# 1. 数据预处理
# 读取数据、删除重复及空值行
df = pd.read_excel('分析师评级报告.xlsx', dtype={'股票代码': str}) # 注意设置dtype参数,让股票代码以字符串格式读进来
df = df.drop_duplicates() # 删除重复行
df = df.dropna(thresh=5) # 删除空值行,thresh=5表示非空值少于5个则删除,本案例因为没有空值行,其实可以不写
# 列拼接、选取合适的列
df['研究机构-分析师'] = df['研究机构'] + '-' + df['分析师']
columns = ['股票名称', '股票代码', '研究机构-分析师', '最新评级', '评级调整', '报告日期']
df = df[columns]
# 日期筛选
today = datetime.datetime.now()
t = today - datetime.timedelta(days=30)
t = t.strftime('%Y-%m-%d')
df = df[df["报告日期"] < t]
(2)通过Tushare库计算股票收益率
收益率 = 结束日的收盘价格 / 开始日的开盘价格 - 1.0
# 2.通过Tushare库计算股票收益率
df_use = df.iloc[0:100] # 为了演示选取了前100行,想运行全部可写df_use = df
rate = [] # 创建一个空列表,用来存储每支股票的收益率
for i, row in df_use.iterrows():
code = row['股票代码']
analysist_date = row['报告日期']
# 1.获取开始日期,也即第二天
begin_date = datetime.datetime.strptime(analysist_date, '%Y-%m-%d')
begin_date = begin_date + datetime.timedelta(days=1)
begin_date = begin_date.strftime('%Y-%m-%d')
# 2.获取结束日期,也即第三十天
end_date = datetime.datetime.strptime(analysist_date, '%Y-%m-%d')
end_date = end_date + datetime.timedelta(days=30)
end_date = end_date.strftime('%Y-%m-%d')
# 3.通过Tushare库计算股票收益率
ts_result = ts.get_hist_data(code, begin_date, end_date)
if ts_result is None or len(ts_result) < 10: # 防止股票没有数据
return_rate = 0
else:
# 防止出现一字涨停现象
if ts_result.iloc[-1]['low'] == ts_result.iloc[-1]['high'] and abs(ts_result.iloc[-1]['p_change'] - 10.0) < 0.1:
return_rate = 0
else:
start_price = ts_result.iloc[-1]['open']
end_price = ts_result.iloc[0]['close']
return_rate = (end_price / start_price) - 1.0
rate.append(return_rate)
df_use['30天收益率'] = rate # 该添加列的方式参考6.2.1小节
(3) 计算平均收益率并进行分析师预测准确度排名
# 3. 计算平均收益率并进行分析师预测准确度排名
# 3.1 计算平均收益率
means = df_use.groupby('研究机构-分析师')[['30天收益率']].mean()
# 3.2 统计预测次数, 记得这里要将列索引重命名
count = df_use.groupby('研究机构-分析师')[['30天收益率']].count()
count = count.rename(columns={'30天收益率': '预测次数'})
# 3.3 合并表格
# 方法1-通过merge()函数合并,注意要设置left_index和right_index按行索引合并
df_final = pd.merge(means, count, left_index=True, right_index=True)
# 方法2-通过concat()函数合并,注意设置axis参数为1使它为横向合并
# df_final = pd.concat([means, count], axis=1)
# 3.4 按照收益率从高往低排序
df_final.sort_values(by='30天收益率', ascending=False, inplace=True)
# 3.5 导出为Excel
df_final.to_excel("分析师预测结果.xlsx")
第13章 用Python生成Word文档
- 用Python创建Word文档,python-docx库
1 python-docx库的基础知识
简单示例:
import docx #通过"import docx"引入python-docx
file = docx.Document() #在后台创建word文档,并赋值为file
#通过file.add_paragraph方法来添加段落
file.add_paragraph('螃蟹在剥我的壳,笔记本在写我')
file.add_paragraph('漫天的我落在枫叶上雪花上')
file.add_paragraph('而你在想我')
file.save("./三行情书.docx") #最后通过file.save方法命名及保存文件
print('Word生成完毕!')
(1) 创建及保存Word文档
创建一个新的Word文档:
import docx
file = docx.Document()
打开已存在的Word文档
import docx
file = docx.Document(文件路径)
(2) 添加标题
通过.add_heading
方法添加标题:
file.add_heading('三行情书2', level=0)
level=0
,表示标题级别为0,字体较大。不推荐采用该方式设置标题,因为它会
默认加一个下划线,可以用(3)讲的添加段落的方式来创建标题。
(3) 添加段落文字
通过. add_paragraph
的方法可以添加段落:
# 3.添加段落文字
file.add_paragraph('我喜欢你')
file.add_paragraph('上一句话是假的')
file.add_paragraph('上一句话也是假的')
(4) 添加图片
通过.add_picture
的方法能添加图片:
file.add_picture('水墨.png')
设置图片的宽和高,分别使用参数width
(宽)和height
(高)
from docx.shared import Inches
file.add_picture('水墨.png', width=Inches(3), height=Inches(3))
图片默认放在最左边,如果想让它居中,可以如下设置:
from docx.enum.text import WD_ALIGN_PARAGRAPH
last_paragraph = file.paragraphs[-1]
last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
(5) 添加分页符
通过.add_page_break()
方法可添加分页符:
# 5.添加分页符
file.add_page_break()
这个办法能实现自动分页,之后的内容将从新的一页开始
(6) 添加表格
通过.add_table
的方法可添加表格:
# 6.添加表格
table = file.add_table(rows=1, cols=3)
table.cell(0,0).text = '克制'
table.cell(0,1).text = '再克制'
table.cell(0,2).text = '在吗'
其中cell(a,b)
表示表格的单元格,a表示第几行,b表示第几列,注意点:序号都是从0开始的,所以cell(0,0)表示第一行第一列。
默认表格样式没有边框。
通过表格样式可设置表格边框,需要在创建表格时加上style="表格样式"
,
代码如下:
table = file.add_table(rows=1, cols=3,style='Light Shading Accent1')
更多表格样式详见:https://python- docx.readthedocs.io/en/latest/user/styles-understanding.html
(7) 读取Word文档
用Python来读取Word文档,代码如下:
file = docx.Document('三行情书.docx') #打开文件
for paragraph in file.paragraphs:
print(paragraph.text)
其中file.paragraphs
就是该Word文档里的所有段落,通过for循环
把每个段落打印出来。
注意点:print时,paragraph.text
才是它的文本内容,不要写paragraph
。
创建一个空列表,用列表名.append()
方法把它们放置到列表里,然后通过'连接符'.join()
的方法把列表转换成字符串,代码如下:
file = docx.Document('三行情书.docx') #打开文件
content = []
for paragraph in file.paragraphs:
print(paragraph.text)
content.append(paragraph.text)
content = ' '.join(content)
print(content)
完整代码整合:
# 1.创建Word对象
import docx
file = docx.Document()
# 2.添加标题
file.add_heading('三行情书2', level=0)
# 3.添加段落文字
file.add_paragraph('我喜欢你')
file.add_paragraph('上一句话是假的')
file.add_paragraph('上一句话也是假的')
# 4.添加图片
file.add_picture('水墨.png') # 需要你自己设置一个图片地址
# 5.添加分页符
file.add_page_break()
# 6.添加表格
table = file.add_table(rows=1, cols=3)
table.cell(0,0).text = '克制'
table.cell(0,1).text = '再克制'
table.cell(0,2).text = '在吗'
# 7.文档保存,存储文件夹需提前创建
file.save('./三行情书2.docx')
print('三行情书2生成完毕')
2 Python创建Word进阶
(1) 设置中文字体
设置中文字体是Python操控Word文档的一个重点也是一个难点,使用方法是把这几行代码直接写在程序的最上面,代码如下:
from docx.oxml.ns import qn
file.styles['Normal'].font.name = u'微软雅黑' # 可换成word里面任意字体
file.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'微软雅黑') # 这边记得也得填一下字体名称
(2) 设置字体大小及颜色
通过add_paragraph
方法新建一个段落后,就不能在“你”字后面增加内容了,必须创建一个新段落来添加“的眼”,通过如下代码
file.add_paragraph('我喜欢你')
file.add_paragraph('的眼')
这里把p.add_run('我喜欢你')
赋值给叫run
的变量,然后用run.font
调用字体属性,代码如下:
p = file.add_paragraph()
run = p.add_run('我喜欢你')
font = run.font
(a) 字体大小
首先从python-docx里引入Pt功能,然后用font.size设置字体大小。
from docx.shared import Pt
font.size = Pt(26)
run
可理解为段落里的文字,只有段落里的文字可设置字体属性,单纯的段落不能设置字体,不能直接写font = p.font
(b) 字体颜色
设置字体颜色,需要先从python-docx
中引入字体颜色的功能,其代码如下:
from docx.shared import RGBColor
font.color.rgb = RGBColor(54, 95, 145)
其中RGB是颜色的一种表现形式,(59,95,145)表示蓝色,如果想查看更多颜色的RGB表现形式,可搜索“颜色RGB对照表”,https://tool.oschina.net/commons?type=3
(c) 字体其他
除字体大小和颜色外,还可设置字体粗体、斜体与下划线,代码如下
font.bold = True # 粗体
font.italic = True # 斜体
font.underline = True # 下划线
(3) 设置段落样式:对齐方式、文本缩进、行间距、段间距、段落序号
(a)对齐方式
首先看对齐方式,先来设置为居中对齐:
from docx.enum.text import WD_ALIGN_PARAGRAPH
p = file.add_paragraph()
p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
p.add_run('而你在想我')
对齐方式 | 对应代码 |
---|---|
居中对齐 | WD_ALIGN_PARAGRAPH.CENTER |
左对齐 | WD_ALIGN_PARAGRAPH.LEFT |
右对齐 | WD_ALIGN_PARAGRAPH.RIGHT |
两端对齐 | WD_ALIGN_PARAGRAPH.JUSTIF |
(b)设置首行缩进
from docx.shared import Inches
p = file.add_paragraph()
p.paragraph_format.first_line_indent = Inches(0.32)
p.add_run('设置首行缩进示例文字')
设置首行缩进需要先从python-docx中引入英寸。小四号字体对应0.16英寸,缩进2个字符长度就是0.32英寸的首行缩进。
(c)设置行间距
from docx.shared import Pt
p = file.add_paragraph()
p.paragraph_format.line_spacing = Pt(16) # 行距,16磅对应三号字体大小
p.add_run('设置行距示例文字')
from docx.shared import Pt
p = file.add_paragraph()
p.paragraph_format.space_before = Pt(14) # 段前距,14磅对应4号字体大小
p.paragraph_format.space_after = Pt(14) # 段后距
p.add_run('设置段前段后距示例文字')
(d)段落序号
file.add_paragraph('点序号', style='List Bullet')
file.add_paragraph('数字序号', style='List Number')
3 案例实战 - 自动生成数据分析Word报告
以上市公司相关舆情数据为例,演示如何在实战中生成一份Word版的数据分析报告。
(1)首先引入会用到的库
import docx
from docx.shared import RGBColor
from docx.shared import Inches
from docx.shared import Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
import time
(2)创建空白Word对象,并设置字体
file = docx.Document()
file.styles['Normal'].font.name = u'微软雅黑' # 可换成word里面任意字体
file.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'微软雅黑') # 这边记得也得填一下
(3)创建封面
p = file.add_paragraph() # 创建一个段落
p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER # 居中设置
p.paragraph_format.space_before = Pt(180) # 段前距为180
p.paragraph_format.space_after = Pt(30) # 段后距为30,
run = p.add_run('上市公司舆情报告') # 在段落里添加内容
font = run.font # 设置字体
font.color.rgb = RGBColor(54, 95, 145) # 颜色设置,这里是用RGB颜色
font.size = Pt(42) # 字体大小设置,和word里面的字号相对应
(4)在封面上补充当天日期
year = time.strftime("%Y")
month = time.strftime("%m")
day = time.strftime("%d")
today = year + '年' + month + '月' + day + '日' # 构造当天日期
p = file.add_paragraph() # 新建一个段落
p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(today) # 在段落中输入当天日期
font = run.font
font.color.rgb = RGBColor(54, 95, 145)
font.size = Pt(26)
(5)写数据分析报告的主体
# 添加分页符
file.add_page_break()
# 设置正文标题
p = file.add_paragraph()
p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER # 段落文字居中设置
run = p.add_run('第一部分 阿里巴巴舆情报告')
run.font.color.rgb = RGBColor(54, 95, 145) # 字体颜色设置
run.font.size = Pt(22) # 字体大小设置
# 连接数据库 提取所有今天的"阿里巴巴"的新闻信息
import pymysql
db = pymysql.connect(host='localhost', port=3306, user='root', password='', database='pachong', charset='utf8')
company = '阿里巴巴'
today = time.strftime("%Y-%m-%d") # 这边采用标准格式的日期格式
cur = db.cursor() # 获取会话指针,用来调用SQL语句
sql = 'SELECT * FROM test WHERE company = %s AND date = %s' # 编写SQL语句
cur.execute(sql, (company,today)) # 执行SQL语句
data = cur.fetchall() # 提取所有数据,并赋值给data变量
print(data)
db.commit() # 这个其实可以不写,因为没有改变表结构
cur.close() # 关闭会话指针
db.close() # 关闭数据库链接
# 编写正文内容之引言
num = len(data)
p = file.add_paragraph() # 添加新段落
p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY # 两端对齐
p.paragraph_format.first_line_indent = Inches(0.32) # 这个控制首行缩进,
introduction = '本次舆情监控目标为阿里巴巴,主要爬取网站为百度新闻,共爬取当天新闻' + str(num) + '篇,具体新闻如下:'
p.add_run(introduction)
# 编写正文内容之具体新闻内容
for i in range(len(data)):
p = file.add_paragraph() # 添加新段落
p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY # 设置两端对齐
p.add_run(str(i + 1) + '. ' + data[i][1]) # 提取新闻标题
# 编写正文内容之表格添加
tb = file.add_table(rows=num + 1, cols=3, style='Light Shading Accent 1') # 之前定义过num = len(data)
tb.cell(0, 0).text = '监控公司'
tb.cell(0, 1).text = '新闻标题'
tb.cell(0, 2).text = '新闻来源'
for i in range(num): # 之前定义过num = len(data)
tb.cell(i+1, 0).text = '阿里巴巴'
tb.cell(i+1, 1).text = data[i][1] # 提取新闻标题
tb.cell(i+1, 2).text = data[i][4] # 提取新闻来源
第14章 基于股票信息及其衍生变量的数据分析
- 根据当日成交量和多日成交量的均值计算涨跌幅
- 做成交量涨跌幅和股价涨跌幅的相关性分析
- 在画图时候,命令
plt.twinx()
和plt.title()
等的作用 - 处理Excel文件的 xlwings库
获得股票基本信息数据
首先以万科A(股票代码:000002)为例演示获取其2019-04-12的交易分笔信息,并将获取到的DataFrame赋值给df变量,代码如下
# 1.获取一天10分钟成交量信息
import tushare as ts
import pandas as pd
stock_code = '000002' # 设置股票代码
current_date = '2019-04-12' # 设置日期
df = ts.get_tick_data(stock_code, date=current_date, src='tt')
print(df)
# 提取前10分钟信息
df['time'] = pd.to_datetime(current_date + ' ' + df['time'])
t = pd.to_datetime(current_date).replace(hour=9, minute=40)
df_10 = df[df.time <= t]
print(df_10)
# 统计前十分钟成交量信息
vol = df_10.volume.sum()
print(vol)
1 根据当日成交量和多日成交量的均值计算涨跌幅
成交量涨跌幅的计算公式有两种,公式1计算方式如下:
成交量涨跌幅 = (10分钟成交量 - 昨日10分钟成交量) / 昨日10分钟成交量 × 100
成交量涨跌幅 = (10分钟成交量 - 10分钟成交量10日均值) / 10分钟成交量10日均值 × 100
# 通过公式1获取成交量涨跌幅
stock_table['昨日10分钟成交量'] = stock_table['10分钟成交量'].shift(-1)
stock_table['成交量涨跌幅1(%)'] = (stock_table['10分钟成交量']-stock_table['昨日10分钟成交量'])/stock_table['昨日10分钟成交量']*100
# 通过公式2获得成交量涨跌幅
ten_mean = stock_table['10分钟成交量'].sort_index().rolling(10, min_periods=1).mean()
stock_table['10分钟成交量10日均值'] = ten_mean
stock_table['成交量涨跌幅2(%)'] = (stock_table['10分钟成交量']-stock_table['10分钟成交量10日均值'])/stock_table['10分钟成交量10日均值']*100
2 做成交量涨跌幅和股价涨跌幅的相关性分析
导入需要的相关性分析库
from scipy.stats import pearsonr
'''3.通过相关性分析选取合适的衍生变量'''
from scipy.stats import pearsonr
# 通过公式1计算的相关性
corr = pearsonr(abs(stock_table['股价涨跌幅(%)'][:-1]), abs(stock_table['成交量涨跌幅1(%)'][:-1]))
print('通过公式1计算的相关系数r值为' + str(corr[0]) + ',显著性水平P值为' + str(corr[1]))
# 通过公式2计算的相关性
corr = pearsonr(abs(stock_table['股价涨跌幅(%)']), abs(stock_table['成交量涨跌幅2(%)']))
print('通过公式2相关系数r值为' + str(corr[0]) + ',显著性水平P值为' + str(corr[1]))
3 在画图时候,命令plt.twinx()和plt.title()等的作用
- plt.twinx()在代码中间加一行
- plt.title()添加标题
4 处理Excel文件的xlwings库
(1) 创建或打开Excel文件
创建一个新的Excel工作簿
import xlwings as xw
# 新建一个Excel文件,并设置为不可见
app = xw.App(visible=False)
wb = app.books.add() # app.books.open(文件路径)
其中,通常将visible
参数设置为False,这样程序就在后台运行了,如果想让运行的Excel在界面上显示,可以将visible设置为True。
创建完Excel工作簿后,可以通过下面的代码保存:
# 保存生成的Excel文件
wb.save(r'华小智.xlsx')
wb.close() # 退出工作簿
app.quit() # 退出程序
除了创建一个新的Excel工作簿,xlwings还可以打开已有的工作簿进行编辑,注意运行时原Excel文件不要处于打开状态
import xlwings as xw
app = xw.App(visible=False)
wb = app.books.open(文件路径)
(2) 获取工作表及单元格
控制单个工作表
sht = wb.sheets.['Sheet1']
新增一张工作表
sht = wb.sheets.add('新工作表')
改某个单元格的信息
sht.range('A1').value = '华小智'
获取某个单元格的信息
a = sht.range('A1').value
(3) 与pandas的交互
import pandas as pd
df = pd.DataFrame([[1, 2], [3, 4]], columns=['a', 'b'])
sht.range('A1').value = df
运行之后就会在Excel中生成一个2*2的表格数据了,其中列名为a和b
(4) 与Matplotlib库的交互
xlwings
还可以与Matplotlib库交互,将其产生的图片导入到Excel中,代码如下
import matplotlib.pyplot as plt
# 生成图片
fig = plt.figure()
x = [1, 2, 3]
y = [2, 4, 6]
plt.plot(x, y)
# 将产生的图片导入到Excel当中
sht.pictures.add(fig, name='图片1', update=True, left=500)
5 实战
# =============================================================================
# 14.4.2 Python创建Excel实战
# =============================================================================
import tushare as ts
import pandas as pd
import matplotlib.pyplot as plt
import xlwings as xw
'''1.获取数据'''
stock_code = '000002'
stock_name = '万科A'
start_date = '2020-02-01' # 设置起始日期,如果是太早的日期容易数据丢失,导致运行失败,所以如果运行失败,请将数据改到今年的数据
end_date = '2020-04-01' # 设置终止日期,注意点同上
# 股票时间区间内的K线图,用于提取开盘价等有用信息
stock_k = ts.get_hist_data(stock_code, start=start_date, end=end_date)
# 建立一个新的DataFrame,用于存储当前股票的信息
stock_table = pd.DataFrame()
# 遍历日期索引,提取所需要的数据
for current_date in stock_k.index:
# 通过loc选中K线图中对应current_date这天的数据
current_k_line = stock_k.loc[current_date]
# 提取这一天前10分钟股票信息
df = ts.get_tick_data(stock_code, date=current_date, src='tt')
df['time'] = pd.to_datetime(current_date + ' ' + df['time'])
t = pd.to_datetime(current_date).replace(hour=9, minute=40)
df_10 = df[df.time <= t]
vol = df_10.volume.sum() # 通过sum()函数求和
# 将数据信息放入字典中
current_stock_info = {
'名称': stock_name,
'日期': pd.to_datetime(current_date),
'开盘价': current_k_line.open,
'收盘价': current_k_line.close,
'股价涨跌幅(%)': current_k_line.p_change,
'10分钟成交量': vol
}
# 通过append的方式增加新的一行,忽略索引
stock_table = stock_table.append(current_stock_info, ignore_index=True)
# 通过set_index()函数将日期那一列设置为索引
stock_table = stock_table.set_index('日期')
# 设置列的顺序
order = ['名称', '开盘价', '收盘价', '股价涨跌幅(%)', '10分钟成交量']
stock_table = stock_table[order]
# 获得衍生变量:10分钟成交量涨跌幅(%)
stock_table['10分钟成交量10日均值'] = stock_table['10分钟成交量'].sort_index().rolling(10, min_periods=1).mean()
stock_table['10分钟成交量涨跌幅(%)'] = (stock_table['10分钟成交量'] - stock_table['10分钟成交量10日均值'])/stock_table['10分钟成交量10日均值']*100
# 选取所需要的列
target_columns = ['名称', '开盘价', '收盘价', '股价涨跌幅(%)', '10分钟成交量', '10分钟成交量涨跌幅(%)']
final_table = stock_table[target_columns]
print(final_table)
'''2.将数据导入到Excel中并可视化呈现'''
# 创建一个Excel文件,并设置为不可见
app = xw.App(visible=False)
wb = app.books.add()
# 新建一张Excel表,命名为stock_name,也即最开始定义的“万科A”
sht = wb.sheets.add(stock_name)
# 将之前生成的final_table表格导入到Excel中
sht.range('A1').value = final_table
# 数据可视化,并将图片导入到Excel当中
fig = plt.figure() # 设置一下要导入Excel中的图片
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 解决负号'-'显示为方块的问题
# 绘制第一个折线图:股价涨跌幅(%)
plt.plot(final_table.index, final_table['股价涨跌幅(%)'].apply(lambda x: abs(x)), label='股价涨跌幅(%)', color='red')
plt.legend(loc='upper left') # 设置图例位置
# 绘制第二个折线图:10分钟成交量涨跌幅(%)
plt.twinx() # 生成双坐标轴
plt.plot(final_table.index, final_table['10分钟成交量涨跌幅(%)'].apply(lambda x: abs(x)), label='10分钟成交量涨跌幅(%)', linestyle='--')
plt.legend(loc='upper right')
# 设置图片标题,自动调整x坐标轴刻度的角度并展示图片
plt.title(stock_name) # 设置标题
plt.gcf().autofmt_xdate() # 自动调整x坐标轴刻度的角度
# 把图片放到excel中
sht.pictures.add(fig, name='图1', update=True, left=500)
wb.save('股票量化分析1.xlsx')
wb.close()
app.quit()
print('股票策略分析及Excel生成完毕')
第16章 器学习之客户违约预测模型搭建
1 利用决策树构建客户违约预测模型的过程
(1) 模型搭建
## 一、模型搭建
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
df = pd.read_excel('D:\\客户信息及违约表现.xlsx')
# 1.提取特征变量和目标变量
X = df.drop(columns='是否违约')
y = df['是否违约']
# 2.划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1) #随机数
# 3.模型训练及搭建
clf = DecisionTreeClassifier(max_depth=3)
clf = clf.fit(X_train, y_train)
因为每次运行程序时,train_test_split()
函数都是随机划分数据的,如果想每次划分数据产生的内容都是一致的,可以设置random_state
参数
(2) 模型预测及评估
# # 二、模型预测及评估
# 1.直接预测是否违约
y_pred = clf.predict(X_test)
print(y_pred)
# 将预测值和实际值汇总看一下
a = pd.DataFrame() # 创建一个空DataFrame
a['预测值'] = list(y_pred)
a['实际值'] = list(y_test)
print(a.tail()) #尾部5行的预测值和实际值
# 查看模型预测准确度
from sklearn.metrics import accuracy_score
score = accuracy_score(y_pred, y_test)
print(score) #准确率 0.825
其实分类决策树模型本质预测的并不是准确的0或1的分类,而是预测其属于某一分类的概率,可以通过如下代码查看预测属于各个分类的概率
# 2.预测不违约&违约概率
y_pred_proba = clf.predict_proba(X_test)
print(y_pred_proba) # 打印看看预测的不违约&违约概率,此时获得y_pred_proba是个
# 二维数组,共两列,左列为不违约概率,右列为违约概率
# 只查看违约概率,其中中括号中第一个元素:冒号表示全部行,
# 第二个元素:1表示第二列,如果把
print(y_pred_proba[:, 1])
(3) 模型预测效果评估
在商业实战中,我们更关心下面两个指标:
真正率(命中率) | True Positive Rate(TPR) | TPR=TP/ (TP+ FN) |
---|---|---|
假正率(假警报率) | False Positive Rate(FPR) | FPR= FP / (FP + TN) |
# 3.模型预测效果评估
# ROC曲线相关知识
from sklearn.metrics import roc_curve
fpr, tpr, thres = roc_curve(y_test.values, y_pred_proba[:, 1])
# 将阈值tpr、假警报率fpr、命中率tpr汇总看一下
a = pd.DataFrame() # 创建一个空DataFrame
a['阈值'] = list(thres)
a['假警报率'] = list(fpr)
a['命中率'] = list(tpr)
print(a)
阈值 假警报率 命中率
0 2.000000 0.000000 0.000000
1 1.000000 0.000000 0.162162
2 0.959350 0.015873 0.554054
3 0.750000 0.023810 0.567568
4 0.442177 0.182540 0.783784
5 0.139955 1.000000 1.000000
一个优秀的客户违约预测模型,我们希望命中率(TPR)尽可能的高,即能尽可能地揪出坏人,同时也希望假警报率(FPR)能尽可能的低,即不要误伤好人。
然而这两者往往成正相关性,因为一旦当调高阈值,比如认为违约率超过90%的才认定为违约,那么会导致假警报率很低,但是命中率也很低。而如果降低阈值的话,比如认为违约率超过10%就认定为违约,那么命中率就会很高,但是假警报率也会很高。
因此为了衡量一个模型的优劣,数据科学家根据不同阈值下的命中率和假警报率绘制了如下的曲线图,称之为ROC曲线
# 绘制ROC曲线,注意图片展示完要将其关闭才会执行下面的程序
import matplotlib.pyplot as plt
plt.plot(fpr, tpr)
plt.show()
在某一个阈值条件下,我们希望命中率能尽可能的高,而假警报率尽可能的低。
数值比较上可以使用AUC值来衡量模型的好坏,AUC值(Area Under Curver)指在曲线下面的面积。
该面积的取值范围通常为0.5到1,0.5表示随机判断,1则代表完美的模型。
# 求出AUC值
from sklearn.metrics import roc_auc_score
score = roc_auc_score(y_test.values, y_pred_proba[:, 1])
print(score)
在商业实战中:
- AUC值能达到0.75以上就已经可以接受了
- 如果能达到0.85以上,则为非常不错的模型了