【问题标题】:How to efficiently get shared key subsets of multiple dicts as multiple arrays?如何有效地将多个字典的共享键子集作为多个数组?
【发布时间】:2021-09-22 12:43:47
【问题描述】:

有没有一种有效的方法来获取多个字典(的键)的交集?

类似于 iterating over shared keys in two dictionaries ,不同之处在于它的想法不是迭代而是获取集合,因此它可以用于获取 dicts 的子集。

d1 = {'a':[1,2], 'b':[2,2]}
d2 = {'e':[3,2], 'b':[5,1], 'a':[5,5]}
d3 = {'b':[8,2], 'a':[3,3], 'c': [1,2]}

所以手动相交很简单

d1.keys() & d2.keys() & d3.keys()

但是 n 维列表呢?我觉得有比这更好的方法:

d_list = [d1, d2, d3]

inter_keys = {}
for i in range(len(d_list)):
    if i == 0:
        inter_keys = d_list[i]
    inter_keys = inter_keys & d_list[i].keys() 

然后得到一个子集

subsets = []
for n in d_list:
    subsets.append( {k: n[k] for k in inter_keys} )

最后用它来获取值子集

v = [ x.values() for x in subsets ]
  • 真的最后一部分被格式化为v = np.array([ np.array(list(x.values())) for x in subsets ]) 得到的ndarray 为:

[[[2 2] [1 2]]

[[5 1] [5 5]]

[[8 2] [3 3]]]

我在想可能有一种方法可以使用像 numpy where 这样的方法来更有效地获取子集,但不确定。

【问题讨论】:

  • where 不是迭代器。它的参数是数组。
  • @hpaulj 是的,这就是为什么我认为可能有一种使用它的方法,因为我首先获得所有字典之间的共享键,我有一个键数组可能是一个长的 'or '声明。
  • dsubsets 都是 dict 的列表。直到你收集到x.values(),你才能得到一个可以做成数组的列表。

标签: python numpy dictionary


【解决方案1】:

我认为你的代码可以简化为:

In [383]: d_list=[d1,d2,d3]
In [388]: inter_keys = d_list[0].keys()
In [389]: for n in d_list[1:]:
     ...:     inter_keys &= n.keys()
     ...: 
In [390]: inter_keys
Out[390]: {'a', 'b'}
In [391]: np.array([[n[k] for k in inter_keys] for n in d_list])
Out[391]: 
array([[[1, 2],
        [2, 2]],

       [[5, 5],
        [5, 1]],

       [[3, 3],
        [8, 2]]])

即迭代获取key的交集,然后将值提取到list的list中,可以做成数组。

inter_keysdict.keys 对象开始,但变为set;两者都适用于&=

我认为没有办法绕过以 dict 索引、n[k] 为核心的双循环。除非您可以使用 valuesitems 列表,否则无法逐个访问 dict 项目。

dictsub_sets 列表是不必要的中间步骤。

所有键和值都可以提取到列表列表中,但这无助于选择公共子集:

In [406]: big_list = [list(d.items()) for d in d_list]
In [407]: big_list
Out[407]: 
[[('a', [1, 2]), ('b', [2, 2])],
 [('e', [3, 2]), ('b', [5, 1]), ('a', [5, 5])],
 [('b', [8, 2]), ('a', [3, 3]), ('c', [1, 2])]]

【讨论】:

  • 我的其他 numpy 评论更适合于类似 recarray 或 pandas 数据帧如何对列名进行切片。我想我希望有一个新的内置 python 来扩展交集运算符,或者更合乎逻辑地创建一个新的子集运算符来包含所需字典的顺序,不仅可以处理键迭代器,还可以处理与那些相关的值键。
  • 我想出了一种使用functions.reduceoperator.iand 来计算inter_keys 的方法,但它似乎并不比我的迭代简单。
  • 我不熟悉用于连接框架列的pandas 工具。 recfunctions 具有连接结构化数组的功能,但我没有使用过多。大多数情况下,他们定义了一个新的dtype 和数组。然后他们复制值,通常是逐个字段。我怀疑dict 比其他结构有更多类似set 的属性。
【解决方案2】:

假设您的字典中的值列表长度相同,您可以使用这种方法:

import numpy as np
d1 = {'a':[1,2], 'b':[2,2]}
d2 = {'e':[3,2], 'b':[5,1], 'a':[5,5]}
d3 = {'b':[8,2], 'a':[3,3], 'c':[1,2]}
d_list = [d1, d2, d3]
inter_map = {} if len(d_list) == 0 else d_list[0]
for d_it in d_list[1:]:
    # combine element lists based on the current intersection. keys that do not match once are removed from inter_map
    inter_map = {k: inter_map[k] + d_it[k]  for k in d_it.keys() & inter_map.keys()}
# inter_map holds a key->value list mapping at this point
values = np.array([item for sublist in inter_map.values() for item in sublist]).reshape([len(inter_map.keys()),
    2 * len(d_list)])
# real_values restructures the values into the order used in your program, assumes you always have 2 values per sublist
real_values = np.zeros(shape=[len(d_list), 2 * len(inter_map.keys())])
for i, k in enumerate(inter_map.keys()):
    real_values[:, 2*i:2*(i+1)] = values[i].reshape([len(d_list), 2])

请注意,此代码不是确定性的,因为不保证映射中键的顺序对于程序的不同运行是相同的。

【讨论】:

    猜你喜欢
    • 2016-03-12
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2021-04-15
    • 1970-01-01
    • 2012-04-24
    • 2016-11-05
    • 2019-02-14
    相关资源
    最近更新 更多