1.python基础
1.1 数据类型
1.1.1 number(数字)
(1) 整型(int):整型就是整数,包括正整数、负整数、零,不带小数点。
int(x) :将x转换为一个整数
(2) 浮点型(float)):浮点型由整数部分与小数点组成,必须包含小数点。
float(x):将x转换为一个浮点数
(3) 布尔型(bool) :布尔型是一种表示逻辑值的简单类型,只有俩个值,一个为 True(真,1),另一个为False(假,0)。
1.1.2 string(字符串)
str(x/s):将x/s转换为字符串
eval(str):用于字符串,去除字符串的引号,获取引号里面的内容
1.1.3 list(列表)
list(s):将序列s转换为一个列表
1.1.4 tuple(元组)
tuple(s):将序列s转换为一个元组
1.1.5 dictionary(字典)
dict(d):创建一个字典,d必须是一个(key,value)元组序列
1.1.6 set(集合)
set(s): 将序列s转换为一个集合
1.1.7
type()函数:查询某一变量的数据类型。
例:
>>>a = 123
>>>print(type(a))
<class 'int'>
1.2 运算符
(1)**(幂运算,返回乘方结果 )
(2)算术运算符: * /(两个数相除,结果为浮点数(小数) ) %(取模,返回两个数相除的余数) //(两个数相除,结果为向下取整数 ) + -
(3)比较运算符:<= < > >= ==(比较两个对象是否相等 ) !=(比较两个对象是否不相等 )
(4)赋值运算符:= %= /= //= -= += *= **=
(5)逻辑运算符:and(“与”运算符,一假为假,全真为真 )
or(“或”运算符,一真为真,全假为假 )
not(“非”运算符,为真则假,为假则真 )
算术运算符 > 比较运算符 > 赋值运算符
1.3 python语句
1.3.1 判断语句
1.if 判断条件 :
语句
2.if 判断条件:
语句
else:
语句
3.if 判断条件:
语句
elif 判断条件:
语句
……
else:
语句
例:
print("请输入学生的成绩:")
a=int(input())
if a>100:
print("error!")
elif a>=90:
print("A")
elif a>=80:
print("B")
elif a>=70:
print("C")
elif a>=60:
print("D")
elif a>=0:
print("E")
else:
print("error!")
1.3.2 循环语句
(1) while语句:
while 表达式:
循环体
(2) for循环:
for 循环变量 in 遍历对象(序列):
循环体
for 循环,从形式上可以分为俩种: 1.遍历对象。
2.使用range函数控制循环次数 range函数: range(起始值,终 止值,步长) 步长 :每次循环序列增长值,默认值为1,为1 时可以省略。
例:求出 1∼100中的质数
#用while语句
a=2
b=2
while a<=100:
c=b
while c<a:
if (a%c)==0:
break
c=c+1
else:
print(a)
a=a+1
#用for循环
for i in range(1, 101):
if i>1:
for j in range(2,i):
if (i%j)==0:
break
else:
print(i)
例:用循环语句画图
a,b,c,d,e = " ","/","\\","*","&"
for i in range(1,4):
print(a*(10-i),end="")
print(f"{d} "*i)
print(f"{d} "*10)
for i in range(1,3):
print(a*i,end="")
print(f"{d} "*(4-i),end="")
print(f"{b} ",end="")
print(f"{d} " * i, end="")
print(f"{c} ",end="")
print(f"{d} "*(4-i))
print(a*3,end="")
print(f"{d} "*3,end="")
print(f"{e} ",end="")
print(f"{d} "*3)
for i in range(3,1,-1):
print(a*i,end="")
print(f"{d} "*(4-i),end="")
print(f"{c} ",end="")
print(f"{d} " * i, end="")
print(f"{b} ",end="")
print(f"{d} "*(4-i))
print(f"{d} "*10)
for i in range(3,0,-1):
print(a*(10-i),end="")
print(f"{d} "*i)
运行结果
1.4 各数据类型的简单功能介绍
1.4.1 字符串
(1)转义字符:\' 输出为:'
\"
输出为:"
\' 输出为:制表符
\' 输出为:换行符
\' 输出为:倒斜杠
(2)
字符串的索引、切片与逆序
注意:索引从0开始
1.获取单个字符时可以使用变量名[索引]
例:
>>>a = "Hello , world!"
>>>print(a[9])
r
2.获取字符串时可以使用变量名变量名[起始索引:结束索引+1:间隔个数]
起始索引默认为0,结束索引默认为最后,间隔个数默认为1,若为默认时可以省略。
例:
>>>a = "Hello , world!"
>>>print(a[0:5])
Hello
>>>print(a[0:5:2])
Hlo
3.逆序 只需要使用变量名[结束索引:开始索引-1:-1]
例:
>>>a = "Hello , world!"
>>>print(a[4:-1:-1])
olleH
>>>print(a[4:-1:-2])
olH
(3) 使用ord()和chr()函数的字符的数值
1.ord(字符)获取一个单字符字符串的代码点。
2.chr(代码点)获取一个整数代码点的单字符字符串。
(4)字符串相关函数的用法
upper() | 字符串.upper() | 将字符串里的字符全部大写 |
lower() | 字符串.lower() | 将字符串里的字符全部小写 |
isupper() | 字符串.isupper() | 字符串中字符是否都是由大写字母组成 |
islower() | 字符串.islower() | 字符串中字符是否都是由小写字母组成 |
isdigit() | 字符串.isdigit() | 字符串中是否都是由数字组成 |
isnumeric() | 字符串.isnumeric() | 字符串中字符是否都是由数字字符组成 |
isalpha() | 字符串.isalpha() | 字符串中是否都是由字母组成,并且非空 |
isalnum() | 字符串.isalnum() | 字符串中是否都是由字母和数字组成,并且非空 |
isdecimal() | 字符串.isdecimal() | 字符串中是否都是由数字字符组成,并且非空 |
isspace() | 字符串.isspace() | 字符串中是否都是由空格、制表符和换行符组成,并且非空 |
istitle() | 字符串.istitle() | 字符串中是否以大写字母开头、后面都是小写字母的单词、数字或空格 |
find() | 字符串.find(字符,起始位置,结束位置) | 查找指定字符在字符串中的位置(索引)(找不到则返回-1) |
index() | 字符串.index(字符,起始位置,结束位置) | 查找指定字符在字符串中的位置(索引)(找不到会报错) |
count() | 字符串.count(字符) | 统计指定字符出现的次数 |
replace() | 字符串.replace(字符,新字符,替换次数) | 用于字符串的修改 |
split() | 字符串.split(分隔符,分割次数) | 拆分字符串。通过指定分隔符对字符串进行切片,并返回分割后的字符串列表。 |
join() | 分隔符.join(序列) | 返回一个用分隔符连接序列各个元素后的字符串(注:分隔符可以为空) |
title() | 字符串.title() | 字符串内的所有单词的首字母大写 |
capittalize() | 字符串.capittalize() | 字符串第一个字母大写 |
startswith() | 字符串1.startswith(字符串2) | 字符串2是否以字符串开始 |
endswith() | 字符串1.endswith(字符串2) | 字符串2是否以字符串结束 |
partition() | 字符串.partition(字符串中片段) | 以字符串中片段为分隔符分隔字符串 |
rjust() | 字符串.rjust(数字,'填充字符') | 右对齐字符串,左边填充上‘填充字符’,总字符长度为‘数字’ |
ljust() | 字符串.ljust(数字,'填充字符') | 左对齐字符串,右边填充上‘填充字符’,总字符长度为‘数字’ |
center() | 字符串.center(数字,'填充字符') | 字符串居中,两边填充上‘填充字符’,总字符长度为‘数字’ |
strip() | 字符串.strip(空格、制表符、换行符和字符串参数) | 删除字符串两边所选字符 |
rstrip() | 字符串.rstrip(空格、制表符、换行符和字符串参数) | 删除字符串右边所选字符 |
lstrip() | 字符串.lstrip(空格、制表符、换行符和字符串参数) | 删除字符串左边所选字符 |
注意:作用中有关是否的方法中,若符合则返回True,不符合则返回False。
例1:
#upper()函数
a="aBcD"
print(a.upper())
运行结果
例2:
#center()函数
a="aBcD"
print(a.center(8,'!'))
运行结果
1.4.2 列表
(1)列表相关函数的用法
列表操作 | 示范 |
创建一个空列表 | a = [ ] |
访问列表中的某个元素 | a[索引] |
访问列表的最后一个元素 | a[-1] |
列表的切片和逆序与字符串格式一致,也是a[ : : ]和a[ : :-1],同时别忘记逆序时开始索引与结束索引的位置。
(2)列表推导式
1.[表达式 for 变量 in 列表]
2.[表达式 for 变量 in 列表 if 条件]
例:
a= [x for x in range(1,21)]
print(a)
b = [x for x in range(1,21) if x%2 == 0]
print(b)
运行结果
(3)len()函数
len()函数可以取得列表的长度。
(4) sum()函数
sum()函数可以将列表中所有数相加。
(5)列表相关函数的用法
操作 | 方法 | 描述 |
增 | 列表.append(元素) | 在列表末尾添加一个元素 |
增 | 列表.extend(序列) | 在列表末尾追加一个序列 |
增 | 列表.insert(索引,元素) | 在列表对应的索引插入元素 |
删 | del 列表[索引] | 删除列表中对应索引的元素 |
删 | 列表.pop(索引) | 删除列表中对应索引的元素,并返回删除的元素,默认最后一个 |
删 | 列表.remove(元素) | 删除列表中对应元素的第一个匹配项 |
删 | 列表.clear() | 清空列表 |
改 | 列表[索引]=新元素 | 将列表对应索引的元素,修改为新元素 |
查 | 列表.index(元素) | 返回元素对应的索引 |
例1:
#append()函数
a=[1,2]
print(a)
a.append(3)
print(a)
运行结果
例2:
#remove()函数
a=[1,1,1,2,2,2]
print(a)
a.remove(1)
print(a)
运行结果
1.4.3 元组
(1)
列表操作 | 示范 |
创建一个空元组 | a =() |
创建包含一个元素的元组 | b =(元素,) |
创建元组 | c =(元素1,元素2,……) |
注意:包含一个元素的元组必须有逗号 。
(2)元组索引
元组序列[开始索引,结束索引]
注意:元组是不可以修改的,包括不能删除其中的元素。元组内的直接数据如果修改则立即报错。但是,如果元组里面有列表,修改列表里面的数据不会报错。
(3)元组相关函数的用法
操作 | 作用 |
index() | 从元组中找出某个对象第一个匹配项的下标位置。 |
count() | 统计某个元素在元组中出现的次数 |
len() | 计数元组元素的个数,并返回值 |
max() | 返回元组中元素的最大值 |
min() | 返回元组中元素的最小值 |
(4)
注意:元组除了两个方面,元组的数据类型几乎和列表数据类型一样: 1.元组输入时用圆括号(),不是方括号[ ] 2.元组的值无法修改、添加或删除。
1.4.4 字典
(1)
字典操作 | 代码示范 |
创建空字典 | dict1 = {}/dict2 = dict() |
创建字典 | dict3 = { 键1 :值1 ,键2: 值2,……} |
(2)字典相关函数的用法
操作 | 代码示范 | 描述 |
访问值 | 字典序列[键] | 访问已知键所对应的值 |
修改值 | 字典序列[键]=新值 | 修改已知键的值 |
添加键值对 | 字典序列[新键]=新值 | 添加新的键值对 |
删 | del 字典序列[键] | 删除字典中对应的键的键值对 |
删 | del 字典序列 | 删除字典 |
删 | 字典序列.clear() | 清空字典 |
查 | 字典序列.items() | 查找字典的所有的键值对 |
查 | 字典序列.keys() | 查找字典的所有的key(键) |
查 | 字典序列.values() | 查找字典的所有的value(值) |
例1:
a={'a':1,'b':2,'c':3}
a['b']=4
print(a)
运行结果
例2:
#keys()
a={'a':1,'b':2,'c':3}
print(a.keys())
运行结果
(3)使用enumerate()函数枚举键值对
enumerate(序列, start=0)
其中start = 0为默认开始值为0
1.4.5 集合
(1)
可以使用大括号 { } 或者 set() 函数创建集合,但创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
集合特点: 1.集合是一个无序的不重复元素序列
2.集合中的元素是唯一的
3.集合可以用来去重
(2)集合相关函数的用法
操作 | 代码示范 | 描述 |
增 | 集合序列.add(数据) | 添加一个数据 |
增 | 集合序列.update(序列数据) | 添加序列 |
删 | 集合序列.remove(数据) | 删除集合中指定的数据,如果数据不存在就报错 |
删 | 集合序列.pop() | 随机删除集合中的某个数据 |
删 | 集合序列.discard(数据) | 删除集合中指定的数据,如果数据不存在不做任何操作 |
例:
#add()函数
a={1,2,3,5}
a.add(6)
print(a)
运行结果
1.5 函数
1.5.1 定义与调用
(1)定义
def 函数名 ( ):
函数语句
(2)调用
函数名( )
注意:1.函数先定义后调用,如果先调用会报错。
2.函数没有被调用时,函数里面的执行语句不会被执行。
1.5.2 返回值
语法: def 函数名 ( ):
函数语句
return 返回值
注意:函数中可以同时包含多个 return 语句,但只会执行了一个return,return的功能是返回值并退出当前函数,return语句后面的语句都不执行。
1.5.3 形参和实参
(1)定义
def 函数名(形参1,形参2):
函数体
(2)调用
函数名(实参1,实参2)
注意:
1.传递的实参与形参个数必须保持一致,否则将发生参数不匹配错误。
2.编写函数时可给每个形参指定默认值,调用函数时给形参提供实参的,将使用指定的实参,若没有则使用默认值。
3.调用函数时,会将实参1传递给形参1,实参2传递给形参2,……如果参数位置不对,可能达不到预期效果,如果参数过多时可以在调用函数时直接指明形参1=实参1。
1.5.4 参数
参数分为三种:
1.位置参数(必备参数)。
2.默认参数(缺省参数)。
3.不定长参数(*args,kwargs) *args常以元组的形式输出,收集任意数量的位置实参 kwargs常以字典的形式输出,收集任意数量的关键字实参。
(1)位置参数
def 函数名(形参1,形参2):
函数体
函数名(实参1,实参2)
(2)默认参数
def 函数名(形参1=数据,…):
函数体
(3)不定长参数
1.不定长参数(*args)
def 函数名(*args):
函数体
函数名(实参1,实参2,实参3,……)
2.不定长参数(kwargs)
def 函数名(kwargs):
函数体
(4)参数定义顺序
位置参数、默认参数、不定长参数(*args,kwargs)
1.5.5 作用域
(1)局部作用域
定义在函数内部的变量拥有一个局部作用域,表示只能在声明它的函数内部访问。在函数体内部,临时保存数据,即当函数调用完成后,则销毁局部变量。
(2)全局作用域
定义在函数外部的变量拥有全局作用域,表示可以在整个程序范围内访问(函数内、外部都能生效)。
(3)修改作用域
global | nonlocal |
声明变量的作用域为全局作用域 | 数中的下划线关键字用来在函数中使用外层(非全局)变量 |
例1:
>>>def great():
>>> global a
>>> a = 40
>>> print(f'函数内:{a}')
>>>a = 20
>>>great()
>>>print(f'函数外:{a}')
函数内:40
函数外:40
例2:
>>>def outer():
>>> num = 10
>>> print(f'outer1:{num}')
>>> def inner():
>>> nonlocal num
>>> num = 20
>>> print(f'inner: {num}')
>>> inner()
>>> print(f'outer2:{num}')
>>>outer()
outer1:10
inner: 20
outer2:20
2.机器学习
常用的工具有如下几
工具 | 作用 |
matplotlib | 画图工具 |
numpy | 表格处理工具 |
padas | 信息处理工具 |
2.1 Matplotlib工具
2.1.1 创建画布
注意:后面的matplotlib.pyplot均代指画布名称。
plt.figure(figsize = (a,b),dpi = )
figsize:指图像的长(a)宽(b) dpi:图像清晰度(越大越清晰)
2.1.2 绘制图像
plt.plot(x,y.color = '',linestyle = '',marker = '',linestyle='',label='')
x:横轴数据
y:纵轴数据
color:图像颜色,可以直接输入英文名称,或者是RGB颜色值(下面附有图)
linewidth:线条宽度
marker: 标记风格
linestyle: 线条样式
label:图例
2.1.3 常用颜色
颜色 | 说明 | 颜色 | 说明 |
r | 红色 | g | 绿色 |
b | 蓝色 | w | 白色 |
c | 青色 | m | 洋红 |
y | 黄色 | k | 黑色 |
2.1.4 标记风格
标记字符 | 说明 | 标记字符 | 说明 |
'.' | 点标记 | ',' | 像素标记(极小点) |
'v' | 倒三角标记 | ’^‘ | 上三角标记 |
'>' | 右三角标记 | '<' | 左三角标记 |
'1' | 下花三角标记 | '2' | 上花三角标记 |
'3' | 左花三角标记 | '4' | 右花三角标记 |
'o' | 实心圈标记 | 's' | 实心方形标记 |
'p' | 实心五角标记 | '*' | 星形标记 |
'h' | 竖六边形标记 | 'H' | 横六边形标记 |
'+' | 十字标记 | 'x' | x标记 |
'D' | 菱形标记 | 'd' | 瘦菱形标记 |
2.1.5 线条样式
样式 | 说明 |
’-‘ | 实线 |
’--‘ | 虚线 |
’-.‘ | 点划线 |
':' | 点虚线 |
2.1.6 设置图例位置
plt.legend(loc=)
loc:图例所在位置
loc位置表
位置 | 描述 | 对应数字 |
best | 最佳位置 | 0 |
upper right | 右上方 | 1 |
upper left | 左上方 | 2 |
lower left | 左下方 | 3 |
lower right | 右下方 | 4 |
right | 右边 | 5 |
center left | 中间左边 | 6 |
center right | 中间右边 | 7 |
lower center | 下方中间 | 8 |
upper center | 上方中间 | 9 |
center | 正中心 | 10 |
2.1.7 图像保存
plt.savefig(path_or_buffer)
path_or_buffer:文件地址
注意:图像保存需要放在显示图像的前面,因为plt.show()会释放figure资源,如果在显示图像之后保存图片将只能保存空图片。
2.1.8 显示图像
plt.show()
2.1.9 基本统计图
(1) 折线图
例:
import matplotlib.pyplot as plt
x = [4, 8, 12, 16, 20]
y = [1, 5, 2, 3, 1]
plt.plot(x, y)
plt.show()
(2) 散点图
plt.scatter(x,y)
例:
import matplotlib.pyplot as plt
a = [1, 2, 3, 4, 5]
b = [6, 7, 8, 9, 10]
plt.scatter(a, b, linewidths=15,c='red',edgecolors=['black', 'green','cyan','lightgreen'])
plt.show()
(3 )柱状图
plt.bar(x,width=,height=,label=,alpha=,align=,color=)
x: 柱状图中的横坐标点list height: 柱状图对应每个横坐标的高度值
width: 柱状图的宽度,默认值为0.8
label: 每个数据样本对应的label,后面调用legend()函数可以显示图例
alpha: 透明度
align:每个柱状图的对齐方法
color:选择柱状图的颜色
例:
import matplotlib.pyplot as plt
import random
x_data = [f"20{i}年" for i in range(16, 21)]
y_data = [random.randint(100, 300) for i in range(6)]
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
for i in range(len(x_data)):
plt.bar(x_data[i], y_data[i])
plt.title("销量分析")
plt.xlabel("年份")
plt.ylabel("销量")
plt.show()
(4)
(4) 直方图
plt.hist(x,bins=None)
x:需要传递的数据 bins:组距
例:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
random_data = np.random.normal(170, 10, 250)
dataframe = pd.DataFrame(random_data)
dataframe.hist()
plt.show()
(5) 饼图
plt.pie(x,explode=None,labels=None,autopct=None,pctdistance=0.6,shadow=False,
labeldistance=1.1,startangle=None,radius=None,counterclock=True,wedgeprops=None,
textprops=None,center=(0,0),frame=False,rotatelabels=False,*,data=None)
x:表示扇形或锲形的数据
explode:表示扇形或锲形离开圆心的距离
labels:表示扇形或锲形对应的标签文本
autopct:表示控制扇形或锲形的数值显示的字符串,可通过格式字符串指定小数点后的位数. pctdistance:表示扇形或锲形对应的数值标签距离圆心的比例,默认为0.6
shadow:表示是否显示阴影
labeldistance:表示标签文本的绘制位置(相对于半径的比例),默认为1.1.
startangle:表示起始绘制角度,默认从x轴的正方向逆时针绘制 radius:表示扇形或锲形的半径. wedgeprops:表示控制扇形或锲形属性的字典.例如:通过wedgeprops={’‘width’:0.7}将锲形的宽度设为0.7.
textprops:表示控制图表中文本属性的字典
center:表示图表中心点位置,默认为(0,0)
frame:表示是否显示图框
例:
import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
plt.pie(x)
plt.show()
2.2 Numpy工具
2.2.1 导入库
import numpy as np
2.2.2 创建ndarray
注意:后面的ndarray均代指对象名称
data = np.array()
2.2.3 基本方法
名称 | 作用 |
data.shape | 查看数组的维度(以元组的形式输出) |
data.ndim | 查看数组维数 |
data.size | 查看数组中的元素数量 |
data.itemsize | 查看一个数组元素的长度 |
data.dtype | 查看数组元素的类型 |
2.2.4 创建数组时指定类型
>>>data = np.array([[1,2,3],[4,5,6]],dtype=np.float32)
>array([[1., 2., 3.],
[4., 5., 6.]], dtype=float32)
名称 | 描述 | 简写 |
np.bool | 用一个字节存储的布尔类型(Ture或False) | 'b' |
np.int8 | 一个字节大小,-128至127 | 'i' |
np.int16 | 整数,-32768至32767 | 'i2' |
np.int32 | 整数,-2^31^至2^31^-1 | 'i4' |
np.int64 | 整数,-2^63^至2^63^-1 | 'i8' |
np.uint8 | 无符号整数,0至255 | 'u' |
np.uint16 | 无符号整数,0至65535 | 'u2' |
np.uint32 | 无符号整数,0至2^32^-1 | 'u4' |
np.uint64 | 无符号整数,0至2^64^-1 | 'u8' |
np.float16 | 半精度浮点数,16位,正负号1位,指数5倍,精度10位 | 'f2' |
np.float32 | 半精度浮点数,32位,正负号1位,指数8倍,精度23位 | 'f4' |
np.float64 | 半精度浮点数,64位,正负号1位,指数11倍,精度52位 | 'f8' |
np.complex64 | 复数,分别用两个32位浮点数表示实部和虚部 | 'c8' |
np.complex128 | 复数,分别用两个64位浮点数表示实部和虚部 | 'c16' |
np.object_ | python对象 | 'o' |
np.string_ | 字符串 | 's' |
np.unicode_ | unicode类型 | 'U' |
注意: 1.np.object,np.string,np.unicode这三种类型后面加上""
2.np.unicode_简写类型为大写的'U',np.uint8简写类型位小写的'u'
2.2.5 生成只含0和1的数组
one = np.ones([index, columns]) #生成index行,columns列且所有元素都为1的数组
zero = np.zeros([index, columns]) #生成index行,columns列且所有元素都为0的数组
ones = np.ones_like(ndarray) #生成一个行数和列数都与ndarry相同的且所有元素均为1的数组
zeros = np.zeros_like(ndarray) #生成一个行数和列数都与ndarry相同的且所有元素均为0的数组
2.2.6 从现有数组中生成
data1 = np.array(data)
data2 = np.asarray(data)
2.2.7 np.array和np.asarray的区别
例:
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
print(a)
a1 = np.array(a)
a2 = np.asarray(a)
a[0][0] = 100
print(a1)
print(a2)
运行结果
类似于深拷贝与浅拷贝,可以看到数组a1使用array进行复制,将a的原始数据拷贝过来,对于数组a中的元素改变,对数组a1的元素没有影响,数组a2使用asarray进行复制,对于数组a中的元素改变,数组a2的元素也跟着数组a中的元素改变而改变。
2.2.8 生成固定范围内的数组
(1) np.linspace
np.linspace(start, stop, num, endpoint)
创建等差数组,指定数量(步长自动计算)
start:序列的起始值
stop:序列的结束值
num:要生成的等间隔样例数量,默认为50
endpoint:序列中是否包含stop值,默认为true
(2) np.arange
np.arange(start,stop,step,dtype)
创建等差数组,指定步长(数量自动计算)
step:步长,默认为1
(3) np.logspace
np.logspace(start,stop,num)
创建等比数组,生成以10的N次幂的数据
num:要生成的等比数组数量,默认为50
2.2.9 生成随机数组
导入库
import random
(1) 正态分布
1.np.random.randn
np.random.randn(d0,d1,d2,……,dn)
功能:从标准正态分布中返回一个或多个样本值
2.np.random.normal
np.random.normal(loc = 0.0,scale = 1.0,size = None)
loc:float 此概率分布的均值(对应整个分布的中心)
scale:float 此概率分布的标准差(对应于分布的宽度,scale值越大越矮胖,scale值越小越瘦高) size:int or tuple of ints 输出的shape,默认为None,只输出一个值
3.np.random.standard_normal
np.random.standard_normal(size = None)
返回指定形状的标准正态分布的数组
(2) 均匀分布
1.np.random.rand
np.random.rand(d0,d1,d2,……,dn)
返回[0.0,1.0)内的一组均匀分布的数
2.np.random.uniform
np.random.uniform(low = 0.0,high = 1.0,size = None)
从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开,即包含low,不包含high
low:采样下界,float类型,默认值为0
high:采样上界,float类型,默认值为1
size:输出样本数目,为int或tuple类型,缺少时输出1个值。例如:size=(m,n,k)则输出mnk个样本 返回值:ndarray类型,其形状和参数与size中定义的一致
3.np.random.randint
np.random.randint(low, high = None,size = None,dtype = 'i')
从一个均匀分布中随机采样,生成一个整数或N维整数数组
取数范围:若high不为None时,取(low,high)之间的随机整数,否则取值[0,low)之间随机整数
2.2.10 数组索引,切片
data[a:b,c:d]
注意:这里的数组切片遵循的规律是先行后列
2.2.11 类型修改
(1) ndarray.astype
ndarray.astype(type)
返回修改类型之后的数组
(2) ndarray.tostring和ndarray.tobytes
ndarray.tostring([order])
ndarray.tobytes([order])
构造包含数组中原始数据字节的python字节
2.2.11 ndarray运算
逻辑运算
(1) np.logical_and
np.logical_and(condition1,condition2)
condition:条件 当两个条件同时满足时返回Ture
(2) np.logical_or
np.logical_or(condition1,condition2)
当其中一个条件满足时返回Ture
(3)np.all
np.all()
所有条件都满足要求返回True,有任一个不满足返回False
(4)np.any
np.any()
任一个条件满足要求返回True
(5)np.where(三元运算符)
满足条件返回前一块内容,不满足返回后一块内容
np.where(condition,1,0)
满足条件返回1,不满足返回0
注意:复合逻辑需要结合np.logical_and和np.logical_or使用
2.2.12 统计指标
方法 | 作用 |
np.min(ndarray,axis) | 查询所有行或所有列的最小值 |
np.max(ndarray,axis) | 查询所有行或所有列的最大值 |
np.median(ndarray,axis) | 查询所有行或所有列的中位数 |
np.mean(ndarray,axis) | 查询所有行或所有列的平均值 |
np.std(ndarray,axis,dtype) | 查询所有行或所有列的标准差 |
np.var(ndarray,axis,dtype) | 查询所有行或所有列的方差 |
np.ardmax(ndarray) | 查询最大值的位置 |
np.ardmin(ndarray) | 查询最小值的位置 |
注意:axis轴的取值并不一定,Numpy中不同的API轴的值都不一样
2.3 Pandas工具
导入库
import pandas as pd
2.3.1 三种主要的数据结构
数据结构 | 描述 |
Series | 一维数据结构 |
DataFrame | 二维数据结构 |
MultiIndex | 三维数据结构 |
Panel | 三维数据结构(MultiIndex老版本) |
2.3.2 DataFrame
注意:后面的dataframe均代指二维的对象名
pd.DataFrame(data = None,index = None,columns = None)
index:行标签,如果没有传入索引参数,则默认会自动创建一个从0-N的整数索引
columns:列标签,如果没有传入索引参数,则默认会自动创建一个从0-N的整数索引
属性 | 作用 |
dataframe.shape | 获取维度 |
dataframe.index | 获取行索引表 |
dataframe.columns | 获取列索引表 |
dataframe.values | 获取其中array的值 |
dataframe.T | 行列互换(转置) |
dataframe.head() | 默认获取前5行,其他行数自行添加 |
dataframe.tail() | 默认获取后5行,其他行数自行添加 |
dataframe.info | 获取每一列的非空元素个数 |
2.3.3 DataFrame索引设置
(1) 修改行列索引值
data.index = new_index
data.columns = new_columns
注意:修改索引值不能单个修改,必须整体修改
(2) 重设索引
dataframe.reset_index(drop = False)
设置新的下标索引 drop:默认为False,不删除原来索引,如果为True,删除原来索引值
(3) 以某列值设置为新的索引
dataframe.set_index(keys,drop = True)
keys:列索引命或者列索引名称的列表 drop:bool值默认为True,当做新的索引,删除原来的列
注意:行索引的修改与重设类似列索引
2.3.4 DataFrame排序
dataframe.sort_values(by = ,ascending =)
单个或多个键排序
by:指定排序参考的键
ascending:默认升序
ascending = False 降序
ascending = True 升序
2.3.5 DataFrame运算
(1) 基本运算
元素相加,维度相等时找不到元素默认用fill_value
dataframe.add(dataframe2, fill_value = None, axis = 1)
元素相减,维度相等时找不到元素默认用fill_value
dataframe.sub(dataframe2, fill_value = None, axis = 1)
元素相除,维度相等时找不到元素默认用fill_value
dataframe.div(dataframe2, fill_value = None, axis = 1)
元素相乘,维度相等时找不到元素默认用fill_value
dataframe.mul(dataframe2, fill_value = None, axis = 1)
(2) 逻辑运算函数
query(expr)
expr:查询字符串
通过逻辑运算函数可以使上面的过程更加方便简单
(3) 统计运算
dataframe.describe()
综合统计包括平均值,最大值,最小值等等
统计函数
函数 | 作用 |
sum | 获取总和 |
mean | 获取平均值 |
median | 获取中位数 |
min | 获取最小值 |
max | 获取最大值 |
mode | 获取众数 |
abs | 获取绝对值 |
prod | 获取累积 |
std | 获取标准差 |
var | 获取方差 |
idxmax | 获取最大值索引 |
idxmin | 获取最小值索引 |
累计统计函数
函数 | 作用 |
cumsum | 计算1/2/3/……/n个数的和 |
cummax | 计算1/2/3/……/n个数的最大值 |
cummin | 计算1/2/3/……/n个数的最小值 |
cumprod | 计算1/2/3/……/n个数的积 |
自定义运算
apply(func,axis=0)
func:自定义运算
axis=0默认是列,axis=1为行进行运算
2.3.6 Pandas画图
dataframe.plot(kind='line')
kind:str,需要绘制图形的种类
图形种类
种类 | 描述 |
line | 折线图 |
bar | 条形图(竖直条状) |
bath | 条形图(水平条状) |
hist | 直方图 |
pie | 饼图 |
scatter | 散点图 |
注意: 1.条形图中可以添加是否堆积dataframe.plot(kind='bar',stacked=True) 2.stacked为True时堆积,为False时不堆积
具体过程跟matplotlib中类似,这里主要介绍利用pandas创造的二维对象dataframe,使用plot可以直接将dataframe转换成数据图类型。
2.3.7 文件读取与文件存储
类 | 数据类型 | 读取文件 | 存储文件 |
text | CSV | read_csv | to_csv |
text | JSON | read_json | to_json |
text | HTML | read_html | to_json |
text | Local clipboard | read_clipboard | to_clipboard |
binary | MS Excel | read_excel | to_excel |
binary | HDF5 Format | read_hdf | to_hdf |
binary | Feather Format | read_feather | to_feather |
binary | Parquet Format | read_parquet | to_parquet |
binary | Msgpack | read_msgpack | to_msgpack |
binary | Stata | read_stata | to_stata |
binary | SAS | read_sas | -- |
binary | Python Pickle Format | read_pickle | to_pickle |
SQL | SQL | read_sql | to_sql |
SQL | Google Big Query | read_gbp | to_gbp |
例:
(1)使用numpy中的随机函数创建一个csv图表,图表一共分为5列10000行,各列名称依次为index,count,class,normal_distribution,0-1,分别代表序号,统计数据,类别,正态分布数据,0-1数据,其中count的数据范围在0-10000,class的数据范围为[1,2,3,4,5],normal_distribution的数据范围为0-10000,0-1的数据范围为[0,1],将创建的csv保存
(2)从创建的图表里读取数据,根据normal_distribution列画出画出正态分布的图像
(3)从创建的图表里读取数据,根据class列中各类的数量画饼图,并标注各类占比
(4)从创建的图表离读取数据,根据class和count列画出柱状图,其中y轴为各类的数值(对应count列)总和,x轴为类名称,并在各柱上方标注值
(5)从创建的图表离读取数据,根据count列画出各阶段分布直方图,均分为十段
(6)从创建的图表离读取数据,根据0-1列和class列模拟出各场红蓝对决比赛中人们的支持率
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.stats import norm
np.random.seed(42)
row = 10000
index = np.arange(1,row + 1)
count = np.random.randint(0,10001,size = row)
class_data = np.random.choice([1,2,3,4,5],size = row)
normal = np.random.uniform(0,10001,size = row)
zero = np.random.rand(row)
data = {'index':index , 'count' : count, 'class' : class_data , 'normal_distribute' : normal,
'0 - 1' : zero}
df = pd.DataFrame(data)
df.to_csv('generated_data.csv',index = False)
data = pd.read_csv('generated_data.csv')
normal_data = data['normal_distribute']
mu,std = norm.fit(normal_data)
plt.hist(normal_data,bins = 50,density= True,alpha = 0.5, color = 'r')
xmin,xmax = plt.xlim()
x = np.linspace(xmin,xmax,100)
p = norm.pdf(x,mu,std)
plt.plot(x,p,'k',linewidth = 2)
plt.xlabel('value')
plt.ylabel('Frequency')
plt.legend(['Nornal_distribution','Data'])
plt.grid(True)
plt.show()
data = pd.read_csv('generated_data.csv')
class_counts = data['class'].value_counts()
plt.figure(figsize = (8,8))
plt.pie(class_counts,labels = class_counts.index,autopct = '%1.1f%%',startangle = 140)
plt.title('disribution of classes')
plt.show()
data = pd.read_csv('generated_data.csv')
plt.rcParams["font.sans-serif"] = ["SimHei"]
class_sum = df.groupby('class')['count'].sum()
plt.figure(figsize=(10,7))
class_sum.plot(kind='bar',color='green',edgecolor='black')
plt.title('各类别总计柱状图',fontsize='20')
plt.xlabel('类别',fontsize='15')
plt.ylabel('总数',fontsize='15')
plt.yticks(np.linspace(0,10000000, 11))
plt.tick_params(labelsize=15)
for a, b in enumerate(class_sum): #标记每列数值
plt.text(a, b, str(b),ha='center',va='bottom',fontsize=12)
plt.show()
data = pd.read_csv('generated_data.csv')
plt.figure(figsize=(8, 6))
plt.hist(data['count'], bins=10, color='azure', edgecolor='black')
plt.xlabel('Count')
plt.ylabel('Frequency')
plt.title('Distribution of Counts in Ten Bins')
plt.grid(True)
plt.show()
data = pd.read_csv('generated_data.csv')
support_rates = data.groupby('class')['0 - 1'].mean()
plt.figure(figsize=(8, 6))
colors = ['azure' if team == 1 else 'skyblue' for team in support_rates.index]
plt.bar(support_rates.index, support_rates, color=colors)
plt.xlabel('Team')
plt.ylabel('Support Rate')
plt.title('Support Rate for azure and skyblue Teams')
plt.ylim(0, 1)
plt.show()
2.4 高级数据处理
2.4.1 缺失值处理
首先需要获取缺失值的标记方式(NAN或者其他标记方式,一般来说都是NAN)
判断数据中是否包含NAN
方法 | 描述 |
pd.isnull(dataframe) 或dataframe.isnull() | 有缺失值返回True,没有缺失值返回False |
pd.notnull(dataframe)或dataframe.isnull() | 没有缺失值返回True,有缺失值返回False |
(1) 删除存在缺失值的对象
dataframe.dropna(axis = 0)
默认删除缺失值所在行,可以通过设置axis=1删除所在列
注意:这里不会修改原数据,需要接收返回值
(2) 替换缺失值
dataframe.fillna(value, inplace = True)
value:替换成的值
inplace:True则会修改原数据,False则不替换修改原数据,生成新的对象
(3) 缺失值没有使用NAN标记
通过如下代码将其他标记(比如’?‘)替换为NAN,再使用上面的方法处理dataframe.replace(to_replace = '?',value = np.nan)
2.4.2 合并
(1) pd.concat
pd.concat([data1, data2], axis = 1)
按照行或列进行合并,axis=0为扩展行,axis=1为扩展列
(2) pd.merge
pd.merge(left, right, how = 'inner', on = None)
可以指定按照两组数据的共同键值对合并或者左右各自
left : dataframe
right :另一个dataframe
on :指定的共同键
how:按照什么方式连接
how的方式
方式 | 作用 | 描述 |
left | 左连接 | 左边加左右交集的部分 |
right | 右连接 | 右边加左右交集的部分 |
outer | 外连接 | 并集部分 |
inner | 内连接 | 交集部分 |
2.4.3 分组与聚合
(1) 分组
DataFrame.groupby(key, as_index = True)
key:分组的列数据,可以多个
as_index:若为True则不保留原来的索引,若为False则保留原来的索引
(2) 聚合
一般是指对分组中的数据执行某些操作,比如求平均值、最大值等,并且会得到一个结果集。
3.sklearn
3.1 决策树
决策树( Decision Tree )是一种非参数的有监督学习方法,它能够从一系列有特征和标签的数据中总结出决策规则,并用树状图的结构来呈现这些规则,以解决分类和回归问题。决策树算法容易理解,适用各种数据,在解决各种问题时都有良好表现,尤其是以树模型为核心的各种集成算法,在各个行业和领域都有广泛的应用。
3.1.1 构建决策树
关键概念:全局最优,局部最优
全局最优:经过组合形成的,整体来说分类效果最好的模型
局部最优:每一次分枝的时候都向着更好的分类效果分枝,但无法确认如此生成的树在全局上是否是最优的
关键概念:贪心算法
通过实现局部最优来达到接近全局最优结果的算法,所有的树模型都是这样的算法。
ID3算法构建决策树
重要概念:不纯度
决策树的每个叶子节点中都会包含一组数据,在这组数据中,如果有某一类标签占有较大的比例,我们就说叶子节点“ 纯 ” ,分枝分得好。某一类标签占的比例越大,叶子就越纯,不纯度就越低,分枝就越好。如果没有哪一类标签的比例很大,各类标签都相对平均,则说叶子节点” 不纯 “ ,分枝不好,不纯度高。
注意:分类型决策树在叶子节点上的决策规则是少数服从多数
通常来说,不纯度越低,决策树对训练集的拟合越好。现在使用的决策树算法在分枝方法上的核心大多是围绕在对某个不纯度相关指标的最优化上。
1.2.3 ID3的局限性
ID3 局限主要源于局部最优化条件,即信息增益的计算方法,其局限性主要有以下几点:
分支度越高(分类水平越多)的离散变量往往子节点的总信息熵会更小, ID3 是按照某一列进行切分,有一些列的分类可能不会对我需要的结果有足够好的指示。极限情况下取ID 作为切分字段,每个分类的纯度都是 100% ,因此这样的分类方式是没有效益的
不能直接处理连续型变量,若要使用 ID3 处理连续型变量,则首先需要对连续变量进行离散化
对缺失值较为敏感,使用 ID3 之前需要提前对缺失值进行处理
没有剪枝的设置,容易导致过拟合,即在训练集上表现很好,测试集上表现很差
机器学习的关键概念:过拟合与欠拟合
过拟合:模型在训练集上表现很好,在测试集上表现很糟糕,学习能力很强但是学得太过精细了
欠拟合:模型在训练集和测试集上都表现糟糕,学习能力不足
3.1.2 熵
在化学中,熵用来衡量一种物质内部的混乱程度,即这种物质的纯度。
在概率论中,设有一事件X XX,则它的熵H ( X ) H(X)H(X)可以衡量事件X XX发生的不确定性。
熵的公式为:
当一组数据中的纯度越高,熵值就越小。
3.1.3 Gini系数
当概率越小时,得到的Gini系数越大,这和熵值的定义是一样的。
3.1.4 分类树与回归树
结果字段来看,分类树必须是类别型的字段,回归树则是数值型的字段。他们的形态很像。建构的过程也很类似,只不过目标不同。
分类树一般能得到比较精准的结果,而且解读性比较高。
决策树的基本流程简单概括
直到没有更多的特征可用,或整体的不纯度指标已经最优,决策树就会停止生长。
每分枝一层,树整体的不纯度会越来越小,决策树追求的是最小不纯度。因此,决策树会一致分枝,直到没有更多的特征可用,或整体的不纯度指标已经最优,决策树就会停止生长。
决策树非常容易过拟合,这是说,它很容易在训练集上表现优秀,却在测试集上表现很糟糕。
3.1.6 建立一颗树
#导入需要的算法库和模块
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
3.1.7 红酒数据集
#红酒数据集
#导入需要的算法库和模块
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
wine = load_wine() #加载并返回葡萄酒数据集(分类)。
wine.data.shape #看一下这个表格是怎么样的
#输出(178, 13),代表存在178行,13列
wine.target #查看表格中有几个标签
# array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
# 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
# 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
# 2, 2])
#即数据集中的标签有三种,0,1和2,也就是这些红酒被分成了三类。
#如果wine是一张表,应该长这样
# import pandas as pd
# pd.concat([pd.DataFrame(wine.data),pd.DataFrame(
# .wine.target)],axis=1)
#这里是将红酒属性数据集和标签列进行了横向链接(也叫合并,学过数据库的都知道)
# wine.feature_names
#查看红酒的属性名字
# wine.target_names
#查看标签名字,也就是分类的名字
Xtrain,Xtest,Ytrain,Ytest=train_test_split(wine.data,wine.target,test_size=0.3)
#将数据集分为训练集和测试集,其中70%为训练集,30%为测试集。
Xtrain.shape
#训练集有124个样本,13个属性
Xtest.shape
#训练集有54个样本,13个属性
Ytrain
#查看训练集的目标属性;有三种,为0,1,2
#建立模型
clf = tree.DecisionTreeClassifier(criterion="entropy") #建立决策分类树,判断不纯度的方法是信息熵
clf = clf.fit(Xtrain,Ytrain) #用训练集数据训练模型
score = clf.score(Xtest,Ytest) #返回预测的准确accuracy(分数)
score#查看预测的模型分数
#画出这棵树
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜 色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
import graphviz
dot_data = tree.export_graphviz(clf,out_file = None,feature_names= feature_name,class_names=["琴酒","雪莉","贝尔摩德"],filled=True,rounded=True)
#以 DOT 格式导出决策树。
# decision_tree:决策树分类器;要导出到 GraphViz 的决策树。
# out_file:对象或字符串,默认=无;输出文件的句柄或名称。如果 None ,则结果以字符串形式返回。
# feature_names:str列表,默认=无;每个函数的名称。如果 None 将使用通用名称(“feature_0”、“feature_1”、...)。
# class_names:str 或 bool 的列表,默认 = 无
# 每个目标类别的名称按数字升序排列。仅与分类相关,不支持multi-output。如果 True ,则显示类名的符号表示。
# filled:布尔,默认=假
# 当设置为 True 时,绘制节点以指示分类的多数类、回归值的极值或 multi-output 的节点纯度。
#rounded:布尔,默认=假;当设置为 True 时,绘制圆角节点框。
graph = graphviz.Source(dot_data) #获取生成的决策树
graph #打印生成的决策树
#特征重要性
clf.feature_importances_
#打印每个属性的重要性的数值,只有数值,不知道数值对应的属性是什么
[*zip(feature_name,clf.feature_importances_)]
#打印每个属性及其对应的重要性的数值
# 建更多的不同的树,然后从中取最好的。
# 在每次分枝时,不从使用全部特征,而是随 机选取一部分特征,从中选取不纯度相关指标最优的作为分枝用的节点。
clf = tree.DecisionTreeClassifier(criterion="entropy",random_state=30)
# random_state 用来设置分枝中的随机模式的参数,默认 None
#random_state是一个随机种子,是在任意带有随机性的类或函数里作为参数来控制随机模式。
#当random_state取某一个值时,也就确定了一种规则。
# 在高维度时随机性会表现更明显,低维度的数据 (比如鸢尾花数据集),随机性几乎不会显现。
#固定random_state后,每次构建的模型是相同的、生成的数据集是相同的、每次的拆分结果也是相同的。
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest) #返回预测的准确度
score
#这里的score会固定在0.925,不论你运行多少遍,它都不会变,因为每次都是选择的最优的树。
#使用splitter来降低过拟合的可能性
clf = tree.DecisionTreeClassifier(criterion="entropy",random_state=30 ,splitter="random")
clf = clf.fit(Xtrain, Ytrain)
score = clf.score(Xtest, Ytest)
score #输出结果为0.944444
# splitter 也是用来控制决策树中的随机选项的,有两种输入值
# 输入 "best" :决策树在分枝时虽然随机,但是还是会 优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_ 查看)
# 输入 "random" :决策树在 分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合。
# 这也是防止过拟合的一种方式。当你预测到你的模型会过拟合,用这两个参数来帮助你降低树建成之后过拟合的可能性。
#画出图像
import graphviz
dot_data = tree.export_graphviz(clf,feature_names= feature_name,class_names=["琴酒","雪莉","贝尔摩德"],filled=True,rounded=True )
graph = graphviz.Source(dot_data)
graph
#我们的树对训练集的拟合程度如何?
score_train = clf.score(Xtrain, Ytrain) #返回预测的准确分数
score_train #查看预测的准确分数,此时结果为1.0 过拟合了
#剪枝方法降低过拟合的可能性:
#(1)max_depth 限制树的最大深度
#(2)min_samples_split 限定,一个节点必须要包含至少 min_samples_split 个训练样本,这个节点才允许被分枝,否则分枝就不会发生。
#(3)min_samples_leaf 限定,一个节点在分枝后的每个子节点都必须包含至少 min_samples_leaf 个训练样本,否则分枝就不会发生
# 或者,分枝会朝着满足每个子节点都包含min_samples_leaf 个样本的方向去发生
#通过限制条件进行剪枝,得到的决策树会更简洁,预测的准确分数也不错
clf=tree.DecisionTreeClassifier(criterion="entropy",random_state=30,splitter="random",max_depth=3,min_samples_leaf=5,min_samples_split=5)
clf = clf.fit(Xtrain, Ytrain)
dot_data = tree.export_graphviz(clf,feature_names= feature_name,class_names=["琴酒","雪莉","贝尔摩德"],filled=True,rounded=True)
graph = graphviz.Source(dot_data)
graph
score = clf.score(Xtest, Ytest)
score #0.9629629629629629
# 超参数的学习曲线,是一条以超参数的取值为横坐标,模型的度量指标为纵坐标的曲线,它是用来衡量不同超参数取值下模型的表现的线。
# 在我们建好的决策树里,我们的模型度量指标就是score 。
import matplotlib.pyplot as plt #导入画图库
test = [] #创建一个列表
for i in range(10): #循环10次
clf = tree.DecisionTreeClassifier(max_depth=i+1,criterion="entropy",random_state=30,splitter="random")
clf = clf.fit(Xtrain, Ytrain) #训练模型
score = clf.score(Xtest, Ytest) #预测的准确分数
test.append(score) #往列表里面添加预测的分数值
plt.plot(range(1,11),test,color="red",label="max_depth") #画图
#横坐标为max_depth取值,纵坐标为score,图线颜色为red,label为图例的名称
#从图中我们可以看到,当max_depth取3的时候,模型得分最高
plt.legend() #给图像加图例
plt.show() #显示所打开的图形
3.2 随机森林
3.2.1 集成算法概述
集成学习( ensemble learning )是时下非常流行的机器学习算法,它本身不是一个单独的机器学习算法,而是通过在数据上构建多个模型,集成所有模型的建模结果。基本上所有的机器学习领域都可以看到集成学习的身影,在现实中集成学习也有相当大的作用,它可以用来做市场营销模拟的建模,统计客户来源,保留和流失,也可用来预测疾病的风险和病患者的易感性。在现在的各种算法竞赛中,随机森林,梯度提升树(GBDT ), Xgboost 等集成算法的身影也随处可见,可见其效果之好,应用之广。
集成算法的目标
集成算法会考虑多个评估器的建模结果,汇总之后得到一个综合的结果, 以此来获取比单个模型更好的回归或 分类表现 。
多个模型集成成为的模型叫做集成评估器( ensemble estimator ),组成集成评估器的每个模型都叫做基评估器(base estimator )。通常来说,有三类集成算法:装袋法( Bagging ),提升法( Boosting )和 stacking 。
装袋法的核心思想是构建多个 相互独立的评估器 ,然后对其预测进行平均或多数表决原则来决定集成评估器的结果。装袋法的代表模型就是随机森林。
提升法中, 基评估器是相关的 ,是按顺序一一构建的。其核心思想是结合弱评估器的力量一次次对难以评估的样本进行预测,从而构成一个强评估器。提升法的代表模型有Adaboost 和梯度提升树。
3.2.2 sklearn中的集成算法
sklearn 中的集成算法模块 ensemble
类 类的功能
ensemble.AdaBoostClassifier AdaBoost分类
ensemble.AdaBoostRegressor Adaboost回归
ensemble.BaggingClassifier 装袋分类器
ensemble.BaggingRegressor 装袋回归器
ensemble.ExtraTreesClassifier Extra-trees分类(超树,极端随机树)
ensemble.ExtraTreesRegressor Extra-trees回归
ensemble.GradientBoostingClassifier 梯度提升分类
ensemble.GradientBoostingRegressor 梯度提升回归
ensemble.IsolationForest 隔离森林
ensemble.RandomForestClassifier 随机森林分类
ensemble.RandomForestRegressor 随机森林回归
ensemble.RandomTreesEmbedding 完全随机树的集成
ensemble.VotingClassifier 用于不合适估算器的软投票/ 多数规则类器
集成算法中,有一半以上都是树的集成模型,可以想见决策树在集成中必定是有很好的效果。
3.2.3 重要参数
(1) 控制基评估器的参数
(2) n_estimators
这是森林中树木的数量,即基评估器的数量。这个参数对随机森林模型的精确性影响是单调的, n_estimators 越 大,模型的效果往往越好 。但是相应的,任何模型都有决策边界, n_estimators 达到一定的程度之后,随机森林的精确性往往不在上升或开始波动,并且,n_estimators 越大,需要的计算量和内存也越大,训练的时间也会越来越长。对于这个参数,我们是渴望在训练难度和模型效果之间取得平衡。
random_state
随机森林的本质是一种装袋集成算法( bagging ),装袋集成算法是对基评估器的预测结果进行平均或用多数表决原则来决定集成评估器的结果。在刚才的红酒例子中,我们建立了25 棵树,对任何一个样本而言,平均或多数表决原则下,当且仅当有13 棵以上的树判断错误的时候,随机森林才会判断错误。单独一棵决策树对红酒数据集的分类准确率在0.85 上下浮动,假设一棵树判断错误的可能性为 0.2(ε) ,那 20 棵树以上都判断错误的可能性是:
其中, i 是判断错误的次数,也是判错的树的数量, ε 是一棵树判断错误的概率,( 1-ε )是判断正确的概率,共判对
25-i 次。采用组合,是因为 25 棵树中,有任意 i 棵都判断错误。
可见,判断错误的几率非常小,这让随机森林在红酒数据集上的表现远远好于单棵决策树。
bootstrap & oob_score
要让基分类器尽量都不一样,一种很容易理解的方法是使用不同的训练集来进行训练,而袋装法正是通过有放回的随机抽样技术来形成不同的训练数据,bootstrap 就是用来控制抽样技术的参数。
在一个含有 n 个样本的原始训练集中,我们进行随机采样,每次采样一个样本,并在抽取下一个样本之前将该样本放回原始训练集,也就是说下次采样时这个样本依然可能被采集到,这样采集n 次,最终得到一个和原始训练集一样大的,n 个样本组成的自助集。由于是随机采样,这样每次的自助集和原始数据集不同,和其他的采样集也是不同的。这样我们就可以自由创造取之不尽用之不竭,并且互不相同的自助集,用这些自助集来训练我们的基分类器,我们的基分类器自然也就各不相同了。
bootstrap参数默认True,代表采用这种有放回的随机抽样技术。通常,这个参数不会被我们设置为False。
然而有放回抽样也会有自己的问题。由于是有放回,一些样本可能在同一个自助集中出现多次,而其他一些却可能被忽略,一般来说,自助集大约平均会包含63%的原始数据。因为每一个样本被抽到某个自助集中的概率为:
当 n 足够大时,这个概率收敛于 1-(1/e) ,约等于 0.632 。因此,会有约 37% 的训练数据被浪费掉,没有参与建模,这些数据被称为袋外数据(out of bag data ,简写为 oob) 。除了我们最开始就划分好的测试集之外,这些数据也可以被用来作为集成算法的测试集。也就是说,在使用随机森林时,我们可以不划分测试集和训练集,只需要用袋外 数据来测试我们的模型即可。 当然,这也不是绝对的,当 n 和 n_estimators 都不够大的时候,很可能就没有数据掉落在袋外,自然也就无法使用oob 数据来测试模型了。
3.2.4 机器学习中调参的基本思想
泛化误差
当模型在未知数据(测试集或者袋外数据)上表现糟糕时,我们说模型的泛化程度不够,泛化误差大,模型的效果不好。泛化误差受到模型的结构(复杂度)影响。看下面这张图,它准确地描绘了泛化误差与模型复杂度的关系,当模型太复杂,模型就会过拟合,泛化能力就不够,所以泛化误差大。当模型太简单,模型就会欠拟合,拟合能力就不够,所以误差也会大。只有当模型的复杂度刚刚好的才能够达到泛化误差最小的目标。
那模型的复杂度与我们的参数有什么关系呢?对树模型来说,树越茂盛,深度越深,枝叶越多,模型就越复杂。所以树模型是天生位于图的右上角的模型,随机森林是以树模型为基础,所以随机森林也是天生复杂度高的模型。随机森林的参数,都是向着一个目标去:减少模型的复杂度,把模型往图像的左边移动,防止过拟合。当然了,调参没有绝对,也有天生处于图像左边的随机森林,所以调参之前,我们要先判断,模型现在究竟处于图像的哪一边。泛化误差的背后其实是“ 偏差 - 方差困境 ” ,原理十分复杂,无论你翻开哪一本书,你都会看见长篇的数学论证和每个字都能看懂但是连在一起就看不懂的文字解释。在下一节偏差vs 方差中,我用最简单易懂的语言为大家解释了泛化
误差背后的原理,大家选读。那我们只需要记住这四点:
1 )模型太复杂或者太简单,都会让泛化误差高,我们追求的是位于中间的平衡点
2 )模型太复杂就会过拟合,模型太简单就会欠拟合
3 )对树模型和树的集成模型来说,树的深度越深,枝叶越多,模型越复杂
4 )树模型和树的集成模型的目标,都是减少模型复杂度,把模型往图像的左边移动
3.2.5 综合展示
例:
import math
import matplotlib.pyplot as plt
import numpy as np
from sklearn.ensemble import RandomForestRegressor # 导入随机森林训练模型
from sklearn.metrics import r2_score # 使用拟合优度r2_score对实验结果进行评估
from sklearn.model_selection import train_test_split
from sklearn import datasets
class DecisionNode(object):
def __init__(self, f_idx, threshold, value=None, L=None, R=None):
self.f_idx = f_idx
self.threshold = threshold
self.value = value
self.L = L
self.R = R
# 改变:不需要排序,取实际的数据作为划分点
def find_best_threshold(dataset: np.ndarray, f_idx: int): # dataset:numpy.ndarray (n,m+1) x<-[x,y] f_idx:feature index
best_gain = -math.inf # 先设置 best_gain 为无穷小
best_threshold = None
candidate = list(set(dataset[:, f_idx].reshape(-1)))
for threshold in candidate:
L, R = split_dataset(dataset, f_idx, threshold) # 根据阈值分割数据集,小于阈值
gain = calculate_var_gain(dataset, L, R) # 根据数据集和分割之后的数
if gain > best_gain: # 如果增益大于最大增益,则更换最大增益和最大
best_gain = gain
best_threshold = threshold
return best_threshold, best_gain
def calculate_var(dataset: np.ndarray):
y_ = dataset[:, -1].reshape(-1)
var = np.var(y_)
return var
def calculate_var_gain(dataset, l, r):
var_y = calculate_var(dataset)
var_gain = var_y - len(l) / len(dataset) * calculate_var(l) - len(r) / len(dataset) * calculate_var(r)
return var_gain
def split_dataset(X: np.ndarray, f_idx: int, threshold: float):
L = X[:, f_idx] < threshold
R = ~L
return X[L], X[R]
def mean_y(dataset):
y_ = dataset[:, -1]
return np.mean(y_)
def build_tree(dataset: np.ndarray, f_idx_list: list, depth, max_depth, min_samples): # return DecisionNode 递归
# 怎么判断depth
class_list = [data[-1] for data in dataset] # 类别 dataset 为空了,
n, m = dataset.shape
k = int(math.log(m, 2)) + 1
if n < min_samples:
return DecisionNode(None, None, value=mean_y(dataset))
elif depth > max_depth:
return DecisionNode(None, None, value=mean_y(dataset))
# 全属于同一类别
elif class_list.count(class_list[0]) == len(class_list):
return DecisionNode(None, None, value=mean_y(dataset))
else:
# 找到使增益最大的属性
best_gain = -math. inf
best_threshold = None
best_f_idx = None
# 选取部分属性进行最优划分
f_idx_list_random = list(np.random.choice(m-1, size=k, replace=False))
for i in f_idx_list_random:
threshold, gain = find_best_threshold(dataset, i)
if gain > best_gain: # 如果增益大于最大增益,则更换最大增益和最大阈值
best_gain = gain
best_threshold = threshold
best_f_idx = i
# 创建分支
L, R = split_dataset(dataset, best_f_idx, best_threshold)
if len(L) == 0:
depth += 1
L_tree = DecisionNode(None, None, mean_y(dataset)) # 叶子节点
else:
depth += 1
L_tree = build_tree(L, f_idx_list, depth, max_depth, min_samples) # return DecisionNode
if len(R) == 0:
R_tree = DecisionNode(None, None, mean_y(dataset)) # 叶子节点
else:
R_tree = build_tree(R, f_idx_list, depth, max_depth, min_samples) # return DecisionNode
return DecisionNode(best_f_idx, best_threshold, value=None, L=L_tree, R=R_tree)
def predict_one(model: DecisionNode, data):
if model.value is not None:
return model.value
else:
feature_one = data[model.f_idx]
branch = None
if feature_one >= model.threshold:
branch = model.R # 走右边
else:
branch = model.L # 走左边
return predict_one(branch, data)
# 有放回随机采样
def random_sample(dataset):
n, _ = dataset.shape
sub_data = np.copy(dataset)
random_data_idx = np.random.choice(n, size=n, replace=True) # 0~(n-1) 产生n个 有放回采样
sub_data = sub_data[random_data_idx]
return sub_data[:, 0:-1], sub_data[:, -1]
class Random_forest(object):
def __init__(self, min_samples, max_depth):
self.min_samples = min_samples # 节点样本数量少于 min_samples, 叶子节点
self.max_depth = max_depth # 最大深度
def fit(self, X: np.ndarray, y: np.ndarray) -> None:
dataset_in = np.c_[X, y]
f_idx_list = [i for i in range(X.shape[1])]
depth = 0
self.my_tree = build_tree(dataset_in, f_idx_list, depth, self.max_depth, self.min_samples)
def predict(self, X: np.ndarray) -> np.ndarray: # 递归 how?
predict_list = []
for data in X:
predict_list.append(predict_one(self.my_tree, data))
return np.array(predict_list)
if __name__ == "__main__":
X, y = datasets.load_diabetes(return_X_y=True)
y_predict_list = []
r2_score_list = []
tree_number = []
MAE_list = []
MAPE_list = []
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
print(y_test.shape) # 89*1
dataset = np.c_[X_train, y_train]
np.seterr(divide='ignore', invalid='ignore')
T = 100
for i in range(T):
X_train_samples, y_train_samples = random_sample(dataset)
m = Random_forest(min_samples=5, max_depth=20)
m.fit(X_train_samples, y_train_samples)
y_predict = m.predict(X_test)
y_predict_list.append(y_predict) # 二维数组
print("epoc", i+1, " done")
y_ = np.mean(y_predict_list, axis=0) # 当前的预测值
score = r2_score(y_test, y_)
r2_score_list.append(score)
tree_number.append((i + 1))
errors = abs(y_ - y_test)
MAE_list.append(np.mean(errors)) # 平均绝对误差
mape = 100 * (errors / y_test)
MAPE_list.append(np.mean(mape)) # 平均绝对百分比误差
#
# print("r2_score_list", r2_score_list)
plt.plot(tree_number[5:-1], r2_score_list[5:-1])
plt.title('r2_score')
plt.xlabel('tree number')
plt.ylabel('r2_score')
plt.show()
# print("MAE_list", MAE_list)
#
# print("MAPE_list", MAPE_list)
plt.plot(tree_number, MAPE_list)
plt.xlabel('tree number')
plt.ylabel('MAPE %')
plt.title("MAPE: Mean Absolute Percentage Error")
plt.show()
y_result = np.mean(y_predict_list, axis=0) # 最终结果
print("r2_score:", r2_score(y_test, y_result))
errors1 = abs(y_result - y_test) # 平均绝对误差
print('Mean Absolute Error:', np.round(np.mean(errors1), 2), 'degrees.')
mape = 100 * (errors1 / y_test) # 平均绝对百分比误差
print('MAPE:', np.round(np.mean(mape), 2), '%.')
# accuracy = 100 - np.mean(mape)
# print('Accuracy:', round(accuracy, 2), '%.')
# ---------------------------画图------------------------------
plt.figure(figsize=(20, 5))
plt.plot([i for i in range(y_test.shape[0])], y_test, color='red', alpha=0.8, label="y_test")
plt.plot([i for i in range(y_test.shape[0])], y_result, color='blue', alpha=0.8, label="y_result")
plt.legend(loc="upper right")
plt.title("My Random forest")
plt.show()
# ----------------------------------sklearn--------------------------------
regressor = RandomForestRegressor(n_estimators=100, min_samples_leaf=5)
regressor.fit(X_train, y_train) # 拟合模型
y_pred = regressor.predict(X_test)
print('sklearn score:{}'.format(r2_score(y_test, y_pred))) # 显示训练结果与测试结果的拟合优度
errors = abs(y_pred - y_test)
# Print out the mean absolute error (mae)
print('Mean Absolute Error:', np.round(np.mean(errors), 2), 'degrees.')
mape = 100 * (errors / y_test)
accuracy = 100 - np.mean(mape)
print('Accuracy:', round(accuracy, 2), '%.')
# ---------------------------画图------------------------------
plt.figure(figsize=(20, 5))
plt.plot([i for i in range(y_test.shape[0])], y_test, color='red', alpha=0.8, label="y_test")
plt.plot([i for i in range(y_test.shape[0])], y_pred, color='blue', alpha=0.8, label="y_pred")
plt.legend(loc="upper right")
plt.title("sklearn RandomForestRegressor")
plt.show()