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

2024-10-02 08:23:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我想找到Mongo文档中嵌套字段的所有唯一值的频率计数器。你知道吗

更具体地说,如果我的收藏说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”的所有唯一值的频率计数器?i、 我的输出应该是{d':{“xx”:2,“x”:1,“xxx”:1}}

这有可能吗?谢谢你的帮助。非常感谢。你知道吗

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

(一)

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

这将给出每个字段的累计计数,例如“c”:25+5+25+25。你知道吗

(二)

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

这将给出字段出现在文档中的总次数-“c”:4,“d”:4等


Tags: sample文档iddbcountgroup计数器xxx
1条回答
网友
1楼 · 发布于 2024-10-02 08:23:48

你基本上走错了路。您有一个到"b.d"的清晰路径,作为要在其上聚合的键,无需将其转换为数组:

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 }

但这实际上有些过分,因为实际上所有的工作都是在最初的^{}语句中完成的。您真正需要做的就是运行它并获取结果,然后将它们合并到一个字典中作为所需的输出:

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 }

此外,它这样做没有添加其他聚合阶段和操作符所需的体操,并且您没有更改真正从服务器返回的内容,因为最初的^{}响应基本上是:

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

因此,这里真正的教训是,虽然您可以在聚合管道中执行花哨的操作,但您真正应该考虑的是,如果替代方法是更干净、可读性更高的代码,您可能不应该这样做。你知道吗

作为参考,尽管所发生的只是额外的^{}使用^{}来创建一个带有kv键的数组,正如在下一个管道阶段所期望的那样。下一阶段使用^{}从上一阶段创建的数组中获取^{}的输出,并基本上将其转换为对象/字典。你知道吗

相比之下,reduce做着完全相同的事情。基本上,我们将光标结果放入list中,这样python函数就可以对该列表进行操作。然后只需遍历列表中的doucments,这些doucments总是以_id作为键,另一个命名属性作为“counted”输出(这里我们使用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)

相关问题 更多 >

    热门问题