在Python中展平字典的词典(2级深度)

Lar*_*rsH 5 python dictionary mapreduce data-structures

我试图将我的大脑包裹起来,但它不够灵活.

在我的Python脚本中,我有一个列表字典的字典.(实际上它会更深一些,但这个级别不涉及这个问题.)我想把所有这些变成一个长列表,扔掉所有字典键.

因此我想改造

{1: {'a': [1, 2, 3], 'b': [0]},
 2: {'c': [4, 5, 1], 'd': [3, 8]}}
Run Code Online (Sandbox Code Playgroud)

[1, 2, 3, 0, 4, 5, 1, 3, 8]
Run Code Online (Sandbox Code Playgroud)

我可以设置map-reduce来迭代外部字典的项目,从每个子字典构建一个子列表,然后将所有子列表连接在一起.

但是对于大型数据集而言,这似乎效率低下,因为中间数据结构(子列表)将被丢弃.有没有办法一次性完成?

除此之外,我很乐意接受一个有效的两级实现......我的map-reduce生锈了!

更新: 对于那些感兴趣的人,下面是我最终使用的代码.

请注意,虽然我在上面询问了列表作为输出,但我真正需要的是一个排序列表; 即,展平的输出可以是可以排序的任何可迭代的.

def genSessions(d):
    """Given the ipDict, return an iterator that provides all the sessions,
    one by one, converted to tuples."""
    for uaDict in d.itervalues():
        for sessions in uaDict.itervalues():
            for session in sessions:
                yield tuple(session)
Run Code Online (Sandbox Code Playgroud)

...

# Flatten dict of dicts of lists of sessions into a list of sessions.
# Sort that list by start time
sessionsByStartTime = sorted(genSessions(ipDict), key=operator.itemgetter(0))
# Then make another copy sorted by end time.
sessionsByEndTime = sorted(sessionsByStartTime, key=operator.itemgetter(1))
Run Code Online (Sandbox Code Playgroud)

再次感谢所有帮助过的人.

[更新:用operator.itemgetter()替换了nthGetter(),感谢@intuited.

Ale*_*lli 17

我希望你意识到你在dict中看到的任何命令都是偶然的 - 它只是因为,当在屏幕上显示时,必须选择一些命令,但绝对不能保证.

各个子列表被链接的订购问题网,

[x for d in thedict.itervalues()
   for alist in d.itervalues()
   for x in alist]
Run Code Online (Sandbox Code Playgroud)

做什么你想要没有任何低效率和中间名单.


Esc*_*alo 6

递归函数可能有效:

def flat(d, out=[]):
 for val in d.values():
  if isinstance(val, dict):
    flat(d, out)
  else:
    out+= val
Run Code Online (Sandbox Code Playgroud)

如果您尝试使用:

>>> d = {1: {'a': [1, 2, 3], 'b': [0]}, 2: {'c': [4, 5, 6], 'd': [3, 8]}}
>>> out = []
>>> flat(d, out)
>>> print out
[1, 2, 3, 0, 4, 5, 6, 3, 8]
Run Code Online (Sandbox Code Playgroud)

请注意,词典没有顺序,因此列表是随机顺序.

您也可以return out(在循环结束时)并且不使用list参数调用该函数.

def flat(d, out=[]):
 for val in d.values():
  if isinstance(val, dict):
    flat(d, out)
  else:
    out+= val
 return out
Run Code Online (Sandbox Code Playgroud)

呼叫:

my_list = flat(d)
Run Code Online (Sandbox Code Playgroud)


int*_*ted 6

编辑:重新阅读原始问题并重新编写答案,假设所有非字典都是要展平的列表.

如果你不确定字典走多远,你会想要使用递归函数.@Arrieta已经发布了一个递归构建非字典值列表的函数.

这是一个生成字典树中连续的非字典值的生成器:

def flatten(d):
    """Recursively flatten dictionary values in `d`.

    >>> hat = {'cat': ['images/cat-in-the-hat.png'],
    ...        'fish': {'colours': {'red': [0xFF0000], 'blue': [0x0000FF]},
    ...                 'numbers': {'one': [1], 'two': [2]}},
    ...        'food': {'eggs': {'green': [0x00FF00]},
    ...                 'ham': ['lean', 'medium', 'fat']}}
    >>> set_of_values = set(flatten(hat))
    >>> sorted(set_of_values)
    [1, 2, 255, 65280, 16711680, 'fat', 'images/cat-in-the-hat.png', 'lean', 'medium']
    """
    try:
        for v in d.itervalues():
            for nested_v in flatten(v):
                yield nested_v
    except AttributeError:
        for list_v in d:
            yield list_v
Run Code Online (Sandbox Code Playgroud)

doctest将生成的迭代器传递给set函数.这可能是你想要的,因为正如Martelli先生所指出的那样,字典的价值没有内在的顺序,因此没有理由跟踪它们被发现的顺序.

您可能希望跟踪每个值的出现次数; 如果将迭代器传递给,则此信息将丢失set.如果你想跟踪它,只需将结果传递flatten(hat)给其他函数而不是set.在Python 2.7下,其他功能可以collections.Counter.为了与较少进化的蟒蛇兼容,您可以编写自己的函数或(与某些效率损失)结合sorted使用itertools.groupby.