Joyful Pandas 第三章 索引
写在前面:
- 前两天每天看了大概4小时左右,对着教程把每个知识点敲了一遍。敲完之后感觉好多知识点彼此之间好相似,关掉教程回想这一章都大概讲了什么、什么顺序,发现只能回忆起局部的小点,框架上自己根本理不清。于是今天把教程又看了一遍,对照着整理导图,导图形成并不费很多时间,只是在整理过程中逐渐发现每个小节的关联、每个知识点的异同,这个反复看的过程比较费时间,不过好在之后终于清晰了一些。尤其在做习题过程中,强迫自己不要看教程去回想每道题对应什么知识点,然后去导图上对照,细节不清的再去看教程,这个过程很痛苦 但是一轮下来感觉也强化了记忆。真不错~再次感谢这么无敌优质的教程。之前在索引这块总是傻傻分不清,这章真的学到好多。
- 也是因为这章知识点琐碎,jupyter notebook太太太长了,我就不放上来了,只放我的导图+课后习题部分。
练习1:公司员工数据集
(1)分别只使用 query 和 loc 选出年龄不超过四十岁且工作部门为 Dairy 或 Bakery 的男性。
我的答案
## query
df.query('((age <= 40)&(department=="Dairy")&(gender=="M"))|'
'((age <= 40)&(department=="Bakery")&(gender=="M"))')
## loc——布尔列表+复合条件
condition_1 = df.age <= 40
condition_2 = df.department == 'Dairy'
condition_3 = df.department == 'Bakery'
condition_4 = df.gender == 'M'
df.loc[condition_1 & (condition_2 | condition_3) & condition_4]
参考答案
## query
dpt = ['Dairy', 'Bakery']
df.query("(age <= 40)&(department == @dpt)&(gender=='M')")
# 好简洁,我服惹...
## loc
df.loc[(df.age<=40)&df.department.isin(dpt)&(df.gender=='M')]
心得
- 偶尔会忘记同一组复合条件要用括号括起来
'((age <= 40)&' '(department=="Dairy"))'
- 逻辑运算符写在括号外!!
'((age <= 40)&' '(department=="Dairy"))|'
- 可以使用
@
引用外部变量,强化记忆一下
(2)选出员工 ID 号为奇数所在行的第1、第3和倒数第2列。
我的答案
# 基于元素
odd_row = df.loc[df.EmployeeID % 2 != 0]
# 基于位置
odd_row.iloc[:,[0,2,-2]]
参考答案
df.iloc[(df.EmployeeID%2==1).values,[0,2,-2]]
心得
- 答案太简洁辽!只是用基于位置的
iloc
写,要注意.values
不要落下它 - 基于位置时要时刻记得索引是从0开始的,最开始写时看到题问第一行直接就写了个1 = =
(3)按步骤完成下面内容
我的答案
# 3-1 把后三列设为索引后交换内外两层——索引层交换
df_3_1 = df.set_index(['department','job_title','gender'])
df_3_1.swaplevel(0,2,axis=0).head(2)
## 3-2 恢复中间一层——索引的重置
df_3_2 = df_3_1.reset_index(['job_title'])
df_3_2.head()
## 3-3 修改外层索引名为 Gender——索引层名字修改
df_3_3 = df_3_2.rename_axis(index={'gender':'Gender'})
df_3_3.head()
## 3-4 用下划线合并两层行索引——map压缩
new_index = df_3_3.index.map(lambda x:(x[0] + '_' + x[1]))
df_3_3.index = new_index
df_3_3
## 3-5 把行索引拆分为原状态——map 反向展开
new_index = df_3_3.index.map(lambda x:tuple(x.split('_')))
df_3_3.index = new_index
df_3_3
## 3-7 恢复默认索引并将列保持为原表的相对位置——重置+索引变形
df_3_7 = df_3_3.reset_index()
df_3_7 = df_3_7.rename(columns={'level_0':'department','level_1':'gender'})
df_3_7 = df_3_7.reindex(df.columns, axis=1)
df_3_7.equals(df) # True
参考答案
df_op = df.copy()
df_op = df_op.set_index(df_op.columns[-3:].tolist()).swaplevel(0,2,axis=0)
df_op = df_op.reset_index(level=1)
df_op = df_op.rename_axis(index={'gender':'Gender'})
df_op.index = df_op.index.map(lambda x:'_'.join(x))
df_op.index = df_op.index.map(lambda x:tuple(x.split('_')))
df_op = df_op.rename_axis(index=['gender', 'department'])
df_op = df_op.reset_index().reindex(df.columns, axis=1)
df_op.equals(df)
心得
lambda x:'_'.join(x)
这里学到啦- 最后一个小问最开始用的是
df_reindex.reindex_like(df_existed)
,要注意两个表位置,前者为有数值+准备变化的表,后者为想变成的样子
练习2:巧克力数据集
(1)把列索引名中的 \n 替换为空格
我的答案
df = df.rename(columns=lambda x:str.replace(x, '\n', ' '))
参考答案
df.columns = [' '.join(i.split('\n')) for i in df.columns]
(2)巧克力 Rating 评分为1至5,每0.25分一档,请选出2.75分及以下且可可含量 Cocoa Percent 高于中位数的样本。
我的答案
mylist = df['Cocoa Percent'].values.tolist()
df['Cocoa Percent'] = [float(x.strip('%'))/100 for x in mylist]
df['Cocoa Percent'].median() # 0.7
df.query('(Rating <= 2.75) & (`Cocoa Percent`>0.7)')
参考答案
df['Cocoa Percent'] = df['Cocoa Percent'].apply(lambda x:float(x[:-1])/100)
df.query('(Rating<3)&(`Cocoa Percent`>`Cocoa Percent`.median())')
心得
- 要熟悉
apply
用法 query
索引名有空格的话,记得用``代替引号
(3)将 Review Date 和 Company Location 设为索引后,选出 Review Date 在2012年之后且 Company Location 不属于 France, Canada, Amsterdam, Belgium 的样本。
我的答案&参考答案
df_new = df.set_index(['Review Date','Company Location'])
idx = pd.IndexSlice
no_location = ['France', 'Canada', 'Amsterdam', 'Belgium']
df_new = df_new.sort_index(level=0)
df_new.loc[idx[2012:,~df_new.index.get_level_values(1).isin(no_location)],:]
心得
- 用的是
loc[idx[*,*]] 型
,前一个*
表示行的选择,后一个*
表示列的选择 - 得到某一层的索引这个知识点忘记了
.index.get_level_values(1)
得到第一层的索引