数据合并
使用Join()合并,合并的方式是根据行和行进行合并。
# 使用join合并,着重关注的是 行的合并import pandas as pd df1=pd.DataFrame({'Red':[1,3,5],'Green':[5,0,3]},index=list('abc'))df2=pd.DataFrame({'Blue':[1,9,8],'Yellow':[6,6,7]},index=list('cde'))print(df1)print(df2)''' Red Greena 1 5b 3 0c 5 3 Blue Yellowc 1 6d 9 6e 8 7'''# 简单合并(默认是left左连接,以左侧df1为基础,连接右侧中index相同的行)df3=df1.join(df2,how='left')print(df3)''' Red Green Blue Yellowa 1 5 NaN NaNb 3 0 NaN NaNc 5 3 1.0 6.0'''# 右连接:以左侧df2为基础,连接左侧df1中index相同的行df4 = df1.join(df2,how='right')print(df4)''' Red Green Blue Yellowc 5.0 3.0 1 6d NaN NaN 9 6e NaN NaN 8 7'''# 外连接(左右两侧的index全连接,取并集)df5 = df1.join(df2,how='outer')print(df5)''' Red Green Blue Yellowa 1.0 5.0 NaN NaNb 3.0 0.0 NaN NaNc 5.0 3.0 1.0 6.0d NaN NaN 9.0 6.0e NaN NaN 8.0 7.0'''# 合并多个DataFrame对象df6 = pd.DataFrame({'Brown':[3,4,5],'White':[1,1,2]},index=list('aed'))print(df6)df7 = df2.join([df1,df6])print(df7)''' Brown Whitea 3 1e 4 1d 5 2 Blue Yellow Red Green Brown Whitec 1 6 5.0 3.0 NaN NaNd 9 6 NaN NaN 5.0 2.0e 8 7 NaN NaN 4.0 1.0'''
merge()函数是列和列合并。
# 使用merge,着重关注的是列的合并df1=pd.DataFrame({'名字':list('ABCDE'),'性别'['男','女','男','男','女'],'职称':['副教授','讲师','助教','教授','助教']},index=range(1001,1006))print(df1)''' 名字 性别 职称1001 A 男 副教授1002 B 女 讲师1003 C 男 助教1004 D 男 教授1005 E 女 助教'''df2=pd.DataFrame({'名字':list('ABDAX'),'课程':['C++','计算机导论','汇编','数据结构','马克思原理'],'职称':['副教授','讲师','教授','副教授','讲师']},index=[1001,1002,1004,1001,3001])print(df2)''' 名字 课程 职称1001 A C++ 副教授1002 B 计算机导论 讲师1004 D 汇编 教授1001 A 数据结构 副教授3001 X 马克思原理 讲师'''# merge默认根据左右对象中出现同名的列作为连接的键,且连接方式是how=’inner’内连接,内连接取两者交集print(pd.merge(df1,df2))''' 名字 性别 职称 课程0 A 男 副教授 C++1 A 男 副教授 数据结构2 B 女 讲师 计算机导论3 D 男 教授 汇编'''# 指定列名合并,suffixes来设置重复列的名字print(pd.merge(df1,df2,on='名字',suffixes=['_1','_2']))''' 名字 性别 职称_1 课程 职称_20 A 男 副教授 C++ 副教授1 A 男 副教授 数据结构 副教授2 B 女 讲师 计算机导论 讲师3 D 男 教授 汇编 教授'''# 左连接,根据左侧为准print(pd.merge(df1,df2,how='left'))'''名字 性别 职称 课程0 A 男 副教授 C++1 A 男 副教授 数据结构2 B 女 讲师 计算机导论3 C 男 助教 NaN4 D 男 教授 汇编5 E 女 助教 NaN'''# 右连接,根据右侧为准print(pd.merge(df1,df2,how='right'))''' 名字 性别 职称 课程0 A 男 副教授 C++1 A 男 副教授 数据结构2 B 女 讲师 计算机导论3 D 男 教授 汇编4 X NaN 讲师 马克思原理'''# 合并所有 取并集print(pd.merge(df1,df2,how='outer'))''' 名字 性别 职称 课程0 A 男 副教授 C++1 A 男 副教授 数据结构2 B 女 讲师 计算机导论3 C 男 助教 NaN4 D 男 教授 汇编5 E 女 助教 NaN6 X NaN 讲师 马克思原理'''# 根据多个键进行连接print(pd.merge(df1,df2,on=['职称','名字']))''' 名字 性别 职称 课程0 A 男 副教授 C++1 A 男 副教授 数据结构2 B 女 讲师 计算机导论3 D 男 教授 汇编'''
多层索引
这里简单介绍一下多层索引,多层级索引,将指标进行分层,索引具有层级结构,可以使得高维度的数据进行降维。
先来学习一下Series对象创建多层索引,并对索引对象查询。
import numpy as npimport pandas as pdfrom pandas import Series,DataFrame# Series创建多层索引s = Series(np.random.randint(0,150,size=6),index=list('abcdef'))print(s) # 原索引s = Series(np.random.randint(0,150,size=6),index=[['a','a','b','b','c','c'],['期中','期末','期中','期末','期中','期末']]) # 创建多层索引print(s)# Series索引对象的获取# 取一个第一级索引print(s['a'])# 取多个第一级索引print(s[['a','b']])# 根据索引获取值print(s['a','期末'])# loc方法取值 根据标签名取值print(s.loc['a'])print(s.loc[['a','b']])print(s.loc['a','期末'])# # iloc方法取值 根据位置取值 (iloc计算的是最内层索引,直接跳过层级,最内层开始取)print(s.iloc[1])print(s.iloc[1:4])# 注:列值中的数字是随机数,这里不附上运行结果,可以先把复制过去,全部注释,然后一行一行的运行
接下来介绍DataFrame创建多层索引的三种方式,及其索引对象的查询。
import numpy as npimport pandas as pdfrom pandas import Series,DataFrame# DataFrame创建多层索引# 方式一:index由两个数组组成df1 = DataFrame(np.random.randint(0,150,size=(6,4)),columns = ['shon','jack','meili','dafeng'], index = [['python','python','math','math','En','En'],['期中','期末','期中','期末','期中','期末']])print(df1)# 方式二:特定结构class1=['python','python','math','math','En','En']class2=['期中','期末','期中','期末','期中','期末']m_index2=pd.MultiIndex.from_arrays([class1,class2])df2=DataFrame(np.random.randint(0,150,(6,4)),index=m_index2)print(df2)# 方式三:乘法方式 product意为乘法class1=['python','math','En']class2=['期中','期末']m_index2=pd.MultiIndex.from_product([class1,class2])df3=DataFrame(np.random.randint(0,150,(6,4)),index=m_index2)print(df3)# Series索引对象的获取# 获取列:print(df1['shon'])# 一级索引print(df1.loc['python'])# 多个一级索引print(df1.loc[['python','math']])# 取一行print(df1.loc['python','期末'])# 取一值print(df1.loc['python','期末'][0])# iloc是只取最内层的索引的print(df2.iloc[0])
时间序列
时间序列是指将同一统计指标的数值按其发生的时间先后顺序排列而成的数列。时间序列分析的主要目的是根据已有的历史数据对未来进行预测。经济数据中大多数以时间序列的形式给出。根据观察时间的不同,时间序列中的时间可以是年份、季度、月份或其他任何时间形式,下面是如何创建时间序列。
import pandas as pdimport numpy as np# 1. 生成一段时间范围'''data_range(start、end、periods)函数主要用于生成一个固定频率的时间索引,使用时须指定三个参数中的两个参数值,否则报错。时间序列频率: D 日历日的每天 B 工作日的每天 H 每小时 T或min 每分钟 S 每秒 L或ms 每毫秒 U 每微秒 M 日历日的月底日期 BM 工作日的月底日期 MS 日历日的月初日期 BMS 工作日的月初日期freq:日期偏移量,取值为string, 默认为'D', freq='1h30min' freq='10D'periods:固定时期,取值为整数或None'''# -----------------date = pd.date_range(start='20200528',periods=10,freq='10D')print(date)'''DatetimeIndex(['2020-05-28', '2020-06-07', '2020-06-17', '2020-06-27', '2020-07-07', '2020-07-17', '2020-07-27', '2020-08-06', '2020-08-16', '2020-08-26'], dtype='datetime64[ns]', freq='10D')'''# 根据closed参数选择是否包含开始和结束时间closed=None,left包含开始时间,不包含结束时间,right与之相反。data_time =pd.date_range(start='2020-05-09',end='2020-05-14',closed='left')print(data_time)'''DatetimeIndex(['2020-05-09', '2020-05-10', '2020-05-11', '2020-05-12', '2020-05-13'], dtype='datetime64[ns]', freq='D')'''# 2. 时间序列在dataFrame中的作用# 可以将时间作为索引index = pd.date_range(start='20200101',periods=10)df = pd.Series(np.random.randint(0,10,size = 10),index=index)print(df)'''2020-01-01 42020-01-02 42020-01-03 02020-01-04 92020-01-05 62020-01-06 72020-01-07 52020-01-08 62020-01-09 42020-01-10 9Freq: D, dtype: int64'''# truncate这个函数将before指定日期之前的值全部过滤出去,after指定日期之后的值全部过滤出去.after = df.truncate(after='20200105')print(after)'''2020-01-01 92020-01-02 42020-01-03 72020-01-04 82020-01-05 9Freq: D, dtype: int64'''
# 接下来是对时间格式的处理,因为运行出来的结果较多,所以运行完一次,我们将print注释# 创建一个含有一千行的时间序列,时间跨度从2019年到2021年long_ts = pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2019',periods=1000))#print(long_ts)# 根据年份获取result = long_ts['2020']#print(result)# 年份和日期获取result = long_ts['2020-05']#print(result)# 使用切片result = long_ts['2020-05-01':'2020-05-06']# print(result)# 通过between_time()返回位于指定时间段的数据集index=pd.date_range("2020-03-17","2020-03-30",freq="2H")ts = pd.Series(np.random.randn(157),index=index)#print(ts.between_time("7:00","17:00"))# 这些操作也都适用于dataframeindex=pd.date_range('1/1/2020',periods=100)df = pd.DataFrame(np.random.randn(100,4),index=index)#print(df.loc['2020-04'])# 将时间戳转化成时间格式 某一个时间到1970年01月01日 00:00:00 的秒数或者毫秒数# 时间戳是现在时间到1970年01月01日 00:00:00的毫秒数或者秒数,可以将时间格式化表示成一个数值,方便时间的计算# 获取当前时间的时间戳import time #print(time.time()) #1590704235.606594#pd.to_datetime(1590704235.606594,unit='s')# 时区转换:utc是协调世界时,时区是以UTC的偏移量的形式表示的# 查询时区名字 选择Asia/Shanghaiimport pytz#print(pytz.common_timezones)#pd.to_datetime(1590704235.606594,unit='s').tz_localize('UTC').tz_convert('Asia/Shanghai')# 处理一列df = pd.DataFrame([1590704235.606594, 1590704295.606594, 1590704355.606594],columns = ['time_stamp'])# 先赋予标准时区,再转换到东八区#pd.to_datetime(df['time_stamp'],unit='s').dt.tz_localize('UTC').dt.tz_convert('Asia/Shanghai')# 处理中文格式日期#pd.to_datetime('2020年5月20日',format='%Y年%m月%d日')
分组聚合
这一部分我们学习对DataFrame的数据按照相应的格式进行分组,使用函数groupby()。
# 分组import pandas as pd import numpy as npdf=pd.DataFrame({'name':['BOSS','Lilei','Lilei','Han','BOSS','BOSS','Han','BOSS'], 'Year':[2018,2018,2018,2018,2019,2019,2019,2019], 'Salary':[999999,20000,25000,3000,9999999,999999,3500,999999], 'Bonus':[100000,20000,20000,5000,200000,300000,3000,400000]})display(df)''' name Year Salary Bonus0 BOSS 2018 999999 1000001 Lilei 2018 20000 200002 Lilei 2018 25000 200003 Han 2018 3000 50004 BOSS 2019 9999999 2000005 BOSS 2019 999999 3000006 Han 2019 3500 30007 BOSS 2019 999999 400000'''# 根据name这一列进行分组group_by_name=df.groupby('name')print(type(group_by_name))# 查看分组print(group_by_name.groups)# 分组后的数量#print(group_by_name.count())''' # df分组后返回的是一个DataFrameGroupBy的对象,不能直接查看它{'BOSS': Int64Index([0, 4, 5, 7], dtype='int64'),'Han': Int64Index([3, 6], dtype='int64'),'Lilei': Int64Index([1, 2], dtype='int64')} Year Salary Bonusname BOSS 4 4 4Han 2 2 2Lilei 2 2 2'''# 查看分组的情况for name,group in group_by_name: print(name)# 组的名字 共三组 print(group)# 组具体内容'''BOSS name Year Salary Bonus0 BOSS 2018 999999 1000004 BOSS 2019 9999999 2000005 BOSS 2019 999999 3000007 BOSS 2019 999999 400000Han name Year Salary Bonus3 Han 2018 3000 50006 Han 2019 3500 3000Lilei name Year Salary Bonus1 Lilei 2018 20000 200002 Lilei 2018 25000 20000'''# 可以选择分组print(group_by_name.get_group('BOSS'))# 按照某一列进行分组, 将name这一列作为分组的键,对year进行分组group_by_name=df['Year'].groupby(df['name'])print(group_by_name.count())'''name Year Salary Bonus0 BOSS 2018 999999 1000004 BOSS 2019 9999999 2000005 BOSS 2019 999999 3000007 BOSS 2019 999999 400000nameBOSS 4Han 2Lilei 2Name: Year, dtype: int64'''# 按照多列进行分组group_by_name_year=df.groupby(['name','Year'])for name,group in group_by_name_year: print(name)# 组的名字 print(group)# 组具体内容'''('BOSS', 2018) name Year Salary Bonus0 BOSS 2018 999999 100000('BOSS', 2019) name Year Salary Bonus4 BOSS 2019 9999999 2000005 BOSS 2019 999999 3000007 BOSS 2019 999999 400000('Han', 2018) name Year Salary Bonus3 Han 2018 3000 5000('Han', 2019) name Year Salary Bonus6 Han 2019 3500 3000('Lilei', 2018) name Year Salary Bonus1 Lilei 2018 20000 200002 Lilei 2018 25000 20000'''# 将某列数据按数据值分成不同范围段进行分组(groupby)运算# 准备数据,生产100个年龄,从20-70岁df = pd.DataFrame({'Age': np.random.randint(20, 70, 100), 'Sex': np.random.choice(['M', 'F'], 100)})# print(df)age_groups = pd.cut(df['Age'], bins=[19,40,65,100]) # 将Age列切成几个bins,分成了三个区间print(age_groups) # 查看分组print(df.groupby(age_groups).count()) # 分组统计# 按‘Age’分组范围对性别(sex)进行分组统计print(pd.crosstab(age_groups, df['Sex']))'''Name: Age, Length: 100, dtype: categoryCategories (3, interval[int64]): [(19, 40] < (40, 65] < (65, 100]] Age SexAge (19, 40] 51 51(40, 65] 43 43(65, 100] 6 6Sex F MAge (19, 40] 27 24(40, 65] 21 22(65, 100] 1 5'''
聚合函数对一组值执行计算,并返回单个值。
以下是常用的聚合函数:
mean | 计算分组平均值 |
---|---|
count | 分组中非NA值的数量 |
sum | 非NA值的和 |
median | 非NA值的算术中位数 |
std | 标准差 |
var | 方差 |
min | 非NA值的最小值 |
max | 非NA值的最大值 |
prod | 非NA值的积 |
first | 第一个非NA值 |
last | 最后一个非NA值 |
mad | 平均绝对偏差 |
mode | 模 |
abs | 绝对值 |
sem | 平均值的标准误差 |
skew | 样品偏斜度(三阶矩) |
kurt | 样品峰度(四阶矩) |
quantile | 样本分位数(百分位上的值) |
cumsum | 累积总和 |
cumprod | 累积乘积 |
cummax | 累积最大值 |
cummin | 累积最小值 |
import pandas as pd import numpy as npdf1=pd.DataFrame({'Data1':np.random.randint(0,10,5), 'Data2':np.random.randint(10,20,5), 'key1':list('aabba'), 'key2':list('xyyxy')})print(df1)''' Data1 Data2 key1 key20 4 19 a x1 6 17 a y2 4 15 b y3 5 14 b x4 6 15 a y'''# 按key1分组,进行聚合计算# 注意:当分组后进行数值计算时,不是数值类的列会被清除print(df1.groupby('key1').sum())''' Data1 Data2key1 a 10 40b 8 37'''# 只算data1 两种方式print(df1['Data1'].groupby(df1['key1']).sum()) # 1. 先取出列再分组求和print(df1.groupby('key1')['Data1'].sum()) #2.分组后求和'''key1a 16b 3Name: Data1, dtype: int64key1a 16b 3Name: Data1, dtype: int64'''# 使用agg()函数做聚合运算 aggregation() 意为聚合函数print(df1.groupby('key1').agg('sum'))# # 可以同时做多个聚合运算print(df1.groupby('key1').agg(['sum','mean','std']))'''Data1 Data2key1 a 10 51b 9 30 Data1 Data2 sum mean std sum mean stdkey1 a 10 3.333333 4.932883 51 17 0.000000b 9 4.500000 0.707107 30 15 4.242641'''# # 可自定义函数,传入agg方法中 grouped.agg(func)def peak_range(df): return df.max() - df.min() # 返回数值范围print(df1.groupby('key1').agg(peak_range))''' Data1 Data2key1 a 3 4b 3 0'''# 同时应用多个聚合函数print(df1.groupby('key1').agg(['mean', 'std', 'count', peak_range])) # 默认列名为函数名print(df1.groupby('key1').agg(['mean', 'std', 'count', ('range', peak_range)])) # 通过元组修改列名''' Data1 Data2 mean std count peak_range mean std count peak_rangekey1 a 1.333333 1.154701 3 2 13 3.464102 3 6b 5.000000 2.828427 2 4 12 1.414214 2 2 Data1 Data2 mean std count range mean std count rangekey1 a 1.333333 1.154701 3 2 13 3.464102 3 6b 5.000000 2.828427 2 4 12 1.414214 2 2'''# 给每列作用不同的聚合函数dict_mapping = { 'Data1':['mean','max'], 'Data2':'sum' }print(df1.groupby('key1').agg(dict_mapping))'''Data1 Data2 mean max sumkey1 a 5.333333 8 46b 7.000000 9 27'''# apply函数# apply函数是pandas里面所有函数中自由度最高的函数;函数可以作为参数放在apply中# 以统计抽烟和不抽烟人的性别,年龄和体重为例df1=pd.DataFrame({'sex':list('FFMFMMF'),'smoker':list('YNYYNYY'),'age': [21,30,17,37,40,18,26], 'weight':[120,100,132,140,94,89,123]})print(df1)''' sex smoker age weight0 F Y 21 1201 F N 30 1002 M Y 17 1323 F Y 37 1404 M N 40 945 M Y 18 896 F Y 26 123'''def bin_age(age): if age >=18: return 1 # 成年 else: return 0 # 未成年 # 抽烟的年龄大于等18的print(df1['age'].apply(bin_age))df1['age'] = df1['age'].apply(bin_age) # 对年龄这一列进行转换,判断成年还是未成年print(df1)'''0 11 12 03 14 15 16 1Name: age, dtype: int64 sex smoker age weight0 F Y 1 1201 F N 1 1002 M Y 0 1323 F Y 1 1404 M N 1 945 M Y 1 896 F Y 1 123'''# 取出抽烟和不抽烟的体重前二def top(smoker,col,n=5):
return smoker.sort_values(by=col)[-n:]
# [-n:]切片,负号表示从末尾往前取
print(df1.groupby('smoker').apply(top,col='weight',n=2))''' sex smoker age weightsmoker N 4 M N 1 94 1 F N 1 100Y 2 M Y 0 132 3 F Y 1 140'''
分组案例
下面我们来对一个电影评论数据进行案例分析,公号留言回复 PandasMovie 即可获取。
# 读取数据并观察data = pd.read_csv('pandas_movie.csv')print('数据的形状:', data.shape)# 数据的形状: (5043, 28)#print(data.head(5)) #发现索引4 有NaN# 处理缺失值data = data.dropna(how='any') # 将Nan去除print(data.head(5)) # 索引4被去除# 我们来分析一下导演和票房总收入(gross)的关系 通过分组排序来观察是否票房高的电影由知名导演拍摄# 按照导演分组计算票房总收入group_director = data.groupby(by='director_name')['gross'].sum()print(group_director)# ascending升降序排列,True升序result = group_director.sort_values()print(type(result)) # 查看分组后结果的数据的数据类型是Series,一维数组print(result) # 可以看到高票房的导演# 电影产量年份趋势from matplotlib import pyplot as pltimport random# 按照年分分析电影产量的情况,以及电影的名称movie_years = data.groupby('title_year')['movie_title']print(movie_years.count())print(movie_years.count().index.tolist()) # 将索引列也就是年份的索引取出,并转换成列表格式print(movie_years.count().values) # 电影的数量x = movie_years.count().index.tolist()y = movie_years.count().values plt.figure(figsize=(20,8),dpi=80)plt.plot(x,y)plt.show()
运行结果:
写在后面:
今天学习是Pandas库的下半部分~
周末快要到了,周末我们的学习计划会停一停~
坚持学习和输出哦~
记得打卡,在群里分享你的代码和笔记~
好文章,我在看❤