Python 对DataFrame元素的访问速度惊人地慢
考虑下面的例子Python 对DataFrame元素的访问速度惊人地慢,python,pandas,Python,Pandas,考虑下面的例子 x={f“b{k}:10000*[None]表示范围(10)内的k} 对于x.keys()中的列: 对于范围(10000)内的i: x[列][i]=i%4#这里有一些小的计算 x=pd.DataFrame(x) 在我的电脑上大约60毫秒就完成了。类似的事情可以直接在熊猫的数据框上完成: x=pd.DataFrame(索引=范围(10000),列=[f“b{i}”表示范围(10)中的i),dtype=np.int32) 对于x.columns中的列: 对于范围(10000)内的i
x={f“b{k}:10000*[None]表示范围(10)内的k}
对于x.keys()中的列:
对于范围(10000)内的i:
x[列][i]=i%4#这里有一些小的计算
x=pd.DataFrame(x)
在我的电脑上大约60毫秒就完成了。类似的事情可以直接在熊猫的数据框上完成:
x=pd.DataFrame(索引=范围(10000),列=[f“b{i}”表示范围(10)中的i),dtype=np.int32)
对于x.columns中的列:
对于范围(10000)内的i:
x、 loc[i,column]=i%4#这里有一些小的计算
这个片段大约在11.8秒后结束。这种操作是否可以直接在数据帧上执行而不会对性能造成重大影响
更新:
使用.at
而不是.loc
可以显著提高性能,但与Python的dict相比仍然要慢得多
x = pd.DataFrame(index=range(10000), columns=[f"b{i}" for i in range(10)], dtype=np.int32)
for column in x.columns:
for i in range(10000):
x.at[i, column] = i % 4
这将在大约1.2秒内完成。这是熊猫的预期。在pandas中执行这样的单个操作肯定会很慢,甚至要开始执行实际的“工作”,还需要几个Python层,例如几个Python级别的函数。这是我的建议 请注意,这是mixin的一部分,它已经可以减慢速度了。但更重要的是,它返回
\u AtIndexer
的一个实例,即
这时,您终于找到了一个\uuu getitem\uuu
方法,但即便如此,还是要看看它需要做什么:
def __getitem__(self, key):
if self.ndim == 2 and not self._axes_are_unique:
# GH#33041 fall back to .loc
if not isinstance(key, tuple) or not all(is_scalar(x) for x in key):
raise ValueError("Invalid call for scalar access (getting)!")
return self.obj.loc[key]
return super().__getitem__(key)
所以它是Python级别的条件,但标准情况实际上只是
return super().__getitem__(key)
因此,另一个Python级别的函数调用。它有什么作用
所以更多的Python级条件,一些其他Python级方法调用
我们可以继续挖掘,看看self.obj.\u get\u value
做了什么,这是在其他一些基类中实现的,但我认为您现在已经理解了这一点
在list/dict中,一旦通过了初始方法解析,就进入了C层,它在那里完成了所有的工作。在pandas中,在Python层中执行了大量的开销,在它被推到numpy
之前,希望最终能够实现批量操作的速度。不过,它没有希望击败在内置python数据结构上完成的操作,而内置python数据结构是围绕编译代码的更薄的包装。pandas的性能因各种方法调用的上千次削减、Python内部簿记逻辑、
编辑:我注意到,我实际上经历了
\uuuuuGetItem\uuuuuuuuuu
逻辑,但要点仍然代表\uuuuuuuSetItem\uuuuuuuuuu
,事实上,中间步骤似乎涉及更多的工作。您是否尝试执行x%4
?您可以在这里阅读coldspeed的答案,它的经验法则很好:数据帧上的循环通常很慢,如果可能,请使用矢量化函数。@Bharathi%4
只是一个虚拟操作,以证明计算不是问题。链接似乎回答了计算价值本身的问题。此问题中的问题似乎与单个项目访问相关@广宏,这不是太慢了吗?
return super().__getitem__(key)
def __getitem__(self, key):
if not isinstance(key, tuple):
# we could have a convertible item here (e.g. Timestamp)
if not is_list_like_indexer(key):
key = (key,)
else:
raise ValueError("Invalid call for scalar access (getting)!")
key = self._convert_key(key)
return self.obj._get_value(*key, takeable=self._takeable)