2016-04-13 6 views

Antwort

3

Wie in den Kommentaren zu dieser Frage wies darauf hin, weder GraphFrames noch GraphX ​​haben eingebaute Unterstützung für bipartite Graphen. Beide haben jedoch mehr als genug Flexibilität, um zweiseitige Graphen erstellen zu können. Eine GraphX-Lösung finden Sie unter this previous answer. Diese Lösung verwendet ein gemeinsames Merkmal zwischen dem unterschiedlichen Vertex-/Objekttyp. Und während das mit RDDs funktioniert, wird das für DataFrames nicht funktionieren. Eine Zeile in einem DataFrame hat ein festes Schema - es kann manchmal keine price Spalte enthalten und manchmal nicht. Es kann eine price Spalte haben, die manchmal null ist, aber die Spalte muss in jeder Zeile existieren.

Stattdessen scheint die Lösung für GraphFrames zu sein, dass Sie ein DataFrame definieren müssen, die im Wesentlichen eine lineare Untertyp der beiden Arten von Objekten in der zweiteiligen Graphen ist - es hat alle Felder beiden Typen enthält von Objekte. Das ist eigentlich ziemlich einfach - ein join mit full_outer wird Ihnen das geben. Etwas wie folgt aus:

val players = Seq(
    (1,"dave", 34), 
    (2,"griffin", 44) 
).toDF("id", "name", "age") 

val teams = Seq(
    (101,"lions","7-1"), 
    (102,"tigers","5-3"), 
    (103,"bears","0-9") 
).toDF("id","team","record") 

Sie könnten dann erstellen ein Super-Set DataFrame wie folgt aus:

val teamPlayer = players.withColumnRenamed("id", "l_id").join(
    teams.withColumnRenamed("id", "r_id"), 
    $"r_id" === $"l_id", "full_outer" 
).withColumn("l_id", coalesce($"l_id", $"r_id")) 
.drop($"r_id") 
.withColumnRenamed("l_id", "id") 

teamPlayer.show 

+---+-------+----+------+------+ 
| id| name| age| team|record| 
+---+-------+----+------+------+ 
|101| null|null| lions| 7-1| 
|102| null|null|tigers| 5-3| 
|103| null|null| bears| 0-9| 
| 1| dave| 34| null| null| 
| 2|griffin| 44| null| null| 
+---+-------+----+------+------+ 

Man könnte es vielleicht ein wenig zu Reiniger mit structs:

val tpStructs = players.select($"id" as "l_id", struct($"name", $"age") as "player").join(
    teams.select($"id" as "r_id", struct($"team",$"record") as "team"), 
    $"l_id" === $"r_id", 
    "full_outer" 
).withColumn("l_id", coalesce($"l_id", $"r_id")) 
.drop($"r_id") 
.withColumnRenamed("l_id", "id") 

tpStructs.show 

+---+------------+------------+ 
| id|  player|  team| 
+---+------------+------------+ 
|101|  null| [lions,7-1]| 
|102|  null|[tigers,5-3]| 
|103|  null| [bears,0-9]| 
| 1| [dave,34]|  null| 
| 2|[griffin,44]|  null| 
+---+------------+------------+ 

I‘ Ich werde auch darauf hinweisen, dass mehr oder weniger die gleiche Lösung in GraphX mit RDDs funktionieren würde. Man konnte immer eine Ecke erstellen über die Verbindung von zwei case classes, die keine traits teilen:

case class Player(name: String, age: Int) 
val playerRdd = sc.parallelize(Seq(
    (1L, Player("date", 34)), 
    (2L, Player("griffin", 44)) 
)) 

case class Team(team: String, record: String) 
val teamRdd = sc.parallelize(Seq(
    (101L, Team("lions", "7-1")), 
    (102L, Team("tigers", "5-3")), 
    (103L, Team("bears", "0-9")) 
)) 

playerRdd.fullOuterJoin(teamRdd).collect foreach println 
(101,(None,Some(Team(lions,7-1)))) 
(1,(Some(Player(date,34)),None)) 
(102,(None,Some(Team(tigers,5-3)))) 
(2,(Some(Player(griffin,44)),None)) 
(103,(None,Some(Team(bears,0-9)))) 

Bei allem Respekt auf die vorherige Antwort, scheint dies eine flexiblere Art und Weise zu handhaben - ohne mit teilen trait zwischen den kombinierten Objekten.