机器学习基础知识,整理自 BJTU 专业选修课《机器学习》课程课件,感谢 lys 大佬的指点(
为了自用从 .ipynb
转成更方便的 markdown
格式……这个自动导出问题还挺多的,注释行必须多加个换行才能在博客正常显示
Part 1 Python Basics and Data Structures
1. 基本类型
python是动态类型语言,不需要声明变量的类型;
常见的基本类型有 int, float, bool, NoneType, str;
a = 1
b = 0.5
c = True
d = None
e = "String"
print()
函数默认在打印内容结尾处换行:
print(a)
print(b)
print(c)
print(d)
print(e)
print()
函数支持多个参数,可用逗号隔开,打印结果也将被空格隔开
print(a, b)
print(c, d, e)
1 0.5
True None String
一个变量名可以被反复赋值,而且不限类型:
a = 1
print(a)
a = 2
print(a)
a = None
print(a)
1
2
None
type()
函数
可以查看变量类型:
print(type(a))
print(type(b))
print(type(c))
print(type(d))
print(type(e))
<class 'NoneType'>
<class 'float'>
<class 'bool'>
<class 'NoneType'>
<class 'str'>
在 python3 中,int 类型和 float 类型是没有限制大小的,长度只受限于机器的内存
a = 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
print(a)
print(type(a))
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
<class 'int'>
返回 float 类型的除法
int 和 float 类型支持加减乘除、乘方、求余等操作;
python3 中整数除法结果默认为 float 类型,想要得到整数值的除法需要用 //
:
a = 3
b = 2
c = 1.5
print(a + b) # 3 + 2
print(a + c) # 3 + 1.5
print(a * c) # 3 * 1.5
print(a / b) # 3 / 2
print(a / c) # 3 / 1.5
print(a // b) # 3 // 2
print(a**2) # a^2
print(a**3) # a^3
print(a % b) # a % b
print(b % c) # b % c
与、或、非 在python中分别为 and
, or
, not
print(1 < 2 and 2 < 3)
print(1 < 2 and 2 > 3)
print(1 < 2 or 2 > 3)
print(1 > 2 or 2 > 3)
print(not 1 > 2)
2. 列表(list)
list 是 python 中最简单,最基础的容器类型,它的特征是一对中括号 [ ]
;
list 中的元素可以是同类型的,也可以是不同类型的:
# list的初始化
a = []
print(a)
a = list()
print(a)
a = [1, 2, 3]
print(a)
a = [1, 2, None, True, "String"]
print(a)
在不考虑内存大小的情况下,list 内的空间是无限大的,我们使用 len()
函数,获取一个列表的长度
print(len(a)) # 列表a的长度
print(len([])) # 空列表的长度
print(len(list())) # 空列表的长度
使用 list[index]
访问第 index+1 个元素:
print(a)
print(a[0])
print(a[1])
print(a[2])
print(a[3])
print(a[4])
print(a[5]) # 越界
print(1 in a) # 1是否在列表a中
print("haha" in a) # "haha"是否在列表a中
在 python 中,可以对列表进行切片(slice),截取多个元素;
list[start: end: step_size]
需要注意的是,切片实际上是 左闭右开 区间,即 start 指向的元素会被选中,end指向的元素不会被选中;
步长(step_size)不指定时,默认为1,即从 start 到 end-1 每个元素都被选中,放入新列表中返回
print(a)
print(a[:]) # 不指定start和end时,默认为0和len(a),即取列表中的所有元素,并返回一个新列表
print(a[0:]) # 从第1个元素到最后一个元素
print(a[2:]) # 从第3个元素到最后一个元素
print(a[1:5]) # 从第2个元素到第5个元素(index为4)
print(a[:-1]) # 从第1个元素到倒数第2个元素
print(a[:-3]) # 从第1个元素到倒数第4个元素
print(a[::1]) # 从第1个元素到最后一个元素,每个元素都取
print(a[::2]) # 从第1个元素到最后一个元素,每隔1个元素取一次
print(a[1::2]) # 从第2个元素到最有一个元素,每隔1个元素取一次
列表生成式
格式为 [expr for element in list] ;
其中 expr 为表达式(可以是对 element 的操作也可以和 element 无关),element 为循环中的每一个元素,list 为列表(实际上任何可迭代对象都可以),比如说要生成从0到9每个数字的平方,可以用:
result = [i**2 for i in range(10)]
print([i**2 for i in range(10)]) # 1到9每个数字的平方
print([type(x) for x in a]) # a中每个元素的类型
取元素时候 index 也可以为负数
尝试取列表a中的倒数第3个元素,将其存入b中
a = [1, 2, None, True, "String"]
# 尝试取列表a中的倒数第3个元素,将其存入b中
# ------start code------
b = a[-3]
# ------end code------
print(b)
列表是一种类型,是 python 内置的类型,该类型有很多常用的方法(method)
比如:添加元素,删除元素,对列表进行排序,查找一个值第一次出现的位置:
# 在列表的末尾插入一个元素
print('before append an element:')
print(a)
a.append("new element")
print('after append an element:')
print(a)
# 在列表的任意位置插入一个元素
print('before insert an element:')
print(a)
# 在下标(index)为1的地方插入一个字符串"new element2"
a.insert(1, "new element2")
print('after insert an element:')
print(a)
# 删除第二个元素,并返回
print("before delete the second element:")
print(a)
# 删除第二个元素,index为1,并返回,赋值给b
b = a.pop(1)
print("after delete the second element:")
print(a)
print("the second element in list a before delete:", b)
# 删除第一次出现的一个元素
a = [1, 2, 3, 1, 2]
print(a)
a.remove(1) # 删除从左到右第一个出现的1
print(a)
a.remove(1) # 删除从左到右第一个出现的1
print(a)
# 对列表进行原地排序
a = [5, 3, 1, 4, 7, 3, 9, 0]
a.sort() # 对a进行从小打大排序
print(a)
a.sort(reverse = True) # 对a进行从大到小排序
print(a)
# 查找第一次出现的一个值
a = [1, 2, 3, 4, 1, 2, 3, 4]
print(a.index(2)) # 打印出从左到右,2这个元素第一次出现的index值
help()
函数
查看对应文档:
help(list) # 使用help函数,查看list的文档
魔术方法(magic methods)
方法名前后有两个下划线的,称为魔术方法,属于 python 的高级技巧
list里的其他方法:
a.clear()
清空列表a.copy()
复制当前列表并返回a.count(b)
返回列表 a 中指定元素 b 的出现次数a.extend(b)
b 是一个列表,将 b 中的所有元素添加到 a 中a.reverse()
将 a 中的元素原地前后转置
a = [1, 2, 3, 4, 5]
# 在下方使用clear方法
# ------start code------
a.clear()
# ------end code------
print(a)
a = [1, 2, 3, 4, 5]
# 在下方使用copy方法,复制给b
# ------start code------
b = a.copy()
# ------end code------
print(b)
a = [1, 2, 3, 4, 5, 1, 3, 6, 2, 4, 1, 4, 2, 6, 8, 2]
# 在下方使用count方法,数出列表a内2的个数
# ------start code------
number = a.count(2)
# ------end code------
print(number)
a = [1, 2, 3, 4, 5]
b = [6, 2, 4, 1, 6, 0]
# 在下方使用extend方法,将b中的元素扩展到a中
# ------start code------
a.extend(b)
# ------end code------
print(a)
print(b)
a = [1, 2, 3, 4, 5, 1, 3, 6, 2, 4, 1, 4, 2, 6, 8, 2]
# 在下方使用reverse方法,将a中的元素转置
# ------start code------
a.reverse()
# ------end code------
print(a)
max()
和 min()
函数
max()
用来求一个可迭代对象中的最大值,如 max(a) ;min()
用来求一个可迭代对象中的最小值,如 min(a) :
a = [1, 2, 3, 4, 5, 1, 3, 6, 2, 4, 1, 4, 2, 6, 8, 2]
# 在下方使用max函数,将a中的最大值存到max_value中,使用min函数,将最小值存到min_value中
# ------start code------
max_value = a.max()
min_value = a.min()
# ------end code------
print(max_value)
print(min_value)
3. 元组(tuple)
元组是一种不可变的类型,它和 list 类似,但是 list 是可变的,tuple不可变,标志是 ( )
:
# 初始化一个tuple
a = ()
print(a)
print(type(a))
a = tuple()
print(a)
# 初始化一个有元素的tuple
a = (1, 2, 3)
print(a)
# 初始化一个仅有一个元素的tuple
a = (1, )
print(a)
# 初始化一个元素的tuple时,如果不加逗号, python 会将小括号理解成数值运算里的括号
a = (1)
print(a)
print(type(a))
和 list 类似,a[index] 来访问元组 a 中的元素
a = (1, 2, 3)
print(a[0])
print(a[1])
print(a[2])
同样,tuple 也是可以用切片取多个元素的
a = (1, 2, 3, 4, 5, 6, 7, 8, 9)
print(a[::2]) # 取index为偶数的元素
print(a[1::2]) # 取index为奇数的元素
print(a[:]) # 取所有的元素
print(a[:-5]) # 取第一个元素到倒数第六个元素
由于元组不可变,没有 list 那样的 sort 和 reverse 方法了
sorted()
函数
列表的 sort()
方法可以就地排序,但是调用这个方法后,并没有返回值;
python 还提供了一个 sorted()
函数,可以将一个可迭代对象内的元素排序后放到一个新 list 中返回
a = [5, 2, 3, 1, 9, 4, 8]
b = sorted(a)
print(b)
c = sorted(a, reverse = True)
print(c)
请尝试对下面的元组a调用sorted函数,将a按从小到大排序的结果存入b中,将a按从大到小排序的结果存入c中
# test1
a = (5, 2, 3, 1, 9, 4, 8)
# ------ start code ------
b = sorted(a)
c = sorted(a, reverse=True)
# ------ end code ------
print(b)
print(c)
sum()
函数
python 中还内置了一个sum函数,用于求一个容器内元素的和:
help(sum)
a = (1.1, 2.2, 3.3, 4.4)
b = sum(a)
print(b)
但是如果要使用 sum 函数对某一个可迭代对象求和,这个可迭代对象里必须都是数值型的类型,比如 int 和 float,如果出现了其他类型,则会报错
a = (1, "list", 2, "tuple")
print(sum(a))
请使用切片(slice)将元组a中index为偶数的元素取出,并将他们的和存入summation中
a = (1, "list", 2, "tuple", 3, "dict", 4, "set")
# ------ start code ------
summation = a[::2]
# ------ end code ------
print(summation)
len()
函数
python 自身并没有求一个容器平均值的函数,不过 python 提供了求和与求长度(元素个数)的函数,请使用这两个函数求下面元组a中元素的平均值
a = (1, 2, 3, 4, 5, 6)
# ------ start code ------
average = a.sum()/len(a)
# ------ end code ------
print(average)
强制类型转换
python 与其他语言一样,提供了强制类型转换的函数,常见的有int, float, str, list, tuple
# 将a转换为浮点型
a = "3.14"
b = float(a)
print(b, type(b))
# 将a转换为整型
a = "3"
b = int(a)
print(b, type(b))
# 将a转换为整型
a = "1"
b = int(a)
print(b, type(b))
# 将a转换为整型
a = 3.99
b = int(a)
print(b, type(b))
需要注意的是,int这个函数,可以将float(小数)转换为整数,但是它会向着趋近于0的方向取整,所以即便是3.99,也会被转换为3
请将下面的字符串a,转换为整型(int),存入b中
a = "-3.14"
# ------ start code ------
b = int(float(a))
# ------ end code ------
print(b)
同样的,list与tuple也可作为强制类型转换的函数
a = (1, 2, 3)
b = list(a)
print(b, type(b))
a = [1, 2, 3]
b = tuple(a)
print(b, type(b))
tuple 的不可变
一旦元组被生成,里面的元素就不可变了,不允许添加、删除以及修改里面的元素
a = [1, 2, 3, 4]
a[0] = 5
print(a)
a = (1, 2, 3, 4)
a[0] = 5
print(a)
但也有可变元组,原因请联想 Java 中的 String 类型;
a = [1, 2, 3]
b = (a, 1, 2)
print(b)
a.append(4)
print(a)
# 打印b试试
# ------ start code ------
print(b)
# ------ end code ------
4. 字符串
python 中的字符串很强大,内置了多种方法,字符串的特征是 " "
, ' '
, ''' '''
, """ """
a = 'Tom'
b = "Tom"
print(a == b)
单引号和双引号没有什么区别,三个引号用于多行的字符串
a = '''TomandJack'''
print(a)
a = 'TomandJack'
+
拼接
print('foo ' + ' bar')
str.strip()
函数:去除空格
print(' ver bo se '.strip())
"%d %f %s"%(int1, float1, string1)
格式化输出
print('int: %d, float: %f, string: %s'%(1, 3.14, 'my god'))
str.upper(), str.lower()
函数:大小写转换
print('Big Small'.upper(), 'Big Small'.lower())
len()
函数
可以查看字符串的长度
len("Tom")
startswith()
, endswith()
函数
字符串有两个常用方法,一个是 startswith,另一个是 endswith,前者用于判断一个字符串是否以某一个字符串为开头,后者用于判断是否以某一字符串为结尾:
print(help(str.startswith))
print(help(str.endswith))
a = "Tom and Jarry"
print(a.startswith('Tom'))
print(a.endswith("Jarry"))
split()
函数
split()
用于分割字符串,返回一个 list:
print(help(str.split))
a = "Tom and Jarry"
print(a.split(' '))
replace()
函数
replace()
用于替换字符串中的子字符串,将替换后的字符串返回,不会影响原字符串
a = "Tom and Jarry"
b = a.replace('Tom', 'Jack')
print(b)
print(a)
strip()
函数
这个方法常用于读文件,或是清理爬虫抓取的内容,很多时候我们读的文件,结尾可能有很多没有用的换行符或空格,如果不处理的话容易引发bug,strip()
可以清除字符串首尾两侧的空格或换行符等
a = " Tom and Jarry \n"
b = a.strip()
b
在此需要提一句,在 Jupyter notebook 的交互式环境下,即使不用 print 也可以打印一个变量的值,直接输入变量名即可;但是这种方法与print函数打印出的内容不同,print 函数会自动解析换行符等字符,但是直接使用变量名显示变量值的方法却不能:
a = " Tom and Jarry \n"
a
a = " Tom and Jarry \n"
print(a)
join()
函数
这个方法可以将一个全是字符串的可迭代对象,如列表,将里面的每个元素之间,插入你想要插入的元素,返回一个新的字符串
print(help(str.join))
a = ['Tom', 'Jack', "Jarry"]
print('\n'.join(a))
a = ['Tom', 'Jack', "Jarry"]
print(', '.join(a))
5. 字典(dict)
python 中的 dict(字典) 是非常强大的数据结构,由键值对组成,一个字典中键只允许有一个。类似其他语言中的map,标志是 { }
# 初始化
a = {}
print(a)
type(a)
# 初始化
a = dict()
print(a)
type(a)
# 初始化
a = {'Name': 'David'}
print(a)
type(a)
使用 []
访问字典中的元素
print(a['Name'])
也可以用 []
赋值:
a['Id'] = 123
print(a)
a['Name'] = 'Jack'print(a)
可以用 in
判断一个元素是否是该字典中的一个键
'Id' in a
'Name' in a
'Age' in a
.keys()
和 .values()
获取所有的键和所有的值:
a.keys()
a.values()
for i in a.keys():
print(i)
for i in a.values():
print(i)
.items()
获得所有的键值对
for i in a.items():
print(i)
可以看到.items()返回的是一个可迭代的对象,里面每个元素都是一个元组。
在 python 中,给多个变量同时赋值有个小技巧
x, y = 1, 2
这实际上是利用了元组的特性,上面的 1,2 组成了一个只有两个元素的元组,可以对两个变量分别赋值,可以利用这个特性交换两个元素:
print(x, y)
x, y = y, x
print(x, y)
如果我们要用for循环遍历一个这样的列表[(1, 2), [3, 4], (5, 6), (7, 8)],也可以使用两个变量分别表示里面的两个元素
t = [(1, 2), [3, 4], (5, 6), (7, 8)]
for x, y in t:
print('x:', x, ',', 'y:', y)
所以我们可以使用这个方法对字典的items方法的返回值进行遍历,即一个变量为键,另一个变量为值
for x, y in a.items():
print('key:%s, value:%s'%(x, y))
上面的打印用了一个小技巧,使用%s占位,在字符串结束后,加上%(),括号里面填写要替换%s的元素
'%s and %s'%("Tom", "Jarry")
如果我们要使用for循环对一个字典遍历,会出现什么情况?
for i in a:
print(i)
可以看到,遍历的是字典的键,所以遍历字典其实还有一种方式,就是遍历它的键,然后用中括号 []
访问值:
for i in a:
print('key: %s, value: %s'%(i, a[i]))
但是不推荐这种方法,如果我们需要同时遍历键和值的话,推荐使用.items(),因为使用上面的这个方法的话 python 会计算a[i]的值,也就是它会去计算i对应的值是多少,如果数据量大的话,这个计算时间是不容忽视的。
python 字典背后的结构是Hash table.
6. 集合(set)
python 中还有一个比较常用的结构,set(集合)
# 初始化
a = set()
print(a)
print(type(a))
# 初始化
a = {1,2,3}
print(a)
print(type(a))
# 初始化
a = {1, }
print(a)
print(type(a))
# 初始化
a = {1}
print(a)
print(type(a))
set()
函数
将一个可迭代的对象转换为集合
# 初始化
a = [1,2,3]
print(set(a))
讲到这里就会提到tuple(元组)的初始化,一般使用括号初始化一个元组,但如果元组中只有一个元素,此时必须写上逗号
a = (1, )
print(a)
print(type(a))
b = (1)
print(b)
print(type(b))
add()
函数
添加元素:
a = {1,2,3}
a.add(4)
print(a)
a.add(1)
print(a)
可以看到,当往集合内添加一个已经存在的元素时,不会报错。
a.remove(1)
print(a)
列表和元组的 remove()
, len()
, sum()
, max()
, min()
, sorted()
函数依然可用;
求交集、并集、差集、对称差集
# 交集
a = {1, 2, 3}
b = {1, 2, 4}
print(a.intersection(b))
print(a & b)
# 并集
a = {1, 2, 3}
b = {1, 2, 4}
print(a.union(b))
print(a | b)
# 差集
a = {1, 2, 3}
b = {1, 2, 4}
print(a.difference(b))
print(a - b)
print(b.difference(a))
print(b - a)
# 对称差集
a = {1, 2, 3}
b = {1, 2, 4}
print(a.symmetric_difference(b))
print(a ^ b)
还有一些常用的方法,比如 clear,update 等,可以通过 help(set) 查看文档
例题:给定一个列表,请检查出a里面不重复元素的个数
import random
temp = [random.randint(0, 1000) for i in range(1000)]
a = [random.choice(temp) for i in range(1000)]
# ------ start code ------
print([([0]+a)[i] - (a+[0])[i] for i in range(len(a))].count(0))
# ------ end code ------
Part 2 Function and Control Sequence
1. 函数
python中的函数很简单,用 def
定义一个函数,函数体有四个空格作为缩进,在函数名后面加一对儿括号就可以调用该函数
def test():
print("test")
test()
参数直接写在括号里面就行,不需要声明它的类型,返回值用 return
返回
def sum_even(lista):
'''
求一个列表中偶数元素之和
'''
s = 0
for i in lista:
if i % 2 == 0:
s += i
return s
sum_even([1,2,3,4,5,6,7,8,9,10])
python 中的函数可以返回多个值,这种时候返回的是一个 tuple
def sum_odd_even(lista):
'''
求一个列表中奇数之和和偶数之和
'''
s_odd = 0
s_even = 0
for i in lista:
if i % 2 == 0:
s_even += i
else:
s_odd += i
return s_odd, s_even
sum_odd_even([1,2,3,4,5,6,7,8,9,10])
在python中定义函数的时候,由于不需要定义形参的类型,所以很多时候函数的参数是很灵活的,像上面的函数,我们不光可以传入list,还可以传入tuple和set,或是其他的可迭代类型
sum_odd_even((1,2,3,4,5,6,7,8,9,10)) # 传入一个tuple
sum_odd_even({1,2,3,4,5,6,7,8,9,10}) # 传入一个set
sum_odd_even({1:"1", 2:"2"}) # 传入一个dict
需要注意的是,如果传入的参数是一个可变类型的变量,如果函数的功能对传入的这个可变类型的变量进行了修改,那么原来传入的这个值就会被改变
def modify(lista):
''' 清空lista '''
lista.clear()
print('clear list!')
a = [1,2,3,4]
modify(a)
clear list!
可以看到,此时的列表已经变成空的了,但是如果我们传入的是一个元组呢
modify((1,2,3))
可以看到,元组是没有clear这个方法的,所以程序报了错。可以看到,元组往往比列表更安全!
默认参数
python中的函数在定义时,可以选择默认参数
def test(num = 0): ''' 打印num这个变量,如果没有num传入,num默认为0 '''
print(num)
test()
test(100)
需要注意一点,如果定义的这个函数有很多参数,那么 默认参数一定要写在非默认参数的后面
def test(num = 0, n):
print(num + n)
上面的定义是有问题的,默认参数后面跟了非默认参数,应该改成下面的样子
def test(n, num = 0):
print(n + num)
test(1)
lambda
函数
和其他大多数语言一样,Python 也提供了匿名函数的功能,语法如下:
lambda param1, param2, ... : f(param1, param2, ...)
比如说定义一个 a+b 的函数:
foo = lambda a, b : a + b
print(foo(2,3))
定义一个 sin(x) + cos(x) 的函数:
import math
sin_cos = lambda x: math.sin(x) + math.cos(x)
print(sin_cos(1.5))
print((lambda x : x**2+x)(10)) # x^2+x
foo = lambda f, x: f(x) + f(2*x)
print(foo(10))
2. 判断
python 中的 if else
没有花括号,使用 4 个空格作为缩进,具有相同缩进的代码块为一个整体。
if 2 > 1:
print("2 > 1")
print("haha")
if 2 < 1:
print("2 > 1")
print("haha")
else:
print("2 < 1")
print("heihei")
使用 if elif else
可以实现多种条件的判断,无需使用 if else
嵌套
x = 1
if x<-1:
y = 2-x
elif 1 <= x <= 1/2:
y = -3 * x
else:
y = x - 2
print(y)
3. 循环
python 中的循环有 for 和 while
while
循环
while 与其他语言的 while 差不多,只要 while 后面的条件满足,就一直执行下去,和 if 语句类似,具有相同缩进的代码块会被同时执行
index = 0
s = 0
while index <= 100:
s += index
index += 1
print(s)
for
循环
python中的for与c中的for有很大区别,python中的for循环的形式是:
for i in iterable:
其中,每循环一次,i 都会分别表示可迭代对象 iterable 中的每一项
for i in [1,2,3,4,5,6,7]:
print(i)
for i in [[1,2,3], [4,5,6], [7,8,9]]:
print(i)
for i in [[1,2,3], [4,5,6], [7,8,9]]:
for j in i:
print(j, end = ' ')
print()
range()
函数
python 中提供了一个 range 函数,这个函数接受 3 个参数,这三个参数与 list 的切片接受的参数差不多
- 第一个参数代表起始值(不指定的时候默认为0);
- 第二个参数是必须传入的参数,代表终点,但是该值不会出现在迭代器中,迭代器中的最后一个数为它前面的一个数;
- 第三个参数为步长,可以缺省,默认为1;
range(5)
for i in range(5):
print(i)
range(2, 5)
for i in range(2, 5):
print(i)
range(1, 11, 2)
for i in range(1, 11, 2):
print(i)
例:使用 while 循环和 for 循环遍历下面的列表 numbers,并打印出奇数的个数
import time
import random
numbers = [random.randint(-100, 100) for i in range(random.randint(20, 100))]
print('data built!')
# ------ start code ------
# for loop
for i in numbers:
if i % 2== 1:
print(i)
# while loop
i = 0
while True:
if i >= len(numbers):
break
elif numbers[i] % 2 == 1:
print(numbers[i])
# ------ end code ------
4. 文件 IO
python中的文件读写很简单,打开文件用open函数,写文件使用文件对象的write方法,不论是读还是写,首先都要先打开一个文件,以下是一种写文件的方式,三种读文件的方法:
f = open('test', 'w') # 打开一个叫做的test的文件,w表示写入(write)
f.write("test") # 写入字符串"test"
f.write("file") # 写入字符串"file"
f.write('\n') # 写入换行符\n
for i in range(10):
f.write(str(i) + '\n') # 写入1到10
f.close() # 关闭文件
f = open('test', 'r') # 使用r读文件(read)
text = f.read() # 使用read方法读文件
f.close()
print(text)
f = open('test', 'r') # 使用r读文件(read)
text = f.readline() # 使用readline方法读文件的一行
f.close()
print(text)
f = open('test', 'r') # 使用r读文件(read)
text = f.readlines() # 使用readlines方法读文件的每一行,放到列表中,并返回
f.close()
print(text)
try...finally
异常处理
需要注意的是,如果在读取文件的过程中,或是在写文件的过程中出现bug,那么 f.close()
很有可能就不会被执行,这样会造成系统资源的浪费,而且可以打开的文件数量是有限的,所以一般我们使用 try…finally 来处理这个问题:
try:
f = open('test', 'r')
text = f.readlines()
1 / 0
finally:
if f:
f.close()
用上面的这种写法,虽然会报错,但是文件确实是被关闭了,可以使用 f.closed
检查
f.closed
可以看到 f.closed 为 True,这说明文件已经被关闭了;
但是上面的这种方法很不简洁,python提供了一个非常简单的写法:
with open('test', 'r') as f:
test = f.read()
print(test)
print("Is the file closed?", f.closed)
1 / 0
print("Is the file closed?", f.closed)
其他文件模式
可以看到,使用 with open() as f
,同样可以做到读写文件,而且这样写起来很简洁,只要把打开文件后要执行的语句,使用 4 个空格作为缩进写在一起即可。
值得注意的是,我们只使用了两种模式,r 和 w,事实上还有 rb 和 wb,分别是读取二进制文件和写入二进制文件,当我们写程序,下载一些东西的时候,比如图片格式,常使用 wb 保存一个图片;
python 还提供了一个模式 ‘a’,这个模式是追加用的,如果用 a 模式打开文件,那么可以直接用 write 方法写入新的信息,这些信息会被追加到文件的尾部。以上这几种是很常用的模式。
可以使用 help(open) 查看 open 的文档
例:尝试使用追加模式,在 test 这个文件后面加入任何你想加的字符串,然后打印整个文件的内容
# ------ start code ------
with open('test', 'w+') as f:
f.write('fooooo')
with open('test', 'r') as f:
print(f.read())
# ------ end code ------
Part 3 Numpy for Numerical Calculation
遇事不决查文档
numpy 基础
numpy 是第三方库,不是 python 自带的,需要自己安装,不过 anaconda 已经集成了numpy;
windows下安装很麻烦,需要安装 vs2015,或者使用 https://www.lfd.uci.edu/~gohlke/pythonlibs/ 这里提供的编译好的安装包进行安装;
linux/OSX 下保证有 gcc 和 g++ 即可;
导入外部包
语法: import package as alias
,其中 package 为包名,alias 为别名,不写 as alias
就是不取别名
如果是从包里导入一个子模块的话: from package import sub_module
# 导入 numpy,简写为 np
import numpy as np
numpy 主要提供的是矩阵运算的功能,底层是 c 语言编写的,而且用了很多优化的策略,所以性能很强
创建矩阵
# 创建一个一维向量
v = np.array([1, 2, 3, 4])
print(v)
print('type:', type(v))
print("v的维度:", v.shape)
可以看到维度为(4, ),v.shape 是一个 tuple,(4, )表示这是一个长度为4的一维向量
# 创建一个二维矩阵
m = np.array([[1, 2],
[3, 4]])
print(m)
print(type(m))
print(m.shape)
可以看到 m 的维度是 (2,2),也就是一个 $2 \times 2$ 的矩阵
m2 = np.array([[1],
[2],
[3],
[4]])
print(m2.shape)
(4, 1)
reshape()
函数
可以看到,m2 的维度是 (4, 1),说明这是一个 4 行 1 列的矩阵,而v的维度是 (4, ),说明v是一个长度为4的一维向量,注意两者的区别;
# 将m2变成一个1行4列的矩阵
m3 = m2.reshape(1, 4)
print(m3)
print(m3.shape)
[[1 2 3 4]](1, 4)
使用 reshape 可以对 array 的维度进行变换,会返回一个新的 array,不会影响该 array 原来的维度
在二维的 array 中,reshape 只要指定一个维度即可,另一个维度可以设置为 -1,让它自动计算;
比如,我们已经知道了 m3 是一个 1 行 4 列的矩阵,我们想将其变成一个1列的矩阵,有多少行让它自己计算,那么就使用 reshape(-1, 1):
m3.reshape(-1, 1)
取元素
在机器学习任务中,我们一般把行作为样本,也就是一行表示一个样本,列作为特征,也就是一列表示一个特征。假设我们有数据集 data,data 是一个 4行3列的数据集。
data = np.random.uniform(0, 1, size = (4, 3))
print(data)
[[0.3890509 0.07852005 0.10866473]
[0.68119759 0.73162121 0.84347843]
[0.92551516 0.79543677 0.49450627]
[0.49077995 0.66073402 0.81769004]]
我们想取出第2行第2列的值:
# 取第二行第二列
data[1, 1] # 下标从0开始
这里使用切片的方法,这个切片和python的list的切片类似,忘了的可以回前面看一下。
切片中的第一个数字表示多少行,第二个数字表示多少列,下标从0开始,所以第二行第二列的值即为data[2, 2]:
# 取第二行,第三列
data[1, 2]
在机器学习任务中,我们经常会取一个样本,或取一个特征,也就是在矩阵中,取一行或一列
# 取出第一行
row_0 = data[0, :]
print(row_0)
print(row_0.shape)
[0.3890509 0.07852005 0.10866473](3,)
使用切片的方法,切片中,第一个元素,0表示第一行,冒号 :
表示从第一个元素取到最后一个元素,这样就能把第0行,所有列的元素都取到。
# 取出第一行
row_0 = data[0, 0: 3]
print(row_0)
print(row_0.shape)
[0.3890509 0.07852005 0.10866473](3,)
上面这种方法和前面只用冒号得到的值一样
# 取第一列
col_0 = data[:, 0]
print(col_0)
print(col_0.shape)
[0.3890509 0.68119759 0.92551516 0.49077995](4,)
可以看到,不论是取行,还是取列,如果只取一行或一列,得到的值就会变成一个一维的 array。很多时候我们还想让他继续保持原来的维度,这时候就需要 reshape 一下
# 取第一行,但还要保持二维
row_0_ = data[0, :].reshape(1, -1)
print(row_0_)
print(row_0_.shape)
[[0.3890509 0.07852005 0.10866473]](1, 3)
# 取第一列,但还要保持二维
col_0_ = data[0, :].reshape(-1, 1)
print(col_0_)
print(col_0_.shape)
[[0.3890509 ] [0.07852005] [0.10866473]](3, 1)
加减法
a = np.array([1,2])
b = np.array([3,4])
print(a, b)
[1 2] [3 4]
a 和 b 都是一个长度为2的一维向量,可以直接相加减
a + b
a - b
b - a
c = np.array([[1, 2], [3, 4]])
d = np.array([[1, 1], [1, 1]])
print('c:', c)
print('-'*30)
print('d:', d)
c 和 d 是同维度的二维矩阵,也是可以直接相加减
c + d
c - d
数乘
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A)
3 * A # = array([[ 3, 6, 9], [12, 15, 18]])
同理,还有数加
3 + A # = array([[4, 5, 6], [7, 8, 9]])
np.dot()
函数:矩阵乘法
我们知道,矩阵 $A \in \mathbb{R}^{m \times n}$ 和矩阵 $B \in \mathbb{R}^{n \times o}$ 是可以直接相乘的,因为前一个矩阵的列数等于后一个矩阵的行数:
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A)
B = np.array([[1, 2],
[3, 4],
[5, 6]])
print(B)
np.dot(A, B) # array([[22, 28], [49, 64]])
.T
矩阵转置
同理,我们将矩阵 $A$ 转置的话,就会变成一个3行2列的矩阵,将 $B$ 转置,变成2行3列,这两个矩阵也是可以相乘的
A.T # A的转置
B.T # B的转置
np.dot(A.T, B.T) # array([[ 9, 19, 29], [12, 26, 40], [15, 33, 51]])
*
矩阵对应元素相乘
很多时候我们需要一种对应元素相乘的方法,$A \in \mathbb{R}^{m \times n}$,$B \in \mathbb{R}^{m \times n}$,定义一种乘法$\odot$,\([A \odot B]_{ij} = [A]_{ij} * [B]_{ij}\)
这种乘法得到的积称为哈达玛积(Hadamard product),也称为 element-wise product,通俗来说就是两个维度相同的矩阵,他们的对应元素相乘:
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A)
B = np.array([[1, 2, 3], [1, 2, 3]])
print(B)
A * B # = array([[ 1, 4, 9], [ 4, 10, 18]])
使用 *
直接将两个array相乘,即为两个矩阵的 hadamard product
sum()
函数:求矩阵内元素之和
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A)
A.sum()
np.sum(A)
求一个矩阵每行的和:
A.sum(axis = 1)
np.sum(A, axis = 1)
求一个矩阵每列之和:
A.sum(axis = 0)
np.sum(A, axis = 0)
max()
函数:求矩阵中最大的元素
A.max()
求每行的最大元素:
A.max(axis = 1)
求每列的最大元素:
A.max(axis = 0)
min()
函数:求最小值
mean()
函数:求均值
std()
函数:求标准差
np.exp()
函数:对矩阵做指数运算
np.log
, np.log2
, np.log10
函数:对矩阵做对数运算
例题:请你求出矩阵A的最小值,均值,标准差,每行的最小值,每列的最小值,每行的均值,每列的均值,每行的标准差,每列的标准差,看一看 np.exp(A) 和 np.log(A) 的效果
## YOUR CODE HERE
np.min(A)
np.mean(A)
np.std(A)
np.min(A, axis=1)
np.min(A, axis=0)
np.mean(A, axis=1)
np.mean(A, axis=0)
np.std(A, axis=1)
np.std(A, axis=0)
np.exp(A)
np.log(A)
除此以外,np.exp,np.log,np.log2,np.log10等操作是可以对 python 中的 int 和 float 类型做运算的
print(np.exp(1))
print(np.exp(2))
print(np.log2(2))
np.argmax()
取出一个一维向量中最大值的下标
这是一个非常重要的操作,为什么这么说呢,在机器学习中,很多问题都是分类问题,也就是给你一张图片,上面有个数字,问你这个数字是几?很多算法会输出这个模型认为这个数字是0, 1, …, 9的概率,也就是一个长度为10的一维向量。
这时候我们要取出概率最大的那个数所在的下标,这个下标就代表这个数字是几了。
比如,我们得到模型当前的输出了,输出为 output
output = np.array([ 0.33847987, 0.7492099 , 0.20938843, 0.53851897, 0.638118 , 0.52182376, 0.98172993, 0.12160851, 0.5551554 , 0.86638236])
可以看到,10个数,分别表示模型认为这个数字是0, 1, …, 9的概率,我们要找数最大的那个的下标是几,使用 np.argmax 即可获取到最大值的下标,也就是0.9817对应的下标:
np.argmax(output)
np.argmin()
找最小值对应的下标
使用np.argmin找出output最小值的下标
print(np.argmin(output))
同样,argmax
和 argmin
也支持 axis 这个参数
A = np.array([[0.33847987, 0.7492099 ],
[0.20938843, 0.53851897],
[0.638118 , 0.52182376],
[0.98172993, 0.12160851],
[0.5551554 , 0.86638236]])
求矩阵每行最大值对应的下标:
np.argmax(A, axis = 1)
求这个矩阵,每列最小值对应的下标
np.argmin(A, axis=0)
广播(broadcast)
这是基础部分的最后一个概念,什么是广播呢,可以理解为复制,假设有两个矩阵$A \in \mathbb{R}^{1 \times 3}$,$B \in \mathbb{R}^{3 \times 3}$
A = np.array([[1, 2, 3]])
print(A.shape)
B = np.random.uniform(size = (3, 3))
print(B.shape)
在数学领域中,A+B是不能做的,因为两个矩阵的维度不一致,在 numpy 中,有一种机制叫做广播,让这两个维度不同的矩阵可以进行加减乘:每次操作的时候,numpy 将矩阵A复制了两份,变成了一个3行3列的矩阵,每行都是之前A的那一行,3行的值一样,然后再与B做操作,这样就得到了四种操作的值。
A + B
A - B
B - A
A * B
那么如果A是一个3行1列的矩阵,能和B做这四种操作吗?
A = np.array([[1],
[2],
[3]])
print(A.shape)
## YOUR CODE HERE
A + B
广播的具体原理就是,如果两个矩阵都是二维的,那么只要有一个维度两个矩阵的值相等,那就会自动扩充另一维度,如果扩充,通过复制的方法,将当前的数据,沿着那个待扩充的维度不断的复制。
以上就是numpy的基础操作
综合练习题
完成一个很简单的机器学习预测任务!我们现在有一个特别简单的模型,它是 \(\hat{y} = \frac{1}{1 + \exp^{-xw + b}}\)
我们现在有一个样本x,它有10个特征,这样的话它就是一个长度为10的一维向量。
在机器学习任务中,模型是对单个样本进行计算的,也就是说,我们有10个特征,那么参数W,有10个数,b有一个数。这样W乘以样本x,就是一个长度为10的向量,与另一个长度为10的向量进行乘积,得到一个长度为1的数,然后加上b,得到长度为1的这个数。在这个模型中,还需要对它取相反数,然后做指数运算,加1,取倒数,就得到了 $\hat{y}$ ,请你求出 $\hat{y}$ 是多少:
x = np.array([ 0.78803502, 0.85090948, 0.17827904, 0.26081458, 0.61807529,
0.06409987, 0.70153396, 0.10446683, 0.52234655, 0.80166488])
W = np.array([[ 0.02256906],
[ 0.77558206],
[ 0.29299293],
[ 0.18633 ],
[ 0.11959697],
[ 0.20485966],
[ 0.55220315],
[ 0.1510716 ],
[ 0.66596428],
[ 0.29461207]])
b = 0.39147861519281024
你的任务是求出 $\hat{y}$
## YOUR CODE HERE
1/(np.exp(-np.sum(x*W.reshape(-1))+b)+1)
答案是 0.81173994
但是在实际问题中,我们往往有上千个样本,X是我们的输入,是一个1000行,10列的矩阵,表示我们有1000个样本,10个特征,那么如何使用这个模型,对这1000个样本,求出他们的1000个值呢?
- 使用for循环,将刚才对单个样本做预测的方法调用1000次即可 这种方法理论上没有问题,但是在python中,for循环的速度要比c和java的for慢很多,计算的时间会很长
- 直接使用矩阵进行运算 $X \in \mathbb{R}^{1000 \times 10}$,$W \in \mathbb{R}^{10 \times 1}$ \(X \times W \in \mathbb{R}^{1000 \times 1}\) 可以看到,矩阵X乘以矩阵W得到一个1000×1的矩阵,这恰好就是将W,分别与矩阵X的每一行相乘,得到了1000个数,然后我们直接加上b,这样利用 numpy 的广播机制,b就会复制999次,将b与每一行相加,得到的还是这1000个值,然后取指数,加1,求倒数即可。所以,第二种方法中,只要将前面计算单个样本中的x换成X即可。而且速度更快
请你在下方完成这两种方法,计算出1000个输出值
from time import time
X = np.random.uniform(size = (1000, 10))
# 使用for循环的计算方法
start_time = time()
# for 循环
[1/(np.exp(-np.sum(x*W.reshape(-1))+b)+1) for x in np.transpose(X)]
end_time = time()
print('time:', end_time - start_time)
# 使用矩阵运算的计算方法
start_time = time()
# np.matmul() 函数:进行两个 numpy 数组的矩阵相乘,计算时会自动进行广播
1/(np.exp(-np.matmul(X, W) + b) + 1)
end_time = time()
print('time:', end_time - start_time)
Part 4 Pandas for Data Analysis
遇事不决查文档
pandas 是一个非常好用的容器工具,构建在 numpy 之上,anaconda 已经集成。
pandas 在管理结构化数据上非常方便,底层是 numpy,所以性能很强劲,而且在处理时间序列数据时非常方便。
jupyter 对 pandas 的支持很好,可以直接显示 pandas 的数据结构,如 DataFrame
import pandas as pd
data = pd.read_csv('test_data.csv')
使用 pd.read_csv
即可读取 csv 文件,返回一个 pd.DataFrame
head()
函数
使用 data.head()
就可以看到这个 dataframe 的前5行,可以使用 data.head(10) 看前10行
仔细观察的话,可以发现这个 dataframe,最上面一行是列名,左侧第一列是行的序号。列名倒是列名,但其实第一列不是行的序号,它可以是任意值,比如”a”,”b”,”c”,这样的字符串,只不过在这个数据集中,它恰好为0,1,2…。我们称最左侧的这一列为索引(index),可以通过它对行进行存取。一会儿在后面会说存取的问题。
data.head()
Id | MSSubClass | MSZoning | LotFrontage | LotArea | Street | Alley | LotShape | LandContour | Utilities | ... | PoolArea | PoolQC | Fence | MiscFeature | MiscVal | MoSold | YrSold | SaleType | SaleCondition | SalePrice | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 60 | RL | 65.0 | 8450 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2008 | WD | Normal | 208500 |
1 | 2 | 20 | RL | 80.0 | 9600 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 5 | 2007 | WD | Normal | 181500 |
2 | 3 | 60 | RL | 68.0 | 11250 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 9 | 2008 | WD | Normal | 223500 |
3 | 4 | 70 | RL | 60.0 | 9550 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2006 | WD | Abnorml | 140000 |
4 | 5 | 60 | RL | 84.0 | 14260 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | NaN | NaN | NaN | 0 | 12 | 2008 | WD | Normal | 250000 |
info()
函数
data.info()
可以看到整个数据的全貌,1460 not-null 就表示有1460个非缺失值,object 表示这列是字符串类型的,int64 表示这列是 int 类型,float64 表示这列是 float 类型
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1460 entries, 0 to 1459
Data columns (total 81 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Id 1460 non-null int64
1 MSSubClass 1460 non-null int64
2 MSZoning 1460 non-null object
3 LotFrontage 1201 non-null float64
4 LotArea 1460 non-null int64
5 Street 1460 non-null object
6 Alley 91 non-null object
7 LotShape 1460 non-null object
8 LandContour 1460 non-null object
9 Utilities 1460 non-null object
10 LotConfig 1460 non-null object
11 LandSlope 1460 non-null object
12 Neighborhood 1460 non-null object
13 Condition1 1460 non-null object
14 Condition2 1460 non-null object
15 BldgType 1460 non-null object
16 HouseStyle 1460 non-null object
17 OverallQual 1460 non-null int64
18 OverallCond 1460 non-null int64
19 YearBuilt 1460 non-null int64
20 YearRemodAdd 1460 non-null int64
21 RoofStyle 1460 non-null object
22 RoofMatl 1460 non-null object
23 Exterior1st 1460 non-null object
24 Exterior2nd 1460 non-null object
25 MasVnrType 1452 non-null object
26 MasVnrArea 1452 non-null float64
27 ExterQual 1460 non-null object
28 ExterCond 1460 non-null object
29 Foundation 1460 non-null object
30 BsmtQual 1423 non-null object
31 BsmtCond 1423 non-null object
32 BsmtExposure 1422 non-null object
33 BsmtFinType1 1423 non-null object
34 BsmtFinSF1 1460 non-null int64
35 BsmtFinType2 1422 non-null object
36 BsmtFinSF2 1460 non-null int64
37 BsmtUnfSF 1460 non-null int64
38 TotalBsmtSF 1460 non-null int64
39 Heating 1460 non-null object
40 HeatingQC 1460 non-null object
41 CentralAir 1460 non-null object
42 Electrical 1459 non-null object
43 1stFlrSF 1460 non-null int64
44 2ndFlrSF 1460 non-null int64
45 LowQualFinSF 1460 non-null int64
46 GrLivArea 1460 non-null int64
47 BsmtFullBath 1460 non-null int64
48 BsmtHalfBath 1460 non-null int64
49 FullBath 1460 non-null int64
50 HalfBath 1460 non-null int64
51 BedroomAbvGr 1460 non-null int64
52 KitchenAbvGr 1460 non-null int64
53 KitchenQual 1460 non-null object
54 TotRmsAbvGrd 1460 non-null int64
55 Functional 1460 non-null object
56 Fireplaces 1460 non-null int64
57 FireplaceQu 770 non-null object
58 GarageType 1379 non-null object
59 GarageYrBlt 1379 non-null float64
60 GarageFinish 1379 non-null object
61 GarageCars 1460 non-null int64
62 GarageArea 1460 non-null int64
63 GarageQual 1379 non-null object
64 GarageCond 1379 non-null object
65 PavedDrive 1460 non-null object
66 WoodDeckSF 1460 non-null int64
67 OpenPorchSF 1460 non-null int64
68 EnclosedPorch 1460 non-null int64
69 3SsnPorch 1460 non-null int64
70 ScreenPorch 1460 non-null int64
71 PoolArea 1460 non-null int64
72 PoolQC 7 non-null object
73 Fence 281 non-null object
74 MiscFeature 54 non-null object
75 MiscVal 1460 non-null int64
76 MoSold 1460 non-null int64
77 YrSold 1460 non-null int64
78 SaleType 1460 non-null object
79 SaleCondition 1460 non-null object
80 SalePrice 1460 non-null int64
dtypes: float64(3), int64(35), object(43)
memory usage: 924.0+ KB
使用 data[列明]取一列,返回的是 pd.Series,可以这样理解,Series 组成 DataFrame,DataFrame 中的行和列都是 Series,DataFrame 是二维的,Series 是一维的
data['SalePrice'].head(10) # Series也是支持head的
0 208500
1 181500
2 223500
3 140000
4 250000
5 143000
6 307000
7 200000
8 129900
9 118000
Name: SalePrice, dtype: int64
取多列数据
还有一种取多列的方式,将要取的列,写到一个list中
columns = ['LotFrontage', 'SalePrice']data[columns].head()
LotFrontage | SalePrice | |
---|---|---|
0 | 65.0 | 208500 |
1 | 80.0 | 181500 |
2 | 68.0 | 223500 |
3 | 60.0 | 140000 |
4 | 84.0 | 250000 |
.iloc
截取行
第一行就用data.iloc[0],第i行就用data.iloc[i-1]
data.iloc[0]
Id 1
MSSubClass 60
MSZoning RL
LotFrontage 65.0
LotArea 8450
...
MoSold 2
YrSold 2008
SaleType WD
SaleCondition Normal
SalePrice 208500
Name: 0, Length: 81, dtype: object
返回的也是一个Series,Series类似字典,可以用左侧的index取到右面的值,比如
data.iloc[0]['SalePrice']
data.iloc[0]['SaleType']
同样的道理,也可以先取列,再取行
data['SalePrice'].iloc[0]
刚才不是说左侧的0,1,2是索引吗,所以 dataframe 也是可以通过索引来取值的;
用data.loc[index]来取行,可以看到,第一行的索引为0,所以可以通过 data.loc[0] 取到第0行。假设第一行的索引为”a”,那我们就可以用data.loc[“a”] 取到第一行。
data.loc[0]
Id 1
MSSubClass 60
MSZoning RL
LotFrontage 65.0
LotArea 8450
...
MoSold 2
YrSold 2008
SaleType WD
SaleCondition Normal
SalePrice 208500
Name: 0, Length: 81, dtype: object
drop()
函数
使用 data.drop(列名, axis = 1, inplace = False)
可以丢掉一列,inplace 表示是否在原地操作,如果为True,就会直接删除data中的这列,不会有返回值,如果为False,就会返回一个删除到该列的新的DataFrame,不改变原来的 DataFrame:
data.drop('SalePrice', axis = 1, inplace = False).head()
Id | MSSubClass | MSZoning | LotFrontage | LotArea | Street | Alley | LotShape | LandContour | Utilities | ... | ScreenPorch | PoolArea | PoolQC | Fence | MiscFeature | MiscVal | MoSold | YrSold | SaleType | SaleCondition | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 60 | RL | 65.0 | 8450 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | 0 | NaN | NaN | NaN | 0 | 2 | 2008 | WD | Normal |
1 | 2 | 20 | RL | 80.0 | 9600 | Pave | NaN | Reg | Lvl | AllPub | ... | 0 | 0 | NaN | NaN | NaN | 0 | 5 | 2007 | WD | Normal |
2 | 3 | 60 | RL | 68.0 | 11250 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | 0 | NaN | NaN | NaN | 0 | 9 | 2008 | WD | Normal |
3 | 4 | 70 | RL | 60.0 | 9550 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | 0 | NaN | NaN | NaN | 0 | 2 | 2006 | WD | Abnorml |
4 | 5 | 60 | RL | 84.0 | 14260 | Pave | NaN | IR1 | Lvl | AllPub | ... | 0 | 0 | NaN | NaN | NaN | 0 | 12 | 2008 | WD | Normal |
5 rows × 80 columns
如果drop的axis = 0,就可以删除行
describe()
函数
使用data.describe()可以看到关于这个dataframe的描述,count表示有多少个非缺失值,mean表示均值,std标准差,min最小值,25%是第一四分位数,50%第二四分位数,75%第三四分位数,max表示最大值
data.describe()
Id | MSSubClass | LotFrontage | LotArea | OverallQual | OverallCond | YearBuilt | YearRemodAdd | MasVnrArea | BsmtFinSF1 | ... | WoodDeckSF | OpenPorchSF | EnclosedPorch | 3SsnPorch | ScreenPorch | PoolArea | MiscVal | MoSold | YrSold | SalePrice | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 1460.000000 | 1460.000000 | 1201.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1452.000000 | 1460.000000 | ... | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 | 1460.000000 |
mean | 730.500000 | 56.897260 | 70.049958 | 10516.828082 | 6.099315 | 5.575342 | 1971.267808 | 1984.865753 | 103.685262 | 443.639726 | ... | 94.244521 | 46.660274 | 21.954110 | 3.409589 | 15.060959 | 2.758904 | 43.489041 | 6.321918 | 2007.815753 | 180921.195890 |
std | 421.610009 | 42.300571 | 24.284752 | 9981.264932 | 1.382997 | 1.112799 | 30.202904 | 20.645407 | 181.066207 | 456.098091 | ... | 125.338794 | 66.256028 | 61.119149 | 29.317331 | 55.757415 | 40.177307 | 496.123024 | 2.703626 | 1.328095 | 79442.502883 |
min | 1.000000 | 20.000000 | 21.000000 | 1300.000000 | 1.000000 | 1.000000 | 1872.000000 | 1950.000000 | 0.000000 | 0.000000 | ... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 2006.000000 | 34900.000000 |
25% | 365.750000 | 20.000000 | 59.000000 | 7553.500000 | 5.000000 | 5.000000 | 1954.000000 | 1967.000000 | 0.000000 | 0.000000 | ... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 5.000000 | 2007.000000 | 129975.000000 |
50% | 730.500000 | 50.000000 | 69.000000 | 9478.500000 | 6.000000 | 5.000000 | 1973.000000 | 1994.000000 | 0.000000 | 383.500000 | ... | 0.000000 | 25.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 6.000000 | 2008.000000 | 163000.000000 |
75% | 1095.250000 | 70.000000 | 80.000000 | 11601.500000 | 7.000000 | 6.000000 | 2000.000000 | 2004.000000 | 166.000000 | 712.250000 | ... | 168.000000 | 68.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 8.000000 | 2009.000000 | 214000.000000 |
max | 1460.000000 | 190.000000 | 313.000000 | 215245.000000 | 10.000000 | 9.000000 | 2010.000000 | 2010.000000 | 1600.000000 | 5644.000000 | ... | 857.000000 | 547.000000 | 552.000000 | 508.000000 | 480.000000 | 738.000000 | 15500.000000 | 12.000000 | 2010.000000 | 755000.000000 |
8 rows × 38 columns
sum()
, mean()
, std()
, max()
, min()
函数
和 numpy 类似,pandas 也支持这些方法
例:请你输出 data.sum(), data.mean(), data.std(), data.max(), data.min() 的结果
# YOUR CODE HERE
data.sum()
data.mean()
data.std()
data.max()
data.min()
Id 1
MSSubClass 20
MSZoning C (all)
LotFrontage 21.0
LotArea 1300
...
MoSold 1
YrSold 2006
SaleType COD
SaleCondition Abnorml
SalePrice 34900
Length: 65, dtype: object
输出到文件中
使用 data.to_csv(“文件名”) 就可以将当前的dataframe保存为.csv文件,同理还有to_excel, to_json, to_dict等方法
# 用.values转化为numpy
print(data.iloc[:5,:5].values)
[[1 60 'RL' 65.0 8450]
[2 20 'RL' 80.0 9600]
[3 60 'RL' 68.0 11250]
[4 70 'RL' 60.0 9550]
[5 60 'RL' 84.0 14260]]
# YOUR CODE HERE
data.to_csv('foo.csv')
Part 5 Matplotlib for Visualization
遇事不决查文档
Matplotlib 是一个画图的库,和numpy,pandas可以无缝衔接,anaconda已经集成;
jupyter对matplotlib的支持很好,可以直接在jupyter中显示画出来的图:
import matplotlib.pyplot as plt
%matplotlib inline
这样图就能在jupyter中直接显示;
在 python 文件中就不要加了,会报错
plt.figure()
的 figsize 参数
import numpy as np
a = np.array([1, 2, 3, 4, 5, 6])
b = np.array([1, 2, 3, 4, 5, 6])
c = np.array([6, 5, 4, 3, 2, 1])
plt.figure(figsize = (6, 6))
plt.plot(a, b, 'o')
plt.figure(figsize) 中,figsize 指定图的大小
尝试改变 figsize 为 (4, 4), (10, 6) 试试:
plt.figure(figsize=(4,4))
plt.plot(a, b, 'o')
plt.figure(figsize=(10,6))
plt.plot(a, b, 'o')
plt.plot()
的字符串参数
尝试将 o
改成 o-
, .
, .-
# o- 是比较大的点和线图
plt.plot(a, b, 'o-')
# . 是散点图
plt.plot(a, b, '.')
# .- 是点线图
plt.plot(a, b, '.-')
plt.show()
plt.plot()
的颜色参数
plt.figure(figsize = (8, 4))
plt.plot(a, b, '-', color = 'red')
尝试将color换成’blue’, ‘yellow’, ‘green’试试
# blue
plt.figure(figsize = (8, 4))
plt.plot(a, b, '-', color = 'blue')
# yellow
plt.figure(figsize = (8, 4))
plt.plot(a, b, '-', color = 'yellow')
# green
plt.figure(figsize = (8, 4))
plt.plot(a, b, '-', color = 'green')
plt.xlabel()
, plt.ylabel()
增加 x,y 标签
plt.title()
增加标题
使用 xlabel, ylabel, title 增加 x 标签,y 标签,标题:
plt.figure(figsize = (8, 4))
plt.plot(a, b, '-', color = 'red')
plt.xlabel("x")
plt.ylabel("y")
plt.title("title")
plt.plot()
对曲线增加 label 参数
还可以对曲线增加 label,然后显示图例,在 plot 函数中,加入 label 关键字,在最后加入 plt.legend()
即可显示出图例:
plt.figure(figsize = (8, 4))
plt.plot(a, b, '-', color = 'red', label = 'training')
plt.plot(a, c, '-', color = 'blue', label = 'testing')
plt.xlabel("x")
plt.ylabel("y")
plt.title("title")
plt.legend()
plt.legend()
接受一个 loc 参数,值可以为:best , upper right , upper left , lower left , lower right , right , center left , center right , lower center , upper center , center
plt.figure(figsize = (8, 4))
plt.plot(a, b, '-', color = 'red', label = 'training')
plt.plot(a, c, '-', color = 'blue', label = 'testing')
plt.xlabel("x")
plt.ylabel("y")
plt.title("title")
plt.legend(loc = 'center')
plt.subplot()
函数生成子图
matplotlib 可以在一张图中画多个子图,plt.subplot
里面有三个数:
- 第一个数表示这个大图里面画多少行;
- 第二个数表示这个大图里面画多少列;
- 第三个数表示当前画的是第几个子图;
比如下面我们要画两个子图,在同一行,所以就是1行,2列,subplot前两个数字就是12。
plt.figure(figsize = (10, 4))
plt.subplot(121)
plt.plot(a, b, '-', color = 'red', label = 'training')
plt.xlabel("x")
plt.ylabel("y")
plt.legend(loc = 'right')
plt.subplot(122)
plt.plot(a, c, '-', color = 'blue', label = 'testing')
plt.xlabel("x")
plt.ylabel("y")
plt.legend(loc = 'right')
plt.suptitle("title")
Part 6 Scikit-learn for Machine Learning
遇事不决查文档
scikit-learn 是python中最有名的机器学习库,构建在numpy, scipy, matplotlib之上,使用起来很简单,速度还很快,而且代码开源,可以在github上学习它的源码,学习各种机器学习算法的实现过程。
sklearn中有几个部分
- clustering 包含了常用的聚类算法
- datasets 包含了常见的开源数据集
- ensemble 包含了常用的集成学习算法
- linear_model 包含了常用的线性模型
- metrics 包含了常用的指标
- naive_bayes 包含了朴素贝叶斯相关的算法
- neural_network 包含了神经网络相关的模型
- svm 包含了支持向量机相关的算法
- tree 包含了树模型相关的算法
我们的课程主要会涉及以上几个部分的使用
例:使用 sklearn 完成分类任务
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from matplotlib.colors import ListedColormap
import warnings
warnings.filterwarnings('ignore')
在这里引入我们需要使用的模型
# 引入数据集
from sklearn.datasets import make_moons
# 引入数据预处理工具
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 引入模型
from sklearn.neural_network import MLPClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# 引入评价指标
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
生成数据集
我们生成一个只有两个特征的数据集,所以可以在二维的平面上绘制出来
这个make_moons函数是sklearn.datasets中的函数,可以生成两个半月形的数据点
X, y = make_moons(n_samples = 100, noise = 0.3, random_state=0)
X是数据,包含100个样本,两个特征,y是标记,两类
y
X.shape
对数据进行可视化,横轴是第一个特征,纵轴是第二个特征,颜色表示类别
plt.figure(figsize = (8, 8))
cm_bright = ListedColormap(['#FF0000', '#0000FF'])
plt.scatter(X[:, 0], X[:, 1], c = y, cmap = cm_bright, edgecolors = 'k')
数据预处理
对数据进行标准化处理以及训练集和测试集的划分,随机选取了60%的样本作为训练,40%作为测试,所以我们会使用60个点进行训练,40个点进行测试
X = StandardScaler().fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.4, random_state=42)
X_train表示用来训练的特征数据,y_train是其对应的标记
X_test表示用来测试的特征数据,y_test是其对应的标记
接下来对数据集进行可视化,不透明的样本为训练样本,半透明的为测试样本
cm = plt.cm.RdBu
cm_bright = ListedColormap(['#FF0000', '#0000FF'])
plt.figure(figsize = (8, 8))
plt.title("Input data")
# Plot the training points
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright, edgecolors='k')
# and testing points
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright, alpha=0.3, edgecolors='k')
对数几率回归
对数几率回归又称逻辑回归,英文是Logistic regression,简称lr,是一个分类算法,常用于二分类任务,也可以处理多分类任务。在scikit-learn中,对数几率回归模型是LogisticRegression这个类,我们创建一个实例后,使用fit方法训练,需要的参数就是训练集的数据和标记。
lr = LogisticRegression()
lr.fit(X_train, y_train)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='warn',
n_jobs=None, penalty='l2', random_state=None, solver='warn',
tol=0.0001, verbose=0, warm_start=False)
这样模型就训练完成了,接下来是预测
prediction = lr.predict(X_test) # 预测
可以看到,prediction就是模型对那40个测试样本的预测结果,而y_test就是他们的真值
y_test
接下来是用各项评价指标来评价模型的预测能力,使用模型的预测值和真值就可以计算出模型的各项指标
# 测试集精度
acc = accuracy_score(y_test, prediction)
# 测试集查准率
precision = precision_score(y_test, prediction)
# 测试集查全率
recall = recall_score(y_test, prediction)
# 测试集f1值
f1 = f1_score(y_test, prediction)
print('对数几率回归在该测试集上的四项指标:')
print('精度:', acc)
print('查准率:', precision)
print('查全率:', recall)
print('f1值:', f1)
对数几率回归在该测试集上的四项指标:
精度: 0.875
查准率: 0.9
查全率: 0.8571428571428571
f1值: 0.8780487804878048
然后我们可以使用同样的方法,使用其他的模型来实验
计算混淆矩阵
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, prediction)
线性判别分析
请你尝试使用其他的模型,如线性判别分析 Linear Discriminant Analysis,完成模型的训练以及四项指标的计算
model = LinearDiscriminantAnalysis()
# 模型的训练
model.fit(X_train, y_train)
# 模型的预测
prediction = model.predict(X_test)
# 计算四项指标
confusion_matrix(y_test, prediction)
可视化
接下来,我们做一些模型可视化的处理,直观的对比各个模型
h = 0.02
x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
编写绘制模型预测结果的函数,这个函数会在图的右下角给出当前模型的预测精度,还会画出模型的决策边界
def plot_model(model, title):
model.fit(X_train, y_train)
score_train = model.score(X_train, y_train)
score_test = model.score(X_test, y_test)
if hasattr(model, "decision_function"):
Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()])
else:
Z = model.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
Z = Z.reshape(xx.shape)
plt.figure(figsize = (14, 6))
# 绘制训练集的子图
plt.subplot(121)
# 绘制决策边界
plt.contourf(xx, yy, Z, cmap = cm, alpha=.8)
# 绘制训练集的样本
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright, edgecolors='k')
# 设置图的上下左右界
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
# 设置子图标题
plt.title("training set")
# 图的右下角写出在当前数据集中的精度
plt.text(xx.max() - .3, yy.min() + .3, ('%.3f' % score_train).lstrip('0'), size=15, horizontalalignment='right')
plt.subplot(122)
plt.contourf(xx, yy, Z, cmap = cm, alpha=.8)
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright, edgecolors='k', alpha=0.6)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("testing set")
plt.text(xx.max() - .3, yy.min() + .3, ('%.3f' % score_test).lstrip('0'), size=15, horizontalalignment='right')
plt.suptitle(title)
对数几率回归
可以看到在右侧的测试集中,蓝色有三个点被模型判定为红色,红色有两个点被模型判定为蓝色,共5个样本被错误判断,所以精度是35 / 40,也就是87.5%
plot_model(LogisticRegression(), 'Logistic Regression')
线性判别分析
plot_model(LinearDiscriminantAnalysis(), 'Linear Discriminant Analysis')
决策树
plot_model(DecisionTreeClassifier(max_depth = 5, random_state = 42), 'Decision Tree')
多层感知机
plot_model(MLPClassifier(hidden_layer_sizes=(100, 100, 100), alpha=1), 'Multi-Layer Perception')
SVM
plot_model(SVC(kernel = "linear", C = 0.025, probability = True), 'Linear SVM')