python itertools groupby 返回元组

Posted

技术标签:

【中文标题】python itertools groupby 返回元组【英文标题】:python itertools groupby return tuple 【发布时间】:2019-07-27 18:38:37 【问题描述】:

我需要解析扁平结构并使用提供的键列表创建嵌套结构。我已经解决了这个问题,但我正在寻找改进,我想了解我可以在我的代码中改变什么。有人可以审查它并使用更好的知识进行重构吗?

src_data = [
  
    "key1": "XX",
    "key2": "X111",
    "key3": "1aa",
    "key4": 1
  ,
  
    "key1": "YY",
    "key2": "Y111",
    "key3": "1bb",
    "key4": 11
  ,
  
    "key1": "ZZ",
    "key2": "Z111",
    "key3": "1cc",
    "key4": 2.4
  ,
  
    "key1": "AA",
    "key2": "A111",
    "key3": "1cc",
    "key4": 33333.2122
  ,
  
    "key1": "BB",
    "key2": "B111",
    "key3": "1bb",
    "key4": 2
  ,
]

这是我迄今为止开发的用于创建最终结果的代码。

def plant_tree(ll):
    master_tree = 

    for i in ll:
        tree = master_tree
        for n in i:
            if n not in tree:
                tree[n] = 
            tree = tree[n]
    return master_tree



def make_nested_object(tt, var):
    elo = lambda l: reduce(lambda x, y: y: x, l[::-1], var)
    return 'n_path': tt, 'n_structure': elo(tt)



def getFromDict(dataDict, mapList):
    return reduce(operator.getitem, mapList, dataDict)


def set_nested_item(dataDict, mapList, val):
    """Set item in nested dictionary"""
    reduce(getitem, mapList[:-1], dataDict)[mapList[-1]] = val
    return dataDict



def update_tree(data_tree):
    # MAKE NESTED OBJECT
    out = (make_nested_object(k, v) for k,v, in res_out.items())


    for dd in out:
        leaf_data = dd['n_structure']
        leaf_path = dd['n_path']
        data_tree = set_nested_item(data_tree, leaf_path, getFromDict(leaf_data, leaf_path))
    return data_tree

这是这个问题中自定义的 itemgeter 函数

def customed_itemgetter(*args):
    # this handles the case when one key is provided
    f = itemgetter(*args)
    if len(args) > 2:
        return f
    return lambda obj: (f(obj),)

定义嵌套级别

nesting_keys = ['key1', 'key3', 'key2']

grouper = customed_itemgetter(*nesting_keys)
ii = groupby(sorted(src_data, key=grouper), grouper)

res_out = key: [k:v for k,v in i.items() if k not in nesting_keys for i in group] for key,group in ii
#
ll = ([dd[x] for x in nesting_keys] for dd in src_data)
data_tree = plant_tree(ll)

得到结果

result = update_tree(data_tree)

如何改进我的代码?

【问题讨论】:

预期输出是什么? <itertools._grouper at 0x7f82d9eb5e48> 是一个生成器表达式,它没有告诉我任何信息 请在 groupby 表达式之后添加您希望最终输出的样子 @DeveshKumarSingh OP 的问题是关于每个元组的第一个元素,而不是关于石斑鱼。 另外for model, group in groupby(src_data, key=grouper): print(model, list(group)) 给了我1 ['a': 1, 'b': 2, 'z': 3] 2 ['a': 2, 'b': 3, 'e': 2] 4 ['a': 4, 'x': 3, 'b': 3] 而不是你在问题中提到的内容 所以实际上字典是1: 2 : [...] 用于样本输入?还是我忽略了什么? 【参考方案1】:

如果itemgetter [Python-doc] 被赋予单个元素,它会返回该单个元素,并且将其包装在单例元组中。

我们可以为此构造一个函数,例如:

from operator import itemgetter

def itemgetter2(*args):
    f = itemgetter(*args)
    if len(args) > 2:
        return f
    return lambda obj: (f(obj),)

那么我们就可以使用新的itemgetter2,比如:

grouper = itemgetter2(*ll)
ii = groupby(sorted(src_data, key=grouper), grouper)

编辑:但是,根据您的问题,您想要执行多级分组,我们可以为此创建一个函数,例如:

def multigroup(groups, iterable, index=0):
    if len(groups) <= index:
        return list(iterable)
    else:
        f = itemgetter(groups[index])
        i1 = index + 1
        return 
            k: multigroup(groups, vs, index=i1)
            for k, vs in groupby(sorted(iterable, key=f), f)
        

对于问题中的data_src,然后生成:

>>> multigroup(['a', 'b'], src_data)
1: 2: ['a': 1, 'b': 2, 'z': 3], 2: 3: ['a': 2, 'b': 3, 'e': 2], 4: 3: ['a': 4, 'x': 3, 'b': 3]

但是,您可以对 list(..) 调用中的值进行后处理。例如,我们可以生成没有分组列中元素的字典:

def multigroup(groups, iterable):
    group_set = set(groups)
    fs = [itemgetter(group) for group in groups]
    def mg(iterable, index=0):
        if len(groups) <= index:
            return [
                k: v for k, v in item.items() if k not in group_set
                for item in iterable
            ]
        else:
            i1 = index + 1
            return 
                k: mg(vs, index=i1)
                for k, vs in groupby(sorted(iterable, key=fs[index]), fs[index])
            
    return mg(iterable)

对于给定的样本输入,我们得到:

>>> multigroup(['a', 'b'], src_data)
1: 2: ['z': 3], 2: 3: ['e': 2], 4: 3: ['x': 3]

或者对于新的样本数据:

>>> pprint(multigroup(['key1', 'key3', 'key2'], src_data))
'AA': '1cc': 'A111': ['key4': 33333.2122],
 'BB': '1bb': 'B111': ['key4': 2],
 'XX': '1aa': 'X111': ['key4': 1],
 'YY': '1bb': 'Y111': ['key4': 11],
 'ZZ': '1cc': 'Z111': ['key4': 2.4]

【讨论】:

@meowgoesthedog:不!例如,如果itemgetter(*args)(x) 返回一些可迭代的内容(如 2 个字符的字符串),它会将其传播到元组的元素上。例如tuple('ab')('a', 'b'),而('ab', ) 仍然是('ab', ) 啊该死的,我的错! +1 @naivepredictor: 编辑中的multigroup 或多或少是您想要的吗? @naivepredictor:第二次编辑,通常应该从字典中“删除”这些键。 @naivepredictor:如果另一个字典有一个'key 3',那么它就像'key1: 'a', 'key2': 'b', 'key3': 'c', 'key1': 'a', 'key3': 'c' 一样有点“奇怪”。问题是结果应该是 'a': ... 的形状,但你建议'a' 的值是什么?一本字典?那么我们如何插入一个没有键的字典呢?一个列表?那么我们如何添加子类别呢?

以上是关于python itertools groupby 返回元组的主要内容,如果未能解决你的问题,请参考以下文章

Python中的字典分组函数(groupby,itertools)

python3:set 和 itertools.groupby 产生不同的结果? [复制]

Python itertools groupby 在列表理解中的多次使用

python itertools groupby 返回元组

python itertools

如何使用 python itertools.groupby() 按字符串的第一个字符对字符串列表进行分组?