通过结合numpy数组和元组列表来有效地创建python词典列表

时间:2020-07-02 19:00:27

标签: python arrays numpy

我正在尝试通过组合元组列表和1d numpy数组来找到一种创建字典的时间和内存性能方法。因此,每本词典应具有以下结构:

Default Case

元组列表如下:

{"id": <first elem from tuple>, "name": <second elem from tuple>, "text": <third elem from tuple>, "value": <elem from array>}

另外,numpy数组的元素数量与列表相同,并且包含类型为np.float16的元素:

list_of_tuples = [("id1", "name1", "text1"), ("id2", "name2", "text2")]

此外,所有NaN值都将被过滤掉。以上示例的结果应为:

value_array = np.ndarray([0.42, np.nan])

我确实可以像这样工作:

{"id": "id1", "name": "name1", "text": "text1", "value": 0.42}

但是,对于许多条目而言,这太慢了,使用索引从列表中获取条目感觉很糟糕/效率低。

2 个答案:

答案 0 :(得分:1)

您绝对可以不必显式使用索引即可工作。这样可以改善性能。

value_array_indices = np.argwhere(~np.isnan(value_array)) 
list_of_tuples = np.array(list_of_tuples)[value_array_indices[0]]
value_array = value_array[value_array_indices[0]]
[{"id": x[0], "name": x[1], "text": x[2], "value": v} for x,v in zip(list_of_tuples, value_array)]

答案 1 :(得分:1)

看起来像是有人在我写这篇文章时发布了类似的解决方案,但由于测得的时间安排和一些解释,我还是会发布它。

使用下面建议的代码,并使用长度为一百万的测试输入(包括单个NaN),与问题代码相比,我发现它可以减少30%的时间。

Time: 0.3486933708190918
{'id': 'id0', 'name': 'name0', 'text': 'text0', 'value': 0.0} {'id': 'id999999', 'name': 'name999999', 'text': 'text999999', 'value': 999999.0} 999999
Time: 1.2175893783569336
{'id': 'id0', 'name': 'name0', 'text': 'text0', 'value': 0.0} {'id': 'id999999', 'name': 'name999999', 'text': 'text999999', 'value': 999999.0} 999999

我认为这里的区别是 partial 不必索引元组列表,但我怀疑其中很大一部分不必为每个实例都实例化zip对象元件。您每次都在处理少量具有相同名称的字典键,因此您实际上并不需要zip在此提供的灵活性,而仅从一个简单的显式表达式创建字典就更直接了。

zip(list_of_tuples, value_array)显然只为整个操作创建了一个zip对象,所以并不重要。)

我也建议在这里使用from math import isnan,而不是每次都进行属性查询以获得math.isnan,尽管事实证明差异并不重要。

from math import isnan
import numpy as np
import time

# construct some test data
n = 1000000
value_array = np.arange(n, dtype=np.float)
value_array[n // 2] = np.nan
list_of_tuples = [(f"id{i}", f"name{i}", f"text{i}")
                  for i in range(len(value_array))]

# timings for suggested alternative method
t0 = time.time()
l = [{"id": t[0],
      "name": t[1],
      "text": t[2],
      "value": v}
     for t, v in zip(list_of_tuples, value_array) if not isnan(v)]
t1 = time.time()
print("Time:", t1 - t0)
print(l[0], l[-1], len(l))

# timings for the method in the question
t0 = time.time()
l = \
[
    dict(
        dict(zip(["id", "name", "text"], list_of_tuples[index])),
        **{"value": value},
    )
    for index, value in enumerate(value_array)
    if not (isnan(value))
]
t1 = time.time()
print("Time:", t1 - t0)
print(l[0], l[-1], len(l))

也尝试并拒绝了:使用

创建一个not isnan值的布尔数组
not_isnan_array = np.logical_not(np.isnan(value_array))

,然后在列表理解中可以执行以下操作:

... for t, v, not_isnan in zip(list_of_tuples, value_array, not_isnan_array) if not_isnan

但是它对时间几乎没有影响,因此不能证明额外的内存使用情况。

更新

对混合版本的进一步实验(在问题的原始版本与建议的替代版本之间)表明,正如我所怀疑的,大部分差异来自避免在每次迭代中创建zip对象。避免显式索引元组列表仅占加速的一小部分。

相关问题