我有一个客户表,其中包含有关每个客户的多个流程的信息。
目标是为每个客户和每个流程提取功能。这意味着每个特征主要是a.find ....
对象上的聚合或排序比较计算。
但是,目标是能够随着时间的推移添加越来越多的功能。所以基本上用户应该能够使用一些过滤器,度量和聚合来定义新函数,并将这个新函数添加到在表上运行的函数池中。
输出应该是具有所有功能的customerID,processID表。
所以我开始一个小小的工作示例:
.groupby(customerID, processID)
然后我定义了2个任意特征,这些特征由这些函数提取:
l = [('CM1','aa1', 100,0.1),('CM1','aa1', 110,0.2),\
('CM1','aa1', 110,0.9),('CM1','aa1', 100,1.5),\
('CX2','bb9', 100,0.1),('CX2','bb9', 100,0.2),\
('CX2','bb9', 110,6.0),('CX2','bb9', 100,0.18)]
rdd = sc.parallelize(l)
df = sqlContext.createDataFrame(rdd,['customid','procid','speed','timestamp'])
+--------+------+-----+---------+
|customid|procid|speed|timestamp|
+--------+------+-----+---------+
| CM1| aa1| 100| 0.1|
| CM1| aa1| 110| 0.2|
| CM1| aa1| 110| 0.9|
| CM1| aa1| 100| 1.5|
| CX2| bb9| 100| 0.1|
| CX2| bb9| 100| 0.2|
| CX2| bb9| 110| 6.0|
| CX2| bb9| 100| 0.18|
+--------+------+-----+---------+
它们返回仅包含聚合值的1元素RRD。 接下来,所有要素函数都应用于给定的过程,并在每个过程的结果RDD中合并:
def extr_ft_1 (proc_data, limit=100):
proc_data = proc_data.filter(proc_data.speed > limit).agg(count(proc_data.speed))
proc_data = proc_data.select(col('count(speed)').alias('speed_feature'))
proc_data.show()
return proc_data
def extr_ft_0 (proc_data):
max_t = proc_data.agg(spark_max(proc_data.timestamp))
min_t = proc_data.agg(spark_min(proc_data.timestamp))
max_t = max_t.select(col('max(timestamp)').alias('max'))
min_t = min_t.select(col('min(timestamp)').alias('min'))
X = max_t.crossJoin(min_t)
X = X.withColumn('time_feature', X.max+X.min)
X = X.drop(X.min).drop(X.max)
X.show()
return (X)
最后一步是将包含每个进程的功能的所有行附加到一个数据帧:
def get_proc_features(proc, data, *features):
proc_data = data.filter( data.customid == proc)
features_for_proc = [feature_value(proc_data) for feature_value in features]
for number, feature in enumerate(features_for_proc):
if number == 0:
l = [(proc,'dummy')]
rdd = sc.parallelize(l)
df = sqlContext.createDataFrame(rdd,['customid','dummy'])
df = df.drop(df.dummy)
df.show()
features_for_proc_rdd = feature
features_for_proc_rdd = features_for_proc_rdd.crossJoin(df)
continue
features_for_proc_rdd = features_for_proc_rdd.crossJoin(feature)
features_for_proc_rdd.show()
return features_for_proc_rdd
变换链如下:
获取客户1的功能1和2:
for number, proc in enumerate(customer_list_1):
if number == 0:
#results = get_trip_features(trip, df, extr_ft_0, extr_ft_1)
results = get_proc_features(proc, df, *extr_feature_funcs)
continue
results = results.unionAll(get_proc_features(proc, df, *extr_feature_funcs))
results.show()
将它们合并到:
+------------+
|time_feature|
+------------+
| 1.6|
+------------+
+-------------+
|speed_feature|
+-------------+
| 2|
+-------------+
对客户2执行相同操作并将所有RDD附加到最终结果RDD:
+------------+--------+-------------+
|time_feature|customid|speed_feature|
+------------+--------+-------------+
| 1.6| CM1| 2|
+------------+--------+-------------+
如果我在群集上运行代码,它适用于2个客户。 但是当我在合理数量的客户上测试它时,我主要得到GC和堆内存错误。
我在这里与许多RDD合作吗?我担心我的代码效率很低,但我不知道从哪里开始优化它。我想我最后只调用一个动作(我将所有show()放在实时模式中,然后只收集()最后一个RDD)。 我非常感谢你的帮助。
答案 0 :(得分:0)
您的代码需要重构,问题不在于RDD,而在于您将其过滤以处理单一密钥然后交叉连接。迭代值使您失去了pyspark的分布式方面。请注意,如果您不需要另一个工作台的功能,则应始终保留一张工作台。
最好的方法是使用数据框和窗口函数。
首先让我们重写你的功能:
import pyspark.sql.functions as psf
def extr_ft_1 (proc_data, w, limit=100):
return proc_data.withColumn(
"speed_feature",
psf.sum((proc_data.speed > limit).cast("int")).over(w)
)
def extr_ft_0(proc_data, w):
return proc_data.withColumn(
"time_feature",
psf.min(proc_data.timestamp).over(w) + psf.max(proc_data.timestamp).over(w)
)
其中w
是窗口规范:
from pyspark.sql import Window
w = Window.partitionBy("customid")
df1 = extr_ft_1(df, w)
df0 = extr_ft_0(df1, w)
df0.show()
+--------+------+-----+---------+-------------+------------+
|customid|procid|speed|timestamp|speed_feature|time_feature|
+--------+------+-----+---------+-------------+------------+
| CM1| aa1| 100| 0.1| 2| 1.6|
| CM1| aa1| 110| 0.2| 2| 1.6|
| CM1| aa1| 110| 0.9| 2| 1.6|
| CM1| aa1| 100| 1.5| 2| 1.6|
| CX2| bb9| 100| 0.1| 1| 6.1|
| CX2| bb9| 100| 0.2| 1| 6.1|
| CX2| bb9| 110| 6.0| 1| 6.1|
| CX2| bb9| 100| 0.18| 1| 6.1|
+--------+------+-----+---------+-------------+------------+
在这里,我们永远不会丢失信息(我们保留所有行),所以如果您想添加额外的功能,您可以。如果您想要最终的汇总结果,只需通过groupBy("customid")
。
请注意,您也可以修改窗口规范中的聚合键以包含procid
。