从稀疏表示创建 Numpy 数组
Create Numpy array from sparse representation
我创建了数据的稀疏表示,并想将其转换为 Numpy 数组。
比方说,我有以下数据(实际上 data
包含更多列表,每个列表更长):
data = [['this','is','my','first','dataset','here'],['but','here', 'is', 'another','one'],['and','yet', 'another', 'one']]
我有两个 dict
项目,将每个单词映射到一个唯一的整数值,反之亦然:
w2i = {'this':0, 'is':1, 'my':2, 'first':3, 'dataset':4, 'here':5, 'but':6, 'another':7, 'one':8, 'and':9, 'yet':10}
此外,我有一个 dict
可以获取每个单词组合的计数:
comb_dict = dict()
for text in data:
sorted_set_text = sorted(list(set(text)))
for i in range(len(sorted_set_text)-1):
for j in range(i+1, len(sorted_set_text)):
if (sorted_set_text[i],sorted_set_text[j]) in comb_dict:
comb_dict[(sorted_set_text[i],sorted_set_text[j])] += 1
else:
comb_dict[(sorted_set_text[i],sorted_set_text[j])] = 1
根据这个字典,我创建了一个稀疏表示如下:
sparse = [(w2i[k[0]],w2i[k[1]],v) for k,v in comb_dict.items()]
此列表由元组组成,其中第一个值表示 x 轴的位置,第二个值表示 y 轴的位置,第三个值表示同时出现的次数:
[(4, 3, 1),
(4, 5, 1),
(4, 1, 1),
(4, 2, 1),
(4, 0, 1),
(3, 5, 1),
(3, 1, 1),
(3, 2, 1),
(3, 0, 1),
(5, 1, 2),
(5, 2, 1),
(5, 0, 1),
(1, 2, 1),
(1, 0, 1),
(2, 0, 1),
(7, 6, 1),
(7, 5, 1),
(7, 1, 1),
(7, 8, 2),
(6, 5, 1),
(6, 1, 1),
(6, 8, 1),
(5, 8, 1),
(1, 8, 1),
(9, 7, 1),
(9, 8, 1),
(9, 10, 1),
(7, 10, 1),
(8, 10, 1)]
现在,我想要得到一个 Numpy array
(11 x 11),其中每一行 i 和列 j 代表一个词,单元格表示词 i 和 j 同时出现的频率。因此,开始是
cooc = np.zeros((len(w2i),len(w2i)), dtype=np.int16)
然后,我想更新 cooc
,以便与 sparse
中的单词组合关联的 row/column 索引将被分配关联值。我该怎么做?
编辑:我知道我可以遍历 cooc
并逐个分配每个单元格。但是,我的数据集很大,这会很耗时。相反,我想将 cooc
转换为 Scipy 稀疏矩阵并使用 toarray()
方法。我该怎么做?
在不使用朴素循环的附加约束后编辑
您可以使用 numba
和 JIT 装饰器在执行前编译循环。这将比一个简单的循环快很多。如果您使用 dtype=np.uint16
,您将使用默认 int64
RAM 的 1/4(最大值为 65535
,因此您应该可以使用 60k 字)。
我用 3 600 000 000 (num_words2) 组合尝试了这个,在我的 c2016 笔记本电脑上 运行 在 43 秒内完成。
完整代码:
from numba import jit
# Create array of each word with dtype uint16 (max value 65535)
n_unique_words = 11
start_words = np.array([row[0] for row in sparse], dtype = np.uint16)
neighbour_words = np.array([row[1] for row in sparse], dtype = np.uint16)
frequences = np.array([row[2] for row in sparse], dtype = np.uint16)
# Numba loop method
@jit(nopython=True) # Set "nopython" mode for best performance, equivalent to @njit
def make_cooc_fast(start_words, neighbour_words, frequencies, n_unique_words): # Function is compiled to machine code when called the first time
big_cooc = np.zeros((n_unique_words, n_unique_words), dtype=np.int16)
for i in range(0, len(start_words)):
big_cooc[start_words[i], neighbour_words[i]] = frequencies[i]
big_cooc[neighbour_words[i], start_words[i]] = frequencies[i]
return big_cooc
cooc = make_cooc_fast(
start_words=start_words,
neighbour_words=neighbour_words,
frequencies=frequences,
n_unique_words=n_unique_words)
原回复
如果我对问题的理解正确,那么您已经完成了所有艰苦的工作。从这里开始,它应该只是:
for row in sparse:
cooc[row[0], row[1]] = row[2]
cooc[row[1], row[0]] = row[2]
它是两行,因为如果单词 a
出现在单词 b
旁边 n
次,那么单词 b
也会出现在单词 a
旁边n
次。
这个领域是我感兴趣的领域 - 如果您想进一步讨论,请随时私信我。
In [268]: alist = [(4, 3, 1),
...: (4, 5, 1),
...: (4, 1, 1),
...: (4, 2, 1),
...
...: (9, 10, 1),
...: (7, 10, 1),
...: (8, 10, 1)]
从列表中创建一个数组:
In [269]: arr = np.array(alist)
In [270]: arr.shape
Out[270]: (29, 3)
并使用数组的列来填充定义的 (11,11) 数组中的槽:
In [271]: res = np.zeros((11,11),int)
In [272]: res[arr[:,0],arr[:,1]]=arr[:,2]
In [273]: res
Out[273]:
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0],
[1, 2, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 1, 0, 2, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
您可以使用相同的列创建一个 scipy.sparse
矩阵。
In [274]: from scipy import sparse
In [276]: M = sparse.coo_matrix((arr[:,2],(arr[:,0],arr[:,1])), shape=(11,11))
In [277]: M
Out[277]:
<11x11 sparse matrix of type '<class 'numpy.int64'>'
with 29 stored elements in COOrdinate format>
In [278]: M.A
Out[278]:
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0],
[1, 2, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 1, 0, 2, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
我认为这些其他答案有点像重新发明已经存在的轮子。
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
data = [['this','is','my','first','dataset','here'],['but','here', 'is', 'another','one'],['and','yet', 'another', 'one']]
我要把它们重新组合起来,然后使用 sklearn 的 CountVectorizer
data = [" ".join(x) for x in data]
encoder = CountVectorizer()
occurrence = encoder.fit_transform(data)
这个出现矩阵是一个稀疏矩阵,把它变成共现矩阵只是一个简单的乘法(对角线是每个token出现的总次数)
co_occurrence = occurrence.T @ occurrence
>>> co_occurrence.A
array([[1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 2, 1, 0, 0, 1, 1, 0, 2, 0, 1],
[0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
[0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
[1, 2, 1, 0, 0, 1, 1, 0, 2, 0, 1],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1]])
并且可以从编码器中恢复 row/column 个标签:
encoder.vocabulary_
{'this': 9,
'is': 6,
'my': 7,
'first': 4,
'dataset': 3,
'here': 5,
'but': 2,
'another': 1,
'one': 8,
'and': 0,
'yet': 10}
我创建了数据的稀疏表示,并想将其转换为 Numpy 数组。
比方说,我有以下数据(实际上 data
包含更多列表,每个列表更长):
data = [['this','is','my','first','dataset','here'],['but','here', 'is', 'another','one'],['and','yet', 'another', 'one']]
我有两个 dict
项目,将每个单词映射到一个唯一的整数值,反之亦然:
w2i = {'this':0, 'is':1, 'my':2, 'first':3, 'dataset':4, 'here':5, 'but':6, 'another':7, 'one':8, 'and':9, 'yet':10}
此外,我有一个 dict
可以获取每个单词组合的计数:
comb_dict = dict()
for text in data:
sorted_set_text = sorted(list(set(text)))
for i in range(len(sorted_set_text)-1):
for j in range(i+1, len(sorted_set_text)):
if (sorted_set_text[i],sorted_set_text[j]) in comb_dict:
comb_dict[(sorted_set_text[i],sorted_set_text[j])] += 1
else:
comb_dict[(sorted_set_text[i],sorted_set_text[j])] = 1
根据这个字典,我创建了一个稀疏表示如下:
sparse = [(w2i[k[0]],w2i[k[1]],v) for k,v in comb_dict.items()]
此列表由元组组成,其中第一个值表示 x 轴的位置,第二个值表示 y 轴的位置,第三个值表示同时出现的次数:
[(4, 3, 1),
(4, 5, 1),
(4, 1, 1),
(4, 2, 1),
(4, 0, 1),
(3, 5, 1),
(3, 1, 1),
(3, 2, 1),
(3, 0, 1),
(5, 1, 2),
(5, 2, 1),
(5, 0, 1),
(1, 2, 1),
(1, 0, 1),
(2, 0, 1),
(7, 6, 1),
(7, 5, 1),
(7, 1, 1),
(7, 8, 2),
(6, 5, 1),
(6, 1, 1),
(6, 8, 1),
(5, 8, 1),
(1, 8, 1),
(9, 7, 1),
(9, 8, 1),
(9, 10, 1),
(7, 10, 1),
(8, 10, 1)]
现在,我想要得到一个 Numpy array
(11 x 11),其中每一行 i 和列 j 代表一个词,单元格表示词 i 和 j 同时出现的频率。因此,开始是
cooc = np.zeros((len(w2i),len(w2i)), dtype=np.int16)
然后,我想更新 cooc
,以便与 sparse
中的单词组合关联的 row/column 索引将被分配关联值。我该怎么做?
编辑:我知道我可以遍历 cooc
并逐个分配每个单元格。但是,我的数据集很大,这会很耗时。相反,我想将 cooc
转换为 Scipy 稀疏矩阵并使用 toarray()
方法。我该怎么做?
在不使用朴素循环的附加约束后编辑
您可以使用 numba
和 JIT 装饰器在执行前编译循环。这将比一个简单的循环快很多。如果您使用 dtype=np.uint16
,您将使用默认 int64
RAM 的 1/4(最大值为 65535
,因此您应该可以使用 60k 字)。
我用 3 600 000 000 (num_words2) 组合尝试了这个,在我的 c2016 笔记本电脑上 运行 在 43 秒内完成。
完整代码:
from numba import jit
# Create array of each word with dtype uint16 (max value 65535)
n_unique_words = 11
start_words = np.array([row[0] for row in sparse], dtype = np.uint16)
neighbour_words = np.array([row[1] for row in sparse], dtype = np.uint16)
frequences = np.array([row[2] for row in sparse], dtype = np.uint16)
# Numba loop method
@jit(nopython=True) # Set "nopython" mode for best performance, equivalent to @njit
def make_cooc_fast(start_words, neighbour_words, frequencies, n_unique_words): # Function is compiled to machine code when called the first time
big_cooc = np.zeros((n_unique_words, n_unique_words), dtype=np.int16)
for i in range(0, len(start_words)):
big_cooc[start_words[i], neighbour_words[i]] = frequencies[i]
big_cooc[neighbour_words[i], start_words[i]] = frequencies[i]
return big_cooc
cooc = make_cooc_fast(
start_words=start_words,
neighbour_words=neighbour_words,
frequencies=frequences,
n_unique_words=n_unique_words)
原回复
如果我对问题的理解正确,那么您已经完成了所有艰苦的工作。从这里开始,它应该只是:
for row in sparse:
cooc[row[0], row[1]] = row[2]
cooc[row[1], row[0]] = row[2]
它是两行,因为如果单词 a
出现在单词 b
旁边 n
次,那么单词 b
也会出现在单词 a
旁边n
次。
这个领域是我感兴趣的领域 - 如果您想进一步讨论,请随时私信我。
In [268]: alist = [(4, 3, 1),
...: (4, 5, 1),
...: (4, 1, 1),
...: (4, 2, 1),
...
...: (9, 10, 1),
...: (7, 10, 1),
...: (8, 10, 1)]
从列表中创建一个数组:
In [269]: arr = np.array(alist)
In [270]: arr.shape
Out[270]: (29, 3)
并使用数组的列来填充定义的 (11,11) 数组中的槽:
In [271]: res = np.zeros((11,11),int)
In [272]: res[arr[:,0],arr[:,1]]=arr[:,2]
In [273]: res
Out[273]:
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0],
[1, 2, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 1, 0, 2, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
您可以使用相同的列创建一个 scipy.sparse
矩阵。
In [274]: from scipy import sparse
In [276]: M = sparse.coo_matrix((arr[:,2],(arr[:,0],arr[:,1])), shape=(11,11))
In [277]: M
Out[277]:
<11x11 sparse matrix of type '<class 'numpy.int64'>'
with 29 stored elements in COOrdinate format>
In [278]: M.A
Out[278]:
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0],
[1, 2, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 1, 1, 0, 2, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
我认为这些其他答案有点像重新发明已经存在的轮子。
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
data = [['this','is','my','first','dataset','here'],['but','here', 'is', 'another','one'],['and','yet', 'another', 'one']]
我要把它们重新组合起来,然后使用 sklearn 的 CountVectorizer
data = [" ".join(x) for x in data]
encoder = CountVectorizer()
occurrence = encoder.fit_transform(data)
这个出现矩阵是一个稀疏矩阵,把它变成共现矩阵只是一个简单的乘法(对角线是每个token出现的总次数)
co_occurrence = occurrence.T @ occurrence
>>> co_occurrence.A
array([[1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 2, 1, 0, 0, 1, 1, 0, 2, 0, 1],
[0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
[0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 2, 2, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
[1, 2, 1, 0, 0, 1, 1, 0, 2, 0, 1],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1]])
并且可以从编码器中恢复 row/column 个标签:
encoder.vocabulary_
{'this': 9,
'is': 6,
'my': 7,
'first': 4,
'dataset': 3,
'here': 5,
'but': 2,
'another': 1,
'one': 8,
'and': 0,
'yet': 10}