Numpy的高级应用

第三方库的导入:

import numpy as np
import pandas as pd
from pandas import DataFrame,Series
from numpy.random import randn

一、ndarray对象的内部机理

numpyadarray可以将同质数据块解释为多维数组的方式,数据类型(dtype)决定了数据的解释方式
ndarray是所有数组对象都是数据块的一个跨度视图
ndarray的内容组成:

  • 一个指向数组(一个系统内存块)的指针
  • 数据类型dtype
  • 一个表示数组形状的元组
np.ones((10,5)).shape
  • 一个跨度元组,其中的整数指的是为了前进到当前维度下一个元素需要跨过的字节数
np.ones((3,4,5),dtype=np.float64).strides

在这里插入图片描述
跨度信息是构建非复制式数组视图的重要因素,跨度可以是负数,使数组在内存中后向移动

二、Numpy数据类型体系

判断dtype是否属于某个大类

ints = np.ones(10,dtype=np.uint16)
floats = np.ones(10,dtype=np.float32)
np.issubdtype(ints.dtype,np.integer)
Out:True

np.issubdtype(floats.dtype,np.floating)
Out:True

调用dtypemro方法查看所有大类

np.float64.mro()
Out:
[numpy.float64,
 numpy.floating,
 numpy.inexact,
 numpy.number,
 numpy.generic,
 float,
 object]

在这里插入图片描述

三、高级数组操作

1、数组重塑

Shape函数

arr = np.arange(8)
arr.reshape((4,2))

多维数组的重塑

arr.reshape((4,2)).reshape((2,4))

“-1”可以作为参数的形状,它表示该维度的大小由数据本身推断而来:

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

也可以传入shape属性的元组

other_arr = np.ones((3,5))
arr.reshape(other_arr.shape)

ravel函数
reshape相反的多维数组的运算过程,称为扁平化或者散开

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

flatten方法返回的是数据的副本,ravel函数不会产生源数据的副本

arr.flatten()

四、C和Fortran顺序

Numpy可以灵活控制在内存中的布局。

Numpy数组是按行优先顺序创建的,也叫C顺序,意味着每行中的数据项是被存放在相邻内存位置上的;另一种顺序是列优先顺序,也叫Fortran顺序,矩阵全都是列优先的

reshaperavel函数可以接受一个表示数组数据存放顺序的order参数。

arr = np.arange(12).reshape((3,4))
arr.ravel('F')
Out:
array([ 0,  4,  8,  1,  5,  9,  2,  6, 10,  3,  7, 11])

arr.ravel('C')
Out:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

在这里插入图片描述

对于,二维或者更高维度的重塑过程,CFortran顺序的关键区别就是维度的行进顺序

  • C/行优先顺序:先经过更高的维度(轴1会优先于轴0被处理)
  • Fortran/列优先顺序:后经过更高的维度(轴0会优先轴1被处理)

五、数组的合并和拆分

np.concatenate可以按指定走将一个由数组组成的序列(如元组、列表)连接到一起

arr1 = np.array([[1,2,3],[4,5,6]])
arr2 = np.array([[7,8,9],[10,11,12]])
np.concatenate([arr1,arr2],axis=0)

np.concatenate([arr1,arr2],axis=1)

Numpy提供了一些较为方便的方法(vstackhstack

np.vstack((arr1,arr2))

np.hstack((arr1,arr2))

split用于将数组沿指定轴拆分为多个数组

from numpy.random import randn
arr = randn(5,2)
arr
Out:
array([[ 0.29126855, -0.66748463],
       [-0.68677925,  0.360508  ],
       [ 0.49896033, -1.62448238],
       [-1.32739471,  1.26042767],
       [ 0.80410505,  0.29827872]])
       
first,second,third = np.split(arr,[1,3])

first
Out:
array([[-1.32739471,  1.26042767],
       [ 0.80410505,  0.29827872]])
second
Out:
array([[-0.68677925,  0.360508  ],
       [ 0.49896033, -1.62448238]])
third
Out:
array([[-1.32739471,  1.26042767],
       [ 0.80410505,  0.29827872]])

1、堆叠辅助类r_和c_

它们可以使得数组的堆叠操作更为简洁

arr = np.arange(6)
arr1 = arr.reshape((3,2))
arr2 = randn(3,2)
np.r_[arr1,arr2]
Out:
array([[ 0.        ,  1.        ],
       [ 2.        ,  3.        ],
       [ 4.        ,  5.        ],
       [-1.50478906, -0.98422073],
       [-1.26121877,  2.42921896],
       [-0.25016918,  0.60142896]])
       
np.c_[np.r_[arr1,arr2],arr]
Out:
array([[ 0.        ,  1.        ,  0.        ],
       [ 2.        ,  3.        ,  1.        ],
       [ 4.        ,  5.        ,  2.        ],
       [-1.50478906, -0.98422073,  3.        ],
       [-1.26121877,  2.42921896,  4.        ],
       [-0.25016918,  0.60142896,  5.        ]])

也可以将切片翻译成数组

np.c_[1:6,-10:-5]
Out:
array([[  1, -10],
       [  2,  -9],
       [  3,  -8],
       [  4,  -7],
       [  5,  -6]])

2、元素的重复操作:tile和repeat

对数组进行重复以产生一个数组,repeat会将数组中的各个元素重复一定次数,从而产生一个更大的数组

arr = np.arange(3)
arr.repeat(3)
arr.repeat([2,3,4])

tile的功能是沿指定轴向堆叠数组的副本,相当于“铺瓷砖”

arr = randn(2,2)
np.tile(arr,(2,1))

3、花式索引的等价函数:take和put

获取数组自己的一个办法是通过整数数组使用花式索引

arr = np.arange(10)*100
inds = [7,1,2,6]
arr[inds]

ndarray 有两个方法专门用于获取和设置单个轴向上的选区

arr.take(inds)
arr.put(inds,42)
arr.put(inds,[41,42,43,44])

inds = [2,0,2,1]
arr = randn(2,4)
arr.take(inds,axis=1)

put函数不接受axis参数,他只会在数组的扁平化进行索引

六、广播

定义:它指的是不同形状的数组之间的算术运算的执行方式。其中将标量值跟数组合并时就会发生最简单的广播。

这里我们说:在这个乘法运算中,标量值4被广播到了其他元素上

arr = np.arange(5)
arr
Out:array([0, 1, 2, 3, 4])
arr*4
Out:array([ 0,  4,  8, 12, 16])

距平化处理,对数组的每一列进行减去列平均值的方式

arr = randn(4,3)
arr.mean(0)
demeaned = arr - arr.mean(0)
demeaned.mean(0)
Out:
array([ 0.00000000e+00, -1.11022302e-16,  1.04083409e-17])
arr
Out:
array([[-1.46031835,  1.3240841 ,  0.1621479 ],
       [-0.61236475, -0.50598835, -0.33708048],
       [-1.2265001 ,  0.85693646,  0.64020573],
       [-1.13596485, -2.56214722,  0.12491285]])

row_means = arr.mean(1)
row_means.reshape((4,1))
demeaned = arr - row_means.reshape((4,1))
demeaned.mean(1)
Out:
array([-5.55111512e-17,  5.55111512e-17,  0.00000000e+00,  7.40148683e-17])

通过广播设置数组的值

arr = np.zeros((4,3))
arr[:]=5

用一个一维数组来设置目标数组的格列,其中np.newaxis参数可以不用构造一个表示新形状的元组,就可以插入新轴:

col = np.array([1.28,-0.42,0.44,1.6])
arr[:] = col[:,np.newaxis]
arr
Out:
array([[ 1.28,  1.28,  1.28],
       [-0.42, -0.42, -0.42],
       [ 0.44,  0.44,  0.44],
       [ 1.6 ,  1.6 ,  1.6 ]])

七、ufunc高级应用

Numpy的各个二元ufunc都有一些用于执行特定矢量化运算的特殊方法。在这里插入图片描述

1、reduce 函数

reduce可以接受一个数组参数,并通过一系列的二元运算对其值进行聚合,例如:我们可以用np.add.reduce对各个元素进行求和

arr = np.arange(10)
np.add.reduce(arr)
arr.sum()

np.logical_and检查数组各行中的值是否是有序的:

arr = randn(5,5)
arr[::2].sort(1) #对部分进行排序
arr[:,:-1]<arr[:,1:]
np.logical_and.reduce(arr[:,:-1]<arr[:,1:],axis=1)
2、accumulate函数

产生一个跟原数组大小相同的中间“累计”值数组:

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

np.add.accumulate(arr,axis=1)
Out:
array([[ 0,  1,  3,  6, 10],
       [ 5, 11, 18, 26, 35],
       [10, 21, 33, 46, 60]])
3、Outer函数

用于计算叉积

arr = np.arange(3).repeat([1,2,2])
np.multiply.outer(arr,np.arange(5))
4、reduceat函数

用于计算“局部简约”,就是一个对数据各切片进行聚合groupby运算。

arr=np.arange(10)
np.add.reduceat(arr,[0,5,8])
5、自定义ufunc函数

numpy.frompyfunc接受一个Python函数以及两个分别表示输入参数数量的整数。
例如:下面是一个能够实现元素级加法的简单函数

  • 方法一:
def add_elements(x,y):
    return x+y
add_them = np.frompyfunc(add_elements,2,1)
add_them(np.arange(8),np.arange(8))
Out:
array([0, 2, 4, 6, 8, 10, 12, 14], dtype=object)

frompyfunc创建的函数总是返回Python对象数组,这一点很不方便。

  • 方法二:
    numpy.vectorize,它在类型推断方面更加智能
add_them = np.vectorize(add_elements,otypes=[np.float64])
add_them(np.arange(8),np.arange(8))
Out:
array([ 0.,  2.,  4.,  6.,  8., 10., 12., 14.])

八、结构化和记录式数组

结构化数组是一种特殊的ndarray,其中的各个元素可以被看作C语言中的结构体或SOL表中带有多个命名字段的行:

dtype = [('x',np.float64),('y',np.int32)]
sarr = np.array([(1.5,6),(np.pi,-2)],dtype=dtype)

最典型的定义结构化dtype的方式就是元组列表,各元组的格式为(field_name, field_type)

sarr[0]
Out:(1.5, 6)

字段保存在dtype.names属性中,在访问结构化数组的某个字段时,返回的是该数据的视图,所以不会发生数据的复制

sarr['x']
Out:array([1.5       , 3.14159265])

1、嵌套dtype和多维字段

可以再设置一个形状,来定义结构化dtype

dtype = [('x',np.int64,3),('y',np.int32)]
arr = np.zeros(4,dtype=dtype)

其中x字段记录的是一个长度为3的数组

arr[0]['x']

这就能使我们单个数组的内存块存放复杂的嵌套结构
示范:嵌套dtype

dtype = [('x',[('a','f8'),('b','f4')]),('y',np.int32)]
data = np.array([((1,2),5),((3,4),6)],dtype=dtype)
data['x']
Out:array([(1., 2.), (3., 4.)], dtype=[('a', '<f8'), ('b', '<f4')])

2、为什么要用结构化数组

他可以将单个内存块解释为带有任意复杂嵌套列的表格型数据。由于数组中的每个元素在内存中都被表示为固定的字节数,所以结构化数组能够提供非常高效的磁盘数据读写

九、排序

ndarraysort实例方法就是就地排序,数组内容的重新排列不会产生新数组。相反,numpy.sort会为原数组创建一个已排序副本

arr = randn(5)
arr
Out:
array([1.04476665, 0.60057523, 1.34554912, 0.71409436, 3.10547606])

np.sort(arr)
Out:
array([0.60057523, 0.71409436, 1.04476665, 1.34554912, 3.10547606])

这个排序方法不可以被设置为降序,可以使用列表的反序列表

arr[::-1]
Out:array([3.10547606, 0.71409436, 1.34554912, 0.60057523, 1.04476665])

1、间接排序:argsort和lexsort

在数据分析工作中,常常需要一个或多个键对数据集进行排序。给定一个或多个键,你就可以得到一个由整数组成的索引数组(索引器

values = np.array([5,0,1,3,2])
indexer = values.argsort()
indexer #返回排序索引
Out:array([1, 2, 4, 3, 0])
values[indexer]
Out:array([0, 1, 2, 3, 5])
  • 根据数组的第一行对其排序
arr = randn(3,5)
arr[0] = values
arr
Out:
array([[ 5.        ,  0.        ,  1.        ,  3.        ,  2.        ],
       [-0.55049158,  1.37840367,  0.57288805,  1.76152185, -0.21042948],
       [-0.57566978,  0.59815094,  0.79902713, -0.60928023, -1.18951258]])
       
arr[:,arr[0].argsort()]
Out:
array([[ 0.        ,  1.        ,  2.        ,  3.        ,  5.        ],
       [ 1.37840367,  0.57288805, -0.21042948,  1.76152185, -0.55049158],
       [ 0.59815094,  0.79902713, -1.18951258, -0.60928023, -0.57566978]])

lexsortargsort差不多,只不过他可以一次性对多个键数组执行间接排序(字典序)

  • 范例:对一些以姓和名标示的数组进行排序
first_name = np.array(['Bob','Jane','Steve','Bill','Barbara'])
last_name = np.array(['Jones','Arnold','Arnold','Jones','Walters'])
sorter = np.lexsort((first_name,last_name))
zip(last_name[sorter],first_name[sorter])

2、其他排序算法

稳定的排序算法会保持等价元素的相对位置。

values = np.array(['2:first','2:second','1:first','1:second','1:third'])
key = np.array([2,2,1,1,1])
indexer = key.argsort(kind='mergesort')

mergesort(合并排序)是这些排序里唯一的稳定排序

values.take(indexer)
Out:
array(['1:first', '1:second', '1:third', '2:first', '2:second'],
      dtype='<U8')

在这里插入图片描述

3、numpy.searchsorted:在有序数组中查找元素

searchsorted是一个在有序数组上执行二分查找的数组方法,只要将值插入到他返回的那个位置就可以维持数组的有序性

arr = np.array([0,1,7,12,15])
arr.searchsorted(9)
Out:3

searchsorted的另一个用法,假设有一个数据数组的值从0-1000之间,还有一个表示“面元边界”的数组,我能希望用它将数据数组拆分开

data = np.floor(np.random.uniform(0,10000,size=50))
bins = np.array([0,100,1000,5000,10000])
data
Out:
array([2557., 8042., 8472., 2732., 3676., 2779., 5785., 5268., 4572.,
       6304., 7420., 5250., 4264., 1196., 5319., 9839., 8194., 9250.,
       2153., 9223., 3687., 9561., 1498., 1830., 2767., 7918., 4045.,
       3495., 7532., 1905., 4262.,  881.,  519., 3322., 3718., 5582.,
       7482., 2577., 4142., 7973., 9000., 3225., 7391.,  639., 2283.,
       8425., 8584., 7463., 2980., 2393.])

为了得到个数据点所属区间的编号

labels = bins.searchsorted(data)
labels
Out:
array([3, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 3, 4, 4, 4, 4, 3, 4, 3, 4,
       3, 3, 3, 4, 3, 3, 4, 3, 3, 2, 2, 3, 3, 4, 4, 3, 3, 4, 4, 3, 4, 2,
       3, 4, 4, 4, 3, 3])

再通过pandasgroupby将结果拆分

Series(data).groupby(labels).mean()
Out:
2     679.666667
3    3002.416667
4    7620.739130
dtype: float64

Numpydigitize也可以计算这种面元编号

np.digitize(data,bins)

十、高级数组的输入输出

内存映像文件

Numpy实现了一个类似于ndarraymemmap对象,它允许将大文件分成小段进行读写,而不是一次性将整个数组读入内存。

使用np.memmap函数传入一个文件路径、数据类型、文件模式以及形状,即可创建一个新的memmap

mmap = np.memmap('mymap',dtype='float64',mode='w+',shape=(10000,10000))
mmap
Out:
memmap([[0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]])

其中memmap的切片也是数据视图

section = mmap[:5]
section[:] = np.random.rand(5,10000)
mmap.flush()
mmap
Out:
memmap([[0.33831052, 0.02845899, 0.94490351, ..., 0.46454688, 0.15109669,
         0.95387255],
        [0.77214426, 0.02520478, 0.76379407, ..., 0.81475754, 0.65447479,
         0.82608759],
        [0.92651679, 0.82303761, 0.9384897 , ..., 0.35232664, 0.48599669,
         0.28068128],
        ...,
        [0.        , 0.        , 0.        , ..., 0.        , 0.        ,
         0.        ],
        [0.        , 0.        , 0.        , ..., 0.        , 0.        ,
         0.        ],
        [0.        , 0.        , 0.        , ..., 0.        , 0.        ,
         0.        ]])

del mmap

只要内存映像超出作用域,就会被垃圾回收期回收,之前对其所有的操作都会被写入磁盘,但是打开一个以及存在的内存映像,仍然需要指明数据类型和形状。

mmap = np.memmap('mymap',dtype='float64',shape=(10000,10000))
mmap
Out:
memmap([[0.16264806, 0.48319848, 0.82222971, ..., 0.49783845, 0.26486926,
         0.42594865],
        [0.00296158, 0.42102723, 0.35295437, ..., 0.29119252, 0.41847388,
         0.01854104],
        [0.40182186, 0.36677069, 0.81543187, ..., 0.66691882, 0.39284047,
         0.11911527],
        ...,
        [0.        , 0.        , 0.        , ..., 0.        , 0.        ,
         0.        ],
        [0.        , 0.        , 0.        , ..., 0.        , 0.        ,
         0.        ],
        [0.        , 0.        , 0.        , ..., 0.        , 0.        ,
         0.        ]])
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值