iloc和loc有什么不同之处?

936

有人能解释一下这两种切片方法有什么不同吗?
我看过文档,还看过这些答案,但我仍然无法理解它们三者之间的差异。对我来说,它们在很大程度上似乎是可互换的,因为它们都在切片的较低级别上。

例如,假设我们想获取DataFrame的前五行。这两种方法如何工作?

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

有人能否提供三个区分使用情况更清晰的案例?


从前,我也想知道这两个函数与 df.ix[:5] 的区别,但是从 pandas 1.0 开始就已经删除了 ix,所以我不再关心了。


12
需要提到SettingWithCopyWarning警告的情况:https://dev59.com/8WIj5IYBdhLWcg3wHhlX 和 https://dev59.com/NWAg5IYBdhLWcg3wWp7- - Paul
12
请注意,ix现已计划停用:https://github.com/pandas-dev/pandas/issues/14218 - JohnE
7个回答

1515

标签 vs. 位置

这两种方法之间的主要区别在于:

  • loc 获取具有特定标签的行(和/或列)。

  • iloc 按整数位置获取行(和/或列)。

为了说明,考虑一个字符序列 s,它具有非单调整数索引:

>>> s = pd.Series(list("abcdef"), index=[49, 48, 47, 0, 1, 2]) 
49    a
48    b
47    c
0     d
1     e
2     f

>>> s.loc[0]    # value at index label 0
'd'

>>> s.iloc[0]   # value at index location 0
'a'

>>> s.loc[0:1]  # rows at index labels between 0 and 1 (inclusive)
0    d
1    e

>>> s.iloc[0:1] # rows at index location between 0 and 1 (exclusive)
49    a
以下是在传递不同对象时s.locs.iloc之间的一些区别/相似之处:

<object>描述s.loc[<object>]s.iloc[<object>]
0单个项目位于索引标签 0 (字符串'd')处的值位于位置 0 的值(字符串'a'
0:1切片两行(标签01一行(位置为 0 的第一行)
1:47带越界结尾的片段零行(空系列)五行(从位置 1 开始)
1:47:-1带有负步长的切片三行(标签从147零行(空系列)
[2, 0]整数列表两行,带有给定标签两行,带有给定位置
s > 'e'布尔序列(指示哪些值具有此属性)一行(包含'f'NotImplementedError
(s>'e').values布尔数组一行(包含'f'loc相同
999不在索引中的int对象KeyErrorIndexError(越界)
-1不在索引中的int对象KeyError返回s中的最后一个值
lambda x: x.index[3]应用于系列的可调用项(在此处返回索引中的第3个项目)s.loc[s.index[3]]s.iloc[s.index[3]]

loc的标签查询功能远不止于整数索引,值得强调一些额外的例子。

这是一个索引包含字符串对象的系列:

>>> s2 = pd.Series(s.index, index=s.values)
>>> s2
a    49
b    48
c    47
d     0
e     1
f     2

由于 loc 是基于标签的,因此可以使用 s2.loc['a'] 从序列中获取第一个值。它还可以用非整数对象进行切片。

>>> s2.loc['c':'e']  # all rows lying between 'c' and 'e' (inclusive)
c    47
d     0
e     1

对于DateTime索引,我们不需要传递确切的日期/时间即可通过标签获取。例如:

>>> s3 = pd.Series(list('abcde'), pd.date_range('now', periods=5, freq='M')) 
>>> s3
2021-01-31 16:41:31.879768    a
2021-02-28 16:41:31.879768    b
2021-03-31 16:41:31.879768    c
2021-04-30 16:41:31.879768    d
2021-05-31 16:41:31.879768    e

然后,要获取2021年3月/4月的行,我们只需要:

>>> s3.loc['2021-03':'2021-04']
2021-03-31 17:04:30.742316    c
2021-04-30 17:04:30.742316    d

行和列

lociloc在DataFrame中的使用方式与它们在Series中的使用方式相同。值得注意的是,这两种方法都可以同时操作列和行。

当给定一个元组时,第一个元素用于索引行,如果存在第二个元素,则用于索引列。

考虑下面定义的DataFrame:

>>> import numpy as np 
>>> df = pd.DataFrame(np.arange(25).reshape(5, 5),  
                      index=list('abcde'), 
                      columns=['x','y','z', 8, 9])
>>> df
    x   y   z   8   9
a   0   1   2   3   4
b   5   6   7   8   9
c  10  11  12  13  14
d  15  16  17  18  19
e  20  21  22  23  24

例如:

>>> df.loc['c': , :'z']  # rows 'c' and onwards AND columns up to 'z'
    x   y   z
c  10  11  12
d  15  16  17
e  20  21  22

>>> df.iloc[:, 3]        # all rows, but only the column at index location 3
a     3
b     8
c    13
d    18
e    23

有时我们希望混合使用标签和位置索引方法来处理行和列,以某种方式结合lociloc的功能。

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

>>> import numpy as np 
>>> df = pd.DataFrame(np.arange(25).reshape(5, 5),  
                      index=list('abcde'), 
                      columns=['x','y','z', 8, 9])
>>> df
    x   y   z   8   9
a   0   1   2   3   4
b   5   6   7   8   9
c  10  11  12  13  14
d  15  16  17  18  19
e  20  21  22  23  24

我们可以使用iloc方法和另一个方法的帮助来实现这个结果:

>>> df.iloc[:df.index.get_loc('c') + 1, :4]
    x   y   z   8
a   0   1   2   3
b   5   6   7   8
c  10  11  12  13

get_loc()是一个索引方法,意思是“获取此索引中标签的位置”。请注意,由于使用iloc进行切片时不包括其终点,因此如果我们想要行'c',则必须将此值加1。


15
很好的解释!我一直有一个相关的问题,那就是loc、iloc和ix与“SettingWithCopy”警告有什么关系(如果有的话)?有一些文档,但说实话我还是有点困惑。http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy - measureallthethings
5
如果使用链式调用 locilocix,仍然可能触发警告。在链接文档中的示例数据帧上执行 dfmi.loc[:, 'one'].loc[:, 'second'] 就像 dfmi['one']['second'] 一样会触发警告,因为第一个索引操作可能返回数据的副本(而不是视图)。请注意,这里的警告是指可能会影响代码性能和正确性的潜在问题。 - Alex Riley
1
如果您想使用日期查找DateIndex,或者类似于df.ix[date, 'Cash']的内容,您会使用什么? - cjm2671
1
@cjm2671:在这种情况下,locix都应该可以使用。例如,df.loc['2016-04-29', 'Cash']将返回所有具有该特定日期的行索引从'Cash'列中。(在检索带有字符串的索引时,您可以尽可能具体,例如,'2016-01'将选择所有在2016年1月份的日期时间,'2016-01-02 11'将选择2016年1月2日时间为11:??:??的日期时间。) - Alex Riley
1
谢谢!解释得很清楚!一个建议:如果现在已经弃用了,也许把所有与“ix”相关的内容移到答案的一个单独部分可能更有意义? - Antony Hatchkins
显示剩余4条评论

171

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开始的整数,在这种情况下,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'

1
df.iloc[:, :] 等同于所有行和列吗? - Ali
2
它就像df.loc[:, :]一样。它可以用于重新分配整个DataFrame的值或创建其视图。 - JoeCondron
1
你好,你知道为什么 loc 和 iloc 的参数要放在方括号 [ ] 中而不是像普通方法一样放在圆括号 ( ) 中吗? - Marine Galantin
@MarineGalantin 因为它们表示的是索引和切片操作,而不是标准方法。你正在选择数据子集。 - eric
df.loc['a']和df['a']之间有什么区别? - skan
1
如果我将'a'视为列,则 df['a'] 将返回整个列的值,而 df.loc['a'] 将抛出错误,因为您必须提供行标签作为第一个值。 如果我将'a'视为一行,则 df['a'] 将会抛出错误,并且 df.loc['a'] 将返回整行的值。 - Srinivas

164

我认为,被接受的答案很令人困惑,因为它使用了只有缺失值的DataFrame。我也不喜欢用基于位置来表示.iloc,而更喜欢整数位置,因为这更具描述性,也正是.iloc的精确含义。关键词是INTEGER-.iloc需要整数。

关于子集选择的更多信息,请参见我极其详细的博客系列


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

enter image description here

加粗字体代表标签,即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:

enter image description here

使用.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,并只选取height、score和state这三列,如下所示:

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数组:

您可以使用.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

enter image description here

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

输入图像描述

为选择行推荐使用.loc/.iloc,因为单独使用索引运算符无法同时选择行和列。

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

11
这是我所遇到的编程主题中表达清晰易懂的解释之一,您在最后所讲的有关正常索引作用于行或列的内容,也是我们拥有loc和iloc方法的原因之一。我在DataCamp课程中遇到了这个警告。a.) df.columns和df.index返回什么?它是字符串列表吗?如果是一个列表,是否允许像df.columns[[2,4]]这样访问两个元素?b.) 我可以在df.columns上调用get_loc()吗? c.) 为什么在iloc的情况下需要调用df['age']>30.values? - pragun
1
这是一个非常好的答案,我喜欢它没有深入探讨已经过时且毫无意义的ix。谢谢。 - omabena
为什么他们使用 loc 而不是 label?这种命名方式似乎只会让人感到困惑。 - eric
df.loc['a'] 和 df['a'] 有什么区别? - skan
很棒的回答。你只链接了Pandas数据选择子集系列的第一部分。其他部分呢?我在第一部分找不到链接到下一部分的地方。 - Evan Aad

4
这个例子将说明区别:

df = pd.DataFrame({'col1': [1,2,3,4,5], 'col2': ["foo", "bar", "baz", "foobar", "foobaz"]})
  col1  col2
0   1   foo
1   2   bar
2   3   baz
3   4   foobar
4   5   foobaz

df = df.sort_values('col1', ascending = False)
      col1  col2
    4   5   foobaz
    3   4   foobar
    2   3   baz
    1   2   bar
    0   1   foo

索引访问:

df.iloc[0, 0:2]
col1         5
col2    foobaz
Name: 4, dtype: object

我们获取已排序数据框的第一行。(这不是索引为0的行,而是索引为4的行)。 基于位置的访问:
df.loc[0, 'col1':'col2']
col1      1
col2    foo
Name: 0, dtype: object

即使df已经排序,我们仍然会获取索引为0的行。

3

.loc.iloc用于索引,即提取数据的部分。实质上,它们的区别在于.loc允许基于标签进行索引,而.iloc允许基于位置进行索引。

如果你对.loc.iloc感到困惑,请记住.iloc是基于索引(从i开始)位置,而.loc是基于标签(从l开始)。

.loc

.loc应该基于索引标签而不是位置,因此类似于基于Python字典的索引。然而,它可以接受布尔数组、切片和标签列表(这些都不能与Python字典一起使用)。

iloc

.iloc根据索引位置进行查找,即pandas类似于Python列表。如果没有该位置的索引,则pandas将引发IndexError

示例

以下示例旨在说明.iloc.loc之间的差异。让我们考虑以下系列:

>>> s = pd.Series([11, 9], index=["1990", "1993"], name="Magic Numbers")
>>> s
1990    11
1993     9
Name: Magic Numbers , dtype: int64

.iloc 示例

>>> s.iloc[0]
11
>>> s.iloc[-1]
9
>>> s.iloc[4]
Traceback (most recent call last):
    ...
IndexError: single positional indexer is out-of-bounds
>>> s.iloc[0:3] # slice
1990 11
1993  9
Name: Magic Numbers , dtype: int64
>>> s.iloc[[0,1]] # list
1990 11
1993  9
Name: Magic Numbers , dtype: int64

.loc 示例

>>> s.loc['1990']
11
>>> s.loc['1970']
Traceback (most recent call last):
    ...
KeyError: ’the label [1970] is not in the [index]’
>>> mask = s > 9
>>> s.loc[mask]
1990 11
Name: Magic Numbers , dtype: int64
>>> s.loc['1990':] # slice
1990    11
1993     9
Name: Magic Numbers, dtype: int64

因为s具有字符串索引值,所以使用整数进行索引时,.loc会失败:

>>> s.loc[0]
Traceback (most recent call last):
    ...
KeyError: 0

1
  • DataFrame.loc():通过索引值选择行
  • DataFrame.iloc():通过行号选择行

示例:

选择表格的前5行,df1是您的数据框

df1.iloc[:5]

从表格中选择第一个A、B行,df1是您的数据框。

df1.loc['A','B']

0
所有的答案都在谈论查询数据框时lociloc之间的区别。另一个区别是loc可以扩大一个Series/数据框,而iloc不能。换句话说,当涉及到在数据框中分配/修改值时,loc可以给一个全新的行分配值(同时改变已有的值),而iloc只能改变数据框中已有的值。
在下面的代码中,使用loc,我们能够向数据框添加一行新数据;但是使用iloc则无法做到同样的操作。
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']})

df.loc[3] = [4, 'd']     # <--- OK (now `df` has 4 rows)

df.iloc[4] = [4, 'd']    # <--- error

对于姐妹方法atiat,同样的逻辑也适用。
df.at[4, 'A'] = 4        # <--- OK
df.iat[5, 0] = 5         # <--- error

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接