一、函数

对函数的初步了解见:
06_函数基础与库的使用

二、使用函数

在知道了函数的定义和使用方法之后,下面进一步介绍在Pandas中如何使用函数。

  1. # 导入包
  2. import pandas as pd
  3. # 创建一个简单的包含两列的DataFrame
  4. df = pd.DataFrame({'a': [10, 20, 30],
  5. 'b': [20, 30, 40]})
  6. print(df)

2.1. Series的apply方法

在Pandas中,当从一个DataFrame取其中一列或一行时,返回的对象类型时Series。

  1. print(type(df['a']))
  2. # <class 'pandas.core.series.Series'>
  3. print(type(df.iloc[0, :]))
  4. # <class 'pandas.core.series.Series'>
  1. Series有一个apply方法,可以传入一个函数(func),传入之后apply方法会把传入的函数应用于Series的每个元素。<br />例如前面定义的计算平方函数:
  1. def my_square(x):
  2. """求平方
  3. """
  4. return x ** 2
  5. sq = df['a'].apply(my_square)
  6. print(sq)
  7. # 查看返回结果的类型
  8. print(type(sq))
  1. 需要注意的时,当把my_square传递给apply时,不需要在my_square后面加圆括号。在my_square函数中,指数被固定为2(即求平方),如果我们把指数变成一个参数,就可以求任意一个数的任意次幂:
  1. def my_exp(x, e):
  2. return x ** e
  3. # 调用该函数时,需要输入两个参数
  4. my_exp(2, 3)
  5. # my_exp应用于Series的apply方法:
  6. ex = df['a'].apply(my_exp, e=3)
  7. print(ex)
  1. 当传递给apply的是一个有多个参数的函数时,需要将其余参数一并传入,并且参数的名称不能省略。

2.2. DataFrame的apply方法

DataFrame也有apply方法,但是其包含两个维度(行和列),因此在使用时需要指定是逐行计算还是逐列计算:

  1. def my_print(x):
  2. print(x)
  3. # 通过axis参数指定,默认axis=0,表示逐列计算,axis=1表示逐行计算
  4. df.apply(my_print)
  5. df.apply(my_print, axis=0)
  6. df.apply(my_print, axis=1)
  1. 当向DataFrameapply方法传递一个函数时,整个轴(行或列)会作为函数的第一个参数传递到函数中。
  1. def avg_3(x, y, z):
  2. return (x + y + z) / 3
  3. # 直接调用会报错
  4. print(df.apply(avg_3))
  5. # 提示没有输入参数y和z
  6. def avg_3_apply(row):
  7. x = row[0]
  8. y = row[1]
  9. z = row[2]
  10. return (x + y + z) / 3
  11. print(df.apply(avg_3_apply))

2.3. apply的应用

前面的介绍中,创建了一个3行2列的小数据集讲解apply方法的工作原理。下面将使用一个更实际的数据集详细介绍apply方法的应用。
本例中数据集来自seaborn库内置的tiantic数据集,数据记录了乘客在沉船事故中是否幸存下来。

  1. import seaborn as sns
  2. titanic = sns.load_dataset("titanic")
  3. # 该数据集是一个DataFrame
  4. print(type(titanic))
  5. # 查看该数据集的基本特征
  6. print(titanic.info())
  7. # RangeIndex: 891 entries, 0 to 890
  8. # Data columns (total 15 columns):
  9. # # Column Non-Null Count Dtype
  10. # --- ------ -------------- -----
  11. # 0 survived 891 non-null int64
  12. # 1 pclass 891 non-null int64
  13. # 2 sex 891 non-null object
  14. # 3 age 714 non-null float64
  15. # 4 sibsp 891 non-null int64
  16. # 5 parch 891 non-null int64
  17. # 6 fare 891 non-null float64
  18. # 7 embarked 889 non-null object
  19. # 8 class 891 non-null category
  20. # 9 who 891 non-null object
  21. # 10 adult_male 891 non-null bool
  22. # 11 deck 203 non-null category
  23. # 12 embark_town 889 non-null object
  24. # 13 alive 891 non-null object
  25. # 14 alone 891 non-null bool
  26. # dtypes: bool(2), category(2), float64(2), int64(4), object(5)
  27. # memory usage: 80.7+ KB
  28. # None
  1. 从返回结果来看,该数据集有891行和15列;其中age值存在的有714个,deck值存在的有203个。下面将定义几个函数,分别用于计算数据中有多少个nullNaN值,以及每一列或每一行数据完整案例所占的百分比。
  1. # 使用NumPy库的sum函数
  2. import numpy as np
  3. import pandas as pd
  4. def count_missing(vec):
  5. """计算缺失值的个数
  6. """
  7. # 根据值是否缺失获取一个由True/False值组成的向量
  8. null_vec = pd.isnull(vec)
  9. # 得到null_vec中null值的个数
  10. # null值对应为True,True的值为1
  11. null_count = np.sum(null_vec)
  12. # 返回缺失值的个数
  13. return null_count
  14. def prop_missing(vec):
  15. """计算缺失值的占比
  16. """
  17. # 调用前面定义的函数计算缺失值个数
  18. missing_num = count_missing(vec)
  19. # 获取元素总个数
  20. total_num = vec.size
  21. # 返回缺失值占比
  22. return missing_num / total_num
  23. def prop_complete(vec):
  24. """计算完整值的占比
  25. """
  26. # 先调用前面定义的函数计算缺失值占比
  27. # 用1减去缺失值占比
  28. return 1 - prop_missing(vec)

下面把前面定义好的函数应用于数据:

  1. # 按列计算
  2. cmis_col = titanic.apply(count_missing)
  3. pmis_col = titanic.apply(prop_missing)
  4. pcom_col = titanic.apply(prop_complete)
  5. print(cmis_col)
  6. print(pmis_col)
  7. print(pcom_col)
  8. # 按行计算
  9. cmis_row = titanic.apply(count_missing, axis=1)
  10. pmis_row = titanic.apply(prop_missing, axis=1)
  11. pcom_row = titanic.apply(prop_complete, axis=1)
  12. print(cmis_row.head())
  13. print(pmis_row.head())
  14. print(pcom_row.head())
  15. # 统计数据中分别有多少行包含多少个缺失值
  16. print(cmis_row.value_counts())
  17. # 将统计的缺失值结果作为新列加入到原来的数据集中
  18. titanic['num_missing'] = titanic.apply(count_missing, axis=1)
  19. print(titanic.head())
  20. # 根据num_missing列对数据集进行筛选
  21. print(titanic.loc[titanic.num_missing > 1, :].sample(10))

2.4. lambda函数

有些时候,apply方法中使用的函数非常简单,单独去定义该函数就会显得比较麻烦,此时可以使用lambda匿名函数的形式应用:

  1. import pandas as pd
  2. # 创建一个简单的包含两列的DataFrame
  3. df = pd.DataFrame({'a': [10, 20, 30],
  4. 'b': [20, 30, 40]})
  5. print(df.loc[:, 'a'].apply(lambda x: x ** 2))
  1. 编写匿名函数需要使用lambda关键字,在向apply传递的过程中与定义好的函数一致,都是将整行或整列数据作为第一个参数传入到函数中。虽然lambda函数也可以编写比较复杂的函数体,但是通常只在需要单行计算时才使用lambda函数。如果lambda函数中包含过多代码,会影响代码的可阅读性。