Numpy学习笔记

863 阅读11分钟

目标

  • 数组的性质
  • 应用Numpy实现数组的基本操作
  • 正态分布
  • 应用Numpy实现数组的逻辑运算
  • 应用Numpy实现数组的统计运算
  • 应用Numpy实现数组间的运算

1 Numpy介绍

1.1 Numpy的优势

Numpy(Numerical Python)是一个开源的Python科学计算库,本身是由C语言开发,用于快速处理任意维度的数组。Numpy支持常见的数组和矩阵操作。对于同样的数值计算任务,使用Numpy比直接使用Python要简洁的多。

  • Numpy优势

    思考:使用Python列表可以存储一维数组,通过列表的嵌套可以实现多维数组,那么为什么还需要使用Numpy的ndarray呢?

    在这里我们通过一段代码运行来体会到ndarray的好处:

      import random
      import time
      import numpy as np
      a = []
      for i in range(100000000):
          a.append(random.random())
      # 通过%time魔法方法, 查看当前行的代码运行一次所花费的时间
    
      %time sum1=sum(a)
      b=np.array(a)
    
      %time sum2=np.sum(b)
    

    其中第一个时间显示的是使用原生Python计算时间,第二个内容是使用numpy计算时间:

    CPU times: user 852 ms, sys: 262 ms, total: 1.11 s
      Wall time: 1.13 s
      CPU times: user 133 ms, sys: 653 µs, total: 133 ms
      Wall time: 134 ms
    

    Numpy专门针对ndarray的操作和运算进行了设计,所以数组的存储效率和输入输出性能远优于Python中的嵌套列表,数组越大,Numpy的优势就越明显。

  • 内存风格

    • list:分离式存储,存储内容多样化

    • ndarray:一体式存储,存储类型必须一样

      numpy内存地址

      从图中我们可以看出ndarray在存储数据的时候,数据与数据的地址都是连续的,这样就给使得批量操作数组元素时速度更快。

      这是因为ndarray中的所有元素的类型都是相同的,而Python列表中的元素类型是任意的,所以ndarray在存储元素时内存可以连续,而python原生list就只能通过寻址方式找到下一个元素。

    • ndarray支持并行化运算(向量化运算)

    • 效率远高于纯Python代码

      Numpy底层使用C语言编写,内部解除了GIL(全局解释器锁),其对数组的操作速度不受Python解释器的限制

2 ndarray的创建与数据类型

2.1 ndarray介绍

NumPy提供了一个N维数组类型ndarray,它描述了相同类型的“items”的集合。具有矢量算术运算能力和复杂的广播能力,并具有执行速度快和节省空间的特点

注:ndarray的下标从0开始,且数组里的所有元素必须是相同类型

  • 属性&形状

    属性名字属性解释
    ndarray.shape数组维度的元组
    ndarray.ndim数组维数
    ndarray.size数组中的元素数量
    ndarray.itemsize一个数组元素的长度(字节)
    ndarray.dtype数组元素的类型
    • 创建数组

      # 创建不同形状的数组
      a = np.array([[1,2,3],[4,5,6]])
      b = np.array([1,2,3,4])
      c = np.array([[[1,2,3],[4,5,6]],[[1,2,3],[4,5,6]]])
      

      打印出形状:

      a.shape	>>> (2,3) # 二维数组
      b.shape	>>> (4,) # 一维数组
      c.shape	>>> (2,2,3) # 三维数组
      
  • 类型

    dtype是numpy.dtype类型,先看看对于数组来说都有哪些类型

    名称描述简写
    np.bool用一个字节存储的布尔类型(True或False)‘b’
    np.int8一个字节大小,-128 至 127'i'
    np.int16整数,-32768 至 32767'i2'
    np.int32整数,-2^31^ 至 2^32^ -1'i4'
    np.int64整数,-2^63^ 至 2^63^ - 1'i8'
    np.uint8无符号整数,0 至 255'u'
    np.uint16无符号整数,0 至 65535'u2'
    np.uint32无符号整数,0 至 2 ** 32 - 1'u4'
    np.uint64无符号整数,0 至 2 ** 64 - 1'u8'
    np.float16半精度浮点数:16位,正负号1位,指数5位,精度10位'f2'
    np.float32单精度浮点数:32位,正负号1位,指数8位,精度23位'f4'
    np.float64双精度浮点数:64位,正负号1位,指数11位,精度52位'f8'
    np.complex64复数,分别用两个32位浮点数表示实部和虚部'c8'
    np.complex128复数,分别用两个64位浮点数表示实部和虚部'c16'
    np.object_python对象'O'
    np.string_字符串'S'
    np.unicode_unicode类型'U'
    • 创建数组的时候指定类型

      a = np.array([[1, 2, 3],[4, 5, 6]], dtype=np.float32)
      a.dtype
      dtype('float32')
      
      arr = np.array(['python', 'tensorflow', 'scikit-learn', 'numpy'], dtype = np.string_)
      arr
      array([b'python', b'tensorflow', b'scikit-learn', b'numpy'], dtype='|S12')
      

      注意:若不指定,整数默认int64,小数默认float64

2.2 ndarray的创建

  • 生成0和1的数组

    np.ones(shape[, dtype, order])

    np.ones_like(a[, dtype, order, subok])

    np.zeros(shape[, dtype, order])

    np.zeros_like(a[, dtype, order, subok])

    zero = np.zeros([3, 4])
    array([[ 0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.]])
    
  • 从现有数组生成

    np.array(object[, dtype, copy, order, subok, ndmin])

    np.asarray(a[, dtype, order])

    a = np.array([[1,2,3],[4,5,6]])
    # 从现有的数组当中创建
    a1 = np.array(a)
    # 相当于索引的形式,并没有真正的创建一个新的
    a2 = np.asarray(a)
    
  • 生成固定范围的数组

    np.linspace (start, stop, num, endpoint):生成等间隔的序列

    numpy.arange(start,stop, step, dtype):创建等差数组-指定步长

    numpy.logspace(start,stop, num):创建等比数列

    numpy.shuffle():将打乱数组序列(类似于洗牌)

    参数说明:

    start 序列的起始值
    stop 序列的终止值,
    num 要生成的等间隔样例数量,默认为50
    endpoint 序列中是否包含stop值,默认为ture
    

    举例:

    # 生成等间隔的数组
    np.linspace(0, 100, 11)
    # 结果
    array([  0.,  10.,  20.,  30.,  40.,  50.,  60.,  70.,  80.,  90., 100.])
    
    # np.arange()
    np.arange(10, 50, 2)
    # 结果
    array([10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48])
    
    # np.logspace():生成10^x
    np.logspace(0, 2, 3)
    # 结果
    array([  1.,  10., 100.])
    
    # np.shuffle(np.arange(15))
    [ 5  8  1  7  4  0 12  9 11  2 13 14 10  3  6]
    
  • 生成随机数组

    np.random模块

    • 均匀分布

      np.random.rand(d0, d1, ..., dn),返回**[0.0,1.0)**内的一组均匀分布的数

      np.random.uniform(*low=0.0*, *high=1.0*, *size=None*),功能:从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开,即包含low,不包含high。low: 采样下界,float类型,默认值为0;high: 采样上界,float类型,默认值为1;size: 输出样本数目,为int或元组(tuple)类型,例如,size=(m,n,k), 则输出mnk个样本,缺省时输出1个值。返回值:ndarray类型,其形状和参数size中描述一致。

      np.random.randint(low, high=None, size=None, dtype='l')。从一个均匀分布中随机采样,生成一个整数或N维整数数组,取数范围:若high不为None时,取[low,high)之间随机整数,否则取值[0,low)之间随机整数。

      # 生成均匀分布的随机数
      x1 = np.random.uniform(-1, 1, 100000000)
      # 结果
      array([ 0.22411206,  0.31414671,  0.85655613, ..., -0.92972446, 0.95985223,  0.23197723])
      
    • 正态分布

      正态分布是一种概率分布。正态分布是具有两个参数μ和σ的连续型随机变量的分布,第一参数μ是服从正态分布的随机变量的均值,第二个参数σ是此随机变量的方差,所以正态分布记作N(μ,σ )

      正态分布
      • 特点

        μ决定了其位置,其标准差σ决定了分布的幅度。当μ = 0,σ = 1时的正态分布是标准正态分布。

        正态分布公式
      • 方差

        是在概率论和统计方差衡量一组数据时离散程度的度量

        方差公式

        其中M为平均值,n为数据总个数,S为标准差,S^2可以理解一个整体为方差。

        标准差公式

        标准差和方差的意义:

        可以理解成数据的一个离散程度的衡量

        离散程度
      • 创建

        np.random.randn(d0, d1, …, dn)。从标准正态分布中返回一个或多个样本值

        **np.random.normal(*loc=0.0*, *scale=1.0*, *size=None*)**loc:float此概率分布的均值(对应着整个分布的中心centre)。scale:float此概率分布的标准差(对应于分布的宽度,scale越大越矮胖,scale越小,越瘦高)。size:int or tuple of ints 输出的shape,默认为None,只输出一个值。

        np.random.standard_normal(size=None)。返回指定形状的标准正态分布的数组。

        案例:随机生成8只股票2周的交易日涨幅数据。

        分析:8只股票,**两周(10天)**的涨跌幅数据,如何获取?

        • 两周的交易日数量为:2 X 5 =10
        • 随机生成涨跌幅在某个正态分布内,比如均值0,方差1
        # 创建符合正态分布的8只股票10天的涨跌幅数据
        stock_change = np.random.normal(0, 1, (8, 10))
        # 结果
        array([[-0.03862668, -1.46128096, -0.75596237,  0.89737022, -1.86978433,
                 0.38845392,  1.14856354, -1.10912275,  1.28900021, -0.86801677],
               [ 1.8701446 ,  0.50407342, -0.74396489, -1.69641331, -0.89969227,
                 2.42459765,  0.78987443, -0.82935223,  0.82546455,  0.40524289],
               [-1.7365776 , -0.81501515, -1.08974743, -1.4138561 ,  0.85495155,
                 0.30076624, -2.0813311 ,  1.52886975,  0.62945643, -1.48714683],
               [-1.12084983, -0.63070289, -0.20677245, -0.49096973, -0.40551104,
                -0.46320893, -0.73190969,  0.00392486,  2.524498  ,  0.25843191],
               [ 0.05001371,  0.52267878,  1.31974783,  0.64840953,  1.56360431,
                -0.79233575,  1.47275167,  0.61070343, -0.33247221, -0.57492172],
               [ 0.7092757 ,  0.00928591,  0.27008373,  0.79050681, -0.95887888,
                 0.03661459,  0.81303927, -0.54139691,  0.69623969,  0.06570421],
               [-1.40481949,  0.49151272,  1.01992235,  1.89152928, -0.76415623,
                 0.3100241 ,  1.27156806, -1.76199057, -0.77419965, -0.17574386],
               [-0.21745814, -1.78433763, -0.7953073 ,  0.4765644 , -0.2589575 ,
                 0.97014013,  1.67321489,  1.73370987,  0.29886514,  1.27186522]])
        
  • 形状修改

    让刚才的股票行、日期列反过来,变成日期行,股票列

    • ndarray.reshape(shape[, order]) Returns an array containing the same data with a new shape.

      # 在转换形状的时候,一定要注意数组的元素匹配
      stock_change.reshape([10, 8])
      stock_change.reshape([-1,20])  # 数组的形状被修改为: (4, 20), -1: 表示通过待计算
      
    • ndarray.resize(new_shape[, refcheck]) Change shape and size of array in-place.

      stock_change.resize([10, 8])
      
    • ndarray.T 数组的转置

      stock_change.shape
      (10, 8)
      stock_change.T.shape
      (8, 10)
      

3 ndarray的索引、切片

3.1 一维数组的索引与切片

与Python的列表索引功能相似

  • 示例代码:

    # 一维数组
    arr1 = np.arange(10)
    print(arr1)
    print(arr1[2:5])
    # 结果
    [0 1 2 3 4 5 6 7 8 9]
    [2 3 4]
    
#### 3.2 多维数组的索引与切片

> arr[r1:r2, c1:c2]
>
> arr[1,1] 等价 arr[1][1]
>
> [:] 代表某个维度的数据

- 示例代码:

  ```python
  # 多维数组
  arr2 = np.arange(12).reshape(3,4)
  print(arr2)
  print(arr2[1])
  print(arr2[0:2, 2:])
print(arr2[:, 1:3])
  # 结果
[[ 0  1  2  3]
   [ 4  5  6  7]
   [ 8  9 10 11]]
  
  [4 5 6 7]
  
  [[2 3]
   [6 7]]
  
  [[ 1  2]
   [ 5  6]
   [ 9 10]]

3.3 条件索引

布尔值多维数组:arr[condition],condition也可以是多个条件组合。

注意,多个条件组合要使用 & | 连接,而不是Python的 and or

  • 示例代码

    # 找出 data_arr 中 2005年后的数据
    data_arr = np.random.rand(3,3)
    print(data_arr)
    
    year_arr = np.array([[2000, 2001, 2000],
                         [2005, 2002, 2009],
                         [2001, 2003, 2010]])
    filtered_arr = data_arr[(year_arr <= 2005) & (year_arr % 2 == 0)]
    print(filtered_arr)
    # 结果
    [[ 0.53514038  0.93893429  0.1087513 ]
     [ 0.32076215  0.39820313  0.89765765]
     [ 0.6572177   0.71284822  0.15108756]]
    
    [ 0.53514038  0.1087513   0.39820313]
    

4 ndarray运算

4.1 计算函数

  1. ceil(): 向上最接近的整数,参数是 number 或 array
  2. floor(): 向下最接近的整数,参数是 number 或 array
  3. rint(): 四舍五入,参数是 number 或 array
  4. isnan(): 判断元素是否为 NaN(Not a Number),参数是 number 或 array
  5. multiply(): 元素相乘,参数是 number 或 array
  6. divide(): 元素相除,参数是 number 或 array
  7. abs():元素的绝对值,参数是 number 或 array
  8. where(condition, x, y): 三元运算符,x if condition else y,复合逻辑需要结合np.logical_and和np.logical_or使用
# 重新生成8只股票10个交易日的涨跌幅数据
stock_change = np.random.normal(0, 1, (8, 10))
stock_change = stock_change[0:5, 0:5]
# 逻辑判断, 如果涨跌幅大于0.5就标记为True 否则为False
stock_change > 0.5
array([[ True, False, False,  True, False],
       [ True,  True, False, False, False],
       [ True, False,  True, False,  True],
       [False,  True, False, False, False],
       [False, False, False,  True,  True]])
# BOOL赋值, 将满足条件的设置为指定的值-布尔索引
stock_change[stock_change > 0.5] = 1
array([[ 1.        , -0.72404879, -1.33045773,  1.        ,  0.3869043 ],
       [ 1.        ,  1.        ,  0.20815446, -1.67860823,  0.06612823],
       [ 1.        ,  0.42753488,  1.        , -0.24375089,  1.        ],
       [-0.971945  ,  1.        , -0.95444661, -0.2602084 , -0.48736497],
       [-0.32183056, -0.92544956, -0.42126604,  1.        ,  1.        ]])

# 判断前四个股票前四天的涨跌幅 大于0的置为1,否则为0
temp = stock_change[:4, :4]
np.where(temp > 0, 1, 0)

# 判断前四个股票前四天的涨跌幅 大于0.5并且小于1的,换为1,否则为0
np.where(np.logical_and(temp > 0.5, temp < 1), 1, 0)
# 判断前四个股票前四天的涨跌幅 大于0.5或者小于-0.5的,换为1,否则为0
np.where(np.logical_or(temp > 0.5, temp < -0.5), 1, 0)

4.2 判断函数

  1. np.any(): 至少有一个元素满足指定条件,返回True
  2. np.all(): 所有的元素满足指定条件,返回True
# 判断stock_change[0:2, 0:5]是否全是上涨的
>>> np.all(stock_change[0:2, 0:5] > 0)
False
# 判断前5只股票这段期间是否有上涨的
>>> np.any(stock_change[0:5, :] > 0 )
True

4.3 统计运算

在数据挖掘/机器学习领域,统计指标的值也是我们分析问题的一种方式。常用的指标如下:

  1. np.mean(), np.sum():所有元素的平均值,所有元素的和,参数是 number 或 array
  2. np.max(), np.min():所有元素的最大值,所有元素的最小值,参数是 number 或 array
  3. np.std(), np.var():所有元素的标准差,所有元素的方差,参数是 number 或 array
  4. np.argmax(), np.argmin():最大值的下标索引值,最小值的下标索引值,参数是 number 或 array
  5. np.cumsum(), np.cumprod():返回一个一维数组,每个元素都是之前所有元素的 累加和 和 累乘积,参数是 number 或 array
  6. 多维数组默认统计全部维度,axis参数可以按指定轴心统计,值为0则按列统计,值为1则按行统计。

案例:股票涨跌幅统计运算

进行统计的时候,axis 轴的取值并不一定,Numpy中不同的API轴的值都不一样,在这里,axis 0代表列, axis 1代表行去进行统计

# 接下来对于这4只股票的4天数据,进行一些统计运算
# 指定行 去统计
print("前四只股票前四天的最大涨幅{}".format(np.max(temp, axis=1)))
# 使用min, std, mean
print("前四只股票前四天的最大跌幅{}".format(np.min(temp, axis=1)))
print("前四只股票前四天的波动程度{}".format(np.std(temp, axis=1)))
print("前四只股票前四天的平均涨跌幅{}".format(np.mean(temp, axis=1)))

如果需要统计出哪一只股票在某个交易日的涨幅最大或者最小?

np.argmax(temp, axis=)— 最大元素对应的下标

np.argmin(temp, axis=) — 最小元素对应的下标

# 获取股票指定哪一天的涨幅最大
print("前四只股票前四天内涨幅最大{}".format(np.argmax(temp, axis=1)))
print("前四天一天内涨幅最大的股票{}".format(np.argmax(temp, axis=0)))

4.4 去重排序函数

np.unique():找到唯一值并返回排序结果,类似于Python的set集合

arr = np.array([[1, 2, 1], [2, 3, 4]])
print(arr)
print(np.unique(arr))
# 结果
[[1 2 1]
 [2 3 4]]

[1 2 3 4]

关于Numpy的总结就到这里,接下来我们一起总结Pandas的使用。

好了,今天就到这里,拜了个拜~