我的表格中有pandas.SparseDataFrame
's
A = pd.SparseDataFrame(
[[a,0,0,b],A
[0,0,0,c],
[0,0,0,0],
[0,0,0,a]])
B = pd.SparseDataFrame(
[[a,0,0,0],
[0,0,c,0],
[0,0,0,c],
[0,0,0,0]])
那些是(标记的)来自同一图形的子图的邻接矩阵(因此在相同位置你可以找到0或相同的值: A [x] [y] = B [x] [y]每个值!= 0 )。换句话说,价值冲突是不可能的。
我想合并(性能明智)这些数据帧,结果应为:
[[a,0,0,b],
[0,0,c,c],
[0,0,0,c],
[0,0,0,a]]
我不清楚pandas将2个Dataframes与相同列标签合并的方式。
我看到update()可以按我的意愿运行,但它会就地编辑调用DataFrame,我不希望这样。唯一的方法是使用update()进行深层复制然后合并?正如我所说,操作应该是性能方面的。
答案 0 :(得分:2)
在方法1,2和3中,我们使用来自scipy.sparse
的稀疏矩阵。有许多种稀疏矩阵scipy
支持,而我仅实验{{ 1}},coo_matrix
,csc_matrix
和csr_matrix
。
首先,将您的功能/标签转换为数字,以便我们可以在此示例中使用一些数学属性:{a到1,b到2,以及c到3}。在方法4中,您不需要转换它。
方法1 :使用其中包含的dok_matrix
和dok_matrix
方法。 update
可以对每个元素进行迭代(idx,value)。
update
HOWEVER ,将它加载回熊猫需要很长时间才能合并!这对于方法1,2和3来说都是如此。因此,如果合并的速度是你的浓度,你可能希望坚持使用# to_coo() will make a sparse matrix | todok() will make coo_matrix a dok_matrix
A = pd.SparseDataFrame([[1,0,0,2],[0,0,0,3],[0,0,0,0],[0,0,0,1]]).to_coo().todok()
B = pd.SparseDataFrame([[1,0,0,0],[0,0,3,0],[0,0,0,3], [0,0,0,0]]).to_coo().todok()
nz = B.nonzero() # non-zero's indeces from B
# only update when B is non-zero
A.update([((nz[0][i], nz[1][i]), B[nz[0][i], nz[1][i]]) for i in range(len(nz[0]))])
%timeit A.update([((nz[0][i], nz[1][i]), B[nz[0][i], nz[1][i]]) for i in range(len(nz[0]))])
52.2 µs ± 4.38 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
超过scipy.sparse
。
pandas.SparseDataFrame
此外,%timeit pd.SparseDataFrame(A)
3.47 ms ± 319 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
在其他矩阵操作中可能会很慢。从我的试验中,dok_matrix
在简单矩阵运算中速度很慢,稍后您将在方法2和3中看到。但是,只有dok_matrix
具有函数dok_matrix
。 (update
是基于字典的。)虽然这种方法看起来很快,但我猜测如果你的矩阵密度更大,那么使用这种方法与下一种方法比较慢。
方法2 :在此方法中,我们将dok_matrix
中大于0的部分与元素相乘B - A
以获取{{1}中的部分而不是B
。似乎比下面的方法4快小位。
B
使用A
会稍快一些。
# to_coo() will make a sparse matrix
A = pd.SparseDataFrame([[1,0,0,2],[0,0,0,3],[0,0,0,0],[0,0,0,1]]).to_coo()
B = pd.SparseDataFrame([[1,0,0,0],[0,0,3,0],[0,0,0,3], [0,0,0,0]]).to_coo()
D = ((-A + B) > 0).multiply(B) + A
%timeit D = ((-A + B) > 0).multiply(B) + A
645 µs ± 25.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
方法3 :此方法使用csc_matrix
与A = pd.SparseDataFrame([[1,0,0,2],[0,0,0,3],[0,0,0,0],[0,0,0,1]]).to_coo().tocsc()
B = pd.SparseDataFrame([[1,0,0,0],[0,0,3,0],[0,0,0,3], [0,0,0,0]]).to_coo().tocsc()
%timeit = ((-A + B) > 0).multiply(B) + A
434 µs ± 84.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
中的((B-A) + np.abs(B-A))/2
相同但不在B
中的相等内容。 (A
和A
中的相同值将被抵消,差异将加倍。因此,我们将其除以2。)
B
使用C = ((B - A) + np.abs(B-A))/2 + A
%timeit C = ((B - A) + np.abs(B-A))/2 + A
852 µs ± 25.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
可以加快结果
csc_matrix
方法4 :这是缓慢,缓慢,用于基准测试的目的。 使用此方法的另一个方案是,您不需要将每个标签与整数相匹配。正如您所提到的,我们只需要在{{1}时填写A = pd.SparseDataFrame([[1,0,0,2],[0,0,0,3],[0,0,0,0],[0,0,0,1]]).to_coo().tocsc()
B = pd.SparseDataFrame([[1,0,0,0],[0,0,3,0],[0,0,0,3], [0,0,0,0]]).to_coo().tocsc()
%timeit C = ((-A + B) + np.abs(B-A))/2 + A
598 µs ± 42.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
的值}}。因此,您可以尝试A
,但它会返回密集矩阵。
B == 0
再次注意,如果你真的需要np.where(B == 0, A, B)
,前三种方法都需要将稀疏矩阵转换回A = pd.SparseDataFrame(
[['a',0,0,'b'],
[0,0,0,'c'],
[0,0,0,0],
[0,0,0,'a']])
B = pd.SparseDataFrame(
[['a',0,0,0],
[0,0,'c',0],
[0,0,0,'c'],
[0,0,0,0]])
pd.SparseDataFrame(np.where(B == 0, A, B))
%timeit pd.SparseDataFrame(np.where(B == 0, A, B))
7.39 ms ± 454 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
。这将使前3种方法几乎与此方法4一样慢。
Takeaways:
如果您的矩阵密集,您可能希望方法2优于方法1.(我的假设)
在这种特殊情况下,请使用csc_matrix
或csr_matrix
或dok_matrix
而不是coo_matrix
。但是,我认为不同的稀疏矩阵在不同的场景中会有用。为您特定的应用程序自己计时!
但我没有考虑从pd.SparseDataFrame转移到稀疏矩阵的时间。这不是微不足道的,将在这里讨论。
SparseDataFrame
从上面的实验中可以看出,SparseDataFrame
占用了大部分时间,并且在所有1,2和3种方法中都使用了它。因此,它不会改变这些方法的整体速度比较。此外,这些仅在小数据中测试。请事先对数据进行测试,因为数据越大,行为应该不同。