Python 值和过去窗口之间的滚动比较,带有百分位/分位数

Python 值和过去窗口之间的滚动比较,带有百分位/分位数,python,arrays,numpy,data-analysis,moving-average,Python,Arrays,Numpy,Data Analysis,Moving Average,我想将数组的每个值x与前n个值的滚动窗口进行比较。更准确地说,我希望看到如果我们将新值添加到上一个窗口中,新值x的百分比是多少: import numpy as np A = np.array([1, 4, 9, 28, 28.5, 2, 283, 3.2, 7, 15]) print A n = 4 # window width for i in range(len(A)-n): W = A[i:i+n] x = A[i+n] q = sum(W <= x) *

我想将数组的每个值
x
与前n个值的滚动窗口进行比较。更准确地说,我希望看到如果我们将新值添加到上一个窗口中,新值
x
的百分比是多少:

import numpy as np
A = np.array([1, 4, 9, 28, 28.5, 2, 283, 3.2, 7, 15])
print A
n = 4  # window width
for i in range(len(A)-n):
    W = A[i:i+n]
    x = A[i+n]
    q = sum(W <= x) * 1.0 / n
    print 'Value:', x, ' Window before this value:', W, ' Quantile:', q
将numpy导入为np
A=np.数组([1,4,9,28,28.5,2283,3.2,7,15])
打印
n=4#窗宽
对于范围内的i(len(A)-n):
W=A[i:i+n]
x=A[i+n]

q=sum(W您可以使用
np.quantile
而不是
sum(A[i:i+n]您可以使用
np.lib.stride\u技巧。正如您链接的问题中的一样。通过您给出的第一个示例,很容易理解:

A = np.array([1, 4, 9, 28, 28.5, 2, 283, 3.2, 7, 15])
n=4
print (np.lib.stride_tricks.as_strided(A, shape=(A.size-n,n),
                                       strides=(A.itemsize,A.itemsize)))
# you get the A.size-n columns of the n rolling elements
array([[  1. ,   4. ,   9. ,  28. ,  28.5,   2. ],
       [  4. ,   9. ,  28. ,  28.5,   2. , 283. ],
       [  9. ,  28. ,  28.5,   2. , 283. ,   3.2],
       [ 28. ,  28.5,   2. , 283. ,   3.2,   7. ]])
现在要进行计算,您可以将此数组与行上的[n:]、
sum
进行比较,然后除以
n

print ((np.lib.stride_tricks.as_strided(A, shape=(n,A.size-n),
                                        strides=(A.itemsize,A.itemsize)) 
          <= A[n:]).sum(0)/(1.*n))
[1.   0.   1.   0.25 0.5  0.75] # same anwser
nb_chunk = 1000 #this number depends on the capacity of you computer, 
                # not sure how to optimize it
Q = np.concatenate([compare_strides(A[chunk*nb_chunk:(chunk+1)*nb_chunk+n],n) 
                    for chunk in range(0,A[n:].size/nb_chunk+1)])/(1.*n)
我无法进行1M-5000测试,但在5000-100上,请查看
timeit
中的差异:

A = np.random.random(5000)
n = 100

%%timeit
Q = np.zeros(len(A)-n)
for i in range(len(Q)):
    Q[i] = sum(A[i:i+n] <= A[i+n]) * 1.0 / n

#1 loop, best of 3: 6.75 s per loop

%%timeit
nb_chunk = 100
Q1 = np.concatenate([compare_strides(A[chunk*nb_chunk:(chunk+1)*nb_chunk+n],n) 
                    for chunk in range(0,A[n:].size/nb_chunk+1)])/(1.*n)

#100 loops, best of 3: 7.84 ms per loop

#check for egality
print ((Q == Q1).all())
Out[33]: True
A=np.random.random(5000)
n=100
%%时间
Q=np.零(len(A)-n)
对于范围内的i(len(Q)):

Q[i]=sum(A[i:i+n]您的代码非常慢,因为您使用的是Python自己的
sum()
而不是
numpy.sum()
numpy.array.sum()
;Python的
sum()
必须在进行计算之前将所有原始值转换为Python对象,这非常慢。只需更改
sum(…)<代码> >代码> No.PoS>(或…>)或<代码>(…)(SUME)/<代码>,运行时间下降到20秒以下。

< P>使用<代码> NP。求和> /代码>而不是已经和了,所以我唯一的建议是另外考虑使用大熊猫及其滚动窗口函数,您可以将任意函数应用到:

import numpy as np
import pandas as pd

A = np.random.random(1000*1000)
df = pd.DataFrame(A)
n = 5000

def fct(x):
    return np.sum(x[:-1] <= x[-1]) * 1.0 / (len(x)-1)

percentiles = df.rolling(n+1).apply(fct)
print(percentiles)
将numpy导入为np
作为pd进口熊猫
A=np.随机.随机(1000*1000)
df=pd.数据帧(A)
n=5000
def fct(x):

返回np.sum(x[:-1]附加基准:和之间的比较:

添加numba并行化时,速度更快:1.8秒!

import numpy as np
from numba import jit, prange

@jit(parallel=True)
def doit(A, Q, n):
    for i in prange(len(Q)):
        Q[i] = np.sum(A[i:i+n] <= A[i+n])

A = np.random.random(1000*1000)
n = 5000
Q = np.zeros(len(A)-n)    
doit(A, Q, n)
将numpy导入为np
从numba导入jit、prange
@jit(并行=真)
def doit(A,Q,n):
因为我在普拉格(伦(Q)):

Q[i]=np.sum(A[i:i+n]你确定它会给出同样的结果吗?我不这么认为。
np.quantile(W,50)
会给出x的值,使得W分位数实际上不是
百分位数的倒数,我匆忙地假设了这一点。它们几乎是一样的。这也是一个很好的解决方案!我花了大约25秒的时间来计算1M个项目,n=5000,就像AleksiTorhamo的回答一样。很高兴知道。但是,在我的手机上,这种方法似乎会对循环带来性能缺陷。但也许可以通过立即将所有结果放入数据帧中以备进一步处理来平衡。如果你想使用
np.sum
,你甚至可以改进t他通过这种方式加快了速度。据我所知,在创建数组时直接使用列表理解比创建一个空数组要快,然后必须在每个循环中访问它来更改值(访问数组中的值需要时间)。另一个原因是将整个数组除以n,而不是每个值都会随着除法的矢量化而加快。创建数组时强制使用
dtype=float
似乎比将
int
数组转换为
float
更快(内存原因?)。使用
np.less_equal
@Ben.T谢谢,我编辑了答案。我还尝试了numba,看看结果;)与原始解决方案相比,这是一个非常好的改进:)@Ben.T…还有一个x3改进,感谢numba并行化;)这里创建一个视图,作为一个2D矩阵,包含所有连续窗口。它给出的结果与。你知道@Ben.T?@Basj的精确和计算差异吗?如果你看到你给出的链接的代码,实际上是
skimage
使用
as__crossed
from
numpy
结尾的方法,因此它使我认为结果是一样的。
import numpy as np
import pandas as pd

A = np.random.random(1000*1000)
df = pd.DataFrame(A)
n = 5000

def fct(x):
    return np.sum(x[:-1] <= x[-1]) * 1.0 / (len(x)-1)

percentiles = df.rolling(n+1).apply(fct)
print(percentiles)
import numpy as np, time

A = np.random.random(1000*1000)
n = 5000

def compare_strides (arr, n):
   return (np.lib.stride_tricks.as_strided(arr, shape=(n,arr.size-n), strides=(arr.itemsize,arr.itemsize)) <= arr[n:]).sum(0)

# Test #1: with strides ===> 11.0 seconds
t0 = time.time()
nb_chunk = 10*1000
Q = np.concatenate([compare_strides(A[chunk*nb_chunk:(chunk+1)*nb_chunk+n],n) for chunk in range(0,A[n:].size/nb_chunk+1)])/(1.*n)
print time.time() - t0, Q

# Test #2: with just np.sum ===> 18.0 seconds
t0 = time.time()
Q2 = np.zeros(len(A)-n)
for i in range(len(Q2)):
    Q2[i] = np.sum(A[i:i+n] <= A[i+n])
Q2 *= 1.0 / n  # here the multiplication is vectorized; if instead, we move this multiplication to the previous line: np.sum(A[i:i+n] <= A[i+n]) * 1.0 / n, it is 6 seconds slower
print time.time() - t0, Q2

print all(Q == Q2)
from numba import jit
import numpy as np

@jit  # if you remove this line, it is much slower (similar to Test #2 above)
def doit():
    A = np.random.random(1000*1000)
    n = 5000
    Q2 = np.zeros(len(A)-n)
    for i in range(len(Q2)):
        Q2[i] = np.sum(A[i:i+n] <= A[i+n])
    Q2 *= 1.0/n
    print(Q2)

doit()
import numpy as np
from numba import jit, prange

@jit(parallel=True)
def doit(A, Q, n):
    for i in prange(len(Q)):
        Q[i] = np.sum(A[i:i+n] <= A[i+n])

A = np.random.random(1000*1000)
n = 5000
Q = np.zeros(len(A)-n)    
doit(A, Q, n)