我有所有浮动列的数据框。例如:
import numpy as np import pandas as pd df = pd.DataFrame(np.arange(12.0).reshape(3,4), columns=list('ABCD')) # A B C D # 0 0.0 1.0 2.0 3.0 # 1 4.0 5.0 6.0 7.0 # 2 8.0 9.0 10.0 11.0
我想为列的所有组合(例如AB,AC,BC等)计算按列的差异。
例如,所需的输出如下所示:
A_B A_C A_D B_C B_D C_D -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0
由于列数可能很大,因此我想尽可能高效/快速地进行计算。我假设我会先将数据帧转换为numpy数组,从而获得很大的提速,所以我会这样做,但是我想知道是否还有其他策略可能会导致性能大幅提高。也许某些矩阵代数或多维数据格式技巧使您不必遍历所有唯一组合。欢迎任何建议。该项目在Python 3中。
这篇文章中列出了两种NumPy提高性能的方法-一种是完全矢量化的方法,另一种是一个循环的方法。
方法1
def numpy_triu1(df): a = df.values r,c = np.triu_indices(a.shape[1],1) cols = df.columns nm = [cols[i]+"_"+cols[j] for i,j in zip(r,c)] return pd.DataFrame(a[:,r] - a[:,c], columns=nm)
样品运行-
In [72]: df Out[72]: A B C D 0 0.0 1.0 2.0 3.0 1 4.0 5.0 6.0 7.0 2 8.0 9.0 10.0 11.0 In [78]: numpy_triu(df) Out[78]: A_B A_C A_D B_C B_D C_D 0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 1 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 2 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0
方法#2
如果我们可以将数组作为输出或数据框使用而无需特殊的列名,那么这是另一个-
def pairwise_col_diffs(a): # a would df.values n = a.shape[1] N = n*(n-1)//2 idx = np.concatenate(( [0], np.arange(n-1,0,-1).cumsum() )) start, stop = idx[:-1], idx[1:] out = np.empty((a.shape[0],N),dtype=a.dtype) for j,i in enumerate(range(n-1)): out[:, start[j]:stop[j]] = a[:,i,None] - a[:,i+1:] return out
运行时测试
由于OP提到多维度数组输出也适用于它们,因此以下是其他作者基于数组的方法-
# @Allen's soln def Allen(arr): n = arr.shape[1] idx = np.asarray(list(itertools.combinations(range(n),2))).T return arr[:,idx[0]]-arr[:,idx[1]] # @DYZ's soln def DYZ(arr): result = np.concatenate([(arr.T - arr.T[x])[x+1:] \ for x in range(arr.shape[1])]).T return result
pandas @Gerges Dib的帖子中没有提供基于解决方案的解决方案,因为与其他解决方案相比,该解决方案的运行速度非常慢。
pandas
时间-
我们将使用三个数据集的大小- 100,500和1000:
100
500
1000
In [118]: df = pd.DataFrame(np.random.randint(0,9,(3,100))) ...: a = df.values ...: In [119]: %timeit DYZ(a) ...: %timeit Allen(a) ...: %timeit pairwise_col_diffs(a) ...: 1000 loops, best of 3: 258 µs per loop 1000 loops, best of 3: 1.48 ms per loop 1000 loops, best of 3: 284 µs per loop In [121]: df = pd.DataFrame(np.random.randint(0,9,(3,500))) ...: a = df.values ...: In [122]: %timeit DYZ(a) ...: %timeit Allen(a) ...: %timeit pairwise_col_diffs(a) ...: 100 loops, best of 3: 2.56 ms per loop 10 loops, best of 3: 39.9 ms per loop 1000 loops, best of 3: 1.82 ms per loop In [123]: df = pd.DataFrame(np.random.randint(0,9,(3,1000))) ...: a = df.values ...: In [124]: %timeit DYZ(a) ...: %timeit Allen(a) ...: %timeit pairwise_col_diffs(a) ...: 100 loops, best of 3: 8.61 ms per loop 10 loops, best of 3: 167 ms per loop 100 loops, best of 3: 5.09 ms per loop