[Python] pandas iloc vs ix vs loc解释,它们有什么不同?


Answers

注意:在熊猫版本0.20.0及以上版本中, deprecated使用ixiloc鼓励使用lociloc 我已经将描述ix完整的答案的部分留给早期版本熊猫的用户参考。 以下示例添加了ix替代方案

首先,这里回顾一下这三种方法:

  • loc从索引获取具有特定标签的行(或列)。
  • iloc在索引中的特定位置获取行(或列)(所以它只接受整数)。
  • ix通常会尝试像loc一样行为,但如果索引中不存在标签,则会退回到像iloc一样iloc

注意一些微妙之处可能会让ix稍微有点棘手:

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

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

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

>>> 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视为标签):

>>> 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.ix[:3]相同的系列,因为它首先查找标签,而不是在位置上工作(并且s的索引是整数类型)。

如果我们尝试使用不在索引中的整数标签(比如6 )呢?

这里s.iloc[:6]按照预期返回系列的前6行。 但是, s.loc[:6]会引发s.loc[:6]因为6不在索引中。

>>> 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 ,而不是引发iloc

>>> 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一样行为:

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

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

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

有时给一个DataFrame,你会想混合行和列的标签和位置索引方法。

例如,考虑下面的DataFrame。 如何最好地将行切成“c” 取前四列?

>>> 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

在早期版本的熊猫中(在0.20.0之前), ix可以让你非常整洁地做到这一点 - 我们可以按照标签划分行,按位划分列(注意对于列, ix默认为基于位置的分片,因为4不是列名称):

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

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

>>> 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切片iloc包含其端点,因此如果我们也希望行'c',则必须将此值加1。

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

Question

有人可以解释这三种切片方法是如何不同的?
我看过这些文档 ,并且看到了these answers ,但我仍然发现自己无法解释三者如何不同。 对我来说,它们似乎可以互换,因为它们处于较低层次的切片。

例如,假设我们想获取DataFrame的前五行。 这三项工作如何?

df.loc[:5]
df.ix[:5]
df.iloc[:5]

有人可以提出三种使用区分更清楚的情况吗?




在我看来,接受的答案令人困惑,因为它只使用缺少值的DataFrame。 我也不喜欢.iloc 基于位置的术语,而是更喜欢整数位置,因为它更具描述性,并且正是.iloc代表的意思。 关键词是INTEGER - .iloc需要INTEGERS。

.ix被弃用且不明确,不应使用

由于.ix已被弃用,我们将只关注.loc.iloc之间的差异。

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

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'])

所有粗体字都是标签。 标签, agecolorfoodheightscorestate用于 。 其他品牌, JaneNickAaronPenelopeDeanChristinaCornelia被用于指数

在DataFrame中选择特定行的主要方式是使用.loc.iloc索引器。 这些索引器中的每一个也可以用来同时选择列,但现在更容易集中在行上。 此外,每个索引器都使用一组紧跟其名称进行选择的括号。

.loc只能通过标签选择数据

我们将首先讨论仅通过索引或列标签选择数据的.loc索引器。 在我们的示例DataFrame中,我们提供了有意义的名称作为索引的值。 许多DataFrame不会有任何有意义的名称,而是默认为0到n-1的整数,其中n是DataFrame的长度。

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

  • 一个字符串
  • 字符串列表
  • 切片符号使用字符串作为开始和停止值

用字符串选择带有.loc的单个行

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

df.loc['Penelope']

这将返回一行数据作为一个Series

age           4
color     white
food      Apple
height       80
score       3.3
state        AL
Name: Penelope, dtype: object

用字符串列表选择带有.loc的多行

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

这将按照列表中指定的顺序返回一个DataFrame:

用切片符号选择带有.loc的多行

切片符号由开始,停止和步骤值定义。 按标签切片时,pandas在返回中包含停止值。 以下片段从Aaron到Dean,包含在内。 其步长未明确定义,但默认为1。

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

复杂切片可以采用与Python列表相同的方式。

.iloc仅通过整数位置选择数据

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

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

  • 一个整数
  • 整数列表
  • 切片符号使用整数作为开始和停止值

用整数选择带.iloc的单个行

df.iloc[4]

这将返回第5行(整数位置4)作为Series

age           32
color       gray
food      Cheese
height       180
score        1.8
state         AK
Name: Dean, dtype: object

用整数列表选择带有.iloc的多行

df.iloc[[2, -2]]

这将返回第三行和第二行到最后一行的DataFrame:

用切片符号选择带有.iloc的多行

df.iloc[:5:3]

使用.loc和.iloc同时选择行和列

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

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

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

这使用行的列表标签和列的切片标记

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

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

同时选择标签和整数位置

.ix被用来与标签和整数位置同时进行选择,这有时候很有用,但有时会令人困惑和模棱两可,并且幸好它已被弃用。 如果您需要使用标签和整数位置混合进行选择,则必须同时制作选择标签或整数位置。

例如,如果我们想要选择NickCornelia以及第2列和第4列,我们可以通过将整数转换为标签来使用.loc ,如下所示:

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

或者,也可以使用get_loc索引方法将索引标签转换为整数。

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

布尔选择

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

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

您可以使用.iloc复制此内容,但不能将其传递为布尔序列。 您必须将布尔序列转换为如下所示的numpy数组:

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

选择所有行

只有列选择可以使用.loc/.iloc 。 您可以通过使用像这样的冒号来选择所有行:

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

索引操作符[]可以切片也可以同时选择行和列。

大多数人都熟悉DataFrame索引操作符的主要用途,即选择列。 一个字符串选择一个列作为一个Series,一个字符串列表将多个列选择为一个DataFrame。

df['food']

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

使用列表可以选择多个列

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

不太熟悉的是,当使用切片符号时,则通过行标签或整数位置进行选择。 这非常令人困惑,而且我几乎从不使用它,但它确实有效。

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

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

用于选择行的.loc/.iloc的明确性是高度优选的。 索引操作符本身无法同时选择行和列。

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



iloc基于整数定位工作。 因此,无论您的行标签是什么,您都可以始终如一地完成第一行

df.iloc[0]

或者最后五行

df.iloc[-5:]

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

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

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

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

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

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

然后我们可以得到第一行

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

'date'列的后两行

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

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

此外,您可以通过使用数据框的__getitem__来执行列检索:

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

现在假设你想混合使用位置和命名索引,即使用行上的名称和列上的位置进行索引(为了说明,我的意思是从我们的数据框中选择,而不是在行索引中使用字符串创建数据框,列索引)。 这是.ix进入的地方:

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

编辑:我认为这也值得一提,你也可以传递布尔向量到loc方法。 例如:

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

将返回df的第1和第3行。 这相当于用于选择的df[b] ,但它也可以用于通过布尔矢量进行赋值:

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