# 啃书计划《利用 Python 进行数据分析》

第4章

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

Numpy,是Numerical Python 的简称,它是目前Python数值计算中最为重要的基础包

虽然 numpy 提供了数值数据操作的计算基础,但大多数读者还是想把 pandas 作为统计分析的基石,尤其是针对表格数据。
pandas提供了更多的针对特定场景的函数功能,例如时间序列操作等numpy等并不包含的功能。

numpy之所以如此重要,其中一个原因就是它的设计对于含有大量数组的数据非常有效。

Numpy ndarray:多维数组对象

Numpy的核心特征之一就是N-维数组对象——ndarray.

生成ndarray

有两种方法可以生成 ndarray :

a = [6,7.5,4,8,9]
s = np.array(a)
s = array([6. , 7.5, 4. , 8. , 9. ])

a = np.array([6,7.5,4,8,9])
a = array([6. , 7.5, 4. , 8. , 9. ])

可以通过 zeros 一次性创造一个全0数组,ones 可以一次性创造一个全1数组,empty 可以创建一个没有初始化数值的数组

np.empty((2,3,2))
# array([[[1.38888748e-311, 3.16202013e-322],
        [0.00000000e+000, 0.00000000e+000],
        [8.45593933e-307, 1.40438095e+165]],

       [[9.23500886e-071, 6.27832385e+174],
        [3.31411587e-033, 7.49695553e-067],
        [6.95634324e-042, 1.04890625e-042]]])

使用np.empty 来生成一个全0数组,并不安全,有时候它会返回未初始化的垃圾数值

arange是Python内建函数range的数组版

a = np.arange(1,10)
# a = array([1, 2, 3, 4, 5, 6, 7, 8, 9])

如果没有特别指定,numpy生成的都是float类型
如果需要转换,则需用到 dtype 类型

dtype类型 & astype类型

可以用dtype 对数组内容进行转换

a = np.array([1.2,3,4,6.5],dtype = int)
# a = array([1, 3, 4, 6])

也可以用astype对数据类型进行转换

a =  np.array([1.2,3,4,6.5])
b = a.astype(int)
# b = array([1, 3, 4, 6])

对于astype来说,每次总是生成一个新的数组

a
# a = array([1.2, 3. , 4. , 6.5]) a的值是不变的,所以需要用一个值来接收

numpy数组算数

数组之所以如此重要是因为它允许你进行批量操作而无需任何for循环,numpy用户称这种特征为向量化
例如:

a = array([1.2, 3. , 4. , 6.5])

a*a
# array([ 1.44,  9.  , 16.  , 42.25])

a+a
# array([ 2.4,  6. ,  8. , 13. ])

对于不同尺寸的数组间操作会用到广播特性,对于本书来说,并不需要深入理解广播特性

基础索引与切片

a = np.arange(10)
# a  = array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

a[3]
# 3

a[5:8]
# array([5, 6, 7])

a[5:8]=12
a
# array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])

如你所见,如果传入了一个数值给数组的切片,例如 a[5:8] = 12 ,数值被传递给了整个切片,区别于Python的内建列表,数组的切片是原数组的视图,这意味着数组不是被复制了,任何对于视图的修改都会反映到原数组上

如果你还是想要一份数组的切片拷贝而不是一份视图的话,你就必须显式地复制这个数组,例如:

a[5:8].copy()
# array([12, 12, 12])

b =a[5:8].copy()
b[1] =10
# b = array([12, 10, 12])

花式索引和普通索引

这一章是书中没有的(书中讲的是神奇索引),但是感觉花式索引用得更多些,神奇索引使用的是整数数组进行数据索引

普通索引就不会有括号也就是index)的出现

a = np.array([[1,2,3,4],[5,6,7,8]])

a[1,3]	# 8

a[0,3:5]	
a[4:,4:]
....

花式索引则不然:

a = np.array([[0,1,2,3,4,5],[10,11,12,13,14,15],[20,21,22,23,24,25],

b = a[3:,(0,2,4)]
# b = array([[30, 32, 34],
           # [40, 42, 44],
           # [50, 52, 54]])

最直白的展示就是多了个(index)

数组转置和换轴

转置是一种特殊的数据重组形式,可以返回底层数据的视图而不需要复制任何内容。数组拥有 transpose 方法也有特殊的T属性

a = np.arange(15).reshape((3,5))

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

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

当进行矩阵计算时,当计算矩阵内积时会使用 np.dot:

b = np.random.randn(6,3)
np.dot(b.T,b)

'''
array([[ 4.25461861, -1.21334502, -0.17279715],
       [-1.21334502,  9.36874022,  1.81220759],
       [-0.17279715,  1.81220759,  8.23414671]])
'''

a.T 方法针对的是二元数组

对于更高维的数组,transpose 方法可以接收包含轴编号的元组,用于置换轴:

a = np.arange(16).reshape((2,2,4))

'''
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])
'''

a.transpose((1,0,2))

'''
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])
'''

具体解读如下:
transpose 的默认参数为 transpose((0,1,2))

数组a中10的坐标为a(1,0,3),经过transpose(1,0,2)转置后的数组b中的10的坐标为b(0,1,3)。原始的transpose参数(默认的参数)为(0,1,2),这个转置相当于将第一个坐标与第二坐标进行了互换。

第1列和最后一列不变是因为
数组a中12的坐标为a(1,1,0),经过transpose(1,0,2)转置后,前两个坐标依然不变所以列数也没有发生改变

通用函数:快速的逐元素组函数

通用函数,也可以称为 ufunc ,是一种在 ndarray 数据中进行逐元素操作的函数。某些简单函数接收一个或多个标量值,并产生一个或多个标量化结果,而通用函数就是对这些简单函数的向量化封装。

a = np.arange(5,15)
# a = array([ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14])

# 开方
np.sqrt(a)
# array([2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.        ,
      # 3.16227766, 3.31662479, 3.46410162, 3.60555128, 3.74165739])

# 取指数
np.exp(a)
'''
array([1.48413159e+02, 4.03428793e+02, 1.09663316e+03, 2.98095799e+03,
       8.10308393e+03, 2.20264658e+04, 5.98741417e+04, 1.62754791e+05,
       4.42413392e+05, 1.20260428e+06])
'''

add 和 maximum 会接收2个数组并返回一个数组作为结果,因此称为2元通用函数:

x = np.random.randn(8)
y = np.random.rand(8)

x
'''
array([ 0.14768468, -0.00975376, -1.59685431, -0.63764595,  0.17355362,
       -0.98002598, -0.35293618,  0.78962007])
'''

y
'''
array([0.7789983 , 0.78713238, 0.73497334, 0.16070788, 0.15408434,
       0.60220624, 0.7037659 , 0.7828968 ])
'''

b = np.add(x,y)
'''
array([ 0.92668298,  0.77737861, -0.86188097, -0.47693807,  0.32763796,
       -0.37781974,  0.35082972,  1.57251687])
'''

c = np.maximum(x,y)
'''
array([0.7789983 , 0.78713238, 0.73497334, 0.16070788, 0.17355362,
       0.60220624, 0.7037659 , 0.78962007])
'''

在这里,maximum 逐元素地返回 x,y 中最大的部分

也有一些通用函数返回多个数组。比如 modf ,是Python 内建函数 divmod 的向量化版本。它返回了一个浮点值数组的小数部分和整数部分。

a = np.random.randn(5)*5
'''
array([ 9.36107465,  1.7961582 , -3.17566554,  4.81236975, -0.13687402])
'''

a_float,a_int = np.modf(a)
# 接受a的整数部分
a_float
# array([ 0.36107465,  0.7961582 , -0.17566554,  0.81236975, -0.13687402])

# 接受a的整数部分
a_int
# array([ 9.,  1., -3.,  4., -0.])

将条件逻辑作为数组操作

a = np.random.randn(4,4)
'''
array([[ 0.12189457, -0.40249024,  1.65568387, -0.68654571],
       [ 0.89665272, -0.7631721 , -1.68733328, -0.80300382],
       [ 0.47949779,  0.63397883, -0.38847957, -0.7401223 ],
       [ 0.49368914, -2.12027668,  0.32092666, -1.07166121]])
'''

a > 0
'''
array([[ True, False,  True, False],
       [ True, False, False, False],
       [ True,  True, False, False],
       [ True, False,  True, False]])
'''

# 将True值替换为2,False值替换为-2
np.where(a>0,2,-2)
'''
array([[ 2, -2,  2, -2],
       [ 2, -2, -2, -2],
       [ 2,  2, -2, -2],
       [ 2, -2,  2, -2]])
'''

# 将True值替换为2,False值保留不动
np.where(a>0,2,a)
'''
array([[ 2.        , -0.40249024,  2.        , -0.68654571],
       [ 2.        , -0.7631721 , -1.68733328, -0.80300382],
       [ 2.        ,  2.        , -0.38847957, -0.7401223 ],
       [ 2.        , -2.12027668,  2.        , -1.07166121]])
'''

数学和统计方法

算均值、算求和

a = np.arange(12).reshape(4,3)

'''
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
'''

np.mean(a)
# a.mean()
# 5.5

a.sum()
# 66

sum 求和
cumsum 求累积和
cumprod 求累积积
cumsum 和 cumprod 的结果并不会聚合,它们会产生一个中间结果

a = np.array([1,2,3,4,5,6])

a.sum()
# 21

a.cumsum()
# array([ 1,  3,  6, 10, 15, 21], dtype=int32)

a.cumprod()
# array([  1,   2,   6,  24, 120, 720], dtype=int32)

布尔值数组的方法

bools = np.array([-1,0,3,0,2,0,-5],dtype = bool)
# bools = array([ True, False,  True, False,  True, False,  True])

bools.any()
# True

bools.all()
# False

any 检查数组中是否至少有一个True,而 all 检查是否每个值都为 True

唯一值

常用的方法是 np.unique ,返回的是数组中唯一值排序后形成的数组:

names = np.array(["Bob","Joe","Will","Bob","Will","Bob","Joe","Joe"])
np.unique(names)
# array(['Bob', 'Joe', 'Will'], dtype='

# 将np.unique 和 纯 python 进行比较
sorted(set(names))
# ['Bob', 'Joe', 'Will']

ints = np.array([3,3,3,4,1,2,3,4,5,3,2])
np.unique(ints)
# array([1, 2, 3, 4, 5])

unique(x) 计算 x 的唯一值,并排序

线性代数

x = np.array([[1.,2.,3.],[4.,3.,6.]])
y = np.array([[6.,23.],[4.,2.],[7.,4.]])

x
'''
array([[1., 2., 3.],
       [4., 3., 6.]])
'''

y
'''
array([[ 6., 23.],
       [ 4.,  2.],
       [ 7.,  4.]])
'''

x.dot(y)
'''
array([[ 35.,  39.],
       [ 78., 122.]])
'''

np.dot(x,y)
'''
array([[ 35.,  39.],
       [ 78., 122.]])
'''

伪随机数生成

可以通过 np.random.seed 更改 numpy 的随机数种子

np.random.seed(24)

你可能感兴趣的:(天工开物,python,数据分析)