我正在尝试对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)
在几毫秒内执行。绝对是一个进步!
答案 0 :(得分:1)
因为您要遍历每一行,所以这是最慢的方式...
不是熊猫的使用方式。
查看此博客文章,了解如何优化10倍至100倍:
https://engineering.upside.com/a-beginners-guide-to-optimizing-pandas-code-for-speed-c09ef2c6a4d6
帖子摘要,以防万一:
从最慢到最快的迭代速度:
for
或.loc
进行.iloc
循环和值查找是最慢的方法。.iterrows()
会更好.itertuples()
比iterrows()
快apply
和lambda
函数更快pd.Series
一起使用矢量化的速度更快(即,将序列传递给函数,而不是逐行应用函数pd.Series
(df.col.values
)的值一起使用最快,因为values
是np.array
,可提供最佳性能。答案 1 :(得分:1)
通常,我认为您最好考虑如何利用广播操作而不是使用显式循环。通常,这将使代码更清晰易读,并且可能更快(不保证。这要视情况而定)。
在开始进行广播之前,我将从symbol
框架中取出atomicMass
和el
列。以下是一个与数据形状相匹配的组合数据的示例(很确定这是您正在尝试执行的操作):
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)