我们在上次课中讲到了Pandas的Series结构,还没看的点这里
ailsa:python数据分析:Pandas之Serieszhuanlan.zhihu.comDataFrame是一个[表格型]的数据结构,DataFrame由按一定顺序排列的多列数据组成.设计,初衷是将Series的使用场景从一维拓展到多维。其实DataFrame就是由多个Series组成的,因此可以说DataFrame是Series的容器。
DataFrame由3部分组成
行索引:index
列索引:columns
值:values
长这个样子
是不是感觉跟Excel表格很像,跟关系型数据库表也很像,跟SPSS的表也像吧,没错,他们都是极其相似的二维表,这种形式的发明还要追溯到1978年的世界上第一款电子表格Visicalc,想要了解,可以戳这里 ailsa:1.Excel数据分析:Excel最最最基础的操作 有讲到电子表格的发展史,由此可见,二维表对于数据分析还是挺便捷的,要不然咋会有这么多分析软件都采用这种形式呢。那对于DataFrame的学习,大家可以把它想象成excel,原理都是一样的,只是一个是用鼠标点点点,一个是代码敲敲敲。
2.1 使用ndarry创建
# DataFrame的参数组成 pd.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
# index指定行索引,columns指定列索引,若不写,则默认从0开始,size指定行数和列数
df = pd.DataFrame(data=np.random.randint(1,10,size=(2,4)),index=["a","b"],columns=["A","B","C","D"])
2.2 使用字典创建
dic = {"name":["张三","李四","王五"],"age":[1,2,3]}
# key为列的索引,行索引则默认从0开始
pd.DataFrame(dic)
可以看出,以字典形式创建,DataFrame以字典的key作为每一列的列名,以字典的值(一个数组)作为每一列的值,DataFrame会自动加上每一行的索引
DataFrame也是分为显示索引和隐式索引
3.1 隐式索引的操作
df = pd.DataFrame(data=np.random.randint(1,10,size=(3,4)))
3.2 显式索引的操作
df1 = pd.DataFrame(data=np.random.randint(1,10,size=(3,4)),index=["a","b","c"],columns=["A","B","C","D"])
总结
1.DataFrame相对于Series而言,多了对于列的操作,但是是建立在Series基础之上。
2.loc是对于显式索引的相关操作(对于标签的处理),iloc是针对隐式索引的相关操作(对于整数的处理)。
3.df[0:2]切片操作是针对行而言,对于df["A"]索引操作是对于列而言;获取单个元素先列后行,df[列][行];loc和iloc操作,逗号前是行区域或行列表,逗号后是列区域或列列表。
df.values 值
df.columns 列
df.index 行
df.shape 几行几列(行,列)
df.size 大小,行数X列数
,以上面的df为事例
5.1 级联
功能:根据指定的行或列进行值的拼接,不参与任何计算,只是把多个df变成1个
pd.concat()参数(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False, sort=None, copy=True)
示例数据
dic1 = {'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']}
dic2 = {'A': ['A4', 'A5', 'A6', 'A7'],
'B': ['B4', 'B5', 'B6', 'B7'],
'C': ['C4', 'C5', 'C6', 'C7'],
'D': ['D4', 'D5', 'D6', 'D7']}
dic3 = {'A': ['A8', 'A9', 'A10', 'A11'],
'B': ['B8', 'B9', 'B10', 'B11'],
'C': ['C8', 'C9', 'C10', 'C11'],
'D': ['D8', 'D9', 'D10', 'D11']}
df1 = pd.DataFrame(dic1,index=[0, 1, 2, 3])
df2 = pd.DataFrame(dic2, index=[4, 5, 6, 7])
df3 = pd.DataFrame(dic3, index=[8, 9, 10, 11])
pd.concat([df1,df2,df3]) # 默认是按照列方向
keys使用,作用于不同层级的名称
pd.concat([df1,df2,df3],keys=['x', 'y', 'z']) # ('x', 'y', 'z')元组也可以
使用ignor_index
# 创建df4
dic4={'a':[1,2,3,4],'b':[5,6,7,8]}
df4 = pd.DataFrame(dic4)
df4
pd.concat([df1,df4],axis=1,ignore_index=True) # 行方向进行级联
join_axes,可以指定根据哪个轴来对齐数据
#新增df5
dic5={'a':[1,2,3,4],'b':[5,6,7,8]}
df5= pd.DataFrame(dic5,index=[2,3,4,5])
df5
df1和df5行方面拼接,只有2和3是一样的
d.concat([df1,df5],axis=1,join_axes=[df1.index])
效果类似于按照df1的行索引进行级联,df1的所有行会显示,而df5只能显示匹配上的
join的用法,取并集或交集
pd.concat([df1,df5],axis=1,join='outer') # 取并集,缺失值显示为NaN
pd.concat([df1,df5],axis=1,join='inner') # 取交集
df.append()方法,类似于添加的意思
df1.append参数:(other, ignore_index=False, verify_integrity=False, sort=None)
默认是列方向级联,跟concat的默认方式是一样的
df1.append(df2)
这种方式操作更简单,后面会经常使用
5.2 合并
pd.merge() 跟SQL中的连表查询很像,需要根据合并条件进行两表合并,也就是两个DataFrame需要具有相同的列,然后再进行条件连接合并,而concat单纯根据索引就能进行拼接。
参数;pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False, validate=None)
示例
两个DataFrame列名相同且内容一致,默认自动连接
dic1 = {"name":["A","B","C","D"],"age":[20,21,22,23]}
dic2 = {"name":["A","C","D","F"],"sex":["M","F","M","M"]}
df1 = pd.DataFrame(dic1)
df2 = pd.DataFrame(dic2)
pd.merge(df1,df2) # name一列相同,自动连接
或者 pd.merge(df1,df2,on="name")
当连接条件列名不一致时,使用lefton 和 right_on
emp_dic = {"姓名":["张三","李四","王五"],"dep_id":[1,2,3]}
dep_dic = {"id":[1,2],"部门名称":["销售部","运营部"]}
emp = pd.DataFrame(emp_dic)
dep = pd.DataFrame(dep_dic)
pd.merge(emp,dep,left_on="dep_id",right_on="id")
SQL: select * from emp e left join dep d on e.dep_id = d.id
当连接条件为索引时,可以使用left_index 和 righti_ndex
pd.merge(emp,dep,left_on="dep_id",right_index=True) # dep_id 与dep的行索引进行匹配
6.1 缺失值处理
# 在np中
None是python自带的,其类型为object,因此,None不能参与到人任何计算中(NoneType)
np.nan(NaN) 是浮点类型(float),能参与到计算中.但计算的结果总是NaN
# 在pandas中
把None和np.nan都视作np.nan
构建示例数据
df = pd.DataFrame(np.random.randint(1,100,size=(5,4)),index=["a","b","c","d","e"],columns=["A","B","C","D"])
df1 = pd.DataFrame(data=np.random.randint(1,10,size=(3,4)),index=["a","b","c"],columns=["A","B","C","D"])
df3 = pd.concat([df1,df],axis=1) # concat级联,axis=1 行方向,后面会讲
df3
6.1.1 查看哪些行或列为存在缺失值
df3.notnull()
df3.notnull().all(axis=1)
6.1.2 删除有缺失值的行或列
df.dropna() 删除有缺失值的整行或整列
f3.dropna的参数:(axis=0, how='any', thresh=None, subset=None, inplace=False)
df3.dropna()
6.1.3 填充缺失值
df.fillna() 对缺失值进行填充
df.fillna参数:(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs)
需求1:把缺失值都填充为6
df3.fillna(6)
需求2:把缺失值按照列方向,填充为前面的那个值
df3.fillna(axis=0,method="ffill") # 改成pad也可以,axis确定了填充的方向,如果不写,默认是列
需求3:把缺失值按照行方向,填充为后面的那个值
df3.fillna(axis=1,method="bfill")# 改成backfill也可以,axis确定了填充的方向,如果不写,
6.2 重复值处理
6.2.1 df.duplicated() 查看重复行
df.duplicated()的参数:(subset=None, keep='first')
直接通过示例就能明白了
创建一个DataFrame
dic = {"A":[1,1,1,5,9],"B":[5,5,6,7,7],"C":[6,6,5,2,8]}
df = pd.DataFrame(dic)
df
需求1:把所有列重复的的行标注出来
df.duplicated() # keep默认为first,subset默认为所有的列,意思就是所有的列同时重复才显示
我们发现第一行和第二行所有列一样,根据keep设置的first的原则,第一次出现的不显示为True,其余的显示为True,这里的True代表重复的意思,我们只要把True的删除,就可以保留唯一值了。
需求2:把A和B两列同时重复的行显示,要求最后的那个不标注为True
df.duplicated(subset=["A","B"],keep='last')
我们发现A和B两列同时重复的还是第一行和第二行,设置keep为last之后,第一行显示为重复行,为True,第二行显示为False。
需求3:把A和B两列重复的行全部显示为True
df.duplicated(subset=["A","B"],keep=False)
keep设置为False之后,所有重复的行都会显示为True
6.2.2 删除重复行,保留唯一值
对于重复行的处理,我们一般都是找出重复行,只保留一个,删除其他的,那duplicated通过keep设置来让用户自由选择是保留第一个,还是最后一个,通过显示True来实现该功能
df.drop_duplicates() 删除重复值
df.drop_duplicates参数:(subset=None, keep='first', inplace=False)
前两个就不说了,跟duplicated一样
inplace=False不在原值上删除,而是复制出一份进行操作,改成True则直接对df进行删除操作,这个也是pandas谨慎的地方,大部分操作都不是在原值上进行的,如果需要可以通过inplace进行设置
需求:删除所有列同时重复的行,默认保留第一个就行
df.drop_duplicates() # 把返回True给删除,很简便
注意:此操作之后,原df并没有发生变化,有两种处理方式,第一种:把删除后的数据赋值给新的变量,第二种:直接修改inplace=True。
6.3 排序
常用的是纵向排序,也就是默认的axis=0的相关操作
sort_index()参数(axis=0, level=None, ascending=True, inplace=False, kind='quicksort', na_position='last', sort_remaining=True, by=None)
df=df.take(np.random.permutation(5),axis=0)
df
df.sort_index()
df.sort_values()
参数:(by, axis=0, ascending=True, inplace=False, kind='quicksort', na_position='last')
对某列进行排序
df.sort_values(by="C")
df.sort_values(by=["C","B"],axis=0,ascending=[False,True])
对行进行排序
df.sort_values(by=[0,2],ascending=[False,True],axis=1)
注:当存在多列或多行排序时,是有优先顺序的,根据列表中的优先顺序,在前面的优先级高,越往后优先级越低。
arr = np.array([[1,2,3],[4,3,2],[5,3,8]])
np.random.shuffle(arr) # shuffle洗牌
arr
arr1 = np.array([[1,2,3],[4,3,2],[5,3,8]])
np.random.permutation(arr1) # permutation 随机排列数组
arr1
np.random.permutation(10)
df.take参数:(indices, axis=0, convert=None, is_copy=True, **kwargs)
axis=0 纵向排序,axis=1横向排序,默认为0,大部分情况下都是纵向排序
与permutation联合使用,可实现随机采样功能
示例数据
df
对行进行随机排序,按照行索引
permutation中的x,类似于range(5),只能取到0 1 2 3 4,因此在跟take联合使用时,x的大小要跟df的行数相一致
df.take(np.random.permutation(5),axis=0)
df.take(np.random.permutation(3),axis=1)
6.4 替换
df.replace()
df.replace参数:(to_replace=None, value=None, inplace=False, limit=None, regex=False, method='pad')
单值替换
df.replace(1,10,inplace=True)
多值替换
1 使用字典
dic={10:"满分",5:"中等"}
df.replace(dic)
# 或者 使用列表
df.replace([10,5],["满分","中等"])
注:DataFrame中无法使用method和limit参数
6.5 映射与运算
6.5.1 map()
使用map通过字段映射,新增一列
emp_dic = {"姓名":["张三","李四","王五"],"dep_id":[1,2,3]}
emp = pd.DataFrame(emp_dic)
# 映射关系表
dic = {"张三":"Hurry","李四":"Lily","王五":"Tom"}
emp['e_name'] = emp['姓名'].map(dic)
使用map作为运算工具
df["up_num"] = df["C"].map(lambda x:x*20)
map常跟匿名函数lambda一块使用,当然也可以使用普通函数
def complex(s):
return s*10
df["up_num1"] = df["C"].map(complex)
注意:并不是任何形式的函数都可以作为map的参数.只有当一个函数具有一个参数且有返回值,那么该函数才可以作为map的参数
6.5.2 apply()
只能当做运算工具,当运算量很大时,建议使用apply
df['up_num2'] = df['C'].apply(lambda x:x*5)
7.1 分组聚合基本操作
DataFrameGroupby
/ SeriesGroupby
)使用groupby进行分组,groups查看分组情况
df.groupby参数:(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, observed=False, **kwargs)
实例数据
sale_data = pd.read_excel('./sale_data.xlsx') # 读取Excel文件
需求1:计算各门店的销售数量
第一步:分组
sale_data.groupby(by="门店编码")
这是一个DataFrameGroupby,主要的功能能是允许你在不额外写循环的情况下, 快速对每一组数据进行操作。
第二步:聚合函数
sale_data.groupby(by="门店编码").sum()
如果不指定列,则默认对所有的列都进行聚合计算
题目要求对销售数量,因此修改如下:
sale_data.groupby(by="门店编码")["销售数量"]
这是一个SeriesGroupBy对象,在不用循环的情况实现聚合计算
sale_data.groupby(by="门店编码")["销售数量"].sum()
得到每个门店的销售数量总和是一个Series,聚合函数,还可以使用
std(标准差)、median(中位数)、min(最大值)、max(最小值)、mean(均值)
查看分组情况
sale_data.groupby(by="门店编码").groups
需求2:计算每个产品的均价,并新增一列到sale_data?
mean_price = sale_data.groupby(by="产品编码")["单价"].mean()
mean_dic = mean_price.to_dict() # to_dict转换成字典形式
mean_dic
sale_data["mean_price"] = sale_data["产品编码"].map(mean_dic)
7.2 分组聚合高级操作
使用groupby分组后,也可以使用transform和apply提供自定义函数实现更多的运算
sale_data.groupby(by="门店编码")["销售数量"].sum()
等价于 sale_data.groupby(by="门店编码")["销售数量"].apply(sum),然后对sum函数进行定义,这里的一些常用聚合函数,是pandas给定义好的,我们根据实际需要可以进行自定义
模拟mean函数实现对于不同产品的平均价格计算
# 定义求平均函数
def my_mean(s): # 使用apply传入的是分组后的,因此是一个Series
sum_num = 0
n=0
for i in s:
sum_num+=i
n+=1
return sum_num/n
# 实现分组聚合计算
sale_data.groupby(by="产品编码")["单价"].apply(my_mean)
# 使用transform返回的是原始数据每一行的值,对原数据结构并没有进行任何修改
sale_data.groupby(by="产品编码")["单价"].transform(my_mean)
7.3 agg聚合操作
全称:aggregate
sale_data.agg({"销售数量":["sum","mean"],"单价":["sum","mean"]}) # aggregate
8.1 DataFrame数据透视
这个数据透视表跟Excel中的数据透视功能是一样的,也是分组计算的一种方式,只能这种方式比groupby更加方便快捷,可操作性强,灵活好用。
df.pivot_table() 数据透视
参数:(values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
示例还是上面的销售数据
需求1: 计算每个门店的总销售数量
sale_data.pivot_table(index="门店编码",values="销售数量",aggfunc="sum")
需求2:计算每个门店的每个产品的销售数量
sale_data.pivot_table(index="门店编码",columns="产品编码",values="销售数量",aggfunc="sum")
需求3: 计算2016年每月的每家门店的销售数量和平均单价
sale_data.pivot_table(index=["月份","门店编码"],values=["销售数量","单价"],aggfunc=["sum","mean"])
8.2 DataFrame交叉表
pd.crosstab() 主要用于分类数据计数,类似于列联表
参数;(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, margins_name='All', dropna=True, normalize=False)
pd.crosstab(sale_data["门店编码"],sale_data["产品编码"])
总结:
1 groupby对原DataFrame进行分组操作,返回DataFrameGroupby或SeriesGroupBy对象,在此基础上可直接进行采用已定义好的聚合函数进行计算。
2 使用apply和transform可以实现对于聚合操作的自定义,根据自己独有的规则设计函数,采用apply对已分组后的数据进行计算,返回的是分组后的计算结果。
3 apply和transform最大的不同在于,apply返回的分组后的列+分组后的计算结果,他已经改变了原始表的结构,而transfrom返回的是原始分组的列,以及对应的每一行的结果,保留了原始表的所有的。类似于SQL中的聚合函数和开窗函数的区别。
4 map是Series的方法,传入的是每个值,而apply既可以用于Series也可以用于DataFrame,并且用于DataFrame时传入的是一个Series,而用于Series时传入的是个值,transform用法与apply一致,只是返回的结果保持跟源数据结构一致。
5 aggregate用法比groupby更加简便。
pandas是python数据分析三剑客中非常重要 一员,所以写的内容比较细,比较多,希望能够对大家有所帮助。