本人CSDN博客为“仿生程序员会梦见电子羊吗”,本文基于markdown本文书写,平台及软件为CSDN与Typora,文中图片存储地址为CSDN,故部分图片可能带有“CSDN@仿生程序员会梦见电子羊吗”的水印,属于本人原创,用于“数据挖掘与商务智能决策”的平时作业及大作业部分。
本篇内容为第一、二章内容,数据分析与Numpy、 pandas、 Matplotlib的应用。
为便于阅读,我将文章内容分为以下几个板块:
其中,各板块的介绍如下:
相对路径
文件相对路径,即代码所在的文件夹,例如上面案例中写的
data.to_excel(‘data.xlsx’)就是在代码所在的文件夹生成Excel文件。
绝对路径
文件绝对路径,就是文件完整的路径名称,例如’E:\大数据分析\data.xlsx’就是
绝对路径,不过因为在Python中反斜杠“\”经常有特殊含义,比如说“\n”
表示换行,所以通常建议写绝对路径的时候写两个反斜杠取消可能存在的单个
反斜杠的特殊含义,写成’E:\大数据分析\data.xlsx’。
除了用两个反斜杠来取消一个反斜杠的特殊意义外,还可以在文件路径的字符
串前面加一个r,也可以取消单个反斜杠的特殊含义。
import numpy as np # 用np代替numpy,让代码更简洁
a = [1, 2, 3, 4] # 创建列表a
b = np.array([1, 2, 3, 4]) #从列表ach
print(a)
print(b)
print(type(a)) #打印a类型
print(type(b)) #打印b类型
[1, 2, 3, 4]
[1 2 3 4]
# 接下来通过列表索引和数组索引来访问列表和数组中的元素,代码如下:
print(a[1])
print(b[1])
print(a[0:2])
print(b[0:2])
2
2
[1, 2]
[1 2]
从上面结果可以看到列表和数组有着相同的索引机制,唯一的区别好像就是数组里面是通过空格分隔元素,而列表用的是逗号。
从上面的分析得知Numpy数组和列表很类似,那么为什么Python又要创建一个Numpy库呢?其原因很多,这里主要讲两点:
1.数组可以比较方便的进行一些数学运算,而列表则比较麻烦;
2.数组可以支持多维的数据,而列表通常只能储存一维的数据。
c = a * 2
d = b * 2
print(c)
print(d)
[1, 2, 3, 4, 1, 2, 3, 4]
[2 4 6 8]
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]]
[[1 2]
[3 4]
[5 6]]
可以看到列表虽然包含着三个小列表,但其还是一个一维的结构,而创建的二维数组则是一个三行两列的二维结构内容,这个也是之后学习pandas库的核心内容了,因为数据数据处理中经常用到二维数组,也即二维表格结构。
# 创建一维数组
b = np.array([1, 2, 3, 4])
# 创建二维数组
f = np.array([[1,2], [3,4], [5,6]])
print(b)
print(f)
[1 2 3 4]
[[1 2]
[3 4]
[5 6]]
除此之外,还有一些常见的创建数组的方式,这里以一维数组为例,我们还可以采用np.arange()函数来产生一维数组,其中括号里可以选择1个或2个或3个参数,代码如下:
# 一个参数 参数值为终点,起点取默认值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]
我们还可以通过np.random模块来创建随机一维数组,比如可以通过np.random.randn(3)来创建一个服从正太分布(均值为0,方差为1的分布)的3个随机数一维数组,代码如下:
a = np.random.randn(3)
print(a) # 因为随机,所以每次运行的结果都会不太一样
[ 0.39911225 -0.94948119 0.84185058]
如果把np.random.randn(3)换成np.random.rand(3),那生成的就是0-1之间的3个随机数,这个在之后2.3.1小节演示绘制散点图的时候会用到。
至于二维数组的创建与学习,可以利用一维数组中的np.arange()函数和reshape方法产生一个二维数组,比如将0到11个数转换成3行4列的二维数组,代码如下:
a = np.arange(12).reshape(3,4)
print(a)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
这里再简单提一种随机二维数组的创建,代码如下。其中np.random.randint()函数用来创建随机整数,括号里第一个元素0表示起始数,第二个元素10表示终止数,第三个元素(4, 4)则表示生成一个4行4列的二维数组
a = np.random.randint(0, 10, (4, 4))
print(a)
[[4 9 8 4]
[1 7 6 0]
[6 6 1 1]
[9 6 7 3]]
相较于Numpy来说,Pandas更善于处理二维数据。Pandas主要有两种数据结构:Series和DataFrame。Series类似于通过Numpy产生的一维数组,不同的是Series对象不仅包含数值,还包含一组索引,其创建方式如下:
import pandas as pd
s1 = pd.Series(['丁一', '王二', '张三'])
print(s1)
0 丁一
1 王二
2 张三
dtype: object
# 它也是一个一维数据结构,并且对于每个元素都有一个行索引可以用来定位,比如可以通过s1[1]来定位到第二个元素“王二”。
print(s1[1])
王二
Series单独使用相对较少,pandas主要采用DataFrame数据结构。DataFrame是一种二维表格数据结构,直观一点的话可以将其看作一个Excel表格。
有三种DataFrame常见的创建方法:通过列表创建、通过字典创建及通过二维数组创建。
1. 通过列表创建DataFrame
import pandas as pd
a = pd.DataFrame([[1, 2], [3, 4], [5, 6]])
a # 在Jupyter Nobebook中,代码框中最后一行代码可以只输入变量名称,即可自动打印,而无需通过print()函数
0 | 1 | |
---|---|---|
0 | 1 | 2 |
1 | 3 | 4 |
2 | 5 | 6 |
可以看到通过pandas的DataFrame功能生成的二维数组更像我们在Excel中看到二维表格数据,它也有行索引和列索引,其中这里的索引序号都是从0开始的。
# 我们还可以自定义其列索引和行索引名称,代码如下:
a = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['date', 'score'], index=['A', 'B', 'C'])
# 通过列表生成DataFrame还可以采用如下的方式,演示代码如下:
a = pd.DataFrame() # 创建一个空DataFrame
date = [1, 3, 5]
score = [2, 4, 6]
a['date'] = date
a['score'] = score
a
date | score | |
---|---|---|
0 | 1 | 2 |
1 | 3 | 4 |
2 | 5 | 6 |
2. 通过字典创建DataFrame
# 通过Pandas创建二维数组 - 字典法
b = pd.DataFrame({'a': [1, 3, 5], 'b': [2, 4, 6]}, index=['x', 'y', 'z'])
b # 在Jupyter Notebook编辑器中可以直接输入b进行查看
a | b | |
---|---|---|
x | 1 | 2 |
y | 3 | 4 |
z | 5 | 6 |
# 如果想让字典键变成行索引,可以通过from_dict的方式来将字典转换成DataFrame,并同时设置orient参数为index,代码如下:
c = pd.DataFrame.from_dict({'a': [1, 3, 5], 'b': [2, 4, 6]}, orient="index")
c
print(c) # 也可以直接输入c进行查看变量结果
0 1 2
a 1 3 5
b 2 4 6
其中orient参数指定字典键对应的方向,默认值为columns,如果不设置成index的话,则还是默认字典键为列索引
补充知识点:通过.T来对表格进行转置
b = pd.DataFrame({'a': [1, 3, 5], 'b': [2, 4, 6]})
print(b)
print(b.T)
a b
0 1 2
1 3 4
2 5 6
0 1 2
a 1 3 5
b 2 4 6
3. 通过二维数组创建
import numpy as np
np.arange(12).reshape(3,4)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
# 通过Numpy产生的二维数组,也可以创建DataFrame,这里以2.1.3小节里提到的二维数组为例生成一个3行4列的DataFrame,代码如下:
import numpy as np
d = pd.DataFrame(np.arange(12).reshape(3,4), index=[1, 2, 3], columns=['A', 'B', 'C', 'D'])
d
A | B | C | D | |
---|---|---|---|---|
1 | 0 | 1 | 2 | 3 |
2 | 4 | 5 | 6 | 7 |
3 | 8 | 9 | 10 | 11 |
补充知识点:修改行索引或列索引名称
a = pd.DataFrame([[1, 2], [3, 4]], columns=['date', 'score'], index=['A', 'B'])
a
date | score | |
---|---|---|
A | 1 | 2 |
B | 3 | 4 |
# 如果想对索引进行重命名的话,rename()函数的使用方法如下:
a = a.rename(index={'A':'阿里', 'B':'腾讯'}, columns={'date':'日期','score':'分数'})
a
日期 | 分数 | |
---|---|---|
阿里 | 1 | 2 |
腾讯 | 3 | 4 |
补充知识点:这里通过rename之后并没有改变原表格结构,需要重新赋值给a才能改变原表格;或者在rename()中设置inplace参数为True,也能实现真正替换,代码如下:
a = pd.DataFrame([[1, 2], [3, 4]], columns=['date', 'score'], index=['A', 'B'])
a.rename(index={'A':'阿里', 'B':'腾讯'}, columns={'date':'日期','score':'分数'}, inplace=True) # 另一种方法
a
日期 | 分数 | |
---|---|---|
阿里 | 1 | 2 |
腾讯 | 3 | 4 |
# 通过.values属性,也可以查看此时的index值
print(a.index.values)
['阿里' '腾讯']
# 如果想给行索引命名,也可以通过如下代码
a.index.name = '艾尔海森'
a
日期 | 分数 | |
---|---|---|
艾尔海森 | ||
阿里 | 1 | 2 |
腾讯 | 3 | 4 |
# 如果想把行索引变成某列的内容,可以使用set_index()函数,代码如下:
a = a.set_index('日期')
a
分数 | |
---|---|
日期 | |
1 | 2 |
3 | 4 |
# 如果此时想把行索引换成数字索引,则可以使用reset_index()函数,代码如下:
a = a.reset_index()
a
日期 | 分数 | |
---|---|---|
0 | 1 | 2 |
1 | 3 | 4 |
1. 文件的读取
# 输入以下代码,用于读取Excel数据:
import pandas as pd
data = pd.read_excel('D://coder//randomnumbers//datamining//Chap2//data.xlsx') # data为DataFrame结构
data.head() # 通过head()可以查看前5行数据,如果写成head(10)则可以查看前10行数据
D:\coder\randomnumbers\venv\lib\site-packages\openpyxl\worksheet\header_footer.py:48: UserWarning: Cannot parse header or footer so it will be ignored
warn("""Cannot parse header or footer so it will be ignored""")
date | score | price | |
---|---|---|---|
0 | 2018-09-03 | 70 | 23.55 |
1 | 2018-09-04 | 75 | 24.43 |
2 | 2018-09-05 | 65 | 23.41 |
3 | 2018-09-06 | 60 | 22.81 |
4 | 2018-09-07 | 70 | 23.21 |
# 其中read_excel还可以设定参数,使用方式如下:
# pd.read_excel('data.xlsx', sheet_name=0, encoding='utf-8')
# 输入以下代码,用于读取CSV文件:
data = pd.read_csv('data.csv')
data.head()
date | score | price | |
---|---|---|---|
0 | 2018-09-03 | 70 | 23.55 |
1 | 2018-09-04 | 75 | 24.43 |
2 | 2018-09-05 | 65 | 23.41 |
3 | 2018-09-06 | 60 | 22.81 |
4 | 2018-09-07 | 70 | 23.21 |
# read_csv也可以指定参数,使用方式如下:
# data = pd.read_csv('data.csv', delimiter=',', encoding='utf-8')
2. 文件写入
# 先生成一个DataFrame
data = pd.DataFrame([[1, 2], [3, 4], [5, 6]], columns=['A列','B列'])
# 将DataFrame导入到Excel当中
data.to_excel('data_new.xlsx')
运行之后将在代码所在的文件夹生成一个名为data_new的Excel文件m
在上表中,保存的Excel第一列还保留了索引信息,如果想将其删去,可以设置to_excel的参数index为False。to_excel的常见参数有如下一些:sheet_name:数据表名;index:True or False,默认为True保存索引信息,即输出文件的第一列为索引值,选择False的话则忽略索引信息;columns:选择所需要的列;encoding:编码方式。
例如要将数据表格导入到Excel文件中并忽略行索引信息,则代码如下:
data.to_excel('data_new.xlsx', index=False)
# 通过类似的方式,可以将数据写入到CSV文件当中,代码如下:
data.to_csv('data_new.csv')
和to_excel类似,to_csv也可以设置index、columns、encoding等参数。注意,如果在导出CSV文件事出现了中文乱码现象,且encoding参数设置成“utf-8”失效,则需要将encoding参数设置成“utf_8_sig”,代码如下:
data.to_csv('演示.csv', index=False, encoding="utf_8_sig")
补充知识点:文件相对路径与绝对路径
相对路径
文件相对路径,即代码所在的文件夹,例如上面案例中写的data.to_excel(‘data.xlsx’)就是在代码所在的文件夹生成Excel文件。
绝对路径
文件绝对路径,就是文件完整的路径名称,例如’E:\大数据分析\data.xlsx’就是绝对路径,不过因为在Python中反斜杠“\”经常有特殊含义,比如说“\n”表示换行,所以通常建议写绝对路径的时候写两个反斜杠取消可能存在的单个反斜杠的特殊含义,写成’E:\\大数据分析\\data.xlsx’。
除了用两个反斜杠来取消一个反斜杠的特殊意义外,还可以在文件路径的字符串前面加一个r,也可以取消单个反斜杠的特殊含义,代码如下:
data.to_excel('E:\\大数据分析\\data.xlsx') # 绝对路径推荐写法1,此时E盘要有一个名为“大数据分析”的文件夹
data.to_excel(r'E:\大数据分析\data.xlsx') # 绝对路径推荐写法2,此时E盘要有一个名为“大数据分析”的文件夹
# 首先创建一个三行三列的表格,行索引设定为r1、r2和r3,列索引设定为c1、c2和c3,以此为例来演示数据的读取与筛选,代码如下:
import pandas as pd
data = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=['r1', 'r2', 'r3'], columns=['c1', 'c2', 'c3'])
data
c1 | c2 | c3 | |
---|---|---|---|
r1 | 1 | 2 | 3 |
r2 | 4 | 5 | 6 |
r3 | 7 | 8 | 9 |
data = pd.DataFrame(np.arange(1,10).reshape(3,3), index=['r1', 'r2', 'r3'], columns=['c1', 'c2', 'c3'])
data
c1 | c2 | c3 | |
---|---|---|---|
r1 | 1 | 2 | 3 |
r2 | 4 | 5 | 6 |
r3 | 7 | 8 | 9 |
1. 数据选取
(1) 按列选取数据
# 通过以下代码可以通过列来选取数据,这里先选取单列。
a = data['c1']
a
r1 1
r2 4
r3 7
Name: c1, dtype: int32
此时返回的结果里没有表头信息了,这是因为通过data[‘c1’]选取一列的时候返回的是一个一维序列结构的类,也可以通过如下代码返回一个二维的表格数据。
b = data[['c1']]
b
c1 | |
---|---|
r1 | 1 |
r2 | 4 |
r3 | 7 |
若要选取多列,则需要在中括号[]中给个列表,比如要读取c1和c3列,则可以写为data[[‘c1’, ‘c3’]]。这里需要特别注意的是,必须是一个列表,而不能是data[‘c1’, ‘c3’],代码如下:
c = data[['c1', 'c3']]
c
c1 | c3 | |
---|---|---|
r1 | 1 | 3 |
r2 | 4 | 6 |
r3 | 7 | 9 |
(2) 按行选取数据
# 选取第2到3行的数据,注意序号从0开始,左闭右开
a = data[1:3]
a
c1 | c2 | c3 | |
---|---|---|---|
r2 | 4 | 5 | 6 |
r3 | 7 | 8 | 9 |
而pandas推荐使用iloc方法来根据行的序号进行行选取,它是根据行序号选取的另一种方法,pandas觉得这样更加直观,不会像data[1:3]可能会引起混淆,代码如下:
b = data.iloc[1:3]
b
c1 | c2 | c3 | |
---|---|---|---|
r2 | 4 | 5 | 6 |
r3 | 7 | 8 | 9 |
而且如果要选取单行的话,就必须得用iloc了,比如选择倒数第一行,代码如下:
c = data.iloc[-1]
c
c1 7
c2 8
c3 9
Name: r3, dtype: int32
除了通过行的序号选取外,还可以通过loc方法根据行的名称来进行选取,代码如下:
d = data.loc[['r2', 'r3']]
d
c1 | c2 | c3 | |
---|---|---|---|
r2 | 4 | 5 | 6 |
r3 | 7 | 8 | 9 |
有的时候如果行数很多,可以通过head()方法来选取前5行,代码如下:
e = data.head()
e
c1 | c2 | c3 | |
---|---|---|---|
r1 | 1 | 2 | 3 |
r2 | 4 | 5 | 6 |
r3 | 7 | 8 | 9 |
这里因为只创建了3行数据,所以通过data.head()会把全部数据都取到,如果只想取前两行的数据,可以写成data.head(2)。
(3) 按照区块来选取
# 如果想选取某几行的某几列,则可以通过如下代码来实现,比如获得c1和c3列的前二行。
a = data[['c1', 'c3']][0:2] # 也可写成data[0:2][['c1', 'c3']]
a
c1 | c3 | |
---|---|---|
r1 | 1 | 3 |
r2 | 4 | 6 |
# 在实战中,通常采用iloc和列选取混合的方式来选取特定的区块或值,代码如下:
b = data.iloc[0:2][['c1', 'c3']]
b
c1 | c3 | |
---|---|---|
r1 | 1 | 3 |
r2 | 4 | 6 |
# 如果要选取单个的值,那么该方法就更有优势,比如选取c3列第一行的信息,就不能写成data['c3'][0]或data[0]['c3']了。下面的写法则比较清晰,iloc[0]先选取第一行,然后再选取c3列。
c = data.iloc[0]['c3']
c
3
# 也可以通过iloc和loc方法来同时选择行和列,代码如下:
d = data.loc[['r1', 'r2'], ['c1', 'c3']]
e = data.iloc[0:2, [0, 2]]
print(d)
print(e)
c1 c3
r1 1 3
r2 4 6
c1 c3
r1 1 3
r2 4 6
# 老版本DataFrame还有一个ix选择区域的方法,它也可以同时选择行和列,而且里面的内容不像loc或者iloc必须为字符索引或者数字索引,代码如下:
#f = data.ix[0:2, ['c1', 'c3']]
#f
2. 数据筛选
# 在方括号里还可以通过判断条件来过滤行,比如选取c1列数字大于1的行,代码如下:
a = data[data['c1'] > 1]
a
c1 | c2 | c3 | |
---|---|---|---|
r2 | 4 | 5 | 6 |
r3 | 7 | 8 | 9 |
# 如果有多个筛选条件,则可以通过“&”符号(表示“且”)或“|”(表示“或”)连接,比如这边筛选,c1列数字大于1且c2列数字小于8的行,代码如下,注意要记得加判断条件两旁的小括号。
b = data[(data['c1'] > 1) & (data['c2'] < 8)]
b
c1 | c2 | c3 | |
---|---|---|---|
r2 | 4 | 5 | 6 |
3. 数据整体情况查看
# 通过表格的shape属性,可以查看表格整体的行数和列数,在表格数据量较大的时候能快速了解表格的行数和列数。
data.shape
(3, 3)
# 通过表格的describe()函数可以快速的查看表格每一列的数量、平均值、标准差、最小值、25分位数、50分位数、75分位数、最大值等信息,代码如下:
data.describe()
c1 | c2 | c3 | |
---|---|---|---|
count | 3.0 | 3.0 | 3.0 |
mean | 4.0 | 5.0 | 6.0 |
std | 3.0 | 3.0 | 3.0 |
min | 1.0 | 2.0 | 3.0 |
25% | 2.5 | 3.5 | 4.5 |
50% | 4.0 | 5.0 | 6.0 |
75% | 5.5 | 6.5 | 7.5 |
max | 7.0 | 8.0 | 9.0 |
# 通过value_counts()函数则可以快速的查看某一列都有什么数据,以及该数据出现的频次,代码如下:
data['c1'].value_counts()
1 1
4 1
7 1
Name: c1, dtype: int64
4. 数据运算、排序与删除
(1) 数据运算
# 从已有的列中,通过数据运算创造一个新的一列,代码如下:
data['c4'] = data['c3'] - data['c1']
data.head()
c1 | c2 | c3 | c4 | |
---|---|---|---|---|
r1 | 1 | 2 | 3 | 2 |
r2 | 4 | 5 | 6 | 2 |
r3 | 7 | 8 | 9 | 2 |
(2) 数据排序
# 通过sort_values()可以根据列对数据进行排序,比如要对c2列进行降序排序,代码如下:
a = data.sort_values(by='c2', ascending=False)
a
c1 | c2 | c3 | c4 | |
---|---|---|---|---|
r3 | 7 | 8 | 9 | 2 |
r2 | 4 | 5 | 6 | 2 |
r1 | 1 | 2 | 3 | 2 |
# 其实如果是按列筛选,我们也可以直接写成如下代码,不用写“by=”,效果一样:
a = data.sort_values('c2', ascending=False)
a
c1 | c2 | c3 | c4 | |
---|---|---|---|---|
r3 | 7 | 8 | 9 | 2 |
r2 | 4 | 5 | 6 | 2 |
r1 | 1 | 2 | 3 | 2 |
# 此外,通过sort_index()可以根据行索引进行排序,如按行索引进行升序排列,代码如下:
a = a.sort_index()
a
c1 | c2 | c3 | c4 | |
---|---|---|---|---|
r1 | 1 | 2 | 3 | 2 |
r2 | 4 | 5 | 6 | 2 |
r3 | 7 | 8 | 9 | 2 |
(3) 数据删除
# 例如删除c1列的数据,代码如下:
a = data.drop(columns='c1')
a
c2 | c3 | c4 | |
---|---|---|---|
r1 | 2 | 3 | 2 |
r2 | 5 | 6 | 2 |
r3 | 8 | 9 | 2 |
# 删除多列的数据,比如c1和c3列,可以通过列表的方式将所需删除的列声明,代码如下:
b = data.drop(columns=['c1', 'c3'])
b
c2 | c4 | |
---|---|---|
r1 | 2 | 2 |
r2 | 5 | 2 |
r3 | 8 | 2 |
# 如果要删除行数据,比如删去第一行和第三行的数据,代码如下:
c = data.drop(index=['r1','r3'])
c
c1 | c2 | c3 | c4 | |
---|---|---|---|---|
r2 | 4 | 5 | 6 | 2 |
注意这里要输入行索引的名称而不是数字序号,不过如果行索引名称本来就是数字,那么可以输入对应数字。上面删除数据后又赋值给新的变量不会改变原来表格data的结构,如果想改变原来表格的结构,可以令inplace参数为True,代码如下:
data.drop(index=['r1','r3'], inplace=True)
data
c1 | c2 | c3 | c4 | |
---|---|---|---|---|
r2 | 4 | 5 | 6 | 2 |
# 假设有如下两个DataFrame表格,需要对它们进行合并:
import pandas as pd
df1 = pd.DataFrame({'公司': ['万科', '阿里', '百度'], '分数': [90, 95, 85]})
df2 = pd.DataFrame({'公司': ['万科', '阿里', '京东'], '股价': [20, 180, 30]})
df1
公司 | 分数 | |
---|---|---|
0 | 万科 | 90 |
1 | 阿里 | 95 |
2 | 百度 | 85 |
df2
公司 | 股价 | |
---|---|---|
0 | 万科 | 20 |
1 | 阿里 | 180 |
2 | 京东 | 30 |
1. merge()函数
# merge()函数根据一个或多个键将不同表格中的行连接起来,示例如下:
df3 = pd.merge(df1, df2)
df3
公司 | 分数 | 股价 | |
---|---|---|---|
0 | 万科 | 90 | 20 |
1 | 阿里 | 95 | 180 |
可以看到通过merge()函数直接选取相同的列名(“公司”这一列)进行合并,而且默认选取的是两种表共有的列内容(万科、阿里),有的时候如果相同的列名不止一个,可以通过on参数指定按照哪一列进行合并,代码如下:
df3 = pd.merge(df1, df2, on=‘公司’)
默认的合并其实是取交集(inner连接),也即取两表共有的内容,如果想取并集(outer连接),也即选取两表所有的内容,可以设置how参数,代码如下:
df3 = pd.merge(df1, df2, how='outer')
df3
公司 | 分数 | 股价 | |
---|---|---|---|
0 | 万科 | 90.0 | 20.0 |
1 | 阿里 | 95.0 | 180.0 |
2 | 百度 | 85.0 | NaN |
3 | 京东 | NaN | 30.0 |
如果想保留左表全部内容,而对右表不太在意的话,可以将how参数设置为left:
df3 = pd.merge(df1, df2, how='left')
df3
公司 | 分数 | 股价 | |
---|---|---|---|
0 | 万科 | 90 | 20.0 |
1 | 阿里 | 95 | 180.0 |
2 | 百度 | 85 | NaN |
同理,如果想保留右表全部内容,而对左表不太在意的话,可以将how参数设置为right。
# 如果想根据行索引进行合并,可以通过设置left_index和right_index参数,代码如下:
df3 = pd.merge(df1, df2, left_index=True, right_index=True)
df3
公司_x | 分数 | 公司_y | 股价 | |
---|---|---|---|---|
0 | 万科 | 90 | 万科 | 20 |
1 | 阿里 | 95 | 阿里 | 180 |
2 | 百度 | 85 | 京东 | 30 |
补充知识点:根据行索引合并的join()函数
通过join()函数也可以根据行索引进行表格合并。join()函数也是一种数据表拼接的常见函数,它是通过行索引进行合并,演示代码如下:
df3 = df1.join(df2, lsuffix='_x', rsuffix='_y')
df3
公司_x | 分数 | 公司_y | 股价 | |
---|---|---|---|---|
0 | 万科 | 90 | 万科 | 20 |
1 | 阿里 | 95 | 阿里 | 180 |
2 | 百度 | 85 | 京东 | 30 |
注意在通过join()函数进行拼接的时候,两张表格中不能有名字相同的列名,如果存在的话,则需要设置lsuffix参数(左表同名列的后缀,suffix的中文翻译就是后缀的意思,l表示left)和rsuffix参数(右表同名列的后缀,这里的r表示right),没有相同列名的话,则可以直接写df1.join(df2),相对于merge()函数写法较为简洁一些。
实战中可以只记merge()函数的用法,这里讲解join()函数的目的是为了看到别人用join()函数的时候能够理解。该知识点在14.3.3小节进行数据表合并的时候便有应用。
2. concat()函数
# 默认情况下,axis=0,按行方向进行连接。
df3 = pd.concat([df1,df2], axis=0)
df3
公司 | 分数 | 股价 | |
---|---|---|---|
0 | 万科 | 90.0 | NaN |
1 | 阿里 | 95.0 | NaN |
2 | 百度 | 85.0 | NaN |
0 | 万科 | NaN | 20.0 |
1 | 阿里 | NaN | 180.0 |
2 | 京东 | NaN | 30.0 |
此时行索引为原来两张表各自的索引,如果想重置索引,可以使用6.2.1小节讲过的reset_index()方法将索引重置,或者在concat()中设置ignore_index=True,忽略原有索引,按新数字索引进行排序。
# 如果想按列方向进行连接,可以设置axis参数为1。
df3 = pd.concat([df1,df2],axis=1)
df3
公司 | 分数 | 公司 | 股价 | |
---|---|---|---|---|
0 | 万科 | 90 | 万科 | 20 |
1 | 阿里 | 95 | 阿里 | 180 |
2 | 百度 | 85 | 京东 | 30 |
3. append()函数
# append()函数可以说concat()函数的简化版,效果和pd.concat([df1,df2]) 类似,代码如下:
df3 = df1.append(df2)
df3
公司 | 分数 | 股价 | |
---|---|---|---|
0 | 万科 | 90.0 | NaN |
1 | 阿里 | 95.0 | NaN |
2 | 百度 | 85.0 | NaN |
0 | 万科 | NaN | 20.0 |
1 | 阿里 | NaN | 180.0 |
2 | 京东 | NaN | 30.0 |
# append()函数还有个常用的功能,和列表.append()一样,可用来新增元素,代码如下:
df3 = df1.append({'公司': '腾讯', '分数': '90'}, ignore_index=True)
df3
公司 | 分数 | |
---|---|---|
0 | 万科 | 90 |
1 | 阿里 | 95 |
2 | 百度 | 85 |
3 | 腾讯 | 90 |
1. 绘制折线图
%matplotlib inline
import matplotlib.pyplot as plt
x = [1, 2, 3]
y = [2, 4, 6]
plt.plot(x, y) # 绘制折线图
import pylab as pl
pl.xticks(rotation=45)
plt.show() # 展示图形
如果想让x和y之间有些数学关系,列表是不太容易进行数学运算的,这时候就可以通过2.1.2小节所讲的Numpy库引入一维数组进行数学运算,代码如下:
import numpy as np
import matplotlib.pyplot as plt
x1 = np.array([1, 2, 3])
# 第一条线:y = x + 1
y1 = x1 + 1
plt.plot(x1, y1) # 使用默认参数画图
# 第二条线:y = x*2
y2 = x1*2
# color设置颜色,linewidth设置线宽,单位像素,linestyle默认为实线,“--”表示虚线
plt.plot(x1, y2, color='red', linewidth=3, linestyle='--')
plt.show()
2. 绘制柱状图
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [5, 4, 3, 2, 1]
plt.bar(x, y)
plt.show()
3. 绘制散点图
import matplotlib.pyplot as plt
import numpy as np
x = np.random.rand(10)
y = np.random.rand(10)
plt.scatter(x, y)
plt.show()
4. 绘制直方图
import matplotlib.pyplot as plt
import numpy as np
# 随机生成10000个服从正态分布的数据
data = np.random.randn(10000)
# 绘制直方图,bins为颗粒度,即直方图的长条形数目,edgecolor为长条形边框颜色
plt.hist(data, bins=40, edgecolor='black')
plt.show()
补充知识点:在pandas库中的快捷绘图技巧
# 这种写法只适合pandas中的DataFrame,不能直接用于Numpy的数组
import pandas as pd
df = pd.DataFrame(data) # 将绘制直方图中的data数组转换成DataFrame()格式
df.hist(bins=40, edgecolor='black')
array([[]],
dtype=object)
# 此外,除了写df.hist()外,还可以通过下面这种pandas库里的通用绘图代码绘图:
df.plot(kind='hist')
这里是通过设置kind参数为hist来绘制直方图,通过这种通用绘图代码,pandas库除了可以便捷的绘制直方图外,它还可以通过设置kind参数快捷地绘制其他图形,演示代码如下,首先通过2.2.1节的知识点创建一个二维DataFrame表格df。
import pandas as pd
df = pd.DataFrame([[8000, 6000], [7000, 5000], [6500, 4000]], columns=['人均收入', '人均支出'], index=['北京', '上海', '广州'])
df
人均收入 | 人均支出 | |
---|---|---|
北京 | 8000 | 6000 |
上海 | 7000 | 5000 |
广州 | 6500 | 4000 |
# 此时可以通过pandas同时绘制折线图或者柱状图,代码如下:
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 解决负号'-'显示为方块的问题
df['人均收入'].plot(kind='line') # kind=line绘制折线图,不设置则默认折线图
df['人均收入'].plot(kind='bar') # kind=bar绘制柱状图
df['人均收入'].plot(kind='pie') # kind=pie绘制饼图
df['人均收入'].plot(kind='box') # kind=box绘制箱体图
1. 添加文字说明
# 通过plt.title(name)给图画添加标题;通过plt.xlable(),plt.ylable()用于添加x轴和y轴标签。
import matplotlib.pyplot as plt
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() # 显示图片
2. 添加图例
# 通过plt.legend()来添加图例,添加前需要设置好lable(标签)参数,代码如下:
import numpy as np
import matplotlib.pyplot as plt
# 第一条线, 设定标签lable为y = x + 1
x1 = np.array([1, 2, 3])
y1 = x1 + 1
plt.plot(x1, y1, label='y = x + 1')
# 第二条线, 设定标签lable为y = x*2
y2 = x1*2
plt.plot(x1, y2, color='red', linestyle='--', label='y = x*2')
plt.legend(loc='upper left') # 图例位置设置为左上角
plt.show()
3. 设置双坐标轴
上面的例子可以在一张图里画出两条线,但如果两条线的取值范围相差比较大,那么画出来的图效果便不太好,那么此时如何来画出两条y坐标轴呢?可以在画完第一个图之后,写如下一行代码即可设置双坐标轴。
plt.twinx()
需要注意的是如果设置了双坐标轴,那么添加图例的时候,每画一次图就得添加一次,而不能在最后统一添加。这里以y = x和y = x^2为例,演示下如何设置双坐标轴,代码如下:
import numpy as np
import matplotlib.pyplot as plt
# 第一条线, 设定标签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()
4. 设置图片大小
plt.rcParams['figure.figsize'] = (8, 6)
x = [1, 2, 3]
y = [2, 4, 6]
plt.plot(x, y)
plt.show() # 显示图片
5. 设置X轴刻度的角度
import matplotlib.pyplot as plt
x = [1, 2, 3]
y = [2, 4, 6]
plt.plot(x, y) # 绘制折线图
import pylab as pl
pl.xticks(rotation=45) # 设置角度为45度
plt.show() # 展示图形
6. 解决中文显示问题
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 解决负号'-'显示为方块的问题
x = [1, 2, 3]
y = [2, 4, 6]
plt.plot(x, y)
plt.title('中文标题') # 添加标题
plt.xlabel('中文X轴') # 添加X轴标签
plt.ylabel('中文Y轴') # 添加Y轴标签
plt.show() # 显示图片
7. 绘制多图
如下图所示,有时我们需要在一张画布上输出多个图形,在Matplotlib库中有当前的图形(figure)以及当前轴(axes)概念,其对应的就是当前画布以及当前子图,在一张画布(figure)上可以绘制多个子图(axes)。绘制多图通常采用subplot()函数或subplots()函数,
首先来讲解subplot()函数,如下图所示,它通常含有三个参数,子图的行数、列数以及第几个子图,例如subplot(221)表示的就是绘制2行2列的子图(共4个子图),并在第1个子图上进行绘图。
# 演示代码如下:
import matplotlib.pyplot as plt
# 绘制第一个子图:折线图
ax1 = plt.subplot(221)
plt.plot([1, 2, 3], [2, 4, 6]) # 这里plt其实也可以换成ax1
# 绘制第二个子图:柱状图
ax2 = plt.subplot(222)
plt.bar([1, 2, 3], [2, 4, 6])
# 绘制第三个子图:散点图
ax3 = plt.subplot(223)
plt.scatter([1, 3, 5], [2, 4, 6])
# 绘制第四个子图:直方图
ax4 = plt.subplot(224)
plt.hist([2, 2, 2, 3, 4])
(array([3., 0., 0., 0., 0., 1., 0., 0., 0., 1.]),
array([2. , 2.2, 2.4, 2.6, 2.8, 3. , 3.2, 3.4, 3.6, 3.8, 4. ]),
)
为了加强大家对画布(figure)和子图(axes)的理解,我们通过下面的代码来做一个简单演示:
plt.rcParams['figure.figsize'] = (8, 4) # 设置画布大小
plt.figure(1) # 第一张画布
ax1 = plt.subplot(121) # 第一张画布的第一个子图
plt.plot([1, 2, 3], [2, 4, 6]) # 这里的plt可以换成ax1
ax2 = plt.subplot(122) # 第一张画布的第二个子图
plt.plot([2, 4, 6], [4, 8, 10])
plt.figure(2) # 第二张画布
plt.plot([1, 2, 3], [4, 5, 6])
[]
在使用subplot()函数的时候,每次在新的子图上画图时,都得调用subplot()函数,例如第四个子图就得写成ax4 = plt.subplot(224),那有没有什么办法,一次性就生成多个子图呢?这时候就可以用到subplots()函数,代码如下:
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
ax1, ax2, ax3, ax4 = axes.flatten()
ax1.plot([1, 2, 3], [2, 4, 6]) # 绘制第一个子图
ax2.bar([1, 2, 3], [2, 4, 6]) # 绘制第二个子图
ax3.scatter([1, 3, 5], [2, 4, 6]) # 绘制第三个子图
ax4.hist([2, 2, 2, 3, 4]) # 绘制第四个子图
(array([3., 0., 0., 0., 0., 1., 0., 0., 0., 1.]),
array([2. , 2.2, 2.4, 2.6, 2.8, 3. , 3.2, 3.4, 3.6, 3.8, 4. ]),
)
此外,如果要在subplot()函数或者subplots()函数生成的子图中设置子图标题、X轴标签或Y轴标签,得通过set_title()函数、set_xlabel()函数、set_ylabel()函数进行设置,演示代码如下:
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
ax1, ax2, ax3, ax4 = axes.flatten()
ax1.plot([1, 2, 3], [2, 4, 6]) # 绘制第一个子图
ax1.set_title('子图1')
ax1.set_xlabel('日期')
ax1.set_ylabel('分数')
ax2.bar([1, 2, 3], [2, 4, 6]) # 绘制第二个子图
ax3.scatter([1, 3, 5], [2, 4, 6]) # 绘制第三个子图
ax4.hist([2, 2, 2, 3, 4]) # 绘制第四个子图
(array([3., 0., 0., 0., 0., 1., 0., 0., 0., 1.]),
array([2. , 2.2, 2.4, 2.6, 2.8, 3. , 3.2, 3.4, 3.6, 3.8, 4. ]),
)
%matplotlib inline
import tushare as ts
import mplfinance as mpf
from pylab import mpl
import pandas as pd
pro = ts.pro_api('9d674d000f7c730dd3108701a1a1c534bf51bfb03a0ff169a9d11848') #
#https://tushare.pro/user/token
df = pro.daily(ts_code='000001.SZ', start_date='20200101', end_date='20201103')
#df.sort_values(by='trade_date',ascending=False)
#取所有行数据,后面取date列,open列等数据
data = df.loc[:, ['trade_date', 'open', 'close', 'high', 'low', 'vol']]
data = data.rename(columns={'trade_date': 'Date', 'open': 'Open', 'close': 'Close', 'high': 'High', 'low': 'Low', 'vol': 'Volume'}) #更换列名,为后面函数变量做准备
#设置date列为索引,覆盖原来索引,这个时候索引还是 object 类型,就是字符串类型。
data.set_index('Date', inplace=True)
#将object类型转化成 DateIndex 类型,pd.DatetimeIndex 是把某一列进行转换,同时把该列的数据设置为索引 index。
data.index = pd.DatetimeIndex(data.index)
#将时间顺序升序,符合时间序列
data = data.sort_index(ascending=True)
# pd.set_option()就是pycharm输出控制显示的设置,下面这几行代码其实没用上,暂时也留在这儿吧
pd.set_option('expand_frame_repr', False)#True就是可以换行显示。设置成False的时候不允许换行
pd.set_option('display.max_columns', None)# 显示所有列
#pd.set_option('display.max_rows', None)# 显示所有行
pd.set_option('colheader_justify', 'centre')# 显示居中
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
mpl.rcParams["figure.figsize"] = [6.4, 4.8]
mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
mpf.plot(data, type='candle', mav=(5, 10, 20), volume=True, show_nontrading=False)
下面有的代码新版本废弃了,注意新版本用法
1. 股票数据读取:Tushare库的安装与使用
首先推荐通过PIP安装法来安装可以调用股价数据的Tushare库(Tushare库官方地址为:http://tushare.org/
以Windows系统为例,具体方法是:通过Win + R组合键调出运行框,输入cmd后回车,然后在弹出框中输入pip install tushare后按一下Enter回车键的方法来进行安装。如果在1.2.3节讲到的Jupyter Notebook编辑器中安装的话,只需要在代码框中输入如下代码然后运行该行代码框即可(注意是英文格式下的!):
!pip install tushare
# 我们只需要通过如下2行代码便可获取到股票基本数据:
import tushare as ts
df = ts.get_k_data('000002', start='2009-01-01', end='2019-01-01')
df.head()
本接口即将停止更新,请尽快使用Pro版接口:https://tushare.pro/document/2
date | open | close | high | low | volume | code | |
---|---|---|---|---|---|---|---|
0 | 2009-01-05 | 5.086 | 5.178 | 5.178 | 5.008 | 936048.88 | 000002 |
1 | 2009-01-06 | 5.163 | 5.333 | 5.372 | 5.109 | 1216831.18 | 000002 |
2 | 2009-01-07 | 5.356 | 5.302 | 5.457 | 5.302 | 834829.31 | 000002 |
3 | 2009-01-08 | 5.217 | 5.333 | 5.410 | 5.163 | 837661.70 | 000002 |
4 | 2009-01-09 | 5.333 | 5.325 | 5.418 | 5.263 | 626815.66 | 000002 |
# 此时如果想要将股票数据获取到Excel文件中,则可以使用2.2.2节相关知识点,代码如下:
df.to_excel('股价数据.xlsx', index=False)
2. 绘制股价走势图
已经有了股价数据后,我们可以通过可视化的方式将其展示出来,这里我们首先利用2.2.1节的补充知识点中的set_index()函数将日期设置为行索引,这样方便等会直接用pandas库进行绘图,代码如下:
df.set_index('date', inplace=True)
df.head()
open | close | high | low | volume | code | |
---|---|---|---|---|---|---|
date | ||||||
2009-01-05 | 5.086 | 5.178 | 5.178 | 5.008 | 936048.88 | 000002 |
2009-01-06 | 5.163 | 5.333 | 5.372 | 5.109 | 1216831.18 | 000002 |
2009-01-07 | 5.356 | 5.302 | 5.457 | 5.302 | 834829.31 | 000002 |
2009-01-08 | 5.217 | 5.333 | 5.410 | 5.163 | 837661.70 | 000002 |
2009-01-09 | 5.333 | 5.325 | 5.418 | 5.263 | 626815.66 | 000002 |
通过2.3.1节补充知识点中pandas绘图的相关知识点来进行图形绘制,代码如下。因为在pandas库中plot()函数默认绘制的是折线图,所以直接写plot()即可,不需要传入kind参数。此外在金融领域,通常用收盘价作为当天价格来绘制股价走势图,因此这里选择的是close这一列。
df['close'].plot()
如果想给图片加一个标题,在pandas库中使用可以在plot()可以在里面传入一个title参数,代码如下,注意因为标题是中文内容,所以要写2.3.2节最后讲到的两行代码防止中文乱码。
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
df['close'].plot(title='万科股价走势图')
补充知识点:直接使用Matplotlib库绘制图表
上面使用的是pandas库中的plot()函数,pandas库其实是集成了Matplotlib库的一些功能,如果有的读者想直接用Matplotlib库进行股价走势画图,可以采用如下代码:
# 通过Tushare库获取股价数据
import tushare as ts
df = ts.get_k_data('000002', start='2009-01-01', end='2019-01-01')
# 要注意的细节:调整日期格式使得横坐标显示清晰
from datetime import datetime
df['date'] = df['date'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d'))
# 绘制折线图
import matplotlib.pyplot as plt
plt.plot(df['date'], df['close'])
plt.show()
本接口即将停止更新,请尽快使用Pro版接口:https://tushare.pro/document/2
C:\Users\LYJZB\Anaconda3\lib\site-packages\matplotlib\cbook\__init__.py:1402: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version. Convert to a numpy array before indexing instead.
x[:, None]
C:\Users\LYJZB\Anaconda3\lib\site-packages\matplotlib\cbook\__init__.py:1402: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version. Convert to a numpy array before indexing instead.
x[:, None]
C:\Users\LYJZB\Anaconda3\lib\site-packages\matplotlib\axes\_base.py:276: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version. Convert to a numpy array before indexing instead.
x = x[:, np.newaxis]
C:\Users\LYJZB\Anaconda3\lib\site-packages\matplotlib\axes\_base.py:278: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version. Convert to a numpy array before indexing instead.
y = y[:, np.newaxis]
1. 股票K线图基础知识
一个实际中的股票K线图如下图所示(这个是“贵州茅台”股票的日线级别的K线图):
没有接触过股票的读者可能会被里面的各个柱状图和折线图搞得一头雾水,而这些图形其实都是通过一些很基础的数据绘制而成,这一节便主要来科普下股票K线图的基本知识。
这些柱状图,通常称之为“K线图”,是由股票的四个价格来绘制的:开盘价(当天上午9点半开始交易时的价格)、收盘价(当天下午3点结束交易时的价格)、最高价(当天股价波动中的最高价)、最低价(当天股价波动中的最低价),简称“高、开、低、收”四个价格。
如下图所示,根据这四个价格便可以绘制出红色和绿色的K线图,因为形似蜡烛,因此也常被称之为蜡烛图。K线图分为两种,如果当天的收盘价高于开盘价,也就是说当天的价格上涨,则称之为阳线,通常绘制成红色;反之如果当天的收盘价低于开盘价,也就是说当天的价格下跌,则称之为阴线,通常绘制成绿色。补充说一句,在美国,反而是红色代表跌,绿色代表涨。
这里再解释下均线图,也就是那些折线图的绘制原理。均线分为5日均线(通常称之为MA5)、10日均线(通常称之为MA10)、20日均线(通常称之为MA20)等,其原理就是将股价的收盘价求均值,例如5日均线就是最近连续5个交易日收盘价之和的平均值,具体的计算公式如下,其中Close1为当天的收盘价,Close2为前一天的收盘价,其余依次类推。
MA5 = (Close1 + Close2 + Close3 + Close4 + Close5)/5
把每个5日均线的值连成一条平滑的曲线就是5日均线图了,同理10日均线图和20日均线图也是类似的原理,这些均线图也就是我们在这一小节最开始看到图中的那些折线图。
了解了股票K线图的基本知识后,下面我们就来进行K线图的绘制工作。
2.绘制股票K线图
(1) 安装绘制K线图的mplfinance库
首先需要安装绘制K线图的相关库:mpl_finance库,其安装办法稍微麻烦一点,推荐通过PIP安装法安装,以Windows系统为例,具体方法是:通过Win + R组合键调出运行框,输入cmd后回车,然后在弹出框中输入如下内容,按一下Enter回车键进行安装:
pip install mplfinance
如果是在在1.2.3节讲到的Jupyter Notebook中安装,则在pip前面加一个英文的感叹号“!”然后运行该代码块即可。
# 通过如下代码,可以在Jupyter Notebook中安装。(需取消注释)
# !pip install https://github.com/matplotlib/mpl_finance/archive/master.zip
(2) 引入绘图相关库
# 首先引入一些绘图需要用到的库,代码如下:
import tushare as ts
import matplotlib.pyplot as plt
import mplfinance as mpf
import seaborn as sns
sns.set()
第一个引入2.4.1节讲到的Tushare库,第二引入2.3.1节讲到的Matplotlib库在;第三个引入刚刚安装的mpl_finance库;第四个seaborn库是一个图表美化库,通过sns.set()即可激活,如果是通过1.2.1节Anaconda安装的Python,那么就自带该库了,无需额外安装。上面的代码直接拿去运行即可。
(3) 通过Tushare库获取股票基本数据
# 通过Tushare库获取股票代码为“000002”的股票“万科A”在2019-06-01至2019-09-30的股价数据,代码如下:
df = ts.get_k_data('000002','2019-06-01', '2019-09-30')
df.head()
本接口即将停止更新,请尽快使用Pro版接口:https://tushare.pro/document/2
date | open | close | high | low | volume | code | |
---|---|---|---|---|---|---|---|
99 | 2019-06-03 | 26.81 | 26.44 | 27.02 | 26.28 | 317567.0 | 000002 |
100 | 2019-06-04 | 26.47 | 26.30 | 26.54 | 26.25 | 203260.0 | 000002 |
101 | 2019-06-05 | 26.64 | 27.03 | 27.28 | 26.63 | 576164.0 | 000002 |
102 | 2019-06-06 | 27.01 | 27.12 | 27.29 | 26.92 | 333792.0 | 000002 |
103 | 2019-06-10 | 27.29 | 27.81 | 28.05 | 27.17 | 527547.0 | 000002 |
(4) 日期格式调整及表格转换
因为绘制K线图的candlestick_ochl()函数只能接收特定格式的日期格式,以及数组格式的内容,所以我们需要将原来文本类型的日期格式调整一下,代码如下:
# 导入日期格式调整涉及的两个库
from matplotlib.pylab import date2num
import datetime
# 对tushare获取到的日期数据转换成candlestick_ohlc()函数可读取的数字格式
def date_to_num(dates):
num_time = []
for date in dates:
date_time = datetime.datetime.strptime(date,'%Y-%m-%d')
num_date = date2num(date_time)
num_time.append(num_date)
return num_time
# 将DataFrame转换为二维数组,并利用date_to_num()函数转换日期
df_arr = df.values # 将DataFrame格式的数据,转换为array二维数组
df_arr[:,0] = date_to_num(df_arr[:,0]) # 将原来日期格式的日期换成数字格式
df_arr[0:5] # 查看此时的df_arr的前5项
array([[737213.0, 26.81, 26.44, 27.02, 26.28, 317567.0, '000002'],
[737214.0, 26.47, 26.3, 26.54, 26.25, 203260.0, '000002'],
[737215.0, 26.64, 27.03, 27.28, 26.63, 576164.0, '000002'],
[737216.0, 27.01, 27.12, 27.29, 26.92, 333792.0, '000002'],
[737220.0, 27.29, 27.81, 28.05, 27.17, 527547.0, '000002']],
dtype=object)
(5) 绘制K线图
转换好数据格式后,K线图的绘制就比较简单了,通过candlestick_ochl()函数便能够轻松的绘制K线图了,代码如下:
kdata = df.rename(columns={'date':'Date','open':'Open','close':'Close','high':'High','low':'Low','volume':'Volume','code':'Code'})
kdata = kdata.set_index('Date')
kdata.index = pd.DatetimeIndex(kdata.index)
#新版本废弃
#fig, ax = plt.subplots(figsize=(15,6))
#mpf.candlestick_ochl(ax, df_arr, width=0.6, colorup='r', colordown='g', alpha=1.0)
#plt.grid(True) # 绘制网格
#ax.xaxis_date() # 设置x轴的刻度为日期
candlestick_ochl()函数的参数:
ax:绘图Axes的实例,也就是画布中的子图;
df_arr:股价历史数据;
width:图像中红绿矩形的宽度;
colorup:收盘价格大于开盘价格时矩形的颜色;
colordown:收盘价格低于开盘价格时矩形的颜色;
alpha:矩形的颜色的透明度;
mpf.plot(kdata, type='candle', show_nontrading=False)
(6) 绘制K线图及均线图
有了K线图之后,我们再来补上均线图,这里我们主要补上5日均线和10日均线图,首先我们通过如下代码构造5日均线和10日均线数据:
df['MA5'] = df['close'].rolling(5).mean()
df['MA10'] = df['close'].rolling(10).mean()
df.head(15) # 查看此时的前15行
date | open | close | high | low | volume | code | MA5 | MA10 | |
---|---|---|---|---|---|---|---|---|---|
99 | 2019-06-03 | 26.81 | 26.44 | 27.02 | 26.28 | 317567.0 | 000002 | NaN | NaN |
100 | 2019-06-04 | 26.47 | 26.30 | 26.54 | 26.25 | 203260.0 | 000002 | NaN | NaN |
101 | 2019-06-05 | 26.64 | 27.03 | 27.28 | 26.63 | 576164.0 | 000002 | NaN | NaN |
102 | 2019-06-06 | 27.01 | 27.12 | 27.29 | 26.92 | 333792.0 | 000002 | NaN | NaN |
103 | 2019-06-10 | 27.29 | 27.81 | 28.05 | 27.17 | 527547.0 | 000002 | 26.940 | NaN |
104 | 2019-06-11 | 27.87 | 28.33 | 28.45 | 27.85 | 449630.0 | 000002 | 27.318 | NaN |
105 | 2019-06-12 | 28.24 | 28.00 | 28.29 | 27.81 | 269372.0 | 000002 | 27.658 | NaN |
106 | 2019-06-13 | 28.00 | 27.83 | 28.05 | 27.58 | 250431.0 | 000002 | 27.818 | NaN |
107 | 2019-06-14 | 28.01 | 27.93 | 28.29 | 27.78 | 311417.0 | 000002 | 27.980 | NaN |
108 | 2019-06-17 | 27.80 | 27.91 | 28.20 | 27.75 | 171672.0 | 000002 | 28.000 | 27.470 |
109 | 2019-06-18 | 28.08 | 27.70 | 28.11 | 27.40 | 219162.0 | 000002 | 27.874 | 27.596 |
110 | 2019-06-19 | 28.20 | 27.73 | 28.38 | 27.59 | 390157.0 | 000002 | 27.820 | 27.739 |
111 | 2019-06-20 | 27.70 | 28.45 | 28.45 | 27.63 | 577484.0 | 000002 | 27.944 | 27.881 |
112 | 2019-06-21 | 28.40 | 28.31 | 28.52 | 28.12 | 492537.0 | 000002 | 28.020 | 28.000 |
113 | 2019-06-24 | 28.12 | 28.13 | 28.25 | 28.03 | 270128.0 | 000002 | 28.064 | 28.032 |
data = data.sort_index(ascending=True)
plt.rcParams['font.sans-serif'] = ['SimHei']
my_color = mpf.make_marketcolors(up='red', down='green', edge='i', wick='i', volume='in')
# 解决mplfinance绘制输出中文乱码
my_style = mpf.make_mpf_style(base_mpf_style='yahoo',marketcolors=my_color, gridaxis='both', gridstyle='-.', y_on_right=True,rc={'font.family': 'SimHei'})
mpf.plot(kdata, type='candle',style = my_style,title='万科A',ylabel='价格',xrotation=0,datetime_format='%Y-%m-%d',mav=(5, 10), show_nontrading=False,figratio=(15, 6),figscale=1)
# 绘制5日均线,10日均线
# 有了5日均线和10日均线数据后,就可以将其绘制在图形中了,代码如下:
#plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
#fig, ax = plt.subplots(figsize=(15,6))
#mpf.candlestick_ochl(ax, df_arr, width=0.6, colorup='r', colordown='g', alpha=1.0)
#plt.plot(df_arr[:,0], df['MA5']) # 绘制5日均线
#plt.plot(df_arr[:,0], df['MA10']) # 绘制10日均线
#plt.grid(True) # 绘制网格
#plt.title('万科A') # 设置标题
#plt.xlabel('日期') # 设置X轴图例
#plt.ylabel('价格') # 设置Y轴图例
#ax.xaxis_date () # 设置x轴的刻度为日期
(7) 绘制股票K线图、均线图、成交量柱状图
在现实中,和股票K线图、均线图一同出现的还有每日成交量的的柱状图,我们利用2.3.2节绘制多图的知识点,即可通过如下代码在一张画布中绘制两个子图,包含K线图、均线图、成交量柱状图:
#fig, axes = plt.subplots(2, 1, sharex=True, figsize=(15,8))
#ax1, ax2 = axes.flatten()
# 绘制第一张子图:K线图和均线图
#mpf.candlestick_ochl(ax1, df_arr, width=0.6, colorup = 'r', colordown = 'g', alpha=1.0)
#ax1.plot(df_arr[:,0], df['MA5']) # 绘制5日均线
#ax1.plot(df_arr[:,0], df['MA10']) # 绘制10日均线
#ax1.set_title('万科A') # 设置子图标题
#ax1.set_ylabel('价格') # 设置子图Y轴标签
#ax1.grid(True)
#ax1.xaxis_date()
# 绘制第二张子图:成交量图
#ax2.bar(df_arr[:,0], df_arr[:,5]) # 绘制成交量柱状图
#ax2.set_xlabel('日期') # 设置子图X轴标签
#ax2.set_ylabel('成交量') # 设置子图Y轴标签
#ax2.grid(True)
#ax2.xaxis_date()
plt.rcParams['font.sans-serif'] = ['SimHei']
my_color = mpf.make_marketcolors(up='red', down='green', edge='i', wick='i', volume='in')
# 解决mplfinance绘制输出中文乱码
my_style = mpf.make_mpf_style(base_mpf_style='yahoo',marketcolors=my_color, gridaxis='both', gridstyle='-.', y_on_right=True,rc={'font.family': 'SimHei'})
mpf.plot(kdata, type='candle',style = my_style,title='万科A',ylabel='价格',xrotation=0,datetime_format='%Y-%m-%d',mav=(5, 10), volume=True,show_nontrading=False,figratio=(15, 6),figscale=1)
# 绘制5日均线,10日均线
其中第1-2行代码利用2.3.2节绘制多图相关知识点先构造一个画布和两个子图,这里同时设置sharex参数为True,这样两张子图就可以共用一个坐标轴了;第4-13行绘制第一张子图,其中在子图中设置标题或者坐标轴标题得使用set_title()、set_ylabel()、set_xlabel()这样的函数;第15-20行绘制第二张子图:成交量图,其中df_arr[:,0]表示二维数组的第1列,也即日期那列,df_arr[:,5]表示二维数组的第6列,也即成交量那列数据,然后通过2.3.1节讲过的bar()函数绘制成柱状图。
我们可以和新浪财经网上的实际图像对比一下,如下图所示,发现通过Python绘制的K线图相关图片和网上的图片基本一致。
至此,数据分析的相关3大武器库已经给大家讲解完毕了,其实关于这三个库还有很多可以挖掘的知识点,由于篇幅有限,这里就不再赘述。这一章内容相对较多,读者朋友可以将这一章当作一个工具章,有需要的时候再返回看看需要用到的知识点。
代码来源官网:https://matplotlib.org/stable/gallery/index.html
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
# Load a numpy record array from yahoo csv data with fields date, open, high,
# low, close, volume, adj_close from the mpl-data/sample_data directory. The
# record array stores the date as an np.datetime64 with a day unit ('D') in
# the date column.
price_data = (cbook.get_sample_data('goog.npz', np_load=True)['price_data']
.view(np.recarray))
price_data = price_data[-250:] # get the most recent 250 trading days
delta1 = np.diff(price_data.adj_close) / price_data.adj_close[:-1]
# Marker size in units of points^2
volume = (15 * price_data.volume[:-2] / price_data.volume[0])**2
close = 0.003 * price_data.close[:-2] / 0.003 * price_data.open[:-2]
fig, ax = plt.subplots()
ax.scatter(delta1[:-1], delta1[1:], c=close, s=volume, alpha=0.5)
ax.set_xlabel(r'$\Delta_i$', fontsize=15)
ax.set_ylabel(r'$\Delta_{i+1}$', fontsize=15)
ax.set_title('Volume and percent change')
ax.grid(True)
fig.tight_layout()
plt.show()
import matplotlib.pyplot as plt
import numpy as np
# Fixing random state for reproducibility
np.random.seed(19680801)
N = 100
r0 = 0.6
x = 0.9 * np.random.rand(N)
y = 0.9 * np.random.rand(N)
area = (20 * np.random.rand(N))**2 # 0 to 10 point radii
c = np.sqrt(area)
r = np.sqrt(x ** 2 + y ** 2)
area1 = np.ma.masked_where(r < r0, area)
area2 = np.ma.masked_where(r >= r0, area)
plt.scatter(x, y, s=area1, marker='^', c=c)
plt.scatter(x, y, s=area2, marker='o', c=c)
# Show the boundary between the regions:
theta = np.arange(0, np.pi / 2, 0.01)
plt.plot(r0 * np.cos(theta), r0 * np.sin(theta))
plt.show()
import numpy as np
import matplotlib
import matplotlib as mpl
import matplotlib.pyplot as plt
vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
"potato", "wheat", "barley"]
farmers = ["Farmer Joe", "Upland Bros.", "Smith Gardening",
"Agrifun", "Organiculture", "BioGoods Ltd.", "Cornylee Corp."]
harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
[2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
[1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
[0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
[0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
[1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
[0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
fig, ax = plt.subplots()
im = ax.imshow(harvest)
# Show all ticks and label them with the respective list entries
ax.set_xticks(np.arange(len(farmers)), labels=farmers)
ax.set_yticks(np.arange(len(vegetables)), labels=vegetables)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=45, ha="right",
rotation_mode="anchor")
# Loop over data dimensions and create text annotations.
for i in range(len(vegetables)):
for j in range(len(farmers)):
text = ax.text(j, i, harvest[i, j],
ha="center", va="center", color="w")
ax.set_title("Harvest of local farmers (in tons/year)")
fig.tight_layout()
plt.show()
import numpy as np
import matplotlib
import matplotlib as mpl
import matplotlib.pyplot as plt
vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
"potato", "wheat", "barley"]
farmers = ["Farmer Joe", "Upland Bros.", "Smith Gardening",
"Agrifun", "Organiculture", "BioGoods Ltd.", "Cornylee Corp."]
harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
[2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
[1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
[0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
[0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
[1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
[0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
def heatmap(data, row_labels, col_labels, ax=None,
cbar_kw=None, cbarlabel="", **kwargs):
"""
Create a heatmap from a numpy array and two lists of labels.
Parameters
----------
data
A 2D numpy array of shape (M, N).
row_labels
A list or array of length M with the labels for the rows.
col_labels
A list or array of length N with the labels for the columns.
ax
A `matplotlib.axes.Axes` instance to which the heatmap is plotted. If
not provided, use current axes or create a new one. Optional.
cbar_kw
A dictionary with arguments to `matplotlib.Figure.colorbar`. Optional.
cbarlabel
The label for the colorbar. Optional.
**kwargs
All other arguments are forwarded to `imshow`.
"""
if ax is None:
ax = plt.gca()
if cbar_kw is None:
cbar_kw = {}
# Plot the heatmap
im = ax.imshow(data, **kwargs)
# Create colorbar
cbar = ax.figure.colorbar(im, ax=ax, **cbar_kw)
cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom")
# Show all ticks and label them with the respective list entries.
ax.set_xticks(np.arange(data.shape[1]), labels=col_labels)
ax.set_yticks(np.arange(data.shape[0]), labels=row_labels)
# Let the horizontal axes labeling appear on top.
ax.tick_params(top=True, bottom=False,
labeltop=True, labelbottom=False)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=-30, ha="right",
rotation_mode="anchor")
# Turn spines off and create white grid.
ax.spines[:].set_visible(False)
ax.set_xticks(np.arange(data.shape[1]+1)-.5, minor=True)
ax.set_yticks(np.arange(data.shape[0]+1)-.5, minor=True)
ax.grid(which="minor", color="w", linestyle='-', linewidth=3)
ax.tick_params(which="minor", bottom=False, left=False)
return im, cbar
def annotate_heatmap(im, data=None, valfmt="{x:.2f}",
textcolors=("black", "white"),
threshold=None, **textkw):
"""
A function to annotate a heatmap.
Parameters
----------
im
The AxesImage to be labeled.
data
Data used to annotate. If None, the image's data is used. Optional.
valfmt
The format of the annotations inside the heatmap. This should either
use the string format method, e.g. "$ {x:.2f}", or be a
`matplotlib.ticker.Formatter`. Optional.
textcolors
A pair of colors. The first is used for values below a threshold,
the second for those above. Optional.
threshold
Value in data units according to which the colors from textcolors are
applied. If None (the default) uses the middle of the colormap as
separation. Optional.
**kwargs
All other arguments are forwarded to each call to `text` used to create
the text labels.
"""
if not isinstance(data, (list, np.ndarray)):
data = im.get_array()
# Normalize the threshold to the images color range.
if threshold is not None:
threshold = im.norm(threshold)
else:
threshold = im.norm(data.max())/2.
# Set default alignment to center, but allow it to be
# overwritten by textkw.
kw = dict(horizontalalignment="center",
verticalalignment="center")
kw.update(textkw)
# Get the formatter in case a string is supplied
if isinstance(valfmt, str):
valfmt = matplotlib.ticker.StrMethodFormatter(valfmt)
# Loop over the data and create a `Text` for each "pixel".
# Change the text's color depending on the data.
texts = []
for i in range(data.shape[0]):
for j in range(data.shape[1]):
kw.update(color=textcolors[int(im.norm(data[i, j]) > threshold)])
text = im.axes.text(j, i, valfmt(data[i, j], None), **kw)
texts.append(text)
return texts
fig, ax = plt.subplots()
im, cbar = heatmap(harvest, vegetables, farmers, ax=ax,
cmap="YlGn", cbarlabel="harvest [t/year]")
texts = annotate_heatmap(im, valfmt="{x:.1f} t")
fig.tight_layout()
plt.show()
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
# Compute pie slices
N = 20
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = 10 * np.random.rand(N)
width = np.pi / 4 * np.random.rand(N)
colors = plt.cm.viridis(radii / 10.)
ax = plt.subplot(projection='polar')
ax.bar(theta, radii, width=width, bottom=0.0, color=colors, alpha=0.5)
plt.show()
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
ax = plt.figure().add_subplot(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
# Plot the 3D surface
ax.plot_surface(X, Y, Z, edgecolor='royalblue', lw=0.5, rstride=8, cstride=8,
alpha=0.3)
# Plot projections of the contours for each dimension. By choosing offsets
# that match the appropriate axes limits, the projected contours will sit on
# the 'walls' of the graph.
ax.contour(X, Y, Z, zdir='z', offset=-100, cmap='coolwarm')
ax.contour(X, Y, Z, zdir='x', offset=-40, cmap='coolwarm')
ax.contour(X, Y, Z, zdir='y', offset=40, cmap='coolwarm')
ax.set(xlim=(-40, 40), ylim=(-40, 40), zlim=(-100, 100),
xlabel='X', ylabel='Y', zlabel='Z')
plt.show()