Apache spark Spark:查找滚动时间窗口内每组出现率最高的值

Apache spark Spark:查找滚动时间窗口内每组出现率最高的值,apache-spark,pyspark,Apache Spark,Pyspark,从以下spark数据帧开始: 从io导入StringIO 作为pd进口熊猫 从pyspark.sql.functions导入col pd_df=pd.read_csvStringIOdevice_id,read_date,id,count 装置A,2017-08-054041,3 装置A,2017-08-064041,3 装置A,2017-08-074041,4 装置A,2017-08-084041,3 装置A,2017-08-094041,3 装置A,2017-08-104041,1 装置A,

从以下spark数据帧开始:

从io导入StringIO 作为pd进口熊猫 从pyspark.sql.functions导入col pd_df=pd.read_csvStringIOdevice_id,read_date,id,count 装置A,2017-08-054041,3 装置A,2017-08-064041,3 装置A,2017-08-074041,4 装置A,2017-08-084041,3 装置A,2017-08-094041,3 装置A,2017-08-104041,1 装置A,2017-08-104045,2 装置A,2017-08-114045,3 装置A,2017-08-124045,3 设备A,2017-08-134045,3,推断日期时间格式=真,解析日期=['读取日期'] df=spark.createDataFramepd_df.列为'read_date',列为'read_date'。强制转换为'date' df.show 输出:

+--------------+----------+----+-----+
|device_id     | read_date|  id|count|
+--------------+----------+----+-----+
|      device_A|2017-08-05|4041|    3|
|      device_A|2017-08-06|4041|    3|
|      device_A|2017-08-07|4041|    4|
|      device_A|2017-08-08|4041|    3|
|      device_A|2017-08-09|4041|    3|
|      device_A|2017-08-10|4041|    1|
|      device_A|2017-08-10|4045|    2|
|      device_A|2017-08-11|4045|    3|
|      device_A|2017-08-12|4045|    3|
|      device_A|2017-08-13|4045|    3|
+--------------+----------+----+-----+
我想在3天的滚动窗口内找到每个设备的最频繁id,read\u date组合。对于时间窗口选择的每组行,我需要通过对每个id的计数求和来找到最频繁的id,然后返回顶部id

预期产出:

+--------------+----------+----+
|device_id     | read_date|  id|
+--------------+----------+----+
|      device_A|2017-08-05|4041|
|      device_A|2017-08-06|4041|
|      device_A|2017-08-07|4041|
|      device_A|2017-08-08|4041|
|      device_A|2017-08-09|4041|
|      device_A|2017-08-10|4041|
|      device_A|2017-08-11|4045|
|      device_A|2017-08-12|4045|
|      device_A|2017-08-13|4045|
+--------------+----------+----+

我开始认为只有使用自定义聚合函数才能实现这一点。由于spark 2.3尚未发布,我将不得不在Scala中编写此文件或使用collect_列表。我遗漏了什么吗?

我设法找到了一个效率很低的解决方案。希望有人能够发现一些改进,以避免python udf和调用collect_列表

从pyspark.sql导入窗口 从pyspark.sql.functions导入col、collect_列表,首先是udf 从pyspark.sql.types导入IntegerType def top_IDID,计数: c=计数器 对于cnid,在ZIPDS中计数,计数: c[cnid]+=计数 返回c.most_common1[0][0] 滚动窗口=3 天数=λi:i*86400 基于时间定义滚动计算窗口 窗口= 窗 .partitionBydevice\u id .orderBycolread\u date.casttimestamp.castlong .Range-Days滚动窗口-1,0 使用window和collect_list在每行上存储与窗口定义匹配的数据 df_collected=df.select “设备id”、“读取日期”, collect_listcol'id'.overwindow.alias'id', collect_listcol'count'.overwindow.别名'counts' 在必要时消除重复行 df_group=df_collected.groupBy'device_id','read_date'。agg 第一个'id'。别名'id', 第一个“计数”。别名“计数”, 注册并应用udf以返回最常见的id top\u id\u udf=udftop\u id,整数类型 df_mapped=df_分组。带有列'top_id',top_id',udfcol'id',列'counts' df_mapped.showtruncate=False 返回:

+---------+----------+------------------------+------------+------+
|device_id|read_date |ids                     |counts      |top_id|
+---------+----------+------------------------+------------+------+
|device_A |2017-08-05|[4041]                  |[3]         |4041  |
|device_A |2017-08-06|[4041, 4041]            |[3, 3]      |4041  |
|device_A |2017-08-07|[4041, 4041, 4041]      |[3, 3, 4]   |4041  |
|device_A |2017-08-08|[4041, 4041, 4041]      |[3, 4, 3]   |4041  |
|device_A |2017-08-09|[4041, 4041, 4041]      |[4, 3, 3]   |4041  |
|device_A |2017-08-10|[4041, 4041, 4041, 4045]|[3, 3, 1, 2]|4041  |
|device_A |2017-08-11|[4041, 4041, 4045, 4045]|[3, 1, 2, 3]|4045  |
|device_A |2017-08-12|[4041, 4045, 4045, 4045]|[1, 2, 3, 3]|4045  |
|device_A |2017-08-13|[4045, 4045, 4045]      |[3, 3, 3]   |4045  |
+---------+----------+------------------------+------------+------+
添加窗口:

在pyspark.sql.functions导入窗口中,求和为求和,日期为添加 df_w=df.withColumn 读取日期,窗口读取日期,3天,1天[开始].castdate 然后处理计数 df_w=df_w.groupBy'device_id'、'read_date'、'id'.aggsum_'count'。别名'count' 例如,使用来自的解决方案之一

从pyspark.sql.window导入窗口 从pyspark.sql.functions导入行号 滚动窗口=3 顶部_df= 东方西 .withColumn 注册护士, 第二排 Window.partitionBydevice\u id,读取日期 .orderBycolcount.desc .其中colrn==1 .orderByread_日期 droprn先生 结果在时间窗口开始时计算-根据需要调整读取日期 final_df=顶部_df.带列'read_date',date_添加'read_date',滚动窗口-1 决赛 +-----+-----+--+---+ |设备| id |读取|日期| id |计数| +-----+-----+--+---+ |装置A | 2017-08-05 | 4041 | 3| |装置A | 2017-08-06 | 4041 | 6| |装置A | 2017-08-07 | 4041 | 10| |装置A | 2017-08-08 | 4041 | 10| |装置A | 2017-08-09 | 4041 | 10| |装置A | 2017-08-10 | 4041 | 7| |装置A | 2017-08-11 | 4045 | 5| |装置A | 2017-08-12 | 4045 | 8| |装置A | 2017-08-13 | 4045 | 9| |装置A | 2017-08-14 | 4045 | 6| |装置A | 2017-08-15 | 4045 | 3| +-----+-----+--+---+
我不认为这是重复的,因为对分组行执行的操作比排序并返回第一行更复杂。实际上,我需要在时间窗口子集内执行另一个分组,以找到组中最常见的设备id,然后返回最上面的设备id。谢谢!我不知道pyspark.sql.functions.window函数!这将添加我以前尝试在窗口上应用排名时丢失的行。您的答案与我的答案不完全一致,但会引导我找到解决方案。在计算排名之前,您只是缺少了一个.groupBy'device\u id'、'read\u date\u window'、'id'。aggsum\u count'。别名'count',最好使用窗口末尾作为日期,以避免解决方案中当前出现的3天偏移。如果我想做这些小的修改,我很乐意接受你的回答!如果在排名前进行聚合,每个组将只有一个观察值,因为“设备id”,“读取日期”窗口与中的设置相同。分区按设备id,读取日期,对吗?我们需要考虑每个id的计数。例如,在3天窗口中,id 1可以出现两次,计数为2,id 2打开 计数为3的ce。在这种情况下,在确定秩之前,应该将两个ID1行相加。这使得id 1的总数为4,这使其成为排名最高的id。抱歉,我可能没有在问题中明确说明这一点!