2016-11-16 6 views
1

У меня есть кадр данных из 3 столбцов. Col 1 - порядковый номер строки, Col 2 - целое число, а Col 3 - имя продукта. Я хотел бы преобразовать это в матрицу, где каждая строка представляет собой уникальную комбинацию порядок/день, и каждый столбец представляет 1/0 для наличия имени продукта для этой комбинации.Преобразование базы данных pandas в разреженную матрицу элементов с составным ключом

В моем подходе до сих пор используется словарь продукта и словарь с составным ключом порядка # & день. Заключительный шаг, который выполняет итерацию через исходный фрейм, чтобы перевернуть биты в матрице на 1s, - это sloooow. Как 10 минут для матрицы размером 363K X 331 и редкостью ~ 97%.

Есть ли другой подход, который я должен рассмотреть?

Например,

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 

Мой подход заключается в создании словаря пар заказа/день:

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 

прилагаю индекс represention к исходный дата-кадр:

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/день 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 

ответ

1

Вот подход, основанный на 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, мы могли бы вычислить col с просто: df.prd.values.astype('S1').view('uint8')-65.

  • В качестве альтернативы, мы могли бы вычислить row с: np.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]]) 
2

Вот один вариант, вы можете попробовать:

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 

 Смежные вопросы

  • Нет связанных вопросов^_^