如何在Python中计算Jaccard指数?

4

我有一个数据集如下所示:

它显示了哪个书店销售了哪本书。

import pandas as pd

books = {'shop': ["A", "B", "C", "D", "E", "A", "B", "C", "D",],
        'book_id': [1, 1, 2, 3, 3, 3, 4, 5, 1,]
        }

df = pd.DataFrame(books, columns = ['shop', 'book_id'])

以下是打印内容:

  shop  book_id
0    A        1
1    B        1
2    C        2
3    D        3
4    E        3
5    A        3
6    B        4
7    C        5
8    D        1

在数据集中,
  • A店售卖1,3
  • B店售卖1,4
  • C店售卖2,5
  • D店售卖3,1
  • E店仅售卖3
现在,我想要计算Jaccard指数。例如,让我们看A店和B店。有三个不同的书本在A和B两家店铺卖出(书本1、3和4)。然而,只有一个产品在两家店铺都有售卖(这是产品1)。因此,在这里的Jaccard指数应该为33.3%(1/3)。
以下是所需数据的样例:
result = {'shop_1': ["A", "B", "A", "C", "A", "D", "A", "E",],
          'shop_2': ["B", "A", "C", "A", "D", "A", "E", "A",],
          'jaccard':  [33.3, 33.33, 0, 0, 100, 100, 50, 50,]
        }
desired_df = pd.DataFrame(result, columns = ['shop_1', 'shop_2', 'jaccard'])

Print
  shop_1 shop_2  jaccard
0      A      B    33.30
1      B      A    33.33
2      A      C     0.00
3      C      A     0.00
4      A      D   100.00
5      D      A   100.00
6      A      E    50.00
7      E      A    50.00
.      .      .      .
.      .      .      .
.      .      .      .   

有人可以帮我实现吗?是否有库可以实现Jaccard指数?


Scipy可能会有所帮助:https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.jaccard.html - Prateek Dewan
感谢@PrateekDewan,我尝试手动输入示例数据来实现它,但由于我是Python的初学者,很难操作真实数据。 - datazang
1个回答

3

如果您的数据不太大,您可以使用广播(broadcasting)方法:

books = pd.crosstab(df.shop, df.book_id)

# underlying numpy
arr = books.values

common = (arr[None,...] | arr[:,None,:]).sum(-1)

output = (books @ books.T)/common

输出:

shop         A         B    C         D    E
shop                                        
A     1.000000  0.333333  0.0  1.000000  0.5
B     0.333333  1.000000  0.0  0.333333  0.0
C     0.000000  0.000000  1.0  0.000000  0.0
D     1.000000  0.333333  0.0  1.000000  0.5
E     0.500000  0.000000  0.0  0.500000  1.0

为了匹配您期望的输出结果:
output = (output.stack().rename_axis(['shop_1','shop_2'])
                .reset_index(name='jaccard')
                .query('shop_1 != shop_2')
         )

输出:

   shop_1 shop_2   jaccard
1       A      B  0.333333
2       A      C  0.000000
3       A      D  1.000000
4       A      E  0.500000
5       B      A  0.333333
7       B      C  0.000000
8       B      D  0.333333
9       B      E  0.000000
10      C      A  0.000000
11      C      B  0.000000
13      C      D  0.000000
14      C      E  0.000000
15      D      A  1.000000
16      D      B  0.333333
17      D      C  0.000000
19      D      E  0.500000
20      E      A  0.500000
21      E      B  0.000000
22      E      C  0.000000
23      E      D  0.500000

非常感谢 @Quany Hoang。我可以问一下这里的 'a' 代表什么吗:common = (a[None,...] | a[:,None,:]).sum(-1) - datazang
错过了重构,它是在上面定义的numpy数组。请查看更新。 - Quang Hoang
嗨@Quang Hoang,我刚刚尝试使用我的真实数据进行操作,但不幸的是,在运行以下代码时内核已经死亡:pd.crosstab(df.shop, df.book_id)。你认为这是因为我的数据太大了(980 MB)吗?你有什么建议可以帮我解决这个问题吗? - datazang
@datazang 您的数据太大了。您有多少个唯一的书籍ID / 商店? - Quang Hoang
有2900万本书的ID,但我试图获取类别ID(仅240K),而不是book_id。内核仍在崩溃。@Quang Hoang - datazang

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