熊猫标量值获取和设置:ix还是iat?

时间:2015-03-06 22:51:07

标签: python indexing pandas

我试图找出何时在pandas DataFrame中使用不同的选择方法。特别是,我正在寻找访问标量值。我经常听到ix被普遍推荐。但在pandas documentation 建议您使用atiat进行快速标量值访问:

Since indexing with [] must handle a lot of cases (single-label access, slicing, boolean indexing, etc.), it has a bit of overhead in order to figure out what you’re asking for. If you only want to access a scalar value, the fastest way is to use the

and iat methods, which are implemented on all of the data structures.

所以,我认为iat应该更快地获取和设置单个单元格。但是,经过一些测试后,我们发现ix可以与读取单元格相媲美或更快,而iat可以更快地为单元格指定值。

这种行为是否记录在任何地方?它总是如此,为什么会发生这种情况?是否必须对返回视图或复制执行某些操作?如果有人能对这个问题有所了解并解释建议获取和设置单元格值以及原因,我将不胜感激。

以下是一些使用pandas的测试(版本0.15.2)。

为了确保此行为不是此版本的错误,我还在0.11.0上测试了它。我没有提供结果,但趋势完全相同 - ix being much faster for getting, and iat for setting individual cells

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.rand(1000,2),columns = ['A','B'])
idx = 0

timeit for i in range(1000): df.ix[i,'A'] = 1
timeit for i in range(1000): df.iat[i,idx] = 2

>> 10 loops, best of 3: 92.6 ms per loop
>> 10 loops, best of 3: 21.7 ms per loop

timeit for i in range(1000): tmp = df.ix[i,'A'] 
timeit for i in range(1000): tmp = df.iat[i,idx] 

>> 100 loops, best of 3: 5.31 ms per loop
>> 10 loops, best of 3: 19.4 ms per loop

1 个答案:

答案 0 :(得分:12)

Pandas使用indexing classes做了一些非常有趣的事情。我不认为我能够描述一种简单的方法来了解使用哪种方法,但我可以对实施提供一些见解。

DataFrame#ix_IXIndexer,不会声明自己的__getitem____setitem__。这两种方法很重要,因为它们控制着如何使用Pandas访问值。由于_IXIndexer未声明这些方法,因此会使用super class _NDFrameIndexer

进一步挖掘_NDFrameIndexer __getitem__表明它相对简单,在某些情况下包含了get_value中的逻辑。在某些情况下,__getitem__接近get_value的速度。

_NDFrameIndexer' s __setitem__是另一回事。起初它看起来很简单,但它调用的第二种方法是_setitem_with_indexer,它在大多数情况下都做了大量的工作。

此信息表明,使用ix获取值的调用在最佳情况下受get_value限制,使用ix调用设置值将需要核心提交者进行解释。

现在DataFrame#iat_iAtIndexer,它也没有声明自己的__getitem____setitem__,因此会回到超级_ScalarAccessIndexer实施。

_ScalarAccessIndexer有一个simple __getitem__实现,但它需要一个循环才能将密钥转换为正确的格式。额外的循环在调用get_value之前会增加一些额外的处理时间。

_ScalarAccessIndexer还有一个公平的simple __setitem__实现,可以在设置值之前转换参数set_value所需的密钥。

此信息表明,使用iat获取值的调用受​​get_valuefor loop的限制。使用iat设置值主要受限于set_value的调用。因此,使用iat获取值会产生一些开销,而设置它们的开销会更小。

<强> TL; DR

我相信您正在使用基于文档的Int64Index索引的正确访问器,但我不认为这意味着它是最快的。可以直接使用get_valueset_value找到最佳效果,但它们需要对Pandas DataFrames的实现方式有更深入的了解。

备注

值得注意的是,关于Pandas的文档提到get_valueset_value已被弃用,而我认为这是iget_value

<强>实施例

为了使用一些索引器(包括直接调用get_valueset_value)来显示性能差异,我创建了这个脚本:

example.py

import timeit


def print_index_speed(stmnt_name, stmnt):
    """
    Repeatedly run the statement provided then repeat the process and take the
    minimum execution time.
    """
    setup = """
import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.rand(1000,2),columns = ['A','B'])
idx = 0
    """

    minimum_execution_time = min(
        timeit.Timer(stmnt, setup=setup).repeat(5, 10))

    print("{stmnt_name}: {time}".format(
        stmnt_name=stmnt_name,
        time=round(minimum_execution_time, 5)))

print_index_speed("set ix", "for i in range(1000): df.ix[i, 'A'] = 1")
print_index_speed("set at", "for i in range(1000): df.at[i, 'A'] = 2")
print_index_speed("set iat", "for i in range(1000): df.iat[i, idx] = 3")
print_index_speed("set loc", "for i in range(1000): df.loc[i, 'A'] = 4")
print_index_speed("set iloc", "for i in range(1000): df.iloc[i, idx] = 5")
print_index_speed(
    "set_value scalar",
    "for i in range(1000): df.set_value(i, idx, 6, True)")
print_index_speed(
    "set_value label",
    "for i in range(1000): df.set_value(i, 'A', 7, False)")

print_index_speed("get ix", "for i in range(1000): tmp = df.ix[i, 'A']")
print_index_speed("get at", "for i in range(1000): tmp = df.at[i, 'A']")
print_index_speed("get iat", "for i in range(1000): tmp = df.iat[i, idx]")
print_index_speed("get loc", "for i in range(1000): tmp = df.loc[i, 'A']")
print_index_speed("get iloc", "for i in range(1000): tmp = df.iloc[i, idx]")
print_index_speed(
    "get_value scalar",
    "for i in range(1000): tmp = df.get_value(i, idx, True)")
print_index_speed(
    "get_value label",
    "for i in range(1000): tmp = df.get_value(i, 'A', False)")

输出:

set ix: 0.9918
set at: 0.06801
set iat: 0.08606
set loc: 1.04173
set iloc: 1.0021
set_value: 0.0452
**set_value**: 0.03516
get ix: 0.04827
get at: 0.06889
get iat: 0.07813
get loc: 0.8966
get iloc: 0.87484
get_value: 0.04994
**get_value**: 0.03111