首页 > 数据库 > 数据库开发技术 > Python向量化:让你的程序运行速度爆炸性增长
循环对我们来说很自然,我们在几乎所有的编程语言中都了解过循环。因此,默认情况下,只要有重复操作,我们最开始想到的就是使用循环实现。但是当我们处理大量的迭代(比如数百万/数十亿行)操作时,使用循环无疑是一种犯罪。因为你可能会遇到程序执行了好几个小时后来才意识到这种方法根本行不通。这就是在Python中向量化(Vectorization)实现变得超级关键的地方,它就像是你的超级英雄,在关键时刻拯救你出困境😄😄😄
那么,什么是向量化呢❓
向量化(Vectorization)就是在数据集上实现(NumPy)数组操作的一种技术。在后台,它一次将操作应用于数组或序列的所有元素(不像for循环一次只能操作一行)。
在这篇文章中,我将用一些具体的示例告诉你我们可以很容易地用向量化取代Python循环,相信这将帮助您节省时间并提高编码技巧。
首先,我们将看一个基本示例——使用Python中的循环和向量化计算数字的总和。
由于可能出现前后两次运行所花时间不一样的情况,因此,处于严谨性和科学性,我们两种方法各计算10次,取平均值进行对比。
import time
times_loop = []
for n in range(10):
start_time = time.time()
total = 0
for item in range(1500000):
total += item
end_time = time.time()
time_consumed = end_time - start_time
print(f'No.{n+1}---Sum is: {total}; Time consumed: {round(time_consumed, 2)} seconds')
times_loop.append(time_consumed)
avg_time = round(sum(times_loop)/len(times_loop), 2)
print(f'Average time consumed: {avg_time} seconds')
import numpy as np
time_vec = []
for n in range(10):
start_time = time.time()
total = np.sum(np.arange(1500000), dtype='int64')
end_time = time.time()
time_consumed = end_time - start_time
print(f'No.{n+1}---Sum is: {total}; Time consumed: {round(time_consumed, 3)} seconds')
time_vec.append(time_consumed)
avg_time = round(sum(time_vec)/len(time_vec), 3)
print(f'Average time consumed: {avg_time} seconds')
Watch out:
使用向量化计算的方法中,必须显示说明数据类型为64位的整型,要么在 arange()方法中给定,要么在sum()方法中给定,否则计算结果会是负数。此外,两种说明数据类型的方式最终消耗的时间也不同,根据测试,在sum()方法中给定比在arange()方法中给定要快(0.001~0.002s)。虽然我们已经考虑到严谨性,采用多次运行的平均消耗时间,但是具体还是会因个人的环境配置而有所差异,所以如果你的消耗时间跟笔者不同,不要惊讶,只要差距在合理范围即可。
结果对比:从以上两种方法的运行结果可以看出,执行同样的计算操作,向量化方法的速度比循环快30倍。在处理pandas的DataFrame时,这种差异将会更加显著。
在数据科学中,在处理Pandas DataFrame时,开发人员使用循环+相应的数学操作来创建新的派生列。
在下面的示例中,我们可以看到,对于这样的用例,用矢量化替换循环是多么容易,且性能更佳。
创建数据框(DataFrame)我们将创建一个由500万行和4列构成的数据框,其值全部由1-50之间的随机数填充。
import numpy as np
import pandas as pd
df = pd.DataFrame(data=np.random.randint(1, 50, size=(5000000, 4)),
columns=['a', 'b', 'c', 'd'])
print(f'The shape of df: {df.shape}')
print(df.head())
接下来,我们想创建一个新列(‘ratio’),计算‘d’,‘c’两列的比率。
time_iterrow = []
for n in range(10):
start_time = time.time()
for idx, row in df.iterrows():
df.at[idx, 'ratio'] = 100 * (row['d'] / row['c'])
end_time = time.time()
time_consumed = end_time - start_time
print(f'No.{n+1}---Time consumed: {round(time_consumed, 2)} seconds')
time_iterrow.append(time_consumed)
avg_time = round(sum(time_iterrow)/len(time_iterrow), 2)
print(f'Average time consumed: {avg_time} seconds')
time_df = []
for n in range(10):
start_time = time.time()
df['ratio'] = 100 * (df['d'] / df['c'])
end_time = time.time()
time_consumed = end_time - start_time
print(f'No.{n+1}---Time consumed: {round(time_consumed, 2)} seconds')
time_df.append(time_consumed)
avg_time = round(sum(time_df)/len(time_df), 2)
print(f'Average time consumed: {avg_time} seconds')
结果对比:当数据集位DataFrame,且数据量更大时,向量化和循环方法之间的速度差异会及其显著(呈千倍扩增)。从上述运行结果可以看出,相同的操作,与循环相比,使用向量化实现的运行速度快了近5719倍。当然,因个人设备不同,运行的结果也会有所差异,不过二者的速度差异至少都应该在1000倍以上。
我们实现过许多需要使用“If-else”逻辑的操作。实际上,可以很容易地用Python中的向量化操作替换这些逻辑。
让我们通过下面的例子来更好地理解它(继续使用我们在示例2中创建的DataFrame):假设我们想基于现有列“a”上的一些条件创建一个新列“e”。
PS:出于时间考虑,我们仅执行一次对比结果,不再运行10次取平均值,因为那将花费特别长的时间(自己手动实践的同学可以继续跟前面一样运行多次取均值的方式👏👏👏)。
import time
start_time = time.time()
for idx, row in df.iterrows():
if row.a == 1:
df.at[idx, 'e'] = row.d
elif (row.a > 1) & (row.a <= 25):
df.at[idx, 'e'] = row.b - row.c
else:
df.at[idx, 'e'] = row.b + row.c
end_time = time.time()
print(f'Time consumed: {round(end_time - start_time, 2)} seconds')
start_time = time.time()
df['e'] = df['b'] + df['c']
df.loc[df['a'] <= 25, 'e'] = df['b'] - df['c']
df.loc[df['a'] == 0, 'e'] = df['d']
end_time = time.time()
print(f'Time consumed: {round(end_time - start_time, 2)} seconds')
结果对比:从上述执行结果可以看出,与带有
if-else语句的for循环相比,向量化操作的速度快了约1406倍。
深度学习需要我们求解多个复杂的方程,这也是数百万甚至数十亿行的问题。在Python中运行循环来求解这些方程速度是非常慢的,向量化通常是最优解决方案。
例如,在下面的多元线性回归方程中计算数百万行y的值:
我们就可以用向量化替代循环实现。
的值是通过使用与对应的数百万个值来求解上述方程来确定的(为了简单起见,我们只看一个简单的乘法步骤)。
创建数据集
# Setting initial values of m
m = np.random.rand(1, 5)
# Input values for 5000000 × 5
x = np.random.rand(5000000, 5)
y = pd.Series()
start_time = time.process_time()
for i in range(5000000):
total = 0
for j in range(5):
total += x[i][j] * m[0][j]
y[i] = total
end_time = time.process_time()
print(f'Computation time: {round(end_time - start_time, 3)} seconds')
⚠️ 使用for循环实现的方法我运行超过90分钟都没有结果,不知道是不是我的电脑问题。后续实践过的同学可以告诉我你花了多长时间😄😄😄
y = pd.Series()
start_time = time.process_time()
y = np.dot(x, m.T)
end_time = time.process_time()
print(f'Computation time: {round(end_time - start_time, 3)} seconds')
结果对比:虽然循环的方法没有得到确切的运行时间,但是至少90分钟,与此相比,向量化方法运行速度至少快了12328倍。这个差异可谓及其夸张了,并且随着数据量的增加,这种差异还会极速扩大。
Python中的向量化速度非常快,在处理非常大的数据集时,应该优先于循环。举个生活中的例子,循环方法就像一个人做100个人的事,而且只能逐一完成,而向量化操作就是100个人同时为一个人打工,这效果和效率显而易见,不可同日而语😮😮😮。
学会用向量化的思维去思考并解决问题,动手实践,久而久之,你的编码能力将会得到显著提升。并且能够极大地节省设备资源(时间、空间),用有限的资源做最成功的事。
. . .
如果你觉得这篇文章对你有用,请“点赞➕在看➕分享➕关注”支持我(关注我,不迷路🤭🤭🤭),你的点赞和关注就是我持续创作分享的动力,让更多的朋友发现并加入我们,共同学习和进步!
Thank you for reading!
- END -


原文作者:数据派探险家;如有侵权,请联系管理员删除。