Pandas进阶叁 索引
pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充,本文部分引用了原教程,并参考了《利用Python进行数据分析》、pandas官网
这几天太忙了,这次没怎么总结学习的内容,先把做的作业题放一下,晚上刚从外地回来,明天开始有更多时间了,这次打卡内容有些单薄,希望助教大大理解(题目思考过程随后补上)
【练一练】
select_dtypes
是一个实用函数,它能够从表中选出相应类型的列,若要选出所有数值型的列,只需使用.select_dtypes('number')
,请利用布尔列表选择的方法结合DataFrame
的dtypes
属性在learn_pandas
数据集上实现这个功能。
在做这道习题的时候发现pandas里没有专门的number类型,由于在第一期学习numpy的时候我看过numpy dtype的类型层级结构,所以记得numpy的dtype里有number类型,numpy的层级结构如图所示:
dtype层级结构
图中有些类有下划线,是为了和python内置类有所区分。numpy包将这些类型作为整个包的全局常量,在这道题中我用了np.issubdtype
来判断一个类型是不是另一个类型的子类,pandas的数据的dtype也是通过numpy实现的,所以在这道题中我遍历所有列,判断其类型是不是np.number
的子类,来求出一个DataFrame实例的所有数值型的列。
import numpy as np
import pandas as pd
df = pd.read_csv('../data/learn_pandas.csv', usecols = ['School', 'Grade', 'Name', 'Gender', 'Weight', 'Transfer'])
df_demo = df.copy()
df_demo[[c for c in df_demo.columns if np.issubdtype(df_demo[c].dtype, np.number)]].head()
Weight | |
---|---|
0 | 46.0 |
1 | 70.0 |
2 | 89.0 |
3 | 41.0 |
4 | 74.0 |
%%timeit
df_demo = df.copy()
df_demo[[c for c in df_demo.columns if np.issubdtype(df_demo[c].dtype, np.number)]].head()
1.05 ms ± 188 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
df_demo.select_dtypes('number').head()
898 µs ± 8.67 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
【练一练】
与单层索引类似,若存在重复元素,则不能使用切片,请去除重复索引后给出一个元素切片的例子。
【END】
此外,在多级索引中的元组有一种特殊的用法,可以对多层的元素进行交叉组合后索引,但同时需要指定loc
的列,全选则用:
表示。其中,每一层需要选中的元素用列表存放,传入loc
的形式为[(level_0_list, level_1_list), cols]
。例如,想要得到所有北大和复旦的大二大三学生,可以如下写出:
【练一练】
尝试在rename_axis
中使用函数完成与例子中一样的功能。
【END】
对于整个索引的元素替换,可以利用迭代器实现:
五、练习
Ex1:公司员工数据集
现有一份公司员工数据集:
df = pd.read_csv('../data/company.csv')
df.head(3)
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
的男性。 - 选出员工
ID
号 为奇数所在行的第1、第3和倒数第2列。 - 按照以下步骤进行索引操作:
- 把后三列设为索引后交换内外两层
- 恢复中间一层
- 修改外层索引名为
Gender
- 用下划线合并两层行索引
- 把行索引拆分为原状态
- 修改索引名为原表名称
- 恢复默认索引并将列保持为原表的相对位置
dpt = ['Dairy', 'Bakery']
df.query("(age <= 40)&(department == @dpt)&(gender=='M')").head(3)
df.loc[(df.age<=40)&df.department.isin(dpt)&(df.gender=='M')].head(3)
df.iloc[(df.EmployeeID%2==1).values,[0,2,-2]].head()
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)
Ex2:巧克力数据集
现有一份关于巧克力评价的数据集:
df = pd.read_csv('../data/chocolate.csv')
df.head(3)
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
替换为空格。 - 巧克力
Rating
评分为1至5,每0.25分一档,请选出2.75分及以下且可可含量Cocoa Percent
高于中位数的样本。 - 将
Review Date
和Company Location
设为索引后,选出Review Date
在2012年之后且Company Location
不属于France, Canada, Amsterdam, Belgium
的样本。
df = pd.read_csv('../data/chocolate.csv')
df.columns = [' '.join(i.split('\n')) for i in df.columns]
df.head(3)
df['Cocoa Percent'] = df['Cocoa Percent'].apply(lambda x:float(x[:-1])/100)
df.query('(Rating<3)&(`Cocoa Percent`>`Cocoa Percent`.median())').head(3)
idx = pd.IndexSlice
exclude = ['France', 'Canada', 'Amsterdam', 'Belgium']
res = df.set_index(['Review Date', 'Company Location']).sort_index(level=0)
res.loc[idx[2012:,~res.index.get_level_values(1).isin(exclude)],:].head(3)