要回答这个问题,我们必须首先考虑 map/reduce 非常适合的问题类型。map/reduce 的最佳问题是那些可以分解为可以单独解决的小子问题的问题。考虑此类问题的一个很好的类比可能是考虑 SQLGROUP BY
构造,它有效地将结果集分成多个块并在每个块上计算一个聚合函数:如果您可以想象通过 a GROUP BY
(尽管数据集大小)解决问题,那么它可能非常适合 map/reduce。
您的具体问题需要将数据划分为地理空间区域,然后为每个区域计算某种聚合。然后,您会将这些区域渲染为可叠加在 Google 地图上的二维平铺图像。
解决此问题的一种自然方法是从一个map
函数开始,该函数接受来自数据源的行流,其中包括地理空间点(纬度/经度)和计数。函数的约定map
是发出表单的元组(key, value)
,因此在这种情况下,您的映射器需要“简化”该点以创建一个键——也就是说,降低其准确性,以便几个相邻点将共享相同的值-- 并将该值与当前点的人口一起返回。这是一些伪代码:
function map(row):
key = simplify_point(row.point) # implementation of this function TBD by you
emit(key, row.population_count)
这将生成一个包含以下项目的中间数据集:
| key | value |
| 37.78,-122.43 | 2303 |
| 37.78,-122.43 | 2009 |
| 37.78,-122.43 | 3001 |
| 37.78,-122.43 | 1238 |
| 37.79,-122.43 | 1343 |
| 37.79,-122.43 | 3005 |
| 37.79,-122.43 | 2145 |
| 37.79,-122.43 | 1536 |
请注意,每个不同的键现在都有多个与其关联的值。该reduce
函数的任务是获取一组具有相同键的值,并生成一个代表整个数据组的单个值。在不知道您手头问题的细节的情况下,我将假设确定每个组中的总人口就足够了,我们可以通过简单地将所有值相加来实现。一个reduce
函数接收一个键和一个在映射器输出中具有该键的所有值的列表,因此我们的 reducer 可以看起来像这样简单(再次,在伪代码中):
function reduce(key, population_counts):
sum = 0
for value in population_counts:
sum = sum + value
emit(key, sum)
对于上面的示例结果集,这将导致以下最终结果:
| key | value |
| 37.78,-122.43 | 8551 |
| 37.79,-122.43 | 8029 |
然后,您可以获取这组较小的点和值,并将它们渲染为地图上不同颜色的区域,从而创建可视化热图。
尽管为了简单起见,我在这里使用了简单的整数计数,但实际上任何类型都可以用作值,因此您可以使用特定类或数组的实例,或者在给定单行数据时可以生成的任何其他值一次。在您的屏幕截图中,您显示了一个悬停提示,它给出了合并以产生给定数据点的记录数,您可以通过让减速器不仅求和而且同时计算行数,并在某种对象中一起返回两者或数据结构。
上面概述了 map/reduce 操作的逻辑工作流程,并描述了一种使用 map/reduce 创建热图的方法。我确定我没有完全解决您的问题,但是如果您可以在我上面描述的工作流程中界定您的问题,那么它可能非常适合 map/reduce 解决方案。我还专注于 map/reduce 的理论,而不是 Hadoop 中的具体实现,但希望您可以轻松地将我描述的概念映射到 Hadoop 提供的构造上。