Как построить стратегию создания массива кортежей с парами одинаковых значений?

Я хотел бы создать стратегию для тестирования 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)

person Marcin Charęziński    schedule 20.01.2020    source источник


Ответы (3)


Я обнаружил, что если мне нужно управлять содержимым внутри существующей структуры стратегии (например, пары одинаковых значений внутри массива), мне нужно пропустить эту стратегию для более низкоуровневых с помощью которое я могу создать «готовое» значение, которое может засеять тип, который я хочу сгенерировать.

Давайте воспользуемся тем, что numpy.array принимает список списков для создания массива. Предположим также, что вы хотите, чтобы каждая строка была уникальной, поскольку в вашем примере не отображаются повторяющиеся строки. Если это нежелательно, удалите unique_by=str из определения depth_strategy.

  1. Сгенерируйте целое число и создайте список этого значения, повторяющийся несколько раз, чтобы соответствовать ШИРИНЕ.
  2. Сгенерируйте список длины ГЛУБИНЫ из тех списков, которые мы создали на первом шаге.
  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.

person Arnoud    schedule 20.01.2020
comment
Я только что понял, что мне следовало перенести np.array в отображение внешней стратегии. Я обновил приведенный выше код, чтобы отразить это. - person Arnoud; 22.01.2020
comment
Спасибо. В конце концов, я повторно использовал вашу логику и составил стратегию внутри компонента @composite. Я обращусь к сторонникам гипотезы о создании научных примеров из тяжелых кулинарных книг. - person Marcin Charęziński; 22.01.2020
comment
Обратите внимание на то, чтобы переместить его в функцию, украшенную @composite. Я все еще пытаюсь полностью понять последствия обсуждения составного v плоской карты и как это можно применить к подобным стратегиям. Возможно, стоит взглянуть, если вы еще не смотрели на это. - person Arnoud; 23.01.2020
comment
unique_by=lambda l: str(l) - ›unique_by=str. @ MarcinCharęziński, мы были бы очень рады связать или объединить кулинарную книгу, проблема в том, чтобы найти время, чтобы написать ее! - person Zac Hatfield-Dodds; 24.01.2020

Не вдаваясь в подробности того, что предлагает np, вы можете просто сгенерировать кортежи с помощью генератора:

tuple_list = [tuple(a) for a in arrays(np.int16, (4,2), elements=integers(-10,10)).example()]
person Josh Sharkey    schedule 20.01.2020
comment
Спасибо за ответ, но эта функция возвращает массив кортежей, который не удовлетворяет условиям, указанным в исходном вопросе. - person Marcin Charęziński; 20.01.2020
comment
Как построить стратегию для создания массива кортежей с парами одинаковых значений? Я не уверен, что именно вы ищете ... - person Josh Sharkey; 20.01.2020

Не уверен, что это именно то, что вам нужно, но arrays из hypothesis.extra.numpy, похоже, не имеет вариантов для дублирования значений.

Вы можете просто построить нужный массив следующим образом:

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 запечен, вы можете иметь оба параметра be, например:

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))
person Grismar    schedule 20.01.2020
comment
Примечание: на этом этапе вы можете задаться вопросом, зачем вообще использовать hypothesis - но вы не предоставили контекст, поэтому вы можете захотеть это сделать по определенным причинам. - person Grismar; 20.01.2020
comment
массивы из hypothesis.extra.numpy, похоже, не имеют параметров для дублирования значений. Я чувствую, что есть способ создать такую ​​стратегию. Либо выстраивая собственную стратегию, либо дополняя или дополняя существующую. - person Marcin Charęziński; 20.01.2020