在Spark中循环更新列值

时间:2019-06-18 11:16:33

标签: scala apache-spark

简要问题

对于更直接的查询,我想按顺序遍历所有行,并根据特定行的某些条件为某些变量(a,b,c)分配一些值,然后我将分配值将其中的1个变量放入该特定行的列中。

详细

我想在spark中更新数据框中的列值。更新将是有条件的,其中我将在行上运行循环并根据该行其他列的值更新一列。

我尝试使用withColumn方法,但出现错误。请提出任何其他方法。 withColumn方法的解析也将有很大帮助。

表格

var table1 = Seq((11, 25, 2, 0), (42, 20, 10, 0)).toDF("col_1", "col_2", "col_3", "col_4")
table1.show()

架构

+-----+-----+-----+-----+
|col_1|col_2|col_3|col_4|
+-----+-----+-----+-----+
|   11|   25|    2|    0|
|   42|   20|   10|    0|
+-----+-----+-----+-----+

我在这里尝试了2种方法:

  1. withColumn
  2. i(“ col_4”)= adj_c

在下面的代码中,根据条件,仅需要以这种方式放置在不同位置初始化的变量

代码

for(i <- table1.rdd.collect()) {
    if(i.getAs[Int]("col_1") > 0) {
       var adj_a = 0
       var adj_c = 0
        if(i.getAs[Int]("col_1") > (i.getAs[Int]("col_2") + i.getAs[Int]("col_3"))) {
            if(i.getAs[Int]("col_1") < i.getAs[Int]("col_2")) {
                adj_a = 10
                adj_c = 2
            }
            else {
                adj_a = 5
            }
        }
        else {
            adj_c = 1
        }
        adj_c = adj_c + i.getAs[Int]("col_2")
        table1.withColumn("col_4", adj_c)
         //i("col_4")  = adj_c
    }
}

第一种情况下的错误

table1.withColumn(“ col_4”,adj_c)

<console>:80: error: type mismatch;
 found   : Int
 required: org.apache.spark.sql.Column
               table1.withColumn("col_4", adj_c)
                                          ^

我也在这里尝试使用col(adj_c),但是它开始失败

<console>:80: error: type mismatch;
 found   : Int
 required: String
               table1.withColumn("col_4", col(adj_c))
                                              ^

第二种情况下的错误

(i(“ col_4”)= adj_c)

<console>:81: error: value update is not a member of org.apache.spark.sql.Row
                i("col_4")  = adj_c
                ^

我希望输出表为:

+-----+-----+-----+-----+
|col_1|col_2|col_3|col_4|
+-----+-----+-----+-----+
|   11|   25|    2|    1|
|   42|   20|   10|    5|
+-----+-----+-----+-----+

请提出可能的解决方案,如有疑问,请回复。

由于存在问题,请帮助我。任何建议都会很有帮助。

3 个答案:

答案 0 :(得分:4)

您应该使用when函数而不是这种复杂的语法,也不需要显式循环,Spark会自行处理。当您执行withColumn时,它会应用于每一行

table1.withColumn("col_4", when($"col_1" > $"col_2" + $"col_3", 5).otherwise(1)).show

快速测试:

输入

table1.show

-----+-----+-----+-----+
|col_1|col_2|col_3|col_4|
+-----+-----+-----+-----+
|   11|   25|    2|    0|
|   42|   20|   10|    0|
+-----+-----+-----+-----+

输出

table1.withColumn("col_4", when($"col_1" > $"col_2" + $"col_3", lit(5)).otherwise(1)).show
+-----+-----+-----+-----+
|col_1|col_2|col_3|col_4|
+-----+-----+-----+-----+
|   11|   25|    2|    1|
|   42|   20|   10|    5|
+-----+-----+-----+-----+

答案 1 :(得分:4)

UDF可以与任何自定义逻辑一起用于计算列值,例如:

val calculateCol4 = (col_1:Int, col_2:Int, col_3:Int)  =>
  if (col_1 > 0) {

    var adj_a = 0
    var adj_c = 0
    if (col_1 > col_2 + col_3) {
      if (col_1 < col_2) {
        adj_a = 10
        adj_c = 2
      }
      else {
        adj_a = 5
      }
    }
    else {
      adj_c = 1
    }
    println("adj_c: "+adj_c)
    adj_c = adj_c + col_2
    // added for return correct result
    adj_c
  }
  // added for return correct result
  else 0

val col4UDF = udf(calculateCol4)
table1.withColumn("col_4",col4UDF($"col_1", $"col_2", $"col_3"))

答案 2 :(得分:3)

使用spark.sql,更易于阅读和理解-

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var input = "some random string with abc and 123";
        var words = new List<String>() {"123", "abc"};

        var foundAll = words.Any(word => input.Contains(word));

        Console.WriteLine("Contains: {0}", foundAll);
    }
}

希望这会有所帮助。