Spark Dataframe unterscheiden Spalten mit doppeltem Namen


80

Wie ich in Spark Dataframe weiß, kann dieser für mehrere Spalten denselben Namen haben, wie im folgenden Datenrahmen-Snapshot gezeigt:

[
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=125231, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0047, 3: 0.0, 4: 0.0043})),
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=145831, f=SparseVector(5, {0: 0.0, 1: 0.2356, 2: 0.0036, 3: 0.0, 4: 0.4132})),
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=147031, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
Row(a=107831, f=SparseVector(5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}), a=149231, f=SparseVector(5, {0: 0.0, 1: 0.0032, 2: 0.2451, 3: 0.0, 4: 0.0042}))
]

Das obige Ergebnis wird durch Verknüpfen mit einem Datenrahmen zu sich selbst erstellt. Sie können sehen, dass 4Spalten mit zwei aund vorhanden sind f.

Das Problem ist, dass ich dort bin, wenn ich versuche, mehr Berechnungen mit der aSpalte durchzuführen. Ich kann keine Möglichkeit finden, die auszuwählen a. Ich habe es versucht df[0]und df.select('a')beide haben mich unter Fehlermeldung zurückgegeben:

AnalysisException: Reference 'a' is ambiguous, could be: a#1333L, a#1335L.

Gibt es in der Spark-API überhaupt eine Möglichkeit, die Spalten wieder von den duplizierten Namen zu unterscheiden? oder vielleicht eine Möglichkeit, die Spaltennamen zu ändern?

Antworten:


58

Ich würde empfehlen, dass Sie die Spaltennamen für Ihre ändern join.

df1.select(col("a") as "df1_a", col("f") as "df1_f")
   .join(df2.select(col("a") as "df2_a", col("f") as "df2_f"), col("df1_a" === col("df2_a"))

Das Ergebnis DataFramewird habenschema

(df1_a, df1_f, df2_a, df2_f)

5
Möglicherweise müssen Sie Ihre Antwort korrigieren, da die Anführungszeichen zwischen den Spaltennamen nicht richtig angepasst werden.
Sameh Sharaf

2
@ SamehSharaf Ich gehe davon aus, dass Sie derjenige sind, der meine Antwort abgelehnt hat? Aber die Antwort ist tatsächlich zu 100% richtig - ich verwende einfach die Scala- 'Kurzschrift für die Spaltenauswahl, sodass es in der Tat kein Problem mit Anführungszeichen gibt.
Glennie Helles Sindholt

31
@GlennieHellesSindholt, fairer Punkt. Es ist verwirrend, weil die Antwort als pythonund markiert ist pyspark.
Jorge Leitao

Was ist, wenn jeder Datenrahmen mehr als 100 Spalten enthält und wir nur einen Spaltennamen umbenennen müssen, der gleich ist? Sicherlich können nicht alle diese Spaltennamen in der select-Klausel
bikashg

5
In diesem Fall könnten Sie mit gehendf1.withColumnRenamed("a", "df1_a")
Glennie Helles Sindholt

100

Beginnen wir mit einigen Daten:

from pyspark.mllib.linalg import SparseVector
from pyspark.sql import Row

df1 = sqlContext.createDataFrame([
    Row(a=107831, f=SparseVector(
        5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
    Row(a=125231, f=SparseVector(
        5, {0: 0.0, 1: 0.0, 2: 0.0047, 3: 0.0, 4: 0.0043})),
])

df2 = sqlContext.createDataFrame([
    Row(a=107831, f=SparseVector(
        5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
    Row(a=107831, f=SparseVector(
        5, {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0})),
])

Es gibt verschiedene Möglichkeiten, wie Sie dieses Problem angehen können. Zunächst können Sie untergeordnete Spalten mit übergeordneten Spalten eindeutig referenzieren:

df1.join(df2, df1['a'] == df2['a']).select(df1['f']).show(2)

##  +--------------------+
##  |                   f|
##  +--------------------+
##  |(5,[0,1,2,3,4],[0...|
##  |(5,[0,1,2,3,4],[0...|
##  +--------------------+

Sie können auch Tabellenaliasnamen verwenden:

from pyspark.sql.functions import col

df1_a = df1.alias("df1_a")
df2_a = df2.alias("df2_a")

df1_a.join(df2_a, col('df1_a.a') == col('df2_a.a')).select('df1_a.f').show(2)

##  +--------------------+
##  |                   f|
##  +--------------------+
##  |(5,[0,1,2,3,4],[0...|
##  |(5,[0,1,2,3,4],[0...|
##  +--------------------+

Schließlich können Sie Spalten programmgesteuert umbenennen:

df1_r = df1.select(*(col(x).alias(x + '_df1') for x in df1.columns))
df2_r = df2.select(*(col(x).alias(x + '_df2') for x in df2.columns))

df1_r.join(df2_r, col('a_df1') == col('a_df2')).select(col('f_df1')).show(2)

## +--------------------+
## |               f_df1|
## +--------------------+
## |(5,[0,1,2,3,4],[0...|
## |(5,[0,1,2,3,4],[0...|
## +--------------------+

7
Vielen Dank für Ihre Bearbeitung, weil Sie in diesen mehrdeutigen Fällen so viele Möglichkeiten gezeigt haben, die richtige Spalte zu erhalten. Ich denke, Ihre Beispiele sollten in den Spark-Programmierleitfaden aufgenommen werden. Ich habe viel gelernt!
Resec

kleine Korrektur: df2_r = **df2** .select(*(col(x).alias(x + '_df2') for x in df2.columns))statt df2_r = df1.select(*(col(x).alias(x + '_df2') for x in df2.columns)). Im Übrigen gute Sachen
Vzzarr

Ich bin damit einverstanden, dass dies Teil des Spark-Programmierhandbuchs sein sollte. Reines Gold. Ich konnte endlich die Quelle der Mehrdeutigkeit entwirren, indem ich Spalten anhand der alten Namen auswählte, bevor ich den Join durchführte. Die Lösung, Suffixe programmgesteuert an die Namen der Spalten anzuhängen, bevor der Join ausgeführt wird, entfernt alle Mehrdeutigkeiten.
Pablo Adames

26

Es gibt einen einfacheren Weg als Aliase für alle Spalten zu schreiben, denen Sie beitreten, indem Sie Folgendes tun:

df1.join(df2,['a'])

Dies funktioniert, wenn der Schlüssel, dem Sie beitreten, in beiden Tabellen identisch ist.

Siehe https://kb.databricks.com/data/join-two-dataframes-duplicated-columns.html


4
Dies ist die eigentliche Antwort ab Spark 2+
Matt

2
Und für Scala: df1.join (df2, Seq ("a"))
mauriciojost

1
Seite wurde verschoben zu: kb.databricks.com/data/…
bogdan.rusu

7

Mit der def drop(col: Column)Methode können Sie die duplizierte Spalte löschen, z. B.:

DataFrame:df1

+-------+-----+
| a     | f   |
+-------+-----+
|107831 | ... |
|107831 | ... |
+-------+-----+

DataFrame:df2

+-------+-----+
| a     | f   |
+-------+-----+
|107831 | ... |
|107831 | ... |
+-------+-----+

Wenn ich df1 mit df2 verbinde, sieht der DataFrame wie folgt aus:

val newDf = df1.join(df2,df1("a")===df2("a"))

DataFrame:newDf

+-------+-----+-------+-----+
| a     | f   | a     | f   |
+-------+-----+-------+-----+
|107831 | ... |107831 | ... |
|107831 | ... |107831 | ... |
+-------+-----+-------+-----+

Jetzt können wir die def drop(col: Column)Methode verwenden, um die duplizierte Spalte 'a' oder 'f' wie folgt zu löschen:

val newDfWithoutDuplicate = df1.join(df2,df1("a")===df2("a")).drop(df2("a")).drop(df2("f"))

Würde dieser Ansatz funktionieren, wenn Sie eine äußere Verknüpfung durchführen und die beiden Spalten unterschiedliche Werte haben?
Prafi

Möglicherweise möchten Sie nicht löschen, wenn unterschiedliche Beziehungen mit demselben Schema bestehen.
Thebluephantom

5

Nachdem ich mich in die Spark-API aliaseingegraben hatte, stellte ich fest, dass ich zuerst einen Alias ​​für den ursprünglichen Datenrahmen erstellen und dann withColumnRenamedjede Spalte des Alias ​​manuell umbenennen kann. Dies führt dazu, joindass der Spaltenname nicht dupliziert wird.

Weitere Informationen finden Sie unter der Spark Dataframe-API :

pyspark.sql.DataFrame.alias

pyspark.sql.DataFrame.withColumnRenamed

Ich denke jedoch, dass dies nur eine mühsame Problemumgehung ist und frage mich, ob es einen besseren Weg für meine Frage gibt.


4

Auf diese Weise können wir in PySpark zwei Dataframes mit denselben Spaltennamen verbinden.

df = df1.join(df2, ['col1','col2','col3'])

Wenn Sie dies printSchema()danach tun , können Sie sehen, dass doppelte Spalten entfernt wurden.


3

Angenommen, die DataFrames, denen Sie beitreten möchten, sind df1 und df2, und Sie verbinden sie in Spalte 'a'. Dann haben Sie zwei Methoden

Methode 1

df1.join (df2, 'a', 'left_outer')

Dies ist eine großartige Methode und wird dringend empfohlen.

Methode 2

df1.join (df2, df1.a == df2.a, 'left_outer'). drop (df2.a)


1

Dies ist möglicherweise nicht der beste Ansatz. Wenn Sie jedoch die doppelten Spalten (nach dem Join) umbenennen möchten, können Sie dies mit dieser kleinen Funktion tun.

def rename_duplicate_columns(dataframe):
    columns = dataframe.columns
    duplicate_column_indices = list(set([columns.index(col) for col in columns if columns.count(col) == 2]))
    for index in duplicate_column_indices:
        columns[index] = columns[index]+'2'
    dataframe = dataframe.toDF(*columns)
    return dataframe

1

Wenn nur die Schlüsselspalte in beiden Tabellen identisch ist, versuchen Sie es auf folgende Weise (Ansatz 1):

left. join(right , 'key', 'inner')

eher als unten (Ansatz 2):

left. join(right , left.key == right.key, 'inner')

Vorteile der Verwendung von Ansatz 1:

  • Der 'Schlüssel' wird im endgültigen Datenrahmen nur einmal angezeigt
  • einfach die Syntax zu verwenden

Nachteile der Verwendung von Ansatz 1:

  • Hilfe nur mit der Schlüsselspalte
  • Szenarien, in denen bei einem Link-Join die Verwendung der Nullanzahl für den rechten Schlüssel geplant ist, funktioniert dies nicht. In diesem Fall muss einer der Schlüssel wie oben erwähnt umbenannt werden.

0

Wenn Sie einen komplizierteren Anwendungsfall haben als in der Antwort von Glennie Helles Sindholt beschrieben, z. B. haben Sie andere / wenige nicht verknüpfte Spaltennamen, die ebenfalls identisch sind und diese bei der Auswahl unterscheiden möchten. Verwenden Sie am besten Aliasse, z.

df3 = df1.select("a", "b").alias("left")\
   .join(df2.select("a", "b").alias("right"), ["a"])\
   .select("left.a", "left.b", "right.b")

df3.columns
['a', 'b', 'b']
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.