数据分析利器 Pandas :apply() 方法 + map() 配对 + 计算描述统计 + 协方差和相关性 + 异常值处理常用方法(基于 python )

张开发
2026/4/7 6:42:44 15 分钟阅读

分享文章

数据分析利器 Pandas :apply() 方法 + map() 配对 + 计算描述统计 + 协方差和相关性 + 异常值处理常用方法(基于 python )
本篇内容里涉及到的全部代码在 jupyter notebook 当中运行。pandas 主要是操作表格的一个三方库。先来介绍一下 pandas 怎样和 numpy 去结合把 numpy 对应的一些二维表格变成真正的 pandas 可以操作的 dataframe 对象。在这之前我们要先安装pandas 三方库。首先安装 pandaspip install pandas -i https://pypi.tunatsinghua.edu.cn/simple。pip install pandas -i https://pypi.tunatsinghua.edu.cn/simple安装之后就可以使用它了。由于后面也要用到 numpy 去造数据所以把 numpy 也引入进来了。import pandas as pd import numpy as np这个地方就是引用当然写完引用要执行一下后面才可以使用它们。执行之后发现没有问题。想去创建数据的话pandas 里面有个 DataFrame括号里面的数据要么读出来用 DataFrame 进行转换要么直接在 DataFrame 里面直接给它生成出来。使用 DataFrame 生成数据在之前的jupyter notebook 使用 numpy 操作 常用统计函数 里面讲过使用 np.arrange(12) 就可以生成出来比如现在想生成 12 个数据。然后我们可以把它改变一下形状由一维数据改为二维数据这时候可以用 reshape[4,3]12 个数据的话可以改成 4 行 3 列。改完形状之后还可以传行的标题一共是 4 行传入 index[sz, bj, cs, sh]。这个地方就是行的表头给它。然后我们还可以给列的表头。列的标头也可以定义一个列表但是这个地方随便写一下用 columnlist(abc)。直接把它拆成了一个列表。这样它每行会有一个表头每列也有一个表头。然后我们用 df 来保存一下最后把 df 打印出来。df pd.DataFrame(np.arange(12).reshape(4,3), index[sz, bj, cs, sh], columnslist(abc)) df执行之后发现列表头是a b c行表头是 sz bj cs sh。这样就形成了表格的图形。所以这里的表头和标题通过 pandas 都可以定义好这是结合了 numpy 一起过来的进行创建的这种速度就会比较快。apply() 方法使用列计算除此之外我们还可以对这里面的数据进行筛选。先做一个简单的筛选因为里面都是数字所以比较好筛。比如现在想把这一列的 a 最小是 0 最大是 9b 最小的是 1 最大的是 10c 也是一样的有最小和最大值。现在希望用最大的值减最小的值又不需要把它取出来一个一个计算这种方法就是列计算。最大值减最小值。这里最大最小可以用 numpy 的方法来计算在 pandas 当中有一个 apply 方法。这个单词直译是应用的意思应用某一个方法。里面是要传入一个整套逻辑的这个逻辑我们用 lambda 的方式自己定义。取值的话就给它传值传最大的值前面变量 x冒号后面是表达式x 的最大值计算出来然后减去 x 的最小值把这个表达式定义为 f。再把 f 传进来。传进来之后它是默认以列来操作的。执行一下。# 最大值 - 最小值 f lambda x:x.max() - x.min() df.apply(f)只要把表达式传入到 apply 当中去它就会对每一组数据进行操作。对所有的元素开平方现在想对所有的数开平方对每一个元素都做这样一个处理它也能够操作。开平方在上一篇提及过用 np.sqrt只需要把这个括到 apply 里面去。df.apply(np.sqrt)这样里面所有的元素都开平方了。不止是开平方还可以对列的所有元素进行求和等等。写法都是一致的。对列中所有元素进行求和和上面写法是一致的只不过传入 apply 的方法由 sqrt 变成了 sum。df.apply(np.sum)我们可以看到返回了所有列元素求和后的结果。这就是对列求和。所有的都是一个方式想对它进行什么操作直接传入到 apply 中就可以了。自定义函数传入现在想找一下最大值和最小值。a 里面最大最小值、b 里面最大最小值还有 c 里面的如果日常不知道其他方法的情况下可以定义一个函数。这个函数适合 lambda 掌握的不是特别牢靠的。这个地方要用到单列数据用 Series。紧接着会补充 Series 是什么东西。用表格的方式返回来。def func(x): return pd.Series([x.min(), x.max()], index[最小值, 最大值]) df.apply(func)最大值和最小值是当成一组数据给传过去的。所以要用列表的形式给它框起来后面再传进来 index。用这种方式主要是为了让它排成一个表格的形式。Series 和 DataFramepandas 当中有两个数据第一个是 Series另外一个是 DataFrame。Series 是线性数据它是一组一组的不是一张表格。当我们在对组里面的数据进行精细化处理的时候我们就用 Series。DataFrame 是一个表格数据表格数据是有行和列的。所以刚刚的代码里pd.Series([x.min(), x.max()], index[最小值, 最大值])我们同时生成两列它是 Series 对每一行数据进行操作然后再通过 index 给它去赋值的。所以这个地方需要注意每次在做的时候需要在每一组数据当中把某一个值取出来这时候用的就是 Series。在去操作一整个表的话就需要再去加这里面操作表格都是一条数据放一条又放一条它是合起来变成的二维数据。所以要操作单条数据或者某一列数据的时候最好的方式就是使用 Series。看一下 pandas 库中 Series 类的初始化签名和文档说明。这是我从网络上搜到的对这段初始化代码的描述可供参考这是pandas库中Series类的初始化签名和文档说明它是pandas里用于处理一维带标签数组的核心数据结构常用来存储序列、时间序列等数据。初始化参数说明data可传入列表、字典、标量等数据源用于初始化Series的值index可自定义索引标签若不传入则默认生成整数索引dtype指定数据类型比如int、float、str等不指定则自动推断name给Series设置名称方便后续识别和操作copy布尔值控制是否复制输入数据默认不复制fastpath内部优化参数一般无需手动设置核心特性灵活索引‌支持整数索引和标签索引索引标签无需唯一但必须是可哈希类型智能统计‌重写了ndarray的统计方法会自动排除NaN缺失值进行计算自动对齐‌Series间的加减乘除等运算会基于索引自动对齐数据无需长度一致小案例——计算税额字典定义一组数据电脑、照相机、手机ps5以及这些东西对应的价格。很多电子产品都有对应的税比如税额占13%也就是当前销售价格全部都要乘上 0.13才能得到我们所需要的税额。而这样的操作如果用 pandas 则会更加方便。data { 商品名称:[电脑, 照相机, 手机, ps5], 销售价格:[10009,12000,7000,4000] }首先要把数据定义成 pandas 可操作的对象。df2 pd.DataFrame(data)方法1可以直接用 df2(销售价格) 对一列数据直接进行操作。直接乘上 0.13打印出来就是商品税额的值。跑一下这两段代码。# 13%的商品税额 df2 pd.DataFrame(data) # 方法1 print(df2[销售价格]*0.13)我们可以看到运行之后税额就被打印出来了。还有第二种方法用 apply 来做。只要把传过去的方式写出来就行了。可以直接写在 apply 的括号里不用再去定义函数。传入 lambda x:x*0.13这个方法作用到的那个值我们需要取出来现在拿到的是表格数据需要把 df2 里面销售价格的数据给它取出来然后再点上 apply。它取出来就是一组 Series所以这个地方就得到商品税额了。再把它赋值一份商品税额的价格给到 df2这个数据可以显示在表格当中。数据要新增一个叫做商品税额。最后打印一下 df2运行出来看看。# 方法2 df2[商品税额] df2[销售价格].apply(lambda x:x*0.13) df2我们可以看到新增了一列商品税额在 df2 里。map 配对s 里有一些产品苹果香蕉胡萝卜。里面有俩苹果就是苹果今天卖出去两个。它们每个价格在 price 里存着。现在需要把商品的价格全部对应上去。这时候就用到了 map 方法。这地方要先执行一下得到这些数据然后再基于这些数据进行操作。s pd.DataFrame({fruit:[apple, banana, carrot, apple, banana, carrot]}) price {apple:6.5, banana:3.9, carrot:3.5}现在想把所有的价格全部都标注出来所以去做配对的时候就可以这样来写。把水果直接取出来然后再点上 map。map 里面把价格传进去。穿的时候不用引上以为不是字符串而是变量传进去之后用 s 对象新增的价格列接住。最后打印一下运行出来看看。s[price] s[fruit].map(price) s我们可以看到运行之后价格全都新增上去了。而且价格里面所有的数据都和产品一一对应上了而且相同的数据可以重复地去匹配。计算描述统计设置索引平均值中位数极值先把数据定义出来然后运行一下后面要用。data { 名字:[Lcuy,Tony,Tom,Jack], 数学:[85,92,78,65], 英语:[76,88,92,72], 历史:[90,82,68,56] }把它格式转换一下。然后点上平均值。运行一下它会报错的。接下来我会解释一下。df3 pd.DataFrame(data) df3.mean()因为里面有一个不能计算的选项这里的名字是没办法求平均值的。如果想让这个值正常计算就必须得把名字排除去。名字不参与计算最好的方式是设置名字为索引值。设置之后它会重新把表格覆盖住。设置索引的函数是 set_index把名字传入看一下这个表格。df3 pd.DataFrame(data) df3 df3.set_index(名字) print(df3) df3.mean()运行之后这个名字会比数学英语历史矮一截说明设置成功了。设置索引之后后面的平均值就可以进行计算了。我们可以看到数学平均值是 80 分英语是 82历史是 74 分。有的时候平均值无法很好反应大部分学生的实际情况中位数还是比较准确的。用中位数试一下。midian() 这个方法。df3.median() # 中位数标准差反应分数的离散程度。std()。数字越大分数差距越大。df3.std() # 标准差极值。每科成绩的最高分最低分这里仅演示最高分。df3.max() # 极大值已经知道最高分了可以把最高分对应的那个人给取出来。# 每科最高分对应的学生 df3.idxmax()最低分的人也可以取出来。# 每科最低分对应的学生 df3.idxmin()协方差和相关性首先造个数据再转换成 pandas 可执行的数据代码执行一下后面需要用。data { 1号数据: [1, 2, 3, 4, 5, 6, 7, 8, 9], 2号数据: [1, 2, 3, 4, 5, 6, 7, 8, 9] } df4 pd.DataFrame(data)先解释一下协方差和相关性是啥么东西。协方差协方差衡量两个变量共同变化的方向即一个变量变大时另一个是变大还是变小。公式如下正值两个变量同向变化。比如身高和体重身高越高体重通常也越重。负值两个变量反向变化。比如汽车速度和到达目的地所需时间速度越快时间越少。零或者接近零两个变量之间没有线性关系。我来翻译成人话衡量两个数据的变化趋势是否是一样的。先看一下这两组数据。协方差是 cov是两组数据进行对比把1号数据作为主体去和2号数据进行对比。运行一下。df4[1号数据].cov(df4[2号数据])这里得到 7.5是比较高的。观察1号数据和2号数据这俩确实也是要增就一起增的。可以把2号数据反向排序看一看。重新定义一下。然后再计算一下协方差运行出来。data2 { 1号数据: [1, 2, 3, 4, 5, 6, 7, 8, 9], 2号数据: [9, 8, 7, 6, 5, 4, 3, 2, 1] } df5 pd.DataFrame(data2) df5[1号数据].cov(df5[2号数据])得出的结果是-7.5。负的话就是负相关。相关性通常指皮尔逊相关系数相关性不仅衡量两个变量共同变化的方向还衡量这种关系的强度和大小它是标准化后的协方差。公示如下取值范围固定在 -1 到 1 之间。1完全正相关散点图是一条向上的直线0无线性相关散点图像一片云-1完全负相关散点图是一条向下的直线。不受单位的影响 无论你用厘米还是米相关性系数的值都是一样的。用的是 corr我们来看一下 data2 里面这两组数据的相关性。df5[1号数据].corr(df5[2号数据])我们可以看到得到的结果是 -1也就是这两组数据呈负相关关系。异常值处理填充先定义一下数据并且转换成 pandas 可操作对象。运行一下后面要用。data { 月份: [1月, 2月, 3月, 4月, 5月], 房租: [2000, 2000, 2000, 2000, 2000], 水电: [300, 280, None, 310, 290], 餐费: [500, None, 480, 600, 560], 交通: [150, 140, 150, None, None] } df5 pd.DataFrame(data)建立完成后看一看这个数据。df5这里面有一些缺失的值。向后填充bfill()向后填充bfill()3月份的水电为空用4月份的310填充餐费和交通同理。这个是向后填充。df5.bfill()我们可以看到这种填充方式有一定的缺陷交通的两个空值后面没有值了就会拿不到。指定值填充fillna()还可以指定值填充用 fillna()比如想用 0 填充就传入 0。运行一下看看效果。df5.fillna(0)可以看到之前的空值现在都变成了 0。这样是方便计算的能极大保留数据的准确性。异常值处理去重统计替换去重完全一样的进行去重。先造个数据然后使用对象的 drop_duplicates() 去重。sales_data pd.DataFrame({ product_id: [A, B, A, C, B, C, A], sale_date: [2024-10-1, 2024-10-2, 2024-10-3, 2024-10-4, 2024-10-5, 2024-10-6, 2024-10-1], sales_amount: [100, 200, 150, 300, 250, 350, 100] }) # 完全一样的就进行去重 sales_data.drop_duplicates()我们可以看到这个结果因为最后的一行和第一行是完全重复的所以把最后的重复行删除了。有的时候在删除之前会统计一下到底哪些数据是比较多的。对象里的方法直接点上 duplicated()可以得到一个表格数据得到一个 series看一下是不是一样的。如果是一样的就会标记为 True。sales_data.duplicated()然后可以用 sum() 求一下和。这样可以去看一下到底多少数据是重复的。sales_data.duplicated().sum()返回了 1说明有 1 组数据是重复的。这个功能是逐行检查是否有重复返回一个布尔数据最后用 sum() 统计 True 的数量。替换先取值看的是想要替换的是哪一个值然后用 replace()。比如我们替换 A 的日期把 2024 年 10 月 1 号替换成 2026 年 4 月 6 号。sales_data[sale_date].replace(2024-10-1, 2026-4-6)可以看到替换成功了。统计数据准备。执行一下后面要用。data { name: [john, jack, rose, bob], Order Date: [2024-10-1, 2024-10-2, 2024-10-3, 2024-10-4], Order Details: [2杯卡布奇诺4杯拿铁, 4杯咖啡3杯摩卡, 3杯拿铁, 2杯卡布奇诺2杯咖啡] } df pd.DataFrame(data)我们对这个数据进行操作现在的问题是“有多少人点了咖啡”很显然要把 “咖啡” 提取出来。但是现在要注意提取的时候有一些问题“咖啡” 是在哪个数据当中很显然在最后一列。Order Details: [2杯卡布奇诺4杯拿铁, 4杯咖啡3杯摩卡, 3杯拿铁, 2杯卡布奇诺2杯咖啡] 这列里面有 “咖啡”。所以我们要先把这个字段取出来。现在要更加细微控制到每个字符用 str用 count() 统计咖啡的数量。统计完成之后再用 sum() 去求和这里面有个问题马上会补充。先这么跑一下看看结果。# 统计有多少人点了咖啡 df6[Order Details].str.count(咖啡).sum()执行之后返回的是 2也就是只点了两杯咖啡。这不能算合格因为只统计了 “咖啡” 这两个字的数量这是不太合理的。还要继续考虑。这种方式能够取出来但不严谨。把这行代码注释掉。前面不用动str 里面有一个 extract()加上这个东西就可以引入正则。首先要取多少倍可以用分组的形式把数据拿过来一般都是数字数字写上 \d写上加号是因为有多个后面有多少个其实没太大作用因为不涉及到贪婪和非贪婪的。现在统计的是数字数字后面如果是其他东西是统计不出来的。这个是第一步后面有一个 “杯咖啡”这是一整块的内容。这算是合格了还要考虑到转义字符前面加上 r之后再进行操作。先来运行一下看看能否筛选到值。# 统计有多少人点了咖啡 # df6[Order Details].str.count(咖啡).sum() df6[Order Details].str.extract(r(\d)杯咖啡)运行之后发现得到了 2 和 4 这两个值。把它保存成一个变量my_data然后把空值填充为 0再去求和统计一下。发现报错了。因为当前的 my_data 里存入的数据是字符串无法进行聚合还要再修改一下。可以用 astype() 转换一般转换成 float。再去运行一下。# 统计有多少人点了咖啡 # df6[Order Details].str.count(咖啡).sum() my_data df6[Order Details].str.extract(r(\d)杯咖啡).astype(float) my_data.fillna(0).sum()运行之后可以看到是 6 杯。这个就是升级的版本。数据分割有一些数字的拆分有的时候数据不是规规矩矩给到手的需要把内容单独拆分为一个表格。df8 pd.DataFrame({ 岗位: [新媒体运营、五险一金、5-10K, Python开发工程师、15-20K、14薪, 其他岗位], 要求: [长沙-经验不限-本科, 北京-2年以上-学历不限, 某地-某经验-某学历] })把 要求 内的数据拆出来。用 str 里的 split()传入 - 来进行切割。my_data df8[要求].str.split(-) my_data可以看到成功依据 - 来把数据拆成了三个部分地点经验以及学历。这时候已经拆完了拆完还需要合并到表格当中去。合并用到的方法是 concat()首先要把原来的那个数据删除删掉要求df8.drop(columns [要求])。删完了之后合并数据以列为准所以设置 axis1。左右并排列数增加。# 切分之后怎么合并到表格中 pd.concat([df8.drop(columns[要求]), my_data],axis1)合并进去了但要求这里的数据是一个列表。还需要在这段代码前加点东西处理一下。把 my_data 的列名定义好。刚刚合并的时候只是把要求里面的数据删掉然后替换掉了并且还要把 my_data 改为 DataFrame 类型。也就是 my_data df8[要求].str.split(-) 改为 my_data df8[要求].str.split(-, expandTrue)。我们把这几行代码放入一个单元格里重新执行一下。my_data df8[要求].str.split(-,expandTrue) # 如果不加expand这就是一个SeriesSeries没有columns属性 my_data my_data.columns[地点,学历,经验] # 切分之后怎么合并到表格中 pd.concat([df8.drop(columns[要求]), my_data],axis1)下一篇介绍 DataFrame 的分组聚合以及透视表。

更多文章