使用scala数据框中的Min和Max查找Normal值

时间:2017-08-28 11:44:49

标签: scala dataset spark-dataframe user-defined-functions unsupervised-learning

我有一个包含39列的数据框,每列都有不同的正常范围。 通过使用正常范围,我想找出正常值,并把0放在其他地方。

这就是我所做的,但我想为39栏做。

val test :(Double => Double) =  (value: Double) =>
{
    if(value >= 45 && value <= 62) 0
    else 1
}

但我不明白如何对每一列使用不同的值。

代表: 我有这个DF

+--------------------+---------+-------------------------+---------+
|a                   |b        |c                        |d        |
+--------------------+---------+-------------------------+---------+
|               207.0|     40.0|                    193.0|     39.0|
|                98.0|     17.0|                    193.0|     15.0|
|               207.0|     13.0|                    193.0|     17.0|
|               207.0|     26.0|                    193.0|     23.0|
|               207.0|     35.0|                    193.0|     24.0|
|               207.0|     91.0|                    193.0|     45.0|
|               207.0|     40.0|                    193.0|     37.0|
|               207.0|     23.0|                    193.0|     23.0|
|               207.0|     26.0|                    193.0|     22.0|
|               207.0|     39.0|                    193.0|     34.0|

我想使用范围

,如下所示
col  range
a   50-160
b   1-21
c   5-40
d   7-27

如果范围内的值则为0,否则为1

+--------------------+---------+-------------------------+---------+
|a                   |b        |c                        |d        |
+--------------------+---------+-------------------------+---------+
|                 1.0|      1.0|                      1.0|      1.0|
|                 0.0|      0.0|                      1.0|      0.0|
|                 1.0|      0.0|                      1.0|      0.0|
|                 1.0|      1.0|                      1.0|      0.0|
|                 1.0|      1.0|                      1.0|      0.0|
|                 1.0|      1.0|                      1.0|      1.0|
|                 1.0|      1.0|                      1.0|      1.0|
|                 1.0|      1.0|                      1.0|      0.0|
|                 1.0|      1.0|                      1.0|      0.0|
|                 1.0|      1.0|                      1.0|      1.0|

I want to do this for 39 columns.(scala/pyspark preferred)

1 个答案:

答案 0 :(得分:1)

您应该定义用户定义的函数(UDF),然后将其应用于您想要的每一列。

以下是有关Scala的用户定义函数的文档。它非常完整,我鼓励你阅读它。

这是一个摘录,可以帮助您快速了解我想要去的地方:

scala> df.withColumn("upper", upper('text)).show
+---+-----+-----+
| id| text|upper|
+---+-----+-----+
|  0|hello|HELLO|
|  1|world|WORLD|
+---+-----+-----+

// You could have also defined the UDF this way
val upperUDF = udf { s: String => s.toUpperCase }

// or even this way
val upperUDF = udf[String, String](_.toUpperCase)

scala> df.withColumn("upper", upperUDF('text)).show
+---+-----+-----+
| id| text|upper|
+---+-----+-----+
|  0|hello|HELLO|
|  1|world|WORLD|
+---+-----+-----+

您看到您的函数适用于整个列,结果将是一个新列。因此,您的函数应如下所示:

def isInRange(e: Number, min: Number, max: Number): Boolean = (e < max && e > min)

然后,对于给定的minValue和maxValue,您所做的只是:

myDF.withColumn("isInRange_a", udf(x => isInRange(x, minValue, maxValue).apply(myDF("a")))

现在可以做的是将它应用于包含(varName,maxValue,minValue)的给定List / DataFrame:

  • map / reduce操作,您可以为每列计算是否为给定范围。然后,你会加入一个给定的密钥(我不太了解你的问题所以我在这里无法帮助你)。这个解决方案有效但随着数据的增长会变得非常低效,因为你可能有几个看起来很相似的键。

  • 递归操作,其目标是执行以下操作:myDF.whithColumn(...).withColumn(...).withColumn(...)

第二个解决方案是我选择的解决方案,因为键看起来很相似。

你是怎么做到的?

def applyMyUDFRecursively(myDF: DataFrame, List[MyRange]: rangesList): DataFrame =
if (rangesList == null || rangesList.isEmpty) myDF
else applyMyUDFRecursively(
    myDF.withColumn(myDF.withColumn("isInRange_" + rangesList.head._0, udf(x => isInRange(x, rangesList.head._1, rangesList.head._2).apply(myDF(rangesList.head._0))), rangesList.tail)

现在您已应用于所有列,但可能包含太多列。做这样的事情:

resultDF.drop(rangesList.map(case x => x._0).collect: _*)

注意类型归属将drop函数应用于map / collect

中获取的列表中的所有元素

with val MyRange = Seq(varName:String,min:Number,max:Number)

EG。对于你的DataFrame,它应该看起来像这样(一个更简单的版本):

def recApply(myDF: DataFrame, cols: List[String]): DataFrame =
if (cols == null || cols.isEmpty) myDF
else recApply(myDF.withColumn(myDF.withColumn("isInRange_" + col.head, udf(x => test(x).apply(myDF(cols.head))), cols.tail)

然后,将此功能应用于您的DF并存储您的结果:

val my_result = recApply(myDF, myDF.cols)