使用 Numpy 加速数据转换

61 阅读3分钟

给定一个由第三方提供的不可更改输入数据,具体结构如下:数据是一个 4 元组列表,其中每个 4 元组表示一个出击。每个出击的第一个元素是一个长度为 1 到 5 的类别列表,从总共 20 个可能类别中选取(不重复);第二个元素是参与人数;第三个是一个表示出击开始的 datetime 对象;第四个元素是一个表示出击结束的 datetime 对象。

现在需要将这些数据转换成以下格式:对于每个类别,需要计算 (a) 此类别的出击次数,(b) 总时间,(c) 每出击平均时间,(d) “工时”总数,即每个出击的持续时间乘以同一出击的参与者人数的总和,以及 (e) 每出击平均“工时”。

执行代码如下:

huake_00066_.jpg

def transform(data):
    t = defaultdict(lambda: (0, 0, 0))

    for row in data:
        delta = row[3] - row[2]
        hours = delta.days * 24 + delta.seconds / 3600
        manHours = row[1] * hours
        for cat in row[0]:
            t[cat] = (t[cat][0] + 1, t[cat][1] + hours, t[cat][2] + manHours)

    return {k: (v[0], v[1], v[1] / v[0], v[2], v[2] / v[0]) for k, v in t.items()}

使用以下代码进行性能分析:

cats = [_ for _ in range(20)]
for test in range(1000):
    data = [(random.sample(cats, random.randint(1, 5)), random.randint(2, 40), datetime.datetime(2013, 1, 1, 8),
             datetime.datetime(2013, 1, 1, 9)) for _ in range(1000)]
    transform(data)

使用-m cProfile.

希望通过使用itertools模块来优化性能,但不知道如何充分利用它。

问题:

  1. 如何优化转换函数以提高速度?
  2. itertools 可以如何帮助实现这一目标?

2、解决方案

itertools 模块提供了强大的工具来处理迭代对象,可以帮助优化转换函数的性能。

2.1 使用 Counter 对象

A. 可以使用 Counter 对象来存储每个类别的出击次数,总时间和“工时”总数。这将比使用默认字典更快,因为 Counter 对象已经针对计数进行了优化。

2.2 使用 NumPy

A. NumPy 提供了高效的数组操作功能,可以用来加速计算总时间和“工时”总数。

以下是被优化的转换函数,使用了 Counter 对象和 NumPy:

import numpy as np
from collections import Counter

def transform2(data):
    cats = Counter()
    total_time = np.zeros(20)
    man_hours = np.zeros(20)

    for row in data:
        delta = row[3] - row[2]
        hours = delta.days * 24 + delta.seconds / 3600
        manHours = row[1] * hours

        cats.update(row[0])
        total_time[row[0]] += hours
        man_hours[row[0]] += manHours

    return {k: (v, total_time[k], total_time[k] / v, man_hours[k], man_hours[k] / v) for k, v in cats.items()}

2.3 性能分析

使用以下代码进行性能分析:

cats = [_ for _ in range(20)]
for test in range(1000):
    data = [(random.sample(cats, random.randint(1, 5)), random.randint(2, 40), datetime.datetime(2013, 1, 1, 8),
             datetime.datetime(2013, 1, 1, 9)) for _ in range(1000)]
    transform2(data)

使用-m cProfile.

优化后的转换函数比原来的转换函数快得多。

以下是对原始转换函数和优化后的转换函数的性能比较:

函数时间 (秒)
transform2.027
transform20.159

优化后的转换函数比原来的转换函数快了 12 倍以上。