gpt4 book ai didi

javascript - 在 python 或 javascript 中正确使用 fold 或 reduce 函数来处理从长到宽的数据?

转载 作者:行者123 更新时间:2023-11-28 17:47:21 26 4
gpt4 key购买 nike

尝试学习像函数式程序员一样思考——我想用我认为是折叠或归约操作的方式转换数据集。在 R 中,我会认为这是一个 reshape 操作,但我不确定如何翻译这种想法。

我的数据是一个 json 字符串,如下所示:

s = 
'[
{"query":"Q1", "detail" : "cool", "rank":1,"url":"awesome1"},
{"query":"Q1", "detail" : "cool", "rank":2,"url":"awesome2"},
{"query":"Q1", "detail" : "cool", "rank":3,"url":"awesome3"},
{"query":"Q#2", "detail" : "same", "rank":1,"url":"newurl1"},
{"query":"Q#2", "detail" : "same", "rank":2,"url":"newurl2"},
{"query":"Q#2", "detail" : "same", "rank":3,"url":"newurl3"}
]'

我想把它变成这样的东西,其中查询是定义“行”的主键,嵌套对应于“rank”值和“url”字段的唯一“行”:

'[
{ "query" : "Q1",
"results" : [
{"rank" : 1, "url": "awesome1"},
{"rank" : 2, "url": "awesome2"},
{"rank" : 3, "url": "awesome3"}
]},
{ "query" : "Q#2",
"results" : [
{"rank" : 1, "url": "newurl1"},
{"rank" : 2, "url": "newurl2"},
{"rank" : 3, "url": "newurl3"},
]}
]'

我知道我可以遍历,但我怀疑有一个函数操作可以进行这种转换,对吗?

也很想知道如何得到更像这样的东西,Version2:

'[
{ "query" : "Q1",
"Common to all results" : [
{"detail" : "cool"}
],
"results" : [
{"rank" : 1, "url": "awesome1"},
{"rank" : 2, "url": "awesome2"},
{"rank" : 3, "url": "awesome3"}
]},
{ "query" : "Q#2",
"Common to all results" : [
{"detail" : "same"}
],
"results" : [
{"rank" : 1, "url": "newurl1"},
{"rank" : 2, "url": "newurl2"},
{"rank" : 3, "url": "newurl3"}
]}
]'

在第二个版本中,我想获取在同一查询下重复的所有数据,并将其放入“其他内容”容器中,其中“等级”下所有唯一的项目都将在“结果”容器中.

我正在 mongodb 中处理 json 对象,可以使用 python 或 javascript 来尝试这种转换。

任何建议,例如此转换的专有名称,可能是在大型数据集上执行此操作的最快方法,我们都将不胜感激!

编辑

在下面结合@abarnert 的优秀解决方案,试图让我的 Version2 上面的任何人都在处理相同类型的问题,需要在一个级别下 fork 一些键,在另一层下 fork 其他键......

这是我尝试过的:

from functools import partial
groups = itertools.groupby(initial, operator.itemgetter('query'))
def filterkeys(d,mylist):
return {k: v for k, v in d.items() if k in mylist}

results = ((key, map(partial(filterkeys, mylist=['rank','url']),group)) for key, group in groups)
other_stuff = ((key, map(partial(filterkeys, mylist=['detail']),group)) for key, group in groups)

???

哦不!

最佳答案

我知道这不是您要的折叠式解决方案,但我会使用 itertools 来实现,它的功能一样(除非您认为 Haskell 的功能不如 Lisp ...... ),也可能是解决这个问题的最 pythonic 方法。

想法是将您的序列视为惰性列表,并对其应用一系列惰性转换,直到获得所需的列表。

这里的关键步骤是groupby :

>>> initial = json.loads(s)
>>> groups = itertools.groupby(initial, operator.itemgetter('query'))
>>> print([key, list(group) for key, group in groups])
[('Q1',
[{'detail': 'cool', 'query': 'Q1', 'rank': 1, 'url': 'awesome1'},
{'detail': 'cool', 'query': 'Q1', 'rank': 2, 'url': 'awesome2'},
{'detail': 'cool', 'query': 'Q1', 'rank': 3, 'url': 'awesome3'}]),
('Q#2',
[{'detail': 'same', 'query': 'Q#2', 'rank': 1, 'url': 'newurl1'},
{'detail': 'same', 'query': 'Q#2', 'rank': 2, 'url': 'newurl2'},
{'detail': 'same', 'query': 'Q#2', 'rank': 3, 'url': 'newurl3'}])]

只需一步,您就可以看到我们已经有多接近。

要重组每个键,将 pair 分组为您想要的字典格式:

>>> groups = itertools.groupby(initial, operator.itemgetter('query'))
>>> print([{"query": key, "results": list(group)} for key, group in groups])
[{'query': 'Q1',
'results': [{'detail': 'cool',
'query': 'Q1',
'rank': 1,
'url': 'awesome1'},
{'detail': 'cool',
'query': 'Q1',
'rank': 2,
'url': 'awesome2'},
{'detail': 'cool',
'query': 'Q1',
'rank': 3,
'url': 'awesome3'}]},
{'query': 'Q#2',
'results': [{'detail': 'same',
'query': 'Q#2',
'rank': 1,
'url': 'newurl1'},
{'detail': 'same',
'query': 'Q#2',
'rank': 2,
'url': 'newurl2'},
{'detail': 'same',
'query': 'Q#2',
'rank': 3,
'url': 'newurl3'}]}]

但是等等,还有一些您想要删除的额外字段。简单:

>>> groups = itertools.groupby(initial, operator.itemgetter('query'))
>>> def filterkeys(d):
... return {k: v for k, v in d.items() if k in ('rank', 'url')}
>>> filtered = ((key, map(filterkeys, group)) for key, group in groups)
>>> print([{"query": key, "results": list(group)} for key, group in filtered])
[{'query': 'Q1',
'results': [{'rank': 1, 'url': 'awesome1'},
{'rank': 2, 'url': 'awesome2'},
{'rank': 3, 'url': 'awesome3'}]},
{'query': 'Q#2',
'results': [{'rank': 1, 'url': 'newurl1'},
{'rank': 2, 'url': 'newurl2'},
{'rank': 3, 'url': 'newurl3'}]}]

唯一剩下要做的就是调用 json.dumps 而不是 print


对于您的后续操作,您希望使用相同的query 获取每一行中相同的所有值,并将它们分组到otherstuff 中,然后列出保留在结果

因此,对于每个组,首先我们要获取公共(public) key 。我们可以通过迭代组中任何成员的键来做到这一点(任何不在第一个成员中的东西都不能在所有成员中),所以:

def common_fields(group):
def in_all_members(key, value):
return all(member[key] == value for member in group[1:])
return {key: value for key, value in group[0].items() if in_all_members(key, value)}

或者,或者……如果我们将每个成员变成一个 set 键值对,而不是一个 dict,那么我们就可以将它们全部相交。这意味着我们终于可以使用 reduce,所以让我们试试看:

def common_fields(group):
return dict(functools.reduce(set.intersection, (set(d.items()) for d in group)))

我认为 dictset 之间的来回转换可能会降低它的可读性,这也意味着你的值必须是可散列的(不是问题你采样数据,因为值都是字符串)......但它肯定更简洁。

当然,这将始终包括 query 作为公共(public)字段,但我们稍后会处理它。 (此外,您希望 otherstuff 成为一个带有一个 dictlist,所以我们会在它周围多加一对括号)。

同时,results和上面一样,只是filterkeys过滤掉了所有的公共(public)字段,而不是过滤掉除rank之外的所有字段> 和 url。放在一起:

def process_group(group):
group = list(group)
common = dict(functools.reduce(set.intersection, (set(d.items()) for d in group)))
def filterkeys(member):
return {k: v for k, v in member.items() if k not in common}
results = list(map(filterkeys, group))
query = common.pop('query')
return {'query': query,
'otherstuff': [common],
'results': list(results)}

所以,现在我们只需使用该函数:

>>> groups = itertools.groupby(initial, operator.itemgetter('query'))
>>> print([process_group(group) for key, group in groups])
[{'otherstuff': [{'detail': 'cool'}],
'query': 'Q1',
'results': [{'rank': 1, 'url': 'awesome1'},
{'rank': 2, 'url': 'awesome2'},
{'rank': 3, 'url': 'awesome3'}]},
{'otherstuff': [{'detail': 'same'}],
'query': 'Q#2',
'results': [{'rank': 1, 'url': 'newurl1'},
{'rank': 2, 'url': 'newurl2'},
{'rank': 3, 'url': 'newurl3'}]}]

这显然不像原始版本那么简单,但希望这一切仍然有意义。只有两个新花样。首先,我们必须多次迭代 groups(一次找到公共(public)键,然后再次提取剩余键)

关于javascript - 在 python 或 javascript 中正确使用 fold 或 reduce 函数来处理从长到宽的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16154847/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com