左连接用于 D3 映射 returns null 阻止下一步数据处理(ndjson-join 或 jq)命令行

Left join for D3 mapping returns null that prevents next step of data processing (ndjson-join or jq) command line

我正在州一级进行 D3 映射。这是我在数据处理中遇到的一个问题。比如地图数据是这样的,(dat1.ndjson)

{state: a, code: aa}
{state: b, code: bb}
{state: c, code: cc}

但通常我们掌握的信息并不完整,例如南极洲通常没有信息,但我们在绘制地图时仍然需要绘制它的轮廓。信息数据就像,(dat2.ndjson)

{state: a, code: aa, count: 1}
{state: b, code: bb, count: 2}

所以,当我尝试对这两个数据进行左连接时,它将 returns (dat3.ndjson)

[{state: a, code: aa},{state: a, code: aa, count: 1}]
[{state: b, code: bb},{state: b, code: bb, count: 2}]
[{state: c, code: cc},null]

这是return编辑者

ndjson-join --left 'd.code' dat1.ndjson dat2.ndjson < merge.ndjson

目的是将这个'count'信息连接到地图数据,所以通常我会先给dat1.ndjson中的所有item赋一个count = 0,像这样,(dat11.ndjson)

{state: a, code: aa, count: 0}
{state: b, code: bb, count: 0}
{state: c, code: cc, count: 0}

然后像我之前展示的那样使用这个左连接方法得到这样的东西,(dat33.ndjson)

[{state: a, code: aa, count: 0},{state: a, code: aa, count: 1}]
[{state: b, code: bb, count: 0},{state: b, code: bb, count: 2}]
[{state: c, code: cc, count: 0},null]

但是问题来了。如果我使用以下命令将所有值加在一起,它将 return 由于第三行中的空值而出错。

ndjson-map '{state: d[0].state, code: d[0].code, count: d[0].count + 
d[1].count}' < dat33.ndjson > merge.ndjson

现在我必须在 R 中进行此数据处理,这需要花费大量时间,因为我需要在 .ndjson 和 .csv 之间进行转换。所以我正在寻找一种更好的方法来做到这一点。我认为可能有一些方法可以使用 'ndjson-cli'、'jq' 或 'awk' 和 'sed' 等

有人有想法吗?谢谢! :)

E.

这是一个包含几个部分的解决方案:

  1. 正在将您的输入转换为有效的 JSON。
  2. 用于执行连接的库函数。
  3. 运行 jq 以产生所需的输出,假设您的 jq 版本足够新。
  4. 如果您只能访问 jq 1.5 怎么办

为了说明一旦您解决了绒毛问题,一切都是多么简单,这里是 "main" jq 程序:

join(.state) | .count //= 0

实际上,这表示:使用 .state 作为连接键执行连接,然后确保设置了 .count。

上述一行的输出将是 NDJSON:

{"state":"a","code":"aa","count":1}
{"state":"b","code":"bb","count":2}
{"state":"c","code":"cc","count":0}

第 1 部分:dat1.json 和 dat2.json

我假设您可以从输入中生成有效的 JSON。 对于示例数据,我使用了 sed:

for i in 1 2 ; do
  sed -e 's/state/"state"/' -e 's/code/"code"/' -e 's/count/"count"/' \
      -e 's/ \([a-z]*\)\([,}]\)/ ""/g' dat$i.ndjson > dat$i.json
done

无论如何,以下假设您有两个文件,dat1.json 和 dat2.json,包含有效的 JSON.

第 2 部分:join

这是一个用于生成连接的小型过滤器库:第一个适用于流,其他适用于数组。这些定义假定您的 jq 有 INDEX/2。如果不是这种情况,请参阅第 4 部分。

def joins(s1; s2; filter1; filter2):
  # combine two dictionaries using `add`
    def merge: . as $in
    | reduce (add|keys_unsorted[]) as $k ({}; .[$k] = ([$in[] | .[$k]] | add));
  [INDEX(s1; filter1 | select(. != null)), INDEX(s2; filter2 | select(. != null))]
  | merge[] ;

def join(filter1; filter2):
  joins(.[0][]; .[1][]; filter1; filter2);

def join(f): join(f; f);

第 3 部分解决方案

首先,让我们保持简单。如果将上述 joinjoins 的定义放在一个文件中,比如说 d3.jq,然后是序言中给出的单行程序,那么下面的调用就可以了,假设你的 jq 有 INDEX:

jq -c -s -f d3.jq <(jq -s . dat1.json) <(jq -s . dat2.json)

这假设您使用的是支持进程替换的 shell。如果没有,那么你可以先运行 "."单独的程序,例如如果你有 sponge:

 for i in 1 2 ; do jq -s . dat$i.json | sponge dat$i.json ; done

使用include

如果你的jq支持include,并且你在私有标准库如~/.jq/jq/jq.jq中有上述join的定义 然后你的主要 jq 程序变成了两行:

include "jq";
join(.state) | .count //= 0'

这意味着您可以省去 d3.jq 并使用调用:

jq -c -s 'include "jq"; join(.state)|.count //= 0' \
   <(jq -s . dat1.json) <(jq -s . dat2.json)

第 4 部分:INDEX

这是最新版本的 jq 提供的 INDEX 的副本。您可以将这些定义添加到 d3.jq(在程序的 "main" 部分之前),或者添加到您的库文件中,等等:

def INDEX(stream; idx_expr):
  reduce stream as $row ({};
    .[$row|idx_expr|
      if type != "string" then tojson
      else .
      end] |= $row);

def INDEX(idx_expr): INDEX(.[]; idx_expr);