概述
我正在使用具有几列的syslog文本文件,但最后一列只是一串键值对。
最后一列的格式为 key1 =“ value1” key2 =“ value2” key3 =“ value3带有空格”
这些键可能只有1个。
我的目标是将数据框拆分为两个新的数据框。一个是在所有消息中都保持一致的列,并是固定数量的列和另一个是键值对的数据帧,其中每个键/值对都在其自己的行中(因为它们的大小是动态的)。 / p>
然后我可以对原始数据帧运行sql select语句,并加入新的数据帧以隔离我想要的选择键,并与其他数据集混搭。
我有一套可以正常工作的测试代码,但是在其中的某些部分苦苦挣扎。
消息行示例
# Apr 1 23:59:59 server1.mycompany.com datetime="2019-04-02T05:50:11Z" timestamp="1554184211000" script="ServerDataCollector" processTimeCount="35" Name="'Test Location'"
# Apr 1 23:59:59 server2.mycompany.com datetime="2019-04-02T05:50:11Z" timestamp="1554184211000" script="ClientDataCollector" Name="'Test Location2' upTime="23513", faultsLast24Hours="0"
这将在AWS中使用,粘合搜寻器将自动以以下格式注册数据:
因此,在没有自定义搜寻器或表体验的情况下,我的数据框看起来像上面的三列。
对于此示例,我将得到以下数据帧,这是一些用于设置示例的代码:
# rawDataFrame: The original data set
# date
# host
# kernel_logs
# headersDataFrame: The first two columns with a unique identifier added
# message_id: unique key
# date:
# host:
# detailsDataFrame: Table containing a row for every key/value pair
# message_id
# key
# value
#
# The concept being I can do a query like:
# SELECT
# *
# FROM headersDataFrame
# INNER JOIN detailsDataFrame USING (message_id)
# WHERE detailsDataFrame.key="something"
# AND headersDataFrame.date=today()
#
#
from pyspark import SparkContext, SQLContext
from pyspark.sql import *
from pyspark.sql.functions import monotonically_increasing_id, split
#sc = SparkContext()
#spark = SQLContext(sc)
Message = Row("date", "host", "kernel_logs")
message1 = Message( \
'Apr 1 23:59:59', \
'server1.mycompany.com', \
'datetime="2019-04-02T05:50:11Z" timestamp="1554184211000" script="ServerDataCollector" processTimeCount="35" Name="\'Test Location\'"' \
)
message2 = Message( \
'Apr 1 23:59:59', \
'server2.mycompany.com', \
'datetime="2019-04-02T05:50:11Z" timestamp="1554184211000" script="ClientDataCollector" Name="\'Test Location2\'"" upTime="23513" faultsLast24Hours="0"' \
)
messageSequence = [message1, message2]
rawDataFrame = spark.createDataFrame(messageSequence)
rawDataFrame.show(truncate=False)
启动Spark应用程序ID YARN应用程序ID种类状态Spark UI驱动程序日志当前会话? 7 application_1557428720231_0008 pyspark空闲链接链接✔SparkSession 可作为“火花”使用。 + -------------- + --------------------- + ------------ -------------------------------------------------- -------------------------------------------------- ------------------------------------ + |日期|主机|内核日志
| + -------------- + --------------------- + ------------ -------------------------------------------------- -------------------------------------------------- ------------------------------------ + | 4月1日23:59:59 | server1.mycompany。 com | datetime =“ 2019-04-02T05:50:11Z” timestamp =“ 1554184211000” script =“ ServerDataCollector” processTimeCount =“ 35” Name =“'测试位置'” | | 4月1日 23:59:59 | server2.mycompany.com | datetime =“ 2019-04-02T05:50:11Z” timestamp =“ 1554184211000” script =“ ClientDataCollector” Name =“'Test Location2'“” upTime =“ 23513” faultsLast24Hours =“ 0” | + -------------- + --------------------- + ------------ -------------------------------------------------- -------------------------------------------------- ------------------------------------ +
当前代码
需要添加唯一键才能在两个表之间进行查找。我确实看到了一篇有关monotonically_increasing_id()函数问题的文章,但尚未处理我的较大数据集,以查看它是否会创建重复项(在过去的其他疑难解答文章中列出)。
rawDataFrameWithPrimaryKey = rawDataFrame.withColumn('message_id', monotonically_increasing_id())
rawDataFrameWithPrimaryKey.show()
+ -------------- + -------------------- + ---------- ---------- + ---------- + |日期|主持人| kernel_logs | message_id | + -------------- + -------------------- + ------------- ------- + ---------- + | 4月1日23:59:59 | server1.mycompany ... | datetime =“ 2019-04 ... | 0 | | 4月1日23:59:59 | server2.mycompany ... | datetime =“ 2019-04 ... | 8589934592 | + -------------- + -------------------- + ------------- ------- + ---------- +
现在拆分为单独的数据帧。
headersDataFrame = rawDataFrameWithPrimaryKey.drop('kernel_logs')
detailsDataFrame = rawDataFrameWithPrimaryKey.drop('date') \
.drop('host')
headersDataFrame.show()
detailsDataFrame.show()
# Split the key/value pair column into an array
detailsDataFrame = detailsDataFrame.withColumn("kernel_logs", split("kernel_logs", "\" "))
detailsDataFrame.show()
+ -------------- + -------------------- + ---------- + |日期|主机| message_id | + -------------- + -------------------- + ---------- + |四月1 23:59:59 | server1.mycompany ... | 0 | | 4月1日 23:59:59 | server2.mycompany ... | 8589934592 | + -------------- + -------------------- + ---------- + >
+ -------------------- + ---------- + | kernel_logs | message_id | + -------------------- + ---------- + | [datetime =“ 2019-0 ... | 0 | || [datetime =“ 2019-0 ... | 8589934592 | + -------------------- + ---------- +
现在,我们需要为每个键/值对创建多个行。
from pyspark.sql.types import ArrayType, StructType, StructField, StringType
from pyspark.sql.functions import col, udf, explode
zip_ = udf(
lambda x: list(zip(x)),
ArrayType(StructType([
# Adjust types to reflect data types
StructField("first", StringType())
]))
)
explodedDetailsDataFrame = (detailsDataFrame
.withColumn("tmp", zip_("kernel_logs"))
# UDF output cannot be directly passed to explode
.withColumn("tmp", explode("tmp"))
.select("message_id", col("tmp.first").alias("keyvalue"))
)
让我们拆分键值列
split_col = split(explodedDetailsDataFrame["keyvalue"], '="')
detailsDataFrame = explodedDetailsDataFrame \
.withColumn("key", split_col.getItem(0)) \
.withColumn("value", split_col.getItem(1)) \
.drop("keyvalue")
detailsDataFrame.show(truncate=False)
+ ---------- + ----------------- + ----------------- --- + | message_id | key | value | + ---------- + ----------------- + -------------------- + | 0 |日期时间| 2019-04-02T05:50:11Z | | 0 |时间戳记
| 1554184211000 | | 0 |脚本
| ServerDataCollector | | 0 | processTimeCount | 35
| | 0 |名称|“测试位置”” | | 8589934592 |日期时间| 2019-04-02T05:50:11Z | | 8589934592 |时间戳记| 1554184211000 | | 8589934592 |脚本 | ClientDataCollector | | 8589934592 |名称|“测试位置2”” | | 8589934592 |正常运行时间| 23513 | | 8589934592 | faultsLast24Hours | 0“ | + ---------- + ----------------- + -------------------- +