iloc和loc有什么不同?

有人能解释一下这两种切片方法有何不同吗?
我看过的文档我已经看过这些答案,但我仍然发现自己无法理解这三者有何不同。对我来说,它们在很大程度上似乎是可以互换的,因为它们处于较低的切片级别。

例如,假设我们想要获得DataFrame的前五行。这两个是如何工作的?

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

有人能提出三个使用区分更清楚的案例吗?


曾几何时,我也想知道这两个函数与df.ix[:5]有什么不同,但是ix已经从熊猫1.0中删除了,所以我不再关心了。

705129 次浏览

标签vs.位置

这两种方法的主要区别是:

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

  • iloc获取整数地点处的行(和/或列)。

为了演示,请考虑具有非单调整数索引的一系列s字符:

>>> s = pd.Series(list("abcdef"), index=[49, 48, 47, 0, 1, 2])49    a48    b47    c0     d1     e2     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    d1    e
>>> s.iloc[0:1] # rows at index location between 0 and 1 (exclusive)49    a

以下是s.locs.iloc在传递各种对象时的一些差异/相似之处:

<对象>字段说明s.loc[<object>]s.iloc[<object>]
0单一项目索引标签0处的值(字符串'd'索引位置 0处的值(字符串'a'
0:1切片两个行(标签01一个行(位置0的第一行)
1:47带有越界端的切片行(空系列)行(位置1起)
1:47:-1负阶跃切片行(标签1回到47行(空系列)
[2, 0]整数列表两个行给定标签两个具有给定位置的行
s > 'e'布尔序列(指示哪些值具有该属性)一个行(包含'f'NotImplementedError
(s>'e').values布尔数组一个行(包含'f'loc相同
999int对象不在索引中KeyErrorIndexError(出界)
-1int对象不在索引中KeyError返回s中的最后一个值
lambda x: x.index[3]可调用的应用于系列(这里返回索引中的3rd项)s.loc[s.index[3]]s.iloc[s.index[3]]

loc的标签查询功能远远超出了整数索引,值得强调几个额外的示例。

这是一个Series,其中索引包含字符串对象:

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

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

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

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

>>> s3 = pd.Series(list('abcde'), pd.date_range('now', periods=5, freq='M'))>>> s32021-01-31 16:41:31.879768    a2021-02-28 16:41:31.879768    b2021-03-31 16:41:31.879768    c2021-04-30 16:41:31.879768    d2021-05-31 16:41:31.879768    e

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

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

行和列

lociloc处理DataFrames的方式与处理Series的方式相同。值得注意的是,这两种方法都可以同时处理列和行。

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

考虑下面定义的DataFrame:

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

然后举个例子:

>>> df.loc['c': , :'z']  # rows 'c' and onwards AND columns up to 'z'x   y   zc  10  11  12d  15  16  17e  20  21  22
>>> df.iloc[:, 3]        # all rows, but only the column at index location 3a     3b     8c    13d    18e    23

有时我们想混合行和列的标签和位置索引方法,以某种方式结合lociloc的功能。

例如,考虑以下DataFrame。如何最好地将行切片到并包括'c'在前四列?

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

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

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

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

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

现在假设您想混合位置索引和命名索引,即在行上使用名称和在列上使用位置索引(为了澄清,我的意思是从我们的数据框中选择,而不是创建一个数据框,在行索引中使用字符串,在列索引中使用整数)。

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'

在我看来,公认的答案令人困惑,因为它使用了一个只有缺失值的DataFrame。我也不喜欢.iloc的术语基于位置,相反,更喜欢整数位置,因为它更具描述性,并且正是.iloc所代表的。关键词是INTEGER-.iloc需要INTEGERS。

有关更多信息,请参阅我关于子集选择的非常详细的博客系列


. ix已弃用且模棱两可,永远不应使用

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

在我们讨论差异之前,重要的是要了解DataFrames有标签来帮助识别每个列和每个索引。让我们看一个示例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'])

在此处输入图片描述

color3中的所有单词都是标签。标签agecolorfoodheightscorestate用于color4。其他标签JaneNickAaronPenelopecolor0、color1、color2用于color5。


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

. loc仅通过标签选择数据

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

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

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

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

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

df.loc['Penelope']

这将数据行作为Series返回

age           4color     whitefood      Appleheight       80score       3.3state        ALName: Penelope, dtype: object

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

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

这将返回一个DataFrame,其中包含按列表中指定的顺序排列的行:

在此处输入图片描述

使用. loc和切片表示法选择多行

切片符号由开始、停止和步长值定义。按标签切片时,熊猫在返回中包含停止值。以下从Aaron到Dean的切片,包括在内。它的步长没有明确定义,但默认为1。

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

在此处输入图片描述

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

. iloc仅按整数位置选择数据

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

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

  • 一个整数
  • 一个整数列表
  • 使用整数作为开始和停止值的切片表示法

选择带有. iloc和整数的单行

df.iloc[4]

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

age           32color       grayfood      Cheeseheight       180score        1.8state         AKName: 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      LambDean    CheeseName: food, dtype: object

带有标签和整数位置的同时选择

.ix用于同时使用标签和整数位置进行选择,这很有用,但有时令人困惑和模棱两可,幸运的是它已被弃用。如果您需要混合使用标签和整数位置进行选择,您将不得不同时选择标签或整数位置。

例如,如果我们想选择行NickCornelia以及列2和4,我们可以通过将整数转换为具有以下内容的标签来使用.loc

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

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

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索引运算符的主要目的,即选择列。字符串选择单个列作为系列,字符串列表选择多个列作为DataFrame。

df['food']
Jane          SteakNick           LambAaron         MangoPenelope      AppleDean         CheeseChristina     MelonCornelia      BeansName: 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'
  • DataFrame.loc():按索引值选择行
  • DataFrame.iloc():按行数选择行

示例:

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

df1.iloc[:5]

选择表的第一个A, B行,df1是您的数据框

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

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

如果您对.loc.iloc感到困惑,请记住.iloc基于索引(从开始)位置,而.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")>>> s1990    111993     9Name: 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] # slice1990 111993  9Name: Magic Numbers , dtype: int64>>> s.iloc[[0,1]] # list1990 111993  9Name: 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 11Name: Magic Numbers , dtype: int64>>> s.loc['1990':] # slice1990    111993     9Name: Magic Numbers, dtype: int64

因为s有字符串索引值,所以.loc在以下情况下会失败使用整数索引:

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

这个例子将说明区别:

df = pd.DataFrame({'col1': [1,2,3,4,5], 'col2': ["foo", "bar", "baz", "foobar", "foobaz"]})col1  col20   1   foo1   2   bar2   3   baz3   4   foobar4   5   foobaz
df = df.sort_values('col1', ascending = False)col1  col24   5   foobaz3   4   foobar2   3   baz1   2   bar0   1   foo

基于索引的访问:

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

我们得到排序数据帧的第一行。(这不是索引为0的行,而是索引为4的行)。

基于位置的访问:

df.loc[0, 'col1':'col2']col1      1col2    fooName: 0, dtype: object

我们得到索引为0的行,即使df已排序。