利用Python进行数据分析(第三版)--第4章Numpy基础

NumPy是Python数值计算中最重要的基础包之一,其被设计为可以高效的处理大型数组的数据

基于Numpy的算法要比纯Python算法快10-100倍,且使用的内存更少

import numpy as np
#后文默认已导入NumPy

my_arr = np.arange(1_000_000)

%timeit my_arr2 = my_arr * 2
#1.1 ms ± 53.8 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

my_list = list(range(1_000_000))

%timeit my_list2 = my_list * 2
#12.7 ms ± 199 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

4.1多维数组对象

数组中的所有元素必须是同类型

4.1.1创建ndarray

array函数接收任意序列型对象,然后生成一个新的包含传入数据的NumPy数组

除非特意指定,否则np.array会尝试为新建的数组推断出合适的数据类型

#array函数接收任意序列型对象,然后生成一个新的包含传入数据的NumPy数组
data1 = [6, 7.5, 8, 0, 1]
arr1 = np.array(data1)

arr1
#array([6. , 7.5, 8. , 0. , 1. ])

#嵌套序列
data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr2 = np.array(data2)

arr2
#array([[1, 2, 3, 4],
#       [5, 6, 7, 8]])

arr1.dtype
#dtype('float64')

其他可用于创建数组的函数.zeros() .ones() .empty(),其他用于创建数组的函数自行搜索

#.zeros .ones分别创建全0和全1的数组,传递表示形状的元组可以创建多维数组
np.zeros(10)
#array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])

np.ones((2,3))
#array([[1., 1., 1.],
#       [1., 1., 1.]])

#.empty可以创建一个没有任何具体值的数组,但其返回的是未初始化的内存值,包含“垃圾”
#当给新数组填充数据时再用
np.empty((2,3,2))

#array([[[1.25658690e-311, 2.86558075e-322],
#        [0.00000000e+000, 0.00000000e+000],
#        [1.25622151e-311, 2.42336543e-057]],
#
#       [[5.49704909e-090, 3.26696491e-032],
#        [8.46379413e+164, 5.68709725e-062],
#        [6.48224659e+170, 5.82471487e+257]]])

如果没有特别指明,因NumPy专注于数值计算,所以类型一般是float64

4.1.2ndarray的数据类型

dtype是一个特殊对象,它包含一个信息:解释NumPy数组在内存存储数据时数据类型的信息

arr1 = np.array([1, 2, 3], dtype = np.float64)

arr1.dtype
#dtype('float64')

.astype()方法可以将数组元素的数据类型转换为另一种数据类型(也存在不能转换的情况)

#将数组从一种数据类型转换或投射成另一种数据类型
arr = np.array([1, 2, 3, 4, 5])

arr.dtype
#dtype('int32')

float_arr = arr.astype(np.float64)

float_arr.dtype
#dtype('float64')

如果将浮点数转换成整数,则会发生截断,舍去小数点

numpy.string_类型(一个字符一个字节),Numpy的字符串数据是大小固定的,发生截断时不会警告

即使新的数据类型与旧的数据类型相同,astype也总创建一个新的数组

4.1.3数组的运算

对数组进行批量运算的操作可以成为向量化(个人理解:和向量的算术运算相似,事实也如此)

#大小相等的数组之间的任何算术运算都会转为元素级的运算
arr = np.array([[1., 2., 3.],[4., 5., 6.]])

arr * arr
#array([[ 1.,  4.,  9.],
#       [16., 25., 36.]])

arr - arr
#array([[1.        , 0.5       , 0.33333333],
#       [0.25      , 0.2       , 0.16666667]])

#数组与标量的运算将标量值转播到数组中的每个元素
1 / arr
#array([[1.        , 0.5       , 0.33333333],
#       [0.25      , 0.2       , 0.16666667]])

arr ** 2
#array([[ 1.,  4.,  9.],
#       [16., 25., 36.]])

#大小相同的数组进行布尔运算则会生成布尔值数组
arr2 = np.array([[0., 4., 1.],[7., 2., 12.]])

arr2 > arr
#arr2 > arr
#array([[False,  True, False],
#       [ True, False,  True]])

4.1.4基本的索引和切片

数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会反映到原数组上

arr = np.arange(10)

arr_slice = arr[5:8]

arr_slice[1] = 1234

arr
#array([   0,    1,    2,    3,    4,    5, 1234,    7,    8,    9])

因为NumPy的设计是为了处理大型数组,所以若也使用复制的方式,内存和性能方面会有很大问题

在一个二维数组中,索引位置上的元素不再是标量而是一维数组,下图为轴的示意:

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

arr3d[0]
#array([[1, 2, 3],
#       [4, 5, 6]])

#标量和数组都可以赋值给arr3d[0]
old_values = arr3d[0].copy()

arr3d[0] = 42

arr3d
#array([[[42, 42, 42],
#        [42, 42, 42]],
#
#       [[ 7,  8,  9],
#        [10, 11, 12]]])

arr3d[0] = old_values

arr3d
#array([[[ 1,  2,  3],
#        [ 4,  5,  6]],
#
#       [[ 7,  8,  9],
#        [10, 11, 12]]])

我意识到我写的太罗嗦了,所以从这里决定简写,挑比较易混淆等知识点写,给自己个提醒

进行切片时,如一个3 * 3的二维数组 arr[:,:1] 是针对二维层面去切的

切出来的将会是3 * 1形状的数组因为冒号意味着选取整个轴

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

arr2d[:, :1]
#array([[1],
#      [4],
#      [7]])

4.1.5布尔型索引

布尔型数组的长度必须与被索引的轴的长度一致,布尔型数组可以选取目标数组中对应位置为True的元素或数组

names = np.array(["Bob", "Joe", "Will", "Bob", "Will", "Joe", "Joe"])
#在这里data是7*2的数组,轴0长度为7,布尔型数组长度也为7
data = np.array([[4, 7], [0, 2], [-5, 6], [0, 0], [1, 2],[-12, -4], [3, 4]])

data[names == "Bob", 1:]
#array([[7],
#      [0]])

通过布尔型索引选取数组中的数据,并给结果复制新的变量,会创建数据的副本,即使返回一模一样的数组也如此

4.1.6花式索引--指用整数数组进行索引

一次传入多个索引数组,返回的是一维数组,按照对应位置元素组成索引去返回

#reshape不复制数组就可以改变数组形状
arr = np.arange(32).reshape((8, 4))

arr[[1, 5, 7, 2], [0, 3, 1, 2]]
#array([ 4, 23, 29, 10])

花式索引会将数据复制到新数组中

4.1.7数组转置和轴对换--不复制原地进行转置

4.2生成伪随机数

numpy.random模块用于从多种概率分布中有效地生成整个样本值数组

python内置的random模块只能一次生成一个样本值。若需要生成大量样本值,numpy.random快了很多

from random import normalvariate

N = 1_000_000

%timeit samples = [normalvariate(0, 1) for _ in range(N)]
#560 ms ± 5.25 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit np.random.standard_normal(N)
#18.5 ms ± 203 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

4.3通用函数:快速的元素级数组函数

numpy.modf可以返回多个数组,这个函数会返回浮点数数组的整数和小数部分

rng = np.random.default_rng(seed=12345)

arr = rng.standard_normal(7) * 5

remainder, whole_part = np.modf(arr)

remainder
#array([-0.11912518,  0.31864229, -0.35330869, -0.29586617, -0.37671654,
#      -0.70442326, -0.83896351])

whole_part
#array([-7.,  6., -4., -1., -0., -3., -6.])

4.4利用数组进行面向数组编程

NumPy数组将许多数据处理任务表述为简洁的数组表达式,而无须编写循环

4.4.4排序

NumPy也通过 sort 方法就地排序

arr = rng.standard_normal(6)

arr
#array([ 0.90219827, -0.46695317, -0.06068952,  0.78884434, -1.25666813,
#       0.57585751])

arr.sort()

arr
#array([-1.25666813, -0.46695317, -0.06068952,  0.57585751,  0.78884434,
#       0.90219827])

顶级方法np.sort()返回的是已排序数组的副本,而不是就地修改

书中剩下部分较容易理解,第4章就到这里了

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值