Как заставить два массива быть равными для использования в pyplot? - PullRequest
0 голосов
/ 04 июля 2019

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

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

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

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()

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

Ответы [ 3 ]

1 голос
/ 04 июля 2019

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

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

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


Редактировать: Это, однако, также имеет свои собственные проблемы, так как действует как дополнительные биты данных со значением 0, когда ваше окно не полностью располагается поверх данных. Это может быть проигнорировано, если выбрано, как это делается в этом решении, но другой подход заключается в заполнении массива конкретными значениями по вашему выбору (см. Ответ Майка Сперри).
1 голос
/ 04 июля 2019

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

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

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

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

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

В целом:

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, но позволяет вам указать количество сэмплов для генерации.Забавно, но np.random.randint имеет исключительную верхнюю границу, больше похоже на random.randrange.

1 голос
/ 04 июля 2019

Вот как вы бы добавили массив numpy к нужной длине с помощью 'nan's' (замените 'Nan' другими значениями или замените 'Constant' другим режимом в зависимости от желаемых результатов) https://docs.scipy.org/doc/numpy/reference/generated/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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...