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

ValueError: x and y must have same first dimension, but have shapes (96,) and (100,)

Это использует стандарт matplotlib.pyplot. Я попытался просто удалить значения X с помощью remove и del, а также переключить все массивы на массивы с нулевыми значениями (поскольку это выходной формат моей функции скользящих средних), а затем попытался добавить условие if в добавление в то время как цикл, но ни один не работал.

import random
import matplotlib
import matplotlib.pyplot as plt
import numpy as np

def movingaverage(values, window):
    weights = np.repeat(1.0, window) / window
    smas = np.convolve(values, weights, 'valid')
    return smas

sampleSize = 100
min = -10
max = 10
window = 5

vX = np.array([])
vY = np.array([])

x = 0
val = 0
while x < sampleSize:
    val += (random.randint(min, max))
    vY = np.append(vY, val)
    vX = np.append(vX, x)
    x += 1

plt.plot(vX, vY)
plt.plot(vX, movingaverage(vY, window))
plt.show()

Ожидаемые результаты будут двумя линиями на одном графике - одна простая скользящая средняя другого.

1
mistEident 4 Июл 2019 в 05:42

3 ответа

Лучший ответ

Просто измените эту строку на следующую:

smas = np.convolve(values, weights,'same')

Опция 'valid' сворачивается, только если окно полностью покрывает массив значений. То, что вы хотите, это «то же самое», которое делает то, что вы ищете.


Редактировать:
1
Akaisteph7 4 Июл 2019 в 04:41

Чтобы ответить на ваш основной вопрос, нужно взять срез оси X, соответствующий данным скользящего среднего. Поскольку у вас есть свертка из 100 элементов данных с окном размером 5, результат действителен для последних 96 элементов. Вы бы построили это так:

plt.plot(vX[window - 1:], movingaverage(vY, window))

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

Во-вторых, запуск явного цикла редко необходим. Как правило, лучше использовать циклы «под капотом», реализованные на самом низком уровне в простых функциях. Это называется векторизацией. Генерация случайных чисел, кумулятивные суммы и инкрементные массивы полностью векторизованы в виде числа. В более общем смысле обычно не очень эффективно смешивать вычислительные функции Python и numpy, включая random.

Наконец, вы можете рассмотреть другой метод свертки. Я хотел бы предложить что-нибудь на основе {{X0} }. Это несколько загадочный, но очень эффективный способ реализовать скользящее окно с массивными массивами. Я покажу это здесь как альтернативу методу свертки, который вы использовали, но не стесняйтесь игнорировать эту часть.

Вобщем:

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

def movingaverage(values, window):
    # this step creates a view into the same buffer
    values = np.lib.stride_tricks.as_strided(values, shape=(window, values.size - window + 1), strides=values.strides * 2)
    smas = values.sum(axis=0)
    smas /= window  # in-place to avoid temp array
    return smas

sampleSize = 100
min = -10
max = 10
window = 5

v_x = np.arange(sampleSize)
v_y = np.cumsum(np.random.random_integers(min, max, sampleSize))

plt.plot(v_x, v_y)
plt.plot(v_x[window - 1:], movingaverage(v_y, window))
plt.show()

Примечание к именам: в Python имена переменных и функций условно называются name_with_underscore. CamelCase зарезервирован для имен классов. np.random.random_integers использует инклюзивные границы, как и random.randint , но позволяет указать количество сэмплов для генерации. Смущает, {{X2} } имеет эксклюзивную верхнюю границу, более похожую на {{X3 } }.

1
Mad Physicist 4 Июл 2019 в 04:07

Вот как вы бы добавили массив numpy к нужной длине с помощью 'nan's' (замените 'Nan' другими значениями или замените 'constant' другим режимом в зависимости от желаемых результатов) https://docs.scipy.org/doc/numpy/ ссылка / полученные / numpy.pad.html

import numpy as np
bob = np.asarray([1,2,3])
alice = np.pad(bob,(0,100-len(bob)),'constant',constant_values=('nan','nan'))

Так что в вашем коде это будет выглядеть примерно так:

import random
import matplotlib
import matplotlib.pyplot as plt
import numpy as np

def movingaverage(values,window):
    weights = np.repeat(1.0,window)/window
    smas = np.convolve(values,weights,'valid')
    shorted = int((100-len(smas))/2)
    print(shorted)
    smas = np.pad(smas,(shorted,shorted),'constant',constant_values=('nan','nan'))
    return smas

sampleSize = 100
min = -10
max = 10
window = 5

vX = np.array([])
vY = np.array([])

x = 0
val = 0
while x < sampleSize:
    val += (random.randint(min,max))
    vY = np.append(vY,val)
    vX = np.append(vX,x)
    x += 1
plt.plot(vX,vY)
plt.plot(vX,(movingaverage(vY,window)))
plt.show()
1
4 Июл 2019 в 03:29