ale*_*ber 5 python similarity pandas scikit-learn sklearn-pandas
我想测量熊猫DataFrame中文本之间的jaccard相似度。更确切地说,我有一些实体组,并且一段时间内每个实体都有一些文本。我想针对每个实体分别分析一段时间内的文本相似度(此处为Jaccard相似度)。
一个最小的例子来说明我的观点:
import pandas as pd
entries = [
{'Entity_Id':'Firm1', 'date':'2001-02-05', 'text': 'This is a text'},
{'Entity_Id':'Firm1', 'date':'2001-03-07', 'text': 'This is a text'},
{'Entity_Id':'Firm1', 'date':'2003-01-04', 'text': 'No similarity'},
{'Entity_Id':'Firm1', 'date':'2007-10-12', 'text': 'Some similarity'},
{'Entity_Id':'Firm2', 'date':'2001-10-10', 'text': 'Another firm'},
{'Entity_Id':'Firm2', 'date':'2005-12-03', 'text': 'Another year'},
{'Entity_Id':'Firm3', 'date':'2002-05-05', 'text': 'Something different'}
]
df = pd.DataFrame(entries)
Run Code Online (Sandbox Code Playgroud)
Entity_Id日期文字
Firm1 2001-02-05 'This is a text'
Firm1 2001-03-07 'This is a text'
Firm1 2003-01-04 'No similarity'
Firm1 2007-10-12 'Some similarity'
Firm2 2001-10-10 'Another firm'
Firm2 2005-12-03 'Another year'
Firm3 2002-05-05 'Something different'
Run Code Online (Sandbox Code Playgroud)
我想要的输出将是这样的:
Entity_Id日期文字Jaccard
Firm1 2001-02-05 'This is a text' NaN
Firm1 2001-03-07 'This is a text' 1
Firm1 2003-01-04 'No similarity' 0
Firm1 2007-10-12 'Some similarity' 0.33
Firm2 2001-10-10 'Another firm' NaN
Firm2 2005-12-03 'Another year' 0.33
Firm3 2002-05-05 'Something different' NaN
Run Code Online (Sandbox Code Playgroud)
也就是说,我喜欢比较一组公司中的所有文本元素,而不管文本之间的时间间隔如何。我想将其始终与之前的文本进行比较。因此,每个公司的第一个条目始终为空,因为没有可比较的文本。
我的方法是将实体标识符的文本移动一个时间间隔(下一个可用日期)。然后确定每个实体的第一个报告并对其进行标记。(我在text_shifted中输入了NaN的原始文本,并稍后将其删除->需要对整个列进行标记化)
df = df.sort_values(['Entity_Id', 'date'], ascending=True)
df['text_shifted'] = df.groupby(['Entity_Id'])['text'].shift(1)
df['IsNaN'] = df['text_shifted'].isnull().astype(int)
df['text_shifted'] = df['text_shifted'].fillna(df['text'])
Run Code Online (Sandbox Code Playgroud)
在下文中,我使用jaccard相似度如下:
def jaccard_similarity(query, document):
intersection = set(query).intersection(set(document))
union = set(query).union(set(document))
return len(intersection)/len(union)
Run Code Online (Sandbox Code Playgroud)
但是我必须先标记输入。但是,如果我做类似的事情:
import nltk
df['text_tokens'] = df.text.apply(nltk.word_tokenize)
df['shift_tokens'] = df.text_shifted.apply(nltk.word_tokenize)
Run Code Online (Sandbox Code Playgroud)
在不简化的文本示例中,将文本标记化需要花费数年的时间,其中每个文本大约有5000个单词,而我大约有100000个文本。
有什么办法可以加快这个过程?我可以避免标记化还是更好地使用sklearn计算相似度?
如果我按照这里建议的那样使用余弦相似度:逐行余弦相似度,我会很快得到结果。但是我一直坚持用jaccard来做。
小智 2
加快该过程的一种方法是使用Pandas on Ray进行并行处理。
您可以尝试 NLTK 实现jaccard_distance来获得 jaccard 相似性。不过,我在处理时间上没有发现任何显着的改进(用于计算相似性),可能在更大的数据集上效果更好。
尝试将 NLTK 实现与自定义 jaccard 相似性函数进行比较(在平均长度为 4 个单词/标记的 200 个文本样本上)
NTLK jaccard_距离:
CPU times: user 3.3 s, sys: 30.3 ms, total: 3.34 s
Wall time: 3.38 s
Run Code Online (Sandbox Code Playgroud)
自定义jaccard相似度实现:
CPU times: user 3.67 s, sys: 19.2 ms, total: 3.69 s
Wall time: 3.71 s
Run Code Online (Sandbox Code Playgroud)