【pandas】 索引

task3 索引

索引器

简单的直接索引

例子

在这里插入图片描述

loc索引器

是基于元素的索引器,loc索引器的一般形式是loc[*, *],其中第一个*代表行的选择,第二个*代表列的选择,如果省略第二个位置写作loc[*],这个*是指行的筛选。

其中,*的位置一共有五类合法对象,分别是:单个元素、元素列表、元素切片、布尔列表以及函数。

在这里插入图片描述

示例

df_demo = df.set_index('Name')
df_demo.head()

在这里插入图片描述

# 单个元素
df_demo.loc['Qiang Sun'] # 多个人叫此名字
df_demo.loc['Qiang Sun', 'School'] # 返回Series
df_demo.loc['Quan Zhao', 'School'] # 返回单个元素
# 元素列表
df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]
# 切片
df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']
df_loc_slice_demo = df_demo.copy()
df_loc_slice_demo.index = range(df_demo.shape[0],0,-1)
df_loc_slice_demo.loc[5:3]
df_loc_slice_demo.loc[3:5] # 没有返回,说明不是整数位置切片
# 布尔列表
df_demo.loc[df_demo.Weight>70].head()
df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head()

condition_1_1 = df_demo.School == 'Fudan University'
condition_1_2 = df_demo.Grade == 'Senior'
condition_1_3 = df_demo.Weight > 70
condition_1 = condition_1_1 & condition_1_2 & condition_1_3
condition_2_1 = df_demo.School == 'Peking University'
condition_2_2 = df_demo.Grade == 'Senior'
condition_2_3 = df_demo.Weight > 80
condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3
df_demo.loc[condition_1 | condition_2]

# 函数
def condition(x):
    condition_1_1 = x.School == 'Fudan University'
    condition_1_2 = x.Grade == 'Senior'
    condition_1_3 = x.Weight > 70
    condition_1 = condition_1_1 & condition_1_2 & condition_1_3
    condition_2_1 = x.School == 'Peking University'
    condition_2_2 = x.Grade == 'Senior'
    condition_2_3 = x.Weight > 80
    condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3
    result = condition_1 | condition_2
    return result
df_demo.loc[condition]

df_demo.loc[lambda x:'Quan Zhao', lambda x:'Gender']

⭐由于函数无法返回如start: end: step的切片形式,故返回切片时要用slice对象进行包装:

df_demo.loc[lambda x: slice('Gaojuan You', 'Gaoqiang Qian')]

练一练

select_dtypes是一个实用函数,它能够从表中选出相应类型的列,若要选出所有数值型的列,只需使用.select_dtypes('number'),请利用布尔列表选择的方法结合DataFramedtypes属性在learn_pandas数据集上实现这个功能。

learn_pandas.loc[:,learn_pandas.dtypes.isin(["object", 'int64'])].head()

【warnings】不要使用链式赋值

在对表或者序列赋值时,应当在使用一层索引器后直接进行赋值操作,这样做是由于进行多次索引后赋值是赋在临时返回的copy副本上的,而没有真正修改元素从而报出SettingWithCopyWarning警告。

iloc索引器

iloc的使用与loc完全类似,只不过是针对位置进行筛选,在相应的*位置处一共也有五类合法对象,分别是:整数、整数列表、整数切片、布尔列表以及函数,函数的返回值必须是前面的四类合法对象中的一个,其输入同样也为DataFrame本身。

df_demo.iloc[1, 1] # 第二行第二列
df_demo.iloc[[0, 1], [0, 1]] # 前两行前两列
df_demo.iloc[1: 4, 2:4] # 切片不包含结束端点
df_demo.iloc[lambda x: slice(1, 4)] # 传入切片为返回值的函数
df_demo.iloc[(df_demo.Weight>80).values].head() #在使用布尔列表的时候要特别注意,不能传入Series而必须传入序列的values

query方法

pandas中,支持把字符串形式的查询表达式传入query方法来查询数据,其表达式的执行结果必须返回布尔列表。在进行复杂索引时,由于这种检索方式无需像普通方法一样重复使用DataFrame的名字来引用列名,一般而言会使代码长度在不降低可读性的前提下有所减少。

query表达式中,帮用户注册了所有来自DataFrame的列名,所有属于该Series的方法都可以被调用,和正常的函数调用并没有区别。⭐对于含有空格的列名,需要使用'col name'的方式进行引用。

同时,在query中还注册了若干英语的字面用法,帮助提高可读性,例如:or, and, or, in, not in

此外,在字符串中出现与列表的比较时,==!=分别表示元素出现在列表和没有出现在列表,等价于is innot in

对于query中的字符串,如果要引用外部变量,只需在变量名前加@符号

# 注册了所有来自DataFrame的列名
df.query('((School == "Fudan University")&'
         ' (Grade == "Senior")&'
         ' (Weight > 70))|'
         '((School == "Peking University")&'
         ' (Grade != "Senior")&'
         ' (Weight > 80))')
# 所有属于该`Series`的方法都可以被调用,和正常的函数调用并没有区别
df.query('Weight > Weight.mean()').head()
# `or, and, or, in, not in`
df.query('(Grade not in ["Freshman", "Sophomore"]) and (Gender == "Male")').head()
# 在字符串中出现与列表的比较时,`==`和`!=`分别表示元素出现在列表和没有出现在列表,等价于`is in`和`not in`
df.query('Grade == ["Junior", "Senior"]').head()

#对于query中的字符串,如果要引用外部变量,只需在变量名前加@符号
low, high =70, 80
df.query('@low <= Weight < @high').head()

随机抽样sample方法

df.sample(n, axis, frac, replace, weights)

  • n :抽样数量、
  • axis::抽样的方向(0为行、1为列)
  • frac:抽样比例(0.3则为从总体中抽出30%的样本)
  • replace:True有放回,False无放回
  • weights:每个样本的抽样相对概率
df_sample = pd.DataFrame({
    
    'id': list('abcde'), 'value': [1, 2, 3, 4, 90]})
df_sample.sample(3, replace = True, weights = df_sample.value)

多级索引

在这里插入图片描述

多级索引结构如上图所示。

np.random.seed(0)
multi_index = pd.MultiIndex.from_product([list('ABCD'), df.Gender.unique()], names=('School', 'Gender'))
multi_column = pd.MultiIndex.from_product([['Height', 'Weight'], df.Grade.unique()], names=('Indicator', 'Grade'))
df_multi = pd.DataFrame(np.c_[(np.random.randn(8,4)*5 + 163).tolist(), (np.random.randn(8,4)*5 + 65).tolist()],
                        index = multi_index, columns = multi_column).round(1)

提取索引名字和值属性

# 索引层的名字
df_multi.index.names
# 列名层的名字
df_multi.columns.names
# 索引的值,多级索引返回以tuple为元素的list
df_multi.index.values
# 列名,同上返回tuple为元素的list
df_multi.columns.values
# 提取指定层索引的值
df_multi.index.get_level_values(0)

多级索引的loc方法

仍然是df.loc[行,列],但多级索引中的单个元素以元组为单位,将单层索引中的标量换成元组即可。

⭐建议:索引前对MultiIndex进行排序以避免性能警告

df_multi = df_multi.sort_index()
df_multi.loc[('Fudan University', 'Junior')].head()
df_multi.loc[[('Fudan University', 'Senior'), ('Shanghai Jiao Tong University', 'Freshman')]].head()
df_multi.loc[df_multi.Weight > 70].head() # 布尔列表也是可用的
练一练

与单层索引类似,若存在重复元素,则不能使用切片,请去除重复索引后给出一个元素切片的例子。

df_new = df_multi.loc[~df_multi.index.duplicated()]
df_new.loc['Fudan University':'Peking University':1]

交叉索引

在多级索引中的元组有一种特殊的用法,可以对多层的元素进行交叉组合后索引,但同时需要指定loc的列,全选则用:表示。其中,每一层需要选中的元素用列表存放,传入loc的形式为[(level_0_list, level_1_list), cols]

res = df_multi.loc[(['Peking University', 'Fudan University'], ['Sophomore', 'Junior']), :]
# 与元组的列表不同
res = df_multi.loc[[('Peking University', 'Junior'), ('Fudan University', 'Sophomore')]]

IndexSlice对象

前面介绍的方法,即使在索引不重复的时候,也只能对元组整体进行切片,而不能对每层进行切片,也不允许将切片和布尔列表混合使用,引入IndexSlice对象就能解决这个问题。Slice对象一共有两种形式,第一种为loc[idx[*,*]]型,第二种为loc[idx[*,*],idx[*,*]]型。

  • loc[idx[*,*]]

    这种情况并不能进行多层分别切片,前一个*表示行的选择,后一个*表示列的选择,与单纯的loc是类似的

    df_ex.loc[idx['C':, ('D', 'f'):]]
    df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0
    
  • loc[idx[*,*],idx[*,*]]

    这种情况能够分层进行切片,前一个idx指代的是行索引,后一个是列索引。

    df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
    

多级索引的构造

除了使用set_index之外,常用的有from_tuples, from_arrays, from_product三种方法,它们都是pd.MultiIndex对象下的函数。

  • from_tuples指根据传入由元组组成的列表进行构造
  • from_arrays指根据传入列表中,对应层的列表进行构造
  • from_product指根据给定多个列表的笛卡尔积进行构造
my_tuple = [('a','cat'),('a','dog'),('b','cat'),('b','dog')]
pd.MultiIndex.from_tuples(my_tuple, names=['First','Second'])

my_array = [list('aabb'), ['cat', 'dog']*2]
pd.MultiIndex.from_arrays(my_array, names=['First','Second'])

my_list1 = ['a','b']
my_list2 = ['cat','dog']
pd.MultiIndex.from_product([my_list1, my_list2], names=['First','Second'])

索引的常用方法

索引层的交换

  • swaplevel:只能交换两个层,可以指定交换的是轴是哪一个,即行索引或列索引
  • reorder_levels:可以交换任意层,可以指定交换的是轴是哪一个,即行索引或列索引
![task3.5](D:\Note\pandas_learning\task3.5.PNG)np.random.seed(0)
L1,L2,L3 = ['A','B'],['a','b'],['alpha','beta']
mul_index1 = pd.MultiIndex.from_product([L1,L2,L3], names=('Upper', 'Lower','Extra'))
L4,L5,L6 = ['C','D'],['c','d'],['cat','dog']
mul_index2 = pd.MultiIndex.from_product([L4,L5,L6], names=('Big', 'Small', 'Other'))
df_ex = pd.DataFrame(np.random.randint(-9,10,(8,8)), index=mul_index1,  columns=mul_index2)
df_ex

在这里插入图片描述

df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一层和第三层交换
df_ex.reorder_levels([2,0,1],axis=0).head() # 列表数字指代原来索引中的层

索引层的删除

  • droplevel方法
df_ex.droplevel(1,axis=1)
df_ex.droplevel([0,1],axis=0)

索引属性的修改

  • 索引层名字的修改:rename_axis
  • 索引值的修改:rename,可指定修改的层level
df_ex.rename_axis(index={
    
    'Upper':'Changed_row'}, columns={
    
    'Other':'Changed_Col'}).head()

df_ex.rename(columns={
    
    'cat':'not_cat'}, level=2).head()
df_ex.rename(index=lambda x:str.upper(x), level=2).head()

# 整个索引的元素替换,使用迭代器
new_values = iter(list('abcdefgh'))
df_ex.rename(index=lambda x:next(new_values), level=2)
  • map方法来完成指定修改
# 指定修改
df_temp = df_ex.copy()
new_idx = df_temp.index.map(lambda x: (x[0], x[1], str.upper(x[2])))
df_temp.index = new_idx
df_temp.head()
# 压缩索引
df_temp = df_ex.copy()
new_idx = df_temp.index.map(lambda x: (x[0]+'-'+x[1]+'-'+x[2]))
df_temp.index = new_idx
df_temp.head() # 单层索引

索引的设置与重置

  • df.set_index([item], append), append表示是否来保留原来的索引,直接把新设定的添加到原索引的内层
  • df.reset_index([item], drop), drop表示是否要把去掉的索引层丢弃,而不是添加到列中
df_new = pd.DataFrame({
    
    'A':list('aacd'), 'B':list('PQRT'), 'C':[1,2,3,4]})
df_new.set_index('A')
df_new.set_index('A', append=True)

df_new.reset_index(['D'])
df_new.reset_index(['D'], drop=True)

索引的变形

  • df.reindex
  • df.reindex_like
df_reindex = pd.DataFrame({
    
    "Weight":[60,70,80], "Height":[176,180,179]}, index=['1001','1003','1002'])
df_reindex.reindex(index=['1001','1002','1003','1004'], columns=['Weight','Gender'])

df_existed = pd.DataFrame(index=['1001','1002','1003','1004'], columns=['Weight','Gender'])
df_reindex.reindex_like(df_existed)

索引的运算

索引的集合运算

S A . i n t e r s e c t i o n ( S B ) = S A ∩ S B ⇔ { x ∣ x ∈ S A   a n d   x ∈ S B } \rm S_A.intersection(S_B) = \rm S_A \cap S_B \Leftrightarrow \rm \{x|x\in S_A\, and\, x\in S_B\} SA.intersection(SB)=SASB{ xxSAandxSB}
S A . u n i o n ( S B ) = S A ∪ S B ⇔ { x ∣ x ∈ S A   o r   x ∈ S B } \rm S_A.union(S_B) = \rm S_A \cup S_B \Leftrightarrow \rm \{x|x\in S_A\, or\, x\in S_B\} SA.union(SB)=SASB{ xxSAorxSB}
S A . d i f f e r e n c e ( S B ) = S A − S B ⇔ { x ∣ x ∈ S A   a n d   x ∉ S B } \rm S_A.difference(S_B) = \rm S_A - S_B \Leftrightarrow \rm \{x|x\in S_A\, and\, x\notin S_B\} SA.difference(SB)=SASB{ xxSAandx/SB}
S A . s y m m e t r i c _ d i f f e r e n c e ( S B ) = S A △ S B ⇔ { x ∣ x ∈ S A ∪ S B − S A ∩ S B } \rm S_A.symmetric\_difference(S_B) = \rm S_A\triangle S_B\Leftrightarrow \rm \{x|x\in S_A\cup S_B - S_A\cap S_B\} SA.symmetric_difference(SB)=SASB{ xxSASBSASB}

df_set_1 = pd.DataFrame([[0,1],[1,2],[3,4]], index = pd.Index(['a','b','a'],name='id1'))
df_set_2 = pd.DataFrame([[4,5],[2,6],[7,1]],index = pd.Index(['b','b','c'],name='id2'))
id1, id2 = df_set_1.index.unique(), df_set_2.index.unique()
id1.intersection(id2)  
id1 & id2
id1.union(id2)
id1 | id2
id1.difference(id2)
(id1 ^ id2) & id1
id1.symmetric_difference(id2)
id1 ^ id2 # ^符号即对称差

可以用来进行一些类数据库的数据选择操作

练习

Ex1:公司员工数据集

现有一份公司员工数据集,

  1. 分别只使用queryloc选出年龄不超过四十岁且工作部门为DairyBakery的男性。
  2. 选出员工ID号 为奇数所在行的第1、第3和倒数第2列。
  3. 按照以下步骤进行索引操作:
  • 把后三列设为索引后交换内外两层
  • 恢复中间一层
  • 修改外层索引名为Gender
  • 用下划线合并两层行索引
  • 把行索引拆分为原状态
  • 修改索引名为原表名称
  • 恢复默认索引并将列保持为原表的相对位置
## Ex1
# 解法一(每个条件要加括号)
df.loc[(df.age<=40) & (df.department.isin(['Dairy','Bakery'])) & (df.gender=='M')].head()
# 解法二(只能用双引号)
df.query('age<=40 and department in ["Dairy", "Bakery"] and gender=="M"').head()

## Ex2
df[df.EmployeeID%2!=0].iloc[:,[0,2,-2]]

## Ex3
column_names = df.columns.values
a = list(column_names[-3:])
df.set_index(a,inplace=True)

df.reset_index(column_names[-2],inplace=True)
df.rename_axis(index = {
    
    column_names[-1]:'Gender'},inplace = True)

new_idx= df.index.map(lambda x:x[0]+'_'+x[1])
df.index = new_idx

new_idx = df.index.map(lambda x:tuple(x.split('_')))
df.index = new_idx

df.rename_axis([column_names[-3],column_names[-1]],inplace = True)

df = df.reset_index()
df = df[column_names]

参考:DataWhale joyful pandas第三章索引.

猜你喜欢

转载自blog.csdn.net/weixin_41545602/article/details/111567799