【问题标题】:pandas loc vs. iloc vs. at vs. iat?pandas loc vs. iloc vs. at vs. iat?
【发布时间】:2015-04-29 17:04:52
【问题描述】:

最近开始从我的安全位置 (R) 扩展到 Python,并且对 Pandas 中的单元定位/选择感到有些困惑。我已阅读文档,但我很难理解各种本地化/选择选项的实际含义。

我是否有理由使用.loc.iloc 而不是atiat,反之亦然? 在什么情况下我应该使用哪种方法?


注意:未来的读者请注意,这个问题已经过时了,并且是在 pandas v0.20 之前编写的,当时曾经存在一个名为 .ix 的函数。该方法后来被分成两个 - lociloc - 以明确区分基于位置和标签的索引。请注意,ix 由于行为不一致且难以理解,已被停用,并且在当前版本的 pandas (>= 1.0) 中不再存在。

【问题讨论】:

  • loc 是基于标签的索引,所以基本上是在一行中查找一个值,iloc 是基于整数行的索引,ix 是首先执行基于标签的通用方法,如果失败然后它下降到基于整数。 at 已弃用,建议您不要再使用它。要考虑的另一件事是您正在尝试做的事情,因为其中一些方法允许切片和列分配,老实说文档非常清楚:pandas.pydata.org/pandas-docs/stable/indexing.html
  • @EdChum - 是什么让你说 at 已弃用?我在at(或iat)文档中没有看到它。
  • 这是一个错误,它没有被弃用,我认为有人说要弃用它,但这个想法被放弃了,因为我认为它更快
  • locixiloc之间的详细解释:stackoverflow.com/questions/31593201/…

标签: python pandas performance indexing lookup


【解决方案1】:

loc: 仅适用于索引
iloc: 适用于位置
at: 获取标量值。这是一个非常快速的 loc
iat: 获取标量值。这是一个非常快的 iloc

还有,

atiat 用于访问标量,即单个元素 在数据框中,而 lociloc 是访问几个 同时元素,可能执行矢量化 操作。

http://pyciencia.blogspot.com/2015/05/obtener-y-filtrar-datos-de-un-dataframe.html

【讨论】:

  • 如果atiatlociloc 的非常快的版本,那么为什么还要使用lociloc
  • atand iat 用于访问标量,即数据框中的单个元素,而 lociloc 用于同时访问多个元素,可能会执行矢量化操作。
  • @ncasas - 如果我阅读了文档权利 .at 只能按索引访问,而 .loc 也可以按列名访问。有没有办法使用更快的 .at 但使用列名而不是索引?就像用 x=df.at[df.Id == source_Id, 'someValue'] 替换 x=df.loc[df.Id == source_Id, 'someValue'].values[0]。带有 .at 的版本会抛出“ValueError:基于整数索引的 At 索引只能有整数索引器”
  • .values[].iat
【解决方案2】:
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

【讨论】:

    【解决方案3】:

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


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

    特别说明:当传递标量索引器时,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 非常相似。 不能对数组索引器进行操作。 可以!分配新的索引和列。

    优于loc 的优点是速度更快。
    缺点是您不能将数组用于索引器。

    # 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_value with takable=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)
    

    【讨论】:

    • 那么,有没有一种简单的方法可以按位置读取/设置多列?此外,比如说,我想将一组值添加到新列中,这很容易做到吗?
    • @wordsmith 有一些简单的方法可以将新列附加到数据帧的末尾。甚至是开始。如果涉及职位,那么没有,没有简单的方法。
    • 这个答案正是我所需要的! Pandas 确实很强大,但它的代价是让所有东西都变得非常难以理解和拼凑起来。
    • 请注意,自 0.21 版起,set_value 已被弃用,取而代之的是 .at.iat
    【解决方案4】:

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

    粗体中的所有单词都是标签。标签agecolorfoodheightscorestate 用于。其他标签JaneNickAaronPenelopeDeanChristinaCornelia 用作行的标签。这些行标签统称为索引


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

    .loc 仅通过标签选择数据

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

    many different inputs 可以用于.loc,其中三个是

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

    使用带有字符串的 .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,其中的行按列表中指定的顺序排列:

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

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

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

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

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

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

    many different inputs 可以用于.iloc,其中三个是

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

    使用带有整数的 .iloc 选择单行

    df.iloc[4]
    

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

    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'
    

    .at.iat 选择

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

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

    .iat 的选择与.iloc 几乎相同,但它只选择一个标量值。您必须为行和列位置传递一个整数

    df.iat[2, 5]
    'FL'
    

    【讨论】:

    • 对于刚开始使用 DataFrames 的我来说,这是迄今为止最有用、最简洁的答案。
    【解决方案5】:

    让我们从这个小df开始:

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

    我们会这样的

    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。

    但是让我们尝试从更大的df中选择并检查速度......

    # -*- 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 快

    :-)

    【讨论】:

      【解决方案6】:

      需要注意的是,对于仅访问列,.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
      

      【讨论】:

        猜你喜欢
        • 2018-04-10
        • 1970-01-01
        • 2015-09-30
        • 2022-01-07
        • 2014-08-17
        • 2011-09-18
        • 1970-01-01
        • 2012-07-27
        相关资源
        最近更新 更多