我正在开发Adobe Analytics报告/数据仓库API,以及如何将这些数据处理成行和列(我使用python和pandas来解决这个问题)。只要只使用一个维度,这一切都很顺利,但是当查询多个维度时,故障的想法就会发挥作用。
例如,当我使用2维时,数据(JSON格式)看起来像这样:
{
"report":{
"data":[
{
"name":"June 13,2017",
"breakdown":[
{
"name":"nl",
"breakdown":[
{
"name":"not logged in",
"counts":[
"10",
"12"
]
},
{
"name":"logged in",
"counts":[
"30",
"2"
]
}
]
}, ... etc.
详细说明,count代表一系列指标,每个细分代表一个维度(数字和名称都是虚构的)。
我想在这里做的是建立我最终可以按行和列链接在一起的数据集,因此示例的第一行将是(" 6月13,2017",& #34; nl","未登录",10,12)。
在示例情况下,我已经可以使用以下代码执行此操作:
for period in dataObj['report']['data']:
for firstbreakdown in period['breakdown']:
for secondbreakdown in firstbreakdown['breakdown']:
print(period['name'], firstbreakdown['name'], secondbreakdown['name'], secondbreakdown['counts'])
在这里处理计数并不是问题,因为我发现我可以轻松地用熊猫做到这一点。但是这里有棘手的部分和我的实际问题:JSON示例中的故障数量取决于查询的维度数。
如何根据查询的维度数量自动生成足够的循环,以便连续获取所有维度值和计数?
我更喜欢优雅和优雅的总是工作的解决方案,而不是if else语句。
答案 0 :(得分:1)
如果你不介意递归(见下文),解决这个问题的一种方法是使用递归函数来迭代越来越深的维度。例如:
def iterBD(dim, breakdown):
if (dim > 0):
return [ [breakdown["name"]] + iterBD(dim - 1, sub) for sub in breakdown["breakdown"] ]
return [ [breakdown["name"]] + data["counts"] for data in breakdown ]
然后使用例如调用您的示例数据iterBD(2, report.data)
。结果将是格式列表:
["June 13,2017", "nl", "not logged in", ["10", "12"]]
["June 13,2017", "nl", "logged in", ["30", "2"]]
...
如果要处理具有不同维数的异构数据/行,则可能会检测是否存在要迭代的其他维。例如:
def iterBD2(breakdown):
if (hasattr(breakdown, "breakdown")):
return [ [breakdown["name"]] + iterBD2(sub) for sub in breakdown["breakdown"] ]
return [ [breakdown["name"]] + data["counts"] for data in breakdown ]
(可能想要hasattr
/ try ... catch
/ {/ p>}查看此How to know if an object has an attribute in Python
此方法将被称为iterBD2(report.data)
,而无需指定尺寸。
以上是一种递归方法,这意味着它依赖于维度相对较少的数据。如果有一行例如成千上万的嵌套维度,Python可能有堆栈问题。与任何递归函数一样,上述内容可以重写为非递归函数。正如您在评论中指出的那样,在特定情况下维度的数量永远不会那么高,如果您必须处理这样的问题,可能是时候重新考虑查询本身以获得更好的数据了。