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

Won*_*hin 6 python lambda dictionary list nested-lists

输入

我有一个非常复杂的清单。

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']]]]]
Run Code Online (Sandbox Code Playgroud)

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

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,}
Run Code Online (Sandbox Code Playgroud)

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

输出

[[[[[1, 5, 9], [2, 6, 10]], [[1, 5, 9], [2, 6, 10]]],
  [[[3, 7, 11, 13], [4, 8, 12, 14]]]]]
Run Code Online (Sandbox Code Playgroud)

我尝试了以下方法来尽可能少地使用 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))
Run Code Online (Sandbox Code Playgroud)

如果您能让我知道是否有一种比此方法更容易或更易读的方法,我将不胜感激。

Bla*_*ven 11

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

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)
Run Code Online (Sandbox Code Playgroud)

编辑:请注意,在任何语言(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]]]]]
Run Code Online (Sandbox Code Playgroud)


Oli*_*Oli 2

以下是一些替代方案:

嵌套列表理解

[[[[[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]
Run Code Online (Sandbox Code Playgroud)

尽管这可能并不容易阅读。

使用 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))
Run Code Online (Sandbox Code Playgroud)

这将该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))
Run Code Online (Sandbox Code Playgroud)

固定递归深度

这种变化递归到固定深度,因此不需要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))
Run Code Online (Sandbox Code Playgroud)

如果你真的想要,你可以使用一个技巧来itertools.accumulate将这个固定的递归深度函数变成一个单一的表达式,但它非常不Pythonic并且很难理解。

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
Run Code Online (Sandbox Code Playgroud)