python 数据分析--数据处理工具Pandas(2)

前面的学习中主要了解了Pandas如何构造序列和数据框,如何读取和写入各种格式的数据,以及如何对数据进行初步描述,本文将进一步了解Pandas如何处理字符串和日期数据,数据清洗,获取数据子集,透视表,分组聚合操作等内容。

4. Pandas处理字符串和日期数据

待处理的数据表[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pIBfCT7G-1585923940264)(attachment:image.png)]

数据处理要求:

  • 更改出生日期birthday和手机号tel两个字段的数据类型。
  • 根据出生日期birthday和开始工作日期start_work两个字段新增年龄和工龄两个字段。
  • 将手机号tel的中间四位隐藏起来。
  • 根据邮箱信息新增邮箱域名字段。
  • 基于other字段取出每个人员的专业信息。
import pandas as pd

#读入数据
employee_info = pd.read_excel(r"E:/Data/3/data_test03.xlsx",header=0)
employee_info.dtypes
name                  object
gender                object
birthday              object
start_work    datetime64[ns]
income                 int64
tel                    int64
email                 object
other                 object
dtype: object
# 更改数据类型
employee_info.birthday = pd.to_datetime(employee_info.birthday, format="%Y/%m/%d")
employee_info.tel = employee_info.tel.astype('str')
employee_info.dtypes
name                  object
gender                object
birthday      datetime64[ns]
start_work    datetime64[ns]
income                 int64
tel                   object
email                 object
other                 object
dtype: object
# 新增年龄和工龄字段
# 年龄 = 当天日期的年份 - 生日那一天的年份
# 工龄 = 当天日期的年份 - 开始工作那一天的年份
employee_info['age'] = pd.datetime.today().year - employee_info.birthday.dt.year
employee_info['workage'] = pd.datetime.today().year - employee_info.start_work.dt.year

# 新增邮箱域名字段
# 字符串分割、巧用了匿名函数 lambda
# split分出来的数据有两部分[邮箱名,域名],域名的索引为1

employee_info['email_domain'] = employee_info.email.apply(func = lambda x: x.split('@')[1])  
employee_info
namegenderbirthdaystart_workincometelemailotherageworkageemail_domain
0赵一1989-08-102012-09-081500013611011234zhaoyi@qq.com{教育:本科,专业:电子商务,爱好:运动}318qq.com
1王二1990-10-022014-03-061250013500012234wanger@163.com{教育:大专,专业:汽修,爱好:}306163.com
2张三1987-03-122009-01-081850013515273330zhangsan@qq.com{教育:本科,专业:数学,爱好:打篮球}3311qq.com
3李四1991-08-162014-06-041300013923673388lisi@gmail.com{教育:硕士,专业:统计学,爱好:唱歌}296gmail.com
4刘五1992-05-242014-08-10850017823117890liuwu@qq.com{教育:本科,专业:美术,爱好:}286qq.com
5雷六1986-12-102010-03-101500013712345612leiliu@126.com{教育:本科,专业:化学,爱好:钓鱼}3410126.com
6贾七1993-04-102015-08-01900013178734511jiaqi@136.com{教育:硕士,专业:物理,爱好:健身}275136.com
7吴八1988-07-192014-10-121350017822335317wuba@qq.com{教育:本科,专业:政治学,爱好:读书}326qq.com
# 隐藏电话号码中间四位数
# 字符串替换,巧用了匿名函数lambda
employee_info.tel = employee_info.tel.apply(func = lambda x: x.replace(x[3:7],'****'))
employee_info
namegenderbirthdaystart_workincometelemailotherageworkageemail_domain
0赵一1989-08-102012-09-0815000136****1234zhaoyi@qq.com{教育:本科,专业:电子商务,爱好:运动}318qq.com
1王二1990-10-022014-03-0612500135****2234wanger@163.com{教育:大专,专业:汽修,爱好:}306163.com
2张三1987-03-122009-01-0818500135****3330zhangsan@qq.com{教育:本科,专业:数学,爱好:打篮球}3311qq.com
3李四1991-08-162014-06-0413000139****3388lisi@gmail.com{教育:硕士,专业:统计学,爱好:唱歌}296gmail.com
4刘五1992-05-242014-08-108500178****7890liuwu@qq.com{教育:本科,专业:美术,爱好:}286qq.com
5雷六1986-12-102010-03-1015000137****5612leiliu@126.com{教育:本科,专业:化学,爱好:钓鱼}3410126.com
6贾七1993-04-102015-08-019000131****4511jiaqi@136.com{教育:硕士,专业:物理,爱好:健身}275136.com
7吴八1988-07-192014-10-1213500178****5317wuba@qq.com{教育:本科,专业:政治学,爱好:读书}326qq.com
# 根据other 字段提取每个人的专业信息
# 用正则表达式匹配专业字段,主要在匹配时 : 和 , 均为中文输入法,英文无法匹配的
employee_info['profession'] = employee_info.other.str.findall('专业:(.*?),')

# findall 提取出来的数据带[] 去除[]
employee_info.profession = employee_info.profession.astype('str')
employee_info.profession = employee_info.profession.apply(func = lambda x: x.replace(x[:],x[1:-1]))
employee_info.profession = employee_info.profession.apply(func = lambda x: x.replace(x[0],' '))
employee_info.head()
namegenderbirthdaystart_workincometelemailotherageworkageemail_domainprofession
0赵一1989-08-102012-09-0815000136****1234zhaoyi@qq.com{教育:本科,专业:电子商务,爱好:运动}318qq.com电子商务
1王二1990-10-022014-03-0612500135****2234wanger@163.com{教育:大专,专业:汽修,爱好:}306163.com汽修
2张三1987-03-122009-01-0818500135****3330zhangsan@qq.com{教育:本科,专业:数学,爱好:打篮球}3311qq.com数学
3李四1991-08-162014-06-0413000139****3388lisi@gmail.com{教育:硕士,专业:统计学,爱好:唱歌}296gmail.com统计学
4刘五1992-05-242014-08-108500178****7890liuwu@qq.com{教育:本科,专业:美术,爱好:}286qq.com美术
# 剔除birthday,start_work和other变量
# 需要将axis参数设置为1,因为默认drop方法是用来删除数据框中的行记录。
employee_info.drop(['birthday','start_work','other'], axis=1,inplace=True)
employee_info
namegenderincometelemailageworkageemail_domainprofession
0赵一15000136****1234zhaoyi@qq.com318qq.com电子商务
1王二12500135****2234wanger@163.com306163.com汽修
2张三18500135****3330zhangsan@qq.com3311qq.com数学
3李四13000139****3388lisi@gmail.com296gmail.com统计学
4刘五8500178****7890liuwu@qq.com286qq.com美术
5雷六15000137****5612leiliu@126.com3410126.com化学
6贾七9000131****4511jiaqi@136.com275136.com物理
7吴八13500178****5317wuba@qq.com326qq.com政治学

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-viK3mTil-1585923940265)(attachment:image.png)]

5. Pandas 数据清洗

在数据处理过程中,一般都需要进行数据的清洗工作,数据清洗过程主要负责看数据集是否存在重复、是否存在缺失、数据是否具有完整性和一致性、数据中是否存在异常值等。这些问题都不利于数据分析,需要加以处理。

5.1 重复观测处理

在搜集数据过程中,可能会存在重复观测的出现,例如通过网络爬虫,就比较容易产生重复数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-woWeaqTU-1585923940266)(attachment:image.png)]
上面的数据就是通过爬虫获得某APP市场中电商类APP的下载量数据(部分),通过肉眼,是能够发现这10行数据中的重复项的,例如,唯品会出现了两次、当当出现了三次。如果搜集上来的数据不是10行,而是10万行,甚至更多时,就无法通过肉眼的方式检测数据是否存在重复项了。

import pandas as pd 

dsapp = pd.read_excel(r"E:/Data/3/data_test04.xlsx")

# 重复观测检测
#使用duplicated方法进行验证,但是该方法返回的是数据集每一行的检验结果,即10行数据会返回10个bool值。
# 加any进行判断,只要有一个检测到,就代表有

print("是否存在重复观测:\n",any(dsapp.duplicated()))
是否存在重复观测:
 True
#删除重复数据

dsapp.drop_duplicates(inplace=True)
dsapp
appcategoryappnamecommentsinstalllovesizeupdate
0网上购物-商城-团购-优惠-快递每日优鲜1297204.7万89.00%15.16MB2017年10月11日
1网上购物-商城苏宁易购5777996.8万73.00%58.9MB2017年09月21日
2网上购物-商城-优惠唯品会25437090.1万86.00%41.43MB2017年10月13日
4网上购物-商城拼多多19213841.9万95.00%13.35MB2017年10月11日
5网上购物-商城-优惠寺库奢侈品1964175.4万100.00%17.21MB2017年09月30日
6网上购物-商城淘宝142444.6亿68.00%73.78MB2017年10月13日
7网上购物-商城-团购-优惠当当1341615.3万61.00%37.01MB2017年10月17日

5.2 缺失值处理

缺失值是指数据集中的某些观测存在遗漏的指标值,缺失值的存在同样会影响到数据分析和挖掘的结果。导致观测的缺失可能有两方面原因,一方面是人为原因(如记录过程中的遗漏、个人隐私而不愿透露等),另一方面是机器或设备的故障所导致(如断电或设备老化等原因)。一般而言,当遇到缺失值(Python中用NaN表示)时,可以采用三种方法处置,分别是删除法、替换法和插补法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MysIVi7X-1585923940266)(attachment:image.png)]
上面的数据来自于某游戏公司的用户注册信息(仅以10行记录为例,该数据集中存在4条红色标注的缺失观测。

import pandas as pd 

Game_user = pd.read_excel(r"E:\Data\3\data_test05.xlsx")
print("数据集是否存在缺失值:\n",any(Game_user.isnull()))

数据集是否存在缺失值:
 True
5.2.1 删除法
# 1. 删除法处理
# 在副本上删除所有缺失的行,Game_user 不变
Game_user.dropna()    
uidregit_dategenderageincome
0812004572016-10-30M23.06500.0
1812011352016-11-08M27.010300.0
3846392812017-04-17M26.06000.0
6638819432015-10-07M21.010000.0
8776383512016-07-12M25.018000.0
# 删除缺失值最多的那一列变量
Game_user.drop('age',axis=1) 
uidregit_dategenderincome
0812004572016-10-30M6500.0
1812011352016-11-08M10300.0
2800437822016-10-13F13500.0
3846392812017-04-17M6000.0
4734998012016-03-21NaN4500.0
5723995102016-01-18MNaN
6638819432015-10-07M10000.0
7354426902015-04-10F5800.0
8776383512016-07-12M18000.0
9852001892017-05-18MNaN
5.2.2 替换法
# 2. 替换法
# 缺失值用前一行值填充
Game_user.fillna(method='ffill')
uidregit_dategenderageincome
0812004572016-10-30M23.06500.0
1812011352016-11-08M27.010300.0
2800437822016-10-13F27.013500.0
3846392812017-04-17M26.06000.0
4734998012016-03-21M26.04500.0
5723995102016-01-18M19.04500.0
6638819432015-10-07M21.010000.0
7354426902015-04-10F21.05800.0
8776383512016-07-12M25.018000.0
9852001892017-05-18M22.018000.0
# 缺失值用后一行填充
Game_user.fillna(method='bfill')
uidregit_dategenderageincome
0812004572016-10-30M23.06500.0
1812011352016-11-08M27.010300.0
2800437822016-10-13F26.013500.0
3846392812017-04-17M26.06000.0
4734998012016-03-21M19.04500.0
5723995102016-01-18M19.010000.0
6638819432015-10-07M21.010000.0
7354426902015-04-10F25.05800.0
8776383512016-07-12M25.018000.0
9852001892017-05-18M22.0NaN

method参数可以接受’ffill’和’bfill’两种值,分别代表前向填充和后向填充。前向填充是指用缺失值的前一个值替换(如左表所示),而后向填充则表示用缺失值的后一个值替换(如右表所示)。右表中的最后一个记录仍包含缺失值,是因为后向填充法找不到该缺失值的后一个值用于替换。缺失值的前向填充或后向填充一般适用于时间序列型的数据集,因为这样的数据前后具有连贯性,而一般的独立性样本并不适用该方法。

# 常数替换
Game_user.fillna(value = 0)
uidregit_dategenderageincome
0812004572016-10-30M23.06500.0
1812011352016-11-08M27.010300.0
2800437822016-10-13F0.013500.0
3846392812017-04-17M26.06000.0
4734998012016-03-2100.04500.0
5723995102016-01-18M19.00.0
6638819432015-10-07M21.010000.0
7354426902015-04-10F0.05800.0
8776383512016-07-12M25.018000.0
9852001892017-05-18M22.00.0
# 统计值替换
Game_user.fillna(value= {'gender':Game_user.gender.mode()[0],
                         'age':Game_user.age.median(),
                        'income':Game_user.income.mean()})
uidregit_dategenderageincome
0812004572016-10-30M23.06500.0
1812011352016-11-08M27.010300.0
2800437822016-10-13F23.013500.0
3846392812017-04-17M26.06000.0
4734998012016-03-21M23.04500.0
5723995102016-01-18M19.09325.0
6638819432015-10-07M21.010000.0
7354426902015-04-10F23.05800.0
8776383512016-07-12M25.018000.0
9852001892017-05-18M22.09325.0

如上代码并没有实际改变df数据框的结果,因为dropna、drop和fillna方法并没有使inplace参数设置为True。可以在实际的学习和工作中挑选一个适当的缺失值处理方法,然后将该方法中的inplace参数设置为True,进而可以真正地改变你所处理的数据集。

5.3 异常值处理

异常值是指那些远离正常值的观测,即“不合群”观测。导致异常值的出现一般是人为的记录错误或者是设备的故障等,异常值的出现会对模型的创建和预测产生严重的后果。当然异常值也不一定都是坏事,有些情况下,通过寻找异常值就能够给业务带来良好的发展,如销毁“钓鱼”网站、关闭“薅羊毛”用户的权限等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cg7IWD46-1585923940268)(attachment:image.png)]

这两种方法的选择标准如下,如果数据近似服从正态分布时,优先选择n个标准差法,因为数据的分布相对比较对称;否则优先选择箱线图法,因为分位数并不会受到极端值的影响。当数据存在异常时,一般可以使用删除法将异常值删除(前提是异常观测的比例不能太大)、替换法(可以考虑使用低于判别上限的最大值或高于判别下限的最小值替换、使用均值或中位数替换等)。下面将以年为单位的太阳黑子个数为例(时间范围:1700—1988),识别并处理异常值:

# 1. 看两种方法是否都存在异常值

import pandas as pd

#数据读入
sunspots = pd.read_table(r"E:\Data\3\sunspots.csv", sep = ',')

# 标准差法检测异常值
xmean = sunspots.counts.mean()
xstd = sunspots.counts.std()
print("标准差法异常上限检测:\n",any(sunspots > xmean + 2 * xstd))
print("标准差法异常下限检测:\n",any(sunspots < xmean - 2 * xstd))

# 箱线法检测异常值
Q1 = sunspots.counts.quantile(q = 0.25)
Q3 = sunspots.counts.quantile(q = 0.75)
IQR = Q3 - Q1
print("箱线法异常上限检测:\n",any(sunspots  > Q3 + 1.5 * IQR ))
print("箱线法异常下限检测:\n",any(sunspots < Q1 - 1.5 * IQR ))
标准差法异常上限检测:
 True
标准差法异常下限检测:
 True
箱线法异常上限检测:
 True
箱线法异常下限检测:
 True

不管是标准差检验法还是箱线图检验法,都发现太阳黑子数据中存在异常值,而且异常值都是超过上限临界值的。接下来,通过绘制太阳黑子数量的直方图和核密度曲线图,用于检验数据是否近似服从正态分布,进而选择一个最终的异常值判别方法:

# 2. 绘制直方图和核密度曲线图,确定采用何种异常判别法。
import matplotlib.pyplot as plt

plt.style.use('ggplot')
sunspots.counts.plot(kind = 'hist', bins =30, normed = True)
sunspots.counts.plot(kind = 'kde')
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9VooviMK-1585923940268)(output_37_0.png)]
很明显,不管是直方图还是核密度曲线,都不服从正太分布,所以采用箱线法进行异常检测,此处也选用替换法来处理异常值。

# 替换法处理异常值:
print("异常值替换前的数据统计特征:\n", sunspots.counts.describe())
print("\n")

#箱线图中的异常值判别上限
UL = Q3 + 1.5 * IQR
print("判别异常值的上限临界值:\n", UL)

# 从数据中找出低于判别上限的最大值
replace_value = sunspots.counts[sunspots.counts < UL].max()
sunspots.counts[sunspots.counts > UL] = replace_value
print("\n")
print("异常值替换后的数据统计特征:\n", sunspots.counts.describe())
异常值替换前的数据统计特征:
 count    289.000000
mean      48.613495
std       39.474103
min        0.000000
25%       15.600000
50%       39.000000
75%       68.900000
max      190.200000
Name: counts, dtype: float64


判别异常值的上限临界值:
 148.85000000000002


异常值替换后的数据统计特征:
 count    289.000000
mean      48.066090
std       37.918895
min        0.000000
25%       15.600000
50%       39.000000
75%       68.900000
max      141.700000
Name: counts, dtype: float64


D:\Anaconda\lib\site-packages\ipykernel_launcher.py:11: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  # This is added back by InteractiveShellApp.init_path()

6. 获取数据子集

有时数据读入后并不是对整体数据进行分析,而是数据中的部分子集,例如,对于地铁乘客量可能只关心某些时间段的流量、对于商品的交易可能只需要分析某些颜色的价格变动、对于医疗诊断数据可能只对某个年龄段的人群感兴趣等。

在Pandas模块中实现数据框子集的获取可以使用iloc、loc和ix三种“方法”,这三种方法既可以对数据行进行筛选,也可以实现变量的挑选它们的语法可以表示成[rows_select,cols_select]。

df1 = pd.DataFrame({'name':['张三','李四','王二','丁一','李五'],
                    'gender':['男','女','女','女','男'],
                    'age':[23,26,22,25,27]},
                    columns = ['name','gender','age'])
df1
namegenderage
0张三23
1李四26
2王二22
3丁一25
4李五27
# 取出所有女性的姓名和年龄
df1.iloc[1:4,[0,2]]
nameage
1李四26
2王二22
3丁一25
df1.loc[1:3,['name','age']]
nameage
1李四26
2王二22
3丁一25
df1.ix[1:3,[0,2]]
nameage
1李四26
2王二22
3丁一25
# 假如数据没有行号
df2 = df1.set_index('name')
df2

df2.iloc[1:4,:]
df2.loc[['李四','王二','丁一'],:]

genderage
name
李四26
王二22
丁一25
# df2.ix[1:4,:]

在上面的df1数据集中,如何返回所有男性的姓名和年龄,如果是基于条件的记录筛选,只能使用loc和ix两种方法。正如代码所示,对iloc方法的那行代码做注释,是因为iloc不允许使用条件筛选。

# 使用筛选条件,取出所有男性的姓名和年龄
# df1.iloc[df1.gender == '男',]
df1.loc[df1.gender == '男',['name','age']]
df1.ix[df1.gender == '男',['name','age']]
nameage
0张三23
4李五27

7. 透视表、合并与连接、分组聚合

7.1 透视表

Pandas模块提供了实现透视表功能的pivot_table函数,*该功能的主要目的就是实现数据的汇总统计。例如,按照某个分组变量统计商品的平均价格、销售数量、最大利润等,或者按照某两个分组变量构成统计学中的列联表(计数统计),甚至是基于多个分组变量统计各组合下的均值、中位数、总和等。

  • data:指定需要构造透视表的数据集。
  • values:指定需要拉入“数值”框的字段列表。
  • index:指定需要拉入“行标签”框的字段列表。
  • columns:指定需要拉入“列标签”框的字段列表。
  • aggfunc:指定数值的统计函数,默认为统计均值,也可以指定numpy模块中的其他统计函数。
  • fill_value:指定一个标量,用于填充缺失值。
  • margins:bool类型参数,是否需要显示行或列的总计值,默认为False。
  • dropna:bool类型参数,是否需要删除整列为缺失的字段,默认为True。
  • margins_name:指定行或列的总计名称,默认为All。
# 数据读取
diamonds = pd.read_table(r"E:/Data/3/diamonds.csv", sep = ',')

# 单个分组变量的均值统计
pd.pivot_table(data= diamonds, index = 'color',values = 'price', 
               margins=True, margins_name='总计')
price
color
D3169.954096
E3076.752475
F3724.886397
G3999.135671
H4486.669196
I5091.874954
J5323.818020
总计3932.799722
import numpy as np
pd.pivot_table(data=diamonds, index='clarity', columns='cut', values = 'carat', 
               aggfunc= np.size, margins=True, margins_name='总计')
cutFairGoodIdealPremiumVery Good总计
clarity
I1210.096.0146.0205.084.0741.0
IF9.071.01212.0230.0268.01790.0
SI1408.01560.04282.03575.03240.013065.0
SI2466.01081.02598.02949.02100.09194.0
VS1170.0648.03589.01989.01775.08171.0
VS2261.0978.05071.03357.02591.012258.0
VVS117.0186.02047.0616.0789.03655.0
VVS269.0286.02606.0870.01235.05066.0
总计1610.04906.021551.013791.012082.053940.0

7.2 多表合并

Pandas模块同样提供了关于多表之间的合并操作concat函数:

  • objs:指定需要合并的对象,可以是序列、数据框或面板数据构成的列表。
  • axis:指定数据合并的轴,默认为0,表示合并多个数据的行,如果为1,就表示合并多个数据的列。
  • join:指定合并的方式,默认为outer,表示合并所有数据,如果改为inner,表示合并公共部分的数据。
  • join_axes:合并数据后,指定保留的数据轴。
  • ignore_index:bool类型的参数,表示是否忽略原数据集的索引,默认为False,如果设True,就表示忽略原索引并生成新索引。
  • keys:为合并后的数据添加新索引,用于区分各个数据部分。
df1 = pd.DataFrame({'name':['张三','李四','王二'],
                    'gender':['男','女','女'],
                    'age':[23,26,22,]} )
df2 = pd.DataFrame({'name':['丁一','李五'],
                    'gender':['女','男'],
                    'age':[25,27]} )
pd.concat([df1,df2],keys = ['df1','df2'], axis=0)
agegendername
df1023张三
126李四
222王二
df2025丁一
127李五
df2 = pd.DataFrame({'Name':['丁一','李五'],
                    'gender':['女','男'],
                    'age':[25,27]} )
pd.concat([df1,df2],keys = ['df1','df2'])
Nameagegendername
df10NaN23张三
1NaN26李四
2NaN22王二
df20丁一25NaN
1李五27NaN

7.3 多表连接

Pandas模块同样提供了关于多表之间的连接操作merge函数,函数的最大缺点是,每次只能操作两张数据表的连接,如果有n张表需要连接,则必须经过n-1次的merge函数使用。

  • left:指定需要连接的主表。
  • right:指定需要连接的辅表。
  • how:指定连接方式,默认为inner内连,还有其他选项,如左连left、右连right和外连
  • outer。on:指定连接两张表的共同字段。
  • left_on:指定主表中需要连接的共同字段。
  • right_on:指定辅表中需要连接的共同字段。
  • left_index:bool类型参数,是否将主表中的行索引用作表连接的共同字段,默认为False。
  • right_index:bool类型参数,是否将辅表中的行索引用作表连接的共同字段,默认为False。
  • sort:bool类型参数,是否对连接后的数据按照共同字段排序,默认为False。
  • suffixes:如果数据连接的结果中存在重叠的变量名,则使用各自的前缀进行区分。
# 构造数据集
df3 = pd.DataFrame({'id':[1,2,3,4,5],'name':['张三','李四','王二','丁一','赵五'],'age':[27,24,25,23,25],'gender':['男','男','男','女','女']})
df4 = pd.DataFrame({'Id':[1,2,2,4,4,4,5],'kemu':['科目1','科目1','科目2','科目1','科目2','科目3','科目1'],'score':[83,81,87,75,86,74,88]})
df5 = pd.DataFrame({'id':[1,3,5],'name':['张三','王二','赵五'],'income':[13500,18000,15000]})

# 三表的数据连接
# 首先df3和df4连接
merge1 = pd.merge(left = df3, right = df4, how = 'left', left_on='id', right_on='Id')
merge1

# 再将连接结果与df5连接
merge2 = pd.merge(left = merge1, right = df5, how = 'left')
merge2
agegenderidnameIdkemuscoreincome
0271张三1.0科目183.013500.0
1242李四2.0科目181.0NaN
2242李四2.0科目287.0NaN
3253王二NaNNaNNaN18000.0
4234丁一4.0科目175.0NaN
5234丁一4.0科目286.0NaN
6234丁一4.0科目374.0NaN
7255赵五5.0科目188.015000.0

7.4 分组聚合

# 通过groupby方法,指定分组变量
grouped = diamonds.groupby(by = ['color','cut'])

# 对分组变量进行统计汇总
result = grouped.aggregate({'color':np.size, 'carat':np.min, 'price':np.mean})

# 调整变量名的顺序
result = pd.DataFrame(result, columns=['color','carat','price'])

# 数据集重命名
result.rename(columns={'color':'counts','carat':'min_weight','price':'avg_price'}, inplace=True)

# 将行索引变量数据框的变量
# result.reset_index(inplace=True)
result
countsmin_weightavg_price
colorcut
DFair1630.254291.061350
Good6620.233405.382175
Ideal28340.202629.094566
Premium16030.203631.292576
Very Good15130.233470.467284
EFair2240.223682.312500
Good9330.233423.644159
Ideal39030.202597.550090
Premium23370.203538.914420
Very Good24000.203214.652083
FFair3120.253827.003205
Good9090.233495.750275
Ideal38260.233374.939362
Premium23310.204324.890176
Very Good21640.233778.820240
GFair3140.234239.254777
Good8710.234123.482204
Ideal48840.233720.706388
Premium29240.234500.742134
Very Good22990.233872.753806
HFair3030.335135.683168
Good7020.254276.254986
Ideal31150.233889.334831
Premium23600.235216.706780
Very Good18240.234535.390351
IFair1750.414685.445714
Good5220.305078.532567
Ideal20930.234451.970377
Premium14280.235946.180672
Very Good12040.245255.879568
JFair1190.304975.655462
Good3070.284574.172638
Ideal8960.234918.186384
Premium8080.306294.591584
Very Good6780.245103.513274
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值