Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/460.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在python或javascript中正确使用fold或reduce函数来处理从长到宽的数据?_Javascript_Python_Functional Programming_Reshape_Fold - Fatal编程技术网

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

在python或javascript中正确使用fold或reduce函数来处理从长到宽的数据?,javascript,python,functional-programming,reshape,fold,Javascript,Python,Functional Programming,Reshape,Fold,试着多学一点函数式程序员的思维方式——我想用我认为是折叠或缩减操作的方式来转换数据集。在R中,我会认为这是一个重塑操作,但我不知道如何翻译这种想法 我的数据是一个json字符串,如下所示: s = '[ {"query":"Q1", "detail" : "cool", "rank":1,"url":"awesome1"}, {"query&qu

试着多学一点函数式程序员的思维方式——我想用我认为是折叠或缩减操作的方式来转换数据集。在R中,我会认为这是一个重塑操作,但我不知道如何翻译这种想法

我的数据是一个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"}
]'
我想把它变成这样,其中query是定义“行”的主键,嵌套与“rank”值和“url”字段相对应的唯一“行”:

我知道我可以迭代,但我怀疑有一个函数操作可以完成这个转换,对吗

我也很想知道如何获得更像这样的版本2:

'[
{ "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"}        
    ]}
]'
在第二个版本中,我希望将同一查询下重复的所有数据放入一个“其他内容”容器中,其中“rank”下唯一的所有项都位于“results”容器中

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

任何建议,如此转换的正确名称,在大型数据集上执行此操作的最快方法,都将不胜感激

编辑 结合下面@abarnert的优秀解决方案,尝试为处理同类问题的任何其他人获取上面的版本2,需要在一个级别下分叉一些键,在另一个级别下分叉其他键

以下是我尝试过的:

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…),而且可能是解决此问题最具python风格的方法

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

这里的关键步骤是:

你可以一步看出我们已经很接近了

要重新构造每个密钥,请将密钥对分组为所需的dict格式:

>>> 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


在后续操作中,您希望使用相同的
查询
获取每行中相同的所有值,并将它们分组到
其他内容
,然后列出
结果中剩余的内容

因此,对于每个组,我们首先要获得公共密钥。我们可以通过迭代组中任何成员的键来实现这一点(不在第一个成员中的任何内容都不能在所有成员中),因此:

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

def common_fields(group):
    return dict(functools.reduce(set.intersection, (set(d.items()) for d in group)))
我认为
dict
set
之间的来回转换可能会降低可读性,这也意味着您的值必须是可散列的(对于示例数据来说不是问题,因为这些值都是字符串)…但它肯定更简洁

当然,这将始终包括
query
作为一个公共字段,但我们将在后面处理它。(另外,您希望
otherstuff
成为一个带有一个
dict
list
,因此我们将在其周围加上一对括号)

同时,
结果
与上述相同,只是
filterkeys
过滤掉所有公共字段,而不是过滤掉除
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'}]}]

这显然不像最初的版本那么简单,但希望它仍然有意义。只有两个新技巧。首先,我们必须多次迭代
(一次查找公共密钥,然后再次提取剩余密钥)

您在这里提出了两个不同的问题。你想知道功能性折叠风格的实现方式,还是最快的实现方式?我更喜欢两者,但会权衡:如果不是太慢,如果它们是相反的目标,我想学习功能性风格?它们不一定是相反的目标,但它们是完全不同的目标,至少可能相互矛盾。此外,要求、甚至思考最快的做事方式几乎从来都不是一个好主意。首先,获得正确且可读的内容,除了避免明显的big-O算法问题外,不要担心性能。然后,如果您的代码太慢,请对其进行分析,然后重写瓶颈以加快速度,让其余代码尽可能可读。这很有意义,谢谢。我很感激你的守则和建议。我可能错误地认为功能性解决方案通常比手工更快?但我完全理解,即使这是一个个案的事情,我应该把问题分开一点更好!对于大多数函数算法,有一个命令式版本具有相同的big-O复杂性,反之亦然。但是在Python中,由于其相对较高的解释器开销,一些函数解决方案通常更快(在调用
map
的内部循环,或者列表理解比显式
for
循环更快),一些通常更慢(尾部递归与迭代),还有一些差不多。而且有些本来就更容易或更难在其他方面进行优化(例如,转换不可变对象,而不是就地改变对象,这意味着您可以进行CPU并行化)。因此,这肯定是正确的答案,但我可以问一个后续问题,以正确的格式获得我所问的版本2,扩展您提供的优秀解决方案吗?编辑原始问题。请求似乎不明确。在您的示例中,所有行都有相同的额外字段
def common_fields(group):
    return dict(functools.reduce(set.intersection, (set(d.items()) for d in group)))
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'}]}]