如何有效地使用 pandas 列和字典构建特征?
How to build features using pandas column and a dictionary efficiently?
我有一个机器学习问题,我正在计算 pandas 数据框文本列与字典值的二元组 Jaccard 相似度。目前我将它们存储为列表,然后将它们转换为列。事实证明,这在生产中非常缓慢。有没有更有效的方法呢?
以下是我目前正在执行的步骤:
对于字典中的每个键:
1. 获取 pandas 列和 dict[key] 的二元语法
2.计算Jaccard相似度
3.附加到一个空列表
4.将列表存储在数据框中
5. 将列表转换为列
from itertools import tee, islice
def count_ngrams(lst, n):
tlst = lst
while True:
a, b = tee(tlst)
l = tuple(islice(a, n))
if len(l) == n:
yield l
next(b)
tlst = b
else:
break
def n_gram_jaccard_similarity(str1, str2,n):
a = set(count_ngrams(str1.split(),n))
b = set(count_ngrams(str2.split(),n))
intersection = a.intersection(b)
union = a.union(b)
try:
return len(intersection) / float(len(union))
except:
return np.nan
def jc_list(sample_dict,row,n):
sim_list = []
for key in sample_dict:
sim_list.append(n_gram_jaccard_similarity(sample_dict[key],row["text"],n))
return str(sim_list)
利用以上函数构建二元组Jaccard相似度特征如下:
df["bigram_jaccard_similarity"]=df.apply(lambda row: jc_list(sample_dict,row,2),axis=1)
df["bigram_jaccard_similarity"] = df["bigram_jaccard_similarity"].map(lambda x:[float(i) for i in [a for a in [s.replace(',','').replace(']', '').replace('[','') for s in x.split()] if a!='']])
df[[i for i in sample_dict]] = pd.DataFrame(df["bigram_jaccard_similarity"].values.tolist(), index= df.index)
示例输入:
df = pd.DataFrame(columns=["id","text"],index=None)
df.loc[0] = ["1","this is a 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"
预期输出:
因此,由于稀疏矩阵的一些广播问题,这比我想象的要困难。此外,在短时间内我无法完全矢量化它。
我在框架中添加了一个额外的文本行:
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"
我们将使用以下 modules/functions/classes:
from sklearn.feature_extraction.text import CountVectorizer
from scipy.sparse import csr_matrix
import numpy as np
并定义一个 CountVectorizer 来创建基于字符的 n_grams
ngram_vectorizer = CountVectorizer(ngram_range=(2, 2), analyzer="char")
随意选择您需要的 n-gram。我建议采用现有的分词器和 n-gram 创建器。你应该找到很多这样的。还可以对 CountVectorizer 进行广泛的调整(例如转换为小写,去除空格等)
我们连接所有数据:
all_data = np.concatenate((df.text.to_numpy(),np.array(list(sample_dict.values()))))
我们这样做,因为我们的向量化器需要对所有出现的标记有一个通用的索引方案。
现在让我们拟合 Count 向量化器并相应地转换数据:
ngrammed = ngram_vectorizer.fit_transform(all_data) >0
ngrammed
现在是一个稀疏矩阵,包含出现在相应行中的标记的标识符,而不是像以前那样的计数。您可以检查 ngram_vecotrizer
并找到从标记到列 ID 的映射。
接下来我们要将示例字典中的每个语法条目与 ngrammed 文本数据的每一行进行比较。我们需要一些魔法:
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 的并集。 intersection
仅当令牌在两个可比较对象中都存在时才为真。请注意,.A1
表示一个 matrix
对象作为基础数组。
现在
pd.concat(jaccard_similarities, axis=1)
给予
r1 r2 r3
0 0.631579 0.444444 0.500000
1 0.480000 0.333333 0.384615
你也可以连接 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
我有一个机器学习问题,我正在计算 pandas 数据框文本列与字典值的二元组 Jaccard 相似度。目前我将它们存储为列表,然后将它们转换为列。事实证明,这在生产中非常缓慢。有没有更有效的方法呢?
以下是我目前正在执行的步骤: 对于字典中的每个键: 1. 获取 pandas 列和 dict[key] 的二元语法 2.计算Jaccard相似度 3.附加到一个空列表 4.将列表存储在数据框中 5. 将列表转换为列
from itertools import tee, islice
def count_ngrams(lst, n):
tlst = lst
while True:
a, b = tee(tlst)
l = tuple(islice(a, n))
if len(l) == n:
yield l
next(b)
tlst = b
else:
break
def n_gram_jaccard_similarity(str1, str2,n):
a = set(count_ngrams(str1.split(),n))
b = set(count_ngrams(str2.split(),n))
intersection = a.intersection(b)
union = a.union(b)
try:
return len(intersection) / float(len(union))
except:
return np.nan
def jc_list(sample_dict,row,n):
sim_list = []
for key in sample_dict:
sim_list.append(n_gram_jaccard_similarity(sample_dict[key],row["text"],n))
return str(sim_list)
利用以上函数构建二元组Jaccard相似度特征如下:
df["bigram_jaccard_similarity"]=df.apply(lambda row: jc_list(sample_dict,row,2),axis=1)
df["bigram_jaccard_similarity"] = df["bigram_jaccard_similarity"].map(lambda x:[float(i) for i in [a for a in [s.replace(',','').replace(']', '').replace('[','') for s in x.split()] if a!='']])
df[[i for i in sample_dict]] = pd.DataFrame(df["bigram_jaccard_similarity"].values.tolist(), index= df.index)
示例输入:
df = pd.DataFrame(columns=["id","text"],index=None)
df.loc[0] = ["1","this is a 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"
预期输出:
因此,由于稀疏矩阵的一些广播问题,这比我想象的要困难。此外,在短时间内我无法完全矢量化它。
我在框架中添加了一个额外的文本行:
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"
我们将使用以下 modules/functions/classes:
from sklearn.feature_extraction.text import CountVectorizer
from scipy.sparse import csr_matrix
import numpy as np
并定义一个 CountVectorizer 来创建基于字符的 n_grams
ngram_vectorizer = CountVectorizer(ngram_range=(2, 2), analyzer="char")
随意选择您需要的 n-gram。我建议采用现有的分词器和 n-gram 创建器。你应该找到很多这样的。还可以对 CountVectorizer 进行广泛的调整(例如转换为小写,去除空格等)
我们连接所有数据:
all_data = np.concatenate((df.text.to_numpy(),np.array(list(sample_dict.values()))))
我们这样做,因为我们的向量化器需要对所有出现的标记有一个通用的索引方案。
现在让我们拟合 Count 向量化器并相应地转换数据:
ngrammed = ngram_vectorizer.fit_transform(all_data) >0
ngrammed
现在是一个稀疏矩阵,包含出现在相应行中的标记的标识符,而不是像以前那样的计数。您可以检查 ngram_vecotrizer
并找到从标记到列 ID 的映射。
接下来我们要将示例字典中的每个语法条目与 ngrammed 文本数据的每一行进行比较。我们需要一些魔法:
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 的并集。 intersection
仅当令牌在两个可比较对象中都存在时才为真。请注意,.A1
表示一个 matrix
对象作为基础数组。
现在
pd.concat(jaccard_similarities, axis=1)
给予
r1 r2 r3
0 0.631579 0.444444 0.500000
1 0.480000 0.333333 0.384615
你也可以连接 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