Можно ли скопировать фрейм данных в середине цепочки методов в новую переменную? Что-то вроде:
import pandas as pd
df = (pd.DataFrame([[2, 4, 6],
[8, 10, 12],
[14, 16, 18],
])
.assign(something_else=100)
.div(2)
.copy_to_new_variable(df_imag) # Imaginated method to copy df to df_imag.
.div(10)
)
print(df_imag) затем вернется:
0 1 2 something_else
0 1.0 2.0 3.0 50.0
1 4.0 5.0 6.0 50.0
2 7.0 8.0 9.0 50.0
.copy_to_new_variable(df_imag) можно заменить на df_imag = df.copy(), но это приведет к нарушению цепочки методов.
Согласитесь с рекомендацией Роганджоша о том, что кодирование с явными побочными эффектами будет запутанным, поэтому проще использовать отдельное присваивание и цепочку выражений. Зачем ты это делаешь? просто сделать копию для отладки? или производственный код?
Это должно быть для производства. Читабельность повысилась бы, если бы я мог использовать что-то вроде .copy_to_new_variable(df_imag) вместо оператора :=. Спасибо за ваши мысли.
mouwsy: .copy_to_new_variable(df_imag) будет синтаксическим сахаром для df_imag :=. Но pandas [df.copy()](pandas.pydata.org/docs/reference/api/pandas.DataFrame.copy.html намеренно не позволяет вам использовать цель назначения в RHS, они действительно не хотят вы помещаете присваивания с побочными эффектами в конвейер. Почему вы хотите сделать это в производстве? Такой код нарушит многие вещи, например оптимизацию (например, numba). Кстати, вы хотите, чтобы копия была глубокая или неглубокая копия?Ваш фрейм данных — целые, плавающие, строки, произвольные объекты...?
Очень, очень важно понимать, что df_imag = df не копирует фрейм данных.






Используйте оператор :=:
df = (df_imag := df.assign(new_var=100).div(2)).div(10)
print(df)
print(df_imag)
Распечатки:
0 1 2 new_var
0 0.1 0.2 0.3 5.0
1 0.4 0.5 0.6 5.0
2 0.7 0.8 0.9 5.0
0 1 2 new_var
0 1.0 2.0 3.0 50.0
1 4.0 5.0 6.0 50.0
2 7.0 8.0 9.0 50.0
Вероятно, следует отметить, что это версия Python 3.8+.
Динамическое создание переменных — не очень хорошая идея, но вы можете легко воспользоваться преимуществами изменяемых объектов, таких как словари.
Добавляем новый метод DataFrame, чтобы сделать это легко:
from pandas.core.base import PandasObject
### this only needs to be done once per session
def to_name(df, dic, name, copy=False):
dic[name] = df.copy() if copy else df
return df
PandasObject.to_name = to_name
###
tmp = {}
df = (pd.DataFrame([[2, 4, 6],
[8, 10, 12],
[14, 16, 18],
])
.assign(something_else=100)
.div(2)
.to_name(tmp, 'after_div2', copy=True)
.div(10)
)
print(tmp['after_div2'])
print(df)
Выход:
# tmp['after_div2']
0 1 2 something_else
0 1.0 2.0 3.0 50.0
1 4.0 5.0 6.0 50.0
2 7.0 8.0 9.0 50.0
# df
0 1 2 something_else
0 0.1 0.2 0.3 5.0
1 0.4 0.5 0.6 5.0
2 0.7 0.8 0.9 5.0
Если вы не хотите исправлять объекты DataFrame, используйте канал :
def to_name(df, dic, name, copy=False):
dic[name] = df.copy() if copy else df
return df
tmp = {}
df = (pd.DataFrame([[2, 4, 6],
[8, 10, 12],
[14, 16, 18],
])
.assign(something_else=100)
.div(2)
.pipe(to_name, tmp, 'after_div2')
.div(10)
.pipe(lambda df: print('\nQuick alternative:', df, sep='\n') or df)
)
print(tmp['after_div2'])
В той же строке вы также можете добавить цепной метод print или снова использовать лямбду в трубе :
from pandas.core.base import PandasObject
### this only needs to be done once per session
def df_print(df, *args):
if args:
print(*args)
print(df)
return df
PandasObject.print = df_print
###
df = (pd.DataFrame([[2, 4, 6],
[8, 10, 12],
[14, 16, 18],
])
.print()
.assign(something_else=100)
.div(2)
.print('\nAfter 2:')
.div(10)
.pipe(lambda df: print('\nQuick alternative:', df, sep='\n') or df)
)
Выход:
0 1 2
0 2 4 6
1 8 10 12
2 14 16 18
After 2:
0 1 2 something_else
0 1.0 2.0 3.0 50.0
1 4.0 5.0 6.0 50.0
2 7.0 8.0 9.0 50.0
Quick alternative:
0 1 2 something_else
0 0.1 0.2 0.3 5.0
1 0.4 0.5 0.6 5.0
2 0.7 0.8 0.9 5.0
Вы также можете создать модуль:
pandas_debug.py
from pandas.core.base import PandasObject
def df_print(df, *args):
if args:
print(*args)
print(df)
return df
PandasObject.print = df_print
def to_name(df, dic, name, copy=False):
dic[name] = df.copy() if copy else df
return df
PandasObject.to_name = to_name
Затем в вашем коде:
import pandas as pd
import pandas_debug
tmp = {}
df = (pd.DataFrame([[2, 4, 6],
[8, 10, 12],
[14, 16, 18],
])
.assign(something_else=100)
.div(2)
.to_name(tmp, 'after_div2')
.div(10)
.print()
)
Большое спасибо, это то, что я искал. Просто для лучшего понимания: что делают from pandas.core.base import PandasObject и PandasObject.to_name = to_name? Могу ли я бросить это? Потому что код работает и без них.
Это необходимо для добавления нового метода DataFrame (to_name), который в противном случае не завершился бы, но вам нужно запустить его только один раз за сеанс, и он будет работать для всех DataFrame. Вам это не понадобится, если вы используете подход pipe.
Хорошо. Цепочка методов в пандах не увеличивает скорость оценки. Связанные методы не оптимизируются. Не было бы абсолютно никакой разницы, если бы вы просто остановили цепочку, сделали копию вручную, а затем продолжили обработку df.