将 pandas 数据帧转换为具有复合键的稀疏键项矩阵

Conversion of pandas dataframe to sparse key-item matrix with composite key

我有一个 3 列的数据框。 Col 1 是字符串订单号,Col 2 是整数天,Col 3 是产品名称。 我想将其转换为一个矩阵,其中每一行代表一个唯一的 order/day 组合,每一列代表一个 1/0 表示该组合的产品名称的存在。

到目前为止,我的方法使用了一个产品字典,以及一个包含订单号和天的复合键的字典。 最后一步,即遍历原始数据帧以便将矩阵中的位翻转为 1s 是 sloooow。对于大小为 363K X 331 且稀疏度约为 97% 的矩阵,需要 10 分钟。

我应该考虑其他方法吗?

例如,

ord_nb  day prod
1   1   A
1   1   B
1   2   B
1   2   C
1   2   D

会变成

A   B   C   D
1   1   0   0
0   1   1   1

我的方法是创建一个包含 order/day 对的字典:

ord_day_dict = {}
print("Making a dictionary of ord-by-day keys...")
gp = df.groupby(['day', 'ord'])
for i,g in enumerate(gp.groups.items()):
    ord_day_dict[g[0][0], g[0][1]] = i

我将索引表示附加到原始数据帧:

df['ord_day_idx'] = 0 #Create a place holder column
for i, row in df.iterrows(): #populate the column with the index
    df.set_value(i,'ord_day_idx',ord_day_dict[(row['day'], row['ord_nb'])])

然后我初始化一个矩阵,其大小为 ord/day X 独特产品:

n_items = df.prod_nm.unique().shape[0] #unique number of products
n_ord_days = len(ord_day_dict) #unique number of ord-by-day combos
df_fac_matrix = np.zeros((n_ord_days, n_items), dtype=np.float64)#-1)

我通过字典将我的产品从字符串转换为索引:

prod_dict = dict()
i = 0
for v in df.prod:
    if v not in prod_dict:
        prod_dict[v] = i
        i = i + 1

最后遍历原始数据框,用 1 填充矩阵,其中特定日期的特定订单包含特定产品。

for line in df.itertuples():
    df_fac_matrix[line[4], line[3]] = 1.0 #in the order-by-day index row and the product index column of our ord/day-by-prod matrix, mark a 1

这是您可以尝试的一种选择:

df.groupby(['ord_nb', 'day'])['prod'].apply(list).apply(lambda x: pd.Series(1, x)).fillna(0)

#              A    B    C    D
#ord_nb day             
#     1   1  1.0  1.0  0.0  0.0
#         2  0.0  1.0  1.0  1.0

这是一种基于 NumPy 的方法,可以将数组作为输出 -

a = df[['ord_nb','day']].values.astype(int)
row = np.unique(np.ravel_multi_index(a.T,a.max(0)+1),return_inverse=1)[1]
col = np.unique(df.prd.values,return_inverse=1)[1]
out_shp = row.max()+1, col.max()+1
out = np.zeros(out_shp, dtype=int)
out[row,col] = 1

请注意,假定第三列的名称为 'prd',以避免与内置名称冲突。

可能以性能为重点的改进 -

  • 如果prd只有从A开始的单字母字符,我们可以简单地计算coldf.prd.values.astype('S1').view('uint8')-65.

  • 或者,我们可以计算 rownp.unique(a[:,0]*(a[:,1].max()+1) + a[:,1],return_inverse=1)[1].

使用稀疏数组节省内存: 对于非常大的数组,我们可以通过将它们存储为稀疏矩阵来节省内存。因此,获得这种稀疏矩阵的最后步骤是 -

from scipy.sparse import coo_matrix

d = np.ones(row.size,dtype=int)
out_sparse = coo_matrix((d,(row,col)), shape=out_shp)

样本输入、输出-

In [232]: df
Out[232]: 
  ord_nb day prd
0      1   1   A
1      1   1   B
2      1   2   B
3      1   2   C
4      1   2   D

In [233]: out
Out[233]: 
array([[1, 1, 0, 0],
       [0, 1, 1, 1]])

In [241]: out_sparse
Out[241]: 
<2x4 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in COOrdinate format>

In [242]: out_sparse.toarray()
Out[242]: 
array([[1, 1, 0, 0],
       [0, 1, 1, 1]])