首页
学习
活动
专区
圈层
工具
发布
50 篇文章
1
【原创佳作】介绍Pandas实战中一些高端玩法
2
pandas 如何实现 excel 中的汇总行?
3
pandas多级索引的骚操作!
4
40000字 Matplotlib 实操干货,真的全!
5
利用Python搞定女朋友的小情绪~
6
Python 绘制惊艳的瀑布图
7
6种方式创建多层索引
8
Python 进阶指南(编程轻松进阶):三、使用 Black 工具来格式化代码
9
数据科学 IPython 笔记本 9.6 聚合:最小、最大和之间的任何东西
10
精通 Pandas 探索性分析:1~4 全
11
高手系列!数据科学家私藏pandas高阶用法大全 ⛵
12
总结了67个pandas函数,完美解决数据处理,拿来即用!
13
PyAutoGUI,一个Python办公自动化利器!
14
解放双手|利用 PyAutoGUI 快速构建自动化操作脚本
15
Python中内置数据库!SQLite使用指南! ⛵
16
数据分析索引总结(中)Pandas多级索引
17
数据分析索引总结(下)Pandas索引技巧
18
数据分析索引总结(上)Pandas单级索引
19
网友需求系列01-Python-matplotlib定制化刻度(主副)绘制
20
用Python自动生成数据分析报告
21
手把手教你用Python操纵Word自动编写离职报告
22
pandas transform 数据转换的 4 个常用技巧!
23
30段极简Python代码:这些小技巧你都Get了么
24
数据处理遇到麻烦不要慌,5个优雅的Numpy函数助你走出困境
25
数据分析最有用的Top 50 Matplotlib图(带有完整的Python代码)(上)
26
数据分析最有用的Top 50 Matplotlib图(带有完整的Python代码)(下)
27
数据分析之Pandas变形操作总结
28
数据分析之Pandas缺失数据处理
29
数据分析之Pandas合并操作总结
30
数据分析之Pandas分组操作总结
31
学习用Pandas处理分类数据!
32
如何用Pandas处理文本数据?
33
Pandas处理时序数据(初学者必会)!
34
Python高阶函数使用总结!
35
机器学习在金融风控的经验总结!
36
你知道怎么用Pandas绘制带交互的可视化图表吗?
37
6个提升效率的pandas小技巧
38
Python数据分析库pandas高级接口dt和str的使用
39
pandas 拼接 concat 5 个常用技巧!
40
pandas分组8个常用技巧!
41
pandas 文本处理大全
42
pandas 筛选数据的 8 个骚操作
43
pandas 分类数据处理大全(附代码)
44
68 个Python内置函数,你用过几个?
45
太秀了!用 pandas 搞定 24 张 Excel 报表
46
用 Python 的 Template 类生成文件报告
47
码如其人,同学你能写一手漂亮的Python函数吗
48
Python处理图片九宫格,炫酷朋友圈
49
Python排序傻傻分不清?一文看透sorted与sort用法
50
python-docx操作word文件(
清单首页python文章详情

数据分析索引总结(上)Pandas单级索引

Datawhale干货

作者:闫钟峰,Datawhale优秀学习者

寄语:本文对单级索引中的loc、iloc、[]三种方法进行了详细的阐述。同时,对布尔索引,快速标量索引方式、区间索引方式做了详细介绍。

读取csv数据的时候, 使用参数index_col指定表中的列作为索引

代码语言:javascript
复制
import numpy as np
import pandas as pd
df = pd.read_csv('data/table.csv',index_col='ID')
df.head()

效果等同于读取数据后, 使用set_index方法指定某一列为索引,但index_col的方式更简洁。

代码语言:javascript
复制
df1 = pd.read_csv('data/table.csv')
df2=df1.set_index(['ID'])
df2.head()

最常用的索引方法有三类:

  • loc表示标签索引
  • iloc表示位置索引
  • []也具有很大的便利性。

loc方法

注意:所有在loc中使用的切片全部包含右端点!

① 单行索引

代码语言:javascript
复制
df.loc[1103]

虽然这里的1103是整数, 但loc索引方式用的是索引标签, 而不是默认整数索引(注意默认整数索引和标签索引这二者有时候是一样的)

② 多行索引

多行索引时,需传入一个list,而不是多个索引

代码语言:javascript
复制
df.loc[[1102,2304]]

多行索引时, 传入的必须是一个list, 而不是两个或多个索引, 否则会报错

代码语言:javascript
复制
#以下索引报错
# TypeError: cannot do label indexing on <class 'pandas.core.indexes.base.Index'> with these indexers [2304] of <class 'int'>
df.loc[1102,2304]

list的切片方法可以沿用

代码语言:javascript
复制
df.loc[1304:].head()

和list的情形一样, 2402::-1表示从索引标签=2402的元素开始,以步长=1返回list的元素, 负号表示方向是从后向前。注意由于用的是loc, 所以这里的2402是标签索引, 这和list所用默认整数索引不一样。

代码语言:javascript
复制
df.loc[2402::-1].head()

③ 单列索引

使用loc方法获取列, 比直接使用列标签获取列更复杂

代码语言:javascript
复制
df.loc[:,'Height'].head()

等价的更简单的获取列的方法,loc和iloc的长处在于, 可以同时对列和行进行切片

代码语言:javascript
复制
df['Height'].head()

更简洁的使用列名标签索引的方式

代码语言:javascript
复制
df.Height.head()

④ 多列索引

代码语言:javascript
复制
df.loc[:,['Height','Math']].head()
# 等价于df[['Height','Math']].head()

注意传入的应该是list

代码语言:javascript
复制
df[['Height','Math']].head()

使用列名标签做切片---这是list里所没有的

代码语言:javascript
复制
df.loc[:,'Height':'Math'].head()

还可以使用iloc的方式进行切片, 这时候传入的应该是默认整数索引, 从0开始, 并且切片的结尾是不包含的

代码语言:javascript
复制
df.iloc[:,4:7].head()

切片方法对列名不成立,若使用报错如下:

代码语言:javascript
复制
# 以下语句报错
# TypeError: cannot do slice indexing on <class 'pandas.core.indexes.numeric.Int64Index'> with these indexers [Height] of <class 'str'>
df['Height':'Math'].head()

⑤ 联合索引

1102=起点,2401=终点,3=步长

代码语言:javascript
复制
df.loc[1102:2401:3,'Height':'Math']#.head()

⑥ 函数式索引

loc中使用的函数,传入参数就是前面的df;本质上这是一个布尔索引: lambda函数分别根据每行的Gender值列返回一个布尔值, 然后用这个布尔值序列来筛选df的行,布尔值为真则返回,否则筛选掉。

代码语言:javascript
复制
df.loc[lambda x:x['Gender']=='M'].head()

看起来和上述略有不同。但实际上, 使用loc等方法筛选行或者列的时候, 都是根据待筛选的行或者列对给定的筛选条件是否为真来决定是否返回该行或该列的。

代码语言:javascript
复制
def f(x):
    return [1101,1103]
df.loc[f]

⑦ 布尔索引

代码语言:javascript
复制
df.loc[df['Address'].isin(['street_7','street_4'])].head()
# 类似的sql语句为 select * from df where Address in ('street_7','street_4')

布尔值的Series

代码语言:javascript
复制
df['Address'].isin(['street_7','street_4'])
df.loc[[True if i[-1]=='4' or i[-1]=='7' else False for i in df['Address'].values]].head()
# 实现相同筛选的sql代码类似于 select * from df where substr(Address,-1,1) in ('4','7')

小节:本质上说,loc中能传入的只有布尔列表和索引子集构成的列表,只要把握这个原则就很容易理解上面那些操作。

iloc方法

① 单行索引

代码语言:javascript
复制
df.iloc[3]

② 多行索引

注意结尾是不包含的---和list的切片保持一致

代码语言:javascript
复制
df.iloc[3:5]

③ 单列索引

代码语言:javascript
复制
df.iloc[:,3].head()

④ 多列索引

逗号前的 : 对行筛选, 表示返回所有行。逗号后的 7::-2 表示从第8列开始,向前每隔一列取一列(步长为2, 2前的负号表示向前迭代)

代码语言:javascript
复制
df.iloc[:,7::-2].head()

⑤ 混合索引

从第四行开始向后以步长为4选择行, 从第八列开始向前以步长为2选择列。得到原始df的若干行和若干列的交叉位置组成的一个子df, 类似于子矩阵。

代码语言:javascript
复制
df.iloc[3::4,7::-2]#.head()

⑥ 函数式索引

注意: 由于是iloc,返回值必须是由默认整数索引作为元素构成的类list的数据结构。

代码语言:javascript
复制
df.iloc[lambda x:[3]].head()

如果返回值不存在, 则会报错。

# IndexError: positional indexers are out-of-bounds

代码语言:javascript
复制
df.iloc[lambda x:[30000]].head()
代码语言:javascript
复制
df.iloc[lambda x:range(3)]

其他比如 list(range(3)) , tuple(range(3))等等类list的数据结构都可以, 但set(range(3))不行。

代码语言:javascript
复制
df.iloc[lambda x:np.arange(3)] 

小节:由上所述,iloc中接收的参数只能为整数或整数列表,不能使用布尔索引。

[]操作符

如果不想陷入困境,请不要在行索引为浮点时使用[]操作符,因为在Series中的浮点[]并不是进行位置比较,而是值比较,非常特殊。

Series的[]操作

① 单元素索引

如果没有指明loc还是iloc, 默认传入的是索引标签

代码语言:javascript
复制
s = pd.Series(df['Math'],index=df.index)
s[1101]

使用loc方法

代码语言:javascript
复制
s.loc[1101]
s.head()

如果传入默认整数索引, 会出错---特别是索引标签也恰好包含了这个你传入的默认整数索引的时候,不会报错,但会返回和你想象中不一样的元素,需要特别注意

代码语言:javascript
复制
s[1]

使用iloc,后边接默认整数索引

代码语言:javascript
复制
s.iloc[1]

② 多行索引

使用的是绝对位置的整数切片,与元素无关,这里容易混淆。

代码语言:javascript
复制
s[0:4]

③ 函数式索引

注意使用lambda函数时,直接切片(如:s[lambda x: 16::-6])就报错,此时使用的不是绝对位置切片,而是元素切片,非常易错。

代码语言:javascript
复制
s[lambda x: x.index[16::-6]]

下面语句报错:

代码语言:javascript
复制
s[lambda x: 16::-6]
#TypeError: cannot do slice indexing on <class 'pandas.core.indexes.numeric.Int64Index'> with these indexers [<function <lambda> at 0x00000000083FFCA8>] of <class 'function'>

是因为方括号不能直接接整数索引切片吗? 并不是, 直接传递整数切片是可以的。

代码语言:javascript
复制
s[16::-6]
代码语言:javascript
复制
def f(x):
    return x[16::-6]
f(s)

改一下lambda函数定义,返回了一个预料之外的Series。

代码语言:javascript
复制
s[lambda x: x[16::-6]]

再改一下,改为取索引。

代码语言:javascript
复制
s[lambda x: x[16::-6].index]

这样就对了---这是因为, 这里的lambda函数返回的是索引, 因此能够根据索引正确地返回s的一段切片。因为lambda函数返回值是索引, 索引通过方括号传递给s,就可以取回s的相应索引位置的元素。

代码语言:javascript
复制
s[16::-6].index

作为对比, 最普通的形式其实是这样的---这里的16是默认整数索引。

代码语言:javascript
复制
s[16::-6]

使用apply方法怎么实现?

代码语言:javascript
复制
s.apply??

④ 布尔索引

代码语言:javascript
复制
s[s>80]
# 类似的sql语句是 select * from s where s.value>80

复杂的布尔索引--可能是支持一阶谓词逻辑的

代码语言:javascript
复制
s[(s>80)&(s<95)] # 没有括号的时候会报错--s[(s>80&s<95)]

DataFrame的[]操作

① 单行索引

这里非常容易写成df['label'],会报错。同Series使用了绝对位置切片。

代码语言:javascript
复制
df[1:2]

如果使用了标签索引, 程序仍会认为传入的是默认整数索引,就会得到意料之外的结果

代码语言:javascript
复制
df[1102:]

这个没有报错, 是因为并没有像上边直接去找整数索引等于1102的,而是用一个空的索引集合去取df的子集。如果想要获得某一行,更好的办法是用如下的 get_loc 方法:

代码语言:javascript
复制
row = df.index.get_loc(1102) # df.index.get_loc 将标签索引转换为默认整数索引
df[row:row+1]
df.index.get_loc??

使用loc方法得到的是个Series

代码语言:javascript
复制
df.loc[1102,:]

为了得到的一行仍然是个df, 需要传入区间--注意传入的1102是标签索引,因此尾端是不包含的。

代码语言:javascript
复制
df.loc[1102:1102,:]

② 多行索引

用切片,如果是选取指定的某几行,推荐使用loc,否则很可能报错。尽管这种写法可以正确执行,但不推荐。

代码语言:javascript
复制
df[3:5]

推荐使用iloc或者loc来明确地说明用的是默认整数索引(iloc, 尾端不包含)还是标签索引(loc,尾端包含)。

代码语言:javascript
复制
df.iloc[3:5]

③ 单列索引

使用列名标签来返回单列,之所以选择列的语法如此简单, 是因为df本质上是将多个Series作为列拼接起来的。

代码语言:javascript
复制
df['School'].head()

将列传递给df构造器

代码语言:javascript
复制
pd.DataFrame(df['School']).head()

使用to_frame方法将Series转为df

代码语言:javascript
复制
df['School'].to_frame().head()

使用iloc方法返回同一列--注意由于是iloc,所以这里传入了列的默认整数索引。

代码语言:javascript
复制
df.iloc[:,0].head()

使用loc时, 需使用标签索引---如果df只有默认整数索引, 这时候默认整数索引同时也是标签索引。

代码语言:javascript
复制
df.loc[:,'School'].head()

只有默认整数索引,因此默认整数索引也是标签索引的一个例子

代码语言:javascript
复制
df1=pd.DataFrame(np.random.randint(1,10,12).reshape(3,4))

对上述df1, 使用iloc, 自然会返回第一列

代码语言:javascript
复制
df1.iloc[:,0]

对上述df1, 使用loc时, 也会正确地返回第一列。

代码语言:javascript
复制
df1.loc[:,0]

但是传入的是切片的时候,会默认使用的是默认整数索引, 因此尾端是不包含的。

代码语言:javascript
复制
df1[0:2]

除非显式地使用loc,才会包含尾端

代码语言:javascript
复制
df1.loc[0:2]

单列索引的另一个更加简单的选择方式

代码语言:javascript
复制
df.School.head()

如果列名标签中有空格, 这没法用这种方式

代码语言:javascript
复制
df.columns=['School Name', 'Class', 'Gender', 'Address', 'Height', 'Weight', 'Math', 'Physics']

如果用这种形式,会报错

代码语言:javascript
复制
df.School Name

上式报错SyntaxError: invalid syntax,这时只有用

代码语言:javascript
复制
df['School Name'].head()

复原columns

代码语言:javascript
复制
df.columns=['School', 'Class', 'Gender', 'Address', 'Height', 'Weight', 'Math', 'Physics']

④ 多列索引

索引多列时,传入的必须是一个list,而不是多个列名标签--方括号应该有两层。

代码语言:javascript
复制
df[['School','Math']].head()

使用loc方式

代码语言:javascript
复制
df.loc[:,['School','Math']].head()

使用iloc方式--传入的列名是默认整数索引,从0开始

代码语言:javascript
复制
df.iloc[:,[0,6]].head()

⑤函数式索引

这里的lambda是一个常数值的函数

代码语言:javascript
复制
df[lambda x:['Math','Physics']].head()

一个选取列名长度大于5的函数,其实这仍然是个常值函数

代码语言:javascript
复制
df[lambda x: [x for x in df.columns if len(x)>5]].head()

改进版--选择列名长度大于5的列

代码语言:javascript
复制
df[lambda x: [a for a in x.columns if len(a)>5]].head()

df1 的列名是默认整数索引, 需先转化为string类型---当然, 由于一位数的长度都小于5, 所以会是个空df。

代码语言:javascript
复制
df1[lambda x: [a for a in x.columns if len(str(a))>5]].head()

lambda函数有时候不是必要的, 用列表推导式同样可以实现上述筛选, 而且更容易理解。关键是要给df后的[]传入一个list。

代码语言:javascript
复制
df[[ x for x in df.columns if len(x)>5]].head()

⑥ 布尔索引

代码语言:javascript
复制
df[df['Gender']=='F'].head()
# 等价的sql语句 select * from df where Gender='F'
代码语言:javascript
复制
df[df.Gender=='F'].head()

小节:一般来说,[]操作符常用于列选择或布尔选择,尽量避免行的选择

布尔索引

1. 布尔符号:'&','|','~':分别代表和and,或or,取反not

代码语言:javascript
复制
df[(df['Gender']=='F')&(df['Address']=='street_2')].head()
# 等价的sql语句 select * from df where Gender='F' and Address='street_2'
代码语言:javascript
复制
df[(df['Math']>85)|(df['Address']=='street_7')].head()
# select * from df where df.Math>85 or df.Address='street_7'
代码语言:javascript
复制
df[~((df['Math']>75)|(df['Address']=='street_1'))].head()
# 等价的sql语句 select * from df where not (math>75 or Address='street_1')
代码语言:javascript
复制
#df[~((df['Math']>75)|(df['Address']=='street_1'))].head()
df[~(df['Math']>75)][~(df['Address']=='street_1')].head()

loc和[]中相应位置都能使用布尔列表选择:

如果不加values就会索引对齐发生错误,Pandas中的索引对齐是一个重要特征,很多时候非常使用。但是若不加以留意,就会埋下隐患。这个筛选不能直接对应到相应的sql ,因为sql没有对列名做筛选的机制。

代码语言:javascript
复制
df.loc[df['Math']>60,(df[:8]['Address']=='street_6').values].head()

在对列的筛选中, 如果不加values属性,得到的是一个布尔值的Series。

代码语言:javascript
复制
df[:8]['Address']=='street_6'

使用了values属性,就得到一个list,就可以用来选择列了。

代码语言:javascript
复制
(df[:8]['Address']=='street_6').values

不加values 会报错,因为传入的不是list。

代码语言:javascript
复制
df.loc[df['Math']>60,(df[:8]['Address']=='street_6')].head()
# IndexingError: Unalignable boolean Series provided as indexer (index of the boolean Series and of the indexed object do not match).

2. isin方法

代码语言:javascript
复制
df[df['Address'].isin(['street_1','street_4'])&df['Physics'].isin(['A','A+'])]
# select * from df where Address in ('street_1','street_4') and Physic in ('A','A+')

上面也可以用字典方式写:all与&的思路是类似的,其中的1代表按照跨列方向判断是否全为True。熟悉sql的话,显然上一个筛选方法更容易理解。

代码语言:javascript
复制
df[df[['Address','Physics']].isin({'Address':['street_1','street_4'],'Physics':['A','A+']}).all(1)

快速标量索引

当只需要取一个单元格里的元素时,at和iat方法能够提供更快的实现

代码语言:javascript
复制
display(df.at[1101,'School'])
display(df.loc[1101,'School'])
display(df.iat[0,0])
display(df.iloc[0,0])
#可尝试去掉注释对比时间
%timeit df.at[1101,'School']
%timeit df.loc[1101,'School']
%timeit df.iat[0,0]
%timeit df.iloc[0,0]
#当数据集更大的时候,差别更明显
代码语言:javascript
复制
df.at[1101,:]
# at方法只能选择单元格?
# TypeError: unhashable type: 'slice'
代码语言:javascript
复制
df.at[1101,df.columns[:3]]
# TypeError: unhashable type: 'Index'
代码语言:javascript
复制
df.at??
#     Access a single value for a row/column label pair.

区间索引

此处介绍并不是说只能在单级索引中使用区间索引,只是作为一种特殊类型的索引方式,在此处先行介绍。

1. 利用interval_range方法

closed参数可选'left''right''both''neither',默认左开右闭#只传入start和end,默认 freq=1。

代码语言:javascript
复制
pd.interval_range(start=0,end=5)

periods参数控制区间个数,freq控制步长。

代码语言:javascript
复制
pd.interval_range(start=0,periods=8,freq=5)
代码语言:javascript
复制
pd.interval_range??

2. 利用cut将数值列转为区间为元素的分类变量,

例如统计数学成绩的区间情况:使用pd.cut函数进行分割后, 如果没有类型转换,此时并不是区间类型,而是category类型。

代码语言:javascript
复制
math_interval = pd.cut(df['Math'],bins=[0,40,60,80,100])

默认是左开右闭区间,可以使用right参数指定是左闭右开还是左开右闭。在选择bins的时候,bins的范围尽量将数据取值区间完全包括在内,避免因区间开闭导致取值被舍去。

代码语言:javascript
复制
math_interval.head()
代码语言:javascript
复制
math_interval.values

3. 区间索引的选取

将数学成绩转化为分数所在区间, 效果类似于降采样。

代码语言:javascript
复制
df_i = df.join(math_interval,rsuffix='_interval')[['Math','Math_interval']].reset_index().set_index('Math_interval')
df_i.head()

它是个Categories类型。

代码语言:javascript
复制
df_i.index

包含该值就会被选中

代码语言:javascript
复制
df_i.loc[65].head()

传入包含两个值的list,则list中每个元素所在的区间都会被选中。

代码语言:javascript
复制
df_i.loc[[65,90]]
df_i.loc[[65,30]]

如果想要选取某个区间,先要把分类变量转为区间变量,再使用overlap方法:

报错--这是由于cut得到的看起来像是区间的数据类型, 实际上是个categories。cut得到的区间实际上是个catagory 类型的数据,并不能直接用来判断和给定区间是否重合,必须使用astype转换为区间类型的数据。

代码语言:javascript
复制
df_i.loc[pd.Interval(70,75)].head() 
df_i[df_i.index.astype('interval').overlaps(pd.Interval(70, 85))].head()

overlaps 用于判断一个由区间构成的类list的表里的元素是否与给定的区间有重合,有重合则返回True---本质上还是传递一个布尔值list给df_i。返回所有的行索引(转换为区间后)与给定区间有重叠的行。

cut得到的区间实际上是个catagory 类型的数据,并不能直接用来判断和给定区间是否重合,必须使用astype转换为区间类型的数据。

代码语言:javascript
复制
df_i.index

overlaps 用于判断一个由区间构成的类list的表里的元素是否与给定的区间有重合,有重合则返回True。

代码语言:javascript
复制
tmp=df_i.index.astype('interval')
tmp.overlaps??

不能同时传入两个区间。

代码语言:javascript
复制
df_i[df_i.index.astype('interval').overlaps(pd.Interval(70, 85),pd.Interval(20, 35))].head()

可以看到,最终传给df_i后的方括号的,还是一个布尔值索引。

代码语言:javascript
复制
tmp.overlaps(pd.Interval(20, 35))
代码语言:javascript
复制
pd.Interval(70, 85)
代码语言:javascript
复制
pd.Interval??
# left, right, close 三个参数
下一篇
举报
领券