使用新值

时间:2018-03-23 04:53:16

标签: apache-spark pyspark

df1包含字段idjson; df2包含字段idjson

df1.count() => 1200; df2.count() => 20

df1包含所有行。 df2只有20行的增量更新。

我的目标是使用df2中的值更新df1。 df2的所有ID都在df1中。但df2已为这些相同的ID更新了值(在json字段中)。

生成的df应包含df1的所有值和df2的更新值。

最好的方法是什么? - 使用最少数量的连接和过滤器。

谢谢!

3 个答案:

答案 0 :(得分:0)

如果您需要来自两个数据帧的数据,您可以union两个数据帧

import spark.implicits._

First Dataframe

val df1 = Seq(
  (1, "a"),
  (2, "b"),
  (3, "c")
).toDF("id", "value")    

第二个数据框

val df2 = Seq(
  (1, "x"), 
  (2, "y")
).toDF("id", "value")

要将结果作为df1df2的数据,请使用union

val resultDF = df1.union(df2)

resultDF.show()

输出:

+---+-----+
|id |value|
+---+-----+
|1  |a    |
|2  |b    |
|3  |c    |
|1  |x    |
|2  |y    |
+---+-----+

答案 1 :(得分:0)

您可以使用一个左连接来实现此目的。

创建示例数据框

使用@Shankar Koirala在his answer中提供的样本数据。

data1 = [
  (1, "a"),
  (2, "b"),
  (3, "c")
]
df1 = sqlCtx.createDataFrame(data1, ["id", "value"])

data2 = [
  (1, "x"), 
  (2, "y")
]

df2 = sqlCtx.createDataFrame(data2, ["id", "value"])

执行左连接

使用id列上的左连接加入两个DataFrame。这将保留左侧DataFrame中的所有行。对于右侧DataFrame中没有匹配id的行,该值将为null

import pyspark.sql.functions as f
df1.alias('l').join(df2.alias('r'), on='id', how='left')\
    .select(
        'id',
         f.col('l.value').alias('left_value'),
         f.col('r.value').alias('right_value')
    )\
    .show()
#+---+----------+-----------+
#| id|left_value|right_value|
#+---+----------+-----------+
#|  1|         a|          x|
#|  3|         c|       null|
#|  2|         b|          y|
#+---+----------+-----------+

选择所需数据

我们将使用不匹配的id具有null来选择最终列的事实。如果它不为null,请使用pyspark.sql.functions.when()使用正确的值,否则保留左值。

df1.alias('l').join(df2.alias('r'), on='id', how='left')\
    .select(
        'id',
        f.when(
            ~f.isnull(f.col('r.value')),
            f.col('r.value')
        ).otherwise(f.col('l.value')).alias('value')
    )\
    .show()
#+---+-----+
#| id|value|
#+---+-----+
#|  1|    x|
#|  3|    c|
#|  2|    y|
#+---+-----+

如果您想按顺序id,可以对此输出进行排序。

使用pyspark-sql

您可以使用pyspark-sql查询执行相同的操作:

df1.registerTempTable('df1')
df2.registerTempTable('df2')

query = """SELECT l.id, 
CASE WHEN r.value IS NOT NULL THEN r.value ELSE l.value END AS value 
FROM df1 l LEFT JOIN df2 r ON l.id = r.id"""
sqlCtx.sql(query.replace("\n", "")).show()
#+---+-----+
#| id|value|
#+---+-----+
#|  1|    x|
#|  3|    c|
#|  2|    y|
#+---+-----+

答案 2 :(得分:0)

我想提供一个更通用的解决方案。如果输入数据有 100 列而不是 2 列会发生什么?我们将花费太多时间来合并这 100 列以保持左连接右侧的值。 解决此问题的另一种方法是从原始 df 中“删除”更新的行,最后与更新的行进行联合。

data_orginal = spark.createDataFrame([
    (1, "a"),
    (2, "b"),
    (3, "c")
], ("id", "value"))

data_updated = spark.createDataFrame([
    (1, "x"),
    (2, "y")
], ("id", "value"))

data_orginal.show()
+---+-----+
| id|value|
+---+-----+
|  1|    a|
|  2|    b|
|  3|    c|
+---+-----+

data_updated.show()
+---+-----+
| id|value|
+---+-----+
|  1|    x|
|  2|    y|
+---+-----+

data_orginal.createOrReplaceTempView("data_orginal")
data_updated.createOrReplaceTempView("data_updated")

src_data_except_updated = spark.sql(f"SELECT * FROM data_orginal WHERE id not in (1,2)")
result_data = src_data_except_updated.union(data_updated)

result_data.show()
+---+-----+
| id|value|
+---+-----+
|  3|    c|
|  1|    x|
|  2|    y|
+---+-----+

注意查询

<块引用>

SELECT * FROM data_orginal WHERE id 不在 (1,2) 中

可以自动生成:

ids_collect = spark.sql(f"SELECT id FROM data_updated").collect()
ids_list = [f"{x.id}" for x in ids_collect]
ids_str = ",".join(ids_list)
query_get_all_except = f"SELECT * FROM data_original WHERE id not in ({ids_str})"