Pandas: Index与Selection方式 (Part I)

本文的内容是关于:

  1. Pandas 三种基本的Index方式: .loc, .iloc, [].

  2. Pandas 基本的Selection Data的方式, Selection By Label, Selection By Position 以及 Selection by Callable.

基本的Index方式

import pandas as pd
import numpy as np

Pandas目前有3种不同的Multi Axis 索引方式

  1. .loc 索引
  2. .iloc 索引
  3. [] 索引, A.K.A get_item() 索引

.loc 索引

使用Multi Axis索引从一个Object中取出值, 使用如下方式 (.iloc相同).

Object Type     Indexers

Series          s.loc[indexer]

DataFrame       df.loc[row_indexer,column_indexer]

Panel           p.loc[item_indexer,major_indexer,minor_indexer]

在以上的notation中, 任何一个axis accessors都可以是 null slice :, 如果没有明确某个 axis的slice,就会被认为是 :. 比如
df.loc['a']
等价于
df.loc['a', :]

# .loc 索引
dates = pd.date_range('1/1/2000', periods=8)
df = pd.DataFrame(np.random.randn(8,4), index=dates, columns=['A','B','C','D'])
df.loc[:,'A':'C']

从以上的索引代码
df.loc[:, 'A':'C']
可以看出, Pandas的 slice是包含了开始和结束的元素的. 这点和Python, Numpy的Slice不同

[] index

[] index的首要功能, 是选择出低维度(lower-dimensional)的slice. 当使用 [] 去索引pandas Object时, 会有如下返回值

Object Type     Selection           Return Value Type
Series          series[label]       scalar value
DataFrame       frame[colname]      Series corresponding to colname
Panel           panel[itemname]     DataFrame corresponding to the itemname
  1. 将一个col name传入 [] 索引
df['A']
2000-01-01    1.002840
2000-01-02    1.339418
2000-01-03    0.934340
2000-01-04   -1.350004
2000-01-05    0.579421
2000-01-06   -0.094316
2000-01-07   -1.327806
2000-01-08   -0.136517
Freq: D, Name: A, dtype: float64
  1. 将一个col list传入 [] 索引
df[['A','B','C']]
df[['B','A']] = df[['A','B']]
df

使用

df[['A','B']] = df[['B','A']]

可以很方便地用于in place transform for a subset of the columns.

该语句等价于:

df.__set_item__(['A','B'], df.__get_item__(['B','A']))

需要注意的是,

df.loc[:, ['B', 'A']] = df[['A','B']]

不能将 AB 互换. 因为使用 .loc 索引时, Pandas会 align all AXES, 我对这个表述的理解是, Pandas会自动对齐各Label以及各Index. 如果要使用 .loc 索引将两个col的值互换, 应该使用以下方法:

df.loc[:, ['B', 'A']] = df[['A', 'B']].values

其中, values 的作用是

Return a Numpy representation of the DataFrame.Only the values in the DataFrame will be returned, the axes labels will be removed

使用这种方式, 可以在取出 A,B两列后, 去除Label信息. 使用 .loc 赋值时, 就不会发生 align AXES的情况.

具体见以下代码:

df
df.loc[:, ['B', 'A']] = df[['A', 'B']]
df
df.loc[:, ['B', 'A']] = df[['A', 'B']].values
df

额外的低频使用的Index方式: Attribute Index

我们也可以用Attribute Index的方式(.attr_name).
Object Type Selection Result
Series index
Dataframe column
Panel dataframe

# Series Attribute Index
sa = pd.Series([1,2,3], index = list("abc"))
sa.a
#DataFrame Attribute Index
dfa = df.copy()
dfa
dfa.A
2000-01-01    1.002840
2000-01-02    1.339418
2000-01-03    0.934340
2000-01-04   -1.350004
2000-01-05    0.579421
2000-01-06   -0.094316
2000-01-07   -1.327806
2000-01-08   -0.136517
Freq: D, Name: A, dtype: float64

当DataFrame中某一列已存在时, 可以使用Attribute Index为该列赋值.

dfa.A = list(range(len(dfa.index)))
dfa

** 当DataFrame中某一列存在时, 无法使用Attribute Index 去获取该列, 也不能使用Attribute Index去创建新列. **

当尝试使用Attribute Index创建一个新列时, 事实上没有创建列, 而是真的为该对象添加了一个新的属性...

而应该使用 [] Index 或者 .loc Index 去创建.

dfNoA = df.loc[:,['B','C', 'D']].copy()
dfNoA
# 用 Attribute Index 尝试创建新列时, 此时 F 会作为 dfNoA的属性存在, 而不是DataFrame中的一个Col
dfNoA.F = list(range(len(df.index)))
/Users/zxwang/.pyenv/versions/tensorflow/lib/python3.6/site-packages/ipykernel_launcher.py:2: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access
dfNoA
dfNoA.F
[0, 1, 2, 3, 4, 5, 6, 7]
# 使用 loc Index 创建新列
dfNoA.loc[:,'A'] = list(range(len(df.index)))
dfNoA
# 使用 [] index 创建新列
dfNoA1 = df.loc[:,['B','C', 'D']].copy()
dfNoA1['A'] = list(range(len(df.index)))
dfNoA1

Slicing Ranges

DataFrame 中使用 [] Index进行Range Slice时, 是在 Row 上进行起作用的.

df
df[:3]

Selection By Label

Pandas提供了一组方法, 以允许纯粹地基于Label进行Index.

.loc 索引方法是Pandas使用label索引时最重要的的索引方法. 以下是 .loc 索引的有效输入

  1. 单独的Label, 比如 'a'或者 5. 但是请注意, 单独的Label传入loc索引, 是对Row Label的索引. 以及5 在此处是 Index的Label, 并不代表 "第5个元素".

  2. List or Array of Labels. ['a', 'b', 'c']

  3. Slice Object. ['a':'c']. 注意, Pandas的Slice是开始和结束都包括在索引里的.

  4. A boolean Array

  5. A callable.

df1 = pd.DataFrame(np.random.randn(6, 4), index=list("abcdef"), columns=list("ABCD"))
df1
# Access with list of index labels
df1.loc[['a','b','c'], :]
#Access with list of col labels
df1.loc[:, ['A', 'C']]
# Access via label slice object
df1.loc['b', 'A':'C']
A   -1.909139
B    0.782975
C    1.155431
Name: b, dtype: float64
# Getting Value with boolean array
df1.loc[:, df1.loc['b'] > 0]

以上的语句表达的含义为:

找到这样的Col的所有值, 这些col在 "b" index(row) 上的值应该大于0. 这个.loc索引语句是根据行值选择列.

如果单独执行 b 语句. 可以看出它返回的是对应各Col的 Boolean Series.

df1.loc['b'] > 0
A    False
B     True
C     True
D    False
Name: b, dtype: bool

Slicing with Labels 细节

很明显, 使用 Label进行Slice是需要关注一些细节的.
在Python的list slice中, slice的开始和结束都是基于"位置"的. 而不管list的长度怎样, 在位置上这些总是连续的. 如果对 Pandas 对象使用基于Label的slice. 很明显地是, Label并不总是连续, 甚至是乱序的. 比如 col label可以是 ['b', 'f', 'a'] , 此时如果使用slice会发生什么?

答案是... 会拿到位于起始label与结束label之间的所有label, 包括起始label和结束label.

s1 = pd.Series(list('abcde'), index=[0,3,2,5,4])
# 拿到开始和结束label之间的所有label, 包括开始和结束label
s1.loc[3:5]
3    b
2    c
5    d
dtype: object

如果slice的开始label和结束label不在Pandas对象的label中, 会发生什么?

这取决于Pandas对象的Label是否已经按顺序排列.

  1. 如果label是排序的, 那么会取最靠近开始label和结束label(假设slice的开始和结束label都不存在于labels中)
  2. 如果label是无序的, 那么会抛出异常.
# s1的index labels 无序, 因此使用 .loc[1:6]会抛出异常.
s1.loc[1:6]
# s1.sort_index() 之后得到的Series对象是有序的, 使用 .loc[1:6] 索引可以成功.
s1.sort_index().loc[1:6]
2    c
3    b
4    e
5    d
dtype: object

Selection by Position

Pandas提供了一组方法, 能够纯粹基于Integer进行索引. 该索引的语法与Python以及Numpy的slicing 很相近: 0-based 索引, 当slice操作时, 结果包含了开始position, 不包括结束position. 使用一个非整数数值索引, 甚至是valid label时, 都会抛出异常.

.iloc 方法是Selection by Position的Primary方法, 以下是该方法的有效输入.

  1. 一个整数, 比如 5
  2. A list or array of integers [4, 3, 0]
  3. Slice 对象
  4. Boolean array
  5. Callable

和 .loc 属性方法的有效输入挺相似的

df2 = pd.DataFrame(np.random.randn(6,4), index=[1,2,3,4,5,6], columns=list("ABCD"))
df2
# iloc 传入slice object
df2.iloc[0:3, 1:3]
# iloc 传入Integer, 此时会根据position选择第3行, 而不是根据Index Label选择第2行
df2.iloc[2,:]
A   -1.445161
B   -1.066899
C    0.703438
D   -0.389675
Name: 3, dtype: float64

Selection By Callable

.loc, iloc, [] 三种索引方式都可以传入callable 作为参数. Callable 必须是一个function, 该function接受一个参数, 这个参数就是要被索引的Pandas对象, 返回有效的输出作为index.

如果以 loc 索引为例, 则该function需要返回的值类型为下列4中之一:

  1. 单独的Label, 比如 'a'或者 5. 但是请注意, 单独的Label传入loc索引, 是对Row Label的索引. 以及5 在此处是 Index的Label, 并不代表 "第5个元素".

  2. List or Array of Labels. ['a', 'b', 'c']

  3. Slice Object. ['a':'c']. 注意, Pandas的Slice是开始和结束都包括在索引里的.

  4. A boolean Array

# Selectin by Callable, lambda 返回的结果是 Series of Boolean.
df2.loc[lambda df : df['A'] > 0]
# 上面的语句等价于以下索引方式
df2.loc[df2['A']>0]

callable 作为 index的参数, 既然callable总是要返回合法的索引方式, 为什么不直接就使用那些合理的索引方法?

Callable其实是为了chained selection准备的, 以减少对临时变量的使用. 官方文档的具体说法是

using callable indexers, you can chain data selection operations without using temporary variable

例子需要在后面补充

以上所有部分的参考文档是

Indexing and Selecting Data https://pandas.pydata.org/pandas-docs/stable/indexing.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容