import numpy as np
# 下面三种方式等效
t1 = np.array([1,2,3,4,5])
t2 = np.array(range(5))
t3 = np.arange(5)
print(t1)
print(type(t1)) # 数组的类型
print(t1.dtype) # t1数组中,数据的类型
输出
[1 2 3 4 5]
<class 'numpy.ndarray'>
int32
数组的类型是ndarray,数组中数据的类型是int32。
arrange的用法和range一致,即参数是start,end,seg
但与range不同的是,arange还能生成小数
t4 = np.arange(10, 15, 0.5)
print(t4)
输出
[10. 10.5 11. 11.5 12. 12.5 13. 13.5 14. 14.5]
除了上面的类型,numpy还支持字符串类型,用 np.str_ 表示
可以在创建的时候,指定其类型
t5 = np.array(range(1,4),dtype="i1")
# 也可以写成t4 = np.array(range(1,4),dtype=int8)
若在创建数组的时候没有指定数据类型,那么默认情况下,根据电脑的位数来确定是32位还是64位,如果电脑是64位,那么就是int64,如果是32位,那么就是int32。
将数值型转化为bool型时,只要不是0,那都是True
##numpy中的bool类型
t6 = np.array([1,1,0,1,0,0],dtype=bool)
print(t6)
print(t6.dtype)
输出
[ True True False True False False]
bool
使用np.astype进行转换
t7 = t6.astype("int8")
print(t7)
print(t7.dtype)
输出
[1 1 0 1 0 0]
int8
# numpy中的小数
# 需要先导入random模块,import random
# random.random()表示生成一个0-1之间的随机数
t7 = np.array([random.random() for i in range(10)])
print(t7)
print(t7.dtype)
# 四舍五入
t8 = np.round(t7,2) # 保留两位小数
print(t8)
输出
[0.86556796 0.43664234 0.75419431 0.09691071 0.95194878 0.3131914
0.70976318 0.53698263 0.29735993 0.96115199]
float64
[0.87 0.44 0.75 0.1 0.95 0.31 0.71 0.54 0.3 0.96]
可以使用np.shape()来查看数组的形状(即几行几列)
t1是一位数组,其形状为(12, ),这是一个元组,但只有含有一个值。
t3是三维数组,的形状为(2, 3, 4),应该从后往前读,最后一维是4,说明最里面的中括号里有四个数,然后是3,说明次里面的中括号有三个对象,最前面是2,说明最大的中括号里面有两个对象。
对于多维数组,其元素地址的变化规律如下:下标从最后一个维度开始变化,然后倒数第二个,一直到最前面的。
也可以这样认为,2表示有2块,(3, 4)表示每块都有一个(3, 4)的矩阵。
需要注意的是,一维数组是(3, ),只有一对中括号,而不是(3, 1)和(1, 3),后两者是二维的,有两对中括号。
np.flatten将矩阵打平成数组
np.flatten是从里往外,依次打开中括号
在numpy中可以理解为方向,使用0,1,2…数字表示,对于一个一维数组,只有一个0轴,对于2维数组(shape(2,2)),有0轴和1轴,对于三维数组(shape(2,2, 3)),有0,1,2轴。
np.arange(0,10).reshape((2,5)),reshpe的参数中2表示0轴长度(包含数据的条数)为2,1轴长度为5,2X5一共10个数据。
在带有axis参数的二维数组上使用np.sum()等聚合函数时,它会将二维数组折叠为一维数组,其折叠的方向由axis参数确定。
如
t1 = np.arange(12,24).reshape((3,4))
print(t1)
print(50 * '-')
t2 = np.sum(t1, axis=0)
print(t2)
输出
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]
--------------------------------------------------
[48 51 54 57]
对于三维数组也是类似,假如t3是三维数组,其shape是(2,3,4),那么第0轴的长度是2,如果按照第0轴的方向折叠,折叠后的shape为(3,4),如下:
不同维度的矩阵或数据进行计算,即为广播。
相当于二维数组的每个元素都加了这个数字
In [6]: t2
Out[6]:
array([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]])
In [7]: t2 + 10
Out[7]:
array([[10, 11, 12, 13, 14, 15],
[16, 17, 18, 19, 20, 21]])
先看下面的实例:
In [11]: t7
Out[11]: array([0, 1, 2, 3, 4, 5])
In [12]: t8
Out[12]:
array([[10, 11, 12, 13, 14, 15],
[16, 17, 18, 19, 20, 21]])
In [13]: t9
Out[13]:
array([[10, 11],
[12, 13],
[14, 15],
[16, 17],
[18, 19],
[20, 21]])
In [14]: t7 + t8
Out[14]:
array([[10, 12, 14, 16, 18, 20],
[16, 18, 20, 22, 24, 26]])
t7能和t8相加
In [15]: t7 + t9
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-15-e3cd48b39d92> in <module>
----> 1 t7 + t9
ValueError: operands could not be broadcast together with shapes (6,) (6,2)
t7+t9就会报错
(6,1)的数组能和(6,2)的相加吗?
根据上面的规律,可以提炼出广播的规律:
注意什么是后缘维度,所谓的后缘维度就是从后面开始算起,从前面开始算起的(前缘维度)有若干维度相同的,不能进行广播,如
其他基本运算,包括地板除,与加法相同
二维或者更高维度的数组和一维数据进行运算,那么一维数组,其长度就是后缘维度
由于csv便于展示,读取和写入,所以很多地方也是用csv的格式存储和传输中小型的数据,为了方便教学,我们会经常操作csv格式的文件,但是操作数据库中的数据也是很容易的实现的
np.loadtxt(fname,dtype=np.float,delimiter=None,skiprows=0,usecols=None,unpack=False)
其中 unpack 若为True,则相当于将原来的数据进行了转置
练习:
现有英国和美国各自在youtube的1000多个视频的“点击,喜欢,不喜欢,评论”数量([“views”,“likes”,“dislikes”,“comment_total”])的csv文件,使用刚刚介绍的方法读取数据:
import numpy as np
us_file_path = "./youtube_video_data/US_video_data_numbers.csv"
uk_file_path = "./youtube_video_data/GB_video_data_numbers.csv"
# 读取第1列和第3列
t1 = np.loadtxt(us_file_path,delimiter=",",dtype="int",usecols=(1, 3))
# 不指定读取哪一列,默认读取全部,让每一列代表一个记录
t2 = np.loadtxt(us_file_path,delimiter=",",dtype="int",unpack=True)
print(t1)
print(50*'*')
print(t2)
输出
[[320053 46245]
[185853 0]
[576597 170708]
...
[ 4231 279]
[ 41032 4737]
[ 34727 4722]]
**************************************************
[[4394029 7860119 5845909 ... 142463 2162240 515000]
[ 320053 185853 576597 ... 4231 41032 34727]
[ 5931 26679 39774 ... 148 1384 195]
[ 46245 0 170708 ... 279 4737 4722]]
In [17]: t1
Out[17]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
In [18]: t1[2] # 取行
Out[18]: array([10, 11, 12, 13, 14])
In [19]: t1[-1] # 取最后一行
Out[19]: array([15, 16, 17, 18, 19])
取连续多行,像列表切片一样,startseg
In [21]: t1[1:3] # 取连续多行
Out[21]:
array([[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
取不连续的多行
In [23]: t1[[1,3]] # 取第1行和第3行
Out[23]:
array([[ 5, 6, 7, 8, 9],
[15, 16, 17, 18, 19]])
对列的操作和行类似,但是在取列的时候,行的位置处要有冒号(:),否则报错
取列的时候,返回值是一维数组,一维数组是以行的形式展示的。
In [27]: t1
Out[27]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
In [28]: t1[:,2] # 取列的时候,行的位置冒号(:)不能省略
Out[28]: array([ 2, 7, 12, 17])
In [29]: t1[:,::2]
Out[29]:
array([[ 0, 2, 4],
[ 5, 7, 9],
[10, 12, 14],
[15, 17, 19]])
取某个具体元素上的值,返回的将不再是数组,而是一个numpy.int或numpy.float的数据
In [31]: t1[2,3]
Out[31]: 13
In [32]: type(t1[2,3])
Out[32]: numpy.int32
取多个不相邻的元素,例如要取(0,0),(2,1),(2,3)这三个位置上的值
返回值将是包含上述三个位置值的一位数组
In [34]: t1
Out[34]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
In [35]: t1[[0,2,2],[0,1,3]] # 第一个方括号里确定行数,第二个方括号确定列数
Out[35]: array([ 0, 11, 13])
取多行多列交叉的值
In [36]: t1[2:5,1:4]
Out[36]:
array([[11, 12, 13],
[16, 17, 18]])
观察下面四个例子
a2[1] = 100不会对a1造成影响,因为a2 = a1[2:]传递的是若干个元素的地址
b2[1] = 100之所以会对b1造成影响,因为b2 = b1[1]是浅拷贝,只拷贝引用,不拷贝对象
c2[1] = 100会对c1造成影响,因为 c2 = c1[2:] 传递的是切片的引用,是把切片作为一个整体,而非“若干元素的”引用,这个和列表有明显的区别
d2[1] = 100也会对d1造成影响,因为d2 = d1[1]同样传递的是切片的引用
同前面的切片一样,再在右边加上一个赋值号,通过赋值号进行修改
对数组切片的复制,如果类型不一致,则强制转化为数组的数据类型
如果我们想把 t 中小于10的数字替换为10,把大于18的替换为18,应该怎么做?
nan(NAN,Nan):not a number表示不是一个数字
什么时候numpy中会出现nan:
当我们读取本地的文件为float的时候,如果有缺失,就会出现nan
当做了一个不合适的计算的时候(比如无穷大(inf)减去无穷大)
inf(-inf,inf):infinity,inf表示正无穷,-inf表示负无穷
什么时候回出现inf包括(-inf,+inf)
比如一个数字除以0,(python中直接会报错,numpy中是一个inf或者-inf)
那么如何指定一个nan或者inf呢?
注意他们的type类型
可以给某个元素直接赋numpy.nan或numpy.inf,但是必须先把原来的数据类型改成浮点型,否则会报错或者出现一些奇怪的数字。
两个np.nan是不相同的
np.isnan(t) 返回一个和t形状相同的数组,数据类型为bool型
np.count_nonzero(t) 返回一个数组t中0(或者False)的个数
那么问题来了,在一组数据中单纯的把nan替换为0,合适么?会带来什么样的影响?
比如,全部替换为0后,替换之前的平均值如果大于0,替换之后的均值肯定会变小,所以更一般的方式是把缺失的数值替换为均值(中值)或者是直接删除有缺失值的一行
求和:t.sum(axis=None)
均值:t.mean(a,axis=None) 受离群点的影响较大
中值:np.median(t,axis=None)
最大值:t.max(axis=None)
最小值:t.min(axis=None)
极值:np.ptp(t,axis=None) 即最大值和最小值只差
标准差:t.std(axis=None)
若未指定axis,默认返回多维数组的全部的统计结果,如果指定axis则返回一个当前轴上的结果
练习,将t1中的np.nan,替换成整个数组剩余元素的平均值。
其中t1为
t1 = np.arange(12).reshape(3,4).astype("float")
t1[2,:] = np.nan
相关代码
import numpy as np
t1 = np.arange(12).reshape(3,4).astype("float")
t1[2,:] = np.nan
def fill_ndarray(t1):
for i in range(t1.shape[1]):
temp_col = t1[:,i] # 当前的一列
nan_num = np.count_nonzero(temp_col!=temp_col)
# 因为temp_col != temp_col返回的是一个bool型的矩阵,
# 而np.nan!=np.nan的结果为True,np.count_nonzero统计的是矩阵中非零的个数
# 因此可以统计其中True的个数,此即为temp_col中np.nan的个数
if nan_num != 0:
temp_not_nan_col = temp_col[temp_col == temp_col]
# 如果temp_col中,某个位置的元素不是nan,那么这个位置上的结果则为True
temp_col[np.isnan(temp_col)] = temp_not_nan_col.mean()
# 在nan的位置上,赋上整个矩阵的均值
# 由于切片赋值是浅拷贝,因此temp_col发生变化,t1也会发生变化
return t1
if __name__ == '__main__': # 直接敲入main,然后回车就行
print(t1)
t1 = fill_ndarray(t1)
print(50*'-')
print(t1)
hstack也能用于一维数组的拼接
注意,上面两个方法中,参数都是元组。
定义两个三维数组
上面的三张图片可以看到,concatenate 可以实现在指定轴上的拼接,axis=0,则表示在0轴,即拼接的时候,只打开一个方括号,axis=1,则打开两个方括号,axis=2,则打开三个方括号,即 concatenate 不仅可以拼接高维数组,也能拼接低维数组。
竖直分割
可以看到,hsplit返回的是由分割结果得到的列表。
上面的hsplit第二个参数是 3,表示均匀分割成三份,每份的列数一样
如果不想均匀分怎么办?可以用一个元组代替,元组中的数字表示划线位置
水平分割 vsplit 的用法与 hsplit 一致。
同前面np.contenate类似,在第几个轴上分割,axis就设置为几
如果不想均分,也可以用元组的方法,指定划线位置
把最深的索引一致的元素拿出来构成新的数组
上图红色框中,a[0, 0, 0],a[0, 1, 0],a[1, 0, 0],a[1, 1, 0]这四个数的最后一个索引都是0,将它们取出,组成一个数组,维度数与前面保持一致,都保持三维。
b中的另外两个元素,与b[0]一致。
下面的操作适合二维数组
最简单的是t.T,常用的还有t.transpose()
In [40]: t
Out[40]:
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
In [41]: t.T
Out[41]:
array([[0, 5],
[1, 6],
[2, 7],
[3, 8],
[4, 9]])
In [42]: t.transpose()
Out[42]:
array([[0, 5],
[1, 6],
[2, 7],
[3, 8],
[4, 9]])
可以看到,np.transpose(a, (1, 2, 0))其实就是进行了下面的操作
b[k, i, j] = a[i, j, k]
np.linalg.inv 对矩阵求逆
A = np.array([[0,1,2],
[1,0,3],
[4,-3,8]])
print("A = ")
print(A)
print()
inverse = np.linalg.inv(A)
print("A的逆矩阵:")
print(inverse)
print()
输出
A =
[[ 0 1 2]
[ 1 0 3]
[ 4 -3 8]]
A的逆矩阵:
[[-4.5 7. -1.5]
[-2. 4. -1. ]
[ 1.5 -2. 0.5]]
np.linalog.solve
这里要注意b的维度,深度学习中,即便是向量,也是将其当成二维数组来,这样可以避免一些未知的错误。
np.random.
如果指定np.seed(1),那么程序每次运行的时候,生成的随机数都是相同的,如果未指定,则不同
以 np.random.rand 为例介绍 random 模块的使用
permutation(x)
x若为数组,那么就返回打乱后的结果,但 x 不变
x若为一个整数,那么就生成一个 0-x 序列,并打乱
random模块的其他常用函数,可以见这篇博客(其实帖子也没必要看,上面介绍的抽样和打乱的几个方法,已经完全够用了):
https://www.cnblogs.com/zuoshoushizi/p/8727773.html
np.argmax(t,axis=0) (对0轴数据进行折叠)
np.argmin(t,axis=1) (对1轴数据进行折叠)
创建一个全0的数组: np.zeros((3,4))
创建一个全1的数组:np.ones((3,4))
创建一个对角线为1的正方形数组(方阵):np.eye(3)
np.all 如果全为True,则返回True,否则返回 False
np.any 只要存在一个True,就返回True,如果一个都没有,那就返回False
现有英国用户在youtube的1000多个视频的“点击,喜欢,不喜欢,评论”数量([“views”,“likes”,“dislikes”,“comment_total”])的csv文件,绘制评论数和喜欢数的散点图
import numpy as np
from matplotlib import pyplot as plt
uk_file_path = "./youtube_video_data/GB_video_data_numbers.csv"
t_uk = np.loadtxt(uk_file_path,delimiter=",",dtype="int")
t_uk = t_uk[t_uk[:,-1]<50000] # 通过bool索引来筛选,将评论大于5万的去掉
# 之所以要筛选5万以下的,是因为评论数超过5万视频,太少了
# print(t_uk)
plt.figure(figsize=(10,6), dpi=80)
plt.scatter(t_uk[:,1],t_uk[:,-1])
# 第一列是喜欢数,最后一列是评论数
plt.show()