关于python:iloc,ix和loc如何不同?

How are iloc, ix and loc different?

有人能解释一下这三种切片方法有什么不同吗?我看过医生,我已经看到了这些答案,但我仍然无法解释这三个答案的不同之处。在我看来,它们在很大程度上是可以互换的,因为它们处于较低的切片级别。

例如,假设我们想要得到一个DataFrame的前五行。这三个都是怎么工作的?

1
2
3
df.loc[:5]
df.ix[:5]
df.iloc[:5]

有人能提出三种使用上的区别更清楚的情况吗?


注:熊猫0.20.0及以上版本不推荐使用ix,鼓励使用lociloc。我留下了这个答案中描述ix的部分,作为熊猫早期版本用户的参考。下面添加了一些示例,说明了ix的替代方案。

首先,这里简要介绍三种方法:

  • loc从索引中获取带有特定标签的行(或列)。
  • iloc在索引中的特定位置获取行(或列)(因此它只接受整数)。
  • ix通常试图表现得像loc,但如果索引中没有标签,则会回到iloc的表现。

重要的是要注意一些细微之处,这些细微之处会使ix的使用变得有些困难:

  • 如果索引是整数类型,那么ix将只使用基于标签的索引,而不返回基于位置的索引。如果标签不在索引中,则会引发错误。

  • 如果索引不只包含整数,那么给定一个整数,ix将立即使用基于位置的索引,而不是基于标签的索引。但是,如果ix被赋予另一种类型(例如字符串),它可以使用基于标签的索引。

为了说明这三种方法之间的差异,请考虑以下系列:

1
2
3
4
5
6
7
8
9
10
11
12
>>> s = pd.Series(np.nan, index=[49,48,47,46,45, 1, 2, 3, 4, 5])
>>> s
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN
4    NaN
5    NaN

我们来看看整型值3的切片。

在这种情况下,s.iloc[:3]返回前3行(因为它将3视为位置),s.loc[:3]返回前8行(因为它将3视为标签):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> s.iloc[:3] # slice the first three rows
49   NaN
48   NaN
47   NaN

>>> s.loc[:3] # slice up to and including label 3
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN

>>> s.ix[:3] # the integer is in the index so s.ix[:3] works like loc
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN
2    NaN
3    NaN

注意,s.ix[:3]返回与s.loc[:3]相同的序列,因为它首先查找标签,而不是处理位置(s的索引为整数类型)。

如果我们尝试使用一个不在索引中的整数标签(比如6),会怎么样?

这里,s.iloc[:6]按预期返回序列的前6行。但是,由于6不在索引中,因此s.loc[:6]会引发一个键错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> s.iloc[:6]
49   NaN
48   NaN
47   NaN
46   NaN
45   NaN
1    NaN

>>> s.loc[:6]
KeyError: 6

>>> s.ix[:6]
KeyError: 6

根据上面提到的微妙之处,s.ix[:6]现在提出了一个keyerror,因为它试图像loc那样工作,但在索引中找不到6。因为我们的索引是整数类型,所以ix不会像iloc那样返回到行为。

但是,如果我们的索引是混合类型的,如果给定一个整数ix,那么它的行为将类似于iloc,而不是引发keyerror:

1
2
3
4
5
6
7
8
9
10
>>> s2 = pd.Series(np.nan, index=['a','b','c','d','e', 1, 2, 3, 4, 5])
>>> s2.index.is_mixed() # index is mix of different types
True
>>> s2.ix[:6] # now behaves like iloc given integer
a   NaN
b   NaN
c   NaN
d   NaN
e   NaN
1   NaN

记住,ix仍然可以接受非整数,其行为与loc类似:

1
2
3
4
>>> s2.ix[:'c'] # behaves like loc given non-integer
a   NaN
b   NaN
c   NaN

作为一般建议,如果您只使用标签进行索引,或者只使用整数位置进行索引,请使用lociloc以避免意外结果-请尝试不使用ix

结合基于位置和基于标签的索引

有时,如果给定了一个数据帧,您将希望为行和列混合使用标签和位置索引方法。

例如,考虑以下数据帧。如何最好地将行切片到"c"并包括"c",并取前四列?

1
2
3
4
5
6
7
8
9
10
>>> df = pd.DataFrame(np.nan,
                      index=list('abcde'),
                      columns=['x','y','z', 8, 9])
>>> df
    x   y   z   8   9
a NaN NaN NaN NaN NaN
b NaN NaN NaN NaN NaN
c NaN NaN NaN NaN NaN
d NaN NaN NaN NaN NaN
e NaN NaN NaN NaN NaN

在早期版本的pandas(0.20.0之前)中,ix让您可以非常灵活地执行此操作—我们可以按标签对行进行切片,按位置对列进行切片(注意,对于列,ix将默认为基于位置的切片,因为4不是列名称):

1
2
3
4
5
>>> df.ix[:'c', :4]
    x   y   z   8
a NaN NaN NaN NaN
b NaN NaN NaN NaN
c NaN NaN NaN NaN

在以后的熊猫版本中,我们可以使用iloc和另一种方法来实现这一结果:

1
2
3
4
5
>>> df.iloc[:df.index.get_loc('c') + 1, :4]
    x   y   z   8
a NaN NaN NaN NaN
b NaN NaN NaN NaN
c NaN NaN NaN NaN

get_loc()是一种索引方法,意思是"获取标签在该索引中的位置"。请注意,由于使用iloc进行切片不包括其端点,因此如果希望行"c",则必须向该值添加1。

在熊猫的文档中还有更多的例子。


iloc基于整数定位。因此,无论您的行标签是什么,您都可以,例如,通过执行以下操作获取第一行

1
df.iloc[0]

或者做最后五行

1
df.iloc[-5:]

您也可以在列中使用它。这将检索第3列:

1
df.iloc[:, 2]    # the : in the first position indicates all rows

您可以组合它们以获得行和列的交叉点:

1
df.iloc[:3, :3] # The upper-left 3 X 3 entries (assuming df has 3+ rows and columns)

另一方面,.loc使用命名索引。让我们用字符串作为行和列标签来设置数据帧:

1
df = pd.DataFrame(index=['a', 'b', 'c'], columns=['time', 'date', 'name'])

那我们就可以坐第一排了

1
df.loc['a']     # equivalent to df.iloc[0]

和第二行的'date'

1
df.loc['b':, 'date']   # equivalent to df.iloc[1:, 1]

等等。现在,有必要指出,DataFrame的默认行和列索引是0的整数,在这种情况下,ilocloc的工作方式相同。这就是为什么你的三个例子是等价的。如果有字符串或日期时间等非数字索引,df.loc[:5]将引发错误。

另外,只需使用数据帧的__getitem__即可进行列检索:

1
df['time']    # equivalent to df.loc[:, 'time']

现在假设您想混合使用位置和命名索引,也就是说,使用行上的名称和列上的位置进行索引(为了澄清,我的意思是从数据帧中选择,而不是创建一个数据帧,其中行索引中包含字符串,列索引中包含整数)。这就是.ix的由来:

1
df.ix[:2, 'time']    # the first two rows of the 'time' column

我认为也值得一提的是,您可以将布尔向量传递给loc方法。例如:

1
2
 b = [True, False, True]
 df.loc[b]

将返回df的第1行和第3行。这相当于用于选择的df[b],但也可用于通过布尔向量分配:

1
df.loc[b, 'name'] = 'Mary', 'John'


在我看来,接受的答案令人困惑,因为它使用的是一个只缺少值的数据帧。我也不喜欢基于.iloc的术语position,相反,我更喜欢整数位置,因为它更具描述性,更确切地说是.iloc的意思。关键字是整数-.iloc需要整数。好的。

有关子集选择的详细信息,请参阅我的博客系列。好的。.ix已弃用且不明确,不应使用

由于.ix被否决,我们只关注.loc.iloc之间的差异。好的。

在我们讨论差异之前,重要的是要理解数据帧具有帮助识别每个列和每个索引的标签。让我们来看一个示例数据框:好的。

1
2
3
4
5
6
7
8
df = pd.DataFrame({'age':[30, 2, 12, 4, 32, 33, 69],
                   'color':['blue', 'green', 'red', 'white', 'gray', 'black', 'red'],
                   'food':['Steak', 'Lamb', 'Mango', 'Apple', 'Cheese', 'Melon', 'Beans'],
                   'height':[165, 70, 120, 80, 180, 172, 150],
                   'score':[4.6, 8.3, 9.0, 3.3, 1.8, 9.5, 2.2],
                   'state':['NY', 'TX', 'FL', 'AL', 'AK', 'TX', 'TX']
                   },
                  index=['Jane', 'Nick', 'Aaron', 'Penelope', 'Dean', 'Christina', 'Cornelia'])

enter image description here。好的。

粗体字都是标签。列使用标签:agecolorfoodheightscorestate。其他标签:JaneNickAaronPenelopeDeanChristinaCornelia用于索引。好的。

选择数据帧中特定行的主要方法是使用.loc.iloc索引器。这些索引器中的每一个都可以用于同时选择列,但现在只关注行更容易。另外,每个索引器使用一组紧跟在其名称后面的括号进行选择。好的。.loc仅按标签选择数据

我们将首先讨论.loc索引器,它只根据索引或列标签选择数据。在我们的示例数据框架中,我们提供了有意义的名称作为索引的值。许多数据帧将没有任何有意义的名称,而是默认为0到n-1之间的整数,其中n是数据帧的长度。好的。

有三种不同的输入可用于.loc。好的。

  • 一根绳子
  • 字符串列表
  • 以字符串作为起始值和终止值的切片表示法

选择带有.loc和字符串的单行好的。

要选择一行数据,请将索引标签放在.loc后面的括号内。好的。

1
df.loc['Penelope']

这将以序列形式返回数据行好的。

1
2
3
4
5
6
7
age           4
color     white
food      Apple
height       80
score       3.3
state        AL
Name: Penelope, dtype: object

使用.loc和字符串列表选择多行好的。

1
df.loc[['Cornelia', 'Jane', 'Dean']]

这将返回一个数据帧,其中的行按列表中指定的顺序排列:好的。

氧化镁好的。

用.loc和slice符号选择多行好的。

切片表示法由开始、停止和步骤值定义。当按标签切片时,pandas在返回中包含stop值。以下是从Aaron到Dean的片段。其步骤大小未明确定义,但默认为1。好的。

1
df.loc['Aaron':'Dean']

氧化镁好的。

复杂切片可以采用与Python列表相同的方式。好的。.iloc仅按整数位置选择数据

现在我们来看看.iloc。数据帧中的每一行和每一列数据都有一个定义它的整数位置。这是对输出中显示的标签的补充。整数位置只是从顶部/左侧从0开始的行/列数。好的。

有三种不同的输入可用于.iloc。好的。

  • 整数
  • 整数列表
  • 以整数作为起始值和终止值的切片表示法

用带整数的.iloc选择一行好的。

1
df.iloc[4]

这将返回第5行(整数位置4)作为一个序列好的。

1
2
3
4
5
6
7
age           32
color       gray
food      Cheese
height       180
score        1.8
state         AK
Name: Dean, dtype: object

用.iloc和整数列表选择多行好的。

1
df.iloc[[2, -2]]

这将返回第三行和第二行到最后一行的数据帧:好的。

enter image description here。好的。

使用.iloc和slice符号选择多行好的。

1
df.iloc[:5:3]

氧化镁好的。使用.loc和.iloc同时选择行和列

两个.loc/.iloc的一个极好的能力是它们同时选择行和列的能力。在上面的示例中,所有列都是从每个选择中返回的。我们可以选择输入类型与行相同的列。我们只需要用逗号分隔行和列的选择。好的。

例如,我们可以选择行Jane和Dean,只选择列高度、分数和状态,如下所示:好的。

1
df.loc[['Jane', 'Dean'], 'height':]

氧化镁好的。

这将使用行的标签列表和列的切片表示法好的。

我们可以自然地用.iloc只使用整数进行类似的操作。好的。

1
2
3
4
df.iloc[[1,4], 2]
Nick      Lamb
Dean    Cheese
Name: food, dtype: object

。同时选择标签和整数位置

.ix用于同时选择标签和整数位置,这是有用的,但有时会混淆和含糊不清,值得庆幸的是,它已被弃用。如果需要混合使用标签和整数位置进行选择,则必须同时选择标签或整数位置。好的。

例如,如果我们要选择行NickCornelia以及列2和4,我们可以使用.loc,将整数转换为标签,如下所示:好的。

1
2
col_names = df.columns[[2, 4]]
df.loc[['Nick', 'Cornelia'], col_names]

或者,使用get_locindex方法将索引标签转换为整数。好的。

1
2
3
labels = ['Nick', 'Cornelia']
index_ints = [df.index.get_loc(label) for label in labels]
df.iloc[index_ints, [2, 4]]

布尔选择

.loc索引器也可以进行布尔选择。例如,如果我们有兴趣查找年龄在30岁以上的所有行,只返回foodscore列,我们可以执行以下操作:好的。

1
df.loc[df['age'] > 30, ['food', 'score']]

您可以用.iloc复制这个,但不能传递一个布尔序列。必须将布尔序列转换为numpy数组,如下所示:好的。

1
df.iloc[(df['age'] > 30).values, [2, 4]]

选择所有行

可以使用.loc/.iloc只选择列。您可以使用如下冒号选择所有行:好的。

1
df.loc[:, 'color':'score':2]

氧化镁好的。索引操作符[]也可以选择行和列,但不能同时选择。

大多数人都熟悉数据帧索引操作符的主要用途,即选择列。字符串选择单个列作为序列,字符串列表选择多个列作为数据帧。好的。

1
2
3
4
5
6
7
8
9
10
df['food']

Jane          Steak
Nick           Lamb
Aaron         Mango
Penelope      Apple
Dean         Cheese
Christina     Melon
Cornelia      Beans
Name: food, dtype: object

使用列表可选择多列好的。

1
df[['food', 'score']]

enter image description here。好的。

人们不太熟悉的是,当使用切片表示法时,选择是通过行标签或整数位置进行的。这是非常混乱的,我几乎从未使用过,但它确实有效。好的。

1
df['Penelope':'Christina'] # slice rows by label

氧化镁好的。

1
df[2:6:2] # slice rows by integer location

氧化镁好的。

选择行时最好使用.loc/.iloc的明确性。仅索引运算符不能同时选择行和列。好的。

1
2
df[3:5, 'color']
TypeError: unhashable type: 'slice'

好啊。