用于映射复杂列表中字典值的方法



输入

我有一张非常复杂的单子。

total_aug_rule_path_list = 
[[[[['#1_0_0', '#2_0_0', '#3_0_0'], ['#1_0_1', '#2_0_1', '#3_0_1']],
[['#1_0_0', '#2_0_0', '#3_0_0'], ['#1_0_1', '#2_0_1', '#3_0_1']]],
[[['#1_1_0', '#2_1_0', '#3_1_0', '#4_1_0'],
['#1_1_1', '#2_1_1', '#3_1_1', '#4_1_1']]]]]

我有一本字典,把列表中的每个元素都作为关键字。

sym2id_dict = {
'#1_0_0': 1,
'#1_0_1': 2,
'#1_1_0': 3,
'#1_1_1': 4,
'#2_0_0': 5,
'#2_0_1': 6,
'#2_1_0': 7,
'#2_1_1': 8,
'#3_0_0': 9,
'#3_0_1': 10,
'#3_1_0': 11,
'#3_1_1': 12,
'#4_1_0': 13,
'#4_1_1': 14,}

我将把列表中的每个元素映射到字典的值。

输出

[[[[[1, 5, 9], [2, 6, 10]], [[1, 5, 9], [2, 6, 10]]],
[[[3, 7, 11, 13], [4, 8, 12, 14]]]]]

我尝试了以下操作,以尽可能少地使用for循环。

list(map(lambda proofpaths_to_goal : 
list(map(lambda proofpaths_to_template :
list(map(lambda proofpath :
list(map(lambda single_augment : list(map(lambda x : sym2id_dict[x], single_augment)),  
proofpath)), proofpaths_to_template)), proofpaths_to_goal)),total_aug_rule_path_list))

如果你能告诉我是否有比这种方法更容易或更可读的方法,我将不胜感激。

您可以将列表转换为字符串文字,并使用正则表达式替换字典中的相应字符串,然后将字符串文字转换回列表。

import re
import ast
s = str(total_aug_rule_path_list)   #converts to string literal
for element in re.findall(r'#d_d_d', s):
s = s.replace(element, str(sym2id_dict[element]))
s = s.replace("'", "")   #because each integer is a string
s = ast.literal_eval(s)   #converts string literal back to list, do NOT use eval(s)
print(s)

编辑:请注意,在任何语言(python、perl、js等(中使用eval()都是危险的,因为这会导致代码注入错误。为了安全起见,请使用ast.literal_eval()

输出

[[[[[1, 5, 9], [2, 6, 10]], [[1, 5, 9], [2, 6, 10]]],
[[[3, 7, 11, 13], [4, 8, 12, 14]]]]]

以下是一些替代方案:

嵌套列表理解

[[[[[sym2id_dict[l4] for l4 in l3] for l3 in l2] for l2 in l1] for l1 in l0] for l0 in total_aug_rule_path_list]

尽管可以说这并不容易阅读。

使用Numpy

此方法不适用于示例列表,因为numpy数组不能是不规则的数组(即,所有嵌套相等的列表都必须具有相同的长度(。然而,当你不使用粗糙的数组时,你可以这样做:

import numpy as np
total_aug_rule_path_list = [[
[[['#1_0_0', '#2_0_0', '#3_0_0'], ['#1_0_1', '#2_0_1', '#3_0_1']]],
[[['#1_1_0', '#2_1_0', '#3_1_0'], ['#1_1_1', '#2_1_1', '#3_1_1']]]
]]
sym2id_dict = {...} # your dict here
total_aug_rule_path_list_array = np.array(total_aug_rule_path_list)
print(np.vectorize(sym2id_dict.get)(total_aug_rule_path_list_array))

这将sym2id_dict.get函数应用于数组中的每个字符串。如果希望它在关键字不在字典中时引发错误,则可以将其更改为sym2id_dict._getitem__

编写自己的递归函数

在列表中重复和迭代

此函数重复出现,直到输入不是列表为止。这将适用于[1, [2, 3]]等列表。如果你想让它处理列表以外的事情,请参阅此处。

def vectorised_apply(f, values):
if isinstance(values,list):
return [vectorised_apply(f,value) for value in values]
else:
return f(values)
print(vectorised_apply(sym2id_dict.get, total_aug_rule_path_list))

固定递归深度

这种变化会重复到固定的深度,因此不需要isinstance检查:


def vectorised_apply_n(f, values, n=0):
if n == 0:
return f(values)
else:
return [vectorised_apply_n(f, value, n=n-1) for value in values]
print(vectorised_apply_n(sym2id_dict.get, total_aug_rule_path_list, 5))

如果你真的想要的话,你可以用itertools.accumulate的技巧把这个固定的递归深度函数变成一个表达式,但它很难理解。

from itertools import accumulate
print(
list(accumulate(
range(5),  # do 5 times because the list is nested 5 times
initial=lambda x: sym2id_dict[x], # base case: lookup in the dictionary
func=lambda rec, _: lambda xs: [rec(x) for x in xs] # recursive case: build a bigger function using a previous function `rec`
))[-1](total_aug_rule_path_list)) # get the last function from the list

最新更新