[英]What is the way to create DataFrame of length of intersections of a list of sets
我有一本裝滿字典的字典。 它可能看起來像這樣:
import pandas as pd
my_dict = {'gs_1': set(('ENS1', 'ENS2', 'ENS3')),
'gs_2': set(('ENS1', 'ENS4', 'ENS5', 'ENS7', 'ENS8')),
'gs_3': set(('ENS2', 'ENS3', 'ENS6'))}
我還構建了一個熊貓DataFrame,看起來像這樣:
my_df = pd.DataFrame(columns=my_dict.keys())
my_df.gs_1=[0, 0, 0]
my_df.gs_2=[0, 0, 0]
my_df.gs_3=[0, 0, 0]
my_df.index = my_dict.keys()
my_df
產量
gs_1 gs_2 gs_3
gs_1 0 0 0
gs_2 0 0 0
gs_3 0 0 0
我的目標是盡可能有效地使用每個集合之間的交點長度填充DataFrame。 嚴格來說,不必先構建DataFrame然后再填充它。 現在,我的解決方案是:
for gs_1 in my_df.index:
for gs_2 in my_df.columns:
my_df.loc[gs_1, gs_2] = len(my_dict[gs_1] & my_dict[gs_2])
my_df
正確的產量
gs_1 gs_2 gs_3
gs_1 3 1 2
gs_2 1 5 0
gs_3 2 0 3
我的問題是,這太慢了。 實際上,gs_n可擴展到6000左右,而我為此預計的運行時間接近2小時。 去這里最好的方法是什么?
這是我基於scipy.spatial.distance_matrix
:
# create unions of values
total = set()
for key, val in my_dict.items():
total = total.union(val)
total = list(total)
# create data frame
df = pd.DataFrame({}, index=total)
for key, val in my_dict.items():
df[key] = pd.Series(np.ones(len(val)), index=list(val))
df = df.fillna(0).astype(bool)
# return result:
x = df.values
np.sum(x[:,np.newaxis,:]&x[:,:,np.newaxis], axis=0)
#array([[3, 1, 2],
# [1, 5, 0],
# [2, 0, 3]], dtype=int32)
# if you want a data frame:
new_df = pd.DataFrame(np.sum(x[:,np.newaxis,:]&x[:,:,np.newaxis],
axis=0),
index=df.columns, columns=df.columns)
以6000 gs_
和100個唯一值gs_
11s:
max_total = 100
my_dict = {}
for i in range(6000):
np.random.seed(i)
sample_size = np.random.randint(1,max_total)
my_dict[i] = np.random.choice(np.arange(max_total), replace=False, size=sample_size)
編輯:如果您有大量唯一值,則可以處理較小的子集,並將它們加起來。 就像是:
chunk_size = 100
ans = np.zeros(num_gs, num_gs)
for x in range(0, len(total), chunk_size):
chunk = total[x:x+chunk_size]
df = pd.DataFrame({}, index=chunk)
for key, val in my_dict.items():
sub_set = val.intersection(set(chunk))
df[key] = pd.Series(np.ones(len(sub_set )), index=list(sub_set ))
df = df.fillna(0).astype(bool)
# return result:
x = df.values
ans += np.sum(x[:,np.newaxis,:]&x[:,:,np.newaxis], axis=0)
如果使用14000個唯一值,則大約需要140 * 15 = 2000秒。 沒那么快,但是明顯少於2小時:-)。
如果內存允許,還可以增加chunk_size
。 那是我的8GB Ram系統的限制:-)。
同樣,也可以並行化子集( chunk
)。
Quang的解決方案效果很好,但是當我嘗試付諸實踐時,它就崩潰了。 即使有了分塊解決方案,我在最后一步也遇到了內存問題:
ans += np.sum(x[:,np.newaxis,:]&x[:,:,np.newaxis], axis=0)
我決定采用另一種方法,並且設法找到了一種解決該問題的解決方案,該解決方案在解決該問題時既更快又更節省內存:
import pandas as pd
import itertools
import numpy as np
my_dict = {'gs_1': set(('ENS1', 'ENS2', 'ENS3')),
'gs_2': set(('ENS1', 'ENS4', 'ENS5', 'ENS7', 'ENS8')),
'gs_3': set(('ENS2', 'ENS3', 'ENS6'))}
gs_series = pd.Series({a:b for a,b in zip(itertools.combinations_with_replacement(my_dict.keys(),2),
[len(c&d) for c,d in itertools.combinations_with_replacement(my_dict.values(),2)])})
gs_df = gs_series.unstack()
proper_index = gs_series.index.get_level_values(0).unique()
gs_df = gs_df.reindex(proper_index)[proper_index.values].copy()
i_lower = np.tril_indices(np.array(len(gs_df.columns)), -1)
gs_matrix = gs_df.values
gs_matrix[i_lower] = gs_matrix.T[i_lower]
gs_df
正確地產生了
gs_1 gs_2 gs_3
gs_1 3.0 1.0 2.0
gs_2 1.0 5.0 0.0
gs_3 2.0 0.0 3.0
基本思想是使用itertools
建立一個字典,其中每兩個集合之間的交點的長度為1,並將其轉換為pd.Series
。 itertools.combinations_with_replacement
一次執行每個比較,因此在pd.Series
,我們具有矩陣的(無序)右上三角形。 按原始索引對行和列進行排序將使我們得到一個正確填充的右上三角,剩下要做的就是將其反映到矩陣的左下三角上。 我最后使用了約8 GB的RAM進行5200x5200矩陣比較,其中每個集合中約有17000個可能的唯一值可填充,每個集合中包含10-1000個唯一值。 幾分鍾就完成了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.