Apache spark pyspark rdd采用最小年龄的最大频率

Apache spark pyspark rdd采用最小年龄的最大频率,apache-spark,pyspark,count,rdd,reduce,Apache Spark,Pyspark,Count,Rdd,Reduce,我有一个rdd,如下所示: [{'age': 2.18430371791803, 'code': u'"315.320000"', 'id': u'"00008RINR"'}, {'age': 2.80033330216659, 'code': u'"315.320000"', 'id': u'"00008RINR"'}, {'age': 2.8222365762732, 'code': u'"315.320000"', 'id': u'"00008RINR"'},

我有一个rdd,如下所示:

[{'age': 2.18430371791803,
  'code': u'"315.320000"',
  'id': u'"00008RINR"'},
 {'age': 2.80033330216659,
  'code': u'"315.320000"',
  'id': u'"00008RINR"'},
 {'age': 2.8222365762732,
  'code': u'"315.320000"',
  'id': u'"00008RINR"'},
 {...}]
rdd1.map(lambda x: (x[1][2],-x[1][0])).reduceByKey(lambda x,y: x+y).collect()
# [('"388.400000"', 2)]
我试图通过使用以下代码获取最高频率的代码,将每个id减少到仅1条记录:

rdd.map(lambda x: (x["id"], [(x["age"], x["code"])]))\
.reduceByKey(lambda x, y: x + y)\
.map(lambda x: [i[1] for i in x[1]])\
.map(lambda x: [max(zip((x.count(i) for i in set(x)), set(x)))])

这个实现有一个问题,它不考虑年龄,所以如果例如一个ID有多个频率为2的代码,它将占用最后一个代码。 为了说明这个问题,请考虑这个减少的ID:

(u'"000PZ7S2G"',
 [(4.3218651186303, u'"388.400000"'),
  (4.34924421126357, u'"388.400000"'),
  (4.3218651186303, u'"389.900000"'),
  (4.34924421126357, u'"389.900000"'),
  (13.3667102491139, u'"794.310000"'),
  (5.99897016368982, u'"995.300000"'),
  (6.02634923989903, u'"995.300000"'),
  (4.3218651186303, u'"V72.19"'),
  (4.34924421126357, u'"V72.19"'),
  (13.3639723398581, u'"V81.2"'),
  (13.3667102491139, u'"V81.2"')])
我的代码将输出:

[(2, u'"V81.2"')]
当我希望它输出时:

[(2, u'"388.400000"')]
因为虽然这两种代码的频率相同,但代码388.400000的年龄较小,并且最先出现

通过在.reduceByKey()之后添加此行:

我可以过滤掉那些年龄大于最小年龄的人,但我只考虑那些年龄最小的人,而不是所有的代码来计算他们的频率。在[max(zip((x,i(i)在集合(x)),集合(x))]中,当集合(x)是x(1)的集合时,我不能应用相同的/相似的逻辑,它不考虑年龄。

我应该补充一点,我不想只使用频率最高的第一个代码,我希望使用频率最高的代码,使用最少的时间,或者使用第一个出现的代码,如果可能的话,只使用rdd操作

在SQL中,我尝试获取的等效代码如下:

SELECT code, count(*) as code_frequency
FROM (SELECT id, code, age
FROM (SELECT id, code, MIN(age) AS age, COUNT(*) as cnt,
             ROW_NUMBER() OVER (PARTITION BY id ORDER BY COUNT(*) DESC, MIN(age)) as seqnum
      FROM tbl
      GROUP BY id, code
     ) t
WHERE seqnum = 1) a
GROUP BY code
ORDER by code_frequency DESC
LIMIT 5;
作为DF(尽管试图避免这种情况):


我非常感谢您的帮助。

如果可以选择将rdd转换为数据帧,我认为这种方法可以解决您的问题:

from pyspark.sql.functions import row_number, col
from pyspark.sql import Window
df = rdd.toDF()
w = Window.partitionBy('id').orderBy('age')
df = df.withColumn('row_number', row_number.over(w)).where(col('row_number') == 1).drop('row_number')
基于代码的SQL等价物,我将逻辑转换为以下rdd1加上一些后处理(从原始RDD开始):

但是,如果要查找所有
id
s的总和(计数),请执行以下操作:

[{'age': 2.18430371791803,
  'code': u'"315.320000"',
  'id': u'"00008RINR"'},
 {'age': 2.80033330216659,
  'code': u'"315.320000"',
  'id': u'"00008RINR"'},
 {'age': 2.8222365762732,
  'code': u'"315.320000"',
  'id': u'"00008RINR"'},
 {...}]
rdd1.map(lambda x: (x[1][2],-x[1][0])).reduceByKey(lambda x,y: x+y).collect()
# [('"388.400000"', 2)]

这看起来像是它的复制品,这是一个类似的问题,具有非常不同的解决方案,其中没有一个是在python中使用rdd操作的。@mad-a,您的问题有点令人困惑。在等效的SQL代码中,计算了最小值(年龄),但从未在任何逻辑中使用。最终的聚合听起来是在
id
s之间合并计算。如果您能为示例数据提供至少一个不同的
id
和预期结果,这将很有帮助。@jxc Hi,很抱歉,我忘了在id分区内的order by语句中在count(*)desc之后添加min(age)。实质上,;如果有两个代码具有相同的计数,我希望将seqnum=1分配给最小年龄的行。我尝试只使用rdd操作。但我也不确定你的代码是否能正常工作,我会编辑op以包含df版本。非常感谢,你真是个英雄!感谢您将其分解并如此简洁地解释。:)
sorted(rdd1.map(lambda x: (x[1][2],1)).countByKey().items(), key=lambda y: -y[1])
# [('"388.400000"', 1)]
rdd1.map(lambda x: (x[1][2],-x[1][0])).reduceByKey(lambda x,y: x+y).collect()
# [('"388.400000"', 2)]