我想弄清楚什么时候在熊猫DataFrame中使用不同的选择方法。特别是,我正在寻找访问标量值的方法。我经常听到ix
被普遍推荐。但在熊猫文献中,建议使用at
和iat
进行快速标量值访问:
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
atand iat methods, which are implemented on all of the data structures.
因此,我假设iat
应该更快地获取和设置单个单元格。但是,经过一些测试后,我们发现ix
在读取单元格时具有可比性,或者更快,而iat
用于为单元格分配值的速度要快得多。
这种行为在任何地方都有记录吗?情况总是如此吗?为什么会发生这种情况?它必须要做什么与返回视图或复制?如果有人能说明这个问题,并解释什么是获得和设置单元格值的建议,以及为什么,我将不胜感激。
下面是一些使用熊猫的测试(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
发布于 2015-03-10 23:03:06
熊猫在索引类上做了一些非常有趣的事情。我认为我无法描述一种简单的方法来知道使用哪种方法,但是我可以给出一些关于实现的见解。
DataFrame#ix
是一个不声明自己的__getitem__
或__setitem__
的_IXIndexer
。这两种方法很重要,因为它们控制如何使用Pandas访问值。因为_IXIndexer
不声明这些方法,所以使用_NDFrameIndexer
。
对_NDFrameIndexer
的__getitem__
的进一步深入研究表明,它相对简单,在某些情况下封装了在get_value
中找到的逻辑,然后在某些场景中__getitem__
接近于get_value
。
_NDFrameIndexer
的__setitem__
是一个不同的故事,起初它看起来很简单,但第二个方法是_setitem_with_indexer
,它在大多数场景中做了大量的工作。
这些信息表明,在最好的情况下,使用ix
获取值的调用受到get_value
的限制,使用ix
设置值的调用需要核心提交人来解释。
现在对于DataFrame#iat
来说,它是一个_iAtIndexer
,它也没有声明自己的__getitem__
或__setitem__
,因此回到了超类_ScalarAccessIndexer
的实现上。
_ScalarAccessIndexer
有一个__getitem__
实现,但它需要一个循环才能将密钥转换成正确的格式。在调用get_value
之前,附加循环会增加一些额外的处理时间。
_ScalarAccessIndexer
还有一个公平的__setitem__
实现,它在设置值之前转换set_value
所需的键。
这些信息表明,使用iat
获取值的调用受到get_value
和用于循环的限制。使用iat
设置值主要受到对set_value
的调用的限制。因此,使用iat
获取值有一定的开销,而设置这些值的开销较小。
TL;博士
我相信您使用了基于文档的Int64Index
索引的正确访问器,但我认为这并不意味着它是最快的。可以直接使用get_value
和set_value
找到最佳性能,但它们需要更深入地了解如何实现Pandas DataFrames。
Notes
值得注意的是,Pandas上的文档提到了get_value
和set_value
被废弃了,我认为这应该是iget_value
。
示例
为了使用几个索引器(包括直接调用get_value
和set_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
https://stackoverflow.com/questions/28909034
复制