Python模块 —— Pandas
- Pandas(二)—— 索引、分组
* - 三、索引
– - 四、分组
–
Pandas(二)—— 索引、分组
大家可以关注 知乎或微信公众号的share16,我们也会同步更新此文章。
三、索引
3.1 索引器
3.1.1 列索引、行索引
列索引是最常见的索引形式,一般通过 [ ]
来实现。
- 取某一列:通过
[列名] 或 .列名
,可以从DataFrame中取出相应的某一列,返回值为Series; - 取某几列:通过
[列名组成的列表]
,可以从DataFrame中取出相应的某几列,其返回值为一个DataFrame;
行索引亦是最常见的索引形式,也能通过 [ ]
来实现。
- 以整数为索引的Series:取某一行(
s[i]
)或某几行(s[[i,j]] 或 s[i:j](左闭右开)
),用法与列表/字符串等索引用法一致; - 以字符串为索引的Series:取某一行(
s[i]
)或某几行(s[[i,j]] 或 s[i:j]
),用法与列表/字符串等索引用法存在差异;若i/j是字符串,遵循左闭右闭;若i/j是整数,遵循左闭右开; - DataFrame的行索引,执行
s[i]
和s[[i,j]]
会返回Error,执行s[i:j]会返回结果;若i/j是字符串,遵循左闭右闭;若i/j是整数,遵循左闭右开;
import pandas as pd
df = pd.read_excel('/xxx/公司员工.xlsx', parse_dates=['birthdate_key'])
print(df['city_name'], df.city_name, df[['city_name', 'department']], sep='\n\n')
s = df['job_title'].head()
print('取某一行 s[i] :', s[1], type(s[1]))
print('\n')
print('取某几行 s[[i,j]] 或 s[i:j]:', s[[1,3]], type(s[[1,3]]), s[1:3], type(s[1:3]), sep='\n\n')
s = pd.Series(range(5,10), index=['a','b','c','d','e'])
print('取某一行 s[i] :', s['a'], type(s['a']))
print('\n')
print('取某几行 s[[i,j]] 或 s[i:j] :', s[['a','c']], type(s[['a','c']]), s['a':'c'], type(s['a':'c']), sep='\n\n')
import numpy as np
import pandas as pd
a = pd.DataFrame(np.arange(10).reshape(5,2), index=list('abcde'),columns=['M','N'])
lst = ["a['a']", "a[['a','b']]", "a['a':'c']", "a[0:2]"]
for i in lst:
try:
print(eval(i), end='\n\n')
except:
print('执行错误\n')
3.1.2 loc索引器、iloc索引器
对于Series/DataFrame而言,有两种索引器,一种是基于’index和columns的值(索引值可以是str或int)’的loc索引器,另一种是基于’位置(取值只能是0,1,2,···,与索引值无关)’的iloc索引器。
- loc索引器的一般形式是
loc[行区域,列区域]
,行区域不能省略,列区域是可以省略的。其行/列区域有五类合法对象,分别是:单个元素、元素列表、元素切片、布尔列表以及函数; - iloc索引器的一般形式是
iloc[行区域,列区域]
,用法与loc类似,只不过是针对位置进行筛选。
import numpy as np
import pandas as pd
a = pd.DataFrame(np.arange(20,35).reshape(5,3), index=list('abcde'), columns=['X','Y','Z'])
b = pd.DataFrame(np.arange(30,45).reshape(5,3), columns=['X','Y','Z'])
print(a, b, sep='\n\n')
lst = ["a.loc['a']", "a.loc['X']", "a.loc[0]", "a.loc['a','X']", "b.loc[1,'Z']", "b.loc[0]","b.iloc[1,2]", "b.iloc[0,'X']"]
for i in lst:
try:
print('{} 的运行结果:\n{}\n'.format(i, eval(i)))
except:
print('{} 的运行结果:\n{}\n'.format(i, '执行错误'))
lst = ["a.loc[['a','c'],['X']]", "a.loc[:,['X','Z']]", "b.loc[[0,2],'Z']", "a.iloc[[0,2],:]","b.iloc[[1,4],[0,2]]", "b.iloc[[1,4],['X','Y']]"]
for i in lst:
try:
print('{} 的运行结果:\n{}\n'.format(i, eval(i)))
except:
print('{} 的运行结果:\n{}\n'.format(i, '执行错误'))
lst = ["a.loc['a':'c' , 'X':'Y']", "b.loc[0:2 , 'X':'Y']", "b.loc[0:2 , 0:]", "a.iloc[0:2 , 0:]","b.iloc[0:2 , 1:3]", "b.iloc[0:2 , 'X':'Z']"]
for i in lst:
try:
print('{} 的运行结果:\n{}\n'.format(i, eval(i)))
except:
print('{} 的运行结果:\n{}\n'.format(i, '执行错误'))
lst = ["a.loc[a.X > 30]", "b.loc[b.X.isin([42]),['X','Y']]", "a.iloc[a.X > 30]","a.iloc[(a.X > 30).values]"]
for i in lst:
try:
print('{} 的运行结果:\n{}\n'.format(i, eval(i)))
except:
print('{} 的运行结果:\n{}\n'.format(i, '执行错误'))
lst = ["a.loc[lambda x:'a',lambda x:'X']", "b.loc[lambda x:slice(0,2),lambda x:slice('X','Z')]", "b.iloc[lambda x: slice(1,4)]","b.iloc[lambda x: slice(1,4),1]"]
for i in lst:
try:
print('{} 的运行结果:\n{}\n'.format(i, eval(i)))
except:
print('{} 的运行结果:\n{}\n'.format(i, '执行错误'))
3.1.3 query方法
筛选查询数据: df.query(condition, inplace, **kwargs)
- condition:默认str类型, 里面可以使用( ==、!=、|、&、~、or、and、or、in、not in等)运算符;若要引用外部变量,只需在变量名前加@符号;
- inplace:默认False;若为True,返回结果会修改原数据;
low,high = 28,36
lst = ["X>X.mean()", "'X>X.mean()'", "'Y in [24,30,36]'","'Z.between(low,high)'", "'Z.between(@low,@high)'"]
for i in lst:
try:
print('a.query({}) 的运行结果:\n{}\n'.format(i, a.query(eval(i))))
except:
print('a.query({}) 的运行结果:\n{}\n'.format(i, '执行错误'))
3.1.4 随机抽样
若把 DataFrame 的每一行看作一个样本,或把每一列看作一个特征,再把整个 DataFrame 看作总体,想要对样本或特征进行随机抽样就可以用 sample函数,即
Series/df.sample(n,frac,replace,weights,random_state,axis,ignore_index)
。
- n:抽样数量;不能与frac一起使用,若frac=None,n则默认为1;
- frac:抽样比例,默认为None,不能与n一起使用;如0.3表示从总体中抽出30%的样本;
- replace:是否有放回抽样;默认False,即不放回抽样;若为True,则是有放回抽样;
- weights:每个样本的抽样相对概率,默认为None;
3.2 多重索引
适用于所有 Series/DataFrame :
- 查看所有 行索引名 和 行索引值 :df.index.names、df.index.values、df.index
- 查看所有 列索引名 和 列索引值 :df.columns.names、df.columns.values、df.columns
- 查看所有 某一层索引 :如df.index.get_level_values(0)
3.2.1 普通列设为索引
df.set_index(keys,drop,append,inplace,verify_integrity)
- keys:某一列或某几列,用列表形式表示;
- drop:默认为True,删除要用作新索引的列;
- append:默认为 False,表示是否保留原来的索引,直接把新设定的索引添加到原索引的内层;
- inplace:默认为 False,若为True,则修改原数据;
- verify_integrity:默认为 False,检查新索引是否有重复项;
3.2.2 多重索引的loc索引器
df.loc[(level_0_list, level_1_list), cols]
df.swaplevel()
:转换内外层索引
Python输出带颜色字体,如红色字体
import pandas as pd
df = pd.read_excel('/xxx/公司员工.xlsx', parse_dates=['birthdate_key'])
df = df.set_index(['city_name', 'department'])
df.set_index(['department', 'city_name'])
print("\033[0;30;43m以 df.set_index(['city_name', 'department']) 的结果为例:\n\033[0m")
cols = ['EmployeeID', 'job_title']
lst1 = ['只索引city_name的某几个值:', '只索引department的某几个值:', '索引city_name和department的某一个值:', '索引city_name和department的某几个值:']
lst2 = [r"df.loc[['Terrace', 'Nanaimo'] , cols]", r"df.swaplevel().loc[['Store Management', 'Meats'], cols].swaplevel()", r"df.loc[('Terrace', 'Meats'), cols]", r"df.loc[(['Terrace','Vancouver'], ['Meats','Training']), cols]"]
for i,j in zip(lst1,lst2):
try:
print('\033[0;31;40m')
print(i+j, '\n运行结果:')
print('\033[0m')
print(eval(j))
except:
print('\033[0;31;40m')
print(i+j, '\n运行结果:')
print('\033[0m')
print('执行错误')
3.2.3 其他
Slice对象一共有两种形式,第一种为 loc[idx[行区域,列区域]]
型,第二种为 loc[idx[行区域],idx[列区域]]
型。(前提是:索引不重复的;使用 silce 对象,要先进行定义,即:idx = pd.IndexSlice。)
多级索引的构造:除了使用set_index之外,常用的有from_tuples、from_arrays、from_product三种方法,它们都是pd.MultiIndex对象下的函数。
- from_tuples:根据传入由元组组成的列表进行构造;
- from_arrays:根据传入列表中,对应层的列表进行构造;
- from_product:根据给定多个列表的笛卡尔积进行构造;
3.2.4 索引的常用方法
如:swaplevel、reorder_levels、droplevel、rename_axis、rename、set_index、reset_index、reindex等。
; 3.3 练习
3.3.1 公司员工数据集
现有一份公司员工数据集点此下载
import pandas as pd
df = pd.read_csv('/xxx/02 公司员工.csv', parse_dates=['birth'])
df.query(" (age )
df.loc[(df.age 40) & (df.department.isin(['Dairy','Bakery'])) & (df.gender == 'M')]
df.query('id%2==1').iloc[:, [0,2,-2]]
df.iloc[(df.id%2==1).values, [0,2,-2]]
df1 = df.set_index(df.columns[-3:].tolist()).swaplevel(0,2)
df1 = df1.reset_index(1)
df1 = df1.rename_axis(index={'gender':'Gender'})
df1.index = df1.index.map(lambda x : '_'.join(x))
df1.index = df1.index.map(lambda x : tuple(x.split('_')))
df1 = df1.rename_axis(index=['gender','department'])
df1 = df1.reset_index().reindex(df.columns, axis=1)
df1.equals(df)
3.3.2 巧克力数据集
现有一份关于巧克力评价的数据集点此下载
import pandas as pd
df = pd.read_csv('/xxx/03 巧克力.csv', encoding='ISO-8859-1')
df['cocoapercent'] = df.cocoapercent.map(lambda x : float(x[:-1])/100 )
df.query(" (rating cocoapercent.median()) ")
df.set_index(['reviewdate','location']).query(" (reviewdate < 2012) and (location not in ['France', 'Canada', 'Amsterdam', 'Belgium']) ")
四、分组
4.1 分组函数
df.groupby(by,axis,level,as_index,sort,group_keys,squeeze,observed,dropna)
- by:列名或列名列表; level:默认None,级别名称;
- as_index:默认True,返回以组标签为索引的对象;
- sort:默认True,对组键进行排序;
- group_keys:默认True,调用 apply 时,将组键添加到索引以识别片段;
- squeeze:默认False,若可能,减少返回类型的维数,否则返回一致的类型;
- observed:默认False,这仅适用于任何 groupers 是分类的;若为True,仅显示分类分组的观察值;若为 False,显示分类分组的所有值;
-
dropna:默认True,且组键包含 NA 值,则 NA 值连同行/列将被删除;若为 False,NA 值也将被视为组中的键;
-
通过
ngroups
属性,可以返回分组个数; - 通过
groups
属性,可以返回从组名映射到组索引列表的字典; - 通过
size
属性,可以返回groupby对象上每个组的元素个数; - 通过
get_group
属性,可以返回所在组对应的行(必须知道组的名字); - 通过
list(df.groupby(···))
属性,可以将DataFrameGroupBy类型转换成列表;
4.2 聚合函数
4.2.1 内置聚合函数
df.groupby(by=xxx)[列名组合].使用操作
使用操作/内置聚合函数:max/min/sum/count/std/median/mean/all/any/idxmax/idxmin/mad/unique/nunique/skew/quantile/sem/size/prod等;
4.2.2 agg/transform/apply方法
Series/df.agg(func,···)
:在指定轴上使用一项或多项操作进行聚合;
Series/df.transform(func,···)
:调用func生成转换后的值,且具有与Series/df相同的轴长的数据;
Series/df.apply(func,···)
:调用func生成转换后的值;
import pandas as pd
df = pd.read_csv('/xxx/04 汽车.csv')
a = df.groupby('Country')
lst1 = ['使用一个函数:', '使用多个函数:', '对特定的列使用特定的聚合函数:', '对特定的列使用特定的聚合函数:', '使用自定义函数:', '聚合结果重命名(用元组表示,即(名字,函数)):']
lst2 = [r"a['Disp','HP'].agg('mean')", r"a['Disp','HP'].agg(['mean','std'])", r"a['Disp','HP'].agg({'Disp':'mean', 'HP':['mean','std']})", r"a['Disp','HP'].agg({'Disp':'mean', 'HP':'mean', 'HP':'std' })", r"a['Disp','HP'].agg(lambda x : x.max()-x.min())", r"a['Disp','HP'].agg([('my_max','max'), ('my_min','min')])"]
for i, j in zip(lst1,lst2):
print('\033[0;30;43m')
print(i+j, '\n')
print('\033[0m')
print(eval(j))
''' agg/transform/apply的区别: '''
a['Price'].agg('mean')
a['Price'].transform('mean')
a['Price'].apply('mean')
备注:
对特定列使用特定函数时,需要使用 {'列名' : [('重命名',func1), func2]}
等,详见本文4.4.1版块的问题1。
4.3 变换和过滤
变换函数
的返回值为同长度的序列,最常用的内置变换函数是累计函数: cumcount/cumsum/cumprod/cummax/cummin ,它们的使用方式和聚合函数类似,只不过完成的是组内累计操作。
组过滤(df.filter(func))
作为行过滤的推广,指的是如果对一个组的全体所在行进行统计的结果,返回True则会被保留,False则该组会被过滤,最后把所有未被过滤的组其对应的所在行拼接起来作为DataFrame返回。
4.4 练习
4.4.1 汽车数据集
现有一份汽车数据集点此下载,其中Brand、Disp、HP,分别代表:汽车品牌、发动机蓄量、发动机输出;
问题:
1. 先剔除所属 Country 数不超过2的汽车,再按 Country 分组计算价格均值、价格变异系数、该 Country 的汽车数量,其中变异系数的计算方法是标准差除以均值,并在结果中把变异系数重命名为 Cov;
import pandas as pd
df = pd.read_csv('/xxx/04 汽车.csv')
a = df.groupby('Country').filter(lambda x : x.shape[0] > 2)
a.groupby('Country').agg({'Price' : ['mean', ('Cov',lambda x : x.std()/x.mean()), 'count']})
n = int(df.shape[0]/3)
condition = ['Head']*n+['Mid']*n+['Tail']*n
df.groupby(condition)['Price'].mean()
b = df.groupby('Type')['Price','HP'].agg({'Price':['max'], 'HP':['min']})
b.columns = b.columns.map(lambda x : '_'.join(x))
b
df.groupby('Type').HP.transform(lambda x : ((x - x.min())/(x.max() - x.min())))
df.groupby('Type')['Disp','HP'].corr().swaplevel().loc['Disp','HP']
df.groupby('Type')[['HP', 'Disp']].apply(lambda x:np.corrcoef(x['HP'].values, x['Disp'].values)[0,1])
谢谢大家 🌹
Original: https://blog.csdn.net/weixin_42330887/article/details/124616407
Author: share16
Title: Pandas(二)—— 索引、分组
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/752387/
转载文章受原作者版权保护。转载请注明原作者出处!