Calculating the averages for each KEY in a Pairwise (K,V) RDD in Spark with Python
我想与Python解决方案共享这个特定的Apache Spark,因为它的文档非常有限。
我想通过KEY计算K / V对的平均值(存储在Pairwise RDD中)。 示例数据如下所示:
1 2 3 4 5 6 7 8 9 10 11 | >>> rdd1.take(10) # Show a small sample. [(u'2013-10-09', 7.60117302052786), (u'2013-10-10', 9.322709163346612), (u'2013-10-10', 28.264462809917358), (u'2013-10-07', 9.664429530201343), (u'2013-10-07', 12.461538461538463), (u'2013-10-09', 20.76923076923077), (u'2013-10-08', 11.842105263157894), (u'2013-10-13', 32.32514177693762), (u'2013-10-13', 26.249999999999996), (u'2013-10-13', 10.693069306930692)] |
现在,以下代码序列并不是达到最佳效果的方法,但它确实有效。 这是我在寻找更好的解决方案之前所做的事情。 这并不可怕,但是-如您将在答案部分中看到的-有一种更简洁,有效的方法。
1 2 3 4 5 6 7 8 9 | >>> import operator >>> countsByKey = sc.broadcast(rdd1.countByKey()) # SAMPLE OUTPUT of countsByKey.value: {u'2013-09-09': 215, u'2013-09-08': 69, ... snip ...} >>> rdd1 = rdd1.reduceByKey(operator.add) # Calculate the numerators (i.e. the SUMs). >>> rdd1 = rdd1.map(lambda x: (x[0], x[1]/countsByKey.value[x[0]])) # Divide each SUM by it's denominator (i.e. COUNT) >>> print(rdd1.collect()) [(u'2013-10-09', 11.235365503035176), (u'2013-10-07', 23.39500642456595), ... snip ... ] |
现在,更好的方法是使用
这是使用
通过KEY,同时计算SUM(我们要计算的平均值的分子)和COUNT(我们要计算的平均值的分母):
1 2 3 | >>> aTuple = (0,0) # As of Python3, you can't pass a literal sequence to a function. >>> rdd1 = rdd1.aggregateByKey(aTuple, lambda a,b: (a[0] + b, a[1] + 1), lambda a,b: (a[0] + b[0], a[1] + b[1])) |
关于上面每个
1 2 3 4 5 6 7 | First lambda expression for Within-Partition Reduction Step:: a: is a TUPLE that holds: (runningSum, runningCount). b: is a SCALAR that holds the next Value Second lambda expression for Cross-Partition Reduction Step:: a: is a TUPLE that holds: (runningSum, runningCount). b: is a TUPLE that holds: (nextPartitionsSum, nextPartitionsCount). |
最后,计算每个KEY的平均值,并收集结果。
1 2 3 4 5 6 7 8 | >>> finalResult = rdd1.mapValues(lambda v: v[0]/v[1]).collect() >>> print(finalResult) [(u'2013-09-09', 11.235365503035176), (u'2013-09-01', 23.39500642456595), (u'2013-09-03', 13.53240060820617), (u'2013-09-05', 13.141148418977687), ... snip ... ] |
我希望这个
在我看来,与具有两个lambda的aggregateByKey相比,更具可读性的是:
1 2 3 | rdd1 = rdd1 \\ .mapValues(lambda v: (v, 1)) \\ .reduceByKey(lambda a,b: (a[0]+b[0], a[1]+b[1])) |
这样,整个平均计算将为:
1 2 3 4 5 | avg_by_key = rdd1 \\ .mapValues(lambda v: (v, 1)) \\ .reduceByKey(lambda a,b: (a[0]+b[0], a[1]+b[1])) \\ .mapValues(lambda v: v[0]/v[1]) \\ .collectAsMap() |
只需添加有关此问题的直观且简短(但不好)的解决方案的注释即可。 Sam的《 24小时自学Apache Spark》一书在上一章中很好地解释了这个问题。
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | rdd = sc.parallelize([ (u'2013-10-09', 10), (u'2013-10-09', 10), (u'2013-10-09', 13), (u'2013-10-10', 40), (u'2013-10-10', 45), (u'2013-10-10', 50) ]) rdd \\ .groupByKey() \\ .mapValues(lambda x: sum(x) / len(x)) \\ .collect() |
输出:
1 | [('2013-10-10', 45.0), ('2013-10-09', 11.0)] |
这是直观且吸引人的,但不要使用它!
尽可能避免使用
稍微增强了prismalytics.io的答案。
在某些情况下,由于我们正在对大量值求和,因此计算总和可能会溢出数。相反,我们可以保留平均值,并继续根据平均值计算平均值,并且减少两部分的计数。
如果您有两个部分的平均值为(a1,c1)和(a2,c2),则总体平均值为:
总数/计数=(总数1 +总数2)/(计数1 +计数2)=(a1 * c1 + a2 * c2)/(c1 + c2)
如果我们标记R = c2 / c1,则可以将其进一步重写为a1 /(1 + R)+ a2 * R /(1 + R)
如果我们进一步将Ri标记为1 /(1 + R),则可以将其写为a1 * Ri + a2 * R * Ri
1 2 3 4 5 6 7 8 9 10 | myrdd = sc.parallelize([1.1, 2.4, 5, 6.0, 2, 3, 7, 9, 11, 13, 10]) sumcount_rdd = myrdd.map(lambda n : (n, 1)) def avg(A, B): R = 1.0*B[1]/A[1] Ri = 1.0/(1+R); av = A[0]*Ri + B[0]*R*Ri return (av, B[1] + A[1]); (av, counts) = sumcount_rdd.reduce(avg) print(av) |
只需使用mapValues而不是map和reduceByKey而不是reduce,就可以将该方法转换为键值。
这是从:https://www.knowbigdata.com/blog/interview-questions-apache-spark-part-2