【问题标题】:How to split a an array into several smaller arrays based on a given criterium between neighbours如何根据邻居之间的给定标准将数组拆分为几个较小的数组
【发布时间】:2019-10-24 08:57:19
【问题描述】:

Mathematica 有两个非常有用的函数,可以根据给定的条件将数组分组为较小数组的列表:Split[] 和 SplitBy[],我需要在 Python3 代码中进行模拟:

Split[list,test] 将函数 "test" 应用于相邻元素对时,将它们视为相同的结果为 True,

SplitBy[list,f] 将列表拆分为子列表,这些子列表由连续元素的运行组成,这些元素在应用 f 时给出相同的值。

如果

a=[2,3,5,7,11,13,17,19,23,29]

Split[a,(#2-#1

[[2,3,5,7],[11,13],[17,19],[23],[29]]

SplitBy[a,(Mod[#,2]==0)&] 给出:

[[2],[3,5,7,11,13,17,19,23,29]]

实际上,要拆分的数组可能是一个二维表,并且测试函数可能对各个列中的元素起作用。

如何在 Python3 中有效地编码这种行为?

【问题讨论】:

    标签: python arrays split wolfram-mathematica


    【解决方案1】:

    对于您的问题的第一部分没有快速答案,lenik 已经提供了一个很好的基于zip 的解决方案,但是使用itertoolsgroupby 函数可以轻松复制SplitBy模块 (doc here)。

    请注意,groupby 将在每次更改键时插入一个分隔符(~ 创建一个新组)。所以如果你想要SplitBy这样的东西,你得先按key函数排序。

    最后,它会给你这样的东西:

    >>> def split_by(l, func):
            groups = []
            sorted_l = sorted(l, key=func)
            for _, g in it.groupby(sorted_l, key=func):
                groups.append(list(g)) 
            return groups
    
    >>> split_by([2,3,5,7,11,13,17,19,23,29], lambda x: x%2)                                                                                                                                                                     
    [[2], [3, 5, 7, 11, 13, 17, 19, 23, 29]]
    

    使用列表推导的单行版本:

    splited_by = [list(g) for _, g in it.groupby(sorted(l, key=func), key=func)]

    在我破旧的笔记本电脑上进行快速时间基准测试:

    • itertools 版本

    >>> %timeit split_by([2,3,5,7,11,13,17,19,23,29], lambda x: x%2) 8.42 µs ± 92.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

    • 压缩版

    >>> %timeit split_by([2,3,5,7,11,13,17,19,23,29], lambda x: x%2) 10.8 µs ± 53.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

    • 尝试/捕获版本

    >>> %timeit split_by([2,3,5,7,11,13,17,19,23,29], lambda x: x%2) 12.6 µs ± 162 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

    【讨论】:

    • 如果反对者至少可以提供反馈或解释我错了的原因,我会很高兴改进这个答案。
    • 我对你的答案投了反对票,但让我澄清一下原因,我不认为答案是错误的,但它有点短,如果你将自己的方式包装成几个函数 split/split_by 并发布简短的 sn-p 应用于提议的操作示例,这足以首先支持您的答案。仅供参考,我不喜欢投反对票,但在这种情况下,我认为这篇文章很有趣,而且这些快速的答案并没有对最初的问题做出公正:)
    • @BPL 感谢您的反馈,我刚刚更新了我的答案;)
    • 好的,然后 +1 ;) 。抱歉,当我投反对票时,我应该在没有您要求的情况下立即提供反馈。事实上,我总是鼓励那些对我的答案投反对票的人来改进它们。谢谢。
    • 有趣的统计数据,感谢发布它们。事实上,这是我昨天发布答案时想要检查的内容,如果内部 try/catch 的开销太大,是的,看起来确实如此。
    【解决方案2】:

    这里有一些可能的解决方案:

    a) 使用 python 内置 zip

    def split(lst, test):
        res = []
        sublst = []
    
        for x, y in zip(lst, lst[1:]):
            sublst.append(x)
            if not test(x, y):
                res.append(sublst)
                sublst = []
    
        if len(lst) > 1:
            sublst.append(lst[-1])
            if not test(lst[-2], lst[-1]):
                res.append(sublst)
                sublst = []
    
        if sublst:
            res.append(sublst)
    
        return res
    
    
    def split_by(lst, test):
        return split(lst, lambda x, y: test(x) == test(y))
    
    
    if __name__ == '__main__':
        a = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
        print(split(a, lambda x, y: y - x < 4))
        print(split_by(a, lambda x: (x % 2) == 0))
    

    b) 带有内部 try/except 的 for 循环:

    def split(lst, test):
        res = []
        sublst = []
    
        for i, x in enumerate(lst):
            try:
                y = lst[i + 1]
                sublst.append(x)
            except IndexError:
                x, y = lst[i - 1], lst[i]
                sublst.append(y)
    
            if not test(x, y):
                res.append(sublst)
                sublst = []
    
        if sublst:
            res.append(sublst)
    
        return res
    
    
    def split_by(lst, test):
        return split(lst, lambda x, y: test(x) == test(y))
    
    
    if __name__ == '__main__':
        a = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
        print(split(a, lambda x, y: y - x < 4))
        print(split_by(a, lambda x: (x % 2) == 0))
    

    输出:

    [[2, 3, 5, 7], [11, 13], [17, 19], [23], [29]]
    [[2], [3, 5, 7, 11, 13, 17, 19, 23, 29]]
    

    【讨论】:

    • 输出中缺少 29。如果你复制粘贴我的代码,至少不要遗漏重要部分......
    • @lenik 编辑了我的答案,但请澄清一下,我考虑从您删除的答案中使用内置 python zip 的事实足以反对+评论我复制粘贴了您的代码?我之前的回答在结构上与你的不同,所以对我来说,这更像是一个简单的幼稚的反对票。不过我可能错了,和平:)
    猜你喜欢
    • 1970-01-01
    • 2010-12-09
    • 2021-10-08
    • 1970-01-01
    • 2017-12-11
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    • 1970-01-01
    相关资源
    最近更新 更多