在函数中附加 DataFrame

Posted

技术标签:

【中文标题】在函数中附加 DataFrame【英文标题】:Append DataFrame inside Function 【发布时间】:2018-11-24 20:42:36 【问题描述】:

我有一个函数test,它接受一个 DataFrame 并将数据附加到它。我希望更改放置在函数中的全局变量。我有下面的脚本:

import pandas as pd
global dff

def test(df):
    df = df.append('asdf':1, 'sdf':2, ignore_index=True)
    return(df)

dff = pd.DataFrame()
test(dff)

在此之后,dff 保持为空;它没有被编辑。但是,如果您这样做:

import pandas as pd

def test(df):
    df['asdf'] = [1,2,3]
    return(df)

dff = pd.DataFrame()
test(dff)

dff'asfd' 列下将有[1,2,3]。请注意,我什至不必将变量声明为 global

为什么会这样?

实际上我很想知道,因为我认为我理解了可变工作空间,但事实证明我错了,我已经厌倦了不断地遇到这个 BS*

我知道问题的解决方法是:

import pandas as pd

def test(df):
    df = df.append('asdf':1, 'sdf':2, ignore_index=True)
    return(df)

dff = pd.DataFrame()
dff = test(dff)

但我真的只是想弄清楚为什么初始方法不起作用,尤其是考虑到我展示的第二个脚本。

*明明不是完全的BS,但是随便编程3年还是看不懂

【问题讨论】:

避免使用全局变量,它们迟早会伤害到你 @EvgenyPogrebnyak 我相信他们会的。这就是为什么我更喜欢第二个脚本的原因。我只是不明白是什么让第二个脚本起作用而第一个脚本不起作用。 关键字“全球”,实际上) 您使用的是全局错误,它们表明您何时使用了当前范围之外的东西,请阅读此***.com/questions/4693120/… @u2berggeist 正确,您可以定义:dff = pd.DataFrame() 而不必使用全局 【参考方案1】:

更新:

我在 PyCon 2015 上发现了一个非常好的演讲,它解释了我在下面试图解释的内容,但图表使其更加清晰。我将在下面留下解释来解释最初的 3 个脚本是如何工作的,但我建议您去看视频:

Ned Batchelder - Facts and Myths about Python names and values - PyCon 2015


所以,我想我已经弄清楚上面两个脚本中发生了什么。我会尝试打破它。如有需要,请随时纠正我。

几条规则:

    变量是指向实际保存数据的基础对象的链接/指针的名称。例如,街道地址。街道地址不是房子;它只是指向一所房子。所以地址(101 Streetway Rd.)就是指针。在 GPS 中,您可能会将其标记为“家”。 “家”这个词本身就是变量。

    函数作用于对象,而不是变量或指针。当您将变量传递给函数时,您实际上是在传递对象,而不是变量或指针。继续以房屋为例,如果您想为房屋添加甲板,您希望甲板承包商在房屋上工作,而不是形而上学的地址。

    函数中的return 命令返回一个指向对象的指针。所以这将是房屋的地址,而不是房屋或您可能称呼房屋的名称。

    = 是一个函数,意思是“指向这个对象”。 =前面的变量是输出,右边的变量是输入。这将是命名房屋的行为。所以Home = 101 Streetway Rd. 使变量Home 指向101 Streetway Rd 上的房子。假设您搬进了邻居家,即 102 Streetway Rd。这可以由Home = Neighbor's House 完成。现在,Home 现在是指针 102 Streetway Rd 的名称。

在这里,我将使用---> 来表示“指向”

在我们开始编写脚本之前,让我们从我们想要的开始。我们想要一个变量指向的对象objdff

脚本 1:

(没有global dff,因为它没有做任何相关的事情)

import pandas as pd

def test(df):
    df = df.append('asdf':1, 'sdf':2, ignore_index=True)
    return(df)

dff = pd.DataFrame()
test(dff)

让我们来看看这个函数。直到我们到达:

dff = pd.DataFrame()

在这里,我们将变量dff 分配给pd.DataFrame 创建的对象,这是一个空数据框。我们将此对象称为objdff。所以在这一行的末尾,我们有dff ---> objdff

下一行:test(dff)

函数作用于对象,所以我们要在dff 指向的对象上运行函数test,即objdff。这将我们带到了函数本身。

def test(df):

在这里,我们有一个本质上是= 的函数。传递给测试函数objdff 的对象由函数变量df 指向。所以现在df --->objdff dff---> objdff

进入下一行:df = df.append(...)

让我们从df.append(...) 开始。 .append(...) 被传递到 objdff。这使得对象objdff 运行一个名为'append' 的函数。正如@Jai 所指出的,.append(...) 方法使用return 命令来输出一个全新的 DataFrame,其中附加了数据。我们将新对象称为objdff_apnd

现在我们可以进入df = ... 部分。我们现在拥有的基本上是df = objdff_apnd。现在这很简单。变量df 现在指向对象objdff_apnd

在这一行的末尾我们有df ---> objdff_apnddff ---> objdff。这就是问题所在。 dff 没有指向我们想要的对象 (objdff_apnd)。

所以最后,变量dff 仍然指向objdff,而不是objdff_apnd。这将我们带到脚本 3(见下文)。

脚本 2:

import pandas as pd

def test(df):
    df['asdf'] = [1,2,3]
    return(df)

dff = pd.DataFrame()
test(dff)

就像脚本 1,dff ---> objdff。在test(dff) 期间,函数变量df ---> objdff。这就是不同的地方。

操作 (?) df['asdf'] = [1,2,3] 再次发送到底层对象 objdff。上次,这导致了一个新对象。然而这一次,['asdf'] 操作直接编辑对象objdff。所以对象objdff 中有额外的“asdf”列。

因此,最后我们有df ---> objdffdff ---> objdff。所以它们指向的是同一个对象,也就是说变量dff指向的是被编辑的对象。

一旦我们在函数外部中断,变量dff 仍然指向objdff,其中包含新数据。这给了我们想要的结果。

脚本 3:

import pandas as pd

def test(df):
    df = df.append('asdf':1, 'sdf':2, ignore_index=True)
    return(df)

dff = pd.DataFrame()
dff = test(dff)

除了dff = test(dff) 之外,此脚本与脚本 1 完全相同。我们稍后会讲到。

从脚本 1 的末尾继续,我们在函数 test(dff) 结束时离开右侧,我们有 dff ---> objdffdf ---> objdff_apnd

函数testreturn 命令,因此返回对象objdff_apnd。这会将行 dff = test(dff) 变为 dff = objdff_apnd

因此,最后我们得到了dff ---> objdff_apnd,这正是我们想要的结果。

【讨论】:

【参考方案2】: 我认为 pandas 数据框、列表和字典所有这些数据类型都是通过引用函数传递的,因此,这种行为。 在您要附加的第一个脚本中,它被附加到一个全新的对象上,因为append 返回一个新对象,因此它没有填充原始数据框。 在第二个脚本中,您将特定的数据框列分配给原始数据框对象,因此它用列填充原始数据框对象,因为您正在修改原始对象 你可以看看这个答案:python pandas dataframe, is it pass-by-value or pass-by-reference

查看此列表示例:

def test1(a):
    a.append(1)

def test2(a):
    a = [1, 2, 3]

def test3(a):
    a[0] = 10

aa = list()
test1(aa)
print(aa)

aa = list()
test2(aa)
print(aa)

aa = list([1])
test3(aa)
print(aa)

输出:

[1]
[]
[10]
将上述列表示例与 pandas 数据框示例联系起来 如果你查看Dataframe的append函数:DataFrame.append(other, ignore_index=False, verify_integrity=False, sort=None)[source] Append rows of other to the end of this frame, returning a new object. Columns not in this frame are added as new columns. 正如您在描述中看到的,append 返回一个新对象 您使用global 关键字的方式是错误的...我认为即使您在第一个脚本中没有global,仍然不会有任何区别...我不详细说明global 关键字,所以我不会提及它。但我知道如何使用关键字,这绝对不是正确的使用方式

【讨论】:

以上是关于在函数中附加 DataFrame的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 3 中附加一个函数范围之外的变量

在写入函数中附加文本 [R]

如何在python中“事后”将装饰器附加到函数上?

附加函数在调整大小的数组中不起作用

如何使用 wordpress 在 DOM 中附加回调函数

如何在现有构造函数中添加附加参数。 C++ ASIO