在pandas数据框中计算交集并集比(Jaccard指数)

6
我有一个类似于数据框的数据:
animal    ids
cat       1,3,4
dog       1,2,4
hamster   5        
dolphin   3,5

数据框很大,有超过 8 万行,ids 列可能包含易于超过数千甚至万个逗号分隔的 id。在给定行中,逗号分隔字符串中的 ids 是唯一的。
我想构建一个数据框,计算 Jaccard 指数,即动物列中每个项与 ids 列中彼此交集的并集。例如,如果我们看猫和狗,它们的并集是 2(ids 1 和 4),交集是 4(ids 1、2、3、4),因此 Jaccard 指数为 2/4=0.5。希望数据集的格式如下:
            cat        dog        hamster    dolphin
cat         1          0.5        0          0.25
dog         0.5        1          0          0
hamster     0          0          1          0.5
dolphin     0.25       0          0.5        1

这意味着使用行索引作为动物的名称,以便我可以快速找到相关的Jaccard指数,例如:
cat_dog_ji = df_new['cat']['dog']
2个回答

5

在这里你可以使用str.get_dummies和一些scipy工具。


from scipy.spatial import distance

u = df["ids"].str.get_dummies(",")
j = distance.pdist(u, "jaccard")
k = df["animal"].to_numpy()
pd.DataFrame(1 - distance.squareform(j), index=k, columns=k)

          cat  dog  hamster  dolphin
cat      1.00  0.5      0.0     0.25
dog      0.50  1.0      0.0     0.00
hamster  0.00  0.0      1.0     0.50
dolphin  0.25  0.0      0.5     1.00

1
很好的使用了 scipy!在这里使用 scipy.distance 在性能方面更好。我正在考虑删除我的答案 ;) +1.. - Shubham Sharma
我认为在分配矩阵j所需的初始内存大小为80000行时,应该非常大,对吗? - Shubham Sharma
你好,感谢你的回答,非常感激。只有一个问题,当我使用我的数据集运行它时,我得到了MemoryError: Unable to allocate 555. GiB for an array with shape (58363, 1277024) and data type int64。 我可以使用int8代替int64吗?也许这样可以帮助节省内存?我该怎么做呢? - Ahmet Cetin
第一条语句就会出现MemoryError: u = df["id"].str.get_dummies(",") - Ahmet Cetin
并且使用int8-它是版本1.1中的默认设置,但自0.23.4以来,您可以在get_dummies中指定dtype: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.get_dummies.html - Itamar Mushkin
显示剩余2条评论

3

用途:

d = df.assign(key=1, ids=df['ids'].str.split(','))
d = d.merge(d, on='key', suffixes=['', '_r'])

i = [np.intersect1d(*x).size / np.union1d(*x).size for x in zip(d['ids'], d['ids_r'])]
d = pd.crosstab(d['animal'], d['animal_r'], i, aggfunc='first').rename_axis(index=None, columns=None)

细节:

使用 DataFrame.assign 创建一个临时列 key,并在列 ids 上使用 Series.str.split。然后使用 DataFrame.merge 基于列 key(本质上是一个交叉连接)将数据框 d 与自身合并。

print(d)

     animal        ids  key animal_r      ids_r
0       cat  [1, 3, 4]    1      cat  [1, 3, 4]
1       cat  [1, 3, 4]    1      dog  [1, 2, 4]
2       cat  [1, 3, 4]    1  hamster        [5]
3       cat  [1, 3, 4]    1  dolphin     [3, 5]
4       dog  [1, 2, 4]    1      cat  [1, 3, 4]
5       dog  [1, 2, 4]    1      dog  [1, 2, 4]
6       dog  [1, 2, 4]    1  hamster        [5]
7       dog  [1, 2, 4]    1  dolphin     [3, 5]
8   hamster        [5]    1      cat  [1, 3, 4]
9   hamster        [5]    1      dog  [1, 2, 4]
10  hamster        [5]    1  hamster        [5]
11  hamster        [5]    1  dolphin     [3, 5]
12  dolphin     [3, 5]    1      cat  [1, 3, 4]
13  dolphin     [3, 5]    1      dog  [1, 2, 4]
14  dolphin     [3, 5]    1  hamster        [5]
15  dolphin     [3, 5]    1  dolphin     [3, 5]

使用 np.interset1dnp.union1d 在列表推导中来计算 Jaccard系数

print(i)
[1.0, 0.5, 0.0, 0.25, 0.5, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.5, 0.25, 0.0, 0.5, 1.0]

最终我们使用pd.crosstab创建一个简单的交叉表,以获得所需格式的结果。
print(d)
          cat  dog  dolphin  hamster
cat      1.00  0.5     0.25      0.0
dog      0.50  1.0     0.00      0.0
dolphin  0.25  0.0     1.00      0.5
hamster  0.00  0.0     0.50      1.0

嗨,感谢您的回答,非常感激。当我尝试运行d = d.merge(d, on='key', suffixes=['', '_r'])时,内核在笔记本中崩溃并重新启动,可能与我拥有的数据集大小有关,有什么想法吗? - Ahmet Cetin
@AhmetCetin 是的,Ahmet,你是对的,这是由于数据集的大小所致。 - Shubham Sharma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接