Python 如何高效地使用pandas列和字典构建功能?

Python 如何高效地使用pandas列和字典构建功能?,python,pandas,dataframe,machine-learning,feature-extraction,Python,Pandas,Dataframe,Machine Learning,Feature Extraction,我有一个机器学习问题,我用字典的值计算熊猫数据框文本列的二元Jaccard相似性。目前我将它们存储为列表,然后将它们转换为列。这被证明是非常缓慢的生产。有没有更有效的方法 以下是我目前正在执行的步骤: 对于dict中的每个键: 1.为pandas专栏和dict[key]获取bigram 2.计算卡片相似度 3.附加到空列表 4.将列表存储在数据框中 5.将列表转换为列 来自itertools导入三通,islice def计数图(lst,n): tlst=lst 尽管如此: a、 b=三通(tls

我有一个机器学习问题,我用字典的值计算熊猫数据框文本列的二元Jaccard相似性。目前我将它们存储为列表,然后将它们转换为列。这被证明是非常缓慢的生产。有没有更有效的方法

以下是我目前正在执行的步骤: 对于dict中的每个键: 1.为pandas专栏和dict[key]获取bigram 2.计算卡片相似度 3.附加到空列表 4.将列表存储在数据框中 5.将列表转换为列

来自itertools导入三通,islice
def计数图(lst,n):
tlst=lst
尽管如此:
a、 b=三通(tlst)
l=元组(islice(a,n))
如果len(l)==n:
产量l
下一(b)
tlst=b
其他:
打破
def n_gram_jaccard_相似性(str1、str2、n):
a=设置(计数内存(str1.split(),n))
b=设置(计数内存(str2.split(),n))
交叉点=a.交叉点(b)
联合=a.联合(b)
尝试:
返回长度(交叉)/浮动(长度(联合))
除:
返回np.nan
def jc_列表(样本目录,第行,n):
sim_列表=[]
对于输入样本记录:
sim_list.append(n_gram_jaccard_相似度(sample_dict[key],row[“text”],n))
返回str(sim_列表)
使用上述功能构建bigram Jaccard相似性特征,如下所示:

df[“bigram\u jaccard\u similarity”]=df.apply(lambda行:jc\u列表(sample\u dict,row,2),axis=1)
df[“bigram_-jaccard_-similarity”]=df[“bigram_-jaccard_-similarity”].map(λx:[浮点(i)表示[a中的i表示[s.replace(',','')。replace('],'')。如果a!='']],则将[s.split中的替换('[','')
df[[i代表样本中的i]=pd.DataFrame(df[“bigram\u jaccard\u similarity”].values.tolist(),index=df.index)
样本输入:

df=pd.DataFrame(列=[“id”,“text”],索引=None)
df.loc[0]=[“1”,“这是一个示例文本”]
导入集合
sample_dict=collections.defaultdict()
样本记录[“r1”]=“这是样本1”
样本记录[“r2”]=“是样本”
示例文本[“r3”]=“示例文本2”
预期产出:


因此,由于稀疏矩阵的一些广播问题,这比我想象的要困难。此外,在很短的时间内,我无法完全矢量化它

我在框架中添加了一个额外的文本行:

df = pd.DataFrame(columns=["id","text"],index=None)
df.loc[0] = ["1","this is a sample text"]
df.loc[1] = ["2","this is a second sample text"]

import collections

sample_dict = collections.defaultdict()
sample_dict["r1"] = "this is sample 1" 
sample_dict["r2"] = "is sample" 
sample_dict["r3"] = "sample text 2"
我们将使用以下模块/函数/类:

from sklearn.feature_extraction.text import CountVectorizer
from scipy.sparse import csr_matrix
import numpy as np
并定义一个计数器矢量器来创建基于字符的n_图

ngram_vectorizer = CountVectorizer(ngram_range=(2, 2), analyzer="char")
请随意选择您需要的n克。我建议使用现有的标记器和n-gram创建者。你应该找到很多。此外,还可以对CountVectorizer进行广泛调整(例如,转换为小写、去掉空格等)

我们连接所有数据:

all_data = np.concatenate((df.text.to_numpy(),np.array(list(sample_dict.values()))))
我们这样做,因为我们的矢量器需要对所有出现的标记有一个通用的索引方案

现在,让我们拟合计数矢量器并相应地变换数据:

ngrammed = ngram_vectorizer.fit_transform(all_data) >0
ngrammed
现在是一个稀疏矩阵,包含出现在相应行中的标记的标识符,不再像以前那样包含计数。您可以检查并找到从标记到列ID的映射

接下来,我们要将示例dict中的每一个grams条目与我们的每一行ngramed文本数据进行比较。我们需要一些魔法:

texts = ngrammed[:len(df)]
samples = ngrammed[len(df):]
text_rows = len(df)

jaccard_similarities = []
for key, ngram_sample in zip(sample_dict.keys(), samples):
    repeated_row_matrix = (csr_matrix(np.ones([text_rows,1])) * ngram_sample).astype(bool)
    support = texts.maximum(repeated_row_matrix)
    intersection = texts.multiply(repeated_row_matrix).todense()
    jaccard_similarities.append(pd.Series((intersection.sum(axis=1)/support.sum(axis=1)).A1, name=key))
support
是一个布尔数组,用于测量两个可比较对象上的n-gram的并集<代码>交叉点仅当两个可比较区域中都存在令牌时才为真。请注意,
.A1
表示一个
矩阵
-对象作为基础基数组

现在

给予

您也可以通过
df
获取

pd.concat([df, pd.concat(jaccard_similarities, axis=1)], axis=1)
  id                          text        r1        r2        r3
0  1         this is a sample text  0.631579  0.444444  0.500000
1  2  this is a second sample text  0.480000  0.333333  0.384615

你能分享一些数据和期望的输出吗?感觉上,你基本上可以使用scikit学习,而不是真正使用熊猫。此外,每行应用速度很慢:)矢量化@Quickbeam2k1:我已经添加了示例输入和输出。你能提供一个矢量化的例子吗?谢谢@Quickbeam2k1!两个快速澄清:1。首先,
jaccard\u score(text.T,repeated\u row\u matrix.T,average=None)
在我将其用于只有一行的数据帧时抛出一个错误。2.给出ngram_range=(2,2)不会产生与预期输出相同的结果。我们如何解决这些问题?好吧,我想我解决了两个问题。首先,我意识到您想要创建基于字符的
n_grams
。顺便说一句,我认为你的
n_grams
的实现是错误的。在
n=1的情况下验证这一点。如果没有。你为什么叫它n_grams。此外,计数多重性存在问题,我现在删除了它(这是
>0
),您可以在上面找到。希望此帮助我能够修复n_gram计算,但
jaccard_分数(text.T,repeated_row\u matrix.T,average=None)
在仅使用一行数据帧时仍然抛出未大小对象的错误
TypeError:len()。它是否使用多行文本运行?这里发生了一些奇怪的事情(我正试图找出错误所在),非常感谢您的努力Quickbeam2k1!这终于奏效了,现在需要检查一下延迟改进:)
         r1        r2        r3
0  0.631579  0.444444  0.500000
1  0.480000  0.333333  0.384615
pd.concat([df, pd.concat(jaccard_similarities, axis=1)], axis=1)
  id                          text        r1        r2        r3
0  1         this is a sample text  0.631579  0.444444  0.500000
1  2  this is a second sample text  0.480000  0.333333  0.384615