参考文章:Spark SQL中的Encoder

DataFrame操作

操作方式举例:

/*
* 数据集: 
*   张三,23
*   李四,24
*   王五,25
*   赵六,26
*/
val spark = SparkSession
      .builder()
      .appName(this.getClass.getSimpleName)
      .master(master = "local[*]")
      .getOrCreate()

    import spark.implicits._
    val df = spark.read.textFile("./data/clm")
      .map(_.split(","))
      .map(x => (x(0), x(1)))
      .toDF("name", "age")
      .cache()
// ========================================================= //

/**
*  withColumn:
*    通过添加列或替换具有相同名称的现有列来返回新的数据集
*     column的表达式只能引用此数据集提供的属性。 添加引用其他数据集的列是错误的
*  
*  DataFrame其它表示方式: df("age")
*/
df.withColumn("new_age", col = df("age") + 1).show()

// 结果:
+----+---+-------+
|name|age|new_age|
+----+---+-------+
|张三| 23|   24.0|
|李四| 24|   25.0|
|王五| 25|   26.0|
|赵六| 26|   27.0|
+----+---+-------+

spark dataframe新增一列的四种方法:

/*
*  spark dataframe新增一列的四种方法:
*    方法一:利用createDataFrame方法,新增列的过程包含在构建rdd和schema中
*    方法二:利用withColumn方法,新增列的过程包含在udf函数中
*    方法三:利用SQL代码,新增列的过程直接写入SQL代码中
*    方法四:以上三种是增加一个有判断的列,如果想要增加一列唯一序号,可以使用monotonically_increasing_id
**/
//dataframe新增一列方法1,利用createDataFrame方法
val trdd = input.select(targetColumns).rdd.map(x=>{
  if (x.get(0).toString().toDouble > critValueR || x.get(0).toString().toDouble < critValueL) 
    Row(x.get(0).toString().toDouble,"F")
  else Row(x.get(0).toString().toDouble,"T")      
  })      
val schema = input.select(targetColumns).schema.add("flag", StringType, true)
val sample3 = ss.createDataFrame(trdd, schema).distinct().withColumnRenamed(targetColumns, "idx")

//dataframe新增一列方法2
val code :(Int => String) = (arg: Int) => {if (arg > critValueR || arg < critValueL) "F" else "T"}
val addCol = udf(code)
val sample3 = input.select(targetColumns).withColumn("flag", addCol(input(targetColumns)))
.withColumnRenamed(targetColumns, "idx")

//dataframe新增一列方法3
input.select(targetColumns).createOrReplaceTempView("tmp")
// sql示例: select user_id, 'ruleTagId' as rule_tag_id from tmp_view
val sample3 = ss.sqlContext.sql("select distinct "+targetColname+
    " as idx,case when "+targetColname+">"+critValueR+" then 'F'"+
    " when "+targetColname+"<"+critValueL+" then 'F' else 'T' end as flag from tmp")

//添加序号列
import org.apache.spark.sql.functions.monotonically_increasing_id
val inputnew = input.withColumn("idx", monotonically_increasing_id)

Spark 中 关于scala 语法理解的问题

// 语法定义:
def flatMap[U : Encoder](func: T => TraversableOnce[U]): Dataset[U] =
        mapPartitions(_.flatMap(func))
// 调用示例:
val lines: Dataset[String] = session.read.textFile("")
val words: Dataset[String] = lines.flatMap(_.split(" "))

// 说明:
// _.split(" ") 相当于一个函数, 传入参数是String类型, 
// 返回类型是split的结果类型 Array[String],

// 而flatmap方法定义为接收 func: T => TraversableOnce[U], 
// 显然这里的T是String类型,

// 而 Array[String] 并不是 TraversableOnce[U] 类型, 
// 因为 Array 没有实现 TraversableOnce 这个特质,

// 传入 _.split(" ") 函数给 flatMap, flatMap为什么没有报错?
// 原因: scala通过隐式转换默认调用了iterator 方法
// 转换为一个可迭代的对象(Array(1,2,3).iterator)

============================================================== 

以下错误,想必在做Spark的DateSet操作时一定是见过吧?

Error:(58, 17) Unable to find encoder for type stored in a Dataset.  
Primitive types (Int, String, etc) and Product types (case classes) 
are supported by importing spark.implicits._  Support for serializing 
other types will be added in future releases.
    peopleDF.map(row => row.get(0)).show()

这是因为在作map转换时需要指定一个转换的Encorder, 
在Scala代码中是通过隐式转换进行的, 而在Java代码中则需要在代码中指明。

Java代码如下:

public static void main(String[] args) {
    SparkSession spark = SparkSession.builder()
                        .master("local[2]")
                        .appName("DataSetEncoderExample")
                        .getOrCreate();

    List<String> data = Arrays.asList("chen", "li", "huang");
    
    //创建DataSet的时候指明数据是String类型
    Dataset<String> ds = spark.createDataset(data, Encoders.STRING());

    /***操作一:**/
    // map操作:把string类型的变换成string类型
    // 此时MapFunction<String, String> 这两个地方都应该是String
    Dataset<String> dsString2String = 
	    ds.map((MapFunction<String, String>) 
		    v -> "Hi," + v, Encoders.STRING());
    dsString2String.show();

    /***操作二:**/
    // map操作:把string类型的变换成int类型
    // 注意此时MapFunction<String, Integer> 这两个地方的类型变化
    // 第一个类型String为原来的DataSet的类型, 第二个类型为输出的类型
    Dataset<Integer> dsString2Int = 
	    ds.map(new MapFunction<String, Integer>(){
            @Override
            public Integer call(String value) throws Exception {
                return value.length();
            }
        }, Encoders.INT());
    dsString2Int.show();

    /***操作三:**/
    // map操作:把string类型的变换成自定义的对象类型
    // 注意此时MapFunction<String, People> 这两个地方的类型变化
    // 第一个类型String为原来的DataSet的类型, 第二个类型People为输出的类型
    Encoder<People> peopleEncoder = Encoders.kryo(People.class);
    Dataset<People> dsString2Object = ds.map(
	    new MapFunction<String, People>(){
            @Override
            public People call(String value) throws Exception {
                return new People(value, value.length());
            }
        }, peopleEncoder);
    dsString2Object.show();
    dsString2Object
            .map((MapFunction<People, String>) 
			     item -> item.getName(), Encoders.STRING())
            .show();

    /***操作四:**/
    // map操作:把string类型的变换成Row对象类型
    // 注意此时MapFunction<String, Row> 这两个地方的类型变化
    // 第一个类型String为原来的DataSet的类型, 第二个类型Row为输出的类型
    // Encoder<Row> rowEncoder = Encoders.kryo(Row.class);
    Encoder<Row> rowEncoder = Encoders.javaSerialization(Row.class);
    Dataset<Row> dsString2Row = ds
                .map((MapFunction<String, Row>) value 
                    -> RowFactory.create(value, value.length()), rowEncoder);
    dsString2Row.show();
    dsString2Row
            .map((MapFunction<Row, String>) 
			    item -> item.getString(0), Encoders.STRING())
            .show();

    spark.stop();
}

People类:

public class People {
    String name;
    Integer age;

    public People( String name, Integer age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}