两万字总结python之numpy库

Numpy库介绍

NumPy是一个功能强大的Python库,主要用于对多维数组执行计算。NumPy这个词来源于两个单词-- Numerical和Python。NumPy提供了大量的库函数和操作,可以帮助程序员轻松地进行数值计算。在数据分析和机器学习领域被广泛使用。它有以下几个特点:

  1. numpy内置了并行运算功能,当系统有多个核心时,做某种计算时,numpy会自动做并行计算。
  2. Numpy底层使用C语言编写,内部解除了GIL(全局解释器锁),其对数组的操作速度不受Python解释器的限制,效率远高于纯Python代码。
  3. 有一个强大的N维数组对象Array(一种类似于列表的东西)。
  4. 实用的线性代数、傅里叶变换和随机数生成函数。

Numpy数组和Python列表性能对比:

比如我们想要对一个Numpy数组和Python列表中的每个素进行求平方。那么代码如下:
python列表:

import numpy as np
import time
t1=time.time()
a=[]
for x in range(1000000):
    a.append(x**2)
t2=time.time()
print('python列表耗时:',t2-t1)

结果:

python列表耗时: 0.34757137298583984

numpy数组:

t3=time.time()
b=np.arange(1000000)**2
t4=time.time()
print('numpy数组耗时:',t4-t3)

结果:

numpy数组耗时: 0.003968954086303711

教程地址:

官网:https://docs.scipy.org/doc/numpy/user/quickstart.html。
中文文档:https://www.numpy.org.cn/user_guide/quickstart_tutorial/index.html。

目录:

NumPy数组基本用法

  • Numpy是Python科学计算库,用于快速处理任意维度的数组。
  • Numpy提供一个N维数组类型ndarray,它描述了相同类型的“items”的集合。
  • Numpy.ndarray支持向量化运算。
  • Numpy使用c语言写的,底部解除了GIL,其对数组的操作速度不在受python解释器限制。

numpy中的数组:

Numpy中的数组的使用跟Python中的列表非常类似。他们之间的区别如下:

  1. 一个列表中可以存储多种数据类型。比如a = [1,‘a’]是允许的,而数组只能存储同种数据类型。
  2. 数组可以是多维的,当多维数组中所有的数据都是数值类型的时候,相当于线性代数中的矩阵,是可以进行相互间的运算的。

创建数组(np.ndarray对象):

在Numpy中的数组的数据类型叫做ndarray。

  1. 根据Python中的列表生成:
import numpy as np
a1 = np.array([1,2,3,4])
print(a1)
print(type(a1))
  1. 使用np.arange生成,np.arange的用法类似于Python中的range:
import numpy as np
a2 = np.arange(2,21,2)
print(a2)
  1. 使用np.random生成随机数的数组:
a1 = np.random.random(2,2) # 生成22列的随机数的数组
a2 = np.random.randint(0,10,size=(3,3)) # 元素是从0-10之间随机的33列的数组
  1. 使用函数生成特殊的数组:
import numpy as np
a1 = np.zeros((2,2)) #生成一个所有元素都是022列的数组
a2 = np.ones((3,2)) #生成一个所有元素都是132列的数组
a3 = np.full((2,2),8) #生成一个所有元素都是822列的数组
a4 = np.eye(3) #生成一个在斜方形上元素为1,其他元素都为03x3的矩阵

ndarray常用属性:

ndarray.dtype:

因为数组中只能存储同一种数据类型,因此可以通过dtype获取数组中的元素的数据类型。以下是ndarray.dtype的常用的数据类型:

数据类型描述唯一标识符
bool用一个字节存储的布尔类型(True或False)‘b’
int8一个字节大小,-128 至 127‘i1’
int16整数,16 位整数(-32768 ~ 32767)‘i2’
int32整数,32 位整数(-2147483648 ~ 2147483647)‘i4’
int64整数,64 位整数(-9223372036854775808 ~ 9223372036854775807)‘i8’
uint8无符号整数,0 至 255‘u1’
uint16无符号整数,0 至 65535‘u2’
uint32无符号整数,0 至 2 ** 32 - 1‘u4’
uint64无符号整数,0 至 2 ** 64 - 1‘u8’
float16半精度浮点数:16位,正负号1位,指数5位,精度10位‘f2’
float32单精度浮点数:32位,正负号1位,指数8位,精度23位‘f4’
float64双精度浮点数:64位,正负号1位,指数11位,精度52位‘f8’
complex64复数,分别用两个32位浮点数表示实部和虚部‘c8’
complex128复数,分别用两个64位浮点数表示实部和虚部‘c16’
object_python对象‘O’
string_字符串‘S’
unicode_unicode类型‘U’

我们可以看到,Numpy中关于数值的类型比Python内置的多得多,这是因为Numpy为了能高效处理处理海量数据而设计的。举个例子,比如现在想要存储上百亿的数字,并且这些数字都不超过254(一个字节内),我们就可以将dtype设置为int8,这样就比默认使用int64更能节省内存空间了。类型相关的操作如下:

  1. 默认数据类型
import numpy as np
a=np.arange(10)
print(a)
print(a.dtype)
# 如果是windows系统,默认是int32
# 如果是mac或者linux系统,则根据系统来

结果:

[0 1 2 3 4 5 6 7 8 9]
int32
  1. 为每个元素指定数据类型
b=np.array([1,2,3,4,5],dtype=np.int8)#指定每个元素的类型
print(b)
print(b.dtype)

结果:

[1 2 3 4 5]
int8

f=np.array(['a','b'],dtype='S')#字符串的唯一标识符'S'
print(f)
print(f.dtype)

结果:

[b'a' b'b']
|S1
  1. 存储对象
class Person:
    def __init__(self,name,age):
        self.name=name
        self.age=age
d=np.array([Person('张三',18),Person('李四',18)])
print(d)
print(d.dtype)

结果:

[<__main__.Person object at 0x000001B757D7CEE0>
 <__main__.Person object at 0x000001B757D7C910>]
object
  1. 修改每个元素的数据类型
import numpy as np
a1 = np.array([1,2,3])
print(a1.dtype) # window系统下默认是int32
# 以下修改dtype
a2 = a1.astype(np.int64) # astype不会修改数组本身,而是会将修改后的结果返回
print(a2.dtype)

结果:

int32
int64

ndarray.size:获取数组的元素个数

import numpy as np
   a1 = np.array([[1,2,3],[4,5,6]])
   print(a1.size) #打印的是6,因为总共有6个元素

ndarray.ndim:数组的维数

 a1 = np.array([1,2,3])
   print(a1.ndim) # 维度为1
   a2 = np.array([[1,2,3],[4,5,6]])
   print(a2.ndim) # 维度为2
   a3 = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
   print(a3.ndim) # 维度为3

ndarray.shape:

numpy 创建的数组都有一个shape属性,它是一个元组,返回各个维度的维数。有时候我们可能需要知道某一维的特定维数。

代码示例1:

 a1 = np.array([1,2,3])
   print(a1.shape) # 输出(3,),意思是一维数组,有3个数据

   a2 = np.array([[1,2,3],[4,5,6]])
   print(a2.shape) # 输出(2,3),意思是二位数组,23列

   a3 = np.array([
       [
           [1,2,3],
           [4,5,6]
       ],
       [
           [7,8,9],
           [10,11,12]
       ]
   ])
   print(a3.shape) # 输出(2,2,3),意思是三维数组,总共有2个块,每个元素是23列的

代码举例2:

二维情况
>>> import numpy as np
>>> y = np.array([[1,2,3],[4,5,6]])
>>> print(y)
[[1 2 3]
 [4 5 6]]
>>> print(y.shape)
(2, 3)
>>> print(y.shape[0])
2
>>> print(y.shape[1])
3
可以看到y是一个两行三列的二维数组,y.shape[0]代表行数,y.shape[1]代表列数。

三维情况
>>> x  = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[0,1,2]],[[3,4,5],[6,7,8]]])
>>>> print(x)
[[[1 2 3]
  [4 5 6]]

 [[7 8 9]
  [0 1 2]]

 [[3 4 5]
  [6 7 8]]]
>>> print(x.shape)
(3, 2, 3)
>>> print(x.shape[0])
3
>>> print(x.shape[1])
2
>>> print(x.shape[2])
3
可以看到x是一个包含了3个两行三列的二维数组的三维数组,x.shape[0]代表包含二维数组的个数,x.shape[1]表示二维数组的行数,x.shape[2]表示二维数组的列数。

总结
可以看到,shape[0]表示最外围的数组的维数,shape[1]表示次外围的数组的维数,数字不断增大,维数由外到内。

另外,我们还可以通过ndarray.reshape来重新修改数组的维数。示例代码如下:

 a1 = np.arange(12) #生成一个有12个数据的一维数组
   print(a1) 

   a2 = a1.reshape((3,4)) #变成一个2维数组,是34列的
   print(a2)

   a3 = a1.reshape((2,3,2)) #变成一个3维数组,总共有2块,每一块是22列的
   print(a3)

   a4 = a2.reshape((12,)) # 将a2的二维数组重新变成一个12列的1维数组,(12,)表示元组只有一个值时后面需要留空
   print(a4)

   a5 = a2.flatten() # 不管a2是几维数组,都将他变成一个一维数组
   print(a5)

简单总结:

  • reshape(1,12)#改变为二维数组,reshape()可以理解为参数有几个就是就为数组
  • shape结果里面有几个元素就代表是几维数组

ndarray.itemsize:

数组中每个元素占的大小,单位是字节。比如以下代码:

   a1 = np.array([1,2,3],dtype=np.int32)
   print(a1.itemsize) # 打印4,因为每个字节是8位,32/8=4个字节

Numpy数组操作

数组与数的计算:

在Python列表中,想要对列表中所有的元素都加一个数,要么采用map函数,要么循环整个列表进行操作。但是NumPy中的数组可以直接在数组上进行操作。示例代码如下:

import numpy as np
a1 = np.random.random((3,4))
print(a1)
# 如果想要在a1数组上所有元素都乘以10,那么可以通过以下来实现
a2 = a1*10#语法糖
print(a2)
# 也可以使用round让所有的元素只保留2位小数
a3 = a2.round(2)

以上例子是相乘,其实相加、相减、相除也都是类似的。

数组与数组的计算:

对应元素间的相关操作:

import numpy as np
a1 = np.arange(0,24).reshape((3,8))
a2 = np.random.randint(1,10,size=(3,8))
a3 = a1 * a2 #相加/相减/相除/相乘都是可以的 对应位置的元素进行相关
print(a1)
print(a2)
print(a3)

结果:

[[ 0  1  2  3  4  5  6  7]
 [ 8  9 10 11 12 13 14 15]
 [16 17 18 19 20 21 22 23]]
[[5 8 7 1 3 3 8 5]
 [1 2 4 3 7 5 3 8]
 [1 7 2 4 1 3 3 4]]
[[  0   8  14   3  12  15  48  35]
 [  8  18  40  33  84  65  42 120]
 [ 16 119  36  76  20  63  66  92]]

与行数相同并且只有1列的数组之间的运算:

import numpy as np
a1 = np.random.randint(10,20,size=(3,8)) #38列
a2 = np.random.randint(1,10,size=(3,1)) #31列
a3 = a1 - a2 #行数相同,且a2只有1列,能互相运算
print(a1)
print('='*30)
print(a2)
print('='*30)
print(a3)

结果:

[[11 17 14 14 16 19 11 16]
 [10 14 15 10 18 11 19 10]
 [19 19 18 17 17 18 18 10]]
==============================
[[4]
 [3]
 [1]]
==============================
[[ 7 13 10 10 12 15  7 12]
 [ 7 11 12  7 15  8 16  7]
 [18 18 17 16 16 17 17  9]]

观察结果不难发现,a1的每一列的每个元素都会减去a2对应每个元素的值


与列数相同并且只有1行的数组之间的运算:

import numpy as np
a1 = np.random.randint(10,20,size=(3,8)) #38列
a2 = np.random.randint(1,10,size=(1,8))
a3 = a1 - a2
print(a1)
print('='*30)
print(a2)
print('='*30)
print(a3)

结果:

[[19 10 10 10 19 11 16 11]
 [10 17 10 13 17 18 10 15]
 [14 10 14 11 16 18 15 17]]
==============================
[[2 4 1 9 6 3 3 8]]
==============================
[[17  6  9  1 13  8 13  3]
 [ 8 13  9  4 11 15  7  7]
 [12  6 13  2 10 15 12  9]]

观察结果不难发现,a1的每一行的每个元素都会减去a2对应每个元素的值

广播原则:

如果两个数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,则认为他们是广播兼容的。广播会在缺失和(或)长度为1的维度上进行。看以下案例分析:

  1. shape为(3,8,2)的数组能和(8,3)的数组进行运算吗?
    分析:不能,因为按照广播原则,从后面往前面数,(3,8,2)和(8,3)中的2和3不相等,所以不能进行运算。

  2. shape为(3,8,2)的数组能和(8,1)的数组进行运算吗?
    分析:能,因为按照广播原则,从后面往前面数,(3,8,2)和(8,1)中的2和1虽然不相等,但是因为有一方的长度为1,所以能参与运算。

  3. shape为(3,1,8)的数组能和(8,1)的数组进行运算吗?
    分析:能,因为按照广播原则,从后面往前面数,(3,1,4)和(8,1)中的4和1虽然不相等且1和8不相等,但是因为这两项中有一方的长度为1,所以能参与运算。

代码举例:
a1=np.array([1,2,3,4])
a2=np.random.randint(1,10,size=(8,1))
a3=a1+a2
print(a1)
print('='*30)
print(a2)
print('='*30)
print(a3)
print('='*30)
print(a3.shape)

结果:

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

数组形状的操作:

可以通过一些函数,非常方便的操作数组的形状。

reshape和resize方法:

  1. reshape是将数组转换成指定的形状,然后返回转换后的结果,对于原数组的形状是不会发生改变的。调用方式:
a1 = np.random.randint(0,10,size=(3,4))
a2 = a1.reshape((2,6)) #将修改后的结果返回,不会影响原数组本身
  1. resize是将数组转换成指定的形状,会直接修改数组本身。并不会返回任何值。调用方式:
a1 = np.random.randint(0,10,size=(3,4))
a1.resize((2,6)) #a1本身发生了改变

flatten和ravel方法:

两个方法都是将多维数组转换为一维数组,但是有以下不同:

  • flatten是将数组转换为一维数组后,然后将这个拷贝返回回去,所以后续对这个返回值进行修改不会影响之前的数组。
  • ravel是将数组转换为一维数组后,将这个视图(可以理解为引用)返回回去,所以后续对这个返回值进行修改会影响之前的数组。
x = np.array([[1, 2], [3, 4]])
x.flatten()[1] = 100 #此时的x[0]的位置元素还是1
x.ravel()[1] = 100 #此时x[0]的位置元素是100

不同数组的组合:

如果有多个数组想要组合在一起,也可以通过其中的一些函数来实现。

  1. vstack:将数组按垂直方向进行叠加。数组的列数必须相同才能叠加。示例代码如下:
a1 = np.random.randint(0,10,size=(3,5))
a2 = np.random.randint(0,10,size=(1,5))
a3 = np.vstack([a1,a2])
  1. hstack:将数组按水平方向进行叠加。数组的行必须相同才能叠加。示例代码如下:
a1 = np.random.randint(0,10,size=(3,2))
a2 = np.random.randint(0,10,size=(3,1))
a3 = np.hstack([a1,a2])

3.concatenate([],axis):将两个数组进行叠加,但是具体是按水平方向还是按垂直方向。则要看axis的参数,如果axis=0,那么代表的是往垂直方向(行)叠加,如果axis=1,那么代表的是往水平方向(列)上叠加,如果axis=None,那么会将两个数组组合成一个一维数组。需要注意的是,如果往水平方向上叠加,那么行必须相同,如果是往垂直方向叠加,那么列必须相同。示例代码如下:

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])
np.concatenate((a, b), axis=0)
# 结果:
array([[1, 2],
    [3, 4],
    [5, 6]])

np.concatenate((a, b.T), axis=1)
# 结果:
array([[1, 2, 5],
    [3, 4, 6]])

np.concatenate((a, b), axis=None)
# 结果:
array([1, 2, 3, 4, 5, 6])

数组的切割:

通过hsplit和vsplit以及array_split可以将一个数组进行切割。

  1. hsplit:按照垂直方向进行切割。用于指定分割成几列,可以使用数字来代表分成几部分,也可以使用数组来代表分割的地方。示例代码如下:
a1 = np.arange(16.0).reshape(4, 4)
np.hsplit(a1,2) #分割成两部分
>>> array([[ 0.,  1.],
     [ 4.,  5.],
     [ 8.,  9.],
     [12., 13.]]), array([[ 2.,  3.],
     [ 6.,  7.],
     [10., 11.],
     [14., 15.]])]

np.hsplit(a1,[1,2]) #代表在下标为1的地方切一刀,下标为2的地方切一刀,分成三部分
>>> [array([[ 0.],
     [ 4.],
     [ 8.],
     [12.]]), array([[ 1.],
     [ 5.],
     [ 9.],
     [13.]]), array([[ 2.,  3.],
     [ 6.,  7.],
     [10., 11.],
     [14., 15.]])]
  1. vsplit:按照水平方向进行切割。用于指定分割成几行,可以使用数字来代表分成几部分,也可以使用数组来代表分割的地方。示例代码如下:
np.vsplit(x,2) #代表按照行总共分成2个数组
>>> [array([[0., 1., 2., 3.],
     [4., 5., 6., 7.]]), array([[ 8.,  9., 10., 11.],
     [12., 13., 14., 15.]])]

np.vsplit(x,(1,2)) #代表按照行进行划分,在下标为1的地方和下标为2的地方分割
>>> [array([[0., 1., 2., 3.]]),
    array([[4., 5., 6., 7.]]),
    array([[ 8.,  9., 10., 11.],
           [12., 13., 14., 15.]])]
  1. split/array_split(array,indicate_or_seciont,axis):用于指定切割方式,在切割的时候需要指定是按照行还是按照列,axis=1代表按照列,axis=0代表按照行。示例代码如下:
np.array_split(x,2,axis=0) #按照垂直方向切割成2部分
>>> [array([[0., 1., 2., 3.],
     [4., 5., 6., 7.]]), array([[ 8.,  9., 10., 11.],
     [12., 13., 14., 15.]])]

数组(矩阵)转置和轴对换:

numpy中的数组其实就是线性代数中的矩阵。矩阵是可以进行转置的。ndarray有一个T属性,可以返回这个数组的转置的结果。示例代码如下:

a1 = np.arange(0,24).reshape((4,6))
a2 = a1.T
print(a2)

另外还有一个方法叫做transpose,这个方法返回的是一个View(可暂时理解为引用),也即修改返回值,会影响到原来数组。示例代码如下:

a1 = np.arange(0,24).reshape((4,6))
a2 = a1.transpose()

为什么要进行矩阵转置呢,有时候在做一些计算的时候需要用到。比如做矩阵的内积的时候。就必须将矩阵进行转置后再乘以之前的矩阵:

a1 = np.arange(0,24).reshape((4,6))
a2 = a1.T
print(a1)
print('='*30)
print(a2)
print('='*30)
print(a1.dot(a2))#dot函数返回两个矩阵的内积

结果:

[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]]
==============================
[[ 0  6 12 18]
 [ 1  7 13 19]
 [ 2  8 14 20]
 [ 3  9 15 21]
 [ 4 10 16 22]
 [ 5 11 17 23]]
==============================
[[  55  145  235  325]
 [ 145  451  757 1063]
 [ 235  757 1279 1801]
 [ 325 1063 1801 2539]]

Numpy索引和切片:

  1. 对一维数组的操作:
import numpy as np
#一维数组进行操作(与python列表切片用法相同)
a1=np.arange(10)#生成个1-9的一维数组
print(a1)
#进行索引操作
print(a1[4])
#进行切片操作
print(a1[4:6])

结果:

[0 1 2 3 4 5 6 7 8 9]
4
[4 5]
  1. 多维数组

索引和切片,索引是不连续的,切片是连续的,若使用逗号分隔,逗号前面是行,逗号后面是列,如果多维数组中只有一个值就是行(需要加中括号),切片无需再加中括号。

import numpy as np
a2=np.random.randint(0,10,size=(4,6))
print(a2)

结果:

[[4 9 9 0 0 9]
 [2 4 4 5 2 0]
 [9 5 4 0 8 5]
 [8 2 3 3 7 4]]
#获取上述矩阵的第一行
print(a2[0])
print('='*30)
#获取中间两行
print(a2[1:3])
print('='*30)
#获取第134print(a2[[0,2,3]])
print('='*30)
#获取第三行第二个数
print(a2[2,1])
print('='*30)
#获取不连续的两个数数
print(a2[[1,2],[2,3]])
print('='*30)
#获取23行,45列的数
print(a2[1:3,3:5])
print('='*30)
#获取列的相关操作:
#获取第一列
print(a2[:,0])
#获取第34print('='*30)
print(a2[:,2:3])

结果:

[ 0  6 12 18]
==============================
[[ 1  7 13 19]
 [ 2  8 14 20]]
==============================
[[ 0  6 12 18]
 [ 2  8 14 20]
 [ 3  9 15 21]]
==============================
8
==============================
[13 20]
==============================
[[19]
 [20]]
==============================
[0 1 2 3 4 5]
==============================
[[12]
 [13]
 [14]
 [15]
 [16]
 [17]]

总结:

  • 数组名[行,列],对于行,列各自又可以使用切片和索引操作
  • 常见操作如下:
    ([索引,索引],[[索引1,索引2],[索引3,索引4]],[:(行切片),:(列切片)],[[x(索引),y,z],[[x1(索引),y1],[x2,y2]…]])
    注意:a[[1,2],[2,3]]表示取第二行第三列和第三行第四列的两个数是不连续的,a[1:2,2:3]表示取第二行第三列和第三行第四列的所有数是连续的.

布尔索引:

布尔运算也是矢量的,比如以下代码:

a1 = np.arange(0,24).reshape((4,6))
print(a1<10) #会返回一个新的数组,这个数组中的值全部都是bool类型

结果:

[[ True  True  True  True  True  True]
 [ True  True  True  True False False]
 [False False False False False False]
 [False False False False False False]]
a1 = np.arange(0,24).reshape((4,6))
a2 = a1 < 10
print(a1[a2]) #这样就会在a1中把a2中为True的元素对应的位置的值提取出来

结果:

[0 1 2 3 4 5 6 7 8 9]

总结:其中布尔运算可以有!=、==、>、<、>=、<=以及&(与)和|(或)。

a1 = np.arange(0,24).reshape((4,6))
a2 = a1[(a1 < 5) | (a1 > 10)]
print(a2)

值的替换:

利用索引,也可以做一些值的替换。把满足条件的位置的值替换成其他的值。比如以下代码:

a1 = np.arange(0,24).reshape((4,6))
a1[3] = 0 #将第三行的所有值都替换成0
print(a1)

也可以使用条件索引来实现:

a1 = np.arange(0,24).reshape((4,6))
a1[a1 < 5] = 0 #将小于5的所有值全部都替换成0
print(a1)

还可以使用函数来实现:

# where函数:
a1 = np.arange(0,24).reshape((4,6))
a2 = np.where(a1 < 10,1,0) #把a1中所有小于10的数全部变成1,其余的变成0
print(a2)

深拷贝与浅拷贝

在操作数组的时候,它们的数据有时候拷贝进一个新的数组,有时候又不是。这经常是初学者感到困惑。下面有三种情况:

不拷贝:

a = np.arange(12)
b = a #这种情况不会进行拷贝
print(b is a) #返回True,说明b和a是相同的

View或者浅拷贝:

有些情况,会进行变量的拷贝,但是他们所指向的内存空间都是一样的,那么这种情况叫做浅拷贝,或者叫做View(视图)。比如以下代码:

a = np.arange(12)
c = a.view()
print(c is a) #返回False,说明c和a是两个不同的变量
c[0] = 100
print(a[0]) #打印100,说明对c上的改变,会影响a上面的值,说明他们指向的内存空间还是一样的,这种叫做浅拷贝,或者说是view

深拷贝:

将之前数据完完整整的拷贝一份放到另外一块内存空间中,这样就是两个完全不同的值了。示例代码如下:

a = np.arange(12)
d = a.copy()
print(d is a) #返回False,说明d和a是两个不同的变量
d[0] = 100
print(a[0]) #打印0,说明d和a指向的内存空间完全不同了。

例子:

像之前讲到的flatten和ravel就是这种情况,ravel返回的就是View,而flatten返回的就是深拷贝。

文件操作

操作CSV文件:

文件保存:

有时候我们有了一个数组,需要保存到文件中,那么可以使用np.savetxt来实现。相关的函数描述如下:

np.savetxt(frame, array, fmt='%.18e', delimiter=None)
* frame : 文件、字符串或产生器,可以是.gz或.bz2的压缩文件
* array : 存入文件的数组
* fmt : 写入文件的格式,例如:%d %.2f %.18e
* delimiter : 分割字符串,默认是任何空格

以下是使用的例子:

a = np.arange(100).reshape(5,20)
np.savetxt("a.csv",a,fmt="%d",delimiter=",")

读取文件:

有时候我们的数据是需要从文件中读取出来的,那么可以使用np.loadtxt来实现。相关的函数描述如下:

np.loadtxt(frame, dtype=np.float, delimiter=None, unpack=False)
* frame:文件、字符串或产生器,可以是.gz或.bz2的压缩文件。
* dtype:数据类型,可选。
* delimiter:分割字符串,默认是任何空格。
* skiprows:跳过前面x行。
* usecols:读取指定的列,用元组组合。
* unpack:如果True,读取出来的数组是转置后的。

np独有的存储解决方案:

numpy中还有一种独有的存储解决方案。文件名是以.npy或者npz结尾的。以下是存储和加载的函数。

  • 存储:np.save(fname,array)或np.savez(fname,array)。其中,前者函数的扩展名是.npy,后者的扩展名是.npz,后者是经过压缩的。
  • 加载:np.load(fname)。

CSV文件操作:

读取csv文件:

import csv

with open('stock.csv','r') as fp:
    reader = csv.reader(fp)
    titles = next(reader)#跳过第一行,next会向指针下移
    for x in reader:
        print(x)

这样操作,以后获取数据的时候,就要通过下表来获取数据。如果想要在获取数据的时候通过标题来获取。那么可以使用DictReader。示例代码如下:

with open( 'stock.csv' ,'r') as fp:
#使用DictReader创建的reader对象
#不会包含标题那行的数据
#reader是一个迭代器,遍历这个迭代器,返回来的是一个字典。
reader = csv.DictReader(fp)
for x in reader:
	value = { "name" :x [ 'secShortName ' ], ' volumn ' :x [ ' turnoverVol']}
	print(value)

写入数据到csv文件:

写入数据到csv文件,需要创建一个writer对象,主要用到两个方法。一个是writerow,这个是写入一行。一个是writerows,这个是写入多行。示例代码如下:

import csv

headers = ['name','age','classroom']
values = [
    ('zhiliao',18,'111'),
    ('wena',20,'222'),
    ('bbc',21,'111')
]
with open('test.csv','w',newline='') as fp:
    writer = csv.writer(fp)
    writer.writerow(headers)
    writer.writerows(values)

也可以使用字典的方式把数据写入进去。这时候就需要使用DictWriter了。示例代码如下:

import csv

headers = ['name','age','classroom']
values = [
    {"name":'wenn',"age":20,"classroom":'222'},
    {"name":'abc',"age":30,"classroom":'333'}
]
with open('test.csv','w',newline='') as fp:
    writer = csv.DictWriter(fp,headers)
    writer.writerow({'name':'zhiliao',"age":18,"classroom":'111'})
    writer.writerows(values)

注意:当需要写入表头:

writer =csv. DictWriter(fp, headers)
#写入表头数据的时候,需要调用writeheader方法
writer.witeheader ()

NAN和INF值处理

首先我们要知道这两个英文单词代表的什么意思:

  • NAN:Not A number,不是一个数字的意思,但它是属于浮点类型的,所以想要进行数据操作的时候需要注意它的类型。
  • INF:Infinity,代表的是无穷大的意思,也是属于浮点类型。np.inf表示正无穷大,-np.inf表示负无穷大,一般在出现除数为0的时候为无穷大。比如2/0。

NAN一些特点:

  • NAN和NAN不相等。比如np.NAN != np.NAN这个条件是成立的。
  • NAN和任何值做运算,结果都是NAN。

有些时候,特别是从文件中读取数据的时候,经常会出现一些缺失值。缺失值的出现会影响数据的处理。因此我们在做数据分析之前,先要对缺失值进行一些处理。处理的方式有多种,需要根据实际情况来做。一般有两种处理方式:删除缺失值,用其他值进行填充。

删除缺失值:

有时候,我们想要将数组中的NAN删掉,那么我们可以换一种思路,就是只提取不为NAN的值。示例代码如下:

# 1. 删除所有NAN的值,因为删除了值后数组将不知道该怎么变化,所以会被变成一维数组
data = np.random.randint(0,10,size=(3,5)).astype(np.float)
data[0,1] = np.nan
data = data[~np.isnan(data)] # 此时的data会没有nan,并且变成一个1维数组

# 2. 删除NAN所在的行
data = np.random.randint(0,10,size=(3,5)).astype(np.float)
# 将第(0,1)(1,2)两个值设置为NAN
data[[0,1],[1,2]] = np.NAN
# 获取哪些行有NAN
lines = np.where(np.isnan(data))[0]
# 使用delete方法删除指定的行,axis=0表示删除行,lines表示删除的行号
data1 = np.delete(data,lines,axis=0)

注意:
除了deiete用axis=0表示行以外,其他的大部分函数都是axis=1来表示行。

用其他值进行替代:

数学英语
5989
9032
7845
34NAN
NAN56
2356

如果想要求每门成绩的总分,以及每门成绩的平均分,那么就可以采用某些值替代。比如求总分,那么就可以把NAN替换成0,如果想要求平均分,那么就可以把NAN替换成其他值的平均值。示例代码如下:

scores = np.loadtxt("nan_scores.csv",skiprows=1,delimiter=",",encoding="utf-8",dtype=np.str)
scores[scores == ""] = np.NAN
scores = scores.astype(np.float)
# 1. 求出学生成绩的总分
scores1 = scores.copy()
socres1.sum(axis=1)

# 2. 求出每门成绩的平均分
scores2 = scores.copy()
for x in range(scores2.shape[1]):
    score = scores2[:,x]
    non_nan_score = score[score == score]
    score[score != score] = non_nan_score.mean()
print(scores2.mean(axis=0))

np.random模块

np.random为我们提供了许多获取随机数的函数。这里统一来学习一下。

np.random.seed:

用于指定随机数生成时所用算法开始的整数值,如果使用相同的seed()值,则每次生成的随即数都相同,如果不设置这个值,则系统根据时间来自己选择这个值,此时每次生成的随机数因时间差异而不同。一般没有特殊要求不用设置。以下代码:

np.random.seed(1)
print(np.random.rand()) # 打印0.417022004702574
print(np.random.rand()) # 打印其他的值,因为随机数种子只对下一次随机数的产生会有影响。

np.random.rand:

生成一个值为[0,1)之间的数组,形状由参数指定,如果没有参数,那么将返回一个随机值。示例代码如下:

data1 = np.random.rand(2,3,4) # 生成234列的数组,值从0-1之间
data2 = np.random.rand() #生成一个0-1之间的随机数

np.random.randn:

生成均值(μ)为0,标准差(σ)为1的标准正态分布的值。示例代码如下:

data = np.random.randn(2,3) #生成一个23列的数组,数组中的值都满足标准正太分布

np.random.randint:

生成指定范围内的随机数,并且可以通过size参数指定维度。示例代码如下:

data1 = np.random.randint(10,size=(3,5)) #生成值在0-10之间,35列的数组
data2 = np.random.randint(1,20,size=(3,6)) #生成值在1-20之间,36列的数组

np.random.choice:

从一个列表或者数组中,随机进行采样。或者是从指定的区间中进行采样,采样个数可以通过参数指定:

data = [4,65,6,3,5,73,23,5,6]
result1 = np.random.choice(data,size=(2,3)) #从data中随机采样,生成23列的数组
result2 = np.random.choice(data,3) #从data中随机采样3个数据形成一个一维数组
result3 = np.random.choice(10,3) #从0-10之间随机取3个值

np.random.shuffle:

把原来数组的元素的位置打乱。示例代码如下:

a = np.arange(10)
np.random.shuffle(a) #将a的元素的位置都会进行随机更换

更多:

更多的random模块的文档,请参考Numpy的官方文档:https://docs.scipy.org/doc/numpy/reference/routines.random.html

Axis理解

简单来说, 最外面的括号代表着 axis=0,依次往里的括号对应的 axis 的计数就依次加 1。什么意思呢?下面再来解释一下。
在这里插入图片描述
最外面的括号就是axis=0,里面两个子括号axis=1。 操作方式:如果指定轴进行相关的操作,那么他会使用轴下的每个直接子元素的第0个位置,第1个位置,第2个位置…分别进行相关的操作。
现在我们用刚刚理解的方式来做几个操作。比如现在有一个二维的数组:

x = np.array([[0,1],[2,3]])
  1. 求x数组在axis=0和axis=1两种情况下的和:
>>> x.sum(axis=0)
 array([2, 4])

为什么得到的是[2,4]呢,原因是我们按照axis=0的方式进行相加,那么就会把最外面轴下的所有直接子元素中的第0个位置进行相加,第1个位置进行相加…依此类推,得到的就是0+2以及2+3,然后进行相加,得到的结果就是[2,4]。

 >>> x.sum(axis=1)
 array([1, 5])

因为我们按照axis=1的方式进行相加,那么就会把轴为1里面的元素拿出来进行求和,得到的就是0,1,进行相加为1,以及2,3进行相加为5,所以最终结果就是[1,5]了。

  1. 用np.max求axis=0和axis=1两种情况下的最大值:
import  numpy as np
np.random.seed(100)
x = np.random.randint(0,10,size=(3,5))
print(x)
x.max(axis=0)

结果:

[[8 8 3 7 7]
 [0 4 2 5 2]
 [2 2 1 0 8]]
array([8, 8, 3, 7, 8])

因为我们是按照axis=0进行求最大值,那么就会在最外面轴里面找直接子元素,然后将每个子元素的第0个值放在一起求最大值,将第1个值放在一起求最大值,以此类推。而如果axis=1,那么就是拿到每个直接子元素,然后求每个子元素中的最大值:

x.max(axis=1)

结果:

array([8, 5, 8])
  1. 用np.delete在axis=0和axis=1两种情况下删除元素:

x = np.array([[0,1],[2,3]])

 >>> np.delete(x,0,axis=0)#
 array([[2, 3]])

np.delete是个例外。我们按照axis=0的方式进行删除,那么他会首先找到最外面的括号下的直接子元素中的第0个,然后删掉,剩下最后一行的数据。

>>> np.delete(x,0,axis=1)
 array([[1],
        [3]])

同理,如果我们按照axis=1进行删除,那么会把第一列的数据删掉。

对于delete函数:
numpy中的delete函数有三个参数:
numpy.delete(arr, obj, axis)
arr:需要处理的矩阵
obj:在什么位置处理
axis:这是一个可选参数,axis = None,1,0

axis=None:arr会先按行展开,然后按照obj,删除第obj-1(从0开始)位置的数,返回一个行矩阵。

axis = 0:arr按行删除

axis = 1:arr按列删除

三维以上数组:

在这里插入图片描述
在这里插入图片描述

通用函数

一元函数:

函数描述
np.abs绝对值
np.sqrt开根
np.square平方
np.exp计算指数(e^x)
np.log,np.log10,np.log2,np.log1p求以e为底,以10为低,以2为低,以(1+x)为底的对数
np.sign将数组中的值标签化,大于0的变成1,等于0的变成0,小于0的变成-1
np.ceil朝着无穷大的方向取整,比如5.1会变成6,-6.3会变成-6
np.floor朝着负无穷大方向取证,比如5.1会变成5,-6.3会变成-7
np.rint,np.round返回四舍五入后的值
np.modf将整数和小数分隔开来形成两个数组
np.isnan判断是否是nan
np.isinf判断是否是inf
np.cos,np.cosh,np.sin,np.sinh,np.tan,np.tanh三角函数
np.arccos,np.arcsin,np.arctan反三角函数

二元函数:

函数描述
np.add加法运算(即1+1=2),相当于+
np.subtract减法运算(即3-2=1),相当于-
np.negative负数运算(即-2),相当于加个负号
np.multiply乘法运算(即2*3=6),相当于*
np.divide除法运算(即3/2=1.5),相当于/
np.floor_divide取整运算,相当于//
np.mod取余运算,相当于%
greater,greater_equal,less,less_equal,equal,not_equal>,>=,<,<=,=,!=的函数表达式
logical_and&的函数表达式
logical_or|的函数表达式

聚合函数:

安全版本即元素值为NAN也不影响相应计算

函数名称NAN安全版本描述
np.sumnp.nansum计算元素的和
np.prodnp.nanprod计算元素的积
np.meannp.nanmean计算元素的平均值
np.stdnp.nanstd计算元素的标准差
np.varnp.nanvar计算元素的方差
np.minnp.nanmin计算元素的最小值
np.maxnp.nanmax计算元素的最大值
np.argminnp.nanargmin找出最小值的索引
np.argmaxnp.nanargmax找出最大值的索引
np.mediannp.nanmedian计算元素的中位数

使用np.sum或者是a.sum即可实现。并且在使用的时候,可以指定具体哪个轴。同样Python中也内置了sum函数,但是Python内置的sum函数执行效率没有np.sum那么高,可以通过以下代码测试了解到:

a = np.random.rand(1000000)
%timeit sum(a) #使用Python内置的sum函数求总和,看下所花费的时间
%timeit np.sum(a) #使用Numpy的sum函数求和,看下所花费的时间

结果:

73.3 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
899 µs ± 39.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

解释:

  • 对单行代码执行计时 :%timeit
  • 对多行代码执行计时:%%timeit:

要在ipython下才可以使用。(所以说Jupyter Notebook当然是可以用的,pycharm里的python环境也是jupyter Notebook的)
%timeit可以测量一行代码多次执行的时间
%%timeit可以测量多行代码多次执行的时间

布尔数组的函数:

函数名称描述
np.any验证任何一个元素是否为真
np.all验证所有元素是否为真

比如想看下数组中是不是所有元素都为0,那么可以通过以下代码来实现:

np.all(a==0) 
# 或者是
(a==0).all()

比如我们想要看数组中是否有等于0的数,那么可以通过以下代码来实现:

np.any(a==0)
# 或者是
(a==0).any()

排序:

  1. np.sort:指定轴进行排序。默认是使用数组的最后一个轴进行排序。
a = np.random.randint(0,10,size=(3,5))
b = np.sort(a) #按照行进行排序,因为最后一个轴是1,那么就是将最里面的元素进行排序。
c = np.sort(a,axis=0) #按照列进行排序,因为指定了axis=0
print(a)
print('='*30)
print(b)
print('='*30)
print(c)

结果:

[[1 6 5 9 2]
 [1 6 8 7 2]
 [8 5 5 3 8]]
==============================
[[1 2 5 6 9]
 [1 2 6 7 8]
 [3 5 5 8 8]]
==============================
[[1 5 5 3 2]
 [1 6 5 7 2]
 [8 6 8 9 8]]

还有ndarray.sort(),这个方法会直接影响到原来的数组,而不是返回一个新的排序后的数组。

  1. np.argsort:返回排序后的下标值。示例代码如下:
np.argsort(a) #默认也是使用最后的一个轴来进行排序。

结果:

array([[0, 4, 2, 1, 3],#这下标是原数组a的值,即原数组a的第一行第一个元素排在了第一位,第五个元素排在了第2...
       [0, 4, 1, 3, 2],
       [3, 1, 2, 0, 4]], dtype=int64)
  1. 降序排序:np.sort默认会采用升序排序。如果我们想采用降序排序。那么可以采用以下方案来实现:
 # 1. 使用负号
 -np.sort(-a)

 # 2. 使用sort和argsort以及take
 indexes = np.argsort(-a) #排序后的结果就是降序的
 np.take(a,indexes) #从a中根据下标提取相应的元素

其他函数补充:

  1. np.apply_along_axis:沿着某个轴执行指定的函数。示例代码如下:
 # 求数组a按行求均值,并且要去掉最大值和最小值。
 np.apply_along_axis(lambda x:x[(x != x.max()) & (x != x.min())].mean(),axis=1,arr=a)#会把数组的每一行传给x
  1. np.linspace:用来将指定区间内的值平均分成多少份。示例代码如下:
 # 将0-1分成12分,生成一个数组
 np.linspace(0,1,12)
  1. np.unique:返回数组中的唯一值。
 # 返回数组a中的唯一值,并且会返回每个唯一值出现的次数。
 np.unique(a,return_counts=True)

更多:

https://docs.scipy.org/doc/numpy/reference/index.html

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值