#python #dataframe #list-comprehension #dictionary-comprehension
#python #фрейм данных #список-понимание #понимание словаря
Вопрос:
Итак, у меня есть куча функций, которые я хочу создать с разными параметрами. Один из параметров df
будет предоставлен вызывающим эти функции. Я думал, что понял это, но когда я действительно использовал его, каждая созданная функция имела одинаковые параметры, последнюю комбинацию в последовательности понимания списка. странно.
from itertools import product
feature_functions = {
**{f'{col}{i}': lambda x: createFeature(df=x, i=i, col=col, name=f'{col}{i}')
for col, i in product(['New', 'Lost', 'Change'], list(range(1, 31)))},
как я уже сказал, я подумал, что это довольно гладко, но когда я использовал это так:
feature_functions['New1'](df)
Я получил этот результат, что означает, что он использовал «Изменение» и 30 для каждой лямбда-функции:
# feature pd.Series:
0 NaN
...
4593 1.002706
Name: Change30, Length: 4594, dtype: float64
Я попробовал несколько вещей, но ничего не изменилось. Как я неправильно использую это понимание словаря?
РЕДАКТИРОВАТЬ: Кстати, одна вещь, которую я сделал, чтобы убедиться, что это правильно, это поместить lambda x: ...
в кавычки, затем я мог просто распечатать все это, и это выглядело довольно хорошо. итак, каким-то образом лямбда-выражение мешает? Я попытался обернуть его, (lambda x: ...)
но это ничего не дало.
{'New1': "lambda x: createFeature(df=x, i=1, col=New, name='New1')",
'New2': "lambda x: createFeature(df=x, i=2, col=New, name='New2')",
'New3': "lambda x: createFeature(df=x, i=3, col=New, name='New3')",
'New4': "lambda x: createFeature(df=x, i=4, col=New, name='New4')",
...
}
Комментарии:
1. Хотелось бы, чтобы у меня было больше времени, чтобы глубже разобраться в этом, потому что это кажется интересным. Я бы предположил, что это как-то связано с тем, что закрытие неправильно фиксирует измененные значения. Возможно, использование functools.partial может быть способом заставить это работать?
Ответ №1:
Вы действительно близки. Здесь не используйте lambda
, а partial
функционируйте из functools
модуля:
# dummy function
def createFeature(df, i, col, name):
print(df)
print(i, col, name)
feature_functions = {
**{f'{col}{i}': partial(createFeature, i=i, col=col, name=f'{col}{i}')
for col, i in product(['New', 'Lost', 'Change'], list(range(1, 31)))}}
Использование:
>>> feature_functions['New1'](pd.DataFrame)
Empty DataFrame
Columns: []
Index: []
1 New New1
>>> feature_functions['Lost23'](pd.DataFrame())
Empty DataFrame
Columns: []
Index: []
23 Lost Lost23
>>> feature_functions['Change12'](pd.DataFrame())
Empty DataFrame
Columns: []
Index: []
12 Change Change12
Ответ №2:
Хорошо, это очень интересно. Если вы создаете функцию, которая возвращает ваш лямбда, например:
def createFeatureCreator(i, col):
return lambda x: createFeature(df=x, i=i, col=col, name=f'{col}{i}')
и сделайте свое понимание (я удалил много ложных вещей, которые у вас были):
feature_functions = {f'{col}{i}': createFeatureCreator(i, col)
for col, i in product(['New', 'Lost', 'Change'], range(1, 31))}
это работает так, как вы и ожидали.
Причина, по которой конструкция «лямбда» не работает напрямую, на самом деле очень интересна: лямбда захватывает среду. Понимание dict — это единая среда, в которой переменные i
и col
изменяются на каждой итерации цикла. Когда создается лямбда-выражение (а на самом деле создается 93 разных лямбда-выражения), все они захватывают одну и ту же среду, поэтому при их выполнении значения i
и col
являются последним значением, которое они имели в среде (f-строка расширяется до вызова функции, который не выполняется, потому что он находится внутрилямбда-выражение, и оно выполняется только тогда, когда вы на самом деле вызываете функцию, поэтому name также кажется «неправильным»).
Комментарии:
1. Я предпочитаю этот ответ, потому что это просто родной python (без импорта partial из functools). Изолируйте лямбда. Я думаю, что большинство людей предпочитают частичный вариант, утверждая, что он более «питонический», поскольку он был создан именно для такого рода вещей. Однако ваш мне нравится больше, потому что я использовал его постоянно, а затем, работая на других языках, в которых не было такой концепции, но были лямбда-или встроенные функции, я сначала не был уверен, что делать, поскольку я использовал partial в качестве опоры.
2. о, кстати, я нашел еще один безумный способ сделать это
{f'{col}{i}': eval(f"lambda x: createFeature(df=x, i={i}, col='{col}', name='{col}{i}')") for ...}
3. @LegitStack Это тоже работает, поскольку лямбда-выражение ничего не использует из среды понимания, но я бы максимально избегал использования оценок. Они, как правило, опасны, помимо того, что имеют плохую производительность (каждая итерация цикла eval будет анализироваться).