Python数据分析复习(二)——Numpy数组计算基础

今天复习Numpy数组对象ndarray的创建、随机数的生成和数组的访问与变换,Numpy矩阵的创建和ufunc函数,利用Python读\写文件并使用函数进行统计分析。

Numpy数组对象ndarray

Python提供了一个array模块。array和list不同,array直接保存数值,和C语言的一维数组比较类似,但是由于Python的array模块不支持多维,也没有各种运算函数,因此不适合做数值运算。Numpy弥补了Python不支持多维等不足之处,它提供了一种存储单一数据类型的多维数组----ndarray。下面实现多维数组的创建、生成随机数、通过索引访问一维或多维数组并变换其形态。

一、创建数组对象

Numpy提供了两种基本的对象:ndarray(N-dimensional Array)和ufunc(Universal Function)。ndarray是存储单一数据类型的多维数组,而ufunc则是能够对数组进行处理的函数。在Numpy中,维度称为轴。

1.1 数组属性

属性名称属性说明
ndim返回int。表示数组的维度
shape返回tuple。表示数组形状,对于n行m列的矩阵,形状为(n,m)
size返回int。表示数组的元素总数,等于数组形状中各元素的积
dtype返回data-type。表示数组中元素的数据类型
itemsize返回int。表示数组中每个元素的存储空间(以B为单位)。eg:一个元素类型为float64的数组的itemsize属性值为8(float64占用64bit,1B为8bit,所以float64占用8B)

1.2 数组创建

Numpy提供的array函数可以创建一维或多维数组,其基本使用格式如下.

numpy.array(object, dtype = None, *, copy = True, order = 'K', subok = False, ndmin = 0, like = None)

array函数的主要参数如表所示

参数名称参数说明
object接收array_like。表示所需创建的数组对象。无默认值
dtype接收data-type。表示数组所需的数据类型,如果未给定,那么选择保存对象所需的最小数据类型。默认为None。
ndmin接收int。用于指定生成数组应该具有的最小维数。

创建一维数组与多维数组并查看数组属性的过程

import numpy as np
arr1 = np.array([1, 2, 3, 4])  # 创建一维数组
print('创建的数组为:', arr1)

运行结果:
创建的数组为:[1 2 3 4]

arr2 = np.array([[1,2,3,4],[4,5,6,7],[7,8,9,10]])  # 创建二维数组
print('创建的数组为:\n', arr2)

运行结果:
创建的数组为:
[[1 2 3 4]
[4 5 6 7]
[7 8 9 10]]

print('数组形状为:', arr2.shape)   # 返回元组类型
print('数组元素类型为:', arr2.dtype)
print('数组元素个数为:', arr2.size)
print('数组每个元素存储空间为:', arr2.itemsize)

运行结果:
数组形状为:(3, 4)
数组元素类型为:int32
数组元素个数为:12
数组每个元素存储空间为:4B

如代码所示,数组arr1只有一行元素,因此它是一维数组。而数组arr2有3行4列元素,因此它是二维数组,第0轴的长度为3(即行数),第1轴的长度为4(即列数)。其中,第0轴也称横轴,第1轴也称纵轴。

还可以通过修改数组的shape属性,在保持数组元素个数不变的情况下改变数组每个轴的长度。

arr2.shape = 4, 3    # 重新设置shape
print('重新设置后的arr2为:\n', arr2)

运行结果:
重新设置后的arr2为:
[[1 2 3]
[4 4 5]
[6 7 7]
[8 9 10]]

上述代码中是先创建一个Python序列,然后通过array函数将其转换为数组,通过此方法创建数组显然效率不高。因此Numpy提供了很多专门用于创建数组的函数。

除了使用array函数创建数组之外,还可以使用arange函数创建数组。arange函数类似与Python自带的range函数,通过指定开始值、终值和步长来创建一维数组,创建的数组不含终值,arange函数的基本使用格式如下

numpy.arange([start, ]stop, [step, ]dtype = None, *, like = None)

常用参数及其说明如表所示

参数名称说明
start接收int或实数。表示数组的起始值,生成的数组包括这个值。默认为0
stop接收int或实数。表示数组的终值,生成的数组取不到这个值。无默认值
step接收int或实数。表示在数组中,值之间的步长。默认为1
dtype接收数据类型。表示输出数组的类型。默认为None
print('使用arange函数创建的数组为:\n', np.arange(0, 1, 0.1))   # 1取不到

使用arange函数创建的数组为:
[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]

linspace函数通过指定起始值、终值和元素个数来创建一维数组,默认包括终值,这一点需要和arange函数区分。linspace函数基本格式如下

numpy.linspace(start, stop, num = 50, endpoint = True, retstep = False, dtype = None, axis = 0)

linspace函数常用参数及其说明如表所示

参数名称说明
start接收array_like。表示起始值。无默认值
stop接收array_like。表示终值。无默认值
num接收int。表示生成的样本数。默认为50
dtype接收数据类型。表示输出数组的类型。默认为None
print('使用linspace创建的数组为:\n', np.linspace(0, 1, 12))    # 0~1之间取12个数组成等差数列

使用linspace创建的数组为:
[0.       0.09090909  ...     1.]

注意:生成的是一个等差数列,从01之间取12个数生成等差数列,终值可取到

logspace函数和linspace函数类似,但它创建的数组是等比数列。

numpy.logspace(start, stop, num = 50, endpoint = True, base = 10.0, dtype = None, axis = 0)

除了base参数和linspace函数的retstep参赛不同外,其余均相同。base参数用于设置日志空间的底数,在不设置的情况下,默认以10为底。

# 生成1~100的20个元素的等比数列
print(np.logspace(0, 2, 20))    # 10的0次方~10的平方之间取10个数组成等比数列,终值可取到
								# 0是指10的0次方,即1
                                # 2是指10的2次方,即100

[1.  1.27427499   1.62377674  ...   61.58482111   78.47599704   100.]

此外,Numpy还提供了其他函数,用于创建特殊数组,如zeros、eye、diag和ones函数等。其中,zeros函数用于创建数组元素全部为0的数组,即将创建的数组的元素全部填充为0。

print('使用zeros创建的数组为:\n', np.zeros((2, 3)))    # 传入的参数是元组

使用zeros创建的数组为:
[[0. 0. 0.]
 [0. 0. 0.]]

eye函数用于生成主对角线上元素为1、其他元素为0的二维数组,类似单位矩阵。

print('使用eye创建的数组为:\n', np.eye(3))   # 传一个数字即可,生成n*n的矩阵

使用eye创建的数组为:
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

diag函数创建类似对角矩阵的数组,即除对角线上元素以为其余元素均为0,对角线上的元素可以是0也可以是其他值。

print('使用diag创建的数组为:\n', np.diag([1, 2, 3, 4]))

使用diag创建的数组为:
[[1 0 0 0]
 [0 2 0 0]
 [0 0 3 0]
 [0 0 0 4]]

ones函数用于创建元素全部为1的数组,即数组元素全部填充为1.

print('使用ones创建的数组为:\n', np.ones((5, 3))

使用ones函数创建的数组为:
[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]

1.3 数组数据类型

在实际的业务数据处理中,为了更准确地计算结果,需要使用不同精度的数据类型。Numpy极大程度扩充了原生Python的数据类型。同时需要强调一点,在Numpy中,数组的数据类型是同质的,即数组中所有元素的数据类型必须是一致的。将元素数据类型保持一致可以更容易确定数组所需要的存储空间。Numpy的基本数据类型及其取值范围如表所示。

类型描述
bool用1位存储的布尔值(True or False)
inti表示由所在平台决定其精度的整数(一般为int32或int64)
int8表示整数,范围为-128~127
int16表示整数,范围为-32768~32767
int32表示整数,范围为 [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31}-1] [231,2311]
int64表示整数,范围为 [ − 2 63 , 2 63 − 1 ] [-2^{63},2^{63}-1] [263,2631]
uint8表示无符号整数,范围为0~255
uint16表示无符号整数,范围为0~65535
uint32表示无符号整数,范围为 [ 0 , 2 32 − 1 ] [0,2^{32}-1] [0,2321]
uint64表示无符号整数,范围为 [ 0 , 2 64 − 1 ] [0,2^{64}-1] [0,2641]
float16表示半精度浮点数(16位),其中用1位表示正负,用5位表示整数,用10位表示尾数
float32表示半精度浮点数(32位),其中用1位表示正负,用8位表示整数,用23位表示尾数
float64或float表示半精度浮点数(64位),其中用1位表示正负,用11位表示整数,用52位表示尾数
complex64表示复数,分别用两个32位浮点数表示实部和虚部
complex128或comlex表示复数,分别用两个64位浮点数表示实部和虚部

Numpy数组中的每一种数据类型均有其对应的转换函数

print('转换结果为:', np.float64(42))   # 整数转换为浮点数
转换结果为:42.0

print('转换结果为:', np.int8(42.0))   # 浮点数转换为整数
转换结果为:42

print('转换结果为:', np.bool(42))   # 整数转换为布尔值
转换结果为:True

print('转换结果为:', np.bool(0))
转换结果为:False

print('转换结果为:', np.float(True))   # 布尔值转换为浮点数
转换结果为:1.0

举个例子:用一个能存储40个字符的字符串来记录商品名称,用一个64位的整数来记录商品的库存数量,最后用一个64位的单精度浮点数来记录商品价格

# 创建数据类型
df = np.dtype([('name', np.str_, 40), ('numitems', np.int64), ('price', np.float64)])
print('数据类型为:\n',df)

数据类型为:
 [('name', '<U40'), ('numitems', '<i8'), ('price', '<f8')]

# 查看数据类型,可以直接查看或使用Numpy中的dtype属性查看
print('数据类型为:', df['name'])    # 这不比第二种香?
print('数据类型为:', np.dtype(df['name']))

运行结果是一样的所以只写一个:
数据类型为: <U40

# 在使用array函数创建数组时,数组的数据类型默认是浮点型。若需要自定义数组数据,则可预先指定数据类型
itemz = np.array([('tomatoes', 42, 4.14), ('cabbages', 13, 1.72)], dtype = df)
print('自定义数组为:', itemz)

自定义数据为:[('tomatoes', 42, 4.14), ('cabbages', 13, 1.72)]

二、生成随机数

Numpy提供了强大的生成随机数的功能,与随机数相关的函数都在random模块中,其中包含可以生成服从多种概率分布的随机数的函数。

numpy.random.random(size = None)

参数size接收int,表示返回随机浮点数的个数,默认为None

print(np.random.random(100))   # 生成100个随机数,每次运行结果都不一样

[0.6896243  0.2323196  0.22278832 0.92236663 0.43133223 0.16263077
 0.85345457 0.07628275 0.24721532 0.9985516  0.50759887 0.93356987
 0.85831719 0.35203677 0.54748974 0.8563262  0.92920345 0.13307874
 0.0084903  0.59674073 0.35656659 0.84096082 0.92093303 0.36085382
 0.73792689 0.33725477 0.73979032 0.13298213 0.54201926 0.4530558
 0.03029311 0.42544943 0.35501842 0.15815889 0.36910851 0.41742486
 0.2735353  0.87970997 0.60230105 0.4242303  0.88782398 0.36971363
 0.14224836 0.06365411 0.00360293 0.68175517 0.08654055 0.10008383
 0.35891911 0.90100052 0.51775199 0.91730246 0.57818358 0.12137687
 0.15770363 0.87246132 0.33321079 0.42958281 0.33271032 0.24133488
 0.53617841 0.59893296 0.87855328 0.22785465 0.12909284 0.26137714
 0.55013794 0.11997438 0.82921027 0.19599665 0.42379569 0.12449305
 0.95190409 0.50147894 0.53958231 0.42860986 0.58302233 0.63538924
 0.30222439 0.20138509 0.49848487 0.93702275 0.70287848 0.62453805
 0.39597925 0.85373285 0.02133832 0.42614334 0.55244785 0.21015451
 0.80105946 0.1787559  0.15593144 0.23201845 0.63089127 0.21048073
 0.00234207 0.30071091 0.43878835 0.005603  ]

rand函数可以生成服从均匀分布的随机数

numpy.random.randn(d0, d1, ..., dn)

参数d0, d1, …, dn接收int,表示返回数组的维度,必须是非负数,如果没有给出参数则返回单个python浮点数,无默认值。

print(numpy.random.rand(10, 5))   # 10行5列

[[0.18588844 0.83933993 0.04776351 0.1952311  0.59151502]
 [0.14343186 0.97918896 0.25457496 0.19892084 0.37210694]
 [0.27658278 0.01679804 0.04560643 0.58078582 0.40855004]
 [0.62087428 0.11590236 0.21537928 0.7254128  0.49558476]
 [0.80465022 0.71827561 0.58297298 0.88546842 0.30070912]
 [0.94952849 0.8989907  0.09777495 0.5874939  0.18053479]
 [0.82647019 0.56415472 0.64553077 0.42089289 0.33999926]
 [0.46630577 0.45284306 0.80589055 0.50750732 0.2555654 ]
 [0.29491609 0.39686369 0.5701884  0.50179941 0.33795022]
 [0.70618156 0.22638261 0.91108087 0.12490352 0.27159384]]

使用rand函数生成服从均匀分布的函数

numpy.random.rand(d0, d1, ..., dn)

参数d0, d1, …, dn接收int,表示返回数组的维度,必须是非负数,如果没有给出参数,那么返回单个Python浮点数,无默认值。

print(np.random.rand(10, 5))

[[0.33168048 0.88337828 0.48430106 0.83651731 0.79624435]
 [0.03196052 0.68915598 0.59945125 0.2313675  0.05957749]
 [0.03181594 0.13942919 0.71497741 0.12606085 0.1606932 ]
 [0.91262123 0.86453401 0.69107096 0.3205785  0.04703187]
 [0.70346898 0.07068287 0.32303662 0.19087805 0.1164385 ]
 [0.5685479  0.16618067 0.97683572 0.83922708 0.12676093]
 [0.72269805 0.70104973 0.41848042 0.19278047 0.22503945]
 [0.13992564 0.89833183 0.55961421 0.24092709 0.94090884]
 [0.36580383 0.91101653 0.28600541 0.74550795 0.07656292]
 [0.25634462 0.26095304 0.15529004 0.91044739 0.49984415]]

randn函数可以生成服从正态分布的随机数

print(np.random.randn(10, 5))
结果跟rand类似,只是服从正态分布了,懒一下(doge)

randint函数可以生成给定范围的随机数

numpy.random.randint(low, high = None, size = None, dtype = int)
参数名称说明
low接收int或array_like的整数,表示数组最小值,无默认值
high接收int或array_like的整数,表示数组最大值,默认值为None
size接收int或整数元组,表示输出数组的形状,默认值为None
dtype接收数据类型,表示输出数组的类型,默认值为int
print(np.random.randint(2, 10, size = (10, 5))   # 返回值为最小值不低于2,最大值不高于10的2行5列数组,最大值10取不到

[[6 9 3 3 5]
 [5 2 5 4 5]]

其他常用函数如表所示

函数说明
seed确定随机数生成器的种子
permutation返回一个序列的随机排列或返回一个随机排列的范围
shuffle对一个序列进行随机排序
binomial产生服从二项分布的随机数
normal产生服从正态(高斯)分布的随机数
beta产生服从beta分布的随机数
chisquare产生服从卡方分布的随机数
gamma产生服从gamma分布的随机数

三、通过索引访问数组

Numpy通常以提供高效率的数组著称,这主要归功于索引的易用性。

3.1 一维数组的索引

一维数组的索引方法与list的索引方法一致

arr = np.arange(10)
print(arr[5])   # 5   用整数作为索引可以获取数组中的某个元素
print(arr[3:5])   # [3 4]   获取数组的一个切片,包括arr[3], 不包括arr[5]
print(arr[:5])   # [0 1 2 3 4]   同理
print(arr[-1])   # 9

arr[2:4] = 100, 101
print(arr)   # [0 1 100 101 4 5 6 7 8 9]   索引可用于修改元素的值
print(arr[1:-1:2])   # 从1开始,因为-1的9取不到,所以按照步长为2只能取到7结束
                     # [1 101 5 7]
print(arr[5:1:-2])   # 步长为负数时,起始索引必须大于结束索引,从5开始,取不到1,且是往回退,所以只能取到101结束
					 # [5 101]

3.2 多维数组的索引

多维数组的每一个轴都有一个索引,各个轴的索引之间用逗号隔开

arr = np.array([[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11]])
print(arr)
'''
[[ 1  2  3  4  5]
 [ 4  5  6  7  8]
 [ 7  8  9 10 11]]
 '''

print(arr[0, 3:])   # 第0行中第3、4列的元素   [4 5]

print(arr[1:, 2:])   # 第1、2行中第2~4的元素
'''
[[ 6  7  8]
 [ 9 10 11]]
'''

print(arr[:, 2])   # 所有行中第2列的元素,也就是整个数组中第二列的元素 [3 6 9]

多维数组也可以使用整数序列索引和布尔值索引进行访问

# 从两个序列的对应位置取出两个整数来组成索引:arr[0,1], arr[1,2], arr[2,3]
print(arr[[0, 1, 2], [1, 2, 3]])   # [2 6 10]
注意:这里会运行成功,但是会有一个FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result
这个警告是因为未来的版本中,将不再支持使用非元组序列进行多维数组索引。为了解决这个问题,我们需要修改索引的方式。需要将非元组序列转换为元组

print(arr[(0,1,2), (1,2,3)])   # [2 6 10] 与上面相比,是把一个非元组序列改成了元组,这样就不会收到警告信息。

总结:在使用NumPy或者Pandas进行多维数组索引时,如果收到了警告信息:“FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use ​​arr[tuple(seq)]​​ instead of ​​arr[seq]​​”,那么我们需要修改索引的方式。将非元组的序列转换为元组,并使用元组的方式进行多维数组的索引,即可解决这个问题。这样不仅可以避免警告信息的产生,还可以保证代码在未来的版本中的兼容性。

print(arr[1:, (0, 2, 3)])   # 索引第1、2行中第0、2、3列的元素
[[4 6 7]
 [7 9 10]]

mask = np.array([1, 0, 1], dtype = np.bool)   # 此时mask是一个布尔数组[True, False, True]
# 用它索引第0、2行中第2列的元素
print(arr[mask, 2])   # 此时mask作为行标,因为第1行是False所以不可取,只能取第0行和第2行,列标为2直接取第二列的元素
					  # [3 9]

四、变换数组的形状

在Numpy中,常用reshape函数改变数组的形状,同时,将改变数组的轴

numpy.reshape(a, newshape, order = 'C')
参数名称说明
a接收array_like,表示需要变换形状的数组,无默认值。
newshape接收int或int型元组,表示变化后的形状,无默认值

reshape函数在改变原始数据的形状的同时不改变原始数据的值,如果指定的形状和数组的元素数目不匹配,那么函数将抛出异常。

arr = np.arange(12).reshape(3, 4)   # 把原来的一维数组改成3行4列
print(arr)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

# 查看数组轴
print(arr.ndim)   	# 2个轴,即变为二维数组

# 使用ravel函数完成数组展平工作				
print(arr.ravel())   # [0 1 2 3 4 5 6 7 8 9 10 11]

# faltten函数也可以完成数组展平工作,与ravel区别在于flatten可以选择横向或纵向展平
print(arr.flatten())   # 横向展平
print(arr.flatten('F'))   # 纵向展平

除了可以改变数组形状外,还可以对数组进行组合,组合主要有横向组合与纵向组合,可使用hstack函数、vstack函数和concatenate函数完成数组的组合
横向组合是将由ndarray对象构成的元组作为参数,传给hstack函数
纵向组合同理,传给vstack函数

arr1 = np.arange(12).reshape(3, 4)
arr2 = arr1 * 3
print('横向组合后的数组为:\n', np.hstack((arr1, arr2)))   # 使用hstack横向组合数组,函数括号内传的是一个元组,切不可丢掉元组自己的括号
print('纵向组合后的数组为:\n', np.vstack((arr1, arr2)))   # 使用vstack纵向组合数组

横向组合后的数组为:
 [[ 0  1  2  3  0  3  6  9]
 [ 4  5  6  7 12 15 18 21]
 [ 8  9 10 11 24 27 30 33]]
纵向组合后的数组为:
 [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [ 0  3  6  9]
 [12 15 18 21]
 [24 27 30 33]]

concatenate函数也可以实现数组的横向和纵向组合,当参数axis=1时,横向组合;axis=0时,纵向组合

print(np.concatenate((arr1, arr2)), axis = 1)   # 横向组合
print(np.concatenate((arr1, arr2)), axis = 0)   # 纵向组合
# 运行结果跟hstack和vstack一样

除了对数组进行横向和纵向的组合之外,还可以对数组进行分割。Numpy提供了hsplit、vsplit、split函数,这些函数可以将数组分割成相同大小的子数组,可以指定原数组中需要分割的位置。

hsplit可以对数组进行横向分割,以由ndarray对象构成的元组作为参数;同理,vsplit实现纵向分割;split函数当参数axis=1时,横向分割,axis=0时,纵向分割。

arr = np.arange(16).reshape(4, 4)
print(np.hsplit(arr, 2))   # 横向分割

[array([[ 0,  1],
       [ 4,  5],
       [ 8,  9],
       [12, 13]]), array([[ 2,  3],
       [ 6,  7],
       [10, 11],
       [14, 15]])]
       
print(np.vsplit(arr, 2))   # 纵向分割

[array([[0, 1, 2, 3],
       [4, 5, 6, 7]]), array([[ 8,  9, 10, 11],
       [12, 13, 14, 15]])]

print(np.split(arr, 2, axis = 1))   # 横向分割
print(np.split(arr, 2, axis = 0))   # 纵向分割
# 运行结果同上
# 需要注意的是split()虽然和concatenate()类似,但传参并不需要像concatenate一样把axis参数放在元组外面,而是放在一起
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值