R 如何将一个大的、复杂的、嵌套很深的JSON文件展平为多个CSV文件—一个链接标识符
我有一个复杂的JSON文件(~8GB),其中包含企业公开可用的数据。我们决定将这些文件拆分为多个CSV文件(或.xlsx中的选项卡),以便客户端可以轻松地使用这些数据。这些文件将通过NZBN列/键链接 我使用R和jsonlite在中读取一个小样本(在扩展到完整文件之前)。我猜我需要某种方法来指定每个文件中的键/列(即,第一个文件将有标题:australianBusinessNumber、australianCompanyNumber、australianServiceAddress,第二个文件将有标题:annualReturnFilingMonth、AnnualReturnLastField、countryOfOrigin…) 下面是两个企业/实体的示例(我也收集了一些数据,因此忽略了实际值): 我读过几乎每一篇关于s/o的类似问题的帖子,似乎都没有给我带来任何运气。我尝试过purrr、*apply命令、自定义展平函数和jqr(jq的r版本——看起来很有希望,但我似乎无法运行) 这里尝试创建单独的文件,但我不确定如何包含链接标识符(NZBN)+我一直在运行进一步的嵌套列表(我不确定有多少层嵌套)R 如何将一个大的、复杂的、嵌套很深的JSON文件展平为多个CSV文件—一个链接标识符,r,json,csv,jq,jsonlite,R,Json,Csv,Jq,Jsonlite,我有一个复杂的JSON文件(~8GB),其中包含企业公开可用的数据。我们决定将这些文件拆分为多个CSV文件(或.xlsx中的选项卡),以便客户端可以轻松地使用这些数据。这些文件将通过NZBN列/键链接 我使用R和jsonlite在中读取一个小样本(在扩展到完整文件之前)。我猜我需要某种方法来指定每个文件中的键/列(即,第一个文件将有标题:australianBusinessNumber、australianCompanyNumber、australianServiceAddress,第二个文件将
批量
我不确定有多少层次的筑巢
这将非常有效地解决这一问题:
jq '
def max(s): reduce s as $s (null;
if . == null then $s elif $s > . then $s else . end);
max(paths|length)' input.json
(对于测试文件,答案是14。)
要获得数据的总体视图(模式),您可以
运行:
其中schema.jq在此处可用。这将产生一个结构模式
例如,我想创建以下元素的csv:
除了标题之外,还有一个jq解决方案:
.companies.entity[]
| [.entityName, .nzbn]
+ (.emailAddress[] | [.uniqueIdentifier, .emailAddress, .emailPurpose, .emailPurposeDescription, .startDate])
| @csv
持股
持股数据是复杂的,因此在下面我使用了本页其他地方定义的to_table
函数
示例数据不包括“公司名称”字段,因此在下面,我添加了一个基于0的“公司索引”字段:
jqr
上述解决方案使用独立的jq可执行文件,但一切顺利,使用相同的过滤器应该很简单,尽管要使用jq的include
,可能最简单的方法是显式指定路径,例如:
include "schema" {search: "~/.jq"};
如果输入JSON足够规则,则
可能会发现以下展平函数很有用,特别是因为它可以根据输入的叶元素的“路径”以字符串数组的形式发出头,可以任意嵌套:
# to_table produces a flat array.
# If hdr == true, then ONLY emit a header line (in prettified form, i.e. as an array of strings);
# if hdr is an array, it should be the prettified form and is used to check consistency.
def to_table(hdr):
def prettify: map( (map(tostring)|join(":") ));
def composite: type == "object" or type == "array";
def check:
select(hdr|type == "array")
| if prettify == hdr then empty
else error("expected head is \(hdr) but imputed header is \(.)")
end ;
. as $in
| [paths(composite|not)] # the paths in array-of-array form
| if hdr==true then prettify
else check, map(. as $p | $in | getpath($p))
end;
例如,要为.emailAddress生成所需的表(无标题),可以编写:
.companies.entity[]
| [.entityName, .nzbn] as $ix
| $ix + (.emailAddress[] | to_table(false))
| @tsv
(添加标题并检查一致性,
现在只剩下练习了,但将在下面讨论。)
生成多个文件
更有趣的是,您可以选择所需的级别,并自动生成多个表。有效地将输出划分为单独文件的一种方法是使用awk。例如,您可以通过管道将使用此jq过滤器获得的输出:
["entityName", "nzbn"] as $common
| .companies.entity[]
| [.entityName, .nzbn] as $ix
| (to_entries[] | select(.value | type == "array") | .key) as $key
| ($ix + [$key] | join("-")) as $filename
| (.[$key][0]|to_table(true)) as $header
# First emit the line giving all the headers:
| $filename, ($common + $header | @tsv),
# Then emit the rows of the table:
(.[$key][]
| ($filename, ($ix + to_table(false) | @tsv)))
到
这将在每个文件中生成标题;如果您想要一致性检查,请将更改为\u table(false)
更改为更改为\u table($header)
很遗憾,我没有jq-我想我需要管理员权限,这是一台工作PC。jqr(jq的R实现)没有提供使用上述代码所获得的信息。语言大致相同吗?感谢您的深入回答,包括下面的to_table函数。我还不能让它工作,但你们的jq解决方案上面的电子邮件地址已经和其他收集工作。cheersHi,我在持股部分遇到了一些问题(比emailAddress示例更复杂):.companys.entity[]|[.entityName,.nzbn]+(.company.holdings[]|.numberOfShares)不会从文件中产生任何结果我在其他响应中添加了“持股”部分,正如你的jq所建议的那样,你没有试图使用来创建一个表
。非常感谢-哇,这很强大。还有最后一个复杂的问题,我似乎无法弄清楚,在每个持股中,可能有多个股东(最多50个)。您的to_table函数似乎只是在同一行上扩展了这些内容(正如预期的那样)-但是,这会为拥有大量股东/股份的公司带来真正广泛的csv。您是否可以在下面的行中重复多个股东/持股(相同的实体名称和nzbn)。这里有一个更新的测试文件:“生成多个文件”一节说明了如何避免拉伸问题,但是您必须决定在什么时候开始创建新文件。也就是说,“生成多个文件”部分是说明性的——您必须理解它才能根据您的需要进行调整。假设JSON具有足够的规则性,那么to_table
的要点是处理可能需要的任何拉伸,以避免丢失数据。
.companies.entity[]
| [.entityName, .nzbn] as $ix
| $ix + (.emailAddress[] | to_table(false))
| @tsv
["entityName", "nzbn"] as $common
| .companies.entity[]
| [.entityName, .nzbn] as $ix
| (to_entries[] | select(.value | type == "array") | .key) as $key
| ($ix + [$key] | join("-")) as $filename
| (.[$key][0]|to_table(true)) as $header
# First emit the line giving all the headers:
| $filename, ($common + $header | @tsv),
# Then emit the rows of the table:
(.[$key][]
| ($filename, ($ix + to_table(false) | @tsv)))
awk -F\\t 'fn {print >> fn; fn=0;next} {fn=$1".tsv"}'