首页 > 解决方案 > 创建集合列表的交集长度的DataFrame的方法是什么

问题描述

我有一本装满集合的字典。它可能看起来像这样:

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'))}

我还构建了一个看起来像这样的 pandas 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 小时。去这里的最佳方式是什么?

标签: pythonpandas

解决方案


这是我的方法基于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)

6000gs_和 100 个唯一值花费了 11 秒:

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) 上进行并行化。


推荐阅读