pandas的loc、iloc、at和iat有什么区别?

228

最近开始从我的舒适区(R)转向 Python,并对 Pandas 中的单元格定位/选择感到有些困惑。我已经阅读了文档,但仍然难以理解各种定位/选择选项的实际影响。

是否有理由在atiat.loc.iloc之间做出选择?在什么情况下应使用哪种方法?


注意:未来的读者请注意,这个问题是旧的,在 pandas v0.20 之前写的,当时存在一个名为 .ix 的函数。后来将此方法拆分为两个 - lociloc - 以明确标记和位置基础的索引之间的区别。请注意,由于不一致的行为和难以理解,ix 被停用,并且在当前版本的 Pandas(>= 1.0)中不存在。


2
请查看以下网址:https://dev59.com/j14c5IYBdhLWcg3wqb0C?rq=1 - cphlewis
4
loc 是基于标签的索引,可以在行中查找值, iloc 是基于整数位置的行索引,ix 是一个通用方法,它首先执行基于标签的索引,如果失败,则使用基于整数位置的索引。建议不要使用已弃用的 at 方法。还有一件事需要考虑是你想做什么,因为其中一些方法允许切片和列赋值,老实说文档很清楚:http://pandas.pydata.org/pandas-docs/stable/indexing.html - EdChum
2
@EdChum - 你为什么说at已经被弃用了?我在at(或者iat)文档中没有看到这个说明。 - Russ
1
那是一个错误,它并没有被弃用。我认为曾经有一些关于弃用它的讨论,但这个想法被放弃了,因为我认为它更快。 - EdChum
4
在此处详细解释了 locixiloc 之间的区别:https://dev59.com/31wZ5IYBdhLWcg3wVO5U#31593712。 - Alex Riley
1
请查看以下链接中非常详细的答案,其中包含DataFrame图片:https://dev59.com/tl4b5IYBdhLWcg3wtjzY#47098873 - Ted Petrou
6个回答

194

12
如果 atiatlociloc 的非常快速的版本,那么为什么还要使用 lociloc - Ray
78
atiat用于访问标量,也就是数据框中的单个元素,而lociloc用于同时访问多个元素,可能进行向量化操作。 - noe
2
.values[] is faster than .iat - Gere
1
@HighGPA .values 会返回一个普通的Numpy数组。所以,是的,你可以像使用Numpy数组一样访问多个元素。 - Gere
如果您在Pandas对象中有4个项目,那么您的索引可能是['a','b','plum','hotdog'],但是您的位置(ilociat使用的位置)将始终为[0, 1, 2, 3] - Ken Williams
显示剩余5条评论

131

鉴于 ix 已被弃用,因此针对 pandas 0.20 进行了更新。这不仅演示了如何使用 locilocatiatset_value,而且演示了如何实现混合位置/基于标签的索引。


loc - 基于标签
允许您将 1-D 数组作为索引器传递。数组可以是索引或列的切片(子集),也可以是与索引或列长度相等的布尔数组。

特别提示: 当传递标量索引器时,loc 可以分配一个新的索引或列值,该值以前不存在。

# label based, but we can use position values
# to get the labels from the index object
df.loc[df.index[2], 'ColName'] = 3

df.loc[df.index[1:3], 'ColName'] = 3

iloc - 基于位置
loc类似,但是根据位置而不是索引值。然而,您无法分配新列或索引。

# position based, but we can get the position
# from the columns object via the `get_loc` method
df.iloc[2, df.columns.get_loc('ColName')] = 3

df.iloc[2, 4] = 3

df.iloc[:3, 2:4] = 3

at - 基于标签的访问方式
与使用标量索引器进行索引类似,但不能用于数组索引器。可以赋值新的索引和列。

loc优点是速度更快。
at缺点是无法使用数组作为索引器。

# label based, but we can use position values
# to get the labels from the index object
df.at[df.index[2], 'ColName'] = 3

df.at['C', 'ColName'] = 3

iat - 基于位置
iloc 类似。不能在数组索引器中使用。不能!分配新的索引和列。

iloc 的优点是速度更快。
缺点是无法使用数组作为索引器。

# position based, but we can get the position
# from the columns object via the `get_loc` method
IBM.iat[2, IBM.columns.get_loc('PNL')] = 3

set_value - 基于标签的
对于标量索引器,与loc非常相似。无法用于数组索引器。可以分配新的索引和列。

优点非常快,因为几乎没有开销!
缺点之所以几乎没有开销是因为pandas没有执行大量的安全检查。使用时需自行承担风险。此外,它不适用于公共使用。

# label based, but we can use position values
# to get the labels from the index object
df.set_value(df.index[2], 'ColName', 3)

set_valuetakable=True)- 基于位置的操作
iloc类似。无法在数组索引器中使用。不能分配新的索引和列。

优点非常快,因为开销很小!
缺点由于pandas没有执行大量的安全检查,所以开销很小。请自行决定使用。此外,这不适用于公共使用。

# position based, but we can get the position
# from the columns object via the `get_loc` method
df.set_value(2, df.columns.get_loc('ColName'), 3, takable=True)

那么,有没有一种简单的方法可以按位置读取/设置多个列?此外,假设我想将一个值数组添加到新列中,这是否容易实现? - wellplayed
@wordsmith,有一些相对简单的方法可以将新列附加到数据框的末尾。甚至可以在开头添加。如果涉及位置,则没有简单的方法。 - piRSquared
这个答案正是我所需要的!Pandas确实很强大,但这是以使一切变得极其复杂和难以理解为代价的。 - slhck
4
请注意,自0.21版本以来,set_value已被弃用,推荐使用.at.iat - nedned
1
无法分配新索引或列的原因应该是清晰明了的;没有列名或索引名,函数就无法创建索引或列。因此,基于标签的函数具有创建新索引或列所需的必要信息。 - kelalaka

89

pandas 从 DataFrame 中选择数据有两种主要方法。

  • 标签选择
  • 整数位置选择

文档使用位置一词来指代整数位置。我不喜欢这个术语,因为我觉得它很容易引起混淆。整数位置更具描述性,正是 .iloc 的含义。关键词在这里是 INTEGER - 选定整数位置时必须使用整数。

在展示总结之前,让我们确保...

.ix 已弃用且模糊不清,不应再使用

pandas 有三个主要的索引器 。我们有索引操作符本身(方括号[]),.loc.iloc。 让我们总结一下:

  • [] - 主要选择列的子集,但也可以选择行。不能同时选择行和列。
  • .loc - 仅按标签选择行和列的子集
  • .iloc - 仅按整数位置选择行和列的子集

我几乎从不使用.at.iat,因为它们没有增加任何额外的功能,并且只有轻微的性能提升。除非您有一个时间非常敏感的应用程序,否则我会不鼓励使用它们。无论如何,我们有它们的总结:

  • .at 仅通过标签在 DataFrame 中选择单个标量值
  • .iat 仅通过整数位置在 DataFrame 中选择单个标量值

除了通过标签和整数位置进行选择外,还存在布尔选择,也称为布尔索引


以下是说明.loc.iloc、布尔选择以及.at.iat的示例

我们首先将重点放在.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后面的括号内放入索引标签。
df.loc['Penelope']

这将返回数据行作为一系列数据

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

enter image description here

复杂的切片可以像Python列表一样进行。

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

现在让我们转向 .iloc。DataFrame 中的每一行和列都有一个定义它的整数位置。这是除了在输出中可视化显示的标签之外的另一个位置。整数位置简单地从顶部/左侧开始计算,行/列数从0开始。

许多不同的输入可以用于.iloc,其中三个是:

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

使用整数使用 .iloc 选择单行

df.iloc[4]

这将返回第五行(整数位置为4)作为一系列数据

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

使用整数列表和 .iloc 选择多行
df.iloc[[2, -2]]

这将返回第三行和倒数第二行的DataFrame:

enter image description here

使用切片符号和.iloc选择多行

df.iloc[:5:3]

enter image description here


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

.loc/.iloc的一个优秀功能是它们能够同时选择行和列。在上面的示例中,每个选择都返回了所有列。我们可以像选择行一样选择列。只需要用逗号分隔行和列的选择。

例如,我们可以选择行Jane和Dean,并只选择高度、得分和州这几列:

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

enter image description here

使用标签列表来表示行,使用切片符号来表示列。
我们可以使用整数在.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]

enter image description here


索引操作符[]可以切片选择行和列,但不能同时进行。

大多数人熟悉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']]

enter image description here

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

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

enter image description here

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

enter image description here

.loc/.iloc 是选择行的明确方法,高度推荐使用。仅使用索引操作符无法同时选择行和列。

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

.at.iat 进行选取

.at 的使用方法与 .loc 几乎相同,但它只选择 DataFrame 中的单个“单元格”。通常我们将此单元格称为标量值。要使用 .at,请传递行和列标签,用逗号隔开。

df.at['Christina', 'color']
'black'

使用.iat进行选择与.iloc几乎相同,但它仅选择单个标量值。您必须为行和列位置都传递整数。
df.iat[2, 5]
'FL'

1
这绝对是迄今为止对我来说最有用和简洁的答案,因为我刚开始使用DataFrames。 - Thynome
非常好的答案。我想我会停止使用 at - Edison

32
df = pd.DataFrame({'A':['a', 'b', 'c'], 'B':[54, 67, 89]}, index=[100, 200, 300])

df

                        A   B
                100     a   54
                200     b   67
                300     c   89
In [19]:    
df.loc[100]

Out[19]:
A     a
B    54
Name: 100, dtype: object

In [20]:    
df.iloc[0]

Out[20]:
A     a
B    54
Name: 100, dtype: object

In [24]:    
df2 = df.set_index([df.index,'A'])
df2

Out[24]:
        B
    A   
100 a   54
200 b   67
300 c   89

In [25]:    
df2.ix[100, 'a']

Out[25]:    
B    54
Name: (100, a), dtype: int64

4

让我们从这个小的数据框开始:

import pandas as pd
import time as tm
import numpy as np
n=10
a=np.arange(0,n**2)
df=pd.DataFrame(a.reshape(n,n))

We'll so have

df
Out[25]: 
        0   1   2   3   4   5   6   7   8   9
    0   0   1   2   3   4   5   6   7   8   9
    1  10  11  12  13  14  15  16  17  18  19
    2  20  21  22  23  24  25  26  27  28  29
    3  30  31  32  33  34  35  36  37  38  39
    4  40  41  42  43  44  45  46  47  48  49
    5  50  51  52  53  54  55  56  57  58  59
    6  60  61  62  63  64  65  66  67  68  69
    7  70  71  72  73  74  75  76  77  78  79
    8  80  81  82  83  84  85  86  87  88  89
    9  90  91  92  93  94  95  96  97  98  99

通过这个,我们有:

df.iloc[3,3]
Out[33]: 33

df.iat[3,3]
Out[34]: 33

df.iloc[:3,:3]
Out[35]: 
    0   1   2   3
0   0   1   2   3
1  10  11  12  13
2  20  21  22  23
3  30  31  32  33



df.iat[:3,:3]
Traceback (most recent call last):
   ... omissis ...
ValueError: At based indexing on an integer index can only have integer indexers

因此,我们无法使用.iat进行子集选择,必须仅使用.iloc。

但是让我们尝试从较大的数据框中选择,然后检查速度...

# -*- coding: utf-8 -*-
"""
Created on Wed Feb  7 09:58:39 2018

@author: Fabio Pomi
"""

import pandas as pd
import time as tm
import numpy as np
n=1000
a=np.arange(0,n**2)
df=pd.DataFrame(a.reshape(n,n))
t1=tm.time()
for j in df.index:
    for i in df.columns:
        a=df.iloc[j,i]
t2=tm.time()
for j in df.index:
    for i in df.columns:
        a=df.iat[j,i]
t3=tm.time()
loc=t2-t1
at=t3-t2
prc = loc/at *100
print('\nloc:%f at:%f prc:%f' %(loc,at,prc))

loc:10.485600 at:7.395423 prc:141.784987

使用.loc我们可以管理子集,使用.at只能处理单个标量,但使用.at比.loc更快。

:-)


1
请注意,仅访问列时,.loc[]慢7-10倍: 测试脚本:
import os
import sys
from timeit import timeit

import numpy as np
import pandas as pd


def setup():
    arr = np.arange(0, 10 ** 2)
    return pd.DataFrame(arr.reshape(10, 10))


if __name__ == "__main__":
    print(f"Python: {sys.version}")
    print(f"Numpy: {np.__version__}")
    print(f"Pandas: {pd.__version__}")

    iters = 10000

    print(
        "[] Method:",
        timeit(
            "data = df[0]",
            setup="from __main__ import setup; df = setup()",
            number=iters,
        ),
    )
    print(
        ".loc() Method:",
        timeit(
            "data = df.loc[:, 0]",
            setup="from __main__ import setup; df = setup()",
            number=iters,
        ),
    )

输出:

Python: 3.8.10 (tags/v3.8.10:3d8993a, May  3 2021, 11:48:03) [MSC v.1928 64 bit (AMD64)]
Numpy: 1.21.1
Pandas: 1.3.3
[] Method: 0.0923579000000001
.loc() Method: 0.6762988000000001

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