如何使这个 for 循环更快?

Posted

技术标签:

【中文标题】如何使这个 for 循环更快?【英文标题】:How to make this for loop faster? 【发布时间】:2019-07-25 23:11:12 【问题描述】:

我知道与其他语言相比,python 循环本身相对较慢,但是当使用正确的函数时,它们会变得更快。 我有一个名为“acoustics”的熊猫数据框,其中包含超过 1000 万行:

print(acoustics)
                        timestamp            c0  rowIndex
0        2016-01-01T00:00:12.000Z  13931.500000   8158791
1        2016-01-01T00:00:30.000Z  14084.099609   8158792
2        2016-01-01T00:00:48.000Z  13603.400391   8158793
3        2016-01-01T00:01:06.000Z  13977.299805   8158794
4        2016-01-01T00:01:24.000Z  13611.000000   8158795
5        2016-01-01T00:02:18.000Z  13695.000000   8158796
6        2016-01-01T00:02:36.000Z  13809.400391   8158797
7        2016-01-01T00:02:54.000Z  13756.000000   8158798

还有我写的代码:

acoustics = pd.read_csv("AccousticSandDetector.csv", skiprows=[1])
weights = [1/9, 1/18, 1/27, 1/36, 1/54]
sumWeights = np.sum(weights)
deltaAc = []
for i in range(5, len(acoustics)):
    time = acoustics.iloc[i]['timestamp']
    sum = 0
    for c in range(5):
        sum += (weights[c]/sumWeights)*(acoustics.iloc[i]['c0']-acoustics.iloc[i-c]['c0'])
    print("Row " + str(i) + " of " + str(len(acoustics)) + " is iterated")
    deltaAc.append([time, sum])

deltaAc = pd.DataFrame(deltaAc)

这需要大量的时间,我怎样才能使它更快?

【问题讨论】:

很好地删除print 将是一个很好的第一步 @SuperStew 我想跟踪进度 你运行的是什么版本的python? acoustics 是多久? @RehimAlizadeh 那么我只会打印 100k 的倍数之类的东西 通常在使用pandas 时,您不想使用for 循环。看起来您正在根据数据框其余部分的某些标准制作一个新系列,对吗?这是 an example 使用 pandas 而不必使用 for 循环。 【参考方案1】:

您可以使用pandas 中的diff 并为数组中的每一行创建所有差异,然后与您的weigths 相乘,最后在轴1 上乘以sum,例如:

deltaAc = pd.DataFrame('timestamp': acoustics.loc[5:, 'timestamp'], 
                       'summation': (np.array([acoustics.c0.diff(i) for i in range(5) ]).T[5:]
                                               *np.array(weights)).sum(1)/sumWeights)

你得到的值与我用你的代码得到的值相同:

print (deltaAc)
                  timestamp  summation
5  2016-01-01T00:02:18.000Z -41.799986
6  2016-01-01T00:02:36.000Z  51.418728
7  2016-01-01T00:02:54.000Z  -3.111184

【讨论】:

谢谢你的解决方法,结果和我写的代码不一样 @RehimAlizadeh 确实,我的代码中有一个误写错误(df 而不是 acoustics),但除此之外,它在列中给出的值与总和与您的代码相同 @RehimAlizadeh 所以 diff 的值 i 允许一次计算每行与之前 i 行的值的所有差异。 np.array 中的列表压缩创建一个数组,其中每一行代表数据帧中前 i 行的行与行之间的差异。 T 转置行和列,[5:] 是不选择前 5 行来执行与您的代码 for i in range(5, len(acoustics)) 等效的操作。 *np.array(weights) 将每列相乘,即第 i 个差异,以矢量化方式具有良好的权重 @RehimAlizadeh 最后,sum 会将同一行上的所有值相加,您可以将其除以总权重以标准化结果。所有这些代码都用于从列总和创建值并创建具有良好时间戳的结果数据帧。我希望它有所帮助。如果您需要更多信息,请告诉我 非常感谢您的解释,非常有帮助!【参考方案2】:

Dataframes 有一个很好的方法rolling 用于构造和应用窗口转换;所以,你根本不需要循环:

# df is your data frame
window_size = 5
weights = pd.np.array([1/9, 1/18, 1/27, 1/36, 1/54])
weights /= weights.sum()
df.loc[:,'deltaAc'] = df.loc[:, 'c0'].rolling(window_size).apply(lambda x: ((x[-1] - x)*weights).sum())

【讨论】:

感谢您的解决方案,速度超快,但结果不一样【参考方案3】:

第一次优化,weights[c]/sumWeights 可以在循环外完成。

weights_array = np.array([1/9, 1/18, 1/27, 1/36, 1/54])
sumWeights = np.sum(weights_array)
tmp = weights_array / sumWeights
...
        sum += tmp[c]*...

我不熟悉 pandas,但是如果您可以将列提取为 1D numpy 数组,那对您来说会很棒。它可能看起来像:

# next lines to be tested, or find the correct way of extracting the column
c0_column = acoustics[['c0']].values
time_column = acoustics[['times']].values
...
sum = numpy.zeros(shape=(len(acoustics)-5,))
delta_ac = []
for c in range(5):
    sum += tmp[c]*(c0_column[5:]-c0_column[5-c:len(acoustics)-c])

for i in range(len(acoustics)-5):
    deltaAc.append([time[5+i], sum[i])

【讨论】:

这段代码并不完全符合我的要求,我想找到(权重相乘(给定点-前一点(从1到5)))的总和(从1到5) 我目前无法测试代码,所以如果有人想根据我的答案来修复错误并让它产生与原始循环完全相同的结果,我会'不介意 ;) 处理索引总是很棘手。但总体思路是,for 循环通常可以替换为数组操作(计算差异时有足够的切片)。

以上是关于如何使这个 for 循环更快?的主要内容,如果未能解决你的问题,请参考以下文章

如何使循环计算更快

什么是更快的嵌套循环或多个循环? [关闭]

for循环倒数到0会更快吗?

R quantstrat 代码中的 While 循环 - 如何使其更快?

如何使代码在 FOR 循环中运行?而不是那么多的IF

循环速度更快,限制固定