我有一個名為“矩陣”的熊貓資料框,它看起來像這樣:
antecedent_sku consequent_sku similarity
0 001 002 0.3
1 001 003 0.2
2 001 004 0.1
3 001 005 0.4
4 002 001 0.4
5 002 003 0.5
6 002 004 0.1
在這個資料框中,我想創建一個相似矩陣以進行進一步的聚類。我分兩步做。
第 1 步:創建一個空的相似度矩陣('similarity')
set_name = set(matrix['antecedent_sku'].values)
similarity = pd.DataFrame(index = list(set_name), columns = list(set_name))
第 2 步:用“矩陣”中的值填充它:
for ind in tqdm(list(similarity.index)):
for col in list(similarity.columns):
if ind==col:
similarity.loc[ind, col] = 1
elif len(matrix.loc[(matrix['antecedent_sku'].values==f'{ind}') & (matrix['consequent_sku'].values==f'{col}'), 'similarity'].values) < 1:
similarity.loc[ind, col] = 0
else:
similarity.loc[ind, col] = matrix.loc[(matrix['antecedent_sku'].values==f'{ind}') & (matrix['consequent_sku'].values==f'{col}'), 'similarity'].values[0]
問題:填充形狀矩陣(3000,3000)需要 4 個小時。
問題:我做錯了什么?我是否應該使用 Cython/Numba 之類的東西來加速代碼,或者問題在于我的方法的架構,我應該使用內置函式或其他一些聰明的方法將“矩陣”轉換為“相似性”而不是雙環形?
PS我運行Python 3.8.7
uj5u.com熱心網友回復:
loc眾所周知,使用 pandas 資料幀迭代非常慢。眾所周知,CPython 解釋器也很慢(通常是回圈)。每個 pandas 操作都有很高的開銷。但是,要點是您迭代 3000x3000 個元素,以便呼叫每個元素,例如matrix['antecedent_sku'].values==f'{ind}'肯定會迭代 3000 個字串專案,這些專案也被稱為低效資料型別(因為處理器需要決議可變長度的 UTF-8多個字符的序列)。由于每次迭代都會執行兩次,并且每次比較都會決議一個新整數,這意味著3000*3000*3000*2 = 54_000_000_000將執行字串比較,并且3000*3000*3000*2*2*3 = 324_000_000_000(低效地)比較整個字符!這不可能很快,因為這是非常低效的. 更不用說每次 9_000_000 次迭代都會創建/洗掉幾個臨時陣列和 Pandas 物件。
首先要做的是通過一些預計算來減少重新計算操作的數量。實際上,您可以將 matrix['antecedent_sku'].values==f'{ind}'(作為 Numpy 陣列,因為熊貓系列效率低下)的值存盤在由 so 索引的字典中,ind以便在回圈中更快地獲取它。這應該使這部分快 3000 倍(因為應該只有 3000 個專案)。更好的是:您可以使用 groupby更有效地做到這一點。
此外,您可以將列轉換為整數(即antecedent_sku?? 和consequent_sku)以避免許多昂貴的字串比較。
然后您可以洗掉無用的操作,例如matrix.loc[..., 'similarity'].values. 事實上,由于您只想知道結果的長度,您可以只使用np.sum二進制 numpy 陣列。實際上,您甚至可以使用,np.any因為您檢查了長度是否小于 1。
然后,您可以通過在 Numpy 操作中指定輸出緩沖區來避免使用預分配緩沖區創建臨時 Numpy 陣列。例如,您可以使用而不是僅使用.np.logical_and(A, B, out=your_preallocated_buffer)A & B
最后,如果(且僅當)所有前面的步驟都不足以使整體計算速度提高數百或數千倍,您可以通過首先將資料幀轉換為 Numpy 陣列來使用 Numba(因為 Numba 不支持資料幀)。如果這還不夠,您可以使用prange(而不是range)和parallel=TrueNumba 的標志來使用多個執行緒。
請注意,Pandas 并不是真正設計來操作 3000 列的資料幀,因此肯定不會很快。Numpy 更適合處理矩陣。
uj5u.com熱心網友回復:
繼杰羅姆(Jerome)的字典后,我做了以下事情:
第一步:創建字典
matrix_dict = matrix.copy()
matrix_dict = matrix_dict.set_index(['antecedent_sku', 'consequent_sku'])['similarity'].to_dict()
matrix_dict 看起來像這樣:
{(001, 002): 0.3}
第 2 步:用來自 matrix_dict 的值填充相似度
for ind in tqdm(list(similarity.index)):
for col in list(similarity.columns):
if ind==col:
similarity.loc[ind, col] = 1
else:
similarity.loc[ind, col] = matrix_dict.get((int(ind), int(col)))
第 3 步:用零填充
similarity = similarity.fillna(0)
結果:x35 性能(4 小時 20 分鐘到 7 分鐘)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/459250.html
