有重复项时如何合并两个countvectorizers?
how to merge two countvectorizers when there are duplicates?
考虑这个简单的例子
data = pd.DataFrame({'text1' : ['hello world', 'hello universe'],
'text2': ['good morning', 'hello two three']})
data
Out[489]:
text1 text2
0 hello world good morning
1 hello universe hello two three
如您所见,text1
和 text2
有一个完全相同的词:hello
。我正在尝试为 text1
和 text2
分别创建 ngram,我想将结果连接到一个 countvectorizer 对象中。
我的想法是,我想为这两个变量分别创建 ngram,并将它们用作 ML 算法中的特征。但是,我确实想要通过将字符串连接在一起而创建的额外 ngram,例如 hello world good morning
中的 world good
。这就是我将 ngram 创建分开的原因。
问题是这样做后,生成的(稀疏)向量将包含重复的 hello
列。
看这里:
vector = CountVectorizer(ngram_range=(1, 2))
v1 = vector.fit_transform(data.text1.values)
print(vector.get_feature_names())
['hello', 'hello universe', 'hello world', 'universe', 'world']
v2 = vector.fit_transform(data.text2.values)
print(vector.get_feature_names())
['good', 'good morning', 'hello', 'hello two', 'morning', 'three', 'two', 'two three']
现在连接 v1
和 v2
得到 13 列
from scipy.sparse import hstack
print(hstack((v1, v2)).toarray())
[[1 0 1 0 1 1 1 0 0 1 0 0 0]
[1 1 0 1 0 0 0 1 1 0 1 1 1]]
正确的文本特征应该是 12:
hello
,word
,hello word
,good
,morning
,good morning
,hello universe
,universe
、two
、three
、hello two
、two three
我可以在这里做些什么来获得合适的唯一词作为特征?
谢谢!
免责声明:这个答案可能不是很复杂,但如果我理解正确你的问题,它应该能完成它的工作。
# create an additional column by chaining the two text columns with a fake word
data['text3'] = data['text1'] + ' xxxxxxxxxx ' + data['text2']
print(data)
# text1 text2 text3
# 0 hello world good morning hello world xxxxxxxxxx good morning
# 1 hello universe hello two three hello universe xxxxxxxxxx hello two three
# instantiate CountVectorizer and fit it
vector = CountVectorizer(ngram_range=(1, 2))
v3 = vector.fit_transform(data.text3.values)
# have a look at the resulting column names
all_colnames = vector.get_feature_names()
print(all_colnames)
# ['good', 'good morning', 'hello', 'hello two', 'hello universe', 'hello world', 'morning', 'three', 'two', 'two three', 'universe', 'universe xxxxxxxxxx', 'world', 'world xxxxxxxxxx', 'xxxxxxxxxx', 'xxxxxxxxxx good', 'xxxxxxxxxx hello']
# select only column names of interest
correct_colnames = [e for e in vector.get_feature_names() if 'xxxxxxxxxx' not in e]
print(correct_colnames)
# ['good', 'good morning', 'hello', 'hello two', 'hello universe', 'hello world', 'morning', 'three', 'two', 'two three', 'universe', 'world']
print(len(all_colnames))
# 17
print(len(correct_colnames))
# 12 # the desired length
# select only the array columns where the fake word is absent
arr = v3.toarray()[:, ['xxxxxxxxxx' not in e for e in colnames]]
print(arr.shape)
print(arr)
# (2, 12)
# [[1 1 1 0 0 1 1 0 0 0 0 1]
# [0 0 2 1 1 0 0 1 1 1 1 0]]
# if you need a pandas.DataFrame as result
new_df = pd.DataFrame(arr, columns=correct_colnames)
print(new_df)
# good good morning hello hello two hello universe hello world morning three two two three universe world
# 0 1 1 1 0 0 1 1 0 0 0 0 1
# 1 0 0 2 1 1 0 0 1 1 1 1 0
其背后的基本原理是:我们插入了一个假词,如 'xxxxxxxxxx'
,这几乎不可能在文本字符串中遇到。该算法会将其视为一个真实的词,因此将用它创建 1-gram 和 2-gram。
然而,我们可以在之后消除那些 n-grams,并且所有相等的词(如本例中的 'hello'
)将不会被单独计算为两个文本列 - 事实上,你可以看到在生成的数据框中,单词 'hello'
在第二行中出现了两次,并且没有重复。
我认为解决这个问题的最佳方法是创建一个使用 CountVectorizer
.
的自定义 Transformer
我会这样做:
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
class MultiRowsCountVectorizer(BaseEstimator, TransformerMixin):
def __init__(self):
self.verctorizer = CountVectorizer(ngram_range=(1, 2))
def fit(self, X, y = None):
#concatenate all textual columns into one column
X_ = np.reshape(X.values, (-1,))
self.verctorizer.fit(X_)
return self
def transform(self, X, y = None):
#join all the textual columns into one column
X_ = X.apply(' '.join, axis=1)
return self.verctorizer.transform(X_)
def get_feature_names(self):
return self.verctorizer.get_feature_names()
transformer = MultiRowsCountVectorizer()
X_ = transformer.fit_transform(data)
transformer.get_feature_names()
fit()
方法通过独立处理列来拟合 CountVectorizer
,而 transform()
将列视为同一行文本。
np.reshape(X.values, (-1,))
正在将形状为 (N, n_columns)
的矩阵转换为大小为 (N*n_columns,)
的一维数组。这确保每个文本字段在 fit()
期间被独立处理。之后,通过将样本的所有文本特征连接在一起,将转换应用于样本的所有文本特征。
这个自定义转换器正在返回所需的 12 个特征:
['good', 'good morning', 'hello', 'hello two', 'hello universe', 'hello world', 'morning', 'three', 'two', 'two three', 'universe', 'world']
并返回以下功能:
[[1 1 1 0 0 1 1 0 0 0 0 1]
[0 0 2 1 1 0 0 1 1 1 1 0]]
NOTES:这个自定义转换器假定 X
是一个 pd.DataFrame
和 n
文本列。
编辑:文本字段需要在 transform()
.
期间与 space 连接
考虑这个简单的例子
data = pd.DataFrame({'text1' : ['hello world', 'hello universe'],
'text2': ['good morning', 'hello two three']})
data
Out[489]:
text1 text2
0 hello world good morning
1 hello universe hello two three
如您所见,text1
和 text2
有一个完全相同的词:hello
。我正在尝试为 text1
和 text2
分别创建 ngram,我想将结果连接到一个 countvectorizer 对象中。
我的想法是,我想为这两个变量分别创建 ngram,并将它们用作 ML 算法中的特征。但是,我确实想要通过将字符串连接在一起而创建的额外 ngram,例如 hello world good morning
中的 world good
。这就是我将 ngram 创建分开的原因。
问题是这样做后,生成的(稀疏)向量将包含重复的 hello
列。
看这里:
vector = CountVectorizer(ngram_range=(1, 2))
v1 = vector.fit_transform(data.text1.values)
print(vector.get_feature_names())
['hello', 'hello universe', 'hello world', 'universe', 'world']
v2 = vector.fit_transform(data.text2.values)
print(vector.get_feature_names())
['good', 'good morning', 'hello', 'hello two', 'morning', 'three', 'two', 'two three']
现在连接 v1
和 v2
得到 13 列
from scipy.sparse import hstack
print(hstack((v1, v2)).toarray())
[[1 0 1 0 1 1 1 0 0 1 0 0 0]
[1 1 0 1 0 0 0 1 1 0 1 1 1]]
正确的文本特征应该是 12:
hello
,word
,hello word
,good
,morning
,good morning
,hello universe
,universe
、two
、three
、hello two
、two three
我可以在这里做些什么来获得合适的唯一词作为特征? 谢谢!
免责声明:这个答案可能不是很复杂,但如果我理解正确你的问题,它应该能完成它的工作。
# create an additional column by chaining the two text columns with a fake word
data['text3'] = data['text1'] + ' xxxxxxxxxx ' + data['text2']
print(data)
# text1 text2 text3
# 0 hello world good morning hello world xxxxxxxxxx good morning
# 1 hello universe hello two three hello universe xxxxxxxxxx hello two three
# instantiate CountVectorizer and fit it
vector = CountVectorizer(ngram_range=(1, 2))
v3 = vector.fit_transform(data.text3.values)
# have a look at the resulting column names
all_colnames = vector.get_feature_names()
print(all_colnames)
# ['good', 'good morning', 'hello', 'hello two', 'hello universe', 'hello world', 'morning', 'three', 'two', 'two three', 'universe', 'universe xxxxxxxxxx', 'world', 'world xxxxxxxxxx', 'xxxxxxxxxx', 'xxxxxxxxxx good', 'xxxxxxxxxx hello']
# select only column names of interest
correct_colnames = [e for e in vector.get_feature_names() if 'xxxxxxxxxx' not in e]
print(correct_colnames)
# ['good', 'good morning', 'hello', 'hello two', 'hello universe', 'hello world', 'morning', 'three', 'two', 'two three', 'universe', 'world']
print(len(all_colnames))
# 17
print(len(correct_colnames))
# 12 # the desired length
# select only the array columns where the fake word is absent
arr = v3.toarray()[:, ['xxxxxxxxxx' not in e for e in colnames]]
print(arr.shape)
print(arr)
# (2, 12)
# [[1 1 1 0 0 1 1 0 0 0 0 1]
# [0 0 2 1 1 0 0 1 1 1 1 0]]
# if you need a pandas.DataFrame as result
new_df = pd.DataFrame(arr, columns=correct_colnames)
print(new_df)
# good good morning hello hello two hello universe hello world morning three two two three universe world
# 0 1 1 1 0 0 1 1 0 0 0 0 1
# 1 0 0 2 1 1 0 0 1 1 1 1 0
其背后的基本原理是:我们插入了一个假词,如 'xxxxxxxxxx'
,这几乎不可能在文本字符串中遇到。该算法会将其视为一个真实的词,因此将用它创建 1-gram 和 2-gram。
然而,我们可以在之后消除那些 n-grams,并且所有相等的词(如本例中的 'hello'
)将不会被单独计算为两个文本列 - 事实上,你可以看到在生成的数据框中,单词 'hello'
在第二行中出现了两次,并且没有重复。
我认为解决这个问题的最佳方法是创建一个使用 CountVectorizer
.
我会这样做:
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
class MultiRowsCountVectorizer(BaseEstimator, TransformerMixin):
def __init__(self):
self.verctorizer = CountVectorizer(ngram_range=(1, 2))
def fit(self, X, y = None):
#concatenate all textual columns into one column
X_ = np.reshape(X.values, (-1,))
self.verctorizer.fit(X_)
return self
def transform(self, X, y = None):
#join all the textual columns into one column
X_ = X.apply(' '.join, axis=1)
return self.verctorizer.transform(X_)
def get_feature_names(self):
return self.verctorizer.get_feature_names()
transformer = MultiRowsCountVectorizer()
X_ = transformer.fit_transform(data)
transformer.get_feature_names()
fit()
方法通过独立处理列来拟合 CountVectorizer
,而 transform()
将列视为同一行文本。
np.reshape(X.values, (-1,))
正在将形状为 (N, n_columns)
的矩阵转换为大小为 (N*n_columns,)
的一维数组。这确保每个文本字段在 fit()
期间被独立处理。之后,通过将样本的所有文本特征连接在一起,将转换应用于样本的所有文本特征。
这个自定义转换器正在返回所需的 12 个特征:
['good', 'good morning', 'hello', 'hello two', 'hello universe', 'hello world', 'morning', 'three', 'two', 'two three', 'universe', 'world']
并返回以下功能:
[[1 1 1 0 0 1 1 0 0 0 0 1]
[0 0 2 1 1 0 0 1 1 1 1 0]]
NOTES:这个自定义转换器假定 X
是一个 pd.DataFrame
和 n
文本列。
编辑:文本字段需要在 transform()
.