Скопируйте фрейм данных в новую переменную с помощью цепочки методов

Можно ли скопировать фрейм данных в середине цепочки методов в новую переменную? Что-то вроде:

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(), но это приведет к нарушению цепочки методов.

Хорошо. Цепочка методов в пандах не увеличивает скорость оценки. Связанные методы не оптимизируются. Не было бы абсолютно никакой разницы, если бы вы просто остановили цепочку, сделали копию вручную, а затем продолжили обработку df.

roganjosh 15.09.2023 22:53

Согласитесь с рекомендацией Роганджоша о том, что кодирование с явными побочными эффектами будет запутанным, поэтому проще использовать отдельное присваивание и цепочку выражений. Зачем ты это делаешь? просто сделать копию для отладки? или производственный код?

smci 15.09.2023 23:05

Это должно быть для производства. Читабельность повысилась бы, если бы я мог использовать что-то вроде .copy_to_new_variable(df_imag) вместо оператора :=. Спасибо за ваши мысли.

mouwsy 15.09.2023 23:18

mouwsy: .copy_to_new_variable(df_imag) будет синтаксическим сахаром для df_imag :=. Но pandas [df.copy()](pandas.pydata.org/docs/reference/api/pandas.Data‌​Frame.copy.html намеренно не позволяет вам использовать цель назначения в RHS, они действительно не хотят вы помещаете присваивания с побочными эффектами в конвейер. Почему вы хотите сделать это в производстве? Такой код нарушит многие вещи, например оптимизацию (например, numba). Кстати, вы хотите, чтобы копия была глубокая или неглубокая копия?Ваш фрейм данных — целые, плавающие, строки, произвольные объекты...?

smci 15.09.2023 23:33

Очень, очень важно понимать, что df_imag = df не копирует фрейм данных.

juanpa.arrivillaga 16.09.2023 10:44
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
5
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Используйте оператор :=:

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+.

Quang Hoang 15.09.2023 22:53
Ответ принят как подходящий

Динамическое создание переменных — не очень хорошая идея, но вы можете легко воспользоваться преимуществами изменяемых объектов, таких как словари.

Добавляем новый метод 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? Могу ли я бросить это? Потому что код работает и без них.

mouwsy 16.09.2023 19:44

Это необходимо для добавления нового метода DataFrame (to_name), который в противном случае не завершился бы, но вам нужно запустить его только один раз за сеанс, и он будет работать для всех DataFrame. Вам это не понадобится, если вы используете подход pipe.

mozway 16.09.2023 20:36

Другие вопросы по теме