Эффективные вычисления встраивания для большого фрейма данных

#python #python-3.x #pandas #list #numpy

#python #python-3.x #pandas #Список #numpy

Вопрос:

Учитывая DataFrame :

     id  articleno           target
0   1   [607303]            607295
1   1   [607295]            607303
2   2   [243404, 617953]    590448
3   2   [590448, 617953]    243404
  

для каждой строки вычислите среднее значение встраивания статьи, просматривая каждый элемент в списках в словаре:

 embeddings = {"607303": np.array([0.19, 0.25, 0.45])
             ,"607295": np.array([0.77, 0.76, 0.55])
             ,"243404": np.array([0.35, 0.44, 0.32])
             ,"617953": np.array([0.23, 0.78, 0.24])
             ,"590448": np.array([0.67, 0.12, 0.10])}
  

Так, например, и для уточнения, для третьей строки (индекс 2) статья вложения для 243404 и 617953 является [0.35, 0.44, 0.32] и [0.23, 0.78, 0.24] , соответственно. Среднее встраивание статьи вычисляется как поэлементное сложение всех элементов, деленное на количество статей, так что: ([0.35, 0.44, 0.32] [0.23, 0.78, 0.24])/2=[0.29, 0.61, 0.28] .

Ожидаемый результат:

     id  dim1     dim2     dim3      target
0   1   0.19     0.25     0.45      607295
1   1   0.77     0.76     0.55      607303
2   2   0.29     0.61     0.28      590448
3   2   0.45     0.45     0.17      243404
  

На самом деле my DataFrame содержит миллионы строк, а списки в articleno могут содержать гораздо больше элементов. Из-за этого итерация по строкам может быть слишком медленной, и может потребоваться более эффективное решение (возможно, векторизованное).

Более того, количество измерений (размер встраивания) известно заранее, но составляет пару сотен, поэтому количество столбцов; dim1 , dim2 , dim3 , ... dimN должно быть динамическим, основанным на размерах встраивания ( N ).

Комментарии:

1. Это делает ваш предыдущий вопрос действительно проблемой XY . Вы можете решить проблему из исходных данных намного проще.

2. @QuangHoang Не могли бы вы немного расширить? Я открыт для идей о том, как построить предварительную обработку данных более эффективным способом.

Ответ №1:

В предыдущем вопросе вы потратили дополнительные мили на разделение элементов в articleno списке, а затем удалили target из articleno списка. Теперь, если вы хотите получить доступ к элементам внутри articleno списка, вам нужно снова пройти дополнительные мили, чтобы разделить их.

Чтобы проиллюстрировать, что я имею в виду, вот подход, который генерирует оба вывода из двух вопросов, добавляя при этом минимальный дополнительный код:

 # construct the embeddings dataframe:
embedding_df = pd.DataFrame(embeddings).T.add_prefix('dim')

# aggregation dictionary
agg_dict = {'countrycode':'first','articleno':list}

# taking mean over embedddings
for i in embedding_df.columns: agg_dict[i] = 'mean'

new_df = df.explode('articleno')

(new_df.join(new_df['articleno'].rename('target'))
    .query('articleno != target')
    .merge(embedding_df, left_on='articleno', right_index=True)  # this line is extra from the previous question
    .groupby(['id','target'], as_index=False)
    .agg(agg_dict)
)
  

Вывод:

    id  target countrycode         articleno  dim0  dim1  dim2
0   2  590448          US  [617953, 617953]  0.23  0.78  0.24
1   2  617953          US  [590448, 590448]  0.67  0.12  0.10
  

Теперь, если вам не нужен articleno столбец в конечном выводе, вы можете даже упростить свой код, сократив объем памяти и время выполнения следующим образом:

 total_embeddings = g[embedding_df.columns].sum()
article_counts = g['id'].transform('size')

new_df[embedding_df.columns] = (total_embeddings.sub(new_df[embedding_df.columns])
                                  .div(article_counts-1, axis=0)
                               )
  

и вы получили бы тот же результат.

Комментарии:

1. Спасибо вам за подробный ответ. Еще одна деталь, когда вы создаете agg_dict with mean , я также хотел бы изменить его на absmax — чтобы получить не только средние вложения, но и максимальные вложения (но максимум в абсолютных значениях) Конечно, это не работает (поскольку absmax не существует). Я пытался использовать функцию вместо строки, например , lambda x: x.abs().idxmax() , но это ужасно медленно и работает не так, как ожидалось. У вас есть какие-нибудь предложения?