使用数组输入在 pandas 中进行矢量化
Vectorization in pandas with array inputs
我想从数据帧中以向量化方式创建一个备用矩阵,包含一个标签向量 和一个 值向量 ,同时知道 所有标签。
另一个限制是,我无法先创建密集数据帧,然后将其转换为备用数据帧,因为它太大而无法保存在内存中。
示例:
所有可能标签的列表:
all_labels = ['a','b','c','d','e',\
'f','g','h','i','j',\
'k','l','m','n','o',\
'p','q','r','s','t',\
'u','v','w','z']
每行中包含特定标签值的数据框:
data = {'labels': [['b','a'],['q'],['n','j','v']],
'scores': [[0.1,0.2],[0.7],[0.3,0.5,0.1]]}
df = pd.DataFrame(data)
预期密集输出:
这是我以非矢量化方式完成的,这会花费太多时间:
from scipy import sparse
from scipy.sparse import coo_matrix
def labels_to_sparse(input_):
all_, lables_, scores_ = input_
rows = [0]*len(all_)
cols = range(len(all_))
vals = [0]*len(all_)
for i in range(len(lables_)):
vals[all_.index(lables_[i])] = scores_[i]
return coo_matrix((vals, (rows, cols)))
df['sparse_row'] = df.apply(
lambda x: labels_to_sparse((all_labels, x['labels'], x['scores'])), axis=1
)
df
尽管这可行,但由于必须使用 df.apply
,因此处理较大数据时速度极慢。有没有办法向量化这个函数,以避免使用 apply
?
最后,我想使用这个数据框来创建矩阵:
my_result = sparse.vstack(df['sparse_row'].values)
my_result.todense() #not really needed - just for visualization
编辑
总结接受的解决方案(由@Divakar提供):
all_labels = np.sort(all_labels)
n = len(df)
lens = list(map(len,df['labels']))
l_ar = np.concatenate(df['labels'].to_list())
d = np.concatenate(df['scores'].to_list())
R = np.repeat(np.arange(n),lens)
C = np.searchsorted(all_labels,l_ar)
my_result = coo_matrix( (d, (R, C)), shape = (n,len(all_labels)))
您可以尝试以下几种替代方法。
方法 1 - 使用列表理解和 reindex
重构您的 DataFrame
from string import ascii_lowercase
all_labels = list(ascii_lowercase)
my_result = (pd.DataFrame([dict(zip(l, v)) for _, (l, v) in df.iterrows()])
.reindex(columns=all_labels).fillna(0).values)
方法 2 - for loop
使用 loc
更新值
my_result = pd.DataFrame(np.zeros((len(df), len(all_labels))), columns=all_labels)
for i, (lab, val) in df.iterrows():
my_result.loc[i, lab] = val
my_result = my_result.values
两者应该产生相同的输出。
[出局]
[[0.2 0.1 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. ]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.7 0.
0. 0. 0. 0. 0. 0. 0. 0. ]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.5 0. 0. 0. 0.3 0. 0. 0. 0.
0. 0. 0. 0.1 0. 0. 0. 0. ]]
这是一个基于 np.searchsorted
-
n = len(df)
lens = list(map(len,df['labels']))
l_ar = np.concatenate(df['labels'])
d = np.concatenate(df['scores'])
out = np.zeros((n,len(all_labels)),dtype=d.dtype)
R = np.repeat(np.arange(n),lens)
C = np.searchsorted(all_labels,l_ar)
out[R, C] = d
注意:如果 all_labels
未排序,我们需要使用 sorter
arg 和 searchsorted
。
要进入稀疏矩阵输出,如coo_matrix
-
from scipy.sparse import csr_matrix,coo_matrix
out_sparse = coo_matrix( (d, (R, C)), shape = (n,len(all_labels)))
我想从数据帧中以向量化方式创建一个备用矩阵,包含一个标签向量 和一个 值向量 ,同时知道 所有标签。
另一个限制是,我无法先创建密集数据帧,然后将其转换为备用数据帧,因为它太大而无法保存在内存中。
示例:
所有可能标签的列表:
all_labels = ['a','b','c','d','e',\
'f','g','h','i','j',\
'k','l','m','n','o',\
'p','q','r','s','t',\
'u','v','w','z']
每行中包含特定标签值的数据框:
data = {'labels': [['b','a'],['q'],['n','j','v']],
'scores': [[0.1,0.2],[0.7],[0.3,0.5,0.1]]}
df = pd.DataFrame(data)
预期密集输出:
这是我以非矢量化方式完成的,这会花费太多时间:
from scipy import sparse
from scipy.sparse import coo_matrix
def labels_to_sparse(input_):
all_, lables_, scores_ = input_
rows = [0]*len(all_)
cols = range(len(all_))
vals = [0]*len(all_)
for i in range(len(lables_)):
vals[all_.index(lables_[i])] = scores_[i]
return coo_matrix((vals, (rows, cols)))
df['sparse_row'] = df.apply(
lambda x: labels_to_sparse((all_labels, x['labels'], x['scores'])), axis=1
)
df
尽管这可行,但由于必须使用 df.apply
,因此处理较大数据时速度极慢。有没有办法向量化这个函数,以避免使用 apply
?
最后,我想使用这个数据框来创建矩阵:
my_result = sparse.vstack(df['sparse_row'].values)
my_result.todense() #not really needed - just for visualization
编辑
总结接受的解决方案(由@Divakar提供):
all_labels = np.sort(all_labels)
n = len(df)
lens = list(map(len,df['labels']))
l_ar = np.concatenate(df['labels'].to_list())
d = np.concatenate(df['scores'].to_list())
R = np.repeat(np.arange(n),lens)
C = np.searchsorted(all_labels,l_ar)
my_result = coo_matrix( (d, (R, C)), shape = (n,len(all_labels)))
您可以尝试以下几种替代方法。
方法 1 - 使用列表理解和 reindex
重构您的 DataFrame
from string import ascii_lowercase
all_labels = list(ascii_lowercase)
my_result = (pd.DataFrame([dict(zip(l, v)) for _, (l, v) in df.iterrows()])
.reindex(columns=all_labels).fillna(0).values)
方法 2 - for loop
使用 loc
更新值
my_result = pd.DataFrame(np.zeros((len(df), len(all_labels))), columns=all_labels)
for i, (lab, val) in df.iterrows():
my_result.loc[i, lab] = val
my_result = my_result.values
两者应该产生相同的输出。
[出局]
[[0.2 0.1 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. ]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.7 0.
0. 0. 0. 0. 0. 0. 0. 0. ]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.5 0. 0. 0. 0.3 0. 0. 0. 0.
0. 0. 0. 0.1 0. 0. 0. 0. ]]
这是一个基于 np.searchsorted
-
n = len(df)
lens = list(map(len,df['labels']))
l_ar = np.concatenate(df['labels'])
d = np.concatenate(df['scores'])
out = np.zeros((n,len(all_labels)),dtype=d.dtype)
R = np.repeat(np.arange(n),lens)
C = np.searchsorted(all_labels,l_ar)
out[R, C] = d
注意:如果 all_labels
未排序,我们需要使用 sorter
arg 和 searchsorted
。
要进入稀疏矩阵输出,如coo_matrix
-
from scipy.sparse import csr_matrix,coo_matrix
out_sparse = coo_matrix( (d, (R, C)), shape = (n,len(all_labels)))