如何在PySpark数据框中为每个键计算百分位数?

11

我有一个PySpark数据框,它由三列x、y和z组成。

在这个数据框中,x可能有多行。我该如何分别计算x中每个键的百分位数?

+------+---------+------+
|  Name|     Role|Salary|
+------+---------+------+
|   bob|Developer|125000|
|  mark|Developer|108000|
|  carl|   Tester| 70000|
|  carl|Developer|185000|
|  carl|   Tester| 65000|
| roman|   Tester| 82000|
| simon|Developer| 98000|
|  eric|Developer|144000|
|carlos|   Tester| 75000|
| henry|Developer|110000|
+------+---------+------+
所需输出:

输出结果:

+------+---------+------+---------+
|  Name|     Role|Salary|      50%|
+------+---------+------+---------+
|   bob|Developer|125000|117500.0 |
|  mark|Developer|108000|117500.0 |
|  carl|   Tester| 70000|72500.0  |
|  carl|Developer|185000|117500.0 |
|  carl|   Tester| 65000|72500.0  |
| roman|   Tester| 82000|72500.0  |
| simon|Developer| 98000|117500.0 |
|  eric|Developer|144000|117500.0 |
|carlos|   Tester| 75000|72500.0  |
| henry|Developer|110000|117500.0 |
+------+---------+------+---------+

@,我不知道输出会是什么样子,我只是想把角色分成几个范围。我所说的分割是指以开发人员为例,将其分成[0-25%的开发人员工资],然后是[25-50%],[50-75%]和[75%-100%]。我这样做是因为我有一个巨大的数据框,我需要知道(过滤)例如高薪的开发人员等。 - bib
你需要花时间弄清楚你想要的输出是什么,并详细描述它。根据你最近的评论和问题所询问的,似乎存在两个不同的事情。就目前而言,这个问题可能会被关闭为“过于广泛”或“不清楚你在问什么”。 - pault
你可能想要了解在Spark中QuantileDiscretizer和Bucketizer的区别。请查看以下链接:[https://dev59.com/QFcQ5IYBdhLWcg3wEPoK] - pault
@pault,感谢您提供的链接建议。也许我表达不够清楚。 - bib
3个回答

24

尝试使用 groupby + F.expr

import pyspark.sql.functions as F

df1 = df.groupby('Role').agg(F.expr('percentile(Salary, array(0.25))')[0].alias('%25'),
                             F.expr('percentile(Salary, array(0.50))')[0].alias('%50'),
                             F.expr('percentile(Salary, array(0.75))')[0].alias('%75'))
df1.show()

输出:

+---------+--------+--------+--------+
|     Role|     %25|     %50|     %75|
+---------+--------+--------+--------+
|   Tester| 68750.0| 72500.0| 76750.0|
|Developer|108500.0|117500.0|139250.0|
+---------+--------+--------+--------+

现在,您可以将df1与原始数据框联接:

df.join(df1, on='Role', how='left').show()

输出:

+---------+------+------+--------+--------+--------+
|     Role|  Name|Salary|     %25|     %50|     %75|
+---------+------+------+--------+--------+--------+
|   Tester|  carl| 70000| 68750.0| 72500.0| 76750.0|
|   Tester|  carl| 65000| 68750.0| 72500.0| 76750.0|
|   Tester| roman| 82000| 68750.0| 72500.0| 76750.0|
|   Tester|carlos| 75000| 68750.0| 72500.0| 76750.0|
|Developer|   bob|125000|108500.0|117500.0|139250.0|
|Developer|  mark|108000|108500.0|117500.0|139250.0|
|Developer|  carl|185000|108500.0|117500.0|139250.0|
|Developer| simon| 98000|108500.0|117500.0|139250.0|
|Developer|  eric|144000|108500.0|117500.0|139250.0|
|Developer| henry|110000|108500.0|117500.0|139250.0|
+---------+------+------+--------+--------+--------+

谢谢您的回答,这正是我所需要的,只有一个小区别,如何直接获取附加到原始数据框的值。请查看更新的帖子。 - bib
一种选择是将结果数据框与原始数据框进行join操作。另一种选择可能是使用window函数。请检查更新的答案 :) - Ala Tarighati

5

array并不是必须的:

F.expr('percentile(Salary, 0.5)')

结合窗口函数,它可以完成该任务:

df = df.withColumn('50%', F.expr('percentile(Salary, 0.5)').over(W.partitionBy('Role')))

df.show()
#  +------+---------+------+--------+
#  |  Name|     Role|Salary|     50%|
#  +------+---------+------+--------+
#  |   bob|Developer|125000|117500.0|
#  |  mark|Developer|108000|117500.0|
#  |  carl|Developer|185000|117500.0|
#  | simon|Developer| 98000|117500.0|
#  |  eric|Developer|144000|117500.0|
#  | henry|Developer|110000|117500.0|
#  |  carl|   Tester| 70000| 72500.0|
#  |  carl|   Tester| 65000| 72500.0|
#  | roman|   Tester| 82000| 72500.0|
#  |carlos|   Tester| 75000| 72500.0|
#  +------+---------+------+--------+

谢谢您分享这个解决方案,我认为它非常整洁,可能比我的“groupby+join”解决方案更快。 - Ala Tarighati

2

这个函数适用于整个列。我正在寻找每个键的百分位数。 - bib
在对列x进行groupBy之后,使用@bib函数? - Ben.T

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接