我今天观察到,选择两列或更多列数据帧可能比仅选择一列慢得多。
如果我使用loc或iloc选择多个列,并且使用list传递列名或索引,则与使用iloc的单列或多列选择相比,性能下降了100倍(但未传递任何列表)< / p>
示例:
df = pd.DataFrame(np.random.randn(10**7,10), columns=list('abcdefghij'))
一列选择:
%%timeit -n 100
df['b']
3.17 µs ± 147 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit -n 100
df.iloc[:,1]
66.7 µs ± 5.95 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit -n 100
df.loc[:,'b']
44.2 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
两列选择:
%%timeit -n 10
df[['b', 'c']]
96.4 ms ± 788 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%%timeit -n 10
df.loc[:,['b', 'c']]
99.4 ms ± 4.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%%timeit -n 10
df.iloc[:,[1,2]]
97.6 ms ± 1.79 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
仅此选择可以像预期的那样工作: [编辑]
%%timeit -n 100
df.iloc[:,1:3]
103 µs ± 17.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
机制上有何不同以及为何如此之大?
[编辑]: 正如@ run-out指出的那样,pd.Series的处理似乎比pd.DataFrame快得多,有人知道为什么会这样吗?
另一方面-它不能解释df.iloc[:,[1,2]]
和df.iloc[:,1:3]
之间的区别
答案 0 :(得分:4)
Pandas作为pandas.Series使用单行或单列,这比在DataFrame体系结构中工作要快。
当您要求时,Pandas可与pandas.Series配合使用
%%timeit -n 10
df['b']
2.31 µs ± 1.59 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
但是,我可以通过将其放在列表中来为同一列调用DataFrame。然后您得到:
%%timeit -n 10
df[['b']]
90.7 ms ± 1.73 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
从上面您可以看到,表现优于DataFrame的是Series。
这是Pandas如何处理列“ b”。
type(df['b'])
pandas.core.series.Series
type(df[['b']])
pandas.core.frame.DataFrame
编辑: 我正在扩展我的答案,因为OP希望更深入地了解为什么pd.series与pd.dataframe的速度如此之大。同样,这也是一个扩展我/我们对基础技术如何工作的理解的好问题。那些有更多专业知识的人请加入。
首先让我们从numpy开始,因为它是熊猫的构建基块。根据pandas的作者以及Python for Data Analysis的作者Wes McKinney的说法,性能在numpy之上超过了python:
This is based partly on performance differences having to do with the
cache hierarchy of the CPU; operations accessing contiguous blocks of memory (e.g.,
summing the rows of a C order array) will generally be the fastest because the mem‐
ory subsystem will buffer the appropriate blocks of memory into the ultrafast L1 or
L2 CPU cache.
让我们看看这个例子的速度差异。让我们从数据帧的列“ b”中创建一个numpy数组。
a = np.array(df['b'])
现在进行性能测试:
%%timeit -n 10
a
结果是:
32.5 ns ± 28.2 ns per loop (mean ± std. dev. of 7 runs, 10 loops each)
这是在pd.series时间2.31 µs内性能的严重提高。
提高性能的另一个主要原因是numpy索引直接进入了NumPy C扩展,但是当您对Series进行索引时,有很多python东西在工作,而且速度慢得多。 (read this article)
让我们看一下为什么这样做的问题:
df.iloc[:,1:3]
明显胜过:
df.iloc[:,[1,2]]
有趣的是,在这种情况下,.loc具有与.iloc相同的性能效果。
我们的第一个大提示是以下代码:
df.iloc[:,1:3] is df.iloc[:,[1,2]]
False
这些给出相同的结果,但是是不同的对象。我已经进行了深入研究,以找出区别。我无法在互联网上或我的书库中找到对此的参考。
看一下源代码,我们可以开始看到一些区别。我指的是indexing.py。
在_iLocIndexer类中,我们可以发现熊猫正在做一些额外的工作,以便在iloc切片中列出。
马上,我们在检查输入时会遇到这两个区别:
if isinstance(key, slice):
return
vs。
elif is_list_like_indexer(key):
# check that the key does not exceed the maximum size of the index
arr = np.array(key)
l = len(self.obj._get_axis(axis))
if len(arr) and (arr.max() >= l or arr.min() < -l):
raise IndexError("positional indexers are out-of-bounds")
这是否足以导致性能下降?我不知道。
尽管.loc稍有不同,但是使用值列表时它也会降低性能。在index.py中查看def _getitem_axis(self,key,axis = None):->在类_LocIndexer(_LocationIndexer)中:
is_list_like_indexer(key)的用于处理列表输入的代码段很长,其中包括很多开销。它包含注释:
# convert various list-like indexers
# to a list of keys
# we will use the *values* of the object
# and NOT the index if its a PandasObject
在处理值列表或整数列表时,肯定会有足够的额外开销,然后直接引导切片导致处理延迟。
其余代码超出了我的工资等级。如果有人可以欣赏和欣赏,那将是非常受欢迎的
答案 1 :(得分:2)
我发现这可能植根于numpy。
numpy有两种索引:
根据文档
高级索引总是返回数据的副本(与 返回视图的基本切片)。
因此,如果您检查
a=df.values
%timeit -n2 a[:,0:3]
%timeit -n2 a[:,[0,1,2]]
您有
The slowest run took 5.06 times longer than the fastest. This could mean that an intermediate result is being cached.
1.57 µs ± 1.3 µs per loop (mean ± std. dev. of 7 runs, 2 loops each)
188 ms ± 2.17 ms per loop (mean ± std. dev. of 7 runs, 2 loops each)
与熊猫数据框非常相似