python速度处理每行VS block

标签 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

现在这似乎太慢了,我想知道以适合内存的块处理它是否会更快。会更快吗?如果是,为什么?

谢谢你的帮助。

最佳答案

循环优化

如果您需要获得一些速度:

  • 可以肯定,您确实需要加速(否则您会在毫无值(value)的任务上花费太多时间)
  • 从循环开始
  • 检查,是否可以防止某些循环
  • 优化/删除循环内的指令
  • 每条指令计数
  • 每个引用计数

  • 这是我的优化代码草稿(未测试):
    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
    

    收集独特的值(value)

    您将 dict 与唯一值列表一起使用:
    unique = {key: [] for key in categorical_keys}
    

    并将其作为列表项添加到它的唯一值(在循环内发生):
    if key in categorical_keys:
        if row[key] not in unique[key]:
            unique[key].extend([value])
    

    如果您直接将值添加到 set 中,您可以安全地进行一些测试(如果该值存在于列表中) -
    集合会照顾,那里只收集唯一值。

    使用 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
    

    如果您的 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
    

    重用一次计算值

    您的代码反复计算该值:
    float(value)
    

    做一次并重复使用。

    还有 mean[key]值一次计算并设置为means[key] , 和
    两行后再次使用该值。更好地将值存储在局部变量中
    并使用它两次。任何查找(如 means[key] )都需要付出一些代价。

    捕获异常通常比值测试更快

    您的代码测试值不为空:
            elif key in numerical_keys:
                if value:
                    # something here
    

    您可以将其替换为直接与值一起使用的代码。如果值错误,它将
    失败和异常 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
    

    你能阻止 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
    

    分块处理数据

    关于分块处理:
  • 它肯定会增加一些复杂性
  • 如果小心完成,它可能会加快程序速度
  • 它可能会减慢速度或提供错误的结果

  • 分块可以提高速度的地方

    读取更大的文件

    这听起来很明显,但图书馆已经解决了它。预计会有轻微改善或没有改善。

    分块获取 CSV 记录

    我不知道 csv.reader 的方法或 csv.DictReader允许得到
    直接记录块。你必须自己做。这是可能的,我
    建议使用 itertools.groupby .

    不要指望它自己加速(它会减慢一点),但它是以后其他基于块的加速的先决条件。

    将一组值添加到集合中

    代码(当前)将值逐个添加到集合中。如果你用大块做
    值(越多越好),它会更快,因为每个 python 调用都有
    一些小的开销。

    计算平均值和 sds 值

    您可以利用 statistics包,这很可能有
    优化的代码(但它似乎是在纯 python 中)。

    无论如何,当您要分块处理数据时,很简单statistics.mean不会为您工作,否则您将不得不汇总
    结果以某种方式结合在一起(如果可能的话)。

    如果您自己计算值,通过仔细编码,您可以获得一些
    加速,主要基于这一事实,您可以直接在一个块中获得值,并且
    不必在每个循环中逐个取消引用值。

    大块结论

    对我来说分块优化似乎太复杂了,很难
    预测,如果它带来任何值(value)。

    关于python速度处理每行VS block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37039641/

    相关文章:

    angular - 应用程序正在生成我的延迟加载模块的一部分,前缀为 default ~ .. 甚至在它被调用之前

    python - 如何将输入值与mysql数据库进行比较,没有匹配数据时自动添加 '0'和 '1'

    Python Webdriver 多线程

    php - 加速 PHP 应用程序

    javascript - 未定义参数的性能损失

    algorithm - 确定时间复杂度

    python - 在 4 个项目的切片中分页 python 列表

    javascript - PeerJS/WebRTC 连接在快速 block 传输时失败

    python - 如何将 pandas DataFrame 结果转换为用户定义的 json 格式

    python - 在自定义用户的配置文件中发现不需要的随机值