Python 开发指南:数值分析库(Numpy、Pandas预览)

2022-09-1812:01:50编程语言入门到精通Comments1,246 views字数 7903阅读模式

Numpy 预览

完整内容可移步至官方指南:Numpy and Scipy Documentation — Numpy and Scipy documentation,这里仅介绍基本功能。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

Numpy 是于 2005 年创建并开源的库,旨在提供比传统 Python 列表快 50 倍的数组对象。为了保证更高的性能,Numpy 有近 35% 的部分是由 C 语言实现的。见:GitHub - numpy/numpy: The fundamental package for scientific computing with Python.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

Numpy 常用于科学计算,因此在本章默认数组的元素全都是不可变数值,且仅讨论一维数组和二维数组。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

数组与矩阵

使用 Numpy 可以创建出数组 ( array )。它可以被看作是原 Python 列表类型的内存优化版本 ( 通过 dtype 指定元素类型以从内存中分配更加紧凑的空间 ),因此兼容原列表的各种操作。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

import numpy as np

arr1d = np.array([1, 2, 3, 4], dtype="int")

#  支持列表推导式
print(*[x*2 for x in arr1d])

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

# 支持 (高维的) 切片
sub = arr2d[0:2, 0:2]
"""
[[1,2],
 [4,5]]
"""
print(*sub)

其中,二维数组可被视为矩阵 ( Matrix ) 或列向量 ( Vector )。可以通过调用数组的 shape 属性确定维度。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

print(arr1d.shape)  # (4,) -> 长度为 4 的一维数组
print(sub.shape)  # (2, 2) -> 2×2 的方阵

使用 reshape() 方法可以将一维数组堆叠为二维数组。-1 参数有特殊的语义,表示 "任意长度",实际取决于原数组的形状。比如对一个二维数组做如下操作:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

  1. reshape(-1) 表示展平成任意长度的一维数组。
  2. reshape(-1, 1) 表示变成任意行数,但列数为 1 的列向量,是二维数组。
  3. reshape(1, -1) 表示变成行数为 1,但列数任意的行向量,是二维数组。
"""
    np.arange(n) 相当于 range(n) 的 numpy 版本。
    matrix:
    [[ 0  1  2  3]
     [ 4  5  6  7]
     [ 8  9 10 11]]
"""
matrix = np.arange(12).reshape(3, 4)
print(matrix)

# [ 0  1  2  3  4  5  6  7  8  9 10 11]
vect_1d = matrix.reshape(-1)
print(vect_1d)
# [[ 0  1  2  3  4  5  6  7  8  9 10 11]]
vect_v = matrix.reshape(1,-1)
print(vect_v)

"""
[[ 0]
 [ 1]
   :
   :
 [11]]
"""
vect_h = matrix.reshape(-1,1)
print(vect_h)

对于一维数组向二维数组的堆叠,可以通过 order 参数指定填充顺序。选 "C" 进行横向次序填充 ( 默认的 ) 或者 "F" 纵向次序填充,比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

 """
    [[ 0  1  2  3]
     [ 4  5  6  7]
     [ 8  9 10 11]]
"""
m1 = np.arange(12).reshape([3,4],order="C")
print(m1)

"""
    [[ 0  3  6  9]
     [ 1  4  7 10]
     [ 2  5  8 11]]
"""
m2 = np.arange(12).reshape([3,4],order="F")
print(m2)

通过向 np.where() 函数传入搜索条件,可以得到满足条件的元素下标。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

v1 = np.array([1, 2, 3, 3, 6, 3])
#  谓词 'v1 == 3' 中的 'v1' 表示 v1 数组中的每一个元素。
indexes = np.where(v1 == 3)
#  表示 [2, 3, 5] 下标出现了值为 3 的元素。 
print(indexes)

数组拼接

Numpy 支持对数组进行拼接。对于一维数组的拼接,称之 堆叠 ( stack )。堆叠可以分为水平堆叠 ( hstack() 函数 ) 和纵向堆叠 ( stack() 函数 )。水平堆叠的结果仍然是一维数组,而纵向堆叠的结果将是矩阵。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

v1 = np.array([1, 2, 3])
v2 = np.array([4, 5, 6])

v3 = np.hstack([v1,v2])  # [1, 2, 3, 4, 5, 6]
print(v3)

"""
    [[1 2 3]
    [4 5 6]]
"""
v4 = np.stack([v1,v2])
print(v4)

np.concatenate() 函数支持矩阵拼接,类似地,这里使用 axis 标识了拼接方向。在不指定参数的情况下默认 axis=1文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

m1 = np.array([
    [1, 2],
    [3, 4]
])

m2 = np.array([
    [5, 6],
    [7, 8]
])

"""
    列数不变,对行追加 (纵向拼接)
    [[1 2]
     [3 4]
     [5 6]
     [7 8]]
"""
m3 = np.concatenate([m1, m2], axis=0)
print(m3)
"""
    行数不变,对列追加 (横向拼接)
    [[1 2 5 6]
    [3 4 7 8]]
"""
m4 = np.concatenate([m1, m2], axis=1)
print(m4)

线性代数

由于二维数组可被视作矩阵或者向量,因此可以延伸出线性代数的相关计算。比如,通过 matrix.T 即可实现转置操作:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

matrix = np.arange(12).reshape(3, 4)
"""
    [[ 0  4  8]
     [ 1  5  9]
     [ 2  6 10]
     [ 3  7 11]]
"""
print(matrix.T)

Numpy 还提供四个函数用于生成单位矩阵,全 0 矩阵,全 1 矩阵,以及随机数矩阵。见:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

n, m = 3, 4

E = np.eye(n)  # 生成 m 阶单位矩阵
I = np.zeros([m, n])  # 生成 m × n 维全 0 矩阵
K = np.ones([m, n])  # 生成 m × n 维全 1 矩阵
H = np.empty([m, n]) # 生成 m × n 随机数矩阵

np.linalg.det() 函数可以展开矩阵行列式并计算求值,而 np.linalg.inv() 函数可求矩阵的逆 ( 底层是求解 Ax = E 的解 x,因此存在精度问题 )。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

A = np.array([
    [1,2],
    [3,4]
],dtype=int)

det = int(np.linalg.det(A))
print(det)  # 1*4 - 2*3

A_inverse = np.linalg.inv(A)
print(A_inverse)

现在假设有另一个矩阵 B。通过 np.matmul() 函数或者 @ 运算符 ( 运算符重载 ) 可以计算矩阵的乘积 ( 不满足交换律 ):文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

B = np.array([
    [1,3],
    [2,4]
], dtype=int)

"""
[[1*1 + 2*2, 1*3 + 2*4],
 [3*1 + 4*2, 3*3 + 4*4]]
"""
print(np.matmul(A, B))
print(A @ B)

特殊地,假设有两个一维数组 a 和 b,此时的 @ 将视作向量内积 ( 满足交换律 ),结果是一个数值。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

a = np.array([1, 2, 3], dtype=int)
b = np.array([4, 5, 6], dtype=int)
c = np.arrat([1, 2], dtype=int)
print(np.matmul(a,b))

通过 np.multiply() 函数或者 * 运算符可以计算矩阵与标量的积 ( 满足交换律 ):文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

"""
[[1*3, 2*3],
 [3*3, 4*3]]
"""
print(np.multiply(A,3))
print(A * 3)

以上方法可以使用一个 np.dot() 函数概括。根据不同的输入,它将表示不同的行为:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

  1. 当两个参数的其中一个为标量时,等价于 A * n
  2. 当两个参数均为二维矩阵时,等价于 A @ B
  3. 当两个参数分别为一个二维矩阵和一维数组时,将一维数组提升为二维列向量,然后作矩阵乘积 @
  4. 当两个参数均为一维矩阵时,视作向量内积,即 a·b

见:numpy中dot()、outer()、multiply()以及matmul()的区别 - 简书 (jianshu.com)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

# np.array_equal 用于比较两个数组元素是否按位相同。
assert np.array_equal(np.dot(a, b), a @ b)
assert np.array_equal(np.dot(A, B), A @ B)
assert np.array_equal(np.dot(A, c), A @ c)
assert np.array_equal(np.dot(A, 3), A * 3)
print("test passed")

为了加以区分,二维矩阵的运算更推荐使用 np.matmul() 或者 @文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

Pandas 预览

如果你很熟悉 SQL 操作,那么 Pandas 上手起来会非常容易。完整内容见官方指南:User Guide — pandas 1.4.3 documentation (pydata.org),这里仅介绍基础功能。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

我们主要使用 Pandas 库处理 表结构 的数据,它们被抽象为了 Dataframe 类型。首先演示基本的 IO 操作:先将 Python 原生的数据结构转换为 Dataframe,然后将它输出到 music.csv 文件,程序演示如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

import pandas as pd

#  在 Pandas 中,表数据的单个列被称之 Series.
artist = ["Billie Holiday", "Jimi Hendrix", "Miles Davis", "SIA"]
genre = ["Jazz", "Rock", "Jazz", "Pop"]
listeners = [1_300_000, 2_700_000, 1_500_000, 2_000_000]
plays = [27_000_000, 70_000_000, 48_000_000, 74_000_000]

dict_ = {"artist": artist,
        "genre": genre,
        "listeners": listeners,
        "plays": plays
        }

# dict 的 kw 对已经隐含了列名信息,因此这里不需要显式指定 columns。
df = pd.DataFrame(dict_)

#  这里不需要额外生成索引下标序列,因此做以下设置: index=False。
df.to_csv(path_or_buf="music.csv",index=False)

pd.DataFrame 可以将 Python 原生的 dict 字典结构转化为表。其中 key 表示了列名,而 value 表示了该列的数据。另一种更加自然的想法是:以行的形式组织 DataFrame,这里将每一行抽象成了元组。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

table = [
    ("Billie Holiday", "Jazz", 1_300_000, 27_000_000),
    ("Jimi Hendrix", "Rock", 2_700_000, 70_000_000),
    ("Miles Davis", "Jazz", 1_500_000, 48_000_000),
    ("SIA", "Pop", 2_000_000, 74_000_000)
]
df = pd.DataFrame(data=table, columns=["artist", "genre", "listeners", "plays"])
df.to_csv(path_or_buf="music.csv", index=False)

生成的表结构如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

artistgenrelistenerplays
Billie HolidayJazz130000027000000
Jimi HendrixRock270000070000000
Miles DavisJazz150000048000000
SIAPop200000074000000

下一步,试着 pd.read_csv(path) 重新将它以 Dataframe 的形式读取进内存。在默认情况下,Pandas 将读取到的第一行视作表头 Header 。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

df = pd.read_csv("music.csv")
"""
               artist genre  listeners     plays
    0  Billie Holiday  Jazz    1300000  27000000
    1    Jimi Hendrix  Rock    2700000  70000000
    2     Miles Davis  Jazz    1500000  48000000
    3             SIA   Pop    2000000  74000000
"""
print(df)

数据提取

下面从最基本的操作开始说起。若要提取出 df 的某一列数据,可以直接使用重载的 [] 运算符 ( 见前一章的运算符重载 ) 进行指定,支持传入单个列名,或者是由多个列名组成的列表。下面演示了提取单个列和多个列的情形:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

df["artist"]  # 提取一个列
df[["artist","plays"]]  # 提取多个列

特地强调,如果仅提取一个列,得到的将是 Series 类型;如果提取多个列,得到的将是 Dataframe 类型。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

如果要从 df 中提取出第 n 行,则使用 df.loc[n] 实现。事实上,这里的 n 代表索引,在默认情况下是 0 起始的数据下标。比如提取第一行的行号:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

# 提取下标 0 行的 row
# 0  Billie Holiday  Jazz    1300000  27000000
row = df.loc[0]

# 从 row 中再提取 artist 列
# Billie Holiday
print(row["artist"])

Pandas 提供了查看数据的前 n 行或最后 n 行的函数:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

n = 2
print(df.head(n))  # 查看前 n 行数据
print(df.tail(n))  # 查看后 n 行数据

过滤 ( WHERE 谓词 ) 是最基本的表操作。比如,筛选出 plays 数据高于 5000 万的数据:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

top_df = df[df["plays"] > 50_000_000]
print(top_df)

另一种直观的方式是调用 query() 方法并直接传递谓词语句,字符串内容遵守 Python 语法。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

top_df = df.query("plays >= 50_000_000 and listeners >= 2_500_000")
print(top_df)

假定要从 genre 列中归纳出音乐人的歌曲类型,则可以使用 unique() 方法进行去重。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

col = df["genre"].unique()

# ['Jazz' 'Rock' 'Pop']
# col.tolist()
print(col)

排列 ( SORT BY 谓词 ) 也是常见的表操作之一。比如,这里按照 plays 列的数据以降序 Desc 排列:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

sorted_df = df.sort_values("plays",ascending=False)
"""
               artist genre  listeners     plays
    3             SIA   Pop    2000000  74000000
    1    Jimi Hendrix  Rock    2700000  70000000
    2     Miles Davis  Jazz    1500000  48000000
    0  Billie Holiday  Jazz    1300000  27000000
"""
print(sorted_df)

聚合操作

可以参考这篇优质文章:Pandas教程 | 超好用的Groupby用法详解 - 知乎 (zhihu.com)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

Pandas 就像 SQL 那样提供聚合操作。比如我们期望按照 genre 字段对表进行分组,并统计每个组的行数。它可以被表达为:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

"""SQL:
    select genre, count(*)
    from df
    group by genre
"""
print(df.groupby("genre")["artist"].count())

除了 count() 计数方法之外,其它的聚合方法还包括:mean() 均值,sum() 求和,median() 中位数,min() 最小值,max() 最大值,var() 方差,std() 标准差。一种更通用的聚合方法为 agg(),它允许接收 **kwargs 来对不同列进行不同的聚合操作,key 为列名,value 为聚合函数名。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

report_df = df.groupby("genre").agg({
    "artist": "count",
    "listeners": "mean",
    "plays": "max"
})

"""
           artist  listeners     plays
    genre                             
    Jazz        2  1400000.0  48000000
    Pop         1  2000000.0  74000000
    Rock        1  2700000.0  70000000
"""
print(report_df)

转换操作

Pandas 提供的 Dataframe可变 数据,这意味着我们可以对原数据表进行改动。比如,根据 listensersplays 计算每个音乐人的影响力 score,并将其作为新的数据列附着在原数据表上:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

df = pd.read_csv("music.csv")
"""
           artist genre  listeners     plays  score
0  Billie Holiday  Jazz    1300000  27000000  0.605
1    Jimi Hendrix  Rock    2700000  70000000  1.535
2     Miles Davis  Jazz    1500000  48000000  1.035
3             SIA   Pop    2000000  74000000  1.580
"""
# 假定 score = 5 * listeners_count + 2 * plays
df["score"] = (df["listeners"] * 5 + df["plays"] * 2) / 10**8
print(df)

map() 函数提供了以 lambda 表达式对 单列数据 ( 即 Series 类型 ) 进行变换的途径。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

"""
               artist genre  listeners   plays
    0  Billie Holiday  Jazz    1300000  50mio-
    1    Jimi Hendrix  Rock    2700000  50mio+
    2     Miles Davis  Jazz    1500000  50mio-
    3             SIA   Pop    2000000  50mio+
"""
df["plays"] = df["plays"].map(lambda x: "50mio+" if x > 50_000_000 else "50mio-")
print(df)

如果要基于多列数据 ( 此时为 DataFrame 类型 ) 进行变换,则需要引入 apply() 方法。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

# axis=1 表示按数据列进行操作
df["scores"] = df[["listeners","plays"]].apply(lambda t: (t["listeners"]*5 + 2*t["plays"]) / 10**8, axis=1)
print(df)

表连接

可以通过 merge() 方法实现类似 SQL 表的内连接 ( 默认 ),左连接,右连接,全外连接操作,通过 how 参数进行配置。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

info = [
    ("Billie Holiday", "US"),
    ("Jimi Hendrix", "US"),
    ("Justin bieber", "Canada"),
]
artist_info = pd.DataFrame(data=info, columns=["artist", "country"])

m0 = df.merge(artist_info)  # how="inner"
m1 = df.merge(artist_info, how="outer")
m2 = df.merge(artist_info, how="left")
m3 = df.merge(artist_info, how="right")

使用 Pandas 自身提供的 concat() 函数可以连接两个数据表。比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

row = [("Justin Bieber", "Pop", 300_000, 1_000_000)]
append_df = pd.DataFrame(data=row, columns=["artist", "genre", "listeners", "plays"])

# axis=0 表示按行进行尾追加
ndf = pd.concat([df, append_df], axis=0)
print(ndf)

作者:花花子文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

来源:稀土掘金文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/27844.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/ymba/27844.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定