减少和计数的结果在pyspark中有所不同

时间:2015-06-10 12:52:19

标签: python hadoop apache-spark

对于我的火花试验,我已经下载了NY taxi csv files并将它们合并到一个文件nytaxi.csv中。然后我把它保存在hadoop fs中。我正在使用带有7个节点管理器的火花。

我正在连接Ipython笔记本电脑。

这是一个示例python脚本,用于计算nytaxi.csv中的行数。

code

返回73491693。 但是,当我尝试通过以下代码计算行数时,它返回一个大约803000的值。

nytaxi=sc.textFile("hdfs://bigdata6:8020/user/baris/nytaxi/nytaxi.csv")
filtered=nytaxi.filter(lambda x:"distance" not in x)
splits = filtered.map(lambda x: float(x.split(",")[9]))
splits.cache()
splits.count()

我想知道为什么结果会有所不同。 感谢

来自csv的示例行: u'740BD5BE61840BE4FE3905CC3EBE3E7E,E48B185060FB0FF49BE6DA43E69E624B,CMT,1,N,2013-10-01 12:44:29,2013-10-01 12:53:26,1,536,1.20,-73.974319,40.741859,-73.99115,40.742424'

3 个答案:

答案 0 :(得分:2)

RDD.reduce()的文档说:

  

使用指定的可交换和关联二元运算符减少此RDD的元素。

def plusOne(sum, v): return sum + 1不是可交换的。它完全忽略了其中一个参数。所以你看到了未定义的行为。 (我建议考虑为什么函数必须是可交换的。如果你理解这一点,你就会更好地理解Spark!)

解决方案是使用RDD.count()代替。但如果你坚持使用reduce(),你就会这样做:

def count(rdd):
  return rdd.map(lambda x: 1).reduce(lambda a, b: a + b)

答案 1 :(得分:2)

问题在于Daniel指出,reduce中使用的操作必须是关联的和可交换的。 Here's the reason from the source itself

val reducePartition: Iterator[T] => Option[T] = iter => {
  if (iter.hasNext) {
    Some(iter.reduceLeft(cleanF))
  } else {
    None
  }
}

请注意,在每个分区上执行的reduce是一个简单的委托给它的迭代器reduceLeft。这不会导致任何问题,因为它只是价值的积累。

val mergeResult = (index: Int, taskResult: Option[T]) => {
  if (taskResult.isDefined) {
    jobResult = jobResult match {
      case Some(value) => Some(f(value, taskResult.get))
      case None => taskResult
    }
  }
}

但是,分区的合并是个问题。以下是您在示例中如何分解的假设(假设在4个均匀分割的分区上有40个计数):

A = 10; B = 10; C = 10; D = 10 //Local reductions. Everything ok
A added in = 10 //Still ok
B added in = f(10, 10) = 11 //Because your definition of f is (first + 1)
                            //This drops the second param of 10
C added in = f(11, 10) = 12 //Again, only adding 1 instead of the actual 10 count

所以,你应该更喜欢count,或者像丹尼尔建议和map那样做,或者你有第三个选择来做aggregate

 rdd.aggregate(0)(_+1, _+_)

这将使计数为0,继续在本地向累加器添加1,然后在合并中将两个累加器一起添加。

答案 2 :(得分:1)

这不是完整的答案

由于我无法将我的发现置于评论中,所以我在这里写它们。

我能够通过一个更简单的例子重现您的问题。

data = xrange(1, 10000)
len(data) #output => 9999
xrangeRDD = sc.parallelize(data, 8)
print xrangeRDD.count()
def plusOne (v,sum):
  #print sum, v
  return v + 1;
a = xrangeRDD.reduce(plusOne)
print a

<强>输出

9999
1256

xrangeRDD = sc.parallelize(data, 4)

<强>输出

9999
2502

xrangeRDD = sc.parallelize(data, 1)

<强>输出

9999
9999

由于我只是不同数量的分区,并且改变了reduce的输出,我认为reduce只是给你一个分区的输出,如这里的模式所示。

我还在学习火花的工作原理。所以我无法在这里得到完整的逻辑,为什么会发生这种情况。我希望通过这个额外的细节,有人可能能够解释这背后的原因。