【问题标题】:How to build a strategy to create array of tuples with pairs of identical values?如何建立一个策略来创建具有相同值对的元组数组?
【发布时间】:2020-02-04 01:35:58
【问题描述】:

我想为 NumPy 测试生成一个策略,输出如下:

array([[-2, -2],
       [-3, -3],
       [5,  5],
       [-1, -1]], dtype=int16)

我尝试的是:

import numpy as np
from hypothesis.strategies import integers
from hypothesis.extra.numpy import arrays
arrays(np.int16, (4,2), elements=integers(-10, 10)).example()

不幸的是,我无法使元组中的值相同,因此上面的查询返回:

array([[ 5,  5],
       [-7,  5],
       [ 5,  5],
       [ 5,  5]], dtype=int16)

【问题讨论】:

    标签: python python-hypothesis


    【解决方案1】:

    我发现如果我需要控制现有策略结构中的内容(例如数组中的一对相同值),我需要跳过该策略以降低我可以用它来构建一个“现成的”值,该值可以播种我想要生成的类型。

    让我们利用 numpy.array 接受列表列表来创建数组。我们还假设您希望每一行都是唯一的,因为您的示例不显示重复的行。如果不需要,请从 depth_strategy 定义中删除 unique_by=str

    1. 生成一个整数并创建一个重复多次以满足 WIDTH 的值的列表。
    2. 生成我们在第一步中创建的列表类型的 DEPTH 长度列表。
    3. 通过嵌套组合这两种策略。
    4. 将第三步的结果输入 numpy.array,确保 dtype 与第一步中用于生成值的策略相匹配。
    # %%
    """Hypothesis strategy for array of tuples with pairs of identical values."""
    from hypothesis import given, settings, strategies as st
    
    import numpy as np
    
    WIDTH = 2
    DEPTH = 4
    MIN_VALUE = -10
    MAX_VALUE = 10
    
    # Build the row - Here for clarification only
    width_strategy = st.integers(MIN_VALUE, MAX_VALUE).map(
        lambda i: tuple(i for _ in range(WIDTH))
    )
    
    # Build the array of rows - Here for clarification only
    depth_strategy = st.lists(
        width_strategy, min_size=DEPTH, max_size=DEPTH, unique_by=str
    ).map(lambda lot: np.array(lot, dtype=np.int64))
    
    # All-in-One
    complete_strategy = st.lists(
        st.integers(MIN_VALUE, MAX_VALUE).map(
            lambda i: tuple(i for _ in range(WIDTH))
        ),
        min_size=DEPTH,
        max_size=DEPTH,
        unique_by=str,
    ).map(lambda lot: np.array(lot, dtype=np.int64))
    
    
    @settings(max_examples=10)
    @given(an_array=complete_strategy)
    def create_numpy_array(an_array):
        """Turn list of lists into numpy array."""
        print(f"A numpy array could be:\n{an_array}")
    
    
    create_numpy_array()
    

    这会产生类似的东西:

    A numpy array could be:
    [[ 3  3]
     [ 9  9]
     [-5 -5]
     [ 0  0]]
    A numpy array could be:
    [[ 3  3]
     [-2 -2]
     [ 4  4]
     [-5 -5]]
    A numpy array could be:
    [[ 7  7]
     [ 0  0]
     [-2 -2]
     [-1 -1]]
    

    请注意,我将 max_examples 设置为 10,因为假设对它认为“麻烦”的值(例如零、NaN、Infinity 等)给出了更高的出现率。因此 example() 或较少数量的示例可能会生成许多全为零的 2x4 数组。幸运的是,unique_by 约束在这里帮助了我们。

    【讨论】:

    • 我刚刚意识到我应该将 np.array 提升到外部策略的映射中。我已经更新了上面的代码以反映这一点。
    • 谢谢。最终,我重用了您的逻辑并在 @composite 组件中编写了策略。我将联系假设的维护者,以创建科学的大量食谱示例。
    • 关于将其移至 @composite 修饰函数的注意事项。我仍在尝试完全掌握composite v flatmap discussion 的后果以及它如何适用于这样的策略。如果您还没有看过它,可能值得一看。
    • unique_by=lambda l: str(l) -> unique_by=str。 @MarcinCharęziński,我们很乐意链接或合并一本食谱,问题是找时间写一本!
    【解决方案2】:

    无需过多研究 np 所提供的功能,您只需使用生成器生成元组:

    tuple_list = [tuple(a) for a in arrays(np.int16, (4,2), elements=integers(-10,10)).example()]
    

    【讨论】:

    • 感谢您的回复,但该函数返回的元组数组不满足原始问题中所述的条件。
    • “如何建立一个策略来创建具有相同值对的元组数组?”我不确定您到底在寻找什么...
    【解决方案3】:

    不确定这是您所追求的,但来自hypothesis.extra.numpyarrays 似乎没有重复值的选项。

    你可以像这样构造你需要的数组:

    import numpy as np
    from hypothesis.strategies import integers
    strat = integers(10, -10)
    np.array([[x, x] for x in [strat.example() for _ in range(4)]], np.int16)
    

    示例结果:

    array([[-9, -9],
           [ 0,  0],
           [-2, -2],
           [ 0,  0]], dtype=int16)
    

    如果你不喜欢 2 维度被烘焙,你可以同时拥有这样的参数:

    def get_array(rows, cols, strat):
        np.array([[x]*cols for x in [strat.example() for _ in range(rows)]], np.int16)
    
    
    get_array(4, 2, integers(-10, 10))
    

    【讨论】:

    • 注意:此时,您可能想知道为什么要使用 hypothesis - 但您没有提供上下文,因此您可能出于特定原因想要这样做。
    • “来自hypothesis.extra.numpy 的数组似乎没有复制值的选项。”我觉得有一种方法可以创建这样的策略。通过构建自己的策略或扩充或组合现有策略。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-30
    • 2016-07-29
    • 1970-01-01
    • 2010-11-07
    • 2010-11-11
    相关资源
    最近更新 更多