Более эффективный способ написания этой лямбда-функции

#python #pandas #performance #lambda

Вопрос:

 import pandas as pd

prizes = ([1, 100], [2, 50], [3, 25])
prizes = pd.DataFrame(prizes, columns=['Rank', 'Payout'])

ranking = ([1, 3, 2], [2, 2, 1], [3, 1, 3])
ranking = pd.DataFrame(ranking, columns=[1, 2, 3])

payouts = pd.DataFrame(range(1, 4), columns=['Lineup'])
mapper = prizes.set_index('Rank')['Payout'].to_dict()
payouts = pd.concat([payouts, ranking[range(1, 4)].apply(lambda s: s.map(mapper)).fillna(-1)], axis=1)

print(ranking)
print(payouts)

   1  2  3
0  1  3  2
1  2  2  1
2  3  1  3
   Lineup    1    2    3
0       1  100   25   50
1       2   50   50  100
2       3   25  100   25
 

Функция лямбда, которая находится чуть выше операторов печати, есть ли способ написать это более эффективно. Это всего лишь небольшой пример того, для чего я использую его внутри большого цикла. Эта часть цикла занимает примерно половину времени всего цикла. Любая помощь будет признательна.

Ответ №1:

Вам не нужно создавать диктант для картографа, устанавливать индекс и обеспечивать, чтобы этого было достаточно (в некотором смысле серия-это диктант); что касается вашего вопроса, вы можете использовать replace вместо этого; это должно быть быстрее:

 mapper = prizes.set_index('Rank')['Payout']

pd.concat([payouts, ranking.replace(mapper)], axis=1)

   Lineup    1    2    3
0       1  100   25   50
1       2   50   50  100
2       3   25  100   25
 

В вашем примере не показана необходимость в fillna; вы можете добавить дополнительные сведения в свои данные для такого сценария. Кроме того, поскольку выплаты-это всего лишь один столбец, вы можете вместо этого создать серию, из которой может быть получен некоторый прирост производительности

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

1. По какой-то причине, когда я вставляю это в свой код, он не заменяет значения ранжирования значениями из mapper. Может быть, это связано с тем, что длина картографа не совпадает с длиной каждого столбца в рейтинге? Все, что он делает, — это выплевывает исходные рейтинги.

2. Вы присвоили новые значения ранжированию?

3. Прошу прощения, я не совсем понимаю, что вы имеете в виду.

Ответ №2:

Вот еще более быстрое (но менее краткое) решение, использующее базовый массив numpy. По сравнению с этим наблюдается прирост в ~1,7 раза replace .

 a = prizes.set_index('Rank')['Payout'].values
b = ranking.values-1 # get index as 0/1/2
c = a.take(b.flatten()).reshape(b.shape) # index in 1D and reshape to 2D
pd.DataFrame(c, columns=ranking.columns)
 

NB. Я разбил шаги для ясности, но это можно было бы сделать без промежуточных переменных

Выход:

      1    2    3
0  100   25   50
1   50   50  100
2   25  100   25
 

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

1. В итоге я получаю «Ошибка типа: Не удается преобразовать данные массива из dtype(‘float64’) в dtype(‘int64’) в соответствии с правилом» безопасно «» с помощью этой строки кода.

2. Это означает, что у вас, вероятно, есть значения с плавающей запятой в рейтинге, убедитесь, что ранги являются целыми числами