Как интерполировать, чтобы я мог «растянуть» массив из 20 элементов в массив из 30 элементов, но при этом общее и процентили остались прежними? - PullRequest
0 голосов
/ 22 сентября 2019

Если у меня есть этот массив NumPy:

x = np.array([10,20])

, и я хочу «растянуть» его, удвоив его размер, я могу очень легко сделать это с помощью

y = np.repeat(x,2)/2

и получить

[5,5,10,10]

Однако, что, если я хочу растянуть его, добавив не 2, а произвольное количество периодов? Это как если бы я хотел повторить нецелое число в формулевыше.

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

Мне нужно«растянуть» массив, например, вычислить новый массив, где пройдено такое же расстояние, скажем, в 30 периодах вместо 20. Мне нужно, чтобы процентили были одинаковыми, чтобы общее пройденное расстояние было одинаковым, суммапервые 10 элементов в первом массиве = сумма первых 15 в новом массиве и т. д.

Линейная интерполяция в порядке.

Я что-то придумал вместе с scipy.interpolateно это кажется немного запутанным, и я былNdering, если есть лучший способ.Шаги:

  • Я начинаю с моего массива y Я устанавливаю x в качестве соответствующего индекса%, так что 1-й элемент равен 1 / длина, а последний = 100%
  • Iдобавить ноль в начале ox и x и y (чтобы избежать интерполяции ниже ошибки диапазона)
  • Я интерполирую накопленную сумму
  • Я использую интерполированную функцию для вычисления «растянутого» массива

Код такой. РЕДАКТИРОВАТЬ : Я изучил ответ @eliadl.Это очень близко к моему, но не на 100% идентично.Мне непонятно, в чем причина разницы - приветствуется любая информация!

Я собрал код ниже, чтобы показать различия.Мой код достигает того, что я имел в виду: если в исходном массиве было 4 элемента, а в новом 10 и CDF (накопительная функция распределения) = 40% для второго элемента, то CDF должен = 40% для 5-го элементановый массив и т. д.

import numpy as np
import pandas as pd
import scipy
import matplotlib.pyplot as plt
import matplotlib
import seaborn
import seaborn as sns
from matplotlib.ticker import FuncFormatter
sns.set(style='darkgrid')

def my_stretch(inp, s):

    y  = inp
    x = np.arange(1, len(y) +1  ) / len(y)

    y_2 = np.hstack([0,y])
    x_2 = np.hstack([0,x])

    f_int = scipy.interpolate.interp1d(x_2 , np.cumsum(y_2) )

    x_new = np.arange(0, len(y) + s + 1 ) / ( len(y) + s )
    y_new_cum = f_int(x_new)
    y_new = np.diff(y_new_cum)

    return y_new

def your_stretch(inp,s):
    x = np.arange(y.size)
    x_stretch = np.linspace(x[0], x[-1], num = x.size + s )
    y_stretch = np.interp(x_stretch, x, y)
    y_stretch *= y.sum() / y_stretch.sum()
    return y_stretch

def cdf(x):
    return np.cumsum(x) / x.sum()

y = np.array([20,10,8,6,4,2])
s = 3
my_s = my_stretch(y,s)
your_s = your_stretch(y,s)

cdf_orig = cdf(y)
cdf_my = cdf(my_s)
cdf_your = cdf(your_s)

fig, ax = plt.subplots(2,1)

sns.lineplot( np.arange(1,len(my_s) + 1 ) / len(my_s) , cdf_my, label = 'mine', marker='o', ax = ax[0] )
sns.lineplot( np.arange(1,len(your_s) + 1)/len(your_s) , cdf_your, label = 'yours', marker ='o', ax = ax[0] )
sns.lineplot( np.arange(1,len(y) + 1 ) / len(y) , cdf_orig, label = 'original', ax = ax[0] )

ax[1].plot( my_s , label = 'mine' , marker='o' )
ax[1].plot(your_s, label = 'yours',  marker='o')

ax[0].set_xlabel('% position (the last item in the array = 1 ↑')
ax[0].set_ylabel('cumulative distribution function')
ax[1].set_xlabel('item in the array ↑')
ax[1].set_ylabel('value')
ax[1].legend()

1 Ответ

1 голос
/ 23 сентября 2019

Использование np.interp и np.linspace:

y = np.array([20, 10, 8, 6, 4, 2])
stretch_by = 1.5

x = np.arange(y.size)  # [0, 1, 2, 3, 4, 5]

x_stretch = np.linspace(
    start=x[0], stop=x[-1], num=x.size * stretch_by,

)  # [0, 0.625, 1.25, 1.875, 2.5, 3.125, 3.75, 4.375, 5]

y_stretch = np.interp(x_stretch, x, y)  # [20, 13.75, 9.5, 8.25, 7, 5.75, 4.5, 3.25, 2]
y_stretch *= y.sum() / y_stretch.sum()  # normalize y_stretch.sum() to y.sum()

print(f"{y}'s sum is {y.sum()}\n")
print(f"{y_stretch}'s sum is {y_stretch.sum()}")

Выход:

[20 10  8  6  4  2]'s sum is 50

[13.51351351  9.29054054  6.41891892  5.57432432  4.72972973  3.88513514
  3.04054054  2.19594595  1.35135135]'s sum is 50.0
...