【Python】當你需要計算1,000,000+次一維向量的相關係數, 怎麼算最快?

背景

最近需要用python計算大量的一維向量之間的相關係數, 其中:

        測試數據: (1000, 100)   one of them (100,)

        模板數據: (1000, 100)   one of them (100,)

那麼就需要計算1,000,000次相關係數,那麼在這種情況下, 計算效率就變得很重要了.

直接看提速好幾百倍的升級版->【Python】當你需要計算1,000,000+次一維向量的相關係數, 怎麼算最快(2)?

----------------------以下爲第一次探索這個問題的原文-------------------------

常見的幾種計算相關係數的方法有: 

基於Pandas

        DataFrame.corr()

        實踐了特別慢, 需要構建frame, 不推薦.         

基於Numpy

        1. np.cov()

              這個是算協方差, 後續還需要手寫代碼進一步計算相關係數

        2.np.corrcoef() 

             這個其實就是皮爾森係數的numpy實現, 算線性相關的.

 

基於scipy的三大相關性係數 (pearson係數, spearman係數, kendll係數)

       3.stats.pearsonr()

              皮爾森係數, 協方差cov(X,Y)除以它們各自標準差的乘積(σX, σY).

              計算正態分佈數據的線性關係, 非線性關係上表現差.優點是計算簡單, 結果就是相關係數r, 也比較好理解.

       4.stats.spearmanr()

              斯皮爾曼相關性係數, 可以理解爲秩次之間的相關性, 只計算他們之間分別排序以後的位置的差距, 不在乎真實的數值. 需要用計算出來的p值和一個spearman秩相關係數界值表對比, 比較麻煩, 而且很難用相關的程度來描述. 

       5.stats.kendlltau()

              肯德爾相關性係數,它也是一種秩相關係數, 不過它所計算的對象是分類變量. 適用於兩個分類變量均爲有序分類的情況.只看排序,不看具體數值. 取值範圍在-1-1之間.

 

具體實踐

import numpy as np
import time
from scipy.stats import pearsonr, spearmanr, kendalltau

# Generate random data
a = np.random.random((1000, 100))
b = np.random.random((1000, 100))
corrmat = np.zeros((a.shape[0], b.shape[0]))

tic1 = time.time()
idx = 0
for i in range(a.shape[0]):
    for j in range(b.shape[0]):
        print('calculate:%d' % idx)
        c = np.cov(a[i], b[j])
        corrmat[i][j] = c[0, 1] / np.sqrt(np.prod(np.diag(c)))
        idx += 1
toc1 = time.time()
idx = 0
tic2 = time.time()
for i in range(a.shape[0]):
    for j in range(b.shape[0]):
        print('calculate:%d' % idx)
        corrmat[i][j] = np.corrcoef(a[i], b[j])[0, 1]
        idx += 1
toc2 = time.time()

idx = 0
tic3 = time.time()
for i in range(a.shape[0]):
    for j in range(b.shape[0]):
        print('calculate:%d' % idx)
        corrmat[i][j] = pearsonr(a[i], b[j])[0]
        idx += 1
toc3 = time.time()

idx = 0
tic4 = time.time()
for i in range(a.shape[0]):
    for j in range(b.shape[0]):
        print('calculate:%d' % idx)
        corrmat[i][j] = spearmanr(a[i], b[j])[0]
        idx += 1
toc4 = time.time()

idx = 0
tic5 = time.time()
for i in range(a.shape[0]):
    for j in range(b.shape[0]):
        print('calculate:%d' % idx)
        corrmat[i][j] = kendalltau(a[i], b[j])[0]
        idx += 1
toc5 = time.time()

print('----------------------')
print('Method 1 time: %.2f' % (toc1 - tic1))
print('Method 2 time: %.2f' % (toc2 - tic2))
print('Method 3 time: %.2f' % (toc3 - tic3))
print('Method 4 time: %.2f' % (toc4 - tic4))
print('Method 5 time: %.2f' % (toc5 - tic5))

 output

Method 1 time: 128.00
Method 2 time: 75.88
Method 3 time: 60.78
Method 4 time: 564.74
Method 5 time: 297.49

結論:

       雖然都是計算皮爾森係數,但是scipy的比numpy自身的算的要快, 第一種方法手寫代碼算更慢,  兩種秩排序都比較慢.

       如果你的數據是正態分佈, 連續數據, 線性相關, 最好是用scipy的stats.pearsonr() 來計算.

 

參考文獻:

https://blog.csdn.net/yanjiangdi/article/details/100939969

 

--------------------------------------以下是更新--------------------------------

        本着實驗的原則,還是測試了基於pandas的corr()這個方法的時間, 計算了在method爲皮爾森 (也就是默認方法,這個可以更改爲spearman,或者kendall )  時的時間消耗        

import time
import numpy as np
import pandas as pd

a = np.random.random((1000, 100))
b = np.random.random((1000, 100))
corrmat = np.zeros((a.shape[0], b.shape[0]))

idx = 0
tic6 = time.time()
for i in range(a.shape[0]):
     for j in range(b.shape[0]):
        aa = a[i]
        bb = b[j]
        data = pd.DataFrame({'A':a[i],
                             'B':b[j]})
        corrmat[i][j] = data.corr().values[0, 1]
toc6 = time.time()

output:

         Method 6 time: 810.66

補充結論:

         說過了, 構建dataframe是真的慢, pandas又不是主要做數據計算的, 慢也很合理.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章