gpt4 book ai didi

python - 计算嵌套字段的所有唯一值的频率计数器

转载 作者:太空宇宙 更新时间:2023-11-03 21:07:12 27 4
gpt4 key购买 nike

我想找到 Mongo Document 中嵌套字段的所有唯一值的频率计数器。

更具体地说,如果我的集合说 db['sample'],包含以下文档 -

{'a' : 1, 'b' : {'c' : 25, 'd' : "x", 'e' : 36}},

{'a' : 2, 'b' : {'c' : 5, 'd' : "xx", 'e' : 36}},

{'a' : 33, 'b' : {'c' : 25, 'd' : "xx", 'e' : 36}},

{'a' : 17, 'b' : {'c' : 25, 'd' : "xxx", 'e' : 36}},

如何获取字段“d”的所有唯一值的频率计数器?即我的输出应该是 {'d' : { "xx": 2, "x": 1, "xxx": 1} }

这可能吗?感谢对此的任何帮助。谢谢。

我查找了聚合和 objectToArray 转换的文档,以将映射转换为数组,并在 PyMongo 中尝试了以下操作

1)

db['sample'].aggregate([ { "$addFields" : { "b" : {"$objectToArray" : "$b"}}},\
{"$unwind" : "$b"},\
{"$group" : { "_id" : "$b.k",\
"count" : {"$sum" : "$b.v"}}} ])

这给出了每个可能的字段的累积计数 - 例如 'c' : 25 + 5 + 25 + 25。

2)

db['sample'].aggregate([ { "$addFields" : { "b" : {"$objectToArray" : "$b"}}},\
{"$unwind" : "$b"}, \
{"$group" : { "_id" : "$b.k", \
"count" : {"$sum" : 1 }}} ])

这给出了字段在文档中出现的总次数 - 'c':4、'd':4 等。

最佳答案

你基本上是以错误的方式处理这个问题的。您有一个清晰的路径作为要聚合的键,无需将其转换为数组:

cursor = db.sample.aggregate([
{ "$group": {
"_id": "$b.d",
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": None,
"data": { "$push": { "k": "$_id", "v": "$count" } }
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$data" }
}}
])

for doc in cursor:
print(doc)

返回

{ 'x': 1, 'xx': 2, 'xxx': 1 }

但这实际上是矫枉过正,因为实际上所有工作都是在最初的 $group 中完成的。陈述。您真正需要做的就是运行它并获取结果并将它们组合到一个字典中作为所需的输出:

cursor = db.sample.aggregate([
{ "$group": {
"_id": "$b.d",
"count": { "$sum": 1 }
}}
])

data = list(cursor)

result = reduce(
lambda x,y:
dict(x.items() + { y['_id']: y['count'] }.items()), data,{})

print(result)

它返回完全相同的东西:

{ 'x': 1, 'xx': 2, 'xxx': 1 }

此外,它不需要添加其他聚合阶段和运算符所需的体操,并且自初始 $group 以来,您没有更改服务器真正返回的内容。响应基本上是:

{ "_id" : "xxx", "count" : 1 }
{ "_id" : "xx", "count" : 2 }
{ "_id" : "x", "count" : 1 }

因此,这里真正的教训是,虽然您可以在聚合管道中进行花哨的操作,但您真正应该考虑的是,当替代方案更干净时,您可能不应该以及更具可读性的代码。

仅供引用,尽管发生的只是附加的 $group使用$push创建一个带有 kv 键的数组,正如下一个管道阶段所期望的那样。下一阶段使用 $replaceRoot获取 $arrayToObject 的输出来自前一阶段创建的数组,基本上将其转换为对象/字典。

相比之下,reduce 正在做完全相同的事情。我们基本上将光标结果放入一个列表中,以便 python 函数可以对该列表进行操作。然后,只需遍历该列表中的文档即可,该列表始终以 _id 作为键,并使用另一个命名属性作为“计数”输出(这里我们使用 count )并且只需将它们转换为最终字典输出的对。

<小时/>

只是为了好玩,基于您最初尝试的一些内容可能是:

db.sample.aggregate([
{ "$addFields": { "b": { "$objectToArray": "$b" } } },
{ "$unwind": "$b" },
{ "$group": {
"_id": {
"_id": "$b.k",
"k": "$b.v"
},
"count": { "$sum": 1 }

}},
{ "$group": {
"_id": "$_id._id",
"data": { "$push": { "k": { "$toString": "$_id.k" }, "v": "$count" } }
}},
{ "$addFields": {
"data": { "$arrayToObject": "$data" }
}}
])

这将返回:

{ "_id" : "c", "data" : { "25" : 3, "5" : 1 } }
{ "_id" : "e", "data" : { "36" : 4 } }
{ "_id" : "d", "data" : { "xxx" : 1, "xx" : 2, "x" : 1 } }

同样,在没有额外的管道阶段进行转换的情况下,通过使用 python 的 mapreduce 可以获得相同的结果:

cursor = db.sample.aggregate([
{ "$addFields": { "b": { "$objectToArray": "$b" } } },
{ "$unwind": "$b" },
{ "$group": {
"_id": {
"_id": "$b.k",
"k": "$b.v"
},
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id._id",
"data": { "$push": { "k": "$_id.k", "v": "$count" } }
}}
])

data = list(cursor)


result = map(lambda d: {
'_id': d['_id'],
'data': reduce(lambda x,y:
dict(x.items() + { y['k']: y['v'] }.items()), d['data'],
{})
},data)

关于python - 计算嵌套字段的所有唯一值的频率计数器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55321910/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com