在我的PySpark代码中,我有一个DataFrame
填充了来自传感器的数据,每一行都有时间戳,event_description和event_value。
每个传感器事件由id和值定义的测量组成。我唯一的保证是所有的"阶段"与单个事件相关的行包含在两个EV_SEP
行之间(未排序)。
每个活动内部"阻止"有一个事件标签,它是与EV_CODE
相关联的值。
+-------------------------+------------+-------------+
| timestamp | event_id | event_value |
+-------------------------+------------+-------------+
| 2017-01-01 00:00:12.540 | EV_SEP | ----- |
+-------------------------+------------+-------------+
| 2017-01-01 00:00:14.201 | EV_2 | 10 |
+-------------------------+------------+-------------+
| 2017-01-01 00:00:13.331 | EV_1 | 11 |
+-------------------------+------------+-------------+
| 2017-01-01 00:00:15.203 | EV_CODE | ABC |
+-------------------------+------------+-------------+
| 2017-01-01 00:00:16.670 | EV_SEP | ----- |
+-------------------------+------------+-------------+
我想创建一个包含该标签的新列,以便我知道所有事件都与该标签相关联:
+-------------------------+----------+-------------+------------+
| timestamp | event_id | event_value | event_code |
+-------------------------+----------+-------------+------------+
| 2017-01-01 00:00:12.540 | EV_SEP | ----- | ABC |
+-------------------------+----------+-------------+------------+
| 2017-01-01 00:00:14.201 | EV_2 | 10 | ABC |
+-------------------------+----------+-------------+------------+
| 2017-01-01 00:00:13.331 | EV_1 | 11 | ABC |
+-------------------------+----------+-------------+------------+
| 2017-01-01 00:00:15.203 | EV_CODE | ABC | ABC |
+-------------------------+----------+-------------+------------+
| 2017-01-01 00:00:16.670 | EV_SEP | ----- | ABC |
+-------------------------+----------+-------------+------------+
使用pandas,我可以轻松获取EV_SEP
行的索引,将表拆分为块,从每个块中取出EV_CODE
并创建具有此值的event_code
列。 / p>
可能的解决方案是:
有没有更好的方法来解决这个问题?
答案 0 :(得分:2)
from pyspark.sql import functions as f
示例数据:
df.show()
+-----------------------+--------+-----------+
|timestamp |event_id|event_value|
+-----------------------+--------+-----------+
|2017-01-01 00:00:12.540|EV_SEP |null |
|2017-01-01 00:00:14.201|EV_2 |10 |
|2017-01-01 00:00:13.331|EV_1 |11 |
|2017-01-01 00:00:15.203|EV_CODE |ABC |
|2017-01-01 00:00:16.670|EV_SEP |null |
|2017-01-01 00:00:20.201|EV_2 |10 |
|2017-01-01 00:00:24.203|EV_CODE |DEF |
|2017-01-01 00:00:31.670|EV_SEP |null |
+-----------------------+--------+-----------+
添加索引:
df_idx = df.filter(df['event_id'] == 'EV_SEP') \
.withColumn('idx', f.row_number().over(Window.partitionBy().orderBy(df['timestamp'])))
df_block = df.filter(df['event_id'] != 'EV_SEP').withColumn('idx', f.lit(0))
'点差'指数:
df = df_idx.union(df_block).withColumn('idx', f.max('idx').over(
Window.partitionBy().orderBy('timestamp').rowsBetween(Window.unboundedPreceding, Window.currentRow))).cache()
添加EV_CODE
:
df_code = df.filter(df['event_id'] == 'EV_CODE').withColumnRenamed('event_value', 'event_code')
df = df.join(df_code, on=[df['idx'] == df_code['idx']]) \
.select(df['timestamp'], df['event_id'], df['event_value'], df_code['event_code'])
最后:
+-----------------------+--------+-----------+----------+
|timestamp |event_id|event_value|event_code|
+-----------------------+--------+-----------+----------+
|2017-01-01 00:00:12.540|EV_SEP |null |ABC |
|2017-01-01 00:00:13.331|EV_1 |11 |ABC |
|2017-01-01 00:00:14.201|EV_2 |10 |ABC |
|2017-01-01 00:00:15.203|EV_CODE |ABC |ABC |
|2017-01-01 00:00:16.670|EV_SEP |null |DEF |
|2017-01-01 00:00:20.201|EV_2 |10 |DEF |
|2017-01-01 00:00:24.203|EV_CODE |DEF |DEF |
+-----------------------+--------+-----------+----------+
答案 1 :(得分:0)
创建一个新的Hadoop InputFormat
将是一种在计算上更有效的方法来实现你的目标(虽然在代码方面可以说是相同或更多的体操)。您可以使用the Python API中的sc.hadoopFile
指定备用Hadoop输入格式,但必须注意从Java格式到Python的转换。然后,您可以指定格式。 PySpark中可用的转换器相对较少,但this reference建议使用Avro converter as an example。您可能还会发现让自定义Hadoop输入格式输出文本然后在Python中另外解析以避免实现转换器的问题。
一旦你有了这个,你就会创建一个特殊的输入格式(使用Hadoop API在Java或Scala中)来处理具有EV_SEP
作为记录分隔符而不是换行符的行的特殊序列。您可以通过在累加器中读取行来简单地收集行(只需要一个简单的ArrayList
可以作为概念证明)然后在找到两个{{1时发出累积的记录列表行中的行。
我想指出,使用EV_SEP
作为此类设计的基础可能很诱人,但输入格式会在换行符处任意分割此类文件,您需要实现自定义逻辑以正确支持拆分文件。或者,您可以通过简单地不实现文件拆分来避免此问题。这是对分区程序的简单修改。
如果确实需要拆分文件,基本思路是:
TextInputFormat
。针对文件拆分的边缘情况检测这些序列将是一个挑战。我建议建立最大字节宽度的行,并从起始点向后读取适当宽度(基本上是行大小的2倍)的滑动窗口块,然后使用预编译的Java正则表达式和匹配器匹配这些窗口。这类似于Sequence Files find their sync marks,但使用正则表达式来检测序列而不是严格的相等。
作为旁注,我会关注你提到的其他一些背景,即按时间戳排序DataFrame可能会改变在不同文件中同一时间段内发生的事件的内容。