我有一个字典d
,其中包含大约500个主键(name1
,name2
等)。每个值本身都是一个小字典,有5个键,分别叫ppty1
,ppty2
等,而相应的值是浮点数转换为字符串。
我希望比以前更快地提取数据,基于['name1', 'ppty3','ppty4']
形式的列表列表(name1
可以由任何其他nameX
和ppty3
和ppty4
可以是任何其他pptyX
)。
在我的应用程序中,我有很多词典,但它们的区别仅在于ppty1
,...,ppty5
字段的值。所有键都是“静态的”。我不在乎是否有一些初步操作,我只想要一个字典的处理时间,理想情况下,比现在快得多。我的糟糕实现,包括循环遍历每个字段大约需要3毫秒。
以下是生成d
和fields
的代码;这只是模拟虚拟数据,不需要改进:
import random
random.seed(314)
# build dictionary
def make_small_dict():
d = {}
for i in range(5):
key = "ppty" + str(i)
d[key] = str(random.random())
return d
d = {}
for i in range(100):
d["name" + str(i)] = make_small_dict()
# build fields
def make_row():
line = ['name' + str(random.randint(0,100))]
[line.append('ppty' + str(random.randint(0,5))) for i in range(2)]
return line
fields = [0]*300
for i in range(300):
fields[i] = [make_row() for j in range(3)]
例如,fields[0]
返回
[['name420', 'ppty1', 'ppty1'],
['name206', 'ppty1', 'ppty2'],
['name21', 'ppty2', 'ppty4']]
所以输出的第一行应该是
[[d['name420']['ppty1'], d['name420']['ppty1'],
[d['name206']['ppty1'], d['name206']['ppty2']],
[d['name21']['ppty2'], d['name21']['ppty4']]]]
我的解决方案:
start = time.time()
data = [0] * len(fields)
i = 0
for field in fields:
data2 = [0] * 3
j = 0
for row in field:
lst = [d[row[0]][key] for key in [row[1], row[2]]]
data2[j] = lst
j += 1
data[i] = data2
i += 1
print time.time() - start
我的主要问题是,如何改进我的代码?另外几个问题:
data
的某些条目的基本操作:您是否建议将提取的值直接存储在np.array中?fields
有一些冗余行,例如['name1', 'ppty3', 'ppty4']
)?i += 1
这样的事情需要一点时间,我该如何避免它们呢?答案 0 :(得分:1)
这很难阅读,所以我开始把功能分解成功能。然后我可以测试,看看是否只使用列表理解。它已经更快了,超过10000次运行与timeit的比较表明,这段代码的运行时间大约是原始代码的64%。
在这种情况下,我保留列表中的所有内容以强制执行,因此它可以直接比较,但您可以使用生成器或映射,并且将计算推回到数据实际消耗时。
def row_lookup(name, key1, key2):
return (d[name][key1], d[name][key2]) # Tuple is faster to construct than list
def field_lookup(field):
return [row_lookup(*row) for row in field]
start = time.time()
result = [field_lookup(field) for field in fields]
print(time.time() - start)
print(data == result)
# without dupes in fields
from itertools import groupby
result = [field_lookup(field) for field, _ in groupby(fields)]
仅将结果分配行更改为:
result = map(field_lookup, fields)
并且运行时变得可以忽略不计,因为map是一个生成器,所以在你询问结果之前它实际上并不会计算数据。这不是一个公平的比较,但如果您不打算消费所有数据,那么您可以节省时间。将函数中的列表推导更改为生成器,您也可以获得相同的好处。在这种情况下,多处理和asyncio没有改善性能时间。
如果您可以更改结构,则可以将字段预处理为仅包含行[['namex', 'pptyx', 'pptyX']..]
的列表。在这种情况下,您可以将其更改为单个列表推导,这使您可以将其降低到原始运行时的大约29%,忽略预处理以缩小字段。
from itertools import groupby, chain
slim_fields = [row for row, _ in groupby(chain.from_iterable(fields))]
results = [(d[name][key1], d[name][key2]) for name, key1, key2 in slim_fields]
在这种情况下,结果只是一个包含值的元组列表:[(value1, value2)..]