使用Spark数据帧比较2个文本文件时如何检查NULL值

时间:2018-10-10 10:12:36

标签: apache-spark pyspark apache-spark-sql

以下代码未能捕获“空”值记录。在df1下面,列NO。 5具有空值(名称字段)。

根据我的以下要求OutputDF,第5条记录应如上所述。但是在下面的代码执行之后,该记录不会进入最终输出。具有“ null”值的记录不会进入输出。除此之外,一切都很好。

df1

NO  DEPT NAME   SAL 
1   IT  RAM     1000    
2   IT  SRI     600 
3   HR  GOPI    1500    
5   HW          700

df2

NO  DEPT NAME   SAL 
1   IT   RAM    1000    
2   IT   SRI    900 
4   MT   SUMP   1200    
5   HW   MAHI   700

OutputDF

NO  DEPT NAME    SAL   FLAG
1   IT  RAM     1000   SAME
2   IT  SRI     900    UPDATE
4   MT  SUMP    1200   INSERT
3   HR  GOPI    1500   DELETE
5   HW  MAHI    700    UPDATE

from pyspark.shell import spark
from pyspark.sql import DataFrame
import pyspark.sql.functions as F
sc = spark.sparkContext

filedf1 = spark.read.option("header","true").option("delimiter", ",").csv("C:\\files\\file1.csv")
filedf2 = spark.read.option("header","true").option("delimiter", ",").csv("C:\\files\\file2.csv")
filedf1.createOrReplaceTempView("table1")
filedf2.createOrReplaceTempView("table2")
df1 = spark.sql( "select * from table1" )
df2 = spark.sql( "select * from table2" )

#DELETE
df_d = df1.join(df2, df1.NO == df2.NO, 'left').filter(F.isnull(df2.NO)).select(df1.NO,df1.DEPT,df1.NAME,df1.SAL, F.lit('DELETE').alias('FLAG'))
print("df_d left:",df_d.show())
#INSERT
df_i = df1.join(df2, df1.NO == df2.NO, 'right').filter(F.isnull(df1.NO)).select(df2.NO,df2.DEPT,df2.NAME,df2.SAL, F.lit('INSERT').alias('FLAG'))
print("df_i right:",df_i.show())
#SAME
df_s = df1.join(df2, df1.NO == df2.NO, 'inner').filter(F.concat(df2.NO,df2.DEPT,df2.NAME,df2.SAL) == F.concat(df1.NO,df1.DEPT,df1.NAME,df1.SAL)).select(df1.NO,df1.DEPT,df1.NAME,df1.SAL, F.lit('SAME').alias('FLAG'))
print("df_s inner:",df_s.show())
#UPDATE
df_u = df1.join(df2, df1.NO == df2.NO, 'inner').filter(F.concat(df2.NO,df2.DEPT,df2.NAME,df2.SAL) != F.concat(df1.NO,df1.DEPT,df1.NAME,df1.SAL)).select(df2.NO,df2.DEPT,df2.NAME,df2.SAL, F.lit('UPDATE').alias('FLAG'))
print("df_u inner:",df_u.show())

df = df_d.union(df_i).union(df_s).union(df_u)
df.show()

这里我正在比较df1和df2,如果在df2中发现新记录并标记为INSERT,如果两个dfs中的记录相同,则取为SAME,如果该记录在df1中而不在df2中则作为DELETE和如果记录同时存在于两个df中,但具有不同的值,则将df2值作为UPDATE。

1 个答案:

答案 0 :(得分:1)

代码有两个问题:

  1. F.concat的结果为null会返回null,因此代码中的这一部分会过滤出第5行:

    .filter(F.concat(df2.NO, df2.NAME, df2.SAL) != F.concat(df1.NO, df1.NAME, df1.SAL))
    
  2. 您仅选择df2。在上面的示例中很好,但是如果df2为空,则结果数据帧将为空。

您可以尝试将其与下面的udf串联:

def concat_cols(row):
    concat_row = ''.join([str(col) for col in row if col is not None])
    return concat_row 

udf_concat_cols = udf(concat_cols, StringType())

函数concat_row可以分为两部分:

  1. “”。join([mylist])是string function。它结合了一切 具有定义的分隔符的列表,在这种情况下,它是一个空字符串。
  2. [如果col不为None,则为行中的col的str(col)]是列表推导,它的读取方式为:对于行中的每一列,如果 该列不是None,然后将str(col)附加到列表中。
    List comprehension只是更Python化的方式:

    mylist = [] 
    for col in row: 
        if col is not None:
            mylist.append(col))
    

您可以将更新代码替换为:

df_u = (df1
.join(df2, df1.NO == df2.NO, 'inner')
.filter(udf_concat_cols(struct(df1.NO, df1.NAME, df1.SAL)) != udf_concat_cols(struct(df2.NO, df2.NAME, df2.SAL)))
.select(coalesce(df1.NO, df2.NO), 
        coalesce(df1.NAME, df2.NAME),
        coalesce(df1.SAL, df2.SAL),
        F.lit('UPDATE').alias('FLAG')))

您应该对#SAME标志执行类似的操作,并为了可读性而中断行。


更新

如果df2始终具有正确的(更新的)结果,则无需合并。 该实例的代码为:

df_u = (df1
.join(df2, df1.NO == df2.NO, 'inner')
.filter(udf_concat_cols(struct(df1.NO, df1.NAME, df1.SAL)) != udf_concat_cols(struct(df2.NO, df2.NAME, df2.SAL)))
.select(df2.NO,
        df2.NAME,
        df2.SAL,
        F.lit('UPDATE').alias('FLAG')))