索引器
感谢datawhale开展的每月组队学习,教材ref: 第三章:索引
表的列索引
序列的行索引
loc
索引器
练一练,实现 select_dtypes('number')
select_dtypes
是一个实用函数,它能够从表中选出相应类型的列,若要选出所有数值型的列,只需使用 .select_dtypes('number')
,请利用布尔列表选择的方法结合 DataFrame
的 dtypes
属性在 learn_pandas
数据集上实现这个功能。
df_demo.loc[:,[str(df_demo[v].dtype) in {'float64', 'int64'} for v in df_demo.columns]]
思路:
- 感觉我这样的写法有点emm,但是结果是一样的,就先这样吧。后面学完再回来看看能不能再优化一点
- 我需要遍历dataframe的列,通过 df.col_name 或 df[‘col_name’] 获得该列series的数据类型
[str(df_demo[v].dtype) for v in df_demo.columns]
获得每个列的数据类型组成的列表- 因为我们是可以在
.loc[]
中使用布尔列表的,于是我们可以对上面得到的 数据类型列表 做出判断(看他们是否为'float64'
和'int64'
,用set可以比用list少一丢丢丢丢丢time complexity 😅),得到布尔列表- 因为
.loc
只传一个进去的话,默认是取行的,但是我们是要取列。那么行的选择就用:
来代表我全部都要!
. 然后列的选择就放我们得到的布尔列表就好。
iloc
索引器
loc
vs iloc
iloc
是 index-based selection,loc
是 label-based selectioniloc
和loc
都是 先行后列iloc
相较来比loc
简单一点,因为iloc
忽略掉了表的索引loc
如果你有meaningful的索引,那么直接用表的索引可能更简单一些iloc
和python本身slice一样,前闭后开。loc
前闭后闭
query方法
随机抽样
多级索引
练一练
与单层索引类似,若存在重复元素,则不能使用切片,请去除重复索引后给出一个元素切片的例子。
temp = df_multi[~df_multi.index.duplicated(keep='first')]
去重之后的dataframe要sort一下,因为直接slice会报UnsortedIndexError
的错temp.sort_index(ascending=True, inplace=True) temp.loc[(slice('Peking University','Tsinghua University',2), slice('Freshman','Sophomore',2)), :]
去重+sorted index之后的dataframe
slice之后得到的,步长都设为了2
索引的常用方法
索引运算
练习
Ex.1 公司员工数据集
现有一份公司员工数据集:
In [167]: df = pd.read_csv('data/company.csv')
In [168]: df.head(3)
Out[168]:
EmployeeID birthdate_key age city_name department job_title gender
0 1318 1/3/1954 61 Vancouver Executive CEO M
1 1319 1/3/1957 58 Vancouver Executive VP Stores F
2 1320 1/2/1955 60 Vancouver Executive Legal Counsel F
分别只使用 query 和 loc 选出年龄不超过四十岁且工作部门为 Dairy 或 Bakery 的男性。
loc
def condition(x): condition_1 = x.age <= 40 condition_2_1 = x.department == 'Dairy' condition_2_2 = x.department == 'Bakery' condition_2 = condition_2_1 | condition_2_2 condition_3 = x.gender == 'M' res = condition_1 & condition_2 & condition_3 return res df.loc[condition]
query
df.query( '((department == "Dairy")&' '(gender == "M")&' '(age <= 40))|' '((department == "Bakery")&' '(gender == "M")&' '(age <= 40))' )
选出员工 ID 号 为奇数所在行的第1、第3和倒数第2列。df.loc[df.EmployeeID%2==0].iloc[:,[0,2,-2]]
先用
loc
[布尔列表]取出所有工号为奇数的
然后用iloc
(因为取基于位置的用iloc会比较方便)行全部都要,所以行的选择用:
,然后列的选择用 整数列表 把我们想要的列放进去。
按照以下步骤进行索引操作
-
把后三列设为索引后交换内外两层
temp = df.set_index(['department','job_title','gender'])
交换内外两层?是指 department 和 gender位置换一下?temp = temp.reorder_levels(['gender','job_title','department'])
df.reorder_levels([新multiindex的顺序列表]
-
恢复中间一层
temp.reset_index(level='job_title')
df.reset_index()
-
修改外层索引名为 Gender
df.index.set_names()
temp.index.set_names('Gender', level=0, inplace=True)
-
用下划线合并两层行索引
这可能不是最优解法吧,但是最终目的是达到的
ind = temp.index
ind = pd.Index([x[0]+'_'+x[1] for x in ind.tolist()])
temp.index = ind
再重新把indx新index赋给dataframe -
把行索引拆分为原状态
🤯
重新创建一个MultiIndex object,再赋给原dataframe就好了
df.index = pd.MultiIndex objecttemp.index = pd.MultiIndex.from_arrays([temp.index.str[:1], temp.index.str[2:]], names=('Gender','Depart'))
-
修改索引名为原表名称
可以在上面那步的时候直接传入参数 names=(索引名)
df.index.names = (new_index_names)temp.index.names = ('gender', 'department')
-
恢复默认索引并将列保持为原表的相对位置
df.reset_index()
#恢复默认索引 df_final = temp.reset_index() # 调整dataframe列的顺序 df_final = df_final[['EmployeeID', 'birthdate_key', 'age', 'city_name', 'department','job_title', 'gender']]
Ex2:巧克力数据集
现有一份关于巧克力评价的数据集:
In [169]: df = pd.read_csv('data/chocolate.csv')
In [170]: df.head(3)
Out[170]:
Company Review\nDate Cocoa\nPercent Company\nLocation Rating
0 A. Morin 2016 63% France 3.75
1 A. Morin 2015 70% France 2.75
2 A. Morin 2015 70% France 3.00
把列索引名中的 \n 替换为空格。
x.replace(‘被替换str’, ‘新str’)
choco_df.columns = [x.replace('\n', ' ') for x in choco_df.columns]
巧克力 Rating 评分为1至5,每0.25分一档,请选出2.75分及以下且可可含量 Cocoa Percent 高于中位数的样本。
rating <= 2.75 && cocoa percent > median
通过 choco_df.info() 我们可以发现 Cocoa Percent那列的 数据类型是 object,所以我们不能对它直接使用 统计函数,我们需要先把它的数据类型转换成floatchoco_df['Cocoa Percent'] = choco_df['Cocoa Percent'].str.rstrip('%').astype('float') / 100.0
然后cocoa_meidan = choco_df['Cocoa Percent'].median() choco_df.loc[(choco_df['Rating'] <= 2.75) & (choco_df['Cocoa Percent']>cocoa_meidan)]
将 Review Date 和 Company Location 设为索引后,选出 Review Date 在2012年之后且 Company Location 不属于 France, Canada, Amsterdam, Belgium 的样本。
choco_df_2 = choco_df.set_index(['Review Date', 'Company Location']) choco_df_2 = choco_df_2.sort_index() mask = ((choco_df_2.index.get_level_values(1) == 'Frence') |(choco_df_2.index.get_level_values(1) == 'Canada') |(choco_df_2.index.get_level_values(1) == 'Amsterdam') |(choco_df_2.index.get_level_values(1) == 'Belgium')) choco_df_2[~mask].loc[idx[2012:]]
首先将Review Date 和 Company Location设为索引,为避免性能警告
😣 尝试了好几次两个条件一起loc,都没能成功.
但是可以先把满足 Company Location条件的 过滤出来,再去slice Review Date在2012 之后的(这里我理解的是包含2012)。
😯 哦… 我看到了
再试一下idx = pd.IndexSlice choco_df_2.loc[idx[2012:, ~choco_df_2.index.get_level_values(1).isin(['France','Canada','Amsterdam','Belgium']) ], :]
哦猴… 结果居然shape不一样…