Python数据分析03:Numpy数组和通用函数

CHAPTER 4

NumPy Basics: Arrays and Vectorized Computation



NumPy(Numerical Python的简称)。在数值计算领域,说Numpy是python最重要的包也不为过。在numpy中有下面这些东西:

  • ndarray,⼀个具有矢量算术运算和复杂⼴播(broadcasting)能力的快速且节省空间的多维数组;
  • ⽤于对整组数据进行快速运算的标准数学函数(无需编写循环);
  • 用于读取/写入(reading/writing)数据到磁盘以及用于操作内存映射文件的便利工具;
  • 线性代数,随机数生成,傅里叶变换能力;
  • 可以用A C API来写C,C++,或FORTRAN。

通过学习理解numpy中数组和数组导向计算,能帮我们理解pandas之类的工具。

NumPy可以⾼效处理⼤数组的数据。这是因为:

  • NumPy是在⼀个连续的内存块中存储数据,独⽴于其他Python内置对象。比起Python的内置序列,NumPy数组使⽤的内存更少。
  • NumPy可以在整个数组上执⾏复杂的计算,而不需要Python的for循环。

下面考察一下性能,⼀个包含⼀百万整数的数组和⼀个等价的Python列表分别乘2,比较一下CPU的运行时间:

>>>import numpy as np
>>>my_arr = np.arange(1000000)
>>>my_list = list(range(1000000))
>>>%time for _ in range(10): my_arr2 = my_arr * 2
Wall time: 32.9 ms
>>>%time for _ in range(10): my_list2 = [x * 2 for x in my_list]
Wall time: 1.25 s

4.1 Numpy ndarry:一种多维数组对象(The NumPy ndarray: A Multidimensional Array Object)

N-dimensional array object(n维数组对象ndarray),这是numpy的关键特征。先来尝试一下,生成一个随机数组:

>>>import numpy as np  #加载时的惯例
# Generate some random data
>>>data = np.random.randn(2, 3)
>>>data
array([[ 0.27813353, -0.46898668,  0.86191913],
       [-0.73249681,  0.65420047,  0.48453482]])

进行一些数学运算:

>>>data * 10  # 数组元素乘以10
array([[ 2.78133531, -4.68986681,  8.61919133],
       [-7.32496812,  6.54200474,  4.84534822]])
>>>data + data * 10  # 相当于乘以11
array([[ 3.05946884, -5.15885349,  9.48111047],
       [-8.05746493,  7.19620521,  5.32988304]])

每个数组都有⼀个shape(表示各维度大小的元组)和⼀个dtype(用于说明数组数据类型的对象):

>>>data.shape
(2, 3)
>>>data.dtype
dtype('float64')

“数组”、“NumPy数组”、“ndarray”,基本上都指的是同⼀样东西,即ndarray对象

4.1.1 创建ndarray(Creating ndarrays)

最简单的方法使用array函数,输入一个序列即可,比如list:

>>>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]])

因为data2是一个list of lists, 所以arr2维度为2。我们用ndimshape属性来确认一下:

>>>arr2.ndim
2
>>>arr2.shape
(2, 4)

除非主动声明,否则np.array会自动给data搭配适合的类型,并保存在dtype里:

>>>arr1.dtype
dtype('float64')
>>>arr2.dtype
dtype('int32')

除了np.array,还有一些其他函数能创建数组。比如zerosones;另外还可以在一个tuple里指定shape

>>>np.zeros(10)
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
>>>np.zeros((3, 6))
array([[0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0.]])
>>>np.empty((2, 3, 2))
array([[[1.10782703e-311, 2.47032823e-322],
        [0.00000000e+000, 0.00000000e+000],
        [0.00000000e+000, 8.75983079e+164]],

       [[4.71754256e-090, 1.48575043e-076],
        [5.41689273e-067, 1.86317740e+160],
        [3.99910963e+252, 1.46030983e-319]]])

注意:np.empty并不能保证返回所有是0的数组,某些情况下,会返回为初始化的垃圾数值。

arange函数是Python内置函数range的数组版:

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

这里是一些创建数组的函数:

函数 描述
array 通过推断数据类型或显式指定数据类型,将输入数据(列表、元组、数组或其它序列类型)转换为ndarray;默认情况下复制输入数据
asarray 将输入转换为ndarray,但如果输入已经是ndarray,则不会复制
arange 类似于内置range函数,但返回一个ndarray而不是一个列表
ones,
ones_like
ones是根据指定的形状和dtype创建一个全1数组;ones_like以另一个数组为参数,并根据其形状和dtype创建一个全1数组
zeros,
zeros_like
类似于ones和 ones like,只不过产生的是全0数组
empty,
empty_like
创建新数组,只分配新内存但不填充任何值,比如oneszeros
full,
full_like
用 "fill value"中的所有值,根据指定的形状和dtype创建一个数组;full_like使用另一个数组并生成一个相同形状和dtype的数组
eye,
identity
创建一个N×N单位矩阵(对角线上为1,其他地方为0)

4.1.2 ndarray的数据类型(Data Types for ndarrays)

dtype保存数据的类型:

>>>arr1 = np.array([1, 2, 3], dtype=np.float64)
>>>arr2 = np.array([1, 2, 3], dtype=np.int32)
>>>arr1.dtype
dtype('float64')
>>>arr2.dtype
dtype('int32')

dtype才是numpy能灵活处理其他外界数据的原因,我们看看Numpy的数据类型

>>>arr = np.array([1, 2, 3, 4, 5])
>>>arr.dtype
dtype('int32')
>>>float_arr = arr.astype(np.float64)
>>>float_arr.dtype
dtype('float64')

如果是把float变为int,小数点后的部分会被丢弃:

>>>arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
>>>arr
array([ 3.7, -1.2, -2.6,  0.5, 12.9, 10.1])

>>>arr.astype(np.int32)
array([ 3, -1, -2,  0, 12, 10], dtype=int32)

还可以用astype把string里的数字变为实际的数字:

>>>numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
>>>numeric_strings
array([b'1.25', b'-9.6', b'42'], dtype='|S4')
>>>numeric_strings.astype(float)
array([ 1.25, -9.6 , 42.  ])

要十分注意numpy.string_类型,这种类型的长度是固定的,所以可能会直接截取部分输入而不给警告。

如果转换(casting)失败的话,会给出一个ValueError提示。

可以用其他数组的dtype直接来制定类型:

>>>int_array = np.arange(10)
>>>calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
>>>int_array.astype(calibers.dtype)
array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])

还可以利用类型的缩写,比如u4就代表unit32

>>>empty_unit32 = np.empty(8, dtype='u4')
>>>empty_unit32
array([         0, 1075314688,          0, 1075707904,          0,
       1075838976,          0, 1072693248], dtype=uint32)

记住:调用astype总会创建⼀个新的数组

4.1.3 Numpy数组的运算(Arithmetic with NumPy Arrays)

数组之所以重要,是因为不用写for循环就能表达很多操作,这种特性叫做vectorization(向量化)。任何两个大小相等的数组之间的运算,都是element-wise(点对点):

>>>arr = np.array([[1., 2., 3.], [4., 5., 6.]])
>>>arr
array([[1., 2., 3.],
       [4., 5., 6.]])
>>>arr * arr
array([[ 1.,  4.,  9.],
       [16., 25., 36.]])
>>>arr - arr
array([[0., 0., 0.],
       [0., 0., 0.]])

element-wise,就是指两个数组的运算,在同一位置的元素间才会进行运算。

这种算数操作如果涉及标量(scalar)的话,会涉及到数组的每一个元素:

>>>1 / arr
array([[1.        , 0.5       , 0.33333333],
       [0.25      , 0.2       , 0.16666667]])
>>>arr ** 0.5
array([[1.        , 1.41421356, 1.73205081],
       [2.        , 2.23606798, 2.44948974]])

两个数组的比较会产生布尔数组:

>>>arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
>>>arr2
array([[  0.,   4.,   1.],
       [  7.,   2.,  12.]])
>>>arr2 > arr
array([[False,  True, False],
       [ True, False,  True]], dtype=bool)

4.1.4 基本的索引和切片(Basic Indexing and Slicing)

一维的我们之前已经在list(Python列表)部分用过了,没什么不同:

>>>arr = np.arange(10)
>>>arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>>arr[5]
5
>>>arr[5:8]
array([5, 6, 7])
>>>arr[5:8] = 12
>>>arr
array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])

这里把12赋给arr[5:8],其实用到了broadcasted(我觉得应该翻译为广式转变)。这里有一个比较重要的概念需要区分,python内建的list与numpy的array有个明显的区别,这里array的切片后的结果只是一个views(视图),用来代表原有array对应的元素,而不是创建了一个新的array。但list里的切片是产生了一个新的list:

>>>arr_slice = arr[5:8]
>>>arr_slice
array([12, 12, 12])

如果我们改变arr_slice的值,会反映在原始的数组arr上:

>>>arr_slice[1] = 12345
>>>arr
array([    0,     1,     2,     3,     4,    12, 12345,    12,     8,
           9])

切片[:]这个赋值给所有元素:

>>>arr_slice[:] = 64
>>>arr
array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])

之所以这样设计是出于性能和内存的考虑,毕竟如果总是复制数据的话,会很影响运算时间。当然如果想要复制,可以使用copy()方法,比如arr[5:8].copy()

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

有两种方式可以访问单一元素:

>>>arr2d[0][2]
3
>>>arr2d[0, 2]
3

我们可以把下图中的axis0看做row(行),把axis1看做column(列):

对于多维数组,如果省略后面的索引,返回的将是一个低纬度的多维数组。比如下面一个2x2x3数组:

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

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

arr3d[0]是一个2x3数组:

>>>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]]])

arr3d[1, 0]会给你一个(1, 0)的一维数组:

>>>arr3d[1, 0]
array([7, 8, 9])
>>>x = arr3d[1]
>>>x
array([[ 7,  8,  9],
       [10, 11, 12]])
>>>x[0]
array([7, 8, 9])

一定要牢记这些切片后返回的数组都是views(视图)

4.1.4.1 切片索引(Indexing with slices)

一维的话和python里的list没什么差别:

>>>arr
array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])
>>>arr[1:6]
array([ 1,  2,  3,  4, 64])

二维的话,数组的切片有点不同:

>>>arr2d
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
>>>arr2d[:2]
array([[1, 2, 3],
       [4, 5, 6]])

可以看到,切片是沿着axis 0(行)来处理的。所以,数组中的切片,是要沿着设置的axis来处理的。我们可以把arr2d[:2]理解为“选中arr2d的前两行”。

当然,给定多个索引后,也可以使用复数切片:

>>>arr2d[:2, 1:]  #前两行,第一列之后
array([[2, 3],
       [5, 6]])

记住,选中的是array view。通过混合整数,能做低维切片。比如:

>>>arr2d[1, :2]  #选中第二行的前两列
array([4, 5])
>>>arr2d[:2, 2]  #选中第三列的前两行
array([3, 6])

冒号表示提取整个axis(轴):

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

对切⽚表达式的赋值操作也会被扩散到整个选区:

>>>arr2d[:2, 1:] = 0
>>>arr2d
array([[1, 0, 0],
       [4, 0, 0],
       [7, 8, 9]])

⼆维数组切片图示:

4.1.5 布尔型索引(Boolean Indexing)

假设我们的数组数据里有一些重复。这里我们用numpy.random里的randn函数来随机生成一些离散数据:

>>>names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
>>>data = np.random.randn(7, 4)
>>>names
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')
>>>data
array([[-1.83802388,  0.99744648,  0.74242634, -0.80980885],
       [ 0.7502862 , -0.26503749, -0.32864712, -0.36100913],
       [-0.93981224, -1.06890467, -1.51573876,  1.03480672],
       [ 1.47333765, -1.65837428, -0.71768973, -0.53103594],
       [ 1.18384176, -2.02514422,  0.67444943,  0.48758923],
       [-1.01212867, -0.94724227, -0.28680484, -0.27459748],
       [-0.65620136,  0.87733924, -1.28361842,  1.57325754]])

假设每一个name对应data数组中的一行,我们想要选中name为’Bob’的所有行。就像四则运算,用比较运算符==

>>>names == 'Bob'
array([ True, False, False,  True, False, False, False])

然后用这个布尔数组当做索引:

>>>data[names == 'Bob']
array([[-1.83802388,  0.99744648,  0.74242634, -0.80980885],
       [ 1.47333765, -1.65837428, -0.71768973, -0.53103594]])

注意:布尔数组和data数组的长度要一样。

我们可以选中names=='Bob'的行,然后索引列:

>>>data[names == 'Bob', 2:]
array([[ 0.74242634, -0.80980885],
       [-0.71768973, -0.53103594]])
>>>data[names == 'Bob', 3]
array([-0.80980885, -0.53103594])

选中除了’Bob’外的所有行,可以用!=或者~

>>>names != 'Bob'
array([False,  True,  True, False,  True,  True,  True])
>>>data[~(names == 'Bob')]
array([[ 0.7502862 , -0.26503749, -0.32864712, -0.36100913],
       [-0.93981224, -1.06890467, -1.51573876,  1.03480672],
       [ 1.18384176, -2.02514422,  0.67444943,  0.48758923],
       [-1.01212867, -0.94724227, -0.28680484, -0.27459748],
       [-0.65620136,  0.87733924, -1.28361842,  1.57325754]])

当想要反转一个条件时,用~操作符很方便:

>>>cond = names == 'Bob'
>>>data[~cond]
array([[ 0.7502862 , -0.26503749, -0.32864712, -0.36100913],
       [-0.93981224, -1.06890467, -1.51573876,  1.03480672],
       [ 1.18384176, -2.02514422,  0.67444943,  0.48758923],
       [-1.01212867, -0.94724227, -0.28680484, -0.27459748],
       [-0.65620136,  0.87733924, -1.28361842,  1.57325754]])

选中2个或3个名字,组合多个布尔条件,用布尔运算符&|,另外python中的关键词andor不管用:

>>>mask = (names == 'Bob') | (names == 'Will')  #或者or
>>>mask
array([ True, False,  True,  True,  True, False, False])
>>>data[mask]
array([[-1.83802388,  0.99744648,  0.74242634, -0.80980885],
       [-0.93981224, -1.06890467, -1.51573876,  1.03480672],
       [ 1.47333765, -1.65837428, -0.71768973, -0.53103594],
       [ 1.18384176, -2.02514422,  0.67444943,  0.48758923]])

用布尔索引总是会返回一份新创建的数据,原本的数据不会被改变。

更改值的方式也很直观。比如我们想让所有负数变为0:

>>>data[data < 0] = 0
>>>data
array([[0.        , 0.99744648, 0.74242634, 0.        ],
       [0.7502862 , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 1.03480672],
       [1.47333765, 0.        , 0.        , 0.        ],
       [1.18384176, 0.        , 0.67444943, 0.48758923],
       [0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.87733924, 0.        , 1.57325754]])

用一维的布尔数组也能更改所有行或列:

>>>names
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')
>>>data[names != 'Joe'] = 7
>>>data
array([[7.        , 7.        , 7.        , 7.        ],
       [0.7502862 , 0.        , 0.        , 0.        ],
       [7.        , 7.        , 7.        , 7.        ],
       [7.        , 7.        , 7.        , 7.        ],
       [7.        , 7.        , 7.        , 7.        ],
       [0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.87733924, 0.        , 1.57325754]])

4.1.6 花式索引(Fancy Indexing)

通过整数数组来索引。假设我们有一个8 x 4的数组:

>>>arr = np.empty((8, 4))
>>>for i in range(8):
>>>    arr[i] = i
>>>arr
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.]])

想要按一定顺序选出几行,可以用一个整数list或整数ndarray来指定顺序:

>>>arr[[4, 3, 0, 6]]
array([[4., 4., 4., 4.],
       [3., 3., 3., 3.],
       [0., 0., 0., 0.],
       [6., 6., 6., 6.]])

用符号来从后选择row:

>>>arr[[-3, -5, -7]]
array([[5., 5., 5., 5.],
       [3., 3., 3., 3.],
       [1., 1., 1., 1.]])

用多维索引数组,能选出由一维数组中的元素,通过在每个tuple中指定索引:

>>>arr = np.arange(32).reshape((8, 4))
>>>arr
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]])
>>>arr[[1, 5, 7, 2], [0, 3, 1, 2]]
array([ 4, 23, 29, 10])

可以看到[ 4, 23, 29, 10]分别对应(1, 0), (5, 3), (7, 1), (2, 2)。不论数组有多少维,fancy indexing的结果总是一维。

>>>arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
array([[ 4,  7,  5,  6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11,  9, 10]])

上面的意思是,先从arr中选出[1, 5, 7, 2]这四行:

array([[ 4,  5,  6,  7],
       [20, 21, 22, 23],
       [28, 29, 30, 31],
       [ 8,  9, 10, 11]])

然后[:, [0, 3, 1, 2]]表示选中所有行,但是列的顺序要按0,3,1,2来排。于是得到:

array([[ 4,  7,  5,  6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11,  9, 10]])

要记住,fancy indexing和切片不同,得到的是一个新的array。

4.1.7 数组转置和轴对换(Transposing Arrays and Swapping Axes)

转置也是返回一个view,而不是新建一个数组。有两种方式,一个是transpose方法,一个是T属性:

>>>arr = np.arange(15).reshape((3, 5))
>>>arr
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>>arr.T
array([[ 0,  5, 10],
       [ 1,  6, 11],
       [ 2,  7, 12],
       [ 3,  8, 13],
       [ 4,  9, 14]])

做矩阵计算的时候,这个功能很常用,计算矩阵乘法的时候,用np.dot:

>>>arr = np.arange(15).reshape((3, 5))
>>>arr
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
>>>arr.T
array([[ 0,  5, 10],
       [ 1,  6, 11],
       [ 2,  7, 12],
       [ 3,  8, 13],
       [ 4,  9, 14]])
>>>np.dot(arr.T, arr)
array([[125, 140, 155, 170, 185],
       [140, 158, 176, 194, 212],
       [155, 176, 197, 218, 239],
       [170, 194, 218, 242, 266],
       [185, 212, 239, 266, 293]])

上面的例子是 (5x3) x (3x5) = (5x5)。得到的结果是5x5维,就是普通的矩阵乘法。

对于多维数组,transpose会接受由轴编号组成的tuple,来交换轴:

>>>arr = np.arange(16).reshape((2, 2, 4))
>>>arr
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])
>>>arr.transpose((1, 0, 2))
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

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

使用.T来转置swapping axes(交换轴)的一个特殊情况。ndarray有方法叫做swapaxes, 这个方法取两个axis值,并交换这两个轴:

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

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])
>>>arr.swapaxes(1, 2)  #只交换second axis和last axis
array([[[ 0,  4],
        [ 1,  5],
        [ 2,  6],
        [ 3,  7]],

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

swapaxes也是返回view,不生成新的data。


4.2 通用函数:快速点对点数组函数(Universal Functions: Fast Element-Wise Array Functions)

universal function, 或 ufunc, 是用来在ndarray中实现element-wise操作的。

可以认为这个ufunc可以把一些简单的函数做快速的向量化封装,输入是一个或多个标量,输出也是一个或多个标量。

很多ufuncs都是点对点的变换,像sqrt或exp:

>>>import numpy as np
>>>arr = np.arange(10)
>>>arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>>np.sqrt(arr)
array([ 0.        ,  1.        ,  1.41421356,  1.73205081,  2.        ,
        2.23606798,  2.44948974,  2.64575131,  2.82842712,  3.        ])
>>>np.exp(arr)
array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
       5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
       2.98095799e+03, 8.10308393e+03])

这些函数叫做一元通用函数(unary ufuncs)。其他一些函数,比如addmaximum,需要两个数组(binary ufuncs),并返回一个数组作为结果:

>>>x = np.random.randn(8)
>>>y = np.random.randn(8)
>>>x
array([ 0.49571348, -1.33311818, -0.23348991, -0.59885667,  1.4995727 ,
       -0.26874393, -0.02278506, -0.12015841])
>>>y
array([ 0.13618795, -0.41214895,  0.50087747,  0.91431258, -0.06031165,
       -0.5692135 , -1.0948818 ,  1.68605986])
>>>np.maximum(x, y)  #取相应位置上最大的元素
array([ 0.49571348, -0.41214895,  0.50087747,  0.91431258,  1.4995727 ,
       -0.26874393, -0.02278506,  1.68605986])

尽管不常见,但ufunc也能返回多个数组。例如modf,这是一个向量版的divmod(python内建函数),modf会返回小数部分和整数部分:

本函数是实现a除以b,然后返回商与余数的元组。如果两个参数a,b都是整数,那么会采用整数除法,结果相当于(a//b, a % b)。如果a或b是浮点数,相当于(math.floor(a/b), a%b)。

>>>arr = np.random.randn(7) * 5
>>>arr
array([ 4.64583719, -3.06159131, 10.77703888, -4.46311365, -5.44400588,
        3.11527145, -0.51038373])
>>>remainder, whole_part = np.modf(arr)
>>>remainder
array([ 0.64583719, -0.06159131,  0.77703888, -0.46311365, -0.44400588,
        0.11527145, -0.51038373])
>>>whole_part
array([ 4., -3., 10., -4., -5.,  3., -0.])

ufunc能接受一个可选参数作为输出,这样可以直接更改原有的数组:

>>>arr
array([       nan,        nan,        nan,        nan, 1.91152854,
              nan, 1.19599591])
>>>np.sqrt(arr)
array([       nan,        nan,        nan,        nan, 1.91152854,
              nan, 1.19599591])
>>>np.sqrt(arr, arr)
array([       nan,        nan,        nan,        nan, 1.91152854,
              nan, 1.19599591])
>>>arr
array([       nan,        nan,        nan,        nan, 1.91152854,
              nan, 1.19599591])

4.2.1 通用函数(Universal Functions)

一元通用函数(Unary universal functions)

函数 描述
abs, fabs 计算整数、浮点或复数的绝对值;对于非复数值,可以使用更快的fabs
sqrt 计算各元素的平方根,相当于arr**0.5
square 计算各元素的平方,相当于arr**2
exp 计算各元素的指数exe^x
log, log10,
log2, log1p
分别以自然对数(底数为e)、底数为10的log、底数为2的log、log(1+x)
sign 计算各元素的正负号:1(正数)、0(零)、-1(负数)
ceil 计算各元素的ceiling值,即大于等于该值的最小整数
floor 计算各元素的floor值,即小于等于该值的最大整数
rint 将各元素值四舍五入到最接近的整数,保留dtype
modf 将数组的小数和整数部分以两个独立数组的形式返回
isnan 返回一个表示“那些值是NaN(这不是一个数字)”的布尔型数组
isfinite, isinf 分别返回一个表示“哪些元素是有穷的(非inf,非NaN)”或“哪些元素是无穷的”的布尔型数组
cos, cosh, sin,
sinh, tan, tanh
普通型和双曲型三角函数
arccos, arccosh, arcsin,
arcsinh, arctan, arctanh
反三角函数
logical_not 计算各元素not x的真值;相当于-arr

二元通用函数(Binary universal functions)

函数 描述
add 将数组中对应的元素相加
subtract 从第一个数组中减去第二个数组中的元素
multiply 数组元素相乘
divide,floor_divide 除法(舍弃余数)
power 对第一个数组中的元素A,根据第二个数组中的元素B,计算ABA^B
maximum, fmax 保留两个数组对应元素处的最大值,忽略NaN
minimum, fmin 保留两个数组对应元素处的最小值,忽略NaN
mod 元素方向的模量(除法的余数)
copysign 将第二个参数中的值符号复制给第一个参数中的值
greater, greater_equal, less,
less_equal, equal, not_equal
执行元素比较,生成布尔型数组,相当于运算符>,>=,<,<=,==,!=
logical_and, logical_or,
logical_xor
计算逻辑运算的元素真值,相当于运算符& ,|,^

参考资料:

发布了22 篇原创文章 · 获赞 6 · 访问量 2856
App 阅读领勋章
微信扫码 下载APP
阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 1024 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览