标量列对象到单列数据框

时间:2019-09-25 09:39:35

标签: scala dataframe apache-spark types casting

我有一个这样的数据框:

      val df = Seq(
      ("a", Seq(2.0)),
      ("a", Seq(1.0)),
      ("a", Seq(0.5)),
      ("b", Seq(24.0)),
      ("b", Seq(12.5)),
      ("b", Seq(6.4)),
      ("b", Seq(3.2)),
      ("c", Seq(104.0)),
      ("c", Seq(107.4))
    ).toDF("key", "value")

我需要使用一种算法,该算法需要在不同的组上输入一个DataFrame对象。 为了使这一点更清楚,假设我必须按组使用StandardScaler缩放。

在熊猫中,我会做类似的事情(在此过程中,许多类型都会发生变化):

from sklearn.preprocessing import StandardScaler
       df.groupby(key) \
       .value \
       .transform(lambda x: StandardScaler \
       .fit_transform(x \
       .values \
       .reshape(-1,1)) \
       .reshape(-1))

我需要在scala中执行此操作,因为我需要使用的算法不是Scaler,而是scala中内置的另一件事。

到目前为止,我已经尝试做这样的事情:

import org.apache.spark.ml.feature.StandardScaler
def f(X : org.apache.spark.sql.Column) : org.apache.spark.sql.Column = {  
      val scaler = new StandardScaler()
        .setInputCol("value")
        .setOutputCol("scaled")

      val output = scaler.fit(X)("scaled")

      (output)

    }

    df.withColumn("scaled_values", f(col("features")).over(Window.partitionBy("key")))

但当然会给我一个错误:

  

command-144174313464261:21:错误:类型不匹配;   找到:org.apache.spark.sql.Column   必填:org.apache.spark.sql.Dataset [_]    val输出= scaler.fit(X)(“ scaled”)

因此,我试图将单个Column对象转换为DataFrame对象,但没有成功。我该怎么办?

如果不可能,是否有解决方法?

更新1

似乎我在代码中犯了一些错误,我试图修复它(我认为我做对了):

      val df = Seq(
      ("a", 2.0),
      ("a", 1.0),
      ("a", 0.5),
      ("b", 24.0),
      ("b", 12.5),
      ("b", 6.4),
      ("b", 3.2),
      ("c", 104.0),
      ("c", 107.4)
    ).toDF("key", "value")


    def f(X : org.apache.spark.sql.DataFrame) : org.apache.spark.sql.Column = {  
         val assembler = new VectorAssembler()
        .setInputCols(Array("value"))
        .setOutputCol("feature")
          val scaler = new StandardScaler()
        .setInputCol("feature")
        .setOutputCol("scaled")
         val pipeline = new Pipeline()
        .setStages(Array(assembler, scaler))
         val output = pipeline.fit(X).transform(X)("scaled")

      (output)
    }  

    someDF.withColumn("scaled_values", f(someDF).over(Window.partitionBy("key")))

我仍然收到错误消息:

  

org.apache.spark.sql.AnalysisException:表达式'scaled#1294'不是   窗口功能内支持。;

我不确定导致此错误的原因,我尝试为该列添加别名,但似乎不起作用。

1 个答案:

答案 0 :(得分:2)

  

因此,我试图将单个Column对象转换为DataFrame对象,但没有成功。我该怎么办?

您不能,column只是引用DataFrame的column,它不包含任何数据,它不是像数据帧那样的数据结构。

您的f函数也将无法正常工作。如果要创建要与Window一起使用的自定义函数,则需要一个UDAF(用户定义的聚合函数),这非常困难...

在您的情况下,我将对key进行分组,收集您的值,然后应用UDF进行缩放。请注意,这仅适用于每个键的数据不能太大(大于1个执行程序的大小),否则您需要UDAF

这里有个例子:

// example scala method, scale to 0-1
def myScaler(data:Seq[Double]) = {
  val mi = data.min
  val ma = data.max
  data.map(x => (x-mi)/(ma-mi))
}

val udf_myScaler = udf(myScaler _)

df
  .groupBy($"key")
  .agg(
    collect_list($"value").as("values")
  )
  .select($"key",explode(arrays_zip($"values",udf_myScaler($"values"))))
  .select($"key",$"col.values",$"col.1".as("values_scaled"))
  .show()

给予:

+---+------+-------------------+
|key|values|      values_scaled|
+---+------+-------------------+
|  c| 104.0|                0.0|
|  c| 107.4|                1.0|
|  b|  24.0|                1.0|
|  b|  12.5|0.44711538461538464|
|  b|   6.4|0.15384615384615385|
|  b|   3.2|                0.0|
|  a|   2.0|                1.0|
|  a|   1.0| 0.3333333333333333|
|  a|   0.5|                0.0|
+---+------+-------------------+