• 常用基本函数



    下面的例子都会使用到已经创建的 test_information.csv 虚拟数据集,它记录了四所学校学生的1000米体测信息。

    下述列名依次代表学校、年级、姓名、性别、身高、体重、是否为转系生、体测场次、测试时间、1000米成绩,本节只需使用其中的前七列。

    df = pd.read_csv('test_information.csv')
    print(df.columns)  # 获取列索引
    ---------
    Index(['School', 'Grade', 'Name', 'Gender', 'Height', 'Weight', 'Transfer',
           'Test_Number', 'Test_Date', 'Time_Record'],
          dtype='object')
    
    
    df = df[df.columns[:7]]  # 获取前7列数据表
    print(df)
    ---------
                                            School Grade  ... Weight Transfer
    0                          Nanchang University    大一  ...     72        N
    1                          Nanchang University    大二  ...     48        N
    2  Jiangxi University of Finance and Economics    大一  ...     60        N
    3  Jiangxi University of Finance and Economics    大二  ...     57        N
    4                    Jiangxi Normal University    大一  ...     57        N
    5                    Jiangxi Normal University    大二  ...     75        N
    6                 Nanchang Hangkong University    大一  ...     52        N
    7                 Nanchang Hangkong University    大二  ...     45        N
    
    [8 rows x 7 columns]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    汇总函数

    head, tail 函数分别表示返回表或者序列的前 n 行和后 n 行,其中 n 默认为 5。

    print(df.head())  # 输出表格的前5行
    ---------
                                            School Grade  ... Weight Transfer
    0                          Nanchang University    大一  ...     72        N
    1                          Nanchang University    大二  ...     48        N
    2  Jiangxi University of Finance and Economics    大一  ...     60        N
    3  Jiangxi University of Finance and Economics    大二  ...     57        N
    4                    Jiangxi Normal University    大一  ...     57        N
    
    [5 rows x 7 columns]
    
    
    print(df.tail())  # 输出表格的后5行
    ---------
                                            School Grade  ... Weight Transfer
    3  Jiangxi University of Finance and Economics    大二  ...     57        N
    4                    Jiangxi Normal University    大一  ...     57        N
    5                    Jiangxi Normal University    大二  ...     75        N
    6                 Nanchang Hangkong University    大一  ...     52        N
    7                 Nanchang Hangkong University    大二  ...     45        N
    
    [5 rows x 7 columns]
    
    
    print(df.head(2))  # 输出表格的前2行
    ---------
                    School Grade        Name  Gender  Height  Weight Transfer
    0  Nanchang University    大一  Li Lanqing    Male   175.0      72        N
    1  Nanchang University    大二      Cao Yu  Female   168.3      48        N
    
    
    print(df.tail(2))  # 输出表格的后2行
    ---------
                             School Grade          Name  ... Height  Weight  Transfer
    6  Nanchang Hangkong University    大一  Wang Huijuan  ...  170.0      52         N
    7  Nanchang Hangkong University    大二  Shen Wenxuan  ...  159.9      45         N
    
    [2 rows x 7 columns]
    
    • 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

    info, describe 分别返回表的信息概况和表中数值列对应的主要统计量。

    info, describe 只能实现较少信息的展示,如果想要对一份数据集进行全面且有效的观察,特别是在列较多的情况下,推荐使用 pandas-profiling 包。

    print(df.info())
    ---------
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 8 entries, 0 to 7
    Data columns (total 7 columns):
     #   Column    Non-Null Count  Dtype  
    ---  ------    --------------  -----  
     0   School    8 non-null      object 
     1   Grade     8 non-null      object 
     2   Name      8 non-null      object 
     3   Gender    8 non-null      object 
     4   Height    8 non-null      float64
     5   Weight    8 non-null      int64  
     6   Transfer  8 non-null      object 
    dtypes: float64(1), int64(1), object(5)
    memory usage: 576.0+ bytes
    None
    
    
    print(df.describe())
    ---------
               Height    Weight
    count    8.000000   8.00000
    mean   169.850000  58.25000
    std      6.989993  10.66034
    min    159.900000  45.00000
    25%    166.125000  51.00000
    50%    169.150000  57.00000
    75%    171.925000  63.00000
    max    183.200000  75.00000
    
    • 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

    特征统计函数

    在 Series 和 DataFrame 上定义了许多统计函数,最常见的是 sum, mean, median, var, std, max, min 。用身高和体重列进行如下演示。

    这些特征统计函数都有 axis 参数,当 axis=0 时表示按列统计,当 axis=1 时表示按行统计,默认 axis=0 。

    df_demo = df[['Height', 'Weight']]
    print(df_demo)
    ---------
       Height  Weight
    0   175.0      72
    1   168.3      48
    2   170.9      60
    3   165.0      57
    4   166.5      57
    5   183.2      75
    6   170.0      52
    7   159.9      45
    
    # 逐行求和
    print(df_demo.sum(axis=1))
    ---------
    0    247.0
    1    216.3
    2    230.9
    3    222.0
    4    223.5
    5    258.2
    6    222.0
    7    204.9
    dtype: float64
        
    # 逐列求和
    print(df_demo.sum())
    ---------
    Height    1358.8
    Weight     466.0
    dtype: float64
    
    # 逐列求最大值
    print(df_demo.max())
    ---------
    Height    183.2
    Weight     75.0
    dtype: float64
    
    # 逐列求最小值
    print(df_demo.min())
    ---------
    Height    159.9
    Weight     45.0
    dtype: float64
    
    # 逐列求均值
    print(df_demo.mean())
    ---------
    Height    169.85
    Weight     58.25
    dtype: float64
    
    # 逐列求方差
    print(df_demo.var())
    ---------
    Height     48.860000
    Weight    113.642857
    dtype: float64
    
    # 逐列求标准差
    print(df_demo.std())
    ---------
    Height     6.989993
    Weight    10.660340
    dtype: float64
    
    # 逐列求中位数
    print(df_demo.median())
    ---------
    Height    169.15
    Weight     57.00
    dtype: float64
        
    # 逐列求分位数
    print(df_demo.quantile(q=0.3))  # 默认q=0.5
    ---------
    Height    166.68
    Weight     52.50
    Name: 0.3, dtype: float64
            
    # 逐列求非缺失值个数
    print(df_demo.count())
    ---------
    Height    8
    Weight    8
    dtype: int64
        
    # 逐列求最大值对应的索引   
    print(df_demo.idxmax())
    ---------
    Height    5
    Weight    5
    dtype: int64
        
    # 逐列求最小值对应的索引   
    print(df_demo.idxmin())
    ---------    
    Height    7
    Weight    7
    dtype: int64
    
    • 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
    • 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
    • 100
    • 101
    • 102

    上面这些所有的函数,由于操作后返回的是标量,所以又称为聚合函数。

    唯一值函数

    对序列使用 unique 和 nunique 可以分别得到其唯一值组成的列表和唯一值的个数,value_counts 可以得到唯一值和其对应出现的频数。

    df = pd.read_csv('test_information.csv')
    print(df['School'].unique())
    ---------
    ['Nanchang University' 'Jiangxi University of Finance and Economics'
     'Jiangxi Normal University' 'Nanchang Hangkong University']
     
    print(df['School'].nunique())
    ---------
    4
    
    print(df['School'].value_counts())
    ---------
    Jiangxi University of Finance and Economics    2
    Nanchang Hangkong University                   2
    Jiangxi Normal University                      2
    Nanchang University                            2
    Name: School, dtype: int64
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    如果想要观察多个列组合的唯一值,可以使用 drop_duplicates 。其中的关键参数是 keep ,默认值 first 表示每个组合保留第一次出现的所在行,last 表示保留最后一次出现的所在行,False 表示把所有重复组合所在的行剔除。

    df_demo = df[['School', 'Gender', 'Transfer']]
    print(df_demo)
    ---------
                                            School  Gender Transfer
    0                          Nanchang University    Male        N
    1                          Nanchang University  Female        N
    2  Jiangxi University of Finance and Economics    Male        N
    3  Jiangxi University of Finance and Economics    Male        N
    4                    Jiangxi Normal University    Male        N
    5                    Jiangxi Normal University    Male        N
    6                 Nanchang Hangkong University  Female        N
    7                 Nanchang Hangkong University  Female        N
    
    
    print(df_demo.drop_duplicates())
    ---------
                                            School  Gender Transfer
    0                          Nanchang University    Male        N
    1                          Nanchang University  Female        N
    2  Jiangxi University of Finance and Economics    Male        N
    4                    Jiangxi Normal University    Male        N
    6                 Nanchang Hangkong University  Female        N
    
    
    # 保留只出现过一次的学校、性别和转系生组合
    print(df_demo.drop_duplicates(keep=False))
    ---------
                    School  Gender Transfer
    0  Nanchang University    Male        N
    1  Nanchang University  Female        N
    
    
    # 在Series上也可以使用
    print(df['School'].drop_duplicates())
    ---------
    0                            Nanchang University
    2    Jiangxi University of Finance and Economics
    4                      Jiangxi Normal University
    6                   Nanchang Hangkong University
    Name: School, dtype: object
    
    • 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

    此外,duplicated 和 drop_duplicates 的功能类似,但前者返回是否重复的布尔列表,其 keep 参数与后者一致。其返回的序列,把重复元素设为 True ,否则为 False 。drop_duplicates 等价于把 duplicated 为 True 的对应行剔除。

    # 默认keep='first',第一个重复的为False,后面的为True
    print(df_demo.duplicated())  
    ---------
    0    False
    1    False
    2    False
    3     True
    4    False
    5     True
    6    False
    7     True
    dtype: bool
    
    # 所有重复的布尔值都为True
    print(df_demo.duplicated(keep=False))  
    ---------
    0    False
    1    False
    2     True
    3     True
    4     True
    5     True
    6     True
    7     True
    dtype: bool
    
    # 在Series上也可以使用
    print(df['School'].duplicated())
    ---------
    0    False
    1     True
    2    False
    3     True
    4    False
    5     True
    6    False
    7     True
    Name: School, dtype: bool
    
    • 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

    替换函数

    一般而言,替换操作是针对某一个列进行的,因此下面的例子都以 Series 举例。pandas 中的替换函数可以归纳为三类:映射替换、逻辑替换、数值替换。此处介绍映射替换中的replace用法。

    在 replace 中,可以通过字典构造,或者传入两个列表来进行替换。

    print(df['Gender'])
    ---------
    0      Male
    1    Female
    2      Male
    3      Male
    4      Male
    5      Male
    6    Female
    7    Female
    Name: Gender, dtype: object
    
    print(df['Gender'].replace({'Male': 0, 'Female': 1}))
    ---------
    0    0
    1    1
    2    0
    3    0
    4    0
    5    0
    6    1
    7    1
    Name: Gender, dtype: int64
    
    print(df['Gender'].replace(['Male', 'Female'], [0, 1]))
    ---------
    0    0
    1    1
    2    0
    3    0
    4    0
    5    0
    6    1
    7    1
    Name: Gender, dtype: int64
    
    • 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

    逻辑替换包括了 where 和 mask ,这两个函数是完全对称的:where 函数在传入条件为 False 的对应行进行替换,而 mask 在传入条件为 True 的对应行进行替换,当不指定替换值时,替换为缺失值。

    需要注意的是,传入的条件只需是与被调用的 Series 索引一致的布尔序列即可。

    s = pd.Series([-1, 1.2345, 199, -50])
    print(s.where(s<0))
    ---------
    0    -1.0
    1     NaN
    2     NaN
    3   -50.0
    dtype: float64
    
    print(s.where(s<0, 100))
    ---------
    0     -1.0
    1    100.0
    2    100.0
    3    -50.0
    dtype: float64
        
    print(s.mask(s<0))
    ---------
    0         NaN
    1      1.2345
    2    199.0000
    3         NaN
    dtype: float64
        
    print(s.mask(s<0, -100))
    ---------
    0   -100.0000
    1      1.2345
    2    199.0000
    3   -100.0000
    dtype: float64
    
    • 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

    数值替换包含了 round, abs, clip 方法,它们分别表示取几位小数、取绝对值和截断。

    s = pd.Series([-1, 1.2345, 100, -50])
    print(s.round(2))
    ---------
    0     -1.00
    1      1.23
    2    199.00
    3    -50.00
    dtype: float64
    
    print(s.abs())
    ---------
    0      1.0000
    1      1.2345
    2    199.0000
    3     50.0000
    dtype: float64
    
    # 两个数分别表示上下截断边界,小于0的数为0,大于2的数为2
    print(s.clip(0, 2))  
    ---------
    0    0.0000
    1    1.2345
    2    2.0000
    3    0.0000
    dtype: float64
    
    • 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

    排序函数

    排序共有两种方式,其一为值排序,其二为索引排序,对应的函数是 sort_values 和 sort_index 。

    df_demo = df[['Name', 'Grade', 'Height', 'Weight']]
    print(df_demo)
    ---------
               Name Grade  Height  Weight
    0    Li Lanqing    大一   175.0      72
    1        Cao Yu    大二   168.3      48
    2  Wang Risheng    大一   170.9      60
    3      Zhang Yi    大二   165.0      57
    4  Xiong Zhihao    大一   166.5      57
    5  Zhang Haiyan    大二   183.2      75
    6  Wang Huijuan    大一   170.0      52
    7  Shen Wenxuan    大二   159.9      45
    
    
    # 根据Height列数值按升序排列所有行
    print(df_demo.sort_values('Height'))
    ---------
               Name Grade  Height  Weight
    7  Shen Wenxuan    大二   159.9      45
    3      Zhang Yi    大二   165.0      57
    4  Xiong Zhihao    大一   166.5      57
    1        Cao Yu    大二   168.3      48
    6  Wang Huijuan    大一   170.0      52
    2  Wang Risheng    大一   170.9      60
    0    Li Lanqing    大一   175.0      72
    5  Zhang Haiyan    大二   183.2      75
    
    
    # 根据Height列数值按降序排列所有行
    print(df_demo.sort_values('Height', ascending=False))
    ---------
               Name Grade  Height  Weight
    5  Zhang Haiyan    大二   183.2      75
    0    Li Lanqing    大一   175.0      72
    2  Wang Risheng    大一   170.9      60
    6  Wang Huijuan    大一   170.0      52
    1        Cao Yu    大二   168.3      48
    4  Xiong Zhihao    大一   166.5      57
    3      Zhang Yi    大二   165.0      57
    7  Shen Wenxuan    大二   159.9      45
    
    • 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

    在排序中,经常遇到多列排序的问题,比如在体重相同的情况下,对身高进行排序,并且保持身高降序排列,体重升序排列。

    # 先根据体重升序排列,在体重相同的情况下再根据身高降序排列
    print(df_demo.sort_values(['Weight', 'Height'], ascending=[True, False]))
    ---------
               Name Grade  Height  Weight
    7  Shen Wenxuan    大二   159.9      45
    1        Cao Yu    大二   168.3      48
    6  Wang Huijuan    大一   170.0      52
    4  Xiong Zhihao    大一   166.5      57
    3      Zhang Yi    大二   165.0      57
    2  Wang Risheng    大一   170.9      60
    0    Li Lanqing    大一   175.0      72
    5  Zhang Haiyan    大二   183.2      75
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    索引排序的用法和值排序完全一致,只不过元素的值在索引中,此时需要指定索引列的名字或者列号,用参数 level 表示。另外,需要注意的是字符串的排列顺序由字母顺序决定。

    df_demo = df[['Name', 'Grade', 'Height', 'Weight']]
    print(df_demo.sort_index(ascending=False))  # 按降序排列
    ---------
               Name Grade  Height  Weight
    7  Shen Wenxuan    大二   159.9      45
    6  Wang Huijuan    大一   170.0      52
    5  Zhang Haiyan    大二   183.2      75
    4  Xiong Zhihao    大一   166.5      57
    3      Zhang Yi    大二   165.0      57
    2  Wang Risheng    大一   170.9      60
    1        Cao Yu    大二   168.3      48
    0    Li Lanqing    大一   175.0      72
    
    
    df_demo = df_demo.set_index('Name')  # 将Name列设置成索引列
    print(df_demo)
    ---------
                 Grade  Height  Weight
    Name                              
    Li Lanqing      大一   175.0      72
    Cao Yu          大二   168.3      48
    Wang Risheng    大一   170.9      60
    Zhang Yi        大二   165.0      57
    Xiong Zhihao    大一   166.5      57
    Zhang Haiyan    大二   183.2      75
    Wang Huijuan    大一   170.0      52
    Shen Wenxuan    大二   159.9      45
    
    
    print(df_demo.sort_index(level='Name'))  # 根据索引列排序
    ---------
                 Grade  Height  Weight
    Name                              
    Cao Yu          大二   168.3      48
    Li Lanqing      大一   175.0      72
    Shen Wenxuan    大二   159.9      45
    Wang Huijuan    大一   170.0      52
    Wang Risheng    大一   170.9      60
    Xiong Zhihao    大一   166.5      57
    Zhang Haiyan    大二   183.2      75
    Zhang Yi        大二   165.0      57
    
    • 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

    apply 方法

    apply 方法常用于 DataFrame 的行迭代或者列迭代,apply的参数往往是一个以序列为输入的函数。例如对于 .mean() ,使用 apply 可以如下地写出。

    df_demo = df[['Height', 'Weight']]
    
    def my_mean(x):
        res = x.mean()
        return res
        
    print(df_demo.apply(my_mean))  # 将df_demo表中的序列逐个输入到my_mean函数中
    ---------
    Height    169.85
    Weight     58.25
    dtype: float64
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    同样的,可以利用 lambda 表达式使得书写简洁,这里的 x 就指代被调用的 df_demo 表中逐个输入的序列。

    print(df_demo.apply(lambda x: x.mean()))
    ---------
    Height    169.85
    Weight     58.25
    dtype: float64
    
    • 1
    • 2
    • 3
    • 4
    • 5

    若指定 axis=1 ,那么每次传入函数的就是行元素组成的 Series ,其结果与之前的逐行均值结果一致。

    print(df_demo.apply(lambda x: x.mean(), axis=1))
    ---------
    0    123.50
    1    108.15
    2    115.45
    3    111.00
    4    111.75
    5    129.10
    6    111.00
    7    102.45
    dtype: float64
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    得益于传入自定义函数的处理,apply 的自由度很高,但这是以性能为代价的。一般而言,使用 pandas 的内置函数处理与使用 apply 来处理同一个任务,其速度会相差较多,因此只有在确实存在自定义需求的情境下才考虑使用 apply 。

  • 相关阅读:
    [BJDCTF2020]EasySearch
    性能测试工具---jmeter讲解
    Linux·网络编程套接字(三)
    专有网络VPC 是什么
    Three.js之绘制中文文字并跟随物体
    货币政策传导与货币政策调控-中国视角下的宏观经济
    【C++】C++ 语言对 C 语言的加强 ③ ( 类型检查增强 - 所有函数和变量必须有类型 | 新增 bool 类型 - bool 类型简介 )
    大数据(九):数据可视化(一)
    英特尔神经网络计算棒
    【Win10】开机启动输入密码——免回车
  • 原文地址:https://blog.csdn.net/weixin_48158964/article/details/126077212