当前位置: 首页 > 知识库问答 >
问题:

如何在Spark中将映射类型转换为SparkML稀疏矢量?

墨高杰
2023-03-14

我的原始模式包含许多我想在ML模型中使用的映射类型,因此我需要将它们转换为SparkML稀疏向量。

root
 |-- colA: map (nullable = true)
 |    |-- key: string
 |    |-- value: double (valueContainsNull = true)
 |-- colB: map (nullable = true)
 |    |-- key: string
 |    |-- value: string (valueContainsNull = true)
 |-- colC: map (nullable = true)
 |    |-- key: string
 |    |-- value: string (valueContainsNull = true)

背景:SparkML模型要求将数据形成特征向量。有一些实用程序可以生成特征向量,但都不支持maptype类型。e、 g.SparkML VectorAssembler允许组合多个列(所有数字类型、布尔类型或向量类型)。

编辑:

到目前为止,我的解决方案是将映射分解为单独的列,然后使用向量汇编程序:

val listkeysColA = df.select(explode($"colA"))
  .select($"key").as[Int].distinct.collect.sorted

val exploded= df.select(listkeysColA.map(x => 
  $"colA".getItem(x).alias(x.toString)): _*).na.fill(0) 

val columnNames = exploded.columns

val assembler = new VectorAssembler().setInputCols(columnNames).setOutputCol("features")

编辑2:

我应该补充一点,我的地图中的数据非常稀疏,事先没有已知的键集。这就是为什么在我当前的解决方案中,我首先对data进行一次传递以收集和排序键。然后我使用getItem(keyName)访问值。

共有1个答案

彭宏义
2023-03-14

据我所知,Spark中没有用于此的内置方法,因此在这种情况下,UDF将是合适的解决方案。这里是一个采用映射[字符串,双精度]的列并返回ML向量的方法:

val toVector = udf((m: Map[String, Double]) => Vectors.dense(m.values.toArray).toSparse)

由于映射没有阶,因此也不能保证生成的向量具有特定阶。

示例输入(df):

+---------------------------------+---------------------------------+
|colA                             |colB                             |
+---------------------------------+---------------------------------+
|Map(a -> 1.0, b -> 2.0, c -> 3.0)|Map(a -> 1.0, b -> 2.0, c -> 3.0)|
+---------------------------------+---------------------------------+

并使用UDF,

val df2 = df.withColumn("colA", toVector($"colA")).withColumn("colB", toVector($"colB"))

给出以下输出:

+-------------+-------------+
|colA         |colB         |
+-------------+-------------+
|[1.0,2.0,3.0]|[1.0,2.0,3.0]|
+-------------+-------------+

其中两列都是向量类型。

root
 |-- colA: vector (nullable = true)
 |-- colB: vector (nullable = true)

如果您想将所有列合并到一个向量中,这里可以使用向量汇编程序(VectorAssembler),如问题编辑中所示。

编辑:

如果您需要保持值的特定顺序,那么您需要像以前一样首先收集所有键。但是,您可以避免使用爆炸

val keys = df.select($"colA")
  .flatMap(_.getAs[Map[String, Int]]("colA").keys)
  .distinct
  .collect
  .sorted

然后适当更改自定义项,以考虑键的顺序,默认值为0.0:

val toVector = udf((m: Map[String, Double]) => 
  Vectors.dense(keys.map(key => m.getOrElse(key, 0.0))).toSparse
)

 类似资料:
  • 但它给出了一个错误。 我对Spark是新手,所以这可能很明显,在我的代码行中可能有明显的错误。请帮忙。谢谢!

  • 我需要将POJO转换成地图。我尝试使用对象映射器,但是像timestamp这样的类型在最终的映射中要么以字符串的形式出现,要么以长的形式出现。有没有什么工具可以做一个简单的转换,使映射具有与POJO中完全相同的对象?(我知道我可以使用反射,但想看看是否有更简单的方法。)

  • 我在Spark中有一个数据框,看起来像这样: 它有30列:只显示其中的一些! 因此,我必须在Scala中将这个数据帧转换成一个键值对,使用键作为数据帧中的一些列,并为这些键分配从索引0到计数(不同的键数)的唯一值。 例如:使用上面的案例,我希望在Scala中的map(key-value)集合中有一个输出,如下所示: 我对斯卡拉和斯帕克是新手,我试着做这样的事情。 但是,这不起作用。:/此操作完成后

  • 问题内容: 我有〜10种不同的文档类型,它们共享10-15个通用字段。但是每种文档类型都有其他字段,其中3个字段最多可以有30-40个其他字段。 我正在考虑为每种文档类型使用不同的映射类型。但是,如果我正确理解了映射的工作方式,ElasticSearch将在内部使用一个包含150-200个字段的映射。因为没有文档对每个字段都有值,所以我最终会得到很多稀疏数据。 根据本文(索引与类型),Elasti

  • 我需要在代码的几个地方将这个映射转换为我的case类,如下所示: 最简单的方法是什么?我能用隐式吗?

  • 我是java新手,正在尝试学习objectmapper。我正在使用它将地图转换为pojo。地图中的键是字符串,所有值都是字符串值,除了我想转换为地图的值。请仔细阅读下面的示例代码,以获得更清晰的图片。 POJO类: 测试代码: 例外: 尝试的变体选项: 我知道我可以将map1字段也保留为String,然后使用另一个对象映射器实例将其转换为map,但我想避免它。有没有办法直接将测试代码中的字符串转换