Numpy Indexing - вопросы о странном поведении - PullRequest
2 голосов
/ 24 февраля 2012

Это тема знаний, основанная на более раннем вопросе, который у меня был сегодня.Вот некоторые странные несоответствия в поведении numpy, свидетелями которого я был.

Во-первых, если вы запустите этот код:

A = ones((10,4))
view = A[:,1]
view.fill(7)
A

Это изменит 2-й столбец на все 7, как массивыиндексируется от 0, а срезы - это просто представления одной и той же матрицы.Круто, это именно то, что я хочу, чтобы произошло.

Теперь, если вы запустите это:

A = ones((10,4))
view = A[:,1:2]
view.fill(7)
A

Это будет иметь те же эффекты, что и в первом примере.Почему a: b определяет столбцы от a до b-1?Есть ли конкретная причина для этого в языке?Кажется, что если я введу, скажем, 1: 3, это даст мне столбцы 1, 2 и 3, а не 1 и 2.

Наконец, если вы запустите это:

A = ones((10,4))
view = A[:,(1,2)]
view.fill(7)
A

Нет никаких побочных эффектов для A. Похоже, что если вы создаете представление с использованием кортежа, оно каким-то образом не правильно распространяет дальнейшие побочные эффекты на исходную матрицу.Есть идеи?

Ответы [ 3 ]

5 голосов
/ 24 февраля 2012

Почему a: b определяет столбцы от a до b-1?

Так работает весь Python, и это большая часть программирования. Он позволяет легко вычислять множество вещей - например, он позволяет длине среза x[a:a + n] быть n и позволяет x[:n] и x[n:] делить x на две части. Вы привыкаете к этому, и в долгосрочной перспективе большинство программистов предпочитают это.

Похоже, что если вы создаете представление с использованием кортежа, оно каким-то образом не правильно распространяет дальнейшие побочные эффекты на исходную матрицу.

Когда вы делаете A[:, (1, 2)], у вас нет представления, но есть новый массив. Когда вы делаете только срезы, как A[:, 1:3], у вас все еще есть непрерывная память для рядов вашего массива, поэтому имеет смысл иметь представления. Когда вы выбираете фрагменты массива с помощью итераций (представьте, что для лучшего понимания того, что вы использовали (0, 2)), было бы неэффективно и неловко иметь bahvior вида.

3 голосов
/ 24 февраля 2012

Почему a: b определяет столбцы от a до b-1?

Это соглашение Python. То же самое верно для обычных списков, и range(a, b) вернет список, содержащий числа от a до b-1 включительно, но не b. Преимущество этого соглашения состоит в том, что при разрезании по a:b, где a и b - числа, будут возвращаться b-a элементы / строки / столбцы вместо более сложных a-b+1.

Похоже, что если вы создаете представление с использованием кортежа, оно каким-то образом не правильно распространяет дальнейшие побочные эффекты на исходную матрицу.

Это идиосинкразия Numpy, вызванная тем, что она может создавать только представления, основанные на срезах; они могут быть эффективно реализованы, в то время как срезы на основе кортежей не могут. Вы можете смоделировать это поведение с помощью следующего фрагмента, который показывает, что синтаксис индекса Python делает под капотом:

class FakeArray(object):
    def __getitem__(self, idx):
        return "You can't change the original FakeArray through me"
    def __setitem__(self, idx, val):
        print("We could set elements %r to %r here" % (idx, val))

Теперь попробуйте

>>> A = FakeArray()
>>> A[1:2]
"You can't change the original FakeArray through me"
>>> A[1:2] = 'ham'
We could set elements slice(1, 2, None) to 'ham'

Итак, A[1:2] - сокращение для A.__getitem__(slice(1, 2, None)), а A[1:2] = 'ham' - сокращение для A.__setitem__(slice(1, 2, None), 'ham'). Поскольку в действительности задействовано два разных метода, поведение среза может сильно отличаться в зависимости от того, является ли оно частью оператора присваивания. В случае Numpy есть тонкое взаимодействие между этой разницей и между slice и tuple объектами.

1 голос
/ 24 февраля 2012

Использование полуоткрытых интервалов не является специфическим для NumPy, оно используется во всем Python.Срезы списков работают точно так же, как и функция range().

Существует несколько преимуществ использования полуоткрытых интервалов в течение закрытых интервалов:

  1. С полуоткрытыми интервалами можно выразить пустые срезы и диапазоны, что было бы трудно для закрытых интервалов.Это часто полезно.

  2. Длина полуоткрытого интервала [a, b) просто задается b - a, что кажется более естественным, чем b - a + 1 для закрытого интервала [a, b]

  3. Смежные интервалы легче выразить.Скажем, у нас есть алгоритм, работающий с кусками k элементов в списке a.Сравните реализацию в Python

    for i in range(0, len(a), k):
        frobnicate(a[i:i + k])
    

    с тем, как будет выглядеть реализация с закрытыми интервалами:

    for i in range(0, len(a) - 1, k):
        frobnicate(a[i:i + k - 1])
    

    В коде будет много - 1, и вы потеряетесвойство, что два интервала являются смежными, если правильное значение первого равно левому значению второго.

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