使用方法链接将 DataFrame 复制到新变量

copy a dataframe to new variable with method chaining

提问人:mouwsy 提问时间:9/16/2023 最后编辑:mouwsy 更新时间:9/16/2023 访问量:72

问:

是否可以将方法链中间的数据帧复制到新变量? 像这样:

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()

Python pandas 方法链接

评论

3赞 roganjosh 9/16/2023
好的,pandas 中的方法链接并不能提高计算速度。链式方法不会得到优化。如果您只是停止链,手动复制,然后继续处理 df,那绝对没有区别
0赞 smci 9/16/2023
同意 roganjosh 的建议,即带有明确副作用的编码会令人困惑,使用单独的赋值和表达式链会更清晰。你为什么要这么做?只是为了复制调试?还是生产代码?
0赞 mouwsy 9/16/2023
它应该用于生产。如果我可以使用类似的东西来代替运算符,那将提高可读性。谢谢你的想法。.copy_to_new_variable(df_imag):=
0赞 smci 9/16/2023
mouwsy:将是 .但是 pandas [](pandas.pydata.org/docs/reference/api/pandas.DataFrame.copy.html 故意不允许你在 RHS 上使用赋值目标,他们真的不希望你把有副作用的赋值放在管道中。为什么要在生产环境中执行此操作?这种代码会破坏很多东西,比如优化(例如numba)。顺便问一下,您希望副本是深拷贝还是浅拷贝?你的数据帧是整数、浮点数、字符串、任意对象......?.copy_to_new_variable(df_imag)df_imag :=df.copy()
1赞 juanpa.arrivillaga 9/16/2023
非常非常重要的一点是,要理解它不会复制数据框df_imag = df

答:

3赞 Andrej Kesely 9/16/2023 #1

使用运算符::=

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

评论

1赞 Quang Hoang 9/16/2023
应该注意这是一个 Python 3.8+ 操作。
2赞 mozway 9/16/2023 #2

动态创建变量不是一个好主意,但您可以轻松地利用可变对象(如字典)。

添加新的 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'])

印刷

在同一行中,您还可以添加一个可链接的方法,或者再次在管道中使用 lambda: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()
      )

评论

0赞 mouwsy 9/17/2023
非常感谢,这就是我一直在找的。只是为了更好地理解:做什么和做什么?我可以放下这些吗?因为代码也可以在没有这些的情况下工作。from pandas.core.base import PandasObjectPandasObject.to_name = to_name
1赞 mozway 9/17/2023
这是添加新的 DataFrame 方法 () 所必需的,否则该方法不会退出,但您只需在每个会话中运行一次,它就适用于所有 DataFrame。如果您使用该方法,则不需要它。to_namepipe