Средняя точка каждой пары numpy.array

У меня есть массив формы:

x = np.array([ 1230., 1230., 1227., 1235., 1217., 1153., 1170.])

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

xm = np.array([ 1230., 1228.5, 1231., 1226., 1185., 1161.5])

Кто-то знает самый простой и быстрый способ сделать это без использования циклов?

Ответ 1

Еще короче, немного слаще:

(x[1:] + x[:-1]) / 2

  • Это быстрее:

    >>> python -m timeit -s "import numpy; x = numpy.random.random(1000000)" "x[:-1] + numpy.diff(x)/2"
    100 loops, best of 3: 6.03 msec per loop
    
    >>> python -m timeit -s "import numpy; x = numpy.random.random(1000000)" "(x[1:] + x[:-1]) / 2"
    100 loops, best of 3: 4.07 msec per loop
    
  • Это абсолютно точно:

    Рассмотрим каждый элемент из x[1:] + x[:-1]. Поэтому рассмотрим x₀ и x₁, первый и второй элементы.

    x₀ + x₁ рассчитывается с точностью до точности, а затем округляется в соответствии с IEEE. Поэтому это был бы правильный ответ, если бы это было все, что было необходимо.

    (x₀ + x₁) / 2 составляет лишь половину этого значения. Это почти всегда можно сделать, уменьшив показатель на единицу, за исключением двух случаев:

    • x₀ + x₁ переполнения. Это приведет к бесконечности (любого знака). Это не то, что нужно, поэтому расчет будет неправильным.

    • x₀ + x₁ Недостатки. По мере уменьшения размера округление будет идеальным и, следовательно, вычисление будет правильным.

    Во всех остальных случаях расчет будет правильным.


    Теперь рассмотрим x[:-1] + numpy.diff(x) / 2. Это, проверяя источник, оценивает непосредственно на

    x[:-1] + (x[1:] - x[:-1]) / 2
    

    поэтому рассмотрим еще раз x₀ и x₁.

    x₁ - x₀ будет иметь серьезные "проблемы" с недополнением для многих значений. Это также потеряет точность при больших отмене. Не сразу понятно, что это не имеет значения, если знаки одинаковы, хотя ошибка эффективно отменяется при добавлении. Важно то, что происходит округление.

    (x₁ - x₀) / 2 будет не менее округлым, но затем x₀ + (x₁ - x₀) / 2 включает другое округление. Это означает, что ошибки будут ползать. Доказательство:

    import numpy
    
    wins = draws = losses = 0
    
    for _ in range(100000):
        a = numpy.random.random()
        b = numpy.random.random() / 0.146
    
        x = (a+b)/2 
        y = a + (b-a)/2
    
        error_mine   = (a-x) - (x-b)
        error_theirs = (a-y) - (y-b)
    
        if x != y:
            if abs(error_mine) < abs(error_theirs):
                wins += 1
            elif abs(error_mine) == abs(error_theirs):
                draws += 1
            else:
                losses += 1
        else:
            draws += 1
    
    wins / 1000
    #>>> 12.44
    
    draws / 1000
    #>>> 87.56
    
    losses / 1000
    #>>> 0.0
    

    Это показывает, что для тщательно выбранной константы 1.46 полные 12-13% ответов неверны с вариантом diff! Как и ожидалось, моя версия всегда правильная.

    Теперь рассмотрим underflow. Хотя мой вариант имеет проблемы с переполнением, это гораздо менее значительная сделка, чем проблемы с отменой. Должно быть очевидно, почему двойное округление от вышеуказанной логики очень проблематично. Доказательство:

    ...
        a = numpy.random.random()
        b = -numpy.random.random()
    ...
    
    wins / 1000
    #>>> 25.149
    
    draws / 1000
    #>>> 74.851
    
    losses / 1000
    #>>> 0.0
    

    Да, он ошибается на 25%!

    На самом деле, это не займет много времени, чтобы получить это до 50%:

    ...
        a = numpy.random.random()
        b = -a + numpy.random.random()/256
    ...
    
    wins / 1000
    #>>> 49.188
    
    draws / 1000
    #>>> 50.812
    
    losses / 1000
    #>>> 0.0
    

    Ну, это не так уж плохо. Я думаю, что это всего лишь 1 наименее значимый бит, пока знаки одинаковы.


Итак, у вас это есть. Мой ответ лучший, если вы не найдете среднее значение двух значений, сумма которых превышает 1.7976931348623157e+308 или меньше -1.7976931348623157e+308.

Ответ 2

Короткий и сладкий:

x[:-1] + np.diff(x)/2

То есть, возьмите каждый элемент x, кроме последнего, и добавьте половину разности между ним и последующим элементом.

Ответ 3

Попробуйте следующее:

midpoints = x[:-1] + np.diff(x)/2

Это довольно легко и должно быть быстрым.

Ответ 4

>>> x = np.array([ 1230., 1230., 1227., 1235., 1217., 1153., 1170.])

>>> (x+np.concatenate((x[1:], np.array([0]))))/2
array([ 1230. ,  1228.5,  1231. ,  1226. ,  1185. ,  1161.5,   585. ])

теперь вы можете просто удалить последний элемент, если хотите

Ответ 5

В конечном итоге я использую эту операцию связкой на многомерных массивах, поэтому я опубликую свое решение (вдохновленное исходным кодом для np.diff())

def zcen(a, axis=0):
    a = np.asarray(a)
    nd = a.ndim
    slice1 = [slice(None)]*nd
    slice2 = [slice(None)]*nd
    slice1[axis] = slice(1, None)
    slice2[axis] = slice(None, -1)
    return (a[slice1]+a[slice2])/2

>>> a = [[1, 2, 3, 4, 5], [10, 20, 30, 40, 50]]
>>> zcen(a)
array([[  5.5,  11. ,  16.5,  22. ,  27.5]])
>>> zcen(a, axis=1)
array([[  1.5,   2.5,   3.5,   4.5],
       [ 15. ,  25. ,  35. ,  45. ]])