[英]Spark SQL DataFrame - distinct() vs dropDuplicates()
[英]Homemade DataFrame aggregation/dropDuplicates Spark
我想在DataFrame df
上執行轉換,以便每個鍵只有一次,而在最終DataFrame中只有一次。
出於機器學習的目的,我不想在數據集中出現偏見。 這永遠都不會發生,但是我從數據源獲取的數據包含這種“怪異”。 因此,如果我的行具有相同的鍵,則希望能夠選擇兩者的組合(例如平均值)或字符串連接(例如標簽)或設置隨機值。
說我的DataFrame df
看起來像這樣:
+---+----+-----------+---------+
|ID1| ID2| VAL1| VAL2|
+---+----+-----------+---------+
| A| U| PIERRE| 1|
| A| U| THOMAS| 2|
| A| U| MICHAEL| 3|
| A| V| TOM| 2|
| A| V| JACK| 3|
| A| W| MICHEL| 2|
| A| W| JULIEN| 3|
+---+----+-----------+---------+
我希望我的最后數據幀out
到只保留一組每個鍵值,隨機。 這可能是另一種聚合(例如,將所有值串聯為字符串),但是我只是不想從中構建一個Integer值,而是構建新的條目。
例如。 最終輸出可能是(每個鍵僅保留第一行):
+---+----+-----------+---------+
|ID1| ID2| VAL1| VAL2|
+---+----+-----------+---------+
| A| U| PIERRE| 1|
| A| V| TOM| 2|
| A| W| MICHEL| 2|
+---+----+-----------+---------+
另一個最終輸出可能是(每個鍵保持隨機行):
+---+----+-----------+---------+
|ID1| ID2| VAL1| VAL2|
+---+----+-----------+---------+
| A| U| MICHAEL| 3|
| A| V| JACK| 3|
| A| W| MICHEL| 2|
+---+----+-----------+---------+
或者,建立一組新的值:
+---+----+--------------------------+----------+
|ID1| ID2| VAL1| VAL2|
+---+----+--------------------------+----------+
| A| U| (PIERRE, THOMAS, MICHAEL)| (1, 2, 3)|
| A| V| (TOM, JACK)| (2, 3)|
| A| W| (MICHEL, JULIEN)| (2, 3)|
+---+----+--------------------------+----------+
答案應將Spark與Scala結合使用。 我還想強調一下,實際的架構要比這復雜得多,我想找到一個通用的解決方案。 另外,我不想從一列取唯一的值,但篩選出具有相同的鍵線。 謝謝!
編輯這是我試圖做的(但是Row.get(colname)
拋出NoSuchElementException: key not found...
):
def myDropDuplicatesRandom(df: DataFrame, colnames: Seq[String]): DataFrame = {
val fields_map: Map[String, (Int, DataType)] =
df.schema.fieldNames.map(fname => {
val findex = df.schema.fieldIndex(fname)
val ftype = df.schema.fields(findex).dataType
(fname, (findex, ftype))
}).toMap[String, (Int, DataType)]
df.sparkSession.createDataFrame(
df.rdd
.map[(String, Row)](r => (colnames.map(colname => r.get(fields_map(colname)._1).toString.replace("`", "")).reduceLeft((x, y) => "" + x + y), r))
.groupByKey()
.map{case (x: String, y: Iterable[Row]) => Utils.randomElement(y)}
, df.schema)
}
這是一種方法:
val df = Seq(
("A", "U", "PIERRE", 1),
("A", "U", "THOMAS", 2),
("A", "U", "MICHAEL", 3),
("A", "V", "TOM", 2),
("A", "V", "JACK", 3),
("A", "W", "MICHEL", 2),
("A", "W", "JULIEN", 3)
).toDF("ID1", "ID2", "VAL1", "VAL2")
import org.apache.spark.sql.functions._
// Gather key/value column lists based on specific filtering criteria
val keyCols = df.columns.filter(_.startsWith("ID"))
val valCols = df.columns diff keyCols
// Group by keys to aggregate combined value-columns then re-expand
df.groupBy(keyCols.map(col): _*).
agg(first(struct(valCols.map(col): _*)).as("VALS")).
select($"ID1", $"ID2", $"VALS.*")
// +---+---+------+----+
// |ID1|ID2| VAL1|VAL2|
// +---+---+------+----+
// | A| W|MICHEL| 2|
// | A| V| TOM| 2|
// | A| U|PIERRE| 1|
// +---+---+------+----+
[UPDATE]
如果我正確地理解了您的擴展需求,那么您正在尋找一種通用方法來通過具有任意agg
函數的鍵來轉換數據幀,例如:
import org.apache.spark.sql.Column
def customAgg(keyCols: Seq[String], valCols: Seq[String], aggFcn: Column => Column) = {
df.groupBy(keyCols.map(col): _*).
agg(aggFcn(struct(valCols.map(col): _*)).as("VALS")).
select($"ID1", $"ID2", $"VALS.*")
}
customAgg(keyCols, valCols, first)
我想說,走這條道路將導致適用的agg
函數非常有限。 雖然上述方法first
,但是您必須為collect_list/collect_set
等實現不同的實現。當然可以手動滾動所有各種類型的agg
函數,但這可能會導致不必要的代碼維護麻煩。
您可以將groupBy
與first
和struct
,如下所示
import org.apache.spark.sql.functions._
val d1 = spark.sparkContext.parallelize(Seq(
("A", "U", "PIERRE", 1),
("A", "U", "THOMAS", 2),
("A", "U", "MICHAEL", 3),
("A", "V", "TOM", 2),
("A", "V", "JACK", 3),
("A", "W", "MICHEL", 2),
("A", "W", "JULIEN", 3)
)).toDF("ID1", "ID2", "VAL1", "VAL2")
d1.groupBy("ID1", "ID2").agg(first(struct("VAL1", "VAL2")).as("val"))
.select("ID1", "ID2", "val.*")
.show(false)
更新:如果您將鍵和值作為參數,則可以如下使用。
val keys = Seq("ID1", "ID2")
val values = Seq("VAL1", "VAL2")
d1.groupBy(keys.head, keys.tail : _*)
.agg(first(struct(values.head, values.tail:_*)).as("val"))
.select( "val.*",keys:_*)
.show(false)
輸出:
+---+---+------+----+
|ID1|ID2|VAL1 |VAL2|
+---+---+------+----+
|A |W |MICHEL|2 |
|A |V |TOM |2 |
|A |U |PIERRE|1 |
+---+---+------+----+
我希望這有幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.