熊猫数据框-为什么此代码这么慢?

时间:2018-07-13 00:51:58

标签: performance pandas

我正在尝试对791行130列的数据框进行计算。

下面的代码显示了一个函数,该函数采用具有791种化合物的数据框。每列中的条目包含给定化合物中给定元素的分数。例如:

Material ... Zr  ... Si   ...  O    ... 

SiO2     ... 0.0 ... 0.33 ...  0.66 ...

有791种化合物,以下函数包含在整个数据帧中,将每个元素与具有元素原子权重的csv文件进行比较,并将其求和。

def add_molar_mass(DF):

  el=pd.read_csv(path)

  # el is a dataframe with a list of 40 elements with their atomic mass

  DF["Molar Mass"]=np.nan

  # creates an empty column "Molar Mass"

  for j in range(len(DF)):
     val=0
      for i in range(len(el)):
         val+=DF[el['symbol'].iloc[i]].iloc[j]*el['atomicMass'].iloc[i]

         # el and DF share 41 columns.
         # el['symbol'].iloc[i] gives the name of the i th element say "Ca"
         # DF[el['symbol'].iloc[i]].iloc[j] gives the fraction of Ca for the 
         # jth member of DF

         DF["Molar Mass"].iloc[j]=round(val,3)

但是,这太慢了。 DF的每一行需要一秒钟才能执行。 (我使用time()为其计时)

是否有明显会减慢此代码速度的东西,如果有的话,有人可以提出一些修改建议吗?


更新:

感谢您的所有答复:

新代码段:

def add_molar_mass(DF):

  el=pd.read_csv(path)

  # el is a dataframe with a list of 40 elements with their atomic mass

  DF["Molar Mass"]=np.nan

  masses = np.array(el['atomicMass'], dtype=float)
  DF["Molar Mass"] = ( DF[el['No']]*masses).sum(axis=1)

在几毫秒内执行。绝对是一个进步!

2 个答案:

答案 0 :(得分:1)

因为您要遍历每一行,所以这是最慢的方式...

不是熊猫的使用方式。

查看此博客文章,了解如何优化10倍至100倍:

https://engineering.upside.com/a-beginners-guide-to-optimizing-pandas-code-for-speed-c09ef2c6a4d6

帖子摘要,以防万一:

从最慢到最快的迭代速度:

  • 使用for.loc进行.iloc循环和值查找是最慢的方法。
  • 使用.iterrows()会更好
  • 使用.itertuples()iterrows()
  • 使用applylambda函数更快
  • pd.Series一起使用矢量化的速度更快(即,将序列传递给函数,而不是逐行应用函数
  • 将向量化与pd.Seriesdf.col.values)的值一起使用最快,因为valuesnp.array,可提供最佳性能。

答案 1 :(得分:1)

通常,我认为您最好考虑如何利用广播操作而不是使用显式循环。通常,这将使代码更清晰易读,并且可能更快(不保证。这要视情况而定)。

在开始进行广播之前,我将从symbol框架中取出atomicMassel列。以下是一个与数据形状相匹配的组合数据的示例(很确定这是您正在尝试执行的操作):

import numpy as np
import pandas as pd

columns = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne']
compounds = pd.DataFrame(np.random.random((20,10)), columns=columns)
el = pd.DataFrame(
  np.vstack((np.random.choice(columns, 5, replace=False), np.random.random(5))).transpose(),
  columns=['symbol', 'atomicMass']
)

masses = np.array(el['atomicMass'], dtype=float)
compounds['Molar Mass'] = (compounds[el['symbol']]*masses).sum(axis=1)