学习python数据分析 二 numpy(1)

NumPy的前身是Numeric。Numeric最早发布于1995年,如今已经废弃了。由于种种原因,不
管是Numeric还是NumPy,都没能进入Python标准库,不过单独安装NumPy也很方便。

为什么使用numpy

对于同样的数值计算任务,使用NumPy要比直接编写Python代码便捷得多。这是因为NumPy
能够直接对数组和矩阵进行操作,可以省略很多循环语句,其众多的数学函数也会让编写代码的
工作轻松许多。

NumPy的底层算法在设计时就有着优异的性能,并且经受住了时间的考验。
NumPy中数组的存储效率和输入输出性能均远远优于Python中等价的基本数据结构(如嵌套
的list容器)。其能够提升的性能是与数组中元素的数目成比例的。对于大型数组的运算,使用
NumPy的确很有优势。对于TB级的大文件,NumPy使用内存映射文件来处理,以达到最优的数
据读写性能。

不过,NumPy数组的通用性不及Python提供的list容器,这是其不足之处。因此在科
学计算之外的领域,NumPy的优势也就不那么明显了。

NumPy的大部分代码都是用C语言写成的,这使得NumPy比纯Python代码高效得多。NumPy
同样支持C语言的API,并且允许在C源代码上做更多的功能拓展。

numpy有那些功能?

  • 多维数组对象:ndarray,是 NumPy 中最重要的类,提供了高效的数值运算、广播(broadcasting)功能以及灵活的索引方式等功能。

  • 通用函数:ufunc,是一种对ndarray中的数据执行元素级运算的函数,支持矢量化计算,即对数组中的每个元素进行操作,比起纯Python实现的循环计算,运算速度更快。

  • 数据类型和类型转换:NumPy 支持更多的数据类型比 Python 自带的数据类型,如浮点型、整型、布尔型、复数型等,此外,NumPy 还支持用户自定义数据类型。

  • 数学函数库:NumPy 包括了大量的数学函数库,如傅里叶变换、随机数生成、线性代数运算、傅里叶变换、数学常数等。

  • 索引、切片和迭代:NumPy 提供了灵活的索引方式,可以使用整数、切片以及布尔值索引、花式索引、混合索引等方式,方便地对多维数组进行切片和迭代。

  • 广播:NumPy 支持广播(broadcasting),即对不同形状的数组进行算术运算的能力。

  • 输入输出:NumPy 提供了一些文件输入和输出的函数,如读写磁盘上的文本文件和二进制文件。

  • 线性代数:NumPy 提供了线性代数函数库,可以实现向量和矩阵的操作,如求解线性方程组、求逆矩阵、特征值分解、奇异值分解等。

安装

笔者这里使用的JupyterLab
执行

1
pip install numpy

完事后可以测试下

1
2
from numpy import *
array([1,2,3])

1
如何可以正常输出下面的数组应该就安装完成了。

核心概念-ndarray

多维数组,我们后面的学习基本上就是围绕他来。

如何创建一个ndarray?

  1. arange 首先就是我们前面看到的arange函数,这个函数将创建从0到我们传参的序列数组

    1
    2
    3
    4
    arange(20)

    array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
    17, 18, 19])
  2. array 直接列举数组元素

    1
    2
    3
    array([2,3,5,6])

    array([2, 3, 5, 6])
  3. zeros 给出指定长度的数组,元素全部是0

    1
    2
    3
    zeros(10)

    array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
  4. ones给出指定长度的数组,元素全部是1

    1
    2
    3
    ones(10)

    array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
  5. empty生成随机值

    1
    2
    3
    4
    empty(5)

    array([4.66227277e-310, 0.00000000e+000, 4.66297182e-310, 4.66297181e-310,
    2.37151510e-322])
  6. eye 创建矩阵,对角线元素是1,其他是0

    1
    2
    3
    4
    5
    6
    eye(4)

    array([[1., 0., 0., 0.],
    [0., 1., 0., 0.],
    [0., 0., 1., 0.],
    [0., 0., 0., 1.]])

多维数组也是一样创建

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

也可以使用reshape将现有的一维数组改变形状,注意这里reshape接受若干参数,这些参数要求乘积等于原数组的长度。

1
2
3
4
5
6
7
8
9
arange(30).reshape(2,3,5)

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

这样原来的0-29就被分为了三维的数组

ndarray的数据类型

如何查看ndarray的数据类型呢?ndarray包含dtype变量,比如我们查看前面的eye(4)的dtype

1
2
3
eye(4).dtype

dtype('float64')

都有那些dtype呢?

  • bool用一位存储的布尔类型(值为TRUE或FALSE)
  • inti由所在平台决定其精度的整数(一般为int32或int64)
  • int8整数,范围为-128至127
  • int16整数,范围为-32768至32767
  • int32整数,范围为2^31至2^31-1
  • int64整数,范围为-2^63至2^63-1
  • uint8无符号整数,范围为0至255
  • uint16无符号整数,范围为0至65535
  • uint32无符号整数,范围为0至2^32-1
  • uint64无符号整数,范围为0至2^64-1
  • float16半精度浮点数(16位):其中用1位表示正负号,5位表示指数,10位表示尾数
  • float32单精度浮点数(32位):其中用1位表示正负号,8位表示指数,23位表示尾数
  • float64或float双精度浮点数(64位):其中用1位表示正负号,11位表示指数,52位表示尾数
  • complex64复数,分别用两个32位浮点数表示实部和虚部
  • complex128或complex复数,分别用两个64位浮点数表示实部和虚部

实际上我们可以在创建ndarray时指定 数据类型

1
2
3
array([2,3,4],dtype=int16)

array([2, 3, 4], dtype=int16)

ndarray的内存占用

1
2
3
a=eye(10)

a.itemsize

可以查看单个元素的占用空间。

如果想要查看ndarray占用的总空间,可以:

1
2
a= eye(10)
a.nbytes

访问

如何访问我们创建好的ndarray呢?

非常类似python原生

1
2
3
arange(10)[2:8]

array([2, 3, 4, 5, 6, 7])

同样的默认参数为数组起始点

1
2
3
arange(10)[:7]

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

也可以步长读取数据

1
2
3
arange(10)[:7:2]

array([0, 2, 4, 6])

还能使用负数翻转数组

1
2
3
arange(10)[::-1]

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

那么多维数组呢?
我们以arange(30).reshape(2,3,5)为例

1
a=arange(30).reshape(2,3,5)

这里使用[x,y,z]类似这样的模式,xyz分别是1维2维3维的索引,比如

1
2
3
a[0,1,0]

5

比如我们想某个维度全部匹配,这里用: 代替:

1
2
3
4
5
6
7
a[0,:,0]

array([ 0, 5, 10])

a[0,0,:]

array([0, 1, 2, 3, 4])

如果有多个冒号,可以用…代替

1
2
3
4
5
a[0,...]

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

改变数组维度

包括前面提到的reshape操作,numpy提供很多方法供你修改数组的维度。

展开

  1. ravel
    我们前面生成的多维数组a可以用这种方式展开

    1
    2
    3
    4
    a.ravel()

    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])
  2. flatten

和ravel非常类似,只不过flatten会创建一个新的数组,而ravel是原数组的视图(并未修改原数组)。下面这个例子可以证明

1
2
3
4
5
6
7
8
9
10
11
12
13
b1 = a.ravel()
b2 = a.flatten()
b1[0]=100
b2[0]=10000
a

array([[[100, 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]]])

由于shape是ndarray的一个元组,所以修改元组的方式也可以修改数组的维度

1
2
3
4
5
6
a.shape=(3,10)
a

array([[100, 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]])
  1. transpose 转置
    行转列,列转行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    a.transpose()

    array([[100, 10, 20],
    [ 1, 11, 21],
    [ 2, 12, 22],
    [ 3, 13, 23],
    [ 4, 14, 24],
    [ 5, 15, 25],
    [ 6, 16, 26],
    [ 7, 17, 27],
    [ 8, 18, 28],
    [ 9, 19, 29]])

组合

有时我们需要将多个ndarray组合为一个新的ndarray

  1. 一维合并/水平合并
    hstack 要求 两个数组高度相同,做1维合并 ,column_stack类似

    1
    2
    3
    4
    5
    6
    a = arange(9).reshape(3,3)
    c = arange(15).reshape(3,5)
    hstack((a,c))
    array([[ 0, 1, 2, 0, 1, 2, 3, 4],
    [ 3, 4, 5, 5, 6, 7, 8, 9],
    [ 6, 7, 8, 10, 11, 12, 13, 14]])
  2. 二维合并/垂直合并

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    a = arange(9).reshape(3,3)
    d = arange(15).reshape(5,3)
    vstack((a,d))

    array([[ 0, 1, 2],
    [ 3, 4, 5],
    [ 6, 7, 8],
    [ 0, 1, 2],
    [ 3, 4, 5],
    [ 6, 7, 8],
    [ 9, 10, 11],
    [12, 13, 14]])
  3. 三维合并/深度合并
    dstack做三维合并

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    a3 = arange(30).reshape(2,3,5)
    b3 = arange(36).reshape(2,3,6)
    dstack((a3,b3))

    array([[[ 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5],
    [ 5, 6, 7, 8, 9, 6, 7, 8, 9, 10, 11],
    [10, 11, 12, 13, 14, 12, 13, 14, 15, 16, 17]],

    [[15, 16, 17, 18, 19, 18, 19, 20, 21, 22, 23],
    [20, 21, 22, 23, 24, 24, 25, 26, 27, 28, 29],
    [25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]]])
  4. 自由合并
    concatenate ,其中参数 axis 指定堆叠轴,除了堆叠轴外,其他轴必须维度完全相同。

    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
    a5=arange(100).reshape(2,2,5,5)
    b5=arange(120).reshape(2,2,5,6)
    concatenate((a5,b5),axis=3)

    array([[[[ 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5],
    [ 5, 6, 7, 8, 9, 6, 7, 8, 9, 10, 11],
    [ 10, 11, 12, 13, 14, 12, 13, 14, 15, 16, 17],
    [ 15, 16, 17, 18, 19, 18, 19, 20, 21, 22, 23],
    [ 20, 21, 22, 23, 24, 24, 25, 26, 27, 28, 29]],

    [[ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35],
    [ 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 41],
    [ 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47],
    [ 40, 41, 42, 43, 44, 48, 49, 50, 51, 52, 53],
    [ 45, 46, 47, 48, 49, 54, 55, 56, 57, 58, 59]]],


    [[[ 50, 51, 52, 53, 54, 60, 61, 62, 63, 64, 65],
    [ 55, 56, 57, 58, 59, 66, 67, 68, 69, 70, 71],
    [ 60, 61, 62, 63, 64, 72, 73, 74, 75, 76, 77],
    [ 65, 66, 67, 68, 69, 78, 79, 80, 81, 82, 83],
    [ 70, 71, 72, 73, 74, 84, 85, 86, 87, 88, 89]],

    [[ 75, 76, 77, 78, 79, 90, 91, 92, 93, 94, 95],
    [ 80, 81, 82, 83, 84, 96, 97, 98, 99, 100, 101],
    [ 85, 86, 87, 88, 89, 102, 103, 104, 105, 106, 107],
    [ 90, 91, 92, 93, 94, 108, 109, 110, 111, 112, 113],
    [ 95, 96, 97, 98, 99, 114, 115, 116, 117, 118, 119]]]])

分割

将单个ndarray分成多个ndarray,是前面的合并操作的逆操作。

  1. 第一维度/水平分割
    hsplit传入分割的片数,要求能够被第一维度元素个数整除。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    a=arange(30).reshape(5,6)
    hsplit(a,2)

    [array([[ 0, 1, 2],
    [ 6, 7, 8],
    [12, 13, 14],
    [18, 19, 20],
    [24, 25, 26]]),
    array([[ 3, 4, 5],
    [ 9, 10, 11],
    [15, 16, 17],
    [21, 22, 23],
    [27, 28, 29]])]
  2. 第二维度/垂直分割
    vsplit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    a=arange(30).reshape(6,5)
    vsplit(a,2)

    [array([[ 0, 1, 2, 3, 4],
    [ 5, 6, 7, 8, 9],
    [10, 11, 12, 13, 14]]),
    array([[15, 16, 17, 18, 19],
    [20, 21, 22, 23, 24],
    [25, 26, 27, 28, 29]])]
  3. 第三维度/深度分割

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    a=arange(30).reshape(5,3,2)
    dsplit(a,2)

    [array([[[ 0],
    [ 2],
    [ 4]],

    [[ 6],
    [ 8],
    [10]],

    [[12],
    [14],
    [16]],

    [[18],
    [20],
    [22]],

    [[24],
    [26],
    [28]]]),
    array([[[ 1],
    [ 3],
    [ 5]],

    [[ 7],
    [ 9],
    [11]],

    [[13],
    [15],
    [17]],

    [[19],
    [21],
    [23]],

    [[25],
    [27],
    [29]]])]
  4. 深度分割

    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
    a=arange(100).reshape(4,5,5)
    split(a, 2, axis=0)

    [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, 32, 33, 34],
    [35, 36, 37, 38, 39],
    [40, 41, 42, 43, 44],
    [45, 46, 47, 48, 49]]]),
    array([[[50, 51, 52, 53, 54],
    [55, 56, 57, 58, 59],
    [60, 61, 62, 63, 64],
    [65, 66, 67, 68, 69],
    [70, 71, 72, 73, 74]],

    [[75, 76, 77, 78, 79],
    [80, 81, 82, 83, 84],
    [85, 86, 87, 88, 89],
    [90, 91, 92, 93, 94],
    [95, 96, 97, 98, 99]]])]

读写

numpy主要支持两种格式保存数据:1. npy 2. csv

npy

该文件格式是专门为NumPy数组设计的,可以保留数组的所有信息,包括数据类型、维度、形状、元素等。这使得.npy文件的读写速度非常快,并且保存的数据非常紧凑。同时,由于.npy文件是二进制格式,因此它不易于通过文本编辑器进行查看和编辑。

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np

# 创建一个NumPy数组
arr = np.array([1, 2, 3, 4, 5])

# 保存数组到文件
np.save('my_array', arr)

# 从文件中读取数组
new_arr = np.load('my_array.npy')

print(new_arr) # 输出 [1 2 3 4 5]

csv

savetext函数则将NumPy数组保存为文本文件,可以通过指定分隔符、精度等参数来控制保存的格式。该函数可以将NumPy数组转换为类似于CSV(逗号分隔值)格式的文本文件,每行表示数组中的一维数据,不同列之间使用指定的分隔符进行分割。由于文本文件的格式是易于读写的,因此savetxt()函数可以用于将NumPy数组保存为可以被其他程序读取和处理的标准格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np

# 创建一个NumPy数组
arr = np.array([[1, 2, 3], [4, 5, 6]])

# 使用save()函数保存数组到文件
np.save('my_array', arr)

# 使用savetxt()函数将数组保存为文本文件
np.savetxt('my_array.txt', arr, fmt='%d', delimiter=',')

# 从文件中读取数组
new_arr1 = np.load('my_array.npy')
new_arr2 = np.loadtxt('my_array.txt', delimiter=',')

print(new_arr1) # 输出 [[1 2 3]
# [4 5 6]]

print(new_arr2) # 输出 [[1. 2. 3.]
# [4. 5. 6.]]

统计

平均

  1. average与mean
    numpy.mean()和numpy.average()函数都可以用于计算数组的平均值,但它们有一些不同之处。
  • numpy.mean(): 这个函数计算给定数组中元素的算术平均值。可以指定轴来计算不同维度上的平均值。默认情况下,计算所有元素的平均值。例如:

    1
    2
    3
    a = np.array([1, 2, 3, 4, 5])
    mean_a = np.mean(a)
    print(mean_a) # 输出:3.0
  • numpy.average(): 这个函数也用于计算数组的平均值,但它允许指定每个元素的权重。如果没有指定权重,则函数的行为与numpy.mean()相同。例如:

1
2
3
4
5
6
import numpy as np

a = np.array([1, 2, 3, 4, 5])
weights = np.array([0.1, 0.2, 0.3, 0.2, 0.2])
weighted_mean_a = np.average(a, weights=weights)
print(weighted_mean_a) # 输出:3.1

在不指定权重的情况下,这两个函数是等价的。

中位数

将所有数据排序后,如果总数是奇数,则中位数是中间一位;如果是偶数则是中间两位的平均值。

1
2
3
4
5
6
7
8
median(array([1,2,3,4,5,1000,100000]))

4.0


median(array([1,2,3,4,5,1000]))

3.5

方差、标准差

方差和标准差都是衡量数据分散程度的统计量,其中方差是每个数据点与平均值的差的平方的平均值,而标准差是方差的平方根。

1
2
3
4
5
6
7
8
9
10
11
std(array([1,2,3,4,5,1000]))

371.56220505081274

var(array([1,2,3,4,5,1000]))

138058.47222222222

371.56220505081274 *371.56220505081274

138058.47222222222

百分位percentile

将数据排序后,排在某个位置的数

1
numpy.percentile(a, q, axis=None, interpolation='linear')

这里axis是要计算的轴,interpolation是插值方法,用于计算非整数百分位数的值。默认为 linear。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np

# 生成一个包含 10 个元素的随机数组
x = np.random.randint(1, 101, size=10)

# 计算 x 数组的第 50 个百分位数
p50 = np.percentile(x, 50)

# 计算 x 数组的第 25 和第 75 个百分位数
p25, p75 = np.percentile(x, [25, 75])

print("x: ", x)
print("50th percentile of x: ", p50)
print("25th and 75th percentiles of x: ", p25, p75)

histogram

numpy.histogram() 函数用于计算一个数组的直方图。直方图是对数据分布情况的图形表示,横轴表示数据范围的分组区间,纵轴表示每个分组区间内的数据频数或频率。

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np
import matplotlib.pyplot as plt

# 生成随机数据
data = np.random.randn(1000)

# 计算直方图
hist, bins = np.histogram(data, bins=50, density=True)

# 绘制直方图
plt.hist(data, bins=bins, density=True, alpha=0.5)
plt.show()

  • 本文作者: fenix
  • 本文链接: https://fenix0.com/py-sci-02/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC 许可协议。转载请注明出处!