利用Python进行数据分析 | NumPy基础(一)—— NumPy ndarray(多维数组对象)

NumPy基础:数组与向量化计算

NumPy,是Numerical Python的简称,它是利用Python进行数值运算的最为重要的基础包。

重要的原因可以归纳为三点:

  1. 可以有效的处理含有大量数组的数据。
  2. NumPy的算法是基于C语言编写的,NumPy数组使用的内存量小于其他的Python内建序列。
  3. NumPy可以针对全量数组进行复杂计算而不需要使用Python循环。

下面的例子将展示NumPy的不同,定义一个NumPy数组,里面存放着1M个整数,再定义一个列表,同样存放着1M个整数,将这两个序列同时乘以2,比较它们运算时间的差异。

In [8]: import numpy as np
In [9]: my_arr = np.arange(1000000)
In [10]: my_list = list(np.arange(1000000))
In [12]: %timeit my_arr2=my_arr*2
2.21 ms ± 19.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [13]: %timeit my_list2=[x*2 for x in my_list]
200 ms ± 1.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

NumPy ndarray:多维数组对象

ndarray是Python中一个快速、灵活的多维同类数据容器,也就是说,它包含的每一个元素都是相同的数据类型。每一个数组都有一个shape属性,用来表征数组每一个维度的数量;每一个数组都有一个dtype属性,用来描述数组的数据类型。

In [14]: data = np.random.randn(2,3)
In [15]: data
Out[15]: 
array([[ 0.1049642 , -0.52542354, -0.07641538],
       [ 0.5484492 , -0.95387232,  0.84849499]])
In [18]: data.shape
Out[18]: (2, 3)
In [19]: data.dtype
Out[19]: dtype('float64')

建议使用标准的NumPy导入方式import numpy as np,numpy这个命名空间中包含了大量与Python内建函数重名的函数(比如min,max)。

In [9]: np.arange(10)
Out[9]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
#arange是python内建函数range的数组版

不论是“数组”,“NumPy数组”还是“ndarray”,它们都表示同一个对象,ndarray对象。

1、生成ndarray

1.1 使用array函数生成数组

In [21]: data1=[6.,3.4,9.,0.,1.]
In [22]: arr1=np.array(data1)
In [23]: arr1
Out[23]: array([6. , 3.4, 9. , 0. , 1. ])
In [24]: data2=[1,2,3],[4,5,6]
In [25]: arr2=np.array(data2)
In [26]: arr2
Out[26]: 
array([[1, 2, 3],
       [4, 5, 6]])

1.2 使用其他函数创建新数组

In [5]: np.zeros(10)
Out[5]: array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
In [6]: np.zeros((3,6))
Out[6]: 
array([[0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0.]])
In [7]: np.empty((2,3,2))
Out[7]: 
array([[[6.23042070e-307, 1.89146896e-307],
        [1.37961302e-306, 6.23053614e-307],
        [6.23053954e-307, 9.34609790e-307]],

       [[8.45593934e-307, 9.34600963e-307],
        [2.13621758e-306, 6.89804132e-307],
        [1.11261162e-306, 8.34443015e-308]]])

表1展示的是标准数组的生成函数。由于NumPy专注于数值计算,没有特别指明的话,默认的数值类型为浮点型(float64)。

表1:数组生成函数

函数名 描述
array 将输入数据(列表、元组、数组、序列或其他)转换成ndarray,如不显式指明数据类型,将自动推断;默认复制所有的输入数据
asarray 将输入转换为ndarray,但如果输入已经是nadarray则不再复制
arange Python内建函数arrange的数组版,返回一个数组
ones 根据给定形状和数据类型生成一个全1数组
ones_like 根据所给的数组生成一个形状一样的全1数组
zeros 根据给定形状和数据类型生成一个全0数组
zeros_like 根据所给的数组生成一个形状一样的全0数组
empty 根据给定形状和数据类型生成一个没有初始化数值的空数组
empty_like 根据所给的数组生成一个形状一样的没有初始化数值的空数组
full 根据给定形状和数据类型生成指定数值的数组
full_like 根据所给的数组生成一个形状一样的内容为指定数值的数组
eye,identity 生成一个N*N特征矩阵(对角线位置为1,其余位置为0)

2、ndarray的数据类型

数据类型 ,即dtype,表示数组里存储的数据类型,对于初学者,仅需要知道数据大类有哪些:浮点型、整数、布尔值、字符串以及某个python对象。

In [19]: arr1=np.array([1,2,3])
In [20]: arr1=np.array([1,2,3],dtype=np.float64)
In [21]: arr1.dtype
Out[21]: dtype('float64')

表2展示的是NumPy所支持的所有数据类型。

表2:NumPy数据类型

类型 类型代码 描述
int8,uint8 i1,u1 有符号和无符号的8数位整数
int16,uint16 i2,u2 有符号和无符号的16数位整数
int32,uint32 i3,u3 有符号和无符号的32数位整数
int64,uint64 i4,u4 有符号和无符号的64数位整数
float16 f2 半精度浮点数
float32 f4 标准单精度浮点数,兼容C语言float
float64 f8 标准双精度浮点数,兼容C语言double和Python float
float128 f16 拓展精度浮点数
complex64,complex128,complex256 c8,c16,c32 分别基于32位、64位、128位浮点数的复数
bool ? 布尔值 True or False
object O Python object类型
string_ S 修正的ASCⅡ字符串类型;例如生成一个长度为10的字符串类型,使用’S10’
unicode_ U 修正的Unicode类型,生成一个长度为10的Unicode类型,使用’U10’

8数位代表的是二进制下的8位,即2的8次方,256,平均分到正负里,就是-128~127。

In [26]: arr=np.array(['Beijing','Wulumuqi','Laiwu'],dtype='S5')
In [27]: arr
Out[27]: array([b'Beiji', b'Wulum', b'Laiwu'], dtype='|S5')

转换数组的数据类型

  1. 使用astype方法显式地转换数组的数据类型
In [28]: arr=np.array([1.1,1.2,1.3,1.4,1.5,-1.6,-1.7,-2.8,-1.9])
In [29]: arr
Out[29]: array([ 1.1,  1.2,  1.3,  1.4,  1.5, -1.6, -1.7, -2.8, -1.9])
In [30]: arr.astype(np.int32)
Out[30]: array([ 1,  1,  1,  1,  1, -1, -1, -2, -1])

如果有一个数组,里面的元素都是表达数字含义的字符串,也可以通过astype将字符串转换为数字:

In [31]: numeric_strings=np.array(['1.25','40','-9.6'],dtype=np.string_)
In [32]: numeric_strings
Out[32]: array([b'1.25', b'40', b'-9.6'], dtype='|S4')
In [33]: numeric_strings.astype(float)
Out[33]: array([ 1.25, 40.  , -9.6 ])
  1. 使用另一个数组的dtype属性
In [38]: int_array=np.arange(10)
In [39]: float_array=np.array([.44,.380,.237,-.12],dtype=np.float64)
In [40]: int_array.astype(float_array.dtype)
Out[40]: array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
  1. 使用类型代码来传入数据类型
In [41]: empty_uint32=np.empty(8,dtype='u4')
In [42]: empty_uint32
Out[42]: 
array([3264175145, 1071393013, 2233382994, 1071141355,  412316860,
       1070487044, 3951369912, 1069463633], dtype=uint32)

使用astype时总是生成一个新的数组,及时传入的dtype与之前一样。

3、NumPy数组运算

运用数组,我们可以不用for循环实现批量操作,这称为数组的向量化 特性。两个相同尺寸的数组之间的运算都应用了逐元素操作的方式。

In [43]: arr=np.array([[1,2,3],[4,5,6]])

In [44]: arr
Out[44]: 
array([[1, 2, 3],
       [4, 5, 6]])

In [45]: arr*arr
Out[45]: 
array([[ 1,  4,  9],
       [16, 25, 36]])

In [46]: arr-arr
Out[46]: 
array([[0, 0, 0],
       [0, 0, 0]])

In [47]: 1/arr
Out[47]: 
array([[1.        , 0.5       , 0.33333333],
       [0.25      , 0.2       , 0.16666667]])

In [48]: arr**0.5
Out[48]: 
array([[1.        , 1.41421356, 1.73205081],
       [2.        , 2.23606798, 2.44948974]])

相同尺寸的数组间比较,会返回一个布尔值数组:

In [49]: arr2=np.array([[1,3,5],[2,4,6]])

In [50]: arr2>arr
Out[50]: 
array([[False,  True,  True],
       [False, False, False]])

4、基础索引与切片

4.1 一维数组

一维数组比较简单,类似于Python的列表。

In [13]: arr=np.arange(10)

In [14]: arr
Out[14]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [15]: arr[5]
Out[15]: 5

In [16]: arr[5:8]
Out[16]: array([5, 6, 7])

In [17]: arr[5:8]=6

In [18]: arr
Out[18]: array([0, 1, 2, 3, 4, 6, 6, 6, 8, 9])

数组的切片是原数组的视图 ,任何对于视图的修改都会反应到原数组上。

如果仅想得到数组切片的拷贝而不是一份视图的话,需用利用copy方法复制这个数组,例:arr[5:8].copy()

4.2 二维数组

在二维数组中,每一个索引值对应的元素不再是一个值,而是一个一维数组。

In [20]: arr2d=np.array([[1,2,3],[4,5,6],[7,8,9]])

In [21]: arr2d[1]
Out[21]: array([4, 5, 6])

In [22]: arr2d[1,1]
Out[22]: 5
4.3 多维数组

在多维数组中,省略后续索引值,返回的对象是降低一个维度的数组。

In [23]: arr3d=np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])

In [24]: arr3d
Out[24]: 
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [10, 11, 12]]])

In [25]: arr3d[0] #返回2*3的二维数组
Out[25]: 
array([[1, 2, 3],
       [4, 5, 6]])

In [26]: arr3d[0,1] #返回的是一个一维数组
Out[26]: array([4, 5, 6])

以上的数组子集,都是原数组的视图,修改其值会改变原数组的值。

In [27]: arr3d[0,1]=3

In [28]: arr3d
Out[28]: 
array([[[ 1,  2,  3],
        [ 3,  3,  3]],

       [[ 7,  8,  9],
        [10, 11, 12]]])
4.4 数组的切片索引

与Python的列表索引语法相似,对于二维数组来说,第一个值切的是行,第二个值切的是列。

利用Python进行数据分析 | NumPy基础(一)—— NumPy ndarray(多维数组对象)_第1张图片

图1 二维数组的切片

5、布尔索引

我们建立两个数组,一个为保存着字符串类型的数组,一个为保存着浮点数的数组。

In [29]: names=np.array(['Zhang Jike','Xu Xin','Ma Long','Zhang Jike','Ma Long','Xu Xin','Xu Xin'])
In [31]: data=np.random.randn(7,4)
In [33]: names
Out[33]: 
array(['Zhang Jike', 'Xu Xin', 'Ma Long', 'Zhang Jike', 'Ma Long',
       'Xu Xin', 'Xu Xin'], dtype=')

In [34]: data
Out[34]: 
array([[-0.42355046, -0.15376933, -0.42192499, -0.72783259],
       [-0.55979452,  1.73036132, -0.76297639, -1.12233218],
       [ 0.83649262, -0.47719457, -0.61221679, -0.34995665],
       [-0.39913564, -1.66393256,  0.58009781, -0.63479108],
       [-1.5643174 , -2.41139907, -1.45880713,  0.86300125],
       [ 0.25906839,  0.5642445 ,  2.21097353, -0.82552486],
       [-0.41486512, -2.44196251,  0.65707701,  0.10321156]])

假设每个人名都和data数组中的一行相对应,我们想要选择所有’Zhang Jike’对应的行,可以在对data数组调用索引时,将索引内容设置为names数组的一个比较操作(该操作会生成一个布尔值数组)。

In [35]: names == 'Zhang Jike'
Out[35]: array([ True, False, False, True, False, False, False])

In [36]: data[names == 'Zhang Jike']
Out[36]: 
array([[-0.42355046, -0.15376933, -0.42192499, -0.72783259],
       [-0.39913564, -1.66393256,  0.58009781, -0.63479108]])

布尔值数组的长度必须与数组轴长度一致,如果长度不一,不会报错,在使用布尔值索引时一定要对照好两者的数量关系。

取反:

In [37]: names != 'Zhang Jike'
Out[37]: array([False,  True,  True, False,  True,  True,  True])
In [39]: data[names != 'Zhang Jike']
Out[39]: 
array([[-0.55979452,  1.73036132, -0.76297639, -1.12233218],
       [ 0.83649262, -0.47719457, -0.61221679, -0.34995665],
       [-1.5643174 , -2.41139907, -1.45880713,  0.86300125],
       [ 0.25906839,  0.5642445 ,  2.21097353, -0.82552486],
       [-0.41486512, -2.44196251,  0.65707701,  0.10321156]])

In [40]: data[~(names == 'Zhang Jike')]
Out[40]: 
array([[-0.55979452,  1.73036132, -0.76297639, -1.12233218],
       [ 0.83649262, -0.47719457, -0.61221679, -0.34995665],
       [-1.5643174 , -2.41139907, -1.45880713,  0.86300125],
       [ 0.25906839,  0.5642445 ,  2.21097353, -0.82552486],
       [-0.41486512, -2.44196251,  0.65707701,  0.10321156]])

选择两个名字作为布尔值索引,需要使用布尔算术运算符,&和|:

In [41]: data[(names=='Zhang Jike')|(names=='Xu Xin')]
Out[41]: 
array([[-0.42355046, -0.15376933, -0.42192499, -0.72783259],
       [-0.55979452,  1.73036132, -0.76297639, -1.12233218],
       [-0.39913564, -1.66393256,  0.58009781, -0.63479108],
       [ 0.25906839,  0.5642445 ,  2.21097353, -0.82552486],
       [-0.41486512, -2.44196251,  0.65707701,  0.10321156]])

In [42]: data[(names=='Zhang Jike')&(names=='Ma Long')]
Out[42]: array([], shape=(0, 4), dtype=float64)

基于常识来设置布尔值数组的值:

In [43]: data[data<0]=0

In [44]: data
Out[44]: 
array([[0.        , 0.        , 0.        , 0.        ],
       [0.        , 1.73036132, 0.        , 0.        ],
       [0.83649262, 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.58009781, 0.        ],
       [0.        , 0.        , 0.        , 0.86300125],
       [0.25906839, 0.5642445 , 2.21097353, 0.        ],
       [0.        , 0.        , 0.65707701, 0.10321156]])

6、神奇索引

神奇索引 是NumPy中的术语,用来描述使用整数数组进行数据索引。

我们创建一个8*4的数组:

In [4]: arr=np.empty((8,4))

In [5]: for i in range(8):
   ...:     arr[i]=i
   ...:     

In [6]: arr
Out[6]: 
array([[0., 0., 0., 0.],
       [1., 1., 1., 1.],
       [2., 2., 2., 2.],
       [3., 3., 3., 3.],
       [4., 4., 4., 4.],
       [5., 5., 5., 5.],
       [6., 6., 6., 6.],
       [7., 7., 7., 7.]])

为了选出一个符合特定顺序的子集,可以通过传递一个包含指明所需顺序的列表或数组来完成:

In [7]: arr[[4,3,0,6]]
Out[7]: 
array([[4., 4., 4., 4.],
       [3., 3., 3., 3.],
       [0., 0., 0., 0.],
       [6., 6., 6., 6.]])

如果使用负的索引,将从尾部开始选择:

In [8]: arr[[-1,-3,-1]]
Out[8]: 
array([[7., 7., 7., 7.],
       [5., 5., 5., 5.],
       [7., 7., 7., 7.]])

传递多个索引数组的情况有所不同,它会根据每个索引元组对应的元素选出一个一维数组:

In [9]: arr=np.arange(32).reshape((8,4))

In [10]: arr
Out[10]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23],
       [24, 25, 26, 27],
       [28, 29, 30, 31]])

In [11]: arr[[2,5],[1,0]]
Out[11]: array([ 9, 20])

In [12]: arr=np.arange(32).reshape(2,4,4)

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

       [[16, 17, 18, 19],
        [20, 21, 22, 23],
        [24, 25, 26, 27],
        [28, 29, 30, 31]]])

In [14]: arr[[0,1],[0,2],[3,1]]
Out[14]: array([ 3, 25])

在上述例子中,第一个神奇索引选择二维数组,元素(2,1)、(5,0)被选中,第二个神奇索引选择三维数组,元素(0,0,3)、(1,2,1)被选中。不论神奇索引选择的维度是几维,返回的结果总是一维的。

神奇索引与索引不同,它总是将数据复制到一个新的数组中。

你可能感兴趣的:(Python,数据分析,python,数据分析,矩阵)