单热编码:列表成员错误
One-hot encoding: list membership error
给定可变数量的字符串,我想对它们进行单热编码,如下例所示:
s1 = 'awaken my love'
s2 = 'awaken the beast'
s3 = 'wake beast love'
# desired result - NumPy array
array([[ 1., 1., 1., 0., 0., 0.],
[ 1., 0., 0., 1., 1., 0.],
[ 0., 0., 1., 0., 1., 1.]])
当前代码:
def uniquewords(*args):
"""Create order-preserved string with unique words between *args"""
allwords = ' '.join(args).split()
return ' '.join(sorted(set(allwords), key=allwords.index)).split()
def encode(*args):
"""One-hot encode the given input strings"""
unique = uniquewords(*args)
feature_vectors = np.zeros((len(args), len(unique)))
for vec, s in zip(feature_vectors, args):
for num, word in enumerate(unique):
vec[num] = word in s
return feature_vectors
问题出在这一行:
vec[num] = word in s
例如,将 'wake' in 'awaken my love'
选为 True
(确实如此,但不符合我的需要)并给出以下略有偏差的结果:
print(encode(s1, s2, s3))
[[ 1. 1. 1. 0. 0. 1.]
[ 1. 0. 0. 1. 1. 1.]
[ 0. 0. 1. 0. 1. 1.]]
我看到 a solution 使用 re
但我不确定如何在这里应用。我怎样才能纠正上面的一行? (摆脱嵌套循环也很好,但我不要求进行一般代码编辑,除非您友善地提供。)
一个 Set 将使 in 运算符平均在 O(1) 中运行。
变化:
vec[num] = word in s
至:
vec[num] = word in set(s.split())
最终版本:
def encode(*args):
"""One-hot encode the given input strings"""
unique = uniquewords(*args)
feature_vectors = np.zeros((len(args), len(unique)))
for vec, s in zip(feature_vectors, args):
for num, word in enumerate(unique):
vec[num] = word in set(s.split())
return feature_vectors
结果:
[[ 1. 1. 1. 0. 0. 0.]
[ 1. 0. 0. 1. 1. 0.]
[ 0. 0. 1. 0. 1. 1.]]
如果你稍微重构一下,把每个句子都当作一个单词列表,那么它会减少你必须做的很多 split
ting 和 join
ing,并使 word in s
的行为自然化一点。但是,set
是成员测试的首选,因为它可以在 O(1)
中执行此操作,并且您应该只为迭代的每个参数构造一个,因此您的代码将导致:
import numpy as np
import itertools
def uniquewords(*args):
"""Create order-preserved string with unique words between *args"""
allwords = list(itertools.chain(*args))
return sorted(set(allwords), key=allwords.index)
def encode(*args):
"""One-hot encode the given input strings"""
args_with_words = [arg.split() for arg in args]
unique = uniquewords(*args_with_words)
feature_vectors = np.zeros((len(args), len(unique)))
for vec, s in zip(feature_vectors, args_with_words):
s_set = set(s)
for num, word in enumerate(unique):
vec[num] = word in s_set
return feature_vectors
print encode("awaken my love", "awaken the beast", "wake beast love")
正确输出
[[ 1. 1. 1. 0. 0. 0.]
[ 1. 0. 0. 1. 1. 0.]
[ 0. 0. 1. 0. 1. 1.]]
完成此操作后,您可能会意识到根本不需要成员资格测试,您只需迭代 s,只需要为需要设置为 1
的词而烦恼。这种方法在更大的数据集上可能要快得多。
import numpy as np
import itertools
def uniquewords(*args):
"""Dictionary of words to their indices in the matrix"""
words = {}
n = 0
for word in itertools.chain(*args):
if word not in words:
words[word] = n
n += 1
return words
def encode(*args):
"""One-hot encode the given input strings"""
args_with_words = [arg.split() for arg in args]
unique = uniquewords(*args_with_words)
feature_vectors = np.zeros((len(args), len(unique)))
for vec, s in zip(feature_vectors, args_with_words):
for word in s:
vec[unique[word]] = 1
return feature_vectors
print encode("awaken my love", "awaken the beast", "wake beast love")
这是一种方法 -
def membership(list_strings):
split_str = [i.split(" ") for i in list_strings]
split_str_unq = np.unique(np.concatenate(split_str))
out = np.array([np.in1d(split_str_unq, b_i) for b_i in split_str]).astype(int)
df_out = pd.DataFrame(out, columns = split_str_unq)
return df_out
样本运行-
In [189]: s1 = 'awaken my love'
...: s2 = 'awaken the beast'
...: s3 = 'wake beast love'
...:
In [190]: membership([s1,s2,s3])
Out[190]:
awaken beast love my the wake
0 1 0 1 1 0 0
1 1 1 0 0 1 0
2 0 1 1 0 0 1
这是另一个利用 np.searchsorted
获取每行的列索引以设置到输出数组中并希望更快 -
def membership_v2(list_strings):
split_str = [i.split(" ") for i in list_strings]
all_strings = np.concatenate(split_str)
split_str_unq = np.unique(all_strings)
col = np.searchsorted(split_str_unq, all_strings)
row = np.repeat(np.arange(len(split_str)) , [len(i) for i in split_str])
out = np.zeros((len(split_str),col.max()+1),dtype=int)
out[row, col] = 1
df_out = pd.DataFrame(out, columns = split_str_unq)
return df_out
请注意,作为数据帧的输出主要用于输出的 better/easier 表示。
您可以使用 pandas 从列表列表创建单热编码转换(例如,字符串列表,其中每个字符串随后被拆分为单词列表)。
import pandas as pd
s1 = 'awaken my love'
s2 = 'awaken the beast'
s3 = 'wake beast love'
words = pd.Series([s1, s2, s3])
df = pd.melt(words.str.split().apply(pd.Series).reset_index(),
value_name='word', id_vars='index')
result = (
pd.concat([df['index'], pd.get_dummies(df['word'])], axis=1)
.groupby('index')
.any()
).astype(float)
>>> result
awaken beast love my the wake
index
0 1 0 1 1 0 0
1 1 1 0 0 1 0
2 0 1 1 0 0 1
>>> result.values
array([[ 1., 0., 1., 1., 0., 0.],
[ 1., 1., 0., 0., 1., 0.],
[ 0., 1., 1., 0., 0., 1.]])
说明
首先,根据您的单词列表创建一个系列。
然后将单词拆分成列并重置索引:
>>> words.str.split().apply(pd.Series).reset_index()
# Output:
# index 0 1 2
# 0 0 awaken my love
# 1 1 awaken the beast
# 2 2 wake beast love
然后融化这个中间数据框,结果如下:
index variable word
0 0 0 awaken
1 1 0 awaken
2 2 0 wake
3 0 1 my
4 1 1 the
5 2 1 beast
6 0 2 love
7 1 2 beast
8 2 2 love
对单词应用 get_dummies
并将结果连接到它们的索引位置。生成的数据帧然后在 index
上分组,any
用于聚合(所有值都是零或一,因此 any
指示是否有该词的一个或多个实例)。 return 是一个布尔矩阵,它被转换为浮点数。要 return numpy 数组,请将 .values
应用于结果。
给定可变数量的字符串,我想对它们进行单热编码,如下例所示:
s1 = 'awaken my love'
s2 = 'awaken the beast'
s3 = 'wake beast love'
# desired result - NumPy array
array([[ 1., 1., 1., 0., 0., 0.],
[ 1., 0., 0., 1., 1., 0.],
[ 0., 0., 1., 0., 1., 1.]])
当前代码:
def uniquewords(*args):
"""Create order-preserved string with unique words between *args"""
allwords = ' '.join(args).split()
return ' '.join(sorted(set(allwords), key=allwords.index)).split()
def encode(*args):
"""One-hot encode the given input strings"""
unique = uniquewords(*args)
feature_vectors = np.zeros((len(args), len(unique)))
for vec, s in zip(feature_vectors, args):
for num, word in enumerate(unique):
vec[num] = word in s
return feature_vectors
问题出在这一行:
vec[num] = word in s
例如,将 'wake' in 'awaken my love'
选为 True
(确实如此,但不符合我的需要)并给出以下略有偏差的结果:
print(encode(s1, s2, s3))
[[ 1. 1. 1. 0. 0. 1.]
[ 1. 0. 0. 1. 1. 1.]
[ 0. 0. 1. 0. 1. 1.]]
我看到 a solution 使用 re
但我不确定如何在这里应用。我怎样才能纠正上面的一行? (摆脱嵌套循环也很好,但我不要求进行一般代码编辑,除非您友善地提供。)
一个 Set 将使 in 运算符平均在 O(1) 中运行。
变化:
vec[num] = word in s
至:
vec[num] = word in set(s.split())
最终版本:
def encode(*args):
"""One-hot encode the given input strings"""
unique = uniquewords(*args)
feature_vectors = np.zeros((len(args), len(unique)))
for vec, s in zip(feature_vectors, args):
for num, word in enumerate(unique):
vec[num] = word in set(s.split())
return feature_vectors
结果:
[[ 1. 1. 1. 0. 0. 0.]
[ 1. 0. 0. 1. 1. 0.]
[ 0. 0. 1. 0. 1. 1.]]
如果你稍微重构一下,把每个句子都当作一个单词列表,那么它会减少你必须做的很多 split
ting 和 join
ing,并使 word in s
的行为自然化一点。但是,set
是成员测试的首选,因为它可以在 O(1)
中执行此操作,并且您应该只为迭代的每个参数构造一个,因此您的代码将导致:
import numpy as np
import itertools
def uniquewords(*args):
"""Create order-preserved string with unique words between *args"""
allwords = list(itertools.chain(*args))
return sorted(set(allwords), key=allwords.index)
def encode(*args):
"""One-hot encode the given input strings"""
args_with_words = [arg.split() for arg in args]
unique = uniquewords(*args_with_words)
feature_vectors = np.zeros((len(args), len(unique)))
for vec, s in zip(feature_vectors, args_with_words):
s_set = set(s)
for num, word in enumerate(unique):
vec[num] = word in s_set
return feature_vectors
print encode("awaken my love", "awaken the beast", "wake beast love")
正确输出
[[ 1. 1. 1. 0. 0. 0.]
[ 1. 0. 0. 1. 1. 0.]
[ 0. 0. 1. 0. 1. 1.]]
完成此操作后,您可能会意识到根本不需要成员资格测试,您只需迭代 s,只需要为需要设置为 1
的词而烦恼。这种方法在更大的数据集上可能要快得多。
import numpy as np
import itertools
def uniquewords(*args):
"""Dictionary of words to their indices in the matrix"""
words = {}
n = 0
for word in itertools.chain(*args):
if word not in words:
words[word] = n
n += 1
return words
def encode(*args):
"""One-hot encode the given input strings"""
args_with_words = [arg.split() for arg in args]
unique = uniquewords(*args_with_words)
feature_vectors = np.zeros((len(args), len(unique)))
for vec, s in zip(feature_vectors, args_with_words):
for word in s:
vec[unique[word]] = 1
return feature_vectors
print encode("awaken my love", "awaken the beast", "wake beast love")
这是一种方法 -
def membership(list_strings):
split_str = [i.split(" ") for i in list_strings]
split_str_unq = np.unique(np.concatenate(split_str))
out = np.array([np.in1d(split_str_unq, b_i) for b_i in split_str]).astype(int)
df_out = pd.DataFrame(out, columns = split_str_unq)
return df_out
样本运行-
In [189]: s1 = 'awaken my love'
...: s2 = 'awaken the beast'
...: s3 = 'wake beast love'
...:
In [190]: membership([s1,s2,s3])
Out[190]:
awaken beast love my the wake
0 1 0 1 1 0 0
1 1 1 0 0 1 0
2 0 1 1 0 0 1
这是另一个利用 np.searchsorted
获取每行的列索引以设置到输出数组中并希望更快 -
def membership_v2(list_strings):
split_str = [i.split(" ") for i in list_strings]
all_strings = np.concatenate(split_str)
split_str_unq = np.unique(all_strings)
col = np.searchsorted(split_str_unq, all_strings)
row = np.repeat(np.arange(len(split_str)) , [len(i) for i in split_str])
out = np.zeros((len(split_str),col.max()+1),dtype=int)
out[row, col] = 1
df_out = pd.DataFrame(out, columns = split_str_unq)
return df_out
请注意,作为数据帧的输出主要用于输出的 better/easier 表示。
您可以使用 pandas 从列表列表创建单热编码转换(例如,字符串列表,其中每个字符串随后被拆分为单词列表)。
import pandas as pd
s1 = 'awaken my love'
s2 = 'awaken the beast'
s3 = 'wake beast love'
words = pd.Series([s1, s2, s3])
df = pd.melt(words.str.split().apply(pd.Series).reset_index(),
value_name='word', id_vars='index')
result = (
pd.concat([df['index'], pd.get_dummies(df['word'])], axis=1)
.groupby('index')
.any()
).astype(float)
>>> result
awaken beast love my the wake
index
0 1 0 1 1 0 0
1 1 1 0 0 1 0
2 0 1 1 0 0 1
>>> result.values
array([[ 1., 0., 1., 1., 0., 0.],
[ 1., 1., 0., 0., 1., 0.],
[ 0., 1., 1., 0., 0., 1.]])
说明
首先,根据您的单词列表创建一个系列。
然后将单词拆分成列并重置索引:
>>> words.str.split().apply(pd.Series).reset_index()
# Output:
# index 0 1 2
# 0 0 awaken my love
# 1 1 awaken the beast
# 2 2 wake beast love
然后融化这个中间数据框,结果如下:
index variable word
0 0 0 awaken
1 1 0 awaken
2 2 0 wake
3 0 1 my
4 1 1 the
5 2 1 beast
6 0 2 love
7 1 2 beast
8 2 2 love
对单词应用 get_dummies
并将结果连接到它们的索引位置。生成的数据帧然后在 index
上分组,any
用于聚合(所有值都是零或一,因此 any
指示是否有该词的一个或多个实例)。 return 是一个布尔矩阵,它被转换为浮点数。要 return numpy 数组,请将 .values
应用于结果。