Apache spark 使用查找表中的withColumn动态添加新列

Apache spark 使用查找表中的withColumn动态添加新列,apache-spark,apache-spark-sql,Apache Spark,Apache Spark Sql,我正在使用spark-sql-2.4.1v和Java8。我有一个场景,需要从查找表中动态添加一列 我有带列的数据框 A、 B,C,…,X,Y,Z 当少数(原始)列(例如:A、B、C)值为空时,我需要获取/替换列(例如:X、Y、Z)值,否则获取原始列值。 我将获取此映射信息作为业务逻辑的一部分。 如果是这样的话,我将遵循下面的硬编码代码 Dataset<Row> substitutedDs = ds .withColumn("A&quo

我正在使用spark-sql-2.4.1v和Java8。我有一个场景,需要从查找表中动态添加一列

我有带列的数据框 A、 B,C,…,X,Y,Z

当少数(原始)列(例如:A、B、C)值为空时,我需要获取/替换列(例如:X、Y、Z)值,否则获取原始列值。 我将获取此映射信息作为业务逻辑的一部分。 如果是这样的话,我将遵循下面的硬编码代码

 Dataset<Row>  substitutedDs = ds
                  .withColumn("A",
                             when(col("A").isNull() , col("X").cast(DataTypes.StringType))
                             .otherwise(col("A").cast(DataTypes.StringType))
                          )
                  .withColumn("C",
                             when(col("C").isNull() , col("Z").cast(DataTypes.StringType))
                             .otherwise(col("C").cast(DataTypes.StringType))
                         

我需要动态地构造上面的“替换的”,这是如何做到的?

在Scala中,我会这样做

val替换映射:映射[字符串,字符串]=//这是您的替换映射,它很小,因为它包含列及其空替换
val df=//这是您的主数据帧
val substitutedDf=substituteMapping.keys().foldLeft(df)((df,k)=>{
df.withColumn(k,when(col(k).isNull,col(substituteMapping(k)))。否则(col(k)))
//做适当的铸造在上面你已经做的职位
})

我认为Java 8中没有
foldLeft
,您可以通过反复修改变量并在Scala中的
substituteMapping
上进行迭代来模拟相同的情况,我希望这样做

val替换映射:映射[字符串,字符串]=//这是您的替换映射,它很小,因为它包含列及其空替换
val df=//这是您的主数据帧
val substitutedDf=substituteMapping.keys().foldLeft(df)((df,k)=>{
df.withColumn(k,when(col(k).isNull,col(substituteMapping(k)))。否则(col(k)))
//做适当的铸造在上面你已经做的职位
})

我认为Java 8中不存在
foldLeft
,您可以通过反复修改变量并在
substituteMapping
上进行迭代来模拟相同的情况。使用Java 8,您可以使用以下重载:

final Dataset<Row> dataframe = ...;
final Map<String, String> substitutes = ...;

final Dataset<Row> afterSubstitutions = codeSubstitutes.entrySet().stream()
    .reduce(dataframe, (df, entry) ->
            df.withColumn(entry.getKey(), when(/* replace with col(entry.getValue()) when null */)),
            (left, right) -> { throw new IllegalStateException("Can't merge two dataframes. This stream should not be a parallel one!"); }
    );

使用Java8,您可以使用以下重载:

final Dataset<Row> dataframe = ...;
final Map<String, String> substitutes = ...;

final Dataset<Row> afterSubstitutions = codeSubstitutes.entrySet().stream()
    .reduce(dataframe, (df, entry) ->
            df.withColumn(entry.getKey(), when(/* replace with col(entry.getValue()) when null */)),
            (left, right) -> { throw new IllegalStateException("Can't merge two dataframes. This stream should not be a parallel one!"); }
    );


作为补充说明,您还可以使用一些伪/随机组合器,如
(左、右)->left
,并假设组合器永远不会被调用。但是,如果有人将
.stream()
更改为
.parallelStream()
,我想说你希望这个失败(理想情况下,在测试中)。如果你不止一次地使用这个方法,你可能想将
throw
提取到一些
throwingCombiner()…这是什么df?我们从哪里得到它?看看Javadoc中的
reduce
方法:第二个参数是一个双函数,您可以控制获取迄今为止积累的任何数据帧(从原始数据帧开始,作为
标识
参数传入)和要“附加”到它的新元素(本例中为映射条目)并返回“包含”的新数据帧这个元素…所以它是reduce方法本身,它将为每个调用提供
df
,从
dataframe
开始,然后对每个条目应用
replaceIfNull
的结果。有意义吗?使用javaapi的reduce函数不起作用…我尝试使用的是List…t他的reduce抛出编译错误…类型流中的方法reduce(Tuple2,BinaryOperator)不适用于参数(Tuple2,(df,entry)->{})。作为旁注,您还可以使用一些伪/随机组合器,如
(左,右)->left
并假设组合器永远不会被调用。但是,如果有人将
.stream()
更改为
.parallelStream()
,我想说的是,如果您不止一次地使用此方法,您可能希望将
抛出的
提取到某个
抛出组合器()中
utility方法…谢谢,但是在这个reduce(数据帧,(df,entry)…这个df是什么?我们从哪里得到它?看看Javadoc中的
reduce
方法:第二个参数是一个双函数,您可以控制获取迄今为止积累的任何数据帧(从原始元素开始,作为
identity
param传入)和要“附加”到它的新元素(在本例中是一个映射条目),并返回“合并”的新数据帧这个元素…所以它是reduce方法本身,它将为每个调用提供
df
,从
dataframe
开始,然后对每个条目应用
replaceIfNull
的结果。有意义吗?使用javaapi的reduce函数不起作用…我尝试使用的是List…t他的reduce抛出编译错误…类型流中的方法reduce(Tuple2,BinaryOperator)不适用于参数(Tuple2,(df,entry)->{})。不幸的是,fold函数在Java中不可用。当我尝试使用Stream()时,它不起作用@Dee Thank Dee但如果第一列为null,我需要用另一列的值替换一列的值…”“重复修改变量”lambda函数外部无法访问lambda内部修改的任何内容。@b如果您要修改
var df
,您可以从lambdaunfortional fold函数中访问它。该函数在Java中不可用。当我尝试使用stream()时,它不起作用@Dee谢谢Dee,但如果第一列为空,我需要将一列的值替换为另一列的值…”“重复修改变量”lambda函数外部无法访问lambda内部修改的任何内容。@b如果您要修改
var df
,可以从lambda访问它
    // ...
    Dataset<Row> nullSafeDf = codeSubstitutes.entrySet().stream()
        .reduce(dataframe, this::replaceIfNull, this::throwingCombiner);
    // ...
}


private Dataset<Row> replaceIfNull(Dataset<Row> df, Map.Entry<String, String> substitution) {
    final String original = substitution.getKey();
    final String replacement = substitution.getValue();
    return df.withColumn(original, when(col(original).isNull(), col(replacement))
            .otherwise(col(original)));
}

private <X> X throwingCombiner(X left, X right) {
    throw new IllegalStateException("Combining not allowed");
}