Rom*_*ain 5 python performance chunks
我试图在一个巨大的文件上执行非常简单的计算,例如计算某些列的标签数量或其他列的平均值和标准偏差.该文件太大,无法容纳在内存中,我目前正在每行处理它:
unique = {key: [] for key in categorical_keys}
means = {key: 0.0 for key in numerical_keys}
sds = {key: 0.0 for key in numerical_keys}
with open('input/train.csv', 'r') as read_file:
reader = csv.DictReader(read_file, delimiter=',', quotechar='|')
for i, row in enumerate(reader):
for key, value in row.iteritems():
if key in categorical_keys:
if row[key] not in unique[key]:
unique[key].extend([value])
elif key in numerical_keys:
if value:
means[key] = (means[key]*i + float(value))/(i+1)
if i > 1:
sds[key] = (sds[key]*(i-1) + (float(value)-means[key])**2)/i
Run Code Online (Sandbox Code Playgroud)
现在这似乎太慢了,我想知道是否可以更快地处理它可以适合内存的块.会更快吗?如果是,为什么?
谢谢你的帮助.
如果您需要获得一些速度:
这是我的优化代码草稿(未经测试):
from collections import defaultdict
unique = defaultdict(set)
means = {key: 0.0 for key in numerical_keys}
sds = {key: 0.0 for key in numerical_keys}
with open('input/train.csv', 'r') as read_file:
reader = csv.DictReader(read_file, delimiter=',', quotechar='|')
for i, row in enumerate(reader):
for key in categorical_keys:
unique[key].add(row[key])
for key in numerical_keys:
try:
# shall throw ValueError if None or empty string
value=float(row[key])
mean_val = (means[key]*i + value)/(i+1)
means[key] = mean_val
# following fails for i < = 1 with ZeroDivisionError
sds[key] = (sds[key]*(i-1) + (value-mead_val)**2)/i
except (ValueError, ZeroDivisionError):
pass
Run Code Online (Sandbox Code Playgroud)
您可以将 dict 与唯一值列表一起使用:
unique = {key: [] for key in categorical_keys}
Run Code Online (Sandbox Code Playgroud)
并向其添加唯一值作为列表项(发生在循环内):
if key in categorical_keys:
if row[key] not in unique[key]:
unique[key].extend([value])
Run Code Online (Sandbox Code Playgroud)
如果您直接将值添加到集合中,您可以安全地进行一些测试(如果列表中存在该值) - 该集合会照顾,只在那里收集唯一的值。
使用defaultdict
将确保您已经有一些空集,以防您使用任何尚未使用的密钥。
您的代码重复循环记录键并测试它们的类型,然后执行以下操作:
if key in categorical_keys:
if row[key] not in unique[key]:
unique[key].extend([value])
elif key in numerical_keys:
if value:
means[key] = (means[key]*i + float(value))/(i+1)
if i > 1:
sds[key] = (sds[key]*(i-1) + (float(value)-means[key])**2)/i
Run Code Online (Sandbox Code Playgroud)
如果您的categorical_keys
和numerical_keys
已正确设置为现有值,您可以保护这些测试。然后你可以直接循环已知的键名:
for key in categorical_keys:
unique[key].add(row[value])
for key in numerical_keys:
try:
# shall throw ValueError if None or empty string
value=float(row[value])
means[key] = (means[key]*i + value)/(i+1)
if i > 1:
sds[key] = (sds[key]*(i-1) + (value-means[key])**2)/i
except ValueError:
pass
Run Code Online (Sandbox Code Playgroud)
您的代码重复计算该值:
float(value)
Run Code Online (Sandbox Code Playgroud)
做一次并重复使用。
该mean[key]
值也被计算并设置到 中means[key]
,两行后再次使用该值。最好将值存储在局部变量中并使用它两次。任何查找(例如means[key]
)都会花费一些费用。
您的代码测试值是否不为空:
elif key in numerical_keys:
if value:
# something here
Run Code Online (Sandbox Code Playgroud)
您可以将其替换为直接与该值一起使用的代码。如果该值错误,它将失败并且异常ValueError
将被捕获并忽略。如果您设置了大部分值,这会加快速度。
try:
value=float(value)
means[key] = (means[key]*i + value)/(i+1)
if i > 1:
sds[key] = (sds[key]*(i-1) + (value-means[key])**2)/i
except ValueError:
pass
Run Code Online (Sandbox Code Playgroud)
if i > 1:
测试吗?此条件在大多数情况下都是正确的,但您需要在每个循环中测试它。如果你找到一种方法(我没有)来阻止这个测试,你也会更快地得到它。
正如您所建议的,我们可以通过捕获ZeroDivisionError
i <= 1 来解决它:
try:
# shall throw ValueError if None or empty string
value=float(value)
means[key] = (means[key]*i + value)/(i+1)
# for i <= 1 shall raise ZeroDivisionError
sds[key] = (sds[key]*(i-1) + (value-means[key])**2)/i
except (ValueError, ZeroDivisionError):
pass
Run Code Online (Sandbox Code Playgroud)
关于块处理:
这听起来很明显,但图书馆已经解决了这个问题。预计会有轻微改善或没有改善。
我不知道有一种方法csv.reader
或csv.DictReader
允许直接获取记录块。你必须自己做。这是可能的,我建议使用itertools.groupby
.
不要指望它本身会加速(它会减慢速度),但这是以后其他基于块的加速的先决条件。
该代码(当前)将值逐一添加到集合中。如果你使用大量的值(越多越好),它会更快,因为每个 python 调用都有一些小的开销。
你可以利用statistics
package,它可能有优化的代码(但它似乎是纯 python 的)。
无论如何,当您要分块处理数据时,简单的方法
statistics.mean
对您不起作用,或者您必须以某种方式将结果聚合在一起(如果可能的话)。
如果您自己计算该值,通过仔细编码,您可以获得一些加速,主要基于这样的事实,您可以直接在一个块中获取值,而不必在每个循环中逐个值地取消引用。
对我来说,分块优化似乎太复杂了,而且很难预测它是否能带来任何价值。