2015-01-20 7 views
5

Ich habe eine Funktion, in der ich die ersten N Spalten eines DataFrame normalisieren. Ich möchte den normalisierten DataFrame zurückgeben, aber das Original in Ruhe lassen. Es scheint aber so, als ob die Funktion auch den übergebenen DataFrame mutiert!Julia: Übergabe eines DataFrame an eine Funktion erstellt einen Zeiger auf den DataFrame?

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

Ausgang:

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" | 

Antwort

9

Julia verwendet ein Verhalten als "Pass-by-Sharing" bekannt. Aus der Dokumentation (Hervorhebung von mir):

Julia Funktionsargumente folgen einer Konvention manchmal "Pass-by-Sharing" genannt, was bedeutet, dass Werte nicht kopiert werden, wenn sie an Funktionen übergeben werden. Funktionsargumente selbst fungieren als neue Variablenbindungen (neue Speicherorte, die auf Werte verweisen können), aber die Werte, auf die sie verweisen, sind mit den übergebenen Werten identisch. Änderungen an veränderbaren Werten (z. B. Arrays), die innerhalb einer Funktion vorgenommen wurden, sind für den Aufrufer sichtbar. Dies ist das gleiche Verhalten wie in Schema, den meisten Lisps, Python, Ruby und Perl, neben anderen dynamischen Sprachen.

In Ihrem speziellen Fall, was Sie scheinen zu tun, ist ein völlig neues und unabhängiges DataFrame für Ihre Normalisierung zu erstellen. Sie können dies unter Verwendung von deepcopy, z.

norm_df = deepcopy(input_df) 

Julia Regel benötigen Sie diese Art von Dingen explizit zu tun, da eine unabhängige Kopie eines großen Datenrahmen zu schaffen rechnerisch teuer sein und Julia ist eine leistungsorientierte Sprache.

wieder von der docs, beachten Sie folgenden wichtigen Unterschied zwischen copy und deepcopy:

copy(x): einer flache Kopie von x erstellen: die äußeren Struktur kopiert wird, aber nicht alle internen Werte. Wenn Sie beispielsweise ein Array kopieren, wird ein neues Array mit identischen Elementen wie das Original erstellt.

deepcopy(x): Erstellen Sie eine tiefe Kopie von x: alles wird rekursiv kopiert, was zu einem völlig unabhängigen Objekt führt. Zum Beispiel erzeugt das tiefe Kopieren eines Arrays ein neues Array, dessen Elemente Tiefkopien der ursprünglichen Elemente sind.

Typ DataFrame ist Array-like, und so deepcopy notwendig.

Eine verwandte SO Frage ist here.

+0

Danke! In welchen Fällen wird also ein Objekt kopiert oder tief kopiert? Wenn ich zum Beispiel "iris2 = iris" mache und dann "iris2" an "normalize()" übergebe, sollte auch "iris" modifiziert werden? –

+0

@AlexanderFlyax Ja, Iris würde geändert werden. Normalerweise erzeugt jede Operation der Form y = x * kein neues unabhängiges Objekt, unabhängig vom Typ (obwohl interessanterweise für Vektoren, y = x [1: end] '* wird * (denke ich), erzeuge ein unabhängiges Objekt). Für konkrete Typen wie 'Int64' erstellt 'copy' * * ein neues unabhängiges Objekt, aber für' Array {Int64} 'erstellt' copy' nur eine unabhängige äußere Struktur, aber keine unabhängigen internen Elemente, daher die Notwendigkeit für ' decopy beim Umgang mit Arrays. 'DataFrame' ist Array-ähnlich, und daher wird 'deepcopy' benötigt. –

+0

Danke für Ihre Erklärung.Ich versuche nicht, Julia kritisch gegenüberzustehen, ich versuche nur zu verstehen: Warum sollte ich eine Kopie eines Arrays erstellen, wenn das Ändern der Kopie das ursprüngliche Array verändert? Wenn ich das ursprüngliche Array ändern wollte, würde ich das einfach tun, oder? Es scheint im besten Fall überflüssig und im schlimmsten Fall kontraproduktiv zu sein. Fehle ich etwas? –

Verwandte Themen