我的原始数据以表格格式显示。它包含来自不同变量的观察。每次观察时都有变量名,时间戳和当时的值。
变量[string],Time [datetime],Value [float]
数据在HDFS中存储为Parquet并加载到Spark Dataframe(df)中。从该数据框。
现在我想为每个变量计算默认统计数据,如均值,标准偏差等。之后,一旦检索到均值,我想过滤/计算该变量的那些与均值紧密相关的值。
由于对other question的回答,我想出了这段代码:
from pyspark.sql.window import Window
from pyspark.sql.functions import *
from pyspark.sql.types import *
w1 = Window().partitionBy("Variable")
w2 = Window.partitionBy("Variable").orderBy("Time")
def stddev_pop_w(col, w):
#Built-in stddev doesn't support windowing
return sqrt(avg(col * col).over(w) - pow(avg(col).over(w), 2))
def isInRange(value, mean, stddev, radius):
try:
if (abs(value - mean) < radius * stddev):
return 1
else:
return 0
except AttributeError:
return -1
delta = col("Time").cast("long") - lag("Time", 1).over(w2).cast("long")
#f = udf(lambda (value, mean, stddev, radius): abs(value - mean) < radius * stddev, IntegerType())
#f2 = udf(lambda value, mean, stddev: isInRange(value, mean, stddev, 2), IntegerType())
#f3 = udf(lambda value, mean, stddev: isInRange(value, mean, stddev, 3), IntegerType())
df_ = df_all \
.withColumn("mean", mean("Value").over(w1)) \
.withColumn("std_deviation", stddev_pop_w(col("Value"), w1)) \
.withColumn("delta", delta) \
# .withColumn("stddev_2", f2("Value", "mean", "std_deviation")) \
# .withColumn("stddev_3", f3("Value", "mean", "std_deviation")) \
#df2.show(5, False)
问题:最后两个注释行无效。它将给出一个AttributeError,因为stddev和mean的传入值为null。我想这是因为我指的是那些也只是在飞行中计算的列,并且在那一刻没有任何价值。 但有没有办法实现这一目标?
目前我正在进行第二次运行:
df = df_.select("*", \
abs(df_.Value - df_.mean).alias("max_deviation_mean"), \
when(abs(df_.Value - df_.mean) < 2 * df_.std_deviation, 1).otherwise(1).alias("std_dev_mean_2"), \
when(abs(df_.Value - df_.mean) < 3 * df_.std_deviation, 1).otherwise(1).alias("std_dev_mean_3"))
答案 0 :(得分:5)
解决方案是使用DataFrame.aggregateByKey函数,该函数聚合每个分区和节点的值,然后将计算节点周围的聚合混合到一个结果值。
伪代码看起来像这样。它的灵感来自this tutorial,但它使用了StatCounter的两个实例,尽管我们一次总结了两个不同的统计数据:
from pyspark.statcounter import StatCounter
# value[0] is the timestamp and value[1] is the float-value
# we are using two instances of StatCounter to sum-up two different statistics
def mergeValues(s1, v1, s2, v2):
s1.merge(v1)
s2.merge(v2)
return
def combineStats(s1, s2):
s1[0].mergeStats(s2[0])
s1[1].mergeStats(s2[1])
return
(df.aggregateByKey((StatCounter(), StatCounter()),
(lambda s, values: mergeValues(s[0], values[0], s[1], values[1]),
(lambda s1, s2: combineStats(s1, s2))
.mapValues(lambda s: ( s[0].min(), s[0].max(), s[1].max(), s[1].min(), s[1].mean(), s[1].variance(), s[1].stddev,() s[1].count()))
.collect())
答案 1 :(得分:2)
这不起作用,因为当你执行
时from pyspark.sql.functions import *
你使用abs
内置pyspark.sql.functions.abs
,它希望列不是本地Python值作为输入。
您创建的UDF也不会处理NULL
个条目。
除非您知道导入的确切内容,否则请勿使用import *
。别名
from pyspark.sql.functions import abs as abs_
或导入模块
from pyspark.sql import functions as sqlf
sqlf.col("x")
除非必要,否则请务必检查UDF中的输入,或者更好地避免使用UDF。