Pyspark中使用窗口函数和透视表


一、处理json格式的hdfs日志

处理json格式的日志,常规方法是使用sc.textFile(hdfs_path)读数据,然后json.loads()将没一行json串解析成字典,但是解析json串时会产生pyton-spark的数据交互,这种交互的成本很大。spark2.0以上的版本支持spark.sql.read.json方法直接读取json格式,但是分布式文件系统的json日志往往是很不规整的,json串的key各有不同,甚至相同的key,值的属性也不一样(1000, “1000”),脏的数据会导致程序报错。

from pyspark.sql import SparkSession
from pyspark.sql.window import Window
from pyspark.sql import functions as F
from pprint import pprint
spark = SparkSession.builder.getOrCreate()
pprint(spark.sparkContext.getConf().getAll())
date = '17-12-14'
root_path = 'hdfs://hadoop-ha/user/st/new/'
hdfs_path = 'hdfs://hadoop-ha/api/%s/**/*.gz' % date
d = spark.sparkContext.textFile(hdfs_path)
df = spark.createDataFrame(d, 'string')

折中的办法是,sc.textFile将数据以rdd读入,不解析,直接将json串转化成dataframe的一列。不设置列名通常默认为value, 如上代码, 结果展示如下。

+--------------------+
|               value|
+--------------------+
|{"drag_count": 0,...|
|{"device_id": "d8...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
|{"drag_count": 0,...|
|{"salt": "PT.n", ...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
|{"device_id": "4e...|
|{"request_time": ...|
|{"request_time": ...|
|{"request_time": ...|
+--------------------+
only showing top 20 rows

二、spark.sql.function原生函数的使用

使用spark原生函数不会产生spark-python的数据交互。函数经过优化,能满足大部分数据处理要求。其中对于处理json字串的get_json_object可以从属性是json的列中取出给定的key值。具体用法如下,从value中取出captcha_id, type, client_type等列。json串中没有给定key的用null补充。
**注意:**嵌套的json格式,可用get_json_object('value', '$.insight.browserLanguage')取出想要的browserLanguage字段。

df = df.select('value',
               F.get_json_object('value', '$.captcha_id').alias('captcha_id'),
               F.get_json_object('value', "$.type").alias('type'),
               F.get_json_object('value', '$.client_type').alias('client_type'),
               F.get_json_object('value', '$.extra_parameter').alias('extra_parameter'),
               F.get_json_object('value', '$.challenge').alias('challenge'),
               F.get_json_object('value', '$.passtime').alias('passtime'),
               F.get_json_object('value', '$.random_parameter').alias('random_parameter'),
               F.get_json_object('value', '$.ip').alias('ip'))
               F.get_json_object('value', 
               '$.insight.browserLanguage').alias('browserLanguage').show()

部分列的结果如下

+--------------------+--------+--------------------+---------------+---------------+
|          captcha_id|is_robot|     extra_parameter|             ip|browserLanguage|
+--------------------+--------+--------------------+---------------+---------------+
|7c56e28e905f0c9f3...|   false|                null| 182.107.171.31|          zh-CN|
|2f5e33b3c37769a84...|   false|{"e":{"st":"TypeE...|171.117.120.102|          zh-CN|
|ffcd1599b3cf9a4cd...|   false|{"e":{"st":"TypeE...|113.195.145.173|          zh-CN|
|7ce498b2b35b1f4ba...|    true|                null|      92.42.8.1|          zh-CN|
|566e50cd4783b181c...|   false|                null| 113.129.239.38|          zh-CN|
|e0ebfb63e6ee7cf7e...|   false|{"ts":15131807856...| 217.209.224.60|          sv-SE|
|e0ebfb63e6ee7cf7e...|   false|{"ts":15131809173...|   80.188.248.1|          cs-CZ|
|f3da43872204b2aa7...|   false|{"ts":15131807886...|   218.31.53.74|          zh-CN|
|7c56e28e905f0c9f3...|   false|{"ts":15131807932...|    49.80.83.38|          zh-CN|
|942640dbfd390721d...|   false|{"ts":15131807858...| 176.112.71.202|             ru|
|942640dbfd390721d...|   false|{"e":{"na":"TypeE...|    93.77.88.20|          ru-RU|
|7c56e28e905f0c9f3...|   false|{"e":{"na":"TypeE...|  114.83.72.122|          zh-CN|
|2f5e33b3c37769a84...|   false|{"e":{"na":"TypeE...|   175.18.33.90|          zh-CN|
|e2b6156afcd010528...|   false|{"e":{"na":"TypeE...|  115.50.190.36|          zh-CN|
|2f5e33b3c37769a84...|   false|{"e":{"na":"TypeE...| 36.228.232.239|          zh-TW|
|79fbb5ed49aecd03a...|   false|{"e":{"ts":"Type"...|202.107.235.102|          zh-CN|
|0262326656f5c0426...|   false|{"e":{"ts":"Type"...|  120.84.10.137|          zh-CN|
|354d9d4cf14d2cf61...|   false|{"e":{"ts":"Type"...|114.226.122.173|          zh-CN|
|e0ebfb63e6ee7cf7e...|   false|{"e":{"ts":"Type"...|   99.203.5.155|          en-US|
|7ce498b2b35b1f4ba...|   false|{"e":{"ts":"Type"...|  106.61.20.253|          zh-CN|
+--------------------+--------+--------------------+---------------+---------------+
only showing top 20 rows

其他spark内置函数如:sumcollect_listcountcollect_setcountDistinct等。详情请查询spark.sql.function源码。

三、窗口函数

我们通常使用groupby的时候想保留原有的列,但这在现有sql数据库查询中不能实现(最新的版本可能有)。窗口函数能帮我们达到这样的要求。
下面,我们想统计每个captcha_id出现的次数,并且我们还想保留is_robot列。如果不使用窗口函数,代码如下:

key = 'captcha_id'
dd1 = df.select('is_robot',
                F.get_json_object('value', '$.%s' % key).alias(key)
                ). \
    dropna(subset=[key]). \
    groupby(key).count().cache()

groupby结果:

+--------------------+--------------+
|          captcha_id|captcha_id_cnt|
+--------------------+--------------+
|05e7077f462b6523b...|         21950|
|1c3f91720d7a7f604...|           829|
|226ed230f305b2f3a...|            10|
|25f06d5dabe2d6e9e...|            23|
|2ebd463b285527c3c...|           126|
|383dc849256f9aee3...|           261|
|38d66226567502a5b...|         17107|
|3a5bb578740870c98...|            12|
|482ac913535ce51db...|           383|
|6f51163b6c7c1f992...|            17|
|721b245d5f3ce5074...|             8|
|857f1cc63d82c9865...|            13|
|999db8e10d6ff2d49...|            14|
|9f857f36da444bc44...|           385|
|a54b9a641ea7851a2...|             1|
|c4a8c2848cde61b2e...|            12|
|c89a27ba59dfd5b94...|             3|
|d8ba661bf6713fb7c...|            35|
|e37813b5915b66861...|           303|
|e7760e57a18d33453...|             5|
+--------------------+--------------+
only showing top 20 rows

聚合后结果中并没有保留is_robot列,而使用窗口函数

key = 'captcha_id'
w = Window().partitionBy(key)
dd1 = df.select('is_robot',
                F.get_json_object('value', '$.%s' % key).alias(key)
                ). \
    dropna(subset=[key]). \
    withColumn('%s_cnt' % key, F.count(key).over(w)).cache()

结果如下:

+--------+--------------------------------+--------------+                      
|is_robot|captcha_id                      |captcha_id_cnt|
+--------+--------------------------------+--------------+
|false   |006129a83fab1b5d22bd5e29142a6c22|2             |
|false   |006129a83fab1b5d22bd5e29142a6c22|2             |
|false   |025a98cc521ac1dcd04188e0f476ce50|2             |
|false   |025a98cc521ac1dcd04188e0f476ce50|2             |
|false   |033f3f9bf8edd87e4e24eedbcc82ab7e|2             |
|false   |033f3f9bf8edd87e4e24eedbcc82ab7e|2             |
|false   |044830f05a9be3df7cacf9a7dab40545|2             |
|false   |044830f05a9be3df7cacf9a7dab40545|2             |
|false   |0a6245de5a490d86aa4f99df9be9f0ef|2             |
|false   |0a6245de5a490d86aa4f99df9be9f0ef|2             |
|false   |0b58d8fd7f1c16366dfa6a33e1eaa7b7|2             |
|false   |0b58d8fd7f1c16366dfa6a33e1eaa7b7|2             |
|false   |0b9a7d096047da61c666308fdf4c38d9|2             |
|true    |0b9a7d096047da61c666308fdf4c38d9|2             |
|false   |0c332e47bd1a24698f2cd3b3d705b7d3|2             |
|false   |0c332e47bd1a24698f2cd3b3d705b7d3|2             |
|false   |0c414d0aa3f7f062d7a64675702f4d67|2             |
|false   |0c414d0aa3f7f062d7a64675702f4d67|2             |
|false   |0c52c51518126029f726e2db7bc5d270|2             |
|false   |0c52c51518126029f726e2db7bc5d270|2             |
+--------+--------------------------------+--------------+
only showing top 20 rows

四、透视表

透视表将长格式转化成宽格式(将一列或多列的值变成列名)。

    pivot_df = dd1.groupBy(key). \
        pivot("is_robot", [True, False]).count(). \
        na.fill({"True": 0, "False": 0}).show()

结果,将is_robot这一列中的两个值true, false变成列名,其中每一列表示个数,如下:

+--------------------+----+-----+
|          captcha_id|true|false|
+--------------------+----+-----+
|1c3f91720d7a7f604...|   0|  118|
|25f06d5dabe2d6e9e...|   0|    3|
|38d66226567502a5b...|   3| 2698|
|482ac913535ce51db...|   0|    3|
|857f1cc63d82c9865...|   0|    1|
|9f857f36da444bc44...|   0|    8|
|e37813b5915b66861...|   0|   27|
|f65a04a92afd93b2b...|   0|   23|
|162c5103ed2526d1d...|   0| 3906|
|2eebc5b805cf6a2d5...|   0|   14|
|36919cf9b5065b216...|   0|   69|
|42c9f3cd76ae1dd85...|   0|   97|
|5d1c5adc14739af22...|   0|   36|
|7b2078c619e679f9f...|   0|   52|
|7b60ad0dd03c22813...|  11| 8004|
|858b9ebf1412700d3...|   0|    9|
|8c8be929eeddf30e0...|   0|   41|
|cc420f2055d0244ad...|   0|   12|
|d9de4e84eb3baba99...|   0|   12|
|dd8557354686547d5...|   0|   10|
+--------------------+----+-----+
only showing top 20 rows

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Polars编写数据透视表时,您可以按照以下步骤进行操作: 1. 导入所需的库和数据:首先,您需要导入Polars库并加载您的数据集。例如,使用`import polars as pl`来导入Polars库,并使用`pl.read_csv('your_data.csv')`来加载您的数据集。 2. 创建Polars DataFrame:使用加载的数据创建一个Polars DataFrame。例如,您可以将加载的数据赋值给一个变量,如`df = pl.read_csv('your_data.csv')`。 3. 执行数据透视操作:使用Polars提供的函数进行数据透视操作。例如,您可以使用`df.pivot(['column1', 'column2'])`来指定要作为行和列的列,并执行数据透视。 4. 指定聚合函数:您还可以指定要应用于透视的值的聚合函数。例如,您可以使用`.select('column3').mean()`来计算'column3'列的平均值。 5. 可选的附加操作:根据需要,您可以执行其他操作,如筛选特定行或列,排序结果等。 6. 显示结果:最后,您可以使用`.show()`函数来显示结果。 下面是一个示例代码片段,展示了如何使用Polars编写数据透视表: ```python import polars as pl # 导入数据 df = pl.read_csv('your_data.csv') # 执行数据透视操作 pivot_table = df.pivot(['column1', 'column2']) # 指定聚合函数 result = pivot_table.select('column3').mean() # 显示结果 result.show() ``` 请根据您的具体数据和需求进行适当的调整和配置。希望这可以帮助到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值