如何将 Spark 数据帧列从 Array[Int] 转换为 linalg.Vector?
How convert Spark dataframe column from Array[Int] to linalg.Vector?
我有一个数据框 df,它看起来像这样:
+--------+--------------------+
| user_id| is_following|
+--------+--------------------+
| 1|[2, 3, 4, 5, 6, 7] |
| 2|[20, 30, 40, 50] |
+--------+--------------------+
我可以确认这具有架构:
root
|-- user_id: integer (nullable = true)
|-- is_following: array (nullable = true)
| |-- element: integer (containsNull = true)
我想使用 Spark 的 ML 例程(例如 LDA)对此进行一些机器学习,需要我将 is_following
列转换为 linalg.Vector
(不是 Scala 向量)。当我尝试通过
执行此操作时
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.ml.linalg.Vectors
val assembler = new VectorAssembler().setInputCols(Array("is_following")).setOutputCol("features")
val output = assembler.transform(df)
然后我收到以下错误:
java.lang.IllegalArgumentException: Data type ArrayType(IntegerType,true) is not supported.
如果我的解释是正确的,那么我需要将此处的类型从整数转换为其他类型。 (双?字符串?)
我的问题是,将此数组转换为可针对 ML 管道正确矢量化的最佳方法是什么?
编辑:如果有帮助,我不必以这种方式构建数据框。我可以改为:
+--------+------------+
| user_id|is_following|
+--------+------------+
| 1| 2|
| 1| 3|
| 1| 4|
| 1| 5|
| 1| 6|
| 1| 7|
| 2| 20|
| ...| ...|
+--------+------------+
因此您的初始输入可能比转换后的输入更适合。 Spark 的 VectorAssembler 要求所有列都是双精度,而不是双精度数组。由于不同的用户可以关注不同数量的人,因此您当前的结构可能很好,您只需要将 is_following 转换为 Double,实际上您可以使用 Spark 的 VectorIndexer https://spark.apache.org/docs/2.1.0/ml-features.html#vectorindexer 或手动执行此操作SQL.
所以 tl;dr 是 - 类型错误是因为 Spark 的 Vector 仅支持双精度(这在不久的将来可能会改变图像数据,但无论如何都不适合您的用例)和您的替代结构实际上可能更适合(没有分组的结构)。
您可能会发现查看 Spark 文档中的协同过滤示例对您的进一步探索很有用 - https://spark.apache.org/docs/latest/ml-collaborative-filtering.html。祝你好运,玩得开心 Spark ML :)
编辑:
我注意到您说您希望对输入执行 LDA,所以让我们也看看如何为该格式准备数据。对于 LDA 输入,您可能需要考虑使用 CountVectorizer
(参见 https://spark.apache.org/docs/2.1.0/ml-features.html#countvectorizer)
将数组转换为 linalg.Vector
并同时将整数转换为双精度数的一个简单解决方案是使用 UDF
.
使用您的数据框:
val spark = SparkSession.builder.getOrCreate()
import spark.implicits._
val df = spark.createDataFrame(Seq((1, Array(2,3,4,5,6,7)), (2, Array(20,30,40,50))))
.toDF("user_id", "is_following")
val convertToVector = udf((array: Seq[Int]) => {
Vectors.dense(array.map(_.toDouble).toArray)
})
val df2 = df.withColumn("is_following", convertToVector($"is_following"))
spark.implicits._
在此处导入以允许使用 $
,可以使用 col()
或 '
。
打印 df2
数据框将得到想要的结果:
+-------+-------------------------+
|user_id|is_following |
+-------+-------------------------+
|1 |[2.0,3.0,4.0,5.0,6.0,7.0]|
|2 |[20.0,30.0,40.0,50.0] |
+-------+-------------------------+
架构:
root
|-- user_id: integer (nullable = false)
|-- is_following: vector (nullable = true)
我有一个数据框 df,它看起来像这样:
+--------+--------------------+
| user_id| is_following|
+--------+--------------------+
| 1|[2, 3, 4, 5, 6, 7] |
| 2|[20, 30, 40, 50] |
+--------+--------------------+
我可以确认这具有架构:
root
|-- user_id: integer (nullable = true)
|-- is_following: array (nullable = true)
| |-- element: integer (containsNull = true)
我想使用 Spark 的 ML 例程(例如 LDA)对此进行一些机器学习,需要我将 is_following
列转换为 linalg.Vector
(不是 Scala 向量)。当我尝试通过
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.ml.linalg.Vectors
val assembler = new VectorAssembler().setInputCols(Array("is_following")).setOutputCol("features")
val output = assembler.transform(df)
然后我收到以下错误:
java.lang.IllegalArgumentException: Data type ArrayType(IntegerType,true) is not supported.
如果我的解释是正确的,那么我需要将此处的类型从整数转换为其他类型。 (双?字符串?)
我的问题是,将此数组转换为可针对 ML 管道正确矢量化的最佳方法是什么?
编辑:如果有帮助,我不必以这种方式构建数据框。我可以改为:
+--------+------------+
| user_id|is_following|
+--------+------------+
| 1| 2|
| 1| 3|
| 1| 4|
| 1| 5|
| 1| 6|
| 1| 7|
| 2| 20|
| ...| ...|
+--------+------------+
因此您的初始输入可能比转换后的输入更适合。 Spark 的 VectorAssembler 要求所有列都是双精度,而不是双精度数组。由于不同的用户可以关注不同数量的人,因此您当前的结构可能很好,您只需要将 is_following 转换为 Double,实际上您可以使用 Spark 的 VectorIndexer https://spark.apache.org/docs/2.1.0/ml-features.html#vectorindexer 或手动执行此操作SQL.
所以 tl;dr 是 - 类型错误是因为 Spark 的 Vector 仅支持双精度(这在不久的将来可能会改变图像数据,但无论如何都不适合您的用例)和您的替代结构实际上可能更适合(没有分组的结构)。
您可能会发现查看 Spark 文档中的协同过滤示例对您的进一步探索很有用 - https://spark.apache.org/docs/latest/ml-collaborative-filtering.html。祝你好运,玩得开心 Spark ML :)
编辑:
我注意到您说您希望对输入执行 LDA,所以让我们也看看如何为该格式准备数据。对于 LDA 输入,您可能需要考虑使用 CountVectorizer
(参见 https://spark.apache.org/docs/2.1.0/ml-features.html#countvectorizer)
将数组转换为 linalg.Vector
并同时将整数转换为双精度数的一个简单解决方案是使用 UDF
.
使用您的数据框:
val spark = SparkSession.builder.getOrCreate()
import spark.implicits._
val df = spark.createDataFrame(Seq((1, Array(2,3,4,5,6,7)), (2, Array(20,30,40,50))))
.toDF("user_id", "is_following")
val convertToVector = udf((array: Seq[Int]) => {
Vectors.dense(array.map(_.toDouble).toArray)
})
val df2 = df.withColumn("is_following", convertToVector($"is_following"))
spark.implicits._
在此处导入以允许使用 $
,可以使用 col()
或 '
。
打印 df2
数据框将得到想要的结果:
+-------+-------------------------+
|user_id|is_following |
+-------+-------------------------+
|1 |[2.0,3.0,4.0,5.0,6.0,7.0]|
|2 |[20.0,30.0,40.0,50.0] |
+-------+-------------------------+
架构:
root
|-- user_id: integer (nullable = false)
|-- is_following: vector (nullable = true)