用Python生成滚动窗口以计算相关性。

5
我有一个大的pandas数据框(97165行和2列),我想计算并保存每100行之间的列之间的相关性,我希望得到以下结果:
第1个相关性 -> 从0到100行 -> corr = 0.265
第2个相关性 -> 从1到101行 -> corr = 0.279
第3个相关性 -> 从2到102行 -> corr = 0.287
每个值都必须被储存,并在图表中显示,因此我必须将所有这些值存储在一个列表或类似于此的东西中。
我一直在阅读与滚动窗口相关的pandas文档,但我没有成功。我尝试生成一个简单的循环来获得一些结果,但是我遇到了内存问题,我尝试过的代码如下:
lcl = 100
a = []
for i in range(len(tabla)):

    x = tabla.iloc[i:lcl, [0]] 
    y = tabla.iloc[i:lcl, [1]]
    z = x['2015_Avion'].corr(y['2015_Hotel'])
    a.append(z) 
    lcl += 1

有什么建议吗?


输入的数据框(Dataframe)中包含什么内容?可以生成一个最小代表数据的样本吗?还有,您在哪一步骤中遇到了内存错误? - Divakar
排序后的整数值 - WinterZ
2个回答

5
我们可以通过处理数组数据来优化内存和性能。 方法1 首先,让我们使用一个数组解决方案来获取两个1D数组之间对应元素的相关系数。这基本上是受到这篇帖子启发的,并且可能看起来像这样 -
def corrcoeff_1d(A,B):
    # Rowwise mean of input arrays & subtract from input arrays themeselves
    A_mA = A - A.mean(-1,keepdims=1)
    B_mB = B - B.mean(-1,keepdims=1)

    # Sum of squares
    ssA = np.einsum('i,i->',A_mA, A_mA)
    ssB = np.einsum('i,i->',B_mB, B_mB)

    # Finally get corr coeff
    return np.einsum('i,i->',A_mA,B_mB)/np.sqrt(ssA*ssB)

现在,要使用它,请使用相同的循环但在数组数据上 -

lcl = 100
ar = tabla.values
N = len(ar)
out = np.zeros(N)
for i in range(N):
    out[i] = corrcoeff_1d(ar[i:i+lcl,0], ar[i:i+lcl,1])

我们可以通过使用卷积来预先计算滚动均值,以计算corrcoeff_1d中使用的A_mA的性能进行进一步优化,但首先让我们解决内存错误问题。
方法#2
这是一个几乎向量化的方法,我们将向量化大部分迭代,除了最后剩余的片段,它们没有正确的窗口长度。循环次数将从97165降至lcl-1,即仅为99。
lcl = 100
ar = tabla.values
N = len(ar)
out = np.zeros(N)

col0_win = strided_app(ar[:,0],lcl,S=1)
col1_win = strided_app(ar[:,1],lcl,S=1)
vectorized_out = corr2_coeff_rowwise(col0_win, col1_win)
M = len(vectorized_out)
out[:M] = vectorized_out

for i in range(M,N):
    out[i] = corrcoeff_1d(ar[i:i+lcl,0], ar[i:i+lcl,1])

辅助函数 -

# https://dev59.com/7FkS5IYBdhLWcg3wSk3b#40085052/ @ Divakar
def strided_app(a, L, S ):  # Window len = L, Stride len/stepsize = S
    nrows = ((a.size-L)//S)+1
    n = a.strides[0]
    return np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n))

# https://dev59.com/IJ7ha4cB1Zd3GeqPhEkG#41703623/ @Divakar
def corr2_coeff_rowwise(A,B):
    # Rowwise mean of input arrays & subtract from input arrays themeselves
    A_mA = A - A.mean(-1,keepdims=1)
    B_mB = B - B.mean(-1,keepdims=1)

    # Sum of squares across rows
    ssA = np.einsum('ij,ij->i',A_mA, A_mA)
    ssB = np.einsum('ij,ij->i',B_mB, B_mB)

    # Finally get corr coeff
    return np.einsum('ij,ij->i',A_mA,B_mB)/np.sqrt(ssA*ssB)

填充NaN数据的相关性

下面列出了使用NumPy解决方案进行基于Pandas计算相关性的方法,用于计算1D数组之间的相关性和行向相关性值。

1)计算两个1D数组之间的标量相关值 -

def nancorrcoeff_1d(A,B):
    # Get combined mask
    comb_mask = ~(np.isnan(A) & ~np.isnan(B))
    count = comb_mask.sum()

    # Rowwise mean of input arrays & subtract from input arrays themeselves
    A_mA = A - np.nansum(A * comb_mask,-1,keepdims=1)/count
    B_mB = B - np.nansum(B * comb_mask,-1,keepdims=1)/count

    # Replace NaNs with zeros, so that later summations could be computed    
    A_mA[~comb_mask] = 0
    B_mB[~comb_mask] = 0

    ssA = np.inner(A_mA,A_mA)
    ssB = np.inner(B_mB,B_mB)

    # Finally get corr coeff
    return np.inner(A_mA,B_mB)/np.sqrt(ssA*ssB)

2) 两个 2D 数组 (m,n) 之间的逐行相关性,得到一个形状为 (m,) 的一维数组 -

def nancorrcoeff_rowwise(A,B):
    # Input : Two 2D arrays of same shapes (mxn). Output : One 1D array  (m,)
    # Get combined mask
    comb_mask = ~(np.isnan(A) & ~np.isnan(B))
    count = comb_mask.sum(axis=-1,keepdims=1)

    # Rowwise mean of input arrays & subtract from input arrays themeselves
    A_mA = A - np.nansum(A * comb_mask,-1,keepdims=1)/count
    B_mB = B - np.nansum(B * comb_mask,-1,keepdims=1)/count

    # Replace NaNs with zeros, so that later summations could be computed    
    A_mA[~comb_mask] = 0
    B_mB[~comb_mask] = 0

    # Sum of squares across rows
    ssA = np.einsum('ij,ij->i',A_mA, A_mA)
    ssB = np.einsum('ij,ij->i',B_mB, B_mB)

    # Finally get corr coeff
    return np.einsum('ij,ij->i',A_mA,B_mB)/np.sqrt(ssA*ssB)

谢谢你的帮助,非常感谢。但是我遇到了与其他方法相同的问题,前77个值都很好,但之后所有的值都是0或nan。我不知道为什么,但这发生在所有解决方案中。 - WinterZ
@WinterZ,正如我之前在评论中所说的 - 生成一个包含最少代表性数据的示例? - Divakar
有一个包含10k行的样本以bz2格式提供 https://ufile.io/xpneh 在我所有的方法中,与你们发生的一样,自相关行77之后我得到了nan值... - WinterZ
@WinterZ,看起来你已经解决了这个问题?现在这两种解决方案都能正常工作吗? - Divakar
问题在于我的数据已经排序,因此有许多重复值。你的解决方案非常好,但在我的情况下,我得到了 nan 值,因为这行代码:A_mA = A - A.mean(-1,keepdims=1)。例如,所有向量都填充为 3 会使 A_mA 等于 0,而当这些值为 0 时,相关性公式无法工作。非常感谢! - WinterZ
@WinterZ请查看末尾的NaN解决方案。使用它们。 - Divakar

3
你提到尝试了“rolling”。具体出了什么问题?这对我有效:

你提到尝试了rolling。具体出了什么问题?这对我有效:

my_res = tabla['2015_Avion'].rolling(100).corr(tabla['2015_Hotel'])

my_res的前 100 个值将会是NaN,因此my_res[99]应该是第一列和第二列中第 0 行和第 99 行的元素之间的相关性,这与只应用于子集的pandas corr函数返回的结果相同。my_res[100]则是第一列和第二列中第 1 行和第 100 行的元素之间的相关性。


大多数输出值都是nan或0,在我的表格中只有从0到10的整数值..所以我不知道为什么会出现nan / 0值。 - WinterZ
你是从某种csv文件中读取这个内容的吗?有可能一些值包含转义字符或者非ASCII字符导致了问题吗?我本来期望它会抛出一个错误,但这取决于corr()函数的工作方式。目前我无法使用一些随机整数重现这个问题。 - Antoine Zambelli
我在Python中没有使用read_pickle或bz2的经验。尝试在一些值上使用print(repr())来检查是否出现任何奇怪的情况(print()可能无法捕获转义字符等)-例如,在给出NaN相关性的窗口中的所有值上。 - Antoine Zambelli
我一直在检查我的数据,发现 nan 值的问题来自于 pandas 的 sort_values 函数...我检查了我的解决方案,没有运行问题。谢谢! - WinterZ
很高兴听到问题得到解决! - Antoine Zambelli
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接