熊猫DataFrame性能


74

Pandas确实很棒,但令我惊讶的是,从Pandas.DataFrame中检索值的效率低下。在下面的玩具示例中,即使DataFrame.iloc方法也比字典慢100倍以上。

问题:这里的教训仅仅是字典是查找价值的更好方法吗?是的,我知道那正是他们的目的。但是我只是想知道是否缺少有关DataFrame查找性能的信息。

我意识到这个问题比“提问”更“有趣”,但是我会接受一个提供洞察力或观点的答案。谢谢。

import timeit

setup = '''
import numpy, pandas
df = pandas.DataFrame(numpy.zeros(shape=[10, 10]))
dictionary = df.to_dict()
'''

f = ['value = dictionary[5][5]', 'value = df.loc[5, 5]', 'value = df.iloc[5, 5]']

for func in f:
    print func
    print min(timeit.Timer(func, setup).repeat(3, 100000))

值=字典[5] [5]

0.130625009537

值= df.loc [5,5]

19.4681699276

值= df.iloc [5,5]

17.2575249672

Answers:


108

字典对于DataFrame的要求就像自行车对汽车的要求一样。您可以比骑车快10英尺,比起启动汽车,使齿轮换挡等更快速。但是,如果您需要走一英里,汽车就可以胜出。

对于某些小的目标目标,命令可能会更快。如果这就是您所需要的,那么请确定使用dict!但是,如果您需要/想要DataFrame的强大功能,那么dict是无可替代的。如果数据结构不能首先满足您的需求,那么比较速度是没有意义的。

现在,例如-更具体地讲-dict对于访问列很有用,但是对访问行却不太方便。

import timeit

setup = '''
import numpy, pandas
df = pandas.DataFrame(numpy.zeros(shape=[10, 1000]))
dictionary = df.to_dict()
'''

# f = ['value = dictionary[5][5]', 'value = df.loc[5, 5]', 'value = df.iloc[5, 5]']
f = ['value = [val[5] for col,val in dictionary.items()]', 'value = df.loc[5]', 'value = df.iloc[5]']

for func in f:
    print(func)
    print(min(timeit.Timer(func, setup).repeat(3, 100000)))

产量

value = [val[5] for col,val in dictionary.iteritems()]
25.5416321754
value = df.loc[5]
5.68071913719
value = df.iloc[5]
4.56006002426

因此,列表的dict检索行的速度比慢5倍df.iloc。随着列数的增加,速度不足会变得更大。(列数就像自行车类比中的脚数。距离越长,汽车就越方便...)

这只是列表的字典不如DataFrame方便/慢的一个例子。

另一个示例是当您为各行设置了DatetimeIndex并希望选择某些日期之间的所有行时。有了DataFrame,您可以使用

df.loc['2000-1-1':'2000-3-31']

如果要使用列表字典,则没有简单的模拟方法。与DataFrame相比,用于选择正确行的Python循环将再次非常慢。


这样的答案可能会添加到FAQ中,请参见:github.com/pydata/pandas/issues/3871
Jeff

4
感谢您提供的两个非常有启发性的示例,也感谢类比,作为一个骑自行车的人,我很感激。
Owen

3
您的示例使用 df.to_dict()的不是列的字典,而是字典的字典。使用dic = {x:df[x].values.tolist() for x in df}您的计算机(在我的机器上),访问列的速度提高了25倍,行访问速度提高了1.5倍。因此字典速度更快
tal

21

看来现在的性能差异要小得多(0.21.1-我忘记了原始示例中的Pandas版本)。词典的访问和之间不仅性能差距.loc减小(从约335倍到126倍速度较慢), (lociloc小于2倍慢于atiat)现在。

In [1]: import numpy, pandas
   ...:    ...: df = pandas.DataFrame(numpy.zeros(shape=[10, 10]))
   ...:    ...: dictionary = df.to_dict()
   ...: 

In [2]: %timeit value = dictionary[5][5]
85.5 ns ± 0.336 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [3]: %timeit value = df.loc[5, 5]
10.8 µs ± 137 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [4]: %timeit value = df.at[5, 5]
6.87 µs ± 64.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [5]: %timeit value = df.iloc[5, 5]
14.9 µs ± 114 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [6]: %timeit value = df.iat[5, 5]
9.89 µs ± 54.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [7]: print(pandas.__version__)
0.21.1

----以下原始答案----

+1用于使用atiat用于标量运算。基准示例:

In [1]: import numpy, pandas
   ...: df = pandas.DataFrame(numpy.zeros(shape=[10, 10]))
   ...: dictionary = df.to_dict()

In [2]: %timeit value = dictionary[5][5]
The slowest run took 34.06 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 310 ns per loop

In [4]: %timeit value = df.loc[5, 5]
10000 loops, best of 3: 104 µs per loop

In [5]: %timeit value = df.at[5, 5]
The slowest run took 6.59 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 9.26 µs per loop

In [6]: %timeit value = df.iloc[5, 5]
10000 loops, best of 3: 98.8 µs per loop

In [7]: %timeit value = df.iat[5, 5]
The slowest run took 6.67 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 9.58 µs per loop

似乎使用atiat)比lociloc)快10倍。


6

我遇到了同样的问题。您可以at用来改善。

“由于使用[]进行索引必须处理很多情况(单标签访问,切片,布尔索引等),因此要弄清楚您的要求会有些麻烦。如果您只想访问标量值,最快的方法是使用atiat方法,它们在所有数据结构上实现。”

请参阅官方参考http://pandas.pydata.org/pandas-docs/stable/indexing.html章节“快速获取和设置标量值”


这是一个很好的参考,但不如上面的答案那么详细。
BCR 2015年

1

我认为访问单元格最快的方法是

df.get_value(row,column)
df.set_value(row,column,value) 

两者都比(我认为)快

df.iat(...) 
df.at(...)

1
似乎at更快-4.68 µs(at)和5.98 µs(get_values)。at由于可以使用命名索引,因此也更加灵活。

0

我在访问数据框行时遇到了不同的现象。在大约10,000,000行的数据帧上测试此简单示例。字典岩石。

def testRow(go):
    go_dict = go.to_dict()
    times = 100000
    ot= time.time()
    for i in range(times):
        go.iloc[100,:]
    nt = time.time()
    print('for iloc {}'.format(nt-ot))
    ot= time.time()
    for i in range(times):
        go.loc[100,2]
    nt = time.time()
    print('for loc {}'.format(nt-ot))
    ot= time.time()
    for i in range(times):
        [val[100] for col,val in go_dict.iteritems()]
    nt = time.time()
    print('for dict {}'.format(nt-ot))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.