我有以下PySpark数据帧(例如df
)。它具有列name
,timestamp
,category
和value
的列。
+------+-------------------+--------+-----+
| name| timestamp|category|value|
+------+-------------------+--------+-----+
| name1|2019-01-17 00:00:00| A|11.23|
| name2|2019-01-17 00:00:00| A|14.57|
| name3|2019-01-10 00:00:00| B| 2.21|
| name4|2019-01-10 00:00:00| B| 8.76|
| name5|2019-01-17 00:00:00| A|18.71|
| name6|2019-01-10 00:00:00| A|17.78|
| name7|2019-01-10 00:00:00| A| 5.52|
| name8|2019-01-10 00:00:00| A| 9.91|
| name9|2019-01-17 00:00:00| B| 1.16|
|name10|2019-01-17 00:00:00| B| 12.0|
+------+-------------------+--------+-----+
我想在上述数据框中添加一个新列,以使我在包含相同category
和{{的成员的分布中的每个名称的值1}}。
我的预期输出如下:
timestamp
什么是最好的方法?
我尝试了以下方法:
+------+-------------------+--------+-----+---------+
|name |timestamp |category|value|pct_value|
+------+-------------------+--------+-----+---------+
|name1 |2019-01-17 00:00:00|A |11.23|1 |
|name10|2019-01-17 00:00:00|B |12.0 |2 |
|name2 |2019-01-17 00:00:00|A |14.57|2 |
|name3 |2019-01-10 00:00:00|B |2.21 |1 |
|name4 |2019-01-10 00:00:00|B |8.76 |2 |
+------+-------------------+--------+-----+---------+
only showing top 5 rows
这将给出正确的预期输出。但是,当我对具有数百万行的实际数据进行尝试时,此方法将花费很长时间(几小时)。
您可以使用下面提到的代码生成上面给定的数据帧(import pyspark.sql.functions as F
from pyspark.sql import Window as W
w_cat = W.partitionBy('category', 'timestamp').orderBy("value")
df_new = ( df.select( '*', F.ntile(1000).over(w_cat).alias( 'pct_value' ) ) ).persist()
df_new.orderBy('name', 'timestamp').show(5,False)
)
df
答案 0 :(得分:1)
您可以尝试使用percentile_approx
功能。
from pyspark.sql import Window
import pyspark.sql.functions as F
grp_window = Window.partitionBy('name')
# For median, i.e. 0.5 quantile
magic_percentile = F.expr('percentile_approx(val, 0.5)')
df.withColumn('pct_value', magic_percentile.over(grp_window))
# OR
df.groupBy('name').agg(magic_percentile.alias('pct_value'))
您也可以使用percent_rank
函数:
df.select('pct_value', percent_rank().over(w).alias("percentile"))\
.where('percentile == 0.6').show()
您还可以传递一组百分位数,但是这里的问题是您将获得list
作为回报:
quantiles = F.expr('percentile_approx(val, array(0.25, 0.5, 0.75))')