通过spark(python)中的MapReduce理解组

时间:2016-01-19 10:06:13

标签: python apache-spark mapreduce pyspark rdd

我正在尝试一个小程序,我正在考虑一个员工数据集,并试图计算在各个部门分配的工资总和。我有一个可重复的例子。

 emp_list=[(u'ACC', [u'101', u'a', u'ACC', u'1000']),
 (u'SALES', [u'102', u'b', u'SALES', u'2000']),
 (u'IT', [u'103', u'c', u'IT', u'3000']),
 (u'ACC', [u'104', u'd', u'ACC', u'4000']),
 (u'ACC', [u'105', u'e', u'ACC', u'5000']),
 (u'HR', [u'106', u'f', u'HR', u'6000']),
 (u'ACC', [u'107', u'g', u'ACC', u'7000']),
 (u'FIN', [u'108', u'h', u'FIN', u'8000']),
 (u'ACC', [u'109', u'k', u'ACC', u'9000']),
 (u'HR', [u'1010', u'l', u'HR', u'10000']),
 (u'ACC', [u'1011', u'm', u'ACC', u'11000']),
 (u'ACC', [u'1012', u'n', u'ACC', u'12000']),
 (u'FIN', [u'1013', u'o', u'FIN', u'13000']),
 (u'IT', [u'1014', u'p', u'IT', u'14000'])]

emp=sc.parallelize(emp_list)

emp.reduceByKey(lambda x,y : x[3]+y[3]).take(10)

我得到的输出是:

[(u'ACC', u'00'),
 (u'HR', u'600010000'),
 (u'FIN', u'800013000'),
 (u'SALES', [u'102', u'b', u'SALES', u'2000']),
 (u'IT', u'300014000')]

任何人都可以解释一下我为什么会为ACCSALES部门获取奇怪的价值观。我想看看这两个人的联合工资。

3 个答案:

答案 0 :(得分:4)

您会得到奇怪的值,因为您的函数逻辑无效。如果你使用Scala而不是Python,那么甚至不会编译。当您应用reduceByKey LHS和RHS时,返回类型应为相同类型:

reduceByKey(func: (V, V) ⇒ V): RDD[(K, V)]

func应该是关联的。

在您的情况下,类型不匹配(输入是列表,返回类型是字符串),并且函数不是关联的。要了解发生了什么,可以考虑两种不同的情况:

  1. 每个键只有一个值。由于未应用func,因此您将此值作为输出。因此(u'SALES', [u'102', u'b', u'SALES', u'2000'])

  2. 每个键有多个值。让我们从ACC中取一个值的子集作为示例,并假设操作顺序定义如下

    (
      # 1st partition
      ([u'101', u'a', u'ACC', u'1000'], [u'104', u'd', u'ACC', u'4000']),
      # 2nd partition
      ([u'105', u'e', u'ACC', u'5000'], [u'107', u'g', u'ACC', u'7000'])
    )
    

    第一次申请func后,我们得到:

    (
       u'10004000',
       ([u'105', u'e', u'ACC', u'5000'], [u'107', u'g', u'ACC', u'7000'])
    )
    

    第二次申请func之后我们得到了

    (
       u'10004000',
       u'50007000'
    )
    

    最后

    u'00'
    

    在实践中,括号可能因配置而异,因此您可以获得不同的输出。

  3. 要获得正确的结果,您应该使用@alexs建议的aggregateByKey / combineByKeymap + reducemap后跟groupByKey }和mapValues。最后一个应该是最有效的方法,因为它不需要中间对象:

    emp.mapValues(lambda x: x[3]).groupByKey().mapValues(lambda xs: "".join(xs))
    

    使用aggregateByKey参考同样的事情:

    from operator import add
    
    rdd.aggregateByKey("", lambda acc, x: acc + x[3], add)
    

答案 1 :(得分:1)

也许这应该有效:

emp.map(lambda k, v: (k, v[3])).reduceByKey(lambda x,y : x+y).take(10)

虽然我没有Spark实例来试试

答案 2 :(得分:1)

你需要记住,当你减少时,减少的元素需要在减少第一次看到它们时以及所有其他时间保持相同的结构。所以这是在你的例子中做到这一点的方法:

emp_list=[(u'ACC', [u'101', u'a', u'ACC', u'1000']),
 (u'SALES', [u'102', u'b', u'SALES', u'2000']),
 (u'IT', [u'103', u'c', u'IT', u'3000']),
 (u'ACC', [u'104', u'd', u'ACC', u'4000']),
 (u'ACC', [u'105', u'e', u'ACC', u'5000']),
 (u'HR', [u'106', u'f', u'HR', u'6000']),
 (u'ACC', [u'107', u'g', u'ACC', u'7000']),
 (u'FIN', [u'108', u'h', u'FIN', u'8000']),
 (u'ACC', [u'109', u'k', u'ACC', u'9000']),
 (u'HR', [u'1010', u'l', u'HR', u'10000']),
 (u'ACC', [u'1011', u'm', u'ACC', u'11000']),
 (u'ACC', [u'1012', u'n', u'ACC', u'12000']),
 (u'FIN', [u'1013', u'o', u'FIN', u'13000']),
 (u'IT', [u'1014', u'p', u'IT', u'14000'])]

emp=sc.parallelize(emp_list)
print emp.reduceByKey(lambda x,y : (1,1,1,x[3] + y[3]))\
.map(lambda x: (x[0], x[1][3])).collect()
>> [(u'ACC', u'100040005000700090001100012000'), (u'HR', u'600010000'),\
 (u'FIN', u'800013000'), (u'SALES', u'2000'), (u'IT', u'300014000')]