Dataframe 朱莉娅:将数据帧传递给函数会创建指向该数据帧的指针?
我有一个函数,其中我规范化了数据帧的前N列。我想返回规范化的数据帧,但不返回原始数据帧。然而,该函数似乎也改变了传递的数据帧Dataframe 朱莉娅:将数据帧传递给函数会创建指向该数据帧的指针?,dataframe,normalization,julia,Dataframe,Normalization,Julia,我有一个函数,其中我规范化了数据帧的前N列。我想返回规范化的数据帧,但不返回原始数据帧。然而,该函数似乎也改变了传递的数据帧 using DataFrames function normalize(input_df::DataFrame, cols::Array{Int}) norm_df = input_df for i in cols norm_df[i] = (input_df[i] - minimum(input_df[i])) /
using DataFrames
function normalize(input_df::DataFrame, cols::Array{Int})
norm_df = input_df
for i in cols
norm_df[i] = (input_df[i] - minimum(input_df[i])) /
(maximum(input_df[i]) - minimum(input_df[i]))
end
norm_df
end
using RDatasets
iris = dataset("datasets", "iris")
println("original df:\n", head(iris))
norm_df = normalize(iris, [1:4]);
println("should be the same:\n", head(iris))
输出:
original df:
6x5 DataFrame
| Row | SepalLength | SepalWidth | PetalLength | PetalWidth | Species |
|-----|-------------|------------|-------------|------------|----------|
| 1 | 5.1 | 3.5 | 1.4 | 0.2 | "setosa" |
| 2 | 4.9 | 3.0 | 1.4 | 0.2 | "setosa" |
| 3 | 4.7 | 3.2 | 1.3 | 0.2 | "setosa" |
| 4 | 4.6 | 3.1 | 1.5 | 0.2 | "setosa" |
| 5 | 5.0 | 3.6 | 1.4 | 0.2 | "setosa" |
| 6 | 5.4 | 3.9 | 1.7 | 0.4 | "setosa" |
should be the same:
6x5 DataFrame
| Row | SepalLength | SepalWidth | PetalLength | PetalWidth | Species |
|-----|-------------|------------|-------------|------------|----------|
| 1 | 0.222222 | 0.625 | 0.0677966 | 0.0416667 | "setosa" |
| 2 | 0.166667 | 0.416667 | 0.0677966 | 0.0416667 | "setosa" |
| 3 | 0.111111 | 0.5 | 0.0508475 | 0.0416667 | "setosa" |
| 4 | 0.0833333 | 0.458333 | 0.0847458 | 0.0416667 | "setosa" |
| 5 | 0.194444 | 0.666667 | 0.0677966 | 0.0416667 | "setosa" |
| 6 | 0.305556 | 0.791667 | 0.118644 | 0.125 | "setosa" |
朱莉娅使用了一种被称为“传递共享”的行为。从文档(我的重点): Julia函数参数遵循一种有时称为“通过共享传递”的约定,这意味着值在传递给函数时不会被复制。函数参数本身充当新的变量绑定(可以引用值的新位置),但它们引用的值与传递的值相同。调用方可以看到在函数中对可变值(如数组)所做的修改。这与Scheme、大多数Lisp、Python、Ruby和Perl以及其他动态语言中的行为相同 在您的特定情况下,您似乎想要做的是为规范化操作创建一个全新的独立数据帧。您可以使用
deepcopy
执行此操作,例如:
norm_df = deepcopy(input_df)
Julia通常会要求您显式地执行这类操作,因为创建大型数据帧的独立副本在计算上可能非常昂贵,而且Julia是一种面向性能的语言
再次从文档中,注意copy
和deepcopy
之间的以下重要区别:
copy(x)
:创建x的浅层副本:复制外部结构,但不复制所有内部值。例如,复制数组会生成一个新数组,该数组的元素与原始数组的元素相同
deepcopy(x)
:创建x的深度副本:所有内容都以递归方式复制,从而生成完全独立的对象。例如,深度复制数组会生成一个新数组,其元素是原始元素的深度副本
类型DataFrame
类似于数组,因此需要deepcopy
一个相关的SO问题是。谢谢!那么在哪种情况下是复制对象还是深度复制?例如,如果我这样做,
iris2=iris
,然后将iris2
传递给normalize()
,那么iris
是否也会被修改?@AlexanderFlyax是的,iris
会被修改。通常,任何形式的y=x
操作都不会创建新的独立对象,无论其类型如何(尽管有趣的是,对于向量,y=x[1:end]
将(我认为)创建一个独立对象)。对于像Int64
这样的具体类型,copy
将创建一个新的独立对象,但是对于Array{Int64}
,copy
只创建一个独立的外部结构,而不创建独立的内部元素,因此在处理数组时需要deepcopy
DataFrame
类似于数组,因此需要使用deepcopy
。感谢您的解释。我不是想批评Julia,只是想理解:如果修改副本会修改原始数组,为什么我需要制作数组的副本?如果我想修改原始数组,我会这么做,不是吗?也就是说,它充其量是多余的,充其量是适得其反的。“我错过什么了吗?”亚历山大·弗利克斯问得好。我不知道!我的最佳猜测是,它是围绕作用域展开的,即在某些情况下,在一个作用域中有一组引用(例如调用函数),同时在不同的作用域中也有一组独立的引用(例如被调用函数),这是很有用的,但这两组引用都指向内存中的同一对象。请不要把这当作福音。也许你可以把这个问题作为一个新的问题提出来(不一定非得针对茱莉亚),然后得到更明智的回答。。。