对于循环 pandas 和 numpy:性能

Posted

技术标签:

【中文标题】对于循环 pandas 和 numpy:性能【英文标题】:For loop pandas and numpy: Performance 【发布时间】:2019-02-13 00:08:59 【问题描述】:

我编写了以下 for 循环。主要思想是,在“A_D”列中每次出现“D”时,它都会查找应该发生某些特定条件的所有可能情况。验证所有条件后,将一个值添加到列表中。

a = []
for i in df.index:
    if df['A_D'][i] == 'D':
         if df['TROUND_ID'][i] == '        ':
             vb = df[(df['O_D'] == df['O_D'][i])
             & (df['A_D'] == 'A' )
             & (df['Terminal'] == df['Terminal'][i])
             & (df['Operator'] == df['Operator'][i])]

            number = df['number_ac'][i]
            try: ## if all the conditions above are verified a value is added to a list
                x = df.START[i] - pd.Timedelta(int(number), unit='m')
                value = vb.loc[(vb.START-x).abs().idxmin()].FlightID
            except: ## if are not verified, several strings are added to the list
                value = 'No_link_found'
        else:
            value = 'Has_link'
    else:
        value = 'IsArrival'
a.append(value)

我的主要问题是 df 有数百万行,因此这个 for 循环太耗时了。是否有不需要使用 for 循环的矢量化解决方案?

【问题讨论】:

这看起来可能是矢量化的,但是你能提供一个minimal reproducible example吗? 应该始终避免 Pandas 中的循环。在上述问题中,您似乎可以通过一次过滤来避免迭代。 感谢您的回答。我的问题是我不知道如何将此代码改编为一个小例子。我试图尽可能减少它。将尝试更多地编辑代码。 @mad_ 这不是普遍真理。这是一个有用的概括,但并不总是正确的。 @mad_,您能澄清一下“立即过滤”是什么意思吗? 【参考方案1】:

一组初始改进:使用apply 而不是循环;在 df["A_D"] == "A" 所在行的开头创建第二个数据框;并将值向量化x

arr = df[df["A_D"] == "A"]
# if the next line is slow, apply it only to those rows where x is needed
df["x"] = df.START - pd.Timedelta(int(df["number_ac"]), unit='m')

def link_func(row):
    if row["A_D"] != "D":
        return "IsArrival"
    if row["TROUND_ID"] != "        ":
        return "Has_link"
    vb = arr[arr["O_D"] == row["O_D"]
             & arr["Terminal"] == row["Terminal"]
             & arr["Operator"] == row["Operator"]]
    try:
        return vb.loc[(vb.START - row["x"]).abs().idxmin()].FlightID
    except:
        return "No_link_found"            

df["a"] = df.apply(link_func, axis=1)

使用apply 是apparently more efficient,但不会自动向量化计算。但是,根据df 的每一行在arr 中查找一个值本质上是耗时的,但是它的实现效率很高。考虑是否可以以某种方式将原始数据帧的两个部分(分别为df["A_D"] == "A"df["A_D"] == "D")重新整形为宽格式。

编辑:您可以通过将查询字符串存储在df 中来加快arr 的查询速度,如下所示:

df["query_string"] = ('O_D == "' + df["O_D"] 
                    + '" & Terminal == "' + df["Terminal"] 
                    + '" & Operator == "' + df["Operator"] + '"')
def link_func(row):
    vb = arr.query(row["query_string"])
    try:
        row["a"] = vb.loc[(vb.START - row["x"]).abs().idxmin()].FlightID
    except:
        row["a"] = "No_link_found"

df.query('(A_D == "D") & (TROUND_ID == "        ")').apply(link_func, axis=1)

【讨论】:

.apply比使用itertuples的循环更有效 感谢@Stuart 的新代码。它真的很有帮助。您知道如何更改代码,以便每次调用函数时都会更改“arr”数据框中的一个值吗?实际上,更改 df 数据框中的一个单元格很简单。但是,我不知道如何(或者即使有可能)更改“arr”数据框中的值。 我认为您可以在链接函数中以通常的方式更改 arr 中的值,但这可能会进一步减慢速度。

以上是关于对于循环 pandas 和 numpy:性能的主要内容,如果未能解决你的问题,请参考以下文章

NumPy和Pandas常用库

总结--- Numpy和Pandas库常用函数

无法在嵌套循环中使用 pandas 附加更大的数据帧。如何更改为 numpy 向量化?

Pandas 掩码 / where 方法与 NumPy np.where

pandas 常见函数的使用

4numpy+pandas速查手册