Numpy безопасное программирование - PullRequest
0 голосов
/ 30 мая 2018

Существуют ли источники или рекомендации по безопасному числовому программированию без ошибок с помощью numpy?

Я спрашиваю, потому что я до боли узнал, что numpy делает много вещей, которые действительно вызывают ошибки, например ...

Добавление матриц разных размеров ("трансляция") без жалоб:

In: np.array([1]) + np.identity(2)
Out: array([[ 2.,  1.],
            [ 1.,  2.]])

Возвращение разных типов данных в зависимости от ввода:

In: scalar1 = 1
In: scalar2 = 1.
In: np.array(scalar1).dtype
Out: dtype('int32')
In: np.array(scalar2).dtype
Out: dtype('float64')

Или просто не выполнять желаемую операцию (опять же, в зависимости от типа данных) без выдачи каких-либо предупреждений:

In: np.squeeze(np.array([[1, 1]])).ndim
Out: 1
In: np.squeeze(np.matrix([[1, 1]])).ndim
Out: 2

Все эти ошибки очень трудно обнаружить, поскольку они не вызывают никаких исключений или предупреждений.и часто возвращают результаты действительных типов данных / форм.Поэтому мой вопрос: Существуют ли общие рекомендации по повышению безопасности и предотвращению ошибок в математическом программировании с помощью numpy?

[Обратите внимание, что я не верю, что этот ответ привлечет "взвешенные ответыи обсуждения ", поскольку речь идет не о личных рекомендациях, а скорее о том, есть ли вообще какие-либо существующие руководящие принципы или источники по этому вопросу, которых я не смог найти.]

Ответы [ 3 ]

0 голосов
/ 30 мая 2018

В некотором смысле, ответ на этот вопрос не отличается от общих рекомендаций по безопасному программированию:

  • Проверяйте и дезинфицируйте код заранее, для каждой функции
  • Поддерживайте соответствующие модульные тесты.

Да, это может звучать как дополнительные накладные расходы, но реальность такова, что вы, вероятно, уже в любом случае уже делаете такие проверки и тесты вручную, поэтому хорошей практикой является положить это на бумагу и формализовать /автоматизировать процесс.Например, хотя вы, возможно, никогда не ожидали конкретно вывода matrix, любой модульный тест, который проверил ваш вывод о том, что ожидаемый array был бы надежно провален.

Возможно, вы также захотите взглянуть на специализированные инструменты тестирования, специфичные для научного кода, например, Гипотеза пакет

Одна вещь, которая - это специфичным для numpy является обработка плавающих ошибок;по умолчанию просто «печатает» оператор предупреждения на стандартный вывод, который может быть легко пропущен (и не учитывает правильную обработку исключений в рабочих процессах).Вы можете преобразовать эту функцию, чтобы выдавать надлежащие предупреждения / исключения, которые вы можете захватить, с помощью метода numpy.seterr - например, numpy.seterr(all='raise').

0 голосов
/ 30 мая 2018

Часто я задаю ТАК вопрошающие, что такое shape?dtype?даже type.Отслеживание этих свойств является важной частью хорошего numpy программирования.Даже в MATLAB я обнаружил, что получение size права было 80% отладки.

type

Пример squeeze вращается вокруг type, класс ndarray против np.matrix подкласс:

In [160]: np.squeeze(np.array([[1, 1]]))
Out[160]: array([1, 1])
In [161]: np.squeeze(np.matrix([[1, 1]]))
Out[161]: matrix([[1, 1]])

np.matrix объект по определению всегда 2d.В этом суть переопределения ndarray операций.

Многие numpy функции делегируют свою работу methods. The code for np.squeeze`:

try:
    squeeze = a.squeeze
except AttributeError:
    return _wrapit(a, 'squeeze')
try:
    # First try to use the new axis= parameter
    return squeeze(axis=axis)
except TypeError:
    # For backwards compatibility
    return squeeze()

Так что In [161] isдействительно:

In [163]: np.matrix([[1, 1]]).squeeze()
Out[163]: matrix([[1, 1]])

np.matrix.squeeze имеет собственную документацию.

Как правило, мы не рекомендуем использовать np.matrix.Он был создан много лет назад, чтобы облегчить жизнь непослушным программистам MATLAB.В те дни у MATLAB были только 2d матрицы (даже сейчас MATLAB 'скаляры' являются 2d).

dtype

np.array - мощная функция.Обычно его поведение интуитивно понятно, но иногда оно делает слишком много предположений.

Обычно он принимает подсказки от ввода, будь то целое число, число с плавающей запятой, строка и / или списки:

In [170]: np.array(1).dtype
Out[170]: dtype('int64')
In [171]: np.array(1.0).dtype
Out[171]: dtype('float64')

Но он предоставляет ряд параметров.Используйте их, если вам нужно больше контроля:

array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0)

In [173]: np.array(1, float).dtype
Out[173]: dtype('float64')
In [174]: np.array('1', float).dtype
Out[174]: dtype('float64')
In [177]: np.array('1', dtype=float,ndmin=2)
Out[177]: array([[1.]])

Посмотрите его документы, а также страницу https://docs.scipy.org/doc/numpy/reference/routines.array-creation.html, на которой перечислены многие другие функции создания массива.Посмотрите также на некоторый их код.

Например, np.atleast_2d выполняет много проверок shape:

def atleast_2d(*arys):
    res = []
    for ary in arys:
        ary = asanyarray(ary)
        if ary.ndim == 0:
            result = ary.reshape(1, 1)
        elif ary.ndim == 1:
            result = ary[newaxis,:]
        else:
            result = ary
        res.append(result)
    if len(res) == 1:
        return res[0]
    else:
        return res

Подобные функции являются хорошими примерами защитного программирования.

Мы получаем много ТАК вопросов о 1d массивах с dtype=object.

In [272]: np.array([[1,2,3],[2,3]])
Out[272]: array([list([1, 2, 3]), list([2, 3])], dtype=object)

np.array пытается создать многомерный массив с униформой dtype.Но если элементы различаются по размеру или не могут быть преобразованы в один и тот же dtype, он вернется к типу object dtype.Это одна из тех ситуаций, когда нам нужно обратить внимание на shape и dtype.

вещание

Вещание было частью numpy навсегда, и нет никакого способавыключая егоOctave и MATLAB добавили его позже и включили предупреждающие переключатели.

Первый защитный шаг - понять принципы вещания, а именно:

  • , он может расширить начальные измерения, чтобы соответствовать
  • это приводит к унитарным измерениям для сопоставления.

Итак, базовый пример:

In [180]: np.arange(3)[:,None] + np.arange(4)
Out[180]: 
array([[0, 1, 2, 3],
       [1, 2, 3, 4],
       [2, 3, 4, 5]])

Первый член (3,) расширен до (3,1).Вторым является (4,), который в результате трансляции расширяется до (1,4).Совместно (3,1) и (1,4) транслируются на (3,4).

У многих простых функций есть параметры, облегчающие отслеживание размеров.Например, sum (и другие) имеет параметр keepdims:

In [181]: arr = _
In [182]: arr.sum(axis=0)
Out[182]: array([ 3,  6,  9, 12])         # (4,) shape
In [183]: arr.sum(axis=0,keepdims=True)
Out[183]: array([[ 3,  6,  9, 12]])       # (1,4) shape
In [184]: arr/_                           # (3,4) / (1,4) => (3,4)
Out[184]: 
array([[0.        , 0.16666667, 0.22222222, 0.25      ],
       [0.33333333, 0.33333333, 0.33333333, 0.33333333],
       [0.66666667, 0.5       , 0.44444444, 0.41666667]])

В этом случае keepdims не является обязательным, поскольку (3,4) / (4,) работает.Но с суммой axis=1 форма становится (3,), которая не может транслироваться с (3,4).Но (3,1) может:

In [185]: arr/arr.sum(axis=1,keepdims=True)
Out[185]: 
array([[0.        , 0.16666667, 0.33333333, 0.5       ],
       [0.1       , 0.2       , 0.3       , 0.4       ],
       [0.14285714, 0.21428571, 0.28571429, 0.35714286]])

Для управления фигурами мне нравится:

  • отображение shape при отладке
  • тестирование фрагментов в интерактивном режиме
  • тест с диагностическими формами, например np.arange(24).reshape(2,3,4)
  • assertion операторы в функциях могут быть полезны assert(arr.ndim==1)

ввод

Последние Python 3В версии добавлен typing модуль

https://docs.python.org/3/library/typing.html

Даже для встроенных типов Python он предварительный.Я не уверен, что многое было добавлено для numpy.

0 голосов
/ 30 мая 2018

Если вы хотите использовать numpy «безопаснее», вам, вероятно, придется создать свою собственную сеть безопасности.Один из способов сделать это - определить оболочки, которые обеспечивают соблюдение правил, которым должен следовать ваш код.Вы можете придумывать свои собственные обертки и тесты по мере продвижения и / или натыкаться на поведение, которое считаете проблематичным.

Некоторые примеры игрушек:

Всегда иметь плавающие массивы:

def arrayf64(*args, **kwargs):
    kwargs.setdefault("dtype", np.float64)
    return np.array(*args, **kwargs)

Отключить трансляцию:

def without_broadcasting(op, arr1, arr2):
    assert arr1.ndim == arr2.ndim
    return op(arr1, arr2)

Предупреждать при использовании np.matrix:

def safe_np_matrix(*args, **kwargs):
    warnings.warn('Unsafe code detected. Usage of np.matrix found.')
    return np.matrix(*args, **kwargs)

np.matrix = safe_np_matrix
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...