Объединение двух фреймов данных в Scala со столбцом, который не имеет точных значений - PullRequest
1 голос
/ 15 октября 2019

Я попытался объединить два кадра данных относительно столбца, которые не имеют точно одинаковые значения.

Ниже дано DF1

+--------+-----+------+
| NUM_ID | TIME|SG1_V |
+--------+-----+------+
|XXXXX01 |1001 |79.0  |
|XXXXX01 |1005 |88.0  |
|XXXXX01 |1010 |99.0  |
|XXXXX01 |1015 |null  |
|XXXXX01 |1020 |100.0 |
|XXXXX02 |1001 |81.0  |
|XXXXX02 |1010 |91.0  |
|XXXXX02 |1050 |93.0  |
|XXXXX02 |1060 |93.0  |
|XXXXX02 |1070 |93.0  |
+--------+-----+------+

Ниже приведено DF2

+---------+-----+------+
| NUM_ID  | TIME|SG2_V |
+---------+-----+------+
|XXXXX01  |1001 |  99.0|
|XXXXX01  |1003 |  22.0|
|XXXXX01  |1007 |  85.0|
|XXXXX01  |1011 |  1.0 |

|XXXXX02  |1001 |  22.0|
|XXXXX02  |1009 |  85.0|
|XXXXX02  |1048 |  1.0 |
|XXXXX02  |1052 |  99.0|
+---------+-----+------+

Мне нужно объединить эти два DF в столбцах NUM_ID, которые должны быть точно такими же, и в столбце TIME, который может / не может быть точным значением.

TIME в DF2 может /не может содержать точное значение, как в DF1. Если значение не является точным, тогда я должен присоединиться к наивысшему доступному значению (то есть - значение столбца в DF2 должно быть = <Точное значение в DF1). </p>

Это будет более ясно после просмотра ожидаемого результата, показанного ниже.

+--------+-----+------+-----+------+
| NUM_ID | TIME|SG1_V | TIME|SG2_V |
+--------+-----+------+-----+------+
|XXXXX01 |1001 |79.0  |1001 |  99.0|
|XXXXX01 |1005 |88.0  |1003 |  22.0|
|XXXXX01 |1010 |99.0  |1007 |  85.0|
|XXXXX01 |1015 |null  |1011 |  1.0 |
|XXXXX01 |1020 |100.0 |1011 |  1.0 |

|XXXXX02 |1001 |81.0  |1001 |  22.0|
|XXXXX02 |1010 |91.0  |1009 |  85.0|
|XXXXX02 |1050 |93.0  |1048 |  1.0 |
|XXXXX02 |1060 |93.0  |1052 |  99.0|
|XXXXX02 |1070 |93.0  |1052 |  99.0|
+--------+-----+------+-----+------+

Для NUM_ID XXXXX01, TIME (1005) в DF1 недоступен в DF2, поэтому он принял ближайшее значение(1003), который меньше, чем 1005.

Как присоединиться таким образом, чтобы, если точное значение не было доступно, затем объединить с ближайшим значением.

Оценить любые отведения. Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 15 октября 2019

Если вам нужно объединить по двум полям, используя определенный интервал для одного из них, вы можете сделать что-то вроде:

  import org.apache.spark.sql.functions.when

  val spark = SparkSession.builder().master("local[1]").getOrCreate()

  val df1 : DataFrame = spark.createDataFrame(spark.sparkContext.parallelize(Seq(Row("XXXXX01",1001,79.0),
    Row("XXXXX01",1005,88.0),
    Row("XXXXX01",1010,99.0),
    Row("XXXXX01",1015, null),
    Row("XXXXX01",1020,100.0),
    Row("XXXXX02",1001,81.0))),
    StructType(Seq(StructField("NUM_ID", StringType, false), StructField("TIME", IntegerType, false), StructField("SG1_V", DoubleType, true))))

  val df2 : DataFrame = spark.createDataFrame(spark.sparkContext.parallelize(Seq(Row("XXXXX01",1001,79.0),
    Row("XXXXX01",1001, 99.0),
    Row("XXXXX01",1003, 22.0),
    Row("XXXXX01",1007, 85.1),
    Row("XXXXX01",1011, 1.0),
    Row("XXXXX02",1001,22.0))),
    StructType(Seq(StructField("NUM_ID", StringType, false), StructField("TIME", IntegerType, false), StructField("SG1_V", DoubleType, false))))

  val interval : Int = 10

  def main(args: Array[String]) : Unit = {
    df1.join(df2, ((df1("TIME")) - df2("TIME") > lit(interval)) && df1("NUM_ID") === df2("NUM_ID")).show()
  } 

В результате вы получите:

+-------+----+-----+-------+----+-----+
| NUM_ID|TIME|SG1_V| NUM_ID|TIME|SG1_V|
+-------+----+-----+-------+----+-----+
|XXXXX01|1015| null|XXXXX01|1001| 79.0|
|XXXXX01|1015| null|XXXXX01|1001| 99.0|
|XXXXX01|1015| null|XXXXX01|1003| 22.0|
|XXXXX01|1020|100.0|XXXXX01|1001| 79.0|
|XXXXX01|1020|100.0|XXXXX01|1001| 99.0|
|XXXXX01|1020|100.0|XXXXX01|1003| 22.0|
|XXXXX01|1020|100.0|XXXXX01|1007| 85.1|
+-------+----+-----+-------+----+-----+
1 голос
/ 15 октября 2019

Простой способ сделать это - использовать одну из оконных функций Spark , row_number () или rank ():

scala> spark.sql("""
     |   SELECT * FROM (
     |     SELECT *,
     |       ROW_NUMBER() OVER (PARTITION BY df1.NUM_ID, df1.TIME ORDER BY (df1.TIME - df2.TIME)) rno
     |     FROM df1 JOIN df2 
     |     ON df2.NUM_ID = df1.NUM_ID AND 
     |        df2.TIME  <= df1.TIME
     |   ) T
     | WHERE T.rno = 1
     |""").show()
+-------+----+-----+-------+----+-----+---+
| NUM_ID|TIME|SG1_V| NUM_ID|TIME|SG2_V|rno|
+-------+----+-----+-------+----+-----+---+
|XXXXX01|1001| 79.0|XXXXX01|1001| 99.0|  1|
|XXXXX01|1005| 88.0|XXXXX01|1003| 22.0|  1|
|XXXXX01|1010| 99.0|XXXXX01|1007| 85.0|  1|
|XXXXX01|1015| null|XXXXX01|1011|  1.0|  1|
|XXXXX01|1020|100.0|XXXXX01|1011|  1.0|  1|
|XXXXX02|1001| 81.0|XXXXX02|1001| 22.0|  1|
|XXXXX02|1010| 91.0|XXXXX02|1009| 85.0|  1|
+-------+----+-----+-------+----+-----+---+

scala>
...