【问题标题】:pandas .at versus .loc熊猫 .at 与 .loc
【发布时间】:2016-09-10 00:48:01
【问题描述】:

我一直在探索如何优化我的代码并遇到了pandas.at 方法。根据documentation

基于标签的快速标量访问器

与 loc 类似,at 提供基于标签的标量查找。您也可以使用这些索引器进行设置。

所以我运行了一些样本:

设置

import pandas as pd
import numpy as np
from string import letters, lowercase, uppercase

lt = list(letters)
lc = list(lowercase)
uc = list(uppercase)

def gdf(rows, cols, seed=None):
    """rows and cols are what you'd pass
    to pd.MultiIndex.from_product()"""
    gmi = pd.MultiIndex.from_product
    df = pd.DataFrame(index=gmi(rows), columns=gmi(cols))
    np.random.seed(seed)
    df.iloc[:, :] = np.random.rand(*df.shape)
    return df

seed = [3, 1415]
df = gdf([lc, uc], [lc, uc], seed)

print df.head().T.head().T

df 看起来像:

            a                                        
            A         B         C         D         E
a A  0.444939  0.407554  0.460148  0.465239  0.462691
  B  0.032746  0.485650  0.503892  0.351520  0.061569
  C  0.777350  0.047677  0.250667  0.602878  0.570528
  D  0.927783  0.653868  0.381103  0.959544  0.033253
  E  0.191985  0.304597  0.195106  0.370921  0.631576

让我们使用.at.loc 并确保我得到相同的东西

print "using .loc", df.loc[('a', 'A'), ('c', 'C')]
print "using .at ", df.at[('a', 'A'), ('c', 'C')]

using .loc 0.37374090276
using .at  0.37374090276

使用.loc测试速度

%%timeit
df.loc[('a', 'A'), ('c', 'C')]

10000 loops, best of 3: 180 µs per loop

使用.at测试速度

%%timeit
df.at[('a', 'A'), ('c', 'C')]

The slowest run took 6.11 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 8 µs per loop

这看起来是一个巨大的速度提升。即使在缓存阶段6.11 * 8也比180快很多

问题

.at 的限制是什么?我有动力使用它。文档说它类似于.loc,但它的行为并不相似。示例:

# small df
sdf = gdf([lc[:2]], [uc[:2]], seed)

print sdf.loc[:, :]

          A         B
a  0.444939  0.407554
b  0.460148  0.465239

print sdf.at[:, :] 的结果是 TypeError: unhashable type

即使意图相似,显然也不相同。

也就是说,谁能提供关于.at 方法可以做什么和不可以做什么的指导?

【问题讨论】:

标签: python pandas dataframe


【解决方案1】:

.at 相比.loc 是一种优化的数据访问方法。

.loc 的数据框选择由 indexed_rows 和 label_columns 定位的所有元素,如其参数中给出的那样。相反,.at 选择位于给定 indexed_row 和 label_column 的数据帧的特定元素。

此外,.at 将一行和一列作为输入参数,而 .loc 可能会采用多行和多列。使用.at 的输出是单个元素,使用.loc 可能是一个Series 或DataFrame。

【讨论】:

  • 返回单个值并非总是如此。如果提供的索引被多次使用,它将返回值数组。
【解决方案2】:

除此之外,Pandas documentationat 函数声明:

访问行/列标签对的单个值。

与 loc 类似,两者都提供基于标签的查找。使用在如果 您只需要在 DataFrame 或 Series 中获取或设置单个值。

对于设置数据locat类似,例如:

df = pd.DataFrame({'A': [1,2,3], 'B': [11,22,33]}, index=[0,0,1])

locat 将产生相同的结果

df.at[0, 'A'] = [101,102]
df.loc[0, 'A'] = [101,102]

    A   B
0   101 11
0   102 22
1   3   33

df.at[0, 'A'] = 103
df.loc[0, 'A'] = 103

    A   B
0   103 11
0   103 22
1   3   33

另外,对于单个值的访问,两者是相同的

df.loc[1, 'A']   # returns a single value (<class 'numpy.int64'>)
df.at[1, 'A']    # returns a single value (<class 'numpy.int64'>)

3

但是,当匹配多个值时,loc 将从 DataFrame 返回一组行/列,而at 将返回一个值数组

df.loc[0, 'A']  # returns a Series (<class 'pandas.core.series.Series'>)

0    103
0    103
Name: A, dtype: int64

df.at[0, 'A']   # returns array of values (<class 'numpy.ndarray'>)

array([103, 103])

更重要的是,loc 可用于匹配一组行/列,并且只能给出一个索引,而at 必须接收列

df.loc[0]  # returns a DataFrame view (<class 'pandas.core.frame.DataFrame'>)

    A   B
0   103 11
0   103 22


# df.at[0]  # ERROR: must receive column

【讨论】:

  • 当你尝试查询df.at[0] 0 不唯一时的错误已更改为ValueError: Invalid call for scalar access (getting)!。这不是最惯用的错误,但如果您希望索引是唯一的,则可能需要这种行为。
【解决方案3】:

当您询问.at 的限制时,这是我最近遇到的一件事(使用 pandas 0.22)。让我们使用来自the documentation的示例:

df = pd.DataFrame([[0, 2, 3], [0, 4, 1], [10, 20, 30]], index=[4, 5, 6], columns=['A', 'B', 'C'])
df2 = df.copy()

    A   B   C
4   0   2   3
5   0   4   1
6  10  20  30

如果我现在这样做

df.at[4, 'B'] = 100

结果和预期的一样

    A    B   C
4   0  100   3
5   0    4   1
6  10   20  30

但是,当我尝试这样做时

 df.at[4, 'C'] = 10.05

似乎.at 试图保存数据类型(这里:int

    A    B   C
4   0  100  10
5   0    4   1
6  10   20  30

这似乎与.loc 不同:

df2.loc[4, 'C'] = 10.05

达到预期效果

    A   B      C
4   0   2  10.05
5   0   4   1.00
6  10  20  30.00

上面示例中的风险在于它是静默发生的(从floatint 的转换)。当尝试对字符串进行相同操作时,它会抛出错误:

df.at[5, 'A'] = 'a_string'

ValueError: int() 以 10 为底的无效文字:'a_string'

但是,如果使用 int() 实际工作的字符串,如 cmets 中 @n1k31t4 所述,例如

df.at[5, 'A'] = '123'

     A   B   C
4    0   2   3
5  123   4   1
6   10  20  30

【讨论】:

  • 好点!我一直在争论我应该在哪里提出这个问题。
  • @piRSquared:是的,如果这被记录在任何地方会很好(可能已经忽略了它,我应该尝试 0.23)。
  • 也发生在 0.23 上。试试df = pd.DataFrame([[1, 2], [3, 4]]), df.at[0, 1] = 'a'
  • 谢谢你,它确实试图保留数字数据类型。我正在尝试df.at[1, 'test'] = 'string',但它不起作用。 .loc 修复了问题
  • 因此,如果您将 int() 能够使用的任何内容传递给它,它将起作用...意味着您可以这样做:df.at[5, "A"] = "123" 它会分配字符串,并将其转换为 int方式。
【解决方案4】:

更新:df.get_value 自 0.21.0 版起已弃用。今后推荐使用df.atdf.iat


df.at 一次只能访问一个值。

df.loc 可以选择多行和/或多列。

请注意,还有df.get_value,它在访问单个值时可能会更快:

In [25]: %timeit df.loc[('a', 'A'), ('c', 'C')]
10000 loops, best of 3: 187 µs per loop

In [26]: %timeit df.at[('a', 'A'), ('c', 'C')]
100000 loops, best of 3: 8.33 µs per loop

In [35]: %timeit df.get_value(('a', 'A'), ('c', 'C'))
100000 loops, best of 3: 3.62 µs per loop

在引擎盖下,df.at[...]calls df.get_value,但它也在按键上执行some type checking

【讨论】:

  • 请注意,get_value 自 0.21.0 版起已弃用。
  • 这个弃用是否意味着我们应该使用 .loc 而不是 .at ?
  • 不,这并不意味着。 at 比 loc 快,但仅适用于单个单元格。 loc 可以处理单元格数组
  • 您可能需要再次测试。已经没有太大区别了。 %timeit df.at[100, ['A']] = 10 661 µs ± 2.91 µs 每个循环(平均值±标准偏差。7 次运行,每次 1000 个循环)%timeit df.loc[100, ['A' ]] = 10 645 µs ± 3.21 µs 每个循环(平均值±标准偏差。7 次运行,每次 1000 次循环)
  • 我发现如果存在重复的索引值,df.at 确实可以访问多行。
猜你喜欢
  • 2021-11-20
  • 2021-02-07
  • 2021-12-06
  • 2016-07-03
  • 2015-10-12
  • 2020-08-20
  • 2018-09-11
  • 2018-11-24
  • 2019-01-19
相关资源
最近更新 更多