【问题标题】:Summing up list of dictionaries based on a condition and deleting few keys根据条件汇总字典列表并删除几个键
【发布时间】:2021-03-14 12:48:32
【问题描述】:

我有一个带有动态键的字典列表(键是从代码生成的)如下:

l=[{"key1":1,"author":"test","year":"2011"},{"key2":5,"author":"test","year":"2012"},
{"key1":3,"author":"test1","year":"2012"},
{"key1":1,"author":"test","year":"2012"}]

现在,如果键相同,我想将第一个键值相加并最终将它们分组。所以,我的最终列表应该如下所示:

l=[{"key1":2,"author":"test","year":["2011","2012"]},{"key2":5,"author":"test","year":"2012"},{"key1":3,"author":"test1","year":"2012"}]

我尝试过pandas groupby,但无法使用,因为密钥是自动生成的。但是,代码如下:

(pd.DataFrame(l)
   .groupby(['author', 'year'], as_index=False)
   .key1.sum()
   .to_dict('r'))

还有什么更好的方法? 规则:

  1. 如果字典中的第一个键相同并且其他键作者和年份保持相同,则将两个值相加
  2. 如果作者不同,则不要加起来
  3. 如果作者相同但年份不同,则将年份分组 和 添加密钥

【问题讨论】:

  • 从您的示例(不是熊猫代码,这似乎不同),您似乎想按一个元组进行分组:(k, author) 其中k 是一个名称与r'key\d+' 匹配的键,并且author 是列表中给定元素 dd['author'] 的值。这似乎很令人费解。另外,我不明白您所说的“动态键”是什么意思。能举个简单的例子吗?
  • @PierreD Key1 只是我给出的一个例子,它可以是 cat 或 start 之类的任何东西,还有更多的键。关键是字典中的相似键具有相同的其他键(作者、年份)必须总结一下。通过动态键,我的意思是动态生成的键
  • 您能否提供一段最少的代码来说明您的意思? dict 只能有可散列的键,这意味着它们已经在你的代码中被计算出来并且(希望)到那时是不可变的。
  • @PierreD 我有一个列表,它将作为字典中的第一个键。作者和年份保持不变,但第一个键会根据用户给出的值不断变化。
  • 那么,是什么让'key1''key2'、...特别?它们不在{'author', 'year'} 中的事实或它们是字典的第一个键(按插入顺序)的事实?

标签: python python-3.x pandas python-2.7


【解决方案1】:

您最好使用更简洁的数据结构,其中 dicts 的第一个映射没有什么特别之处,并且第一个映射被拆分为例如'key':first_mapping_key'count':first_mapping_value

从您的 dicts 结构列表(其中“第一个键是特殊的”)中做到这一点的一种方法是:

def transform(d):
    (k, v), *t = d.items()
    return dict(key=k, count=v, **dict(t))

lmod = [transform(d) for d in l]
lmod
# out:
[{'key': 'key1', 'count': 0, 'author': 'test', 'year': '2010'},
 {'key': 'key1', 'count': 1, 'author': 'test', 'year': '2011'},
 {'key': 'key2', 'count': 5, 'author': 'test', 'year': '2012'},
 {'key': 'key1', 'count': 3, 'author': 'test1', 'year': '2012'},
 {'key': 'key1', 'count': 1, 'author': 'test', 'year': '2012'}]

现在您可以轻松地进行分组并汇总到您心中的内容。例如:

(pd.DataFrame(lmod)
 .query('count != 0')
 .groupby(['key', 'author'])
 .agg({'count': sum, 'year': set})
)

第二个主题是如何在不使用 pandas 的情况下进行分组和聚合。这是一种使用第一原则(仅使用核心库函数)的方法:

def grp_key(d):
    return d['key'], d['author']

def expect_single(a):
    values = set(a)
    assert len(values) == 1
    return next(iter(values))

_funcdict = {
    'key': expect_single,
    'author': expect_single,
    'count': sum,
}
def agg(lod):
    keys = {k: 1 for d in lod for k in d}  # insertion-order union of all keys
    d = {k: _funcdict.get(k, set)(d.get(k) for d in lod) for k in keys}
    return d

应用:

out = [
    agg(list(g))
    for k, g in groupby(sorted([
        d for d in lmod if d['count'] != 0
    ], key=grp_key), key=grp_key)
]
out
# output:
[{'key': 'key1', 'count': 2, 'author': 'test', 'year': {'2011', '2012'}},
 {'key': 'key1', 'count': 3, 'author': 'test1', 'year': {'2012'}},
 {'key': 'key2', 'count': 5, 'author': 'test', 'year': {'2012'}}]

【讨论】:

    【解决方案2】:

    .groupby(['author', 'year'] 的结果上尝试groupby-agg。除authoryear 之外的每个键都在单独的步骤中应用聚合。

    df = pd.DataFrame(l)
    df_gp = df.groupby(['author', 'year'], as_index=False).sum()
    
    def agg_key(df, key):
        return df[df[key] != 0].groupby("author", as_index=False).agg({
            # collect the years
            "year": lambda sr: [str(el) for el in sr],
            # sum the key
            key: "sum",
        }).to_dict(orient="records")
    
    # keys except group and author
    keys = df.columns[~df.columns.isin(["author", "year"])]
    
    # apply aggregation and flatten list of lists
    ans = [el for key in keys for el in agg_key(df_gp, key)]
    

    输出

    print(ans)
    
    [{'author': 'test', 'year': ['2011', '2012'], 'key1': 2.0},
     {'author': 'test1', 'year': ['2012'], 'key1': 3.0},
     {'author': 'test', 'year': ['2012'], 'key2': 5.0}]
    

    为了类型一致性(推荐),单个“年份”作为单元素 list 而不是 str 返回。

    【讨论】:

    • 我无法对 key1 和 key2 进行编码,因为它们会不断变化,并且可能还有更多的键,例如 key3 和 key4。另外,我拥有的是字典列表而不是数据框
    • 我有一个根据用户值更改的值列表。每次用户输入一个值时,都会生成一个新列表,列表中的项目成为键,值是键的词频。因此我的钥匙不固定
    • 这不是一个可实施的规则。为了实现,我需要知道:哪些键需要求和,哪些不需要?请提供明确的规则。例如:“除作者和年份外,每个键都应相加”。
    • 是的,除作者和年份外,每个键都应相加,但如果同一键的作者和年份不同,则必须将它们分组在一个列表中。我编辑了问题
    • 如果没有值,我们是否有可能在年份列表中添加 0。例如,如果我在总列表中有 [2001,2002,2004,2006] 的年份范围如果其中一年不存在,我们可以添加一个零吗?例如,对于第一项“年”:[2001,0,0,2006]。这只是我对问题的扩展
    猜你喜欢
    • 2014-07-23
    • 2022-01-20
    • 1970-01-01
    • 2017-11-06
    • 2021-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    相关资源
    最近更新 更多