JQ:计算每个组的对象数,用于输入的子集

Posted

技术标签:

【中文标题】JQ:计算每个组的对象数,用于输入的子集【英文标题】:JQ: count number of objects per group, for a subset of input 【发布时间】:2019-03-01 01:01:42 【问题描述】:

我需要用 JQ 计算每个组中的对象数量,但只针对最近的 N 个对象。

样本输入,N=3:

"modified":"Mon Sep 25 14:20:00 +0000 2018","object_id":1,"group_id":"C"
"modified":"Mon Sep 25 14:23:00 +0000 2018","object_id":2,"group_id":"A"
"modified":"Mon Sep 25 14:21:00 +0000 2018","object_id":3,"group_id":"B"
"modified":"Mon Sep 25 14:22:00 +0000 2018","object_id":4,"group_id":"A"

预期输出:

"A",2
"B",1

我什至没有选择一个基于日期的子集来保留对象的结构:这是我设法实现的最好的结果:

 [
   .modified |= strptime("%a %b %d %H:%M:%S %z %Y") |
   .modified |= mktime |
   .modified |= strftime("%Y-%m-%d %H:%M:%S")
 ]  |
 sort_by(.modified) |
 .[] |
 modified, object_id, group_id

由于某种原因,结果仍未排序。

我也未能将此类列表转换为数组以仅选择 N 个最近的条目。

然后我需要以某种方式计算每组的对象数量。


总的来说,看起来我需要一个非常直观的解释,说明数组和对象列表如何相互转换,以及如何修改它们的一些字段,然后只提取所需的字段。不幸的是,到目前为止我发现的教程没有帮助。

【问题讨论】:

【参考方案1】:

假设您的输入文件是:

cat file
"modified":"Mon Sep 25 14:20:00 +0000 2018","object_id":1,"class_id":"C"
"modified":"Mon Sep 25 14:23:00 +0000 2018","object_id":2,"class_id":"A"
"modified":"Mon Sep 25 14:21:00 +0000 2018","object_id":3,"class_id":"B"
"modified":"Mon Sep 25 14:22:00 +0000 2018","object_id":4,"class_id":"A"

您可以尝试以下方法:

<file jq -s '
   [ .[] | 
     (.modified |= (strptime("%a %b %d %H:%M:%S +0000 %Y") | mktime)) 
   ] | 
   sort_by(.modified) |              # sort using converted time
   .[-3:] |                          # take the last 3
   group_by(.class_id) |             # group ids together
   .[] |                             
   (.[0].class_id): length'        # create the object using the id name and table length

   "A": 2


  "B": 1

请注意,在我的系统上,strptime 的选项 %z 不起作用。所以我把它换成了+0000(反正时间转换中没有用到)。

【讨论】:

非常感谢,它确实有效!需要澄清两件事:(1)如何将其输出转换为 CSV,即字面意思是 "A",2 \n "B",1"? (2) 您能否分享一些关于 Slurp 开关的易于阅读的描述,以解释为什么您的代码可以使用它(并且没有它就无法工作),以及更一般地说,应该/不使用 Slurp 的经验法则? @was-s-rubleff (1) 使用[(.[0].class_id), length] | @csv-r 选项。 (2)我猜这超出了范围,值得提出自己的问题......【参考方案2】:

accepted answer 使用-s 命令行选项,它要求整个输入数据适合内存。对于非常大的数据集,这可能是不可能的。

自 jq 1.5 发布(2015 年)以来,有一个替代方案可用。因此,这里提出了一种使用inputs 的内存高效解决方案。

关键功能封装在以下jq过滤器中:

# Return an array of n items as if by 
# [stream] | sort_by(filter) | .[-n:]
def maxn(stream; filter; n):
  def maxn:
    sort_by(filter) | .[-n :];
  reduce stream as $x ([]; . + [$x] | maxn);

现在只需另外三行即可获得手头问题的解决方案(N==3):

maxn(inputs; .modified | strptime("%a %b %d %H:%M:%S +0000 %Y") | mktime; 3)
| group_by(.class_id)[]
| (.[0].class_id): length

请注意,这里假设使用了 -n 命令行选项。如果省略,第一行输入将被忽略。

大N

对于大型数据集,如果 N 的值也很大,那么调整上面的内容以使用 jq 的支持 fot binary search (bsearch) 而不是 sort_by 可能是值得的。缓存mktime 值可能同样值得。

【讨论】:

哇,在我的情况下,内存方面确实是一个可能的问题,所以感谢您提供的替代解决方案——肯定也会尝试一下。

以上是关于JQ:计算每个组的对象数,用于输入的子集的主要内容,如果未能解决你的问题,请参考以下文章

如何跳过jq输入中的前n个对象

jq json将对象移动到嵌套对象并迭代未知名称/对象数

对 pandas 中的多层次数据进行子集化

如何计算 JQ 中每个对象的键值对的出现次数?

7.1 深搜-子集和问题 (枚举子集+剪枝)

416-分割等和子集(01背包)