Spark中,有两种方法可以将数据存为txt文件,一种是基于RDD存储,另一种是使用Spark SQL的Dataset数据结构。
两种存储方法对比:
比较项 | 基于RDD | 基于Dataset |
---|---|---|
是否可追加 | 否 | 是 |
存储列数 | 不限制 | 只能一列 |
总的来说,就是基于RDD存储txt文件的话,无法对目录文件进行追加及覆盖写操作,但是可以直接存储多列,而基于Dataset存储的时候,是可以按照SaveMode进行各种选择方式的,但是只能存储一列。
解决办法就是想办法将Dataset中的多列数据合并为一列数据。
基于RDD的:
/**
* 转为RDD存储为TXT文件时,无法进行覆盖和追加
* 数据为Dataset<Row>
*/
dataset.coalesce(1).toJavaRDD().map((Function<Row, String>) row -> row.toString())
.map((Function<String, String>) line -> line.replace(",", "|"))
.saveAsTextFile("/data/example.txt");
基于Dataset操作的时候:
第一步:使用SparkSQL的UDF函数,将Vector向量转为double类型的Array数组
第二步:将所有的列合并为一列,并制定分割符,然后按需求存储数据
Step1:创建示例Dataset
/**
* 准备数据
*/
List<Row> data = Arrays.asList(
RowFactory.create("vector1", Vectors.dense(0.2564752136, -2.154645158, 0.2648491548)),
RowFactory.create("vector2", Vectors.dense(1.2564213055, -6.158132184, -2.448131585)),
RowFactory.create("vector3", Vectors.dense(-5.184321865, 5.1481355498, 8.5481513218))
);
StructType schema = createStructType(new StructField[]{
createStructField("vectorId", DataTypes.StringType, false),
createStructField("vector", new VectorUDT(), false)
});
Dataset<Row> dataset = sparkSession.createDataFrame(data, schema);
打印数据格式可以看到
dataset.printSchema();
root
|-- vectorId: string (nullable = false)
|-- vector: vector (nullable = false)
dataset.show(false);
+--------+----------------------------------------+
|vectorId|vector |
+--------+----------------------------------------+
|vector1 |[0.2564752136,-2.154645158,0.2648491548]|
|vector2 |[1.2564213055,-6.158132184,-2.448131585]|
|vector3 |[-5.184321865,5.1481355498,8.5481513218]|
+--------+----------------------------------------+
Step2:使用用户自定义函数UDF,将Vector向量转为Array
UserDefinedFunction vector2Array = udf((UDF1<Vector, double[]>) vector -> vector.toArray(), DataTypes.createArrayType(DataTypes.DoubleType));
Dataset<Row> arrayData = dataset.withColumn("vec2array", vector2Array.apply(dataset.col("vector")))
.select("vectorId", new String[]{"vec2array"});
打印数据格式可以看到
arrayData.printSchema();
root
|-- vectorId: string (nullable = false)
|-- vec2array: array (nullable = true)
| |-- element: double (containsNull = true)
arrayData.show(false);
+--------+------------------------------------------+
|vectorId|vec2array |
+--------+------------------------------------------+
|vector1 |[0.2564752136, -2.154645158, 0.2648491548]|
|vector2 |[1.2564213055, -6.158132184, -2.448131585]|
|vector3 |[-5.184321865, 5.1481355498, 8.5481513218]|
+--------+------------------------------------------+
看着没啥区别,但是数据类型已经变成array了。
Step3:使用concat_ws函数,合并所有的列
/**
* 使用concat_ws函数,组合新的列,并指定连接符为“|”
*/
Dataset<Row> result = arrayData.selectExpr("concat_ws('|', vectorId, vec2array) as concatCol");
打印数据格式可以看到
result.printSchema();
root
|-- concatCol: string (nullable = false)
result.show(false);
+----------------------------------------------+
|concatCol |
+----------------------------------------------+
|vector1|0.2564752136|-2.154645158|0.2648491548|
|vector2|1.2564213055|-6.158132184|-2.448131585|
|vector3|-5.184321865|5.1481355498|8.5481513218|
+----------------------------------------------+
Step4、保存数据
/**
* 使用Dataset的Savemode任意保存
*/
result.write().mode(SaveMode.Overwrite).text("/Your/HDFS/Path/");
问题解决!
完整代码:
public static void main(String[] args) {
SparkSession sparkSession = SparkSession
.builder()
.master("local[3]")
.appName("VectorExample")
.getOrCreate();
/**
* 准备数据
*/
List<Row> data = Arrays.asList(
RowFactory.create("vector1", Vectors.dense(0.2564752136, -2.154645158, 0.2648491548)),
RowFactory.create("vector2", Vectors.dense(1.2564213055, -6.158132184, -2.448131585)),
RowFactory.create("vector3", Vectors.dense(-5.184321865, 5.1481355498, 8.5481513218))
);
StructType schema = createStructType(new StructField[]{
createStructField("vectorId", DataTypes.StringType, false),
createStructField("vector", new VectorUDT(), false)
});
Dataset<Row> dataset = sparkSession.createDataFrame(data, schema);
/**
* 定义UDF函数,将vector转为Array数组
*/
UserDefinedFunction vector2Array = udf((UDF1<Vector, double[]>) vector -> vector.toArray(), DataTypes.createArrayType(DataTypes.DoubleType));
Dataset<Row> arrayData = dataset.withColumn("vec2array", vector2Array.apply(dataset.col("vector")))
.select("vectorId", new String[]{"vec2array"});
/**
* 使用concat_ws函数,组合新的列,并指定连接符为“|”
*/
Dataset<Row> result = arrayData.selectExpr("concat_ws('|', vectorId, vec2array) as concatCol");
result.printSchema();
result.show(false);
/**
* 使用Dataset的Savemode任意保存
*/
result.write().mode(SaveMode.Overwrite).text("/Your/HDFS/Path/");
}