From b5d84c2285fd8adf6e5ae13007b8f40e4edb9fe5 Mon Sep 17 00:00:00 2001 From: Rachel Date: Wed, 7 Oct 2015 19:30:06 -0700 Subject: [PATCH 001/198] first tries..... --- GoldiLocks/DataFrameStatistics.scala | 200 +++++++++++++++++++++++ GoldiLocks/QuantileAndDistinct.scala | 231 +++++++++++++++++++++++++++ GoldiLocks/QuantileOnly.scala | 133 +++++++++++++++ 3 files changed, 564 insertions(+) create mode 100644 GoldiLocks/DataFrameStatistics.scala create mode 100644 GoldiLocks/QuantileAndDistinct.scala create mode 100644 GoldiLocks/QuantileOnly.scala diff --git a/GoldiLocks/DataFrameStatistics.scala b/GoldiLocks/DataFrameStatistics.scala new file mode 100644 index 0000000..505dcfd --- /dev/null +++ b/GoldiLocks/DataFrameStatistics.scala @@ -0,0 +1,200 @@ + +import org.apache.spark.rdd.RDD +import org.apache.spark.sql.{DataFrame, Row} + +import scala.collection.immutable.HashMap + +/** + * Methods for computing statics on columns in a dataFrame + */ +object DataFrameStatistics { + //toDO: Right now all the keys have to fit in memory... maybe that is a bad idea? + + def findMedianAndDistinctByKey(inputData: DataFrame, medianColumns: List[Int], + distinctColumns: List[Int], groupByIndices: List[Int]) = { + val allColumns = (medianColumns ++ distinctColumns).distinct + val valPairs = DataFrameProcessingUtils.createHashMapBySeveralGroups(inputData, + allColumns, groupByIndices) + val calculator = new RankStatsByGroup(valPairs, allColumns) + val (mapOfMedians, distinct) = calculator.getMedianAndDistinct() + val medianCols = mapMediansToWide(mapOfMedians, medianColumns) + //add the distinct values + medianCols.map{ case ((group, ar )) => + val distinctRow = distinctColumns.map( i => distinct.getOrElse((i, group), -1L ) ) + (group , (ar, distinctRow)) + } + } + + /** + * Maps the result of the median to one row per group + */ + private def mapMediansToWide( + medianMap: RDD[((Int, String), + Double )], medians: List[Int] + ): RDD[(String, Array[Double])] = { + val locationMedianIndexMap = medians.zipWithIndex.toMap + medianMap.sparkContext.broadcast(locationMedianIndexMap) + val zero = Array.fill[Double](medians.length)(Double.NaN) + val useGroupAsKey = medianMap.mapPartitions( + iter => iter.map{ + case (((index, group), median)) => + (group, (locationMedianIndexMap.get(index), median )) + }.filter( + x => x._2._1.isDefined + ).map{ + case ((group, (Some(i), v ))) => (group, (i, v)) + } + ) + useGroupAsKey.aggregateByKey(zero)( + seqOp = + (row, x) => { + val (index, value) = x + row.updated(index, value) + }, + combOp = + (a, b) => a.zip(b).map { case ((x, y)) => + val (xNan, yNan) = (x.isNaN, y.isNaN) + (xNan, yNan) match { + case (true, true) => Double.NaN + case (false, true) => x + case (true, false) => y + //this is a non exhaustive match, if both x and y have values we should throw exception } + } + }) + } +} + +/** + * Methods to process data frames to key/value pairs. + */ +object DataFrameProcessingUtils{ + //this delim is used to seperate the keys, its ugly but it works + val keyDelim : String = "_alpine> { + val hashMap = new collection.mutable.HashMap[(Double, Int), Long]() + it.foreach( row => { + activeCols.foreach( i => { + val v = row.get(i).toString.toDouble + val key = (v, i) + val count = hashMap.getOrElseUpdate(key, 0) + hashMap.update(key, count + 1 ) + }) + }) + val newM = hashMap.toArray + newM.toIterator + }) + map + } + + /** + * @param groupByIndex the index of the groupBy Column + * @return rdd with ((cellValue, (cellIndex, groupByValue)), count) + */ + def createHashMapByGroup(inputData : DataFrame, + activeCols : Seq[Int], groupByIndex : Int + ) : RDD[((Double, (Int, String)), Long)] = { + val map = inputData.rdd.mapPartitions(it => { + val hashMap = new collection.mutable.HashMap[(Double, (Int, String)), Long]() + it.foreach( row => { + val groupKey = row.get(groupByIndex).toString + activeCols.foreach( i => { + val v = row.get(i).toString.toDouble + val key = (v, (i, groupKey)) + val count = hashMap.getOrElseUpdate(key, 0) + hashMap.update(key, count + 1 ) + }) + }) + val newM = hashMap.toArray + newM.toIterator + }) + map + } + + /** + * Creates an + * @param groupByIndices the index of the groupBy Column + * @return rdd with ((cellValue, (cellIndex, groupByValue)), count) + */ + def createHashMapBySeveralGroups(inputData : DataFrame, + activeCols : Seq[Int], groupByIndices : List[Int] + ) : RDD[((Double, (Int, String)), Long)] = { + val map = inputData.rdd.mapPartitions(it => { + val hashMap = new collection.mutable.HashMap[(Double, (Int, String)), Long]() + it.foreach( row => { + val groupKey = buildKey(row, groupByIndices) + activeCols.foreach( i => { + val v = row.get(i).toString.toDouble + val key = (v, (i, groupKey)) + val count = hashMap.getOrElseUpdate(key, 0) + hashMap.update(key, count + 1 ) + }) + }) + val newM = hashMap.toArray + newM.toIterator + }) + map + } + + /** + * * Used in for the MultivariateStatisticalSumerizer methods. + * Maps a data frame to key value pair of (group, vector) + + * @param inputData the data frame + * @param activeCols the columns that will be used in the operation + * @param groupByIndices the indices of the group by columns from which we will build the key + * @return + */ + def mapToKeyedVectors(inputData : DataFrame, activeCols : Array[Int], groupByIndices : List[Int ]) = { + inputData.map( row => { + val key = buildKey(row, groupByIndices) + val vector = org.apache.spark.mllib.linalg.Vectors.dense(activeCols.map( + i => row.get(i).toString.toDouble) + ) + (key, vector) + }) + } + + /** + * Build the key for the vector or HashMap by combining the values of the group by columns + * at each row. + * Uses the special delimiter field of this class. + */ + protected def buildKey(row : Row , groupByKeys : List[Int]) : String = { + val key : StringBuilder = new StringBuilder + key.append(row.get(groupByKeys.head).toString) + groupByKeys.tail.foreach( index => { + key.append(keyDelim + row.get(index).toString ) + }) + key.toString() + } + + /** + * Splits the concatenated key into an array + */ + def getKey(key : String ): Array[String] ={ + val s = key.split(keyDelim) + if(s.isEmpty) Array(key) else s + } +} + +/** + * Useful when using HashMaps as Accumulators + */ +object HashMapUtil extends Serializable { + def mergeMaps[A, B]( + mapA : HashMap[A, B], + mapB : HashMap[A, B], + merge : (B,B)=> B) : HashMap[A,B] = { + var nextMap = mapB + //add thing to map B + mapA.foreach{case ((key, aValue)) => + val bValue = mapB.get(key) + nextMap = bValue match { + case (Some(v)) => nextMap.updated(key, merge(aValue, v)) + case None => nextMap.updated(key, aValue) + } + } + nextMap + } +} \ No newline at end of file diff --git a/GoldiLocks/QuantileAndDistinct.scala b/GoldiLocks/QuantileAndDistinct.scala new file mode 100644 index 0000000..0bb9219 --- /dev/null +++ b/GoldiLocks/QuantileAndDistinct.scala @@ -0,0 +1,231 @@ + + + +import org.apache.spark.rdd.RDD +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer +import scala.reflect.runtime.{universe => ru} + +/** + * Computes Rank statistics and distinct values on data that has been mapped to + * Value, columnIndex, count triples. + *These methods aren't currently used in the Aggregation plugin, + * because the group by key field is required + * @param valPairs + * @param colIndexList + */ +class RankStats(valPairs : RDD[((Double, Int), Long )], + val colIndexList : List[Int]) { + private var reduced : RDD[((Double, Int), Long )] = _ + private var sorted : RDD[((Double, Int), Long )] = _ + val n = colIndexList.last+1 + + def getDistinctByColumn() = { + reduced = valPairs.reduceByKey((a, b) => a + b) + val countsByPartition = getDistinctForeachPart() + val distinct = getTotalDistinct(countsByPartition) + distinct + } + + def getMedianAndDistinct() = { + //toDo: use range partitioner to sort this + reduced = valPairs.reduceByKey((a, b) => a + b) + sorted = reduced.sortByKey() + val countAndSumByPart = getDistinctAndTotalForeachPart() + val (distinct, total) = getDistinctAndTotal(countAndSumByPart) + val half : Long = total.head / 2 + val sumsByPart = countAndSumByPart.sortBy(x => x._1)map{ case(index, (counts, sums ) ) => (index, sums)} + val mapOfLocations = getLocationsOfRanksWithinEachPart(sumsByPart, List(half)) + val medians = findElementsIteratively(mapOfLocations).groupByKey().collectAsMap() + (medians, distinct) + } + + /** + * Median and distinct step 1: + * Gets the number of distinct values and the total values by colIndex and group on each + * partition. Doing this in two steps should prevent large shuffles across the network. + * + * Returns with a triple for each partition with: + * 1. The index of the partition + * 2. A array of (colIndex) -> distinct values + * 3. A array of (colIndex) -> sum of Values + * (this is used in the 'getLocationsOfRanksWithinEachPart' method to calculate the medians) + */ + protected def getDistinctAndTotalForeachPart() = { + val zero = Array.fill[Long](n)(0) + sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { + val (countArray, sumArray) = PartitionProcessingUtil.totalAndDistinct(it, zero) + Iterator((index, (countArray, sumArray))) + }).collect() + } + + /** + * Median and distinct step 2. + * Given the per-partition distinct value and total counts gives us the total number of + * distinct values and the total number of values in the data set. + * Note: Right now, I am allowing for the columns to have different numbers or + * values, say because some of the cells had bad data, so the totals are for + * each column index. In the method that calculates medians, i assume that they are the same. + * + * Note: To just find the median, this step isn't really needed + * + * @param totalForEachPart result of the getDistinctForeachPart method + * + * @return A tupple of: + * 1. a array of (index) -> distinctValues + * 2. a array of (index) -> total values + */ + protected def getDistinctAndTotal(totalForEachPart : + Array[(Int, (Array[Long], Array[Long]))] ) = { + val runningCounts = Array.fill[Long](n)(0L) + val runningSums = Array.fill[Long](n)(0L) + var i = 0 + while (i < totalForEachPart.length){ + val (index, (countAr , sumAr)) = totalForEachPart(i) + countAr.zipWithIndex.foreach{ + case (count, colIndex) => runningCounts(colIndex) += count } + sumAr.zipWithIndex.foreach{ + case (sum, colIndex) => runningSums(colIndex) += sum } + i +=1 + } + (runningCounts, runningSums) + } + + + /** + * Median and distinct step 3: + * @param partitionMap- the result of the previous method + * @return and Array, locations where locations(i) = (i, list of each (colIndex, Value) + * in that partition value pairs that correspond to one of the target rank statistics for that col + */ + def getLocationsOfRanksWithinEachPart( + partitionMap : Array[(Int, Array[Long])], + targetRanks : List[Long ]) : Array[(Int, List[(Int, Long)])] = { + val runningTotal = Array.fill[Long](n)(0) + partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> { + val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() + totals.zipWithIndex.foreach{ case (colCount, colIndex) => { + val runningTotalCol = runningTotal(colIndex) + runningTotal(colIndex) += colCount + val ranksHere = targetRanks.filter(rank => + (runningTotalCol <= rank && runningTotalCol + colCount >= rank) + ) + ranksHere.foreach(rank => { + relevantIndexList += ((colIndex, rank-runningTotalCol)) + }) + }} //end of mapping col counts + (partitionIndex, relevantIndexList.toList) + }} + } + + /** + * Median and distinct step 4: + *Returns an iterator of columnIndex, + * value pairs which correspond only to the values at which are + * rank statistics. + */ + def findElementsIteratively(locations : Array[(Int, List[(Int, Long)])]) = { + sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { + val targetsInThisPart = locations(index)._2 + val len = targetsInThisPart.length + if(len >0 ) { + val newIt = PartitionProcessingUtil.getValuesFromRanks(it, targetsInThisPart) + newIt} + else Iterator.empty + } ) + } + + //Stuff used just to find distinct values bellow this + + /** + * @return an array of each partition and three values + * 1. An array of longs, which is the count for each column of the values on that partition + * 2. The first value of that partition + * 3. the last value on that partition + */ + def getDistinctForeachPart() : + Array[ Array[Long]] = { + val zero = Array.fill[Long](n)(0) + reduced.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { + + //toDo: Would it be better to keep as iterator and loop? + val keyPair : Array[Long] = it.aggregate(zero)( + (a , v : ((Double ,Int), Long)) => { + val ((value, colIndex) , count) = v + //don't add the count, just as one, because we are looking for the distinct values + a(colIndex) += 1 + a}, + (a : Array[Long], b : Array[Long]) => { + require(a.length == b.length) + a.zip(b).map{ case(aVal, bVal) => aVal + bVal} + }) + Iterator( keyPair) + }).collect() + } + + def getTotalDistinct(totalForEachPart : + (Array[Array[Long]]) ) = { + + val runningTotal = totalForEachPart(0) + var i = 1 + while (i < totalForEachPart.length){ + val countAr = totalForEachPart(i) + countAr.zipWithIndex.foreach{ + case (count, colIndex) => runningTotal(colIndex) += count } + + i +=1 + } + runningTotal + } + +} + +object PartitionProcessingUtil extends Serializable { + + def totalAndDistinct(it : Iterator[((Double, Int), Long)], zero : Array[Long] ) : (Array[Long], Array[Long]) = { + // val zero1 = Array.fill[Long](n)(0L) + val keyPair : (Array[Long], Array[Long]) = it.aggregate((Array.fill[Long](zero.length)(0L), Array.fill[Long](zero.length)(0L)))( + (acc : (Array[Long], Array[Long]) , v : ((Double ,Int), Long)) => { + val (a, sumAr ) = acc + val ((value, colIndex) , count) = v + a(colIndex) = a(colIndex) + 1 + sumAr(colIndex) = sumAr(colIndex) + count + (a, sumAr)}, + (a , b ) => { + val (aCounts, aSums) = a + val (bCounts, bSums) = b + require(aCounts.length == bCounts.length) + require(aSums.length == bSums.length) + val nextCounts = aCounts.zip(bCounts).map{ case(aVal, bVal) => aVal + bVal} + val nextSums = aSums.zip(bSums).map{case( aVal, bVal) => aVal + bVal} + (nextCounts, nextSums) + }) + keyPair + } + /** + * @param it iterator from the map partitions step in the find elements iteratively step + * @param targetsInThisPart the results of the 'getLocationsOfRanksWithinEachPart' method + * @return The nth value (or values, if the target ranks list is longer than one) + * for each column, if that nth value is in this partition. + */ + def getValuesFromRanks(it: Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)]): Iterator[(Int, Double)] = { + val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) + val keysInThisPart = targetsInThisPart.map(_._1).distinct + val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() + //toDo: refactor to not add 0L but user getOrElse update instead + keysInThisPart.foreach(key => runningTotals += ((key, 0L))) + val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + it.foreach { case ((value, colIndex), count) => { + if (keysInThisPart.contains(colIndex) ) { + val total = runningTotals(colIndex) + val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) + ranksPresent.foreach(r => { + newIt += ((colIndex, value)) + }) + runningTotals.update(colIndex, total + count) + } + }} + newIt.toIterator + } +} + diff --git a/GoldiLocks/QuantileOnly.scala b/GoldiLocks/QuantileOnly.scala new file mode 100644 index 0000000..179c24b --- /dev/null +++ b/GoldiLocks/QuantileOnly.scala @@ -0,0 +1,133 @@ + + +import org.apache.spark.Partition +import org.apache.spark.rdd.RDD +import org.apache.spark.storage.StorageLevel + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer +import scala.reflect.runtime.{universe => ru} + +class QuantileWithHashMap(val valPairs: RDD[((Double, Int), Long)], val colIndexList: List[Int], targetRanks: List[Long]) { + /* + * n is value of the last column index in the valPairs. It represents the width of the part of the dataset + * that we care about. It is possible that n will be greater than the number + * of columns if some columns between 0 and n are not included + */ + val n = colIndexList.last+1 + + /** + * @return A map of colIndex -> Array of rank stats for column indices (corresponding to the class param) + */ + def findQuantiles( ) = { + val sorted = valPairs.sortByKey() + sorted.persist(StorageLevel.MEMORY_AND_DISK) + val parts : Array[Partition] = sorted.partitions + val map1 = getTotalsForeachPart(sorted, parts.length) + val map2 = getLocationsOfRanksWithinEachPart(map1) + val result = findElementsIteratively(sorted, map2) + result.groupByKey().collectAsMap() + } + + + def findQuantilesWithCustomStorage(storageLevel: StorageLevel, checkPoint : Boolean, directory : String = "") = { + val sorted = valPairs.sortByKey() + if(storageLevel!=StorageLevel.NONE){ + sorted.persist(storageLevel) + } + if(checkPoint){ + sorted.sparkContext.setCheckpointDir(directory) + if(storageLevel.equals(StorageLevel.NONE)){ + println("Warning: Checkpointing without storing can be very slow. Consider changing Storage level Param ") + } + sorted.checkpoint() + } + val parts : Array[Partition] = sorted.partitions + val map1 = getTotalsForeachPart(sorted, parts.length) + val map2 = getLocationsOfRanksWithinEachPart(map1) + val result = findElementsIteratively(sorted, map2) + result.groupByKey().collectAsMap() + } + + /** + * @param sorted + * @param numPartitions + * @return an RDD the length of the number of partitions, where each row is a triple (partition Index + */ + def getTotalsForeachPart(sorted: RDD[((Double, Int), Long)], numPartitions: Int) = { + val zero = Array.fill[Long](n)(0) + sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { + val keyPair : Array[Long] = it.aggregate(zero)( + (a : Array[Long], v : ((Double ,Int), Long)) => { + val ((value, colIndex) , count) = v + a(colIndex) = a(colIndex) + count + a}, + (a : Array[Long], b : Array[Long]) => { + require(a.length == b.length) + a.zip(b).map{ case(aVal, bVal) => aVal + bVal} + }) + Iterator((index, keyPair)) + }).collect() + } + /** + * @param partitionMap- the result of the previous method + * @return and Array, locations where locations(i) = (i, list of each (colIndex, Value) + * in that partition value pairs that correspond to one of the target rank statistics for that col + */ + def getLocationsOfRanksWithinEachPart(partitionMap : Array[(Int, Array[Long])]) : Array[(Int, List[(Int, Long)])] = { + val runningTotal = Array.fill[Long](n)(0) + partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> { + val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() + totals.zipWithIndex.foreach{ case (colCount, colIndex) => { + val runningTotalCol = runningTotal(colIndex) + runningTotal(colIndex) += colCount + val ranksHere = targetRanks.filter(rank => + (runningTotalCol <= rank && runningTotalCol + colCount >= rank) + ) + ranksHere.foreach(rank => { + relevantIndexList += ((colIndex, rank-runningTotalCol)) + }) + }} //end of mapping col counts + (partitionIndex, relevantIndexList.toList) + }} + } + + /** + * @param sorted + * @param locations + * @return An iterator of columnIndex, value pairs which correspond only to the values at which are + * rank statistics. + */ + def findElementsIteratively(sorted : RDD[((Double, Int), Long)], locations : Array[(Int, List[(Int, Long)])]) = { + sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { + val targetsInThisPart = locations(index)._2 + val len = targetsInThisPart.length + if(len >0 ) { + val newIt = PartitionProcessingUtil2.getNewValues(it, targetsInThisPart) + newIt} + else Iterator.empty + } ) + } +} + +object PartitionProcessingUtil2 extends Serializable { + + def getNewValues(it: Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)]): Iterator[(Int, Double)] = { + val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) + val keysInThisPart = targetsInThisPart.map(_._1).distinct + val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() + keysInThisPart.foreach(key => runningTotals += ((key, 0L))) + val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + it.foreach { case ((value, colIndex), count) => { + if (keysInThisPart.contains(colIndex) ) { + val total = runningTotals(colIndex) + val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) + ranksPresent.foreach(r => { + newIt += ((colIndex, value)) + }) + runningTotals.update(colIndex, total + count) + } + }} + newIt.toIterator + } +} From 3e02f1993ed7103d13b48cdc6dc1bd198eab090d Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 7 Oct 2015 19:50:16 -0700 Subject: [PATCH 002/198] Would be good if we had a build system --- .gitignore | 6 ++++++ build.sbt | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sbt/sbt | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 build.sbt create mode 100755 sbt/sbt diff --git a/.gitignore b/.gitignore index c58d83b..0f86cc0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,9 @@ project/plugins/project/ # Scala-IDE specific .scala_dependencies .worksheet + +# emacs stuff +\#*\# +\.\#* +*~ +sbt/*launch*.jar \ No newline at end of file diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..3e9b07e --- /dev/null +++ b/build.sbt @@ -0,0 +1,54 @@ +organization := "com.highperformancespark" + +name := "spark-testing-base" + +publishMavenStyle := true + +version := "0.0.1" + +scalaVersion := "2.10.4" + +crossScalaVersions := Seq("2.10.4", "2.11.6") + +javacOptions ++= Seq("-source", "1.7", "-target", "1.7") + +sparkVersion := "1.5.1" + +spDependencies += "holdenk/spark-testing-base:0.1.3" + +sparkComponents ++= Seq("core", "streaming", "sql",) + +parallelExecution in Test := false + +javaOptions ++= Seq("-Xms512M", "-Xmx2048M", "-XX:MaxPermSize=2048M", "-XX:+CMSClassUnloadingEnabled") + +// additional libraries +libraryDependencies ++= Seq( + "org.scalatest" %% "scalatest" % "2.2.1", + "org.scalacheck" %% "scalacheck" % "1.12.4", + "junit" % "junit" % "4.10", + "org.eclipse.jetty" % "jetty-util" % "9.3.2.v20150730", + "com.novocode" % "junit-interface" % "0.10" % "test->default") + + +scalacOptions ++= Seq("-deprecation", "-unchecked") + +pomIncludeRepository := { x => false } + +resolvers ++= Seq( + "JBoss Repository" at "http://repository.jboss.org/nexus/content/repositories/releases/", + "Spray Repository" at "http://repo.spray.cc/", + "Cloudera Repository" at "https://repository.cloudera.com/artifactory/cloudera-repos/", + "Akka Repository" at "http://repo.akka.io/releases/", + "Twitter4J Repository" at "http://twitter4j.org/maven2/", + "Apache HBase" at "https://repository.apache.org/content/repositories/releases", + "Twitter Maven Repo" at "http://maven.twttr.com/", + "scala-tools" at "https://oss.sonatype.org/content/groups/scala-tools", + "sonatype-releases" at "https://oss.sonatype.org/content/repositories/releases/", + "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/", + "Second Typesafe repo" at "http://repo.typesafe.com/typesafe/maven-releases/", + "Mesosphere Public Repository" at "http://downloads.mesosphere.io/maven", + Resolver.sonatypeRepo("public") +) + +licenses := Seq("Apache License 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.html")) diff --git a/sbt/sbt b/sbt/sbt new file mode 100755 index 0000000..aac1085 --- /dev/null +++ b/sbt/sbt @@ -0,0 +1,52 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This script launches sbt for this project. If present it uses the system +# version of sbt. If there is no system version of sbt it attempts to download +# sbt locally. +SBT_VERSION=0.13.9 +URL1=http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/${SBT_VERSION}/sbt-launch.jar +URL2=http://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/${SBT_VERSION}/sbt-launch.jar +JAR=sbt/sbt-launch-${SBT_VERSION}.jar + +# Download sbt launch jar if it hasn't been downloaded yet +if [ ! -f ${JAR} ]; then + # Download + printf "Attempting to fetch sbt\n" + set -x + JAR_DL=${JAR}.part + if hash wget 2>/dev/null; then + (wget --progress=bar ${URL1} -O ${JAR_DL} || wget --progress=bar ${URL2} -O ${JAR_DL}) && mv ${JAR_DL} ${JAR} + elif hash axel 2>/dev/null; then + (axel ${URL1} -o ${JAR_DL} || axel ${URL2} -o ${JAR_DL}) && mv ${JAR_DL} ${JAR} + else + printf "You do not have curl or wget installed, please install sbt manually from http://www.scala-sbt.org/\n" + exit -1 + fi +fi +if [ ! -f ${JAR} ]; then + # We failed to download + printf "Our attempt to download sbt locally to ${JAR} failed. Please install sbt manually from http://www.scala-sbt.org/\n" + exit -1 +fi +printf "Launching sbt from ${JAR}\n" +java \ + -Xmx1200m -XX:MaxPermSize=350m -XX:ReservedCodeCacheSize=256m \ + -jar ${JAR} \ + "$@" From 991e8edd742ef5739257e973c2950fa8220b80a8 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 7 Oct 2015 19:51:15 -0700 Subject: [PATCH 003/198] Move GoldiLocks example --- .../GoldiLocks}/DataFrameStatistics.scala | 0 .../GoldiLocks}/QuantileAndDistinct.scala | 0 .../GoldiLocks}/QuantileOnly.scala | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {GoldiLocks => src/main/com/high-performance-spark-examples/GoldiLocks}/DataFrameStatistics.scala (100%) rename {GoldiLocks => src/main/com/high-performance-spark-examples/GoldiLocks}/QuantileAndDistinct.scala (100%) rename {GoldiLocks => src/main/com/high-performance-spark-examples/GoldiLocks}/QuantileOnly.scala (100%) diff --git a/GoldiLocks/DataFrameStatistics.scala b/src/main/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala similarity index 100% rename from GoldiLocks/DataFrameStatistics.scala rename to src/main/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala diff --git a/GoldiLocks/QuantileAndDistinct.scala b/src/main/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala similarity index 100% rename from GoldiLocks/QuantileAndDistinct.scala rename to src/main/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala diff --git a/GoldiLocks/QuantileOnly.scala b/src/main/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala similarity index 100% rename from GoldiLocks/QuantileOnly.scala rename to src/main/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala From 810e7c7e1e18daf686aa8d4f40d1950ad9cf2146 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 7 Oct 2015 20:07:56 -0700 Subject: [PATCH 004/198] fix sbt build and remove some blank lines --- build.sbt | 2 +- project/plugins.sbt | 11 +++++++++++ .../GoldiLocks/DataFrameStatistics.scala | 0 .../GoldiLocks/QuantileAndDistinct.scala | 0 .../GoldiLocks/QuantileOnly.scala | 2 -- 5 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 project/plugins.sbt rename src/main/{ => scala}/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala (100%) rename src/main/{ => scala}/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala (100%) rename src/main/{ => scala}/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala (99%) diff --git a/build.sbt b/build.sbt index 3e9b07e..db04038 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,7 @@ sparkVersion := "1.5.1" spDependencies += "holdenk/spark-testing-base:0.1.3" -sparkComponents ++= Seq("core", "streaming", "sql",) +sparkComponents ++= Seq("core", "streaming", "sql") parallelExecution in Test := false diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..b81ac59 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,11 @@ +addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.6.0") + +resolvers += "sonatype-releases" at "https://oss.sonatype.org/content/repositories/releases/" + +resolvers += "Spark Package Main Repo" at "https://dl.bintray.com/spark-packages/maven" + +addSbtPlugin("org.spark-packages" % "sbt-spark-package" % "0.2.2") + +//addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") + +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.3") diff --git a/src/main/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala similarity index 100% rename from src/main/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala rename to src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala diff --git a/src/main/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala similarity index 100% rename from src/main/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala rename to src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala diff --git a/src/main/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala similarity index 99% rename from src/main/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala rename to src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala index 179c24b..ff3acf0 100644 --- a/src/main/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala @@ -1,5 +1,3 @@ - - import org.apache.spark.Partition import org.apache.spark.rdd.RDD import org.apache.spark.storage.StorageLevel From 3a63a68e5a42b49a7fb19fc96648dbb11aa245ae Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 7 Oct 2015 20:12:26 -0700 Subject: [PATCH 005/198] Add a travis config file --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..aa60a1e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: scala +scala: + - 2.10.4 +before_install: + - pip install --user codecov +after_success: +# For now no coverage report + - codecov \ No newline at end of file From 7d77086210e063c408185fbdbd254acb36cc8859 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 7 Oct 2015 21:20:33 -0700 Subject: [PATCH 006/198] Yay it compiles --- .../GoldiLocks/RankStatsByGroup.scala | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala new file mode 100644 index 0000000..aff52cf --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala @@ -0,0 +1,246 @@ +package com.highperformancespark.examples.goldilocks + +import org.apache.spark.rdd.RDD + +import scala.collection.immutable.HashMap +import scala.collection.mutable.ArrayBuffer + +class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], + colIndexList : List[Int]){ + val n = colIndexList.last+1 + private var reduced : RDD[((Double, (Int, String)), Long)] = _ + private var sorted : RDD[((Double, (Int, String)), Long)] = _ + + /** + * + * @return A tupples with + * 1. KeyValueRDD of (index, group) -> Median + * 2. Hashmap of (index, group) -> # distinct values + */ + def getMedianAndDistinct () = { + reduced = valPairs.reduceByKey((a, b) => a + b) + sorted = reduced.sortByKey() + val countAndSumByPart = calculateDistinctAndTotalForeachPart() + val (distinct, total) = getTotalAndDistinct(countAndSumByPart) + val half : Long = total.get(valPairs.first()._1._2).get / 2 + val mapOfLocations = getLocationsOfRanksWithinEachPart(countAndSumByPart, List(half)) + val medians = findElementsIteratively( mapOfLocations).groupByKey().mapValues(_.head) + (medians, distinct) + } + + def getDistinct() = { + reduced = valPairs.reduceByKey((a, b) => a + b) + val countsByPartition = calculateDistinctForEachPart() + val distinct = getTotalDistinct(countsByPartition) + distinct + } + /** + * Median and distinct step 1: + * Gets the number of distinct values and the total values by colIndex and group on each + * partition. Doing this in two steps should prevent large shuffles across the network. + * + * Returns with a triple for each partition with: + * 1. The index of the partition + * 2. A HashMap of (colIndex, group) -> distinct values + * 3. A HashMap of (colIndex, group) -> sum of Values + * (this is used in the 'getLocationsOfRanksWithinEachPart' method to calculate the medians) + */ + protected def calculateDistinctAndTotalForeachPart() : + Array[ (Int, HashMap[(Int, String ), Long], HashMap[(Int, String ), Long])] = { + sorted.mapPartitionsWithIndex( + (index : Int, it : Iterator[((Double, (Int , String ) ), Long)]) => { + // val count = n.toLong.toInt + val (countArray, sumArray) = GroupedPartitionProcessingUtil.totalAndDistinctByGroup(it) + Iterator((index, countArray, sumArray)) + }).collect() + } + + /** + * Median and distinct step 2. + * Given the per-partition distinct value and total counts gives us the total number of + * distinct values and the total number of values in the data set. + * Note: Right now, I am allowing for the columns to have different numbers or + * values, say because some of the cells had bad data, so the totals are for + * each column index. In the method that calculates medians, i assume that they are the same. + * + * Note: To just find the median, this step isn't really needed + * + * @param totalForEachPart result of the getDistinctForeachPart method + * + * @return A tupple of: + * 1. a hashmap of (index, group) -> distinctValues + * 2. a hashmap of (index, group) -> total values + */ + protected def getTotalAndDistinct( + totalForEachPart : Array[(Int, HashMap[(Int,String ), Long ], + HashMap[(Int,String ), Long ])]) : (HashMap[(Int,String ), Long ], + HashMap[(Int,String ), Long ]) = { + totalForEachPart.map( + t => (t._2, t._3)).reduce( + (hashMap1, hashMap2) => { + val ( count1, sum1 ) = hashMap1 + val ( count2, sum2) = hashMap2 + val sumFunction = (a: Long , b: Long ) => a + b + val mergedCounts = HashMapUtil.mergeMaps(count1, count2, sumFunction) + val mergedSums = HashMapUtil.mergeMaps(sum1, sum2, sumFunction) + + (mergedCounts, mergedSums) + } ) + } + + /** + * Median and distinct step 3: + * Calculates the location of the desired rank statistics within each partition. + * @param partitionMap the result of the calculateDistinctAndTotalForeachPart method + * @param targetRanks the rank statistics we want to calculate for each index/ group. Median should + * be the size of the dataset/2 + * @return An array with a HashMap for each partition of + * (colIndex, group) -> List of the indices of the iterator on this partition that contain + * the rank statics that we are looking for. + */ + protected def getLocationsOfRanksWithinEachPart( + partitionMap : Array[(Int, HashMap[(Int, String), Long], HashMap[(Int, String ), Long])], + targetRanks : List[Long] + ) : Array[ HashMap[(Int, String ), List[Long] ]] = { + //as we go through the data linearly, keep track of the number of elements we have seen for + //each partition + var runningTotal = HashMap[(Int, String ), Long]() + //sort by partition number + val sortedParts = partitionMap.sortBy(_._1).map( t => (t._2, t._3)) + sortedParts.map { + + case (( countMap, sumsMap )) => + var relevantIndexMap = HashMap[(Int, String), List[Long] ]() + sumsMap.foreach{ + case (((colIndex, group), colCount )) => + //the running totals for this column/group. If we haven't seen it yet, 0L + val runningTotalCol = runningTotal.getOrElse((colIndex, group), 0L) + runningTotal = runningTotal.updated((colIndex, group), runningTotalCol + colCount ) + //if the count for this column/ and group is in the right range, add it to the map + val ranksHere = targetRanks.filter(rank => + runningTotalCol <= rank && runningTotalCol + colCount >= rank ) + + //add ranksHere to the relevant index map + ranksHere.foreach( + rank => { + val location = rank - runningTotalCol + val indicesForThisKey = relevantIndexMap.getOrElse((colIndex, group ), List[Long]()) + relevantIndexMap = relevantIndexMap.updated( + (colIndex, group), location :: indicesForThisKey) + }) + }//end sums map for each + relevantIndexMap + } + } + + /** + * Median and distinct step 4: + * Given the location within each partition of the desired rank static, goes through the sorted + * data an actually gets that information + * @param locations the results of the getLocationsOfRanksWithinEachPart method + * @return + */ + protected def findElementsIteratively( locations : Array[ HashMap[(Int, String ), List[Long] ]]) = { + sorted.mapPartitionsWithIndex((index, iter) => { + val targetsInThisPart = locations(index) + val len = targetsInThisPart.keys.size + if(len >0 ) { + val newIt = GroupedPartitionProcessingUtil.getValuesFromRanks(iter, targetsInThisPart) + newIt + } + else Iterator.empty + } ) + } + + /** + * Calculates the number of distinct values per (index,keyPair) on each partition). + * The per partition counts are important for the distinct median finding + */ + protected def calculateDistinctForEachPart() : + Array[HashMap[(Int,String ), Long ]] = { + val zero = new HashMap[(Int, String), Long]() + + reduced.mapPartitionsWithIndex((index : Int, + it : Iterator[((Double, (Int, String)), Long)]) => { + val hashMap : HashMap[(Int, String ), Long ] = it.aggregate(zero)( + (map : HashMap[(Int, String), Long ], v : ((Double, (Int, String)), Long) ) => { + val ((value, (colIndex, group)) , count) = v + val prevCount = map.getOrElse( (colIndex, group), 0L ) + //don't add the count, just as one, because we are looking for the distinct values + map.updated((colIndex, group), prevCount + 1 ) + }, + (a , b ) => { + val mergeFunction = (a : Long, b : Long ) => a + b + val h : HashMap[(Int, String ), Long ] = HashMapUtil.mergeMaps[(Int, String ), Long](a, b , mergeFunction) + h + }) + Iterator(hashMap) + }).collect() + } + + protected def getTotalDistinct(totalForEachPart : Array[HashMap[(Int,String ), Long ]] + ) = { + totalForEachPart.reduce((hashMap1, hashMap2) => HashMapUtil.mergeMaps(hashMap1, + hashMap2, (a: Long , b: Long ) => a + b)) + } + + +} + +/** + * Serializable methods performed on iterators in the mapPartitions steps + * in the above clasee + */ +object GroupedPartitionProcessingUtil extends Serializable{ + /** + * Calculates counts an sums per group and partition + */ + def totalAndDistinctByGroup(it : Iterator[((Double, (Int, String)), Long)] ) + : ( HashMap[(Int, String), Long], HashMap[(Int, String), Long]) = { + + val keyPair : ( HashMap[(Int, String), Long], HashMap[(Int, String), Long]) = + it.aggregate(( new HashMap[(Int, String), Long](), new HashMap[(Int, String), Long]()))( + (acc , v : ((Double ,(Int, String )), Long)) => { + val (countMap, sumMap ) = acc + val ((_, (colIndex, group )) , count) = v + val prevCount = countMap.getOrElse( (colIndex, group), 0L ) + val prevSum = sumMap.getOrElse((colIndex, group), 0L) + //don't add the count, just as one, because we are looking for the distinct values + val nextCounts = countMap.updated((colIndex, group), prevCount + 1 ) + val nextSums = sumMap.updated((colIndex, group), prevSum + count) + ( nextCounts, nextSums) } , + (a , b ) => { + val (aCounts, aSums) = a + val (bCounts, bSums) = b + + val nextCounts = HashMapUtil.mergeMaps[(Int, String), Long](aCounts, bCounts, (a :Long , b : Long ) => a+b) + val nextSums = HashMapUtil.mergeMaps[(Int, String), Long](aSums, bSums , (a :Long , b : Long ) => a+b) + (nextCounts, nextSums) + }) + keyPair + } + //return iterator of (colIndex,group)-> value, then make it into a map. + /** + * @param it iterator from the map partitions step in the find elements iteratively step + * @param targetsInThisPart the results of the 'getLocationsOfRanksWithinEachPart' method + * @return The nth value (or values, if the target ranks list is longer than one) + * for each column, if that nth value is in this partition. + */ + def getValuesFromRanks(it : Iterator[((Double, (Int, String)), Long)], targetsInThisPart :HashMap[(Int, String), List[Long]] ) = { + val keysInThisPart = targetsInThisPart.keySet + var runningTotals = HashMap[(Int, String), Long]() + + val newIt: ArrayBuffer[((Int, String), Double)] = new scala.collection.mutable.ArrayBuffer() + it.foreach { case (((value, key), count)) => + if (keysInThisPart.contains(key)) { + val total = runningTotals.getOrElse(key, 0L) + val ranksPresent = targetsInThisPart(key).filter(v => (v <= count + total) && (v > total)) + ranksPresent.foreach(r => { + newIt += ((key, value)) + }) + runningTotals = runningTotals.updated(key, total + count) + } + } + newIt.toIterator + } +} From 6b07f48a269076ab1465231bcb280366a415c04e Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 7 Oct 2015 21:25:03 -0700 Subject: [PATCH 007/198] Update packages and mllib --- build.sbt | 2 +- .../GoldiLocks/DataFrameStatistics.scala | 3 ++- .../GoldiLocks/QuantileAndDistinct.scala | 4 +--- .../GoldiLocks/QuantileOnly.scala | 2 ++ 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build.sbt b/build.sbt index db04038..4dec3b8 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,7 @@ sparkVersion := "1.5.1" spDependencies += "holdenk/spark-testing-base:0.1.3" -sparkComponents ++= Seq("core", "streaming", "sql") +sparkComponents ++= Seq("core", "streaming", "sql", "mllib") parallelExecution in Test := false diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala index 505dcfd..79d8faa 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala @@ -1,3 +1,4 @@ +package com.highperformancespark.examples.goldilocks import org.apache.spark.rdd.RDD import org.apache.spark.sql.{DataFrame, Row} @@ -197,4 +198,4 @@ object HashMapUtil extends Serializable { } nextMap } -} \ No newline at end of file +} diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala index 0bb9219..c32b337 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala @@ -1,5 +1,4 @@ - - +package com.highperformancespark.examples.goldilocks import org.apache.spark.rdd.RDD import scala.collection.mutable @@ -228,4 +227,3 @@ object PartitionProcessingUtil extends Serializable { newIt.toIterator } } - diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala index ff3acf0..f8c1e79 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala @@ -1,3 +1,5 @@ +package com.highperformancespark.examples.goldilocks + import org.apache.spark.Partition import org.apache.spark.rdd.RDD import org.apache.spark.storage.StorageLevel From 326390b4362137b46747ea9a46557d188ec4c6ea Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 7 Oct 2015 21:58:01 -0700 Subject: [PATCH 008/198] Add a bit of a note about what the params are since the coffee wasn't helping --- .../GoldiLocks/QuantileOnly.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala index f8c1e79..fbe57d6 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala @@ -8,6 +8,10 @@ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.reflect.runtime.{universe => ru} +/** + * valPairs is an K/V RDD of ((Value, ColumnIndex), OccurenceCount) + * colIndexList is the list of column indexes we wish to extract ranks for + */ class QuantileWithHashMap(val valPairs: RDD[((Double, Int), Long)], val colIndexList: List[Int], targetRanks: List[Long]) { /* * n is value of the last column index in the valPairs. It represents the width of the part of the dataset From 2d3b951da14d98b4461883706b432c33ee086d6c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 7 Oct 2015 21:58:24 -0700 Subject: [PATCH 009/198] Add an Artisanal (non spark-testing-base) test --- .../QuantileOnlyArtisanalTest.scala | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala new file mode 100644 index 0000000..27f2244 --- /dev/null +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -0,0 +1,35 @@ +package com.highperformancespark.examples.goldilocks + +import org.apache.spark._ +import org.apache.spark.rdd._ + +import org.scalatest.{BeforeAndAfterAll, FunSuite} + +// START-TEST +class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { + @transient private var _sc: SparkContext = _ + def sc: SparkContext = _sc + + val conf = new SparkConf().setMaster("local[4]").setAppName("test") + + override def beforeAll() { + _sc = new SparkContext(conf) + super.beforeAll() + } + + test("retrieve quantiles") { + val input: RDD[((Double, Int), Long)] = sc.parallelize( + List(((2.0, 1), 10L), ((1.0, 1), 5L), ((3.0, 1), 4L))) + val result = new QuantileWithHashMap(input, List(1), List(1L, 6L)).findQuantiles() + val expected = List(1.0, 2.0) + assert(expected === result(1).toList) + } + + override def afterAll() { + // We clear the driver port so that we don't try and bind to the same port on restart + System.clearProperty("spark.driver.port") + _sc = null + super.afterAll() + } +} +// END-TEST From 1f7f78a4ac0b8f82185dfafc6a67436634315aaf Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 8 Oct 2015 00:20:14 -0700 Subject: [PATCH 010/198] re-indent --- .../GoldiLocks/DataFrameStatistics.scala | 50 +++---- .../GoldiLocks/QuantileAndDistinct.scala | 12 +- .../GoldiLocks/RankStatsByGroup.scala | 136 +++++++++--------- 3 files changed, 99 insertions(+), 99 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala index 79d8faa..730c75c 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala @@ -12,7 +12,7 @@ object DataFrameStatistics { //toDO: Right now all the keys have to fit in memory... maybe that is a bad idea? def findMedianAndDistinctByKey(inputData: DataFrame, medianColumns: List[Int], - distinctColumns: List[Int], groupByIndices: List[Int]) = { + distinctColumns: List[Int], groupByIndices: List[Int]) = { val allColumns = (medianColumns ++ distinctColumns).distinct val valPairs = DataFrameProcessingUtils.createHashMapBySeveralGroups(inputData, allColumns, groupByIndices) @@ -30,9 +30,9 @@ object DataFrameStatistics { * Maps the result of the median to one row per group */ private def mapMediansToWide( - medianMap: RDD[((Int, String), - Double )], medians: List[Int] - ): RDD[(String, Array[Double])] = { + medianMap: RDD[((Int, String), + Double )], medians: List[Int] + ): RDD[(String, Array[Double])] = { val locationMedianIndexMap = medians.zipWithIndex.toMap medianMap.sparkContext.broadcast(locationMedianIndexMap) val zero = Array.fill[Double](medians.length)(Double.NaN) @@ -41,27 +41,27 @@ object DataFrameStatistics { case (((index, group), median)) => (group, (locationMedianIndexMap.get(index), median )) }.filter( - x => x._2._1.isDefined - ).map{ + x => x._2._1.isDefined + ).map{ case ((group, (Some(i), v ))) => (group, (i, v)) } ) useGroupAsKey.aggregateByKey(zero)( seqOp = (row, x) => { - val (index, value) = x - row.updated(index, value) - }, + val (index, value) = x + row.updated(index, value) + }, combOp = (a, b) => a.zip(b).map { case ((x, y)) => - val (xNan, yNan) = (x.isNaN, y.isNaN) - (xNan, yNan) match { - case (true, true) => Double.NaN - case (false, true) => x - case (true, false) => y - //this is a non exhaustive match, if both x and y have values we should throw exception } - } - }) + val (xNan, yNan) = (x.isNaN, y.isNaN) + (xNan, yNan) match { + case (true, true) => Double.NaN + case (false, true) => x + case (true, false) => y + //this is a non exhaustive match, if both x and y have values we should throw exception } + } + }) } } @@ -93,8 +93,8 @@ object DataFrameProcessingUtils{ * @return rdd with ((cellValue, (cellIndex, groupByValue)), count) */ def createHashMapByGroup(inputData : DataFrame, - activeCols : Seq[Int], groupByIndex : Int - ) : RDD[((Double, (Int, String)), Long)] = { + activeCols : Seq[Int], groupByIndex : Int + ) : RDD[((Double, (Int, String)), Long)] = { val map = inputData.rdd.mapPartitions(it => { val hashMap = new collection.mutable.HashMap[(Double, (Int, String)), Long]() it.foreach( row => { @@ -118,8 +118,8 @@ object DataFrameProcessingUtils{ * @return rdd with ((cellValue, (cellIndex, groupByValue)), count) */ def createHashMapBySeveralGroups(inputData : DataFrame, - activeCols : Seq[Int], groupByIndices : List[Int] - ) : RDD[((Double, (Int, String)), Long)] = { + activeCols : Seq[Int], groupByIndices : List[Int] + ) : RDD[((Double, (Int, String)), Long)] = { val map = inputData.rdd.mapPartitions(it => { val hashMap = new collection.mutable.HashMap[(Double, (Int, String)), Long]() it.foreach( row => { @@ -147,7 +147,7 @@ object DataFrameProcessingUtils{ * @return */ def mapToKeyedVectors(inputData : DataFrame, activeCols : Array[Int], groupByIndices : List[Int ]) = { - inputData.map( row => { + inputData.map( row => { val key = buildKey(row, groupByIndices) val vector = org.apache.spark.mllib.linalg.Vectors.dense(activeCols.map( i => row.get(i).toString.toDouble) @@ -184,9 +184,9 @@ object DataFrameProcessingUtils{ */ object HashMapUtil extends Serializable { def mergeMaps[A, B]( - mapA : HashMap[A, B], - mapB : HashMap[A, B], - merge : (B,B)=> B) : HashMap[A,B] = { + mapA : HashMap[A, B], + mapB : HashMap[A, B], + merge : (B,B)=> B) : HashMap[A,B] = { var nextMap = mapB //add thing to map B mapA.foreach{case ((key, aValue)) => diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala index c32b337..2868f08 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala @@ -14,7 +14,7 @@ import scala.reflect.runtime.{universe => ru} * @param colIndexList */ class RankStats(valPairs : RDD[((Double, Int), Long )], - val colIndexList : List[Int]) { + val colIndexList : List[Int]) { private var reduced : RDD[((Double, Int), Long )] = _ private var sorted : RDD[((Double, Int), Long )] = _ val n = colIndexList.last+1 @@ -75,7 +75,7 @@ class RankStats(valPairs : RDD[((Double, Int), Long )], * 2. a array of (index) -> total values */ protected def getDistinctAndTotal(totalForEachPart : - Array[(Int, (Array[Long], Array[Long]))] ) = { + Array[(Int, (Array[Long], Array[Long]))] ) = { val runningCounts = Array.fill[Long](n)(0L) val runningSums = Array.fill[Long](n)(0L) var i = 0 @@ -98,8 +98,8 @@ class RankStats(valPairs : RDD[((Double, Int), Long )], * in that partition value pairs that correspond to one of the target rank statistics for that col */ def getLocationsOfRanksWithinEachPart( - partitionMap : Array[(Int, Array[Long])], - targetRanks : List[Long ]) : Array[(Int, List[(Int, Long)])] = { + partitionMap : Array[(Int, Array[Long])], + targetRanks : List[Long ]) : Array[(Int, List[(Int, Long)])] = { val runningTotal = Array.fill[Long](n)(0) partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> { val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() @@ -143,7 +143,7 @@ class RankStats(valPairs : RDD[((Double, Int), Long )], * 3. the last value on that partition */ def getDistinctForeachPart() : - Array[ Array[Long]] = { + Array[ Array[Long]] = { val zero = Array.fill[Long](n)(0) reduced.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { @@ -163,7 +163,7 @@ class RankStats(valPairs : RDD[((Double, Int), Long )], } def getTotalDistinct(totalForEachPart : - (Array[Array[Long]]) ) = { + (Array[Array[Long]]) ) = { val runningTotal = totalForEachPart(0) var i = 1 diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala index aff52cf..c4058ae 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala @@ -6,7 +6,7 @@ import scala.collection.immutable.HashMap import scala.collection.mutable.ArrayBuffer class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], - colIndexList : List[Int]){ + colIndexList : List[Int]){ val n = colIndexList.last+1 private var reduced : RDD[((Double, (Int, String)), Long)] = _ private var sorted : RDD[((Double, (Int, String)), Long)] = _ @@ -18,8 +18,8 @@ class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], * 2. Hashmap of (index, group) -> # distinct values */ def getMedianAndDistinct () = { - reduced = valPairs.reduceByKey((a, b) => a + b) - sorted = reduced.sortByKey() + reduced = valPairs.reduceByKey((a, b) => a + b) + sorted = reduced.sortByKey() val countAndSumByPart = calculateDistinctAndTotalForeachPart() val (distinct, total) = getTotalAndDistinct(countAndSumByPart) val half : Long = total.get(valPairs.first()._1._2).get / 2 @@ -29,7 +29,7 @@ class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], } def getDistinct() = { - reduced = valPairs.reduceByKey((a, b) => a + b) + reduced = valPairs.reduceByKey((a, b) => a + b) val countsByPartition = calculateDistinctForEachPart() val distinct = getTotalDistinct(countsByPartition) distinct @@ -46,13 +46,13 @@ class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], * (this is used in the 'getLocationsOfRanksWithinEachPart' method to calculate the medians) */ protected def calculateDistinctAndTotalForeachPart() : - Array[ (Int, HashMap[(Int, String ), Long], HashMap[(Int, String ), Long])] = { + Array[ (Int, HashMap[(Int, String ), Long], HashMap[(Int, String ), Long])] = { sorted.mapPartitionsWithIndex( (index : Int, it : Iterator[((Double, (Int , String ) ), Long)]) => { - // val count = n.toLong.toInt - val (countArray, sumArray) = GroupedPartitionProcessingUtil.totalAndDistinctByGroup(it) - Iterator((index, countArray, sumArray)) - }).collect() + // val count = n.toLong.toInt + val (countArray, sumArray) = GroupedPartitionProcessingUtil.totalAndDistinctByGroup(it) + Iterator((index, countArray, sumArray)) + }).collect() } /** @@ -72,20 +72,20 @@ class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], * 2. a hashmap of (index, group) -> total values */ protected def getTotalAndDistinct( - totalForEachPart : Array[(Int, HashMap[(Int,String ), Long ], - HashMap[(Int,String ), Long ])]) : (HashMap[(Int,String ), Long ], - HashMap[(Int,String ), Long ]) = { + totalForEachPart : Array[(Int, HashMap[(Int,String ), Long ], + HashMap[(Int,String ), Long ])]) : (HashMap[(Int,String ), Long ], + HashMap[(Int,String ), Long ]) = { totalForEachPart.map( t => (t._2, t._3)).reduce( - (hashMap1, hashMap2) => { - val ( count1, sum1 ) = hashMap1 - val ( count2, sum2) = hashMap2 - val sumFunction = (a: Long , b: Long ) => a + b - val mergedCounts = HashMapUtil.mergeMaps(count1, count2, sumFunction) - val mergedSums = HashMapUtil.mergeMaps(sum1, sum2, sumFunction) - - (mergedCounts, mergedSums) - } ) + (hashMap1, hashMap2) => { + val ( count1, sum1 ) = hashMap1 + val ( count2, sum2) = hashMap2 + val sumFunction = (a: Long , b: Long ) => a + b + val mergedCounts = HashMapUtil.mergeMaps(count1, count2, sumFunction) + val mergedSums = HashMapUtil.mergeMaps(sum1, sum2, sumFunction) + + (mergedCounts, mergedSums) + } ) } /** @@ -99,37 +99,37 @@ class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], * the rank statics that we are looking for. */ protected def getLocationsOfRanksWithinEachPart( - partitionMap : Array[(Int, HashMap[(Int, String), Long], HashMap[(Int, String ), Long])], - targetRanks : List[Long] - ) : Array[ HashMap[(Int, String ), List[Long] ]] = { + partitionMap : Array[(Int, HashMap[(Int, String), Long], HashMap[(Int, String ), Long])], + targetRanks : List[Long] + ) : Array[ HashMap[(Int, String ), List[Long] ]] = { //as we go through the data linearly, keep track of the number of elements we have seen for //each partition var runningTotal = HashMap[(Int, String ), Long]() //sort by partition number val sortedParts = partitionMap.sortBy(_._1).map( t => (t._2, t._3)) - sortedParts.map { + sortedParts.map { - case (( countMap, sumsMap )) => - var relevantIndexMap = HashMap[(Int, String), List[Long] ]() - sumsMap.foreach{ - case (((colIndex, group), colCount )) => + case (( countMap, sumsMap )) => + var relevantIndexMap = HashMap[(Int, String), List[Long] ]() + sumsMap.foreach{ + case (((colIndex, group), colCount )) => //the running totals for this column/group. If we haven't seen it yet, 0L val runningTotalCol = runningTotal.getOrElse((colIndex, group), 0L) runningTotal = runningTotal.updated((colIndex, group), runningTotalCol + colCount ) - //if the count for this column/ and group is in the right range, add it to the map - val ranksHere = targetRanks.filter(rank => - runningTotalCol <= rank && runningTotalCol + colCount >= rank ) - - //add ranksHere to the relevant index map - ranksHere.foreach( - rank => { - val location = rank - runningTotalCol - val indicesForThisKey = relevantIndexMap.getOrElse((colIndex, group ), List[Long]()) - relevantIndexMap = relevantIndexMap.updated( - (colIndex, group), location :: indicesForThisKey) - }) - }//end sums map for each - relevantIndexMap + //if the count for this column/ and group is in the right range, add it to the map + val ranksHere = targetRanks.filter(rank => + runningTotalCol <= rank && runningTotalCol + colCount >= rank ) + + //add ranksHere to the relevant index map + ranksHere.foreach( + rank => { + val location = rank - runningTotalCol + val indicesForThisKey = relevantIndexMap.getOrElse((colIndex, group ), List[Long]()) + relevantIndexMap = relevantIndexMap.updated( + (colIndex, group), location :: indicesForThisKey) + }) + }//end sums map for each + relevantIndexMap } } @@ -144,10 +144,10 @@ class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], sorted.mapPartitionsWithIndex((index, iter) => { val targetsInThisPart = locations(index) val len = targetsInThisPart.keys.size - if(len >0 ) { + if(len >0 ) { val newIt = GroupedPartitionProcessingUtil.getValuesFromRanks(iter, targetsInThisPart) newIt - } + } else Iterator.empty } ) } @@ -157,21 +157,21 @@ class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], * The per partition counts are important for the distinct median finding */ protected def calculateDistinctForEachPart() : - Array[HashMap[(Int,String ), Long ]] = { + Array[HashMap[(Int,String ), Long ]] = { val zero = new HashMap[(Int, String), Long]() reduced.mapPartitionsWithIndex((index : Int, - it : Iterator[((Double, (Int, String)), Long)]) => { + it : Iterator[((Double, (Int, String)), Long)]) => { val hashMap : HashMap[(Int, String ), Long ] = it.aggregate(zero)( (map : HashMap[(Int, String), Long ], v : ((Double, (Int, String)), Long) ) => { val ((value, (colIndex, group)) , count) = v val prevCount = map.getOrElse( (colIndex, group), 0L ) //don't add the count, just as one, because we are looking for the distinct values - map.updated((colIndex, group), prevCount + 1 ) + map.updated((colIndex, group), prevCount + 1 ) }, (a , b ) => { val mergeFunction = (a : Long, b : Long ) => a + b - val h : HashMap[(Int, String ), Long ] = HashMapUtil.mergeMaps[(Int, String ), Long](a, b , mergeFunction) + val h : HashMap[(Int, String ), Long ] = HashMapUtil.mergeMaps[(Int, String ), Long](a, b , mergeFunction) h }) Iterator(hashMap) @@ -179,7 +179,7 @@ class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], } protected def getTotalDistinct(totalForEachPart : Array[HashMap[(Int,String ), Long ]] - ) = { + ) = { totalForEachPart.reduce((hashMap1, hashMap2) => HashMapUtil.mergeMaps(hashMap1, hashMap2, (a: Long , b: Long ) => a + b)) } @@ -196,27 +196,27 @@ object GroupedPartitionProcessingUtil extends Serializable{ * Calculates counts an sums per group and partition */ def totalAndDistinctByGroup(it : Iterator[((Double, (Int, String)), Long)] ) - : ( HashMap[(Int, String), Long], HashMap[(Int, String), Long]) = { + : ( HashMap[(Int, String), Long], HashMap[(Int, String), Long]) = { val keyPair : ( HashMap[(Int, String), Long], HashMap[(Int, String), Long]) = it.aggregate(( new HashMap[(Int, String), Long](), new HashMap[(Int, String), Long]()))( - (acc , v : ((Double ,(Int, String )), Long)) => { - val (countMap, sumMap ) = acc - val ((_, (colIndex, group )) , count) = v - val prevCount = countMap.getOrElse( (colIndex, group), 0L ) - val prevSum = sumMap.getOrElse((colIndex, group), 0L) - //don't add the count, just as one, because we are looking for the distinct values - val nextCounts = countMap.updated((colIndex, group), prevCount + 1 ) - val nextSums = sumMap.updated((colIndex, group), prevSum + count) - ( nextCounts, nextSums) } , - (a , b ) => { - val (aCounts, aSums) = a - val (bCounts, bSums) = b - - val nextCounts = HashMapUtil.mergeMaps[(Int, String), Long](aCounts, bCounts, (a :Long , b : Long ) => a+b) - val nextSums = HashMapUtil.mergeMaps[(Int, String), Long](aSums, bSums , (a :Long , b : Long ) => a+b) - (nextCounts, nextSums) - }) + (acc , v : ((Double ,(Int, String )), Long)) => { + val (countMap, sumMap ) = acc + val ((_, (colIndex, group )) , count) = v + val prevCount = countMap.getOrElse( (colIndex, group), 0L ) + val prevSum = sumMap.getOrElse((colIndex, group), 0L) + //don't add the count, just as one, because we are looking for the distinct values + val nextCounts = countMap.updated((colIndex, group), prevCount + 1 ) + val nextSums = sumMap.updated((colIndex, group), prevSum + count) + ( nextCounts, nextSums) } , + (a , b ) => { + val (aCounts, aSums) = a + val (bCounts, bSums) = b + + val nextCounts = HashMapUtil.mergeMaps[(Int, String), Long](aCounts, bCounts, (a :Long , b : Long ) => a+b) + val nextSums = HashMapUtil.mergeMaps[(Int, String), Long](aSums, bSums , (a :Long , b : Long ) => a+b) + (nextCounts, nextSums) + }) keyPair } //return iterator of (colIndex,group)-> value, then make it into a map. From 93865a6a206ab1166b74ab83c8cbd3bbce0f62ab Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 8 Oct 2015 13:57:30 -0700 Subject: [PATCH 011/198] update tag format --- .../GoldiLocks/QuantileOnlyArtisanalTest.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index 27f2244..303d1a1 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -5,7 +5,7 @@ import org.apache.spark.rdd._ import org.scalatest.{BeforeAndAfterAll, FunSuite} -// START-TEST +// tag:MAGIC-PANDA[] class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { @transient private var _sc: SparkContext = _ def sc: SparkContext = _sc @@ -32,4 +32,4 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { super.afterAll() } } -// END-TEST +// end:MAGIC-PANDA[] From e678b1e073da7de06feb86802938492e228b8bb1 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 8 Oct 2015 14:30:09 -0700 Subject: [PATCH 012/198] use _ for tag --- .../GoldiLocks/QuantileOnlyArtisanalTest.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index 303d1a1..b890423 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -5,7 +5,7 @@ import org.apache.spark.rdd._ import org.scalatest.{BeforeAndAfterAll, FunSuite} -// tag:MAGIC-PANDA[] +// tag:MAGIC_PANDA[] class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { @transient private var _sc: SparkContext = _ def sc: SparkContext = _sc @@ -32,4 +32,4 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { super.afterAll() } } -// end:MAGIC-PANDA[] +// end:MAGIC_PANDA[] From 197105285e0bdae54cec31f5e62d229fb1ec38f9 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 10 Oct 2015 15:50:42 -0700 Subject: [PATCH 013/198] update tag to :: --- .../GoldiLocks/QuantileOnlyArtisanalTest.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index b890423..94275ae 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -5,7 +5,7 @@ import org.apache.spark.rdd._ import org.scalatest.{BeforeAndAfterAll, FunSuite} -// tag:MAGIC_PANDA[] +// tag::MAGIC_PANDA[] class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { @transient private var _sc: SparkContext = _ def sc: SparkContext = _sc @@ -32,4 +32,4 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { super.afterAll() } } -// end:MAGIC_PANDA[] +// end::MAGIC_PANDA[] From d33c2716b7159f28a838ca1a7ab808b65fbcda6c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 10 Oct 2015 18:47:34 -0700 Subject: [PATCH 014/198] add two kind of simple examples --- .../dataframe/HappyPandas.scala | 11 ++++++++++ .../tokenize/SampleTokenize.scala | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala create mode 100644 src/main/scala/com/high-performance-spark-examples/tokenize/SampleTokenize.scala diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala new file mode 100644 index 0000000..42e4b17 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -0,0 +1,11 @@ +/** + * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. + */ +import org.apache.spark.sql.DataFrame + +object HappyPanda { + def happyPandas(pandaInfo: DataFrame): DataFrame = { + pandaInfo.select(pandaInfo("place"), + (pandaInfo("happy_pandas") / pandaInfo("pandas")).as("percentHappy")) + } +} diff --git a/src/main/scala/com/high-performance-spark-examples/tokenize/SampleTokenize.scala b/src/main/scala/com/high-performance-spark-examples/tokenize/SampleTokenize.scala new file mode 100644 index 0000000..a941803 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/tokenize/SampleTokenize.scala @@ -0,0 +1,21 @@ +package com.highperformancespark.example.tokenize + +import org.apache.spark.rdd.RDD + +object SampleTokenize { + //tag::DIFFICULT + def difficultTokenizeRDD(input: RDD[String]) = { + input.flatMap(_.split(" ")) + } + //end::DIFFICULT + + //tag::EASY + def tokenizeRDD(input: RDD[String]) = { + input.flatMap(tokenize) + } + + protected[tokenize] def tokenize(input: String) = { + input.split(" ") + } + //end::EASY +} From ab0f11b7572dc6817bf50fbca6523371f71ddb99 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 10 Oct 2015 20:23:32 -0700 Subject: [PATCH 015/198] stop the spark context --- .../dataframe/HappyPandas.scala | 5 ++++- .../GoldiLocks/QuantileOnlyArtisanalTest.scala | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 42e4b17..67645f2 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -6,6 +6,9 @@ import org.apache.spark.sql.DataFrame object HappyPanda { def happyPandas(pandaInfo: DataFrame): DataFrame = { pandaInfo.select(pandaInfo("place"), - (pandaInfo("happy_pandas") / pandaInfo("pandas")).as("percentHappy")) + (pandaInfo("happyPandas") / pandaInfo("totalPandas")).as("percentHappy")) + } + def minHappyPandas(pandaInfo: DataFrame, num: Int): DataFrame = { + pandaInfo.filter(pandaInfo("happyPandas") >= num) } } diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index 94275ae..221ac80 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -27,6 +27,7 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { override def afterAll() { // We clear the driver port so that we don't try and bind to the same port on restart + sc.stop() System.clearProperty("spark.driver.port") _sc = null super.afterAll() From fe14bc82ef4d3d4b14542a76da19971958b7144e Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 10 Oct 2015 20:24:04 -0700 Subject: [PATCH 016/198] fix stuff --- .../dataframe/HappyPandas.scala | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala new file mode 100644 index 0000000..5eb42fc --- /dev/null +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -0,0 +1,43 @@ +/** + * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. + */ + +import org.apache.spark.sql.{DataFrame, Row} +import org.apache.spark.sql.types._ + +import com.holdenkarau.spark.testing._ + +import org.scalatest.FunSuite +import org.scalatest.exceptions.TestFailedException + +class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSuiteBase { + val inputList = List(PandaInfo("toronto", 2, 1), PandaInfo("san diego", 3, 2)) + + //tag::approxEqualDataFrames + test("verify simple happy pandas") { + val sqlCtx = sqlContext + import sqlCtx.implicits._ + val expectedResult = List(Row("toronto", 0.5), Row("san diego", 2/3.0)) + val expectedDf = sqlCtx.createDataFrame(sc.parallelize(expectedResult), + StructType(List(StructField("place", StringType), + StructField("percentHappy", DoubleType)))) + val inputDF = sqlCtx.createDataFrame(inputList) + val result = HappyPanda.happyPandas(inputDF) + approxEqualDataFrames(expectedDf, result, 1E-5) + } + //end::approxEqualDataFrames + + //tag::exactEqualDataFrames + test("verify exact equality") { + val sqlCtx = sqlContext + import sqlCtx.implicits._ + val inputDF = sqlCtx.createDataFrame(inputList) + val result = HappyPanda.minHappyPandas(inputDF, 2) + val resultRows = result.collect() + assert(List(Row("san diego", 3, 2)) === resultRows) + } + //end::exactEqualDataFrames + +} + +case class PandaInfo(place: String, totalPandas: Integer, happyPandas: Integer) From ae8fdcea3564d320680ca03dc42e4cb9066d28a3 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 11 Oct 2015 12:43:24 -0700 Subject: [PATCH 017/198] Add a quick function for generating some test like data --- .../tools/GenerateScalingData.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala new file mode 100644 index 0000000..1fd73bb --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -0,0 +1,15 @@ +import org.apache.spark._ +import org.apache.spark.rdd.RDD +import org.apache.spark.mllib.random.RandomRDDs + +object GenerateScalingData { + /** + * Generate a Goldilocks data set. We expect the key to follow an exponential + * distribution and the data its self to be normal. + */ + def generateGoldilocks(sc: SparkContext, size: Long): RDD[List[String]] = { + val keyRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt) + val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = 500) + keyRDD.zip(valuesRDD).map{case (k, v) => List(k.toString) ++ v.toArray.map(_.toString)} + } +} From b150cc321ab25416c9dcdf45171f9e40b2f58a46 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 11 Oct 2015 12:44:54 -0700 Subject: [PATCH 018/198] Add tags --- .../tools/GenerateScalingData.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index 1fd73bb..d32dce6 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -3,6 +3,7 @@ import org.apache.spark.rdd.RDD import org.apache.spark.mllib.random.RandomRDDs object GenerateScalingData { + // tag::MAGIC_PANDA /** * Generate a Goldilocks data set. We expect the key to follow an exponential * distribution and the data its self to be normal. @@ -12,4 +13,5 @@ object GenerateScalingData { val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = 500) keyRDD.zip(valuesRDD).map{case (k, v) => List(k.toString) ++ v.toArray.map(_.toString)} } + // end::MAGIC_PANDA } From 1b1874e9cd76196ff9e287b3c86f66c2e5043692 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 12 Oct 2015 21:29:25 -0700 Subject: [PATCH 019/198] Add some []s to the tags --- .../tokenize/SampleTokenize.scala | 8 ++++---- .../tools/GenerateScalingData.scala | 4 ++-- .../dataframe/HappyPandas.scala | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/tokenize/SampleTokenize.scala b/src/main/scala/com/high-performance-spark-examples/tokenize/SampleTokenize.scala index a941803..7fb4177 100644 --- a/src/main/scala/com/high-performance-spark-examples/tokenize/SampleTokenize.scala +++ b/src/main/scala/com/high-performance-spark-examples/tokenize/SampleTokenize.scala @@ -3,13 +3,13 @@ package com.highperformancespark.example.tokenize import org.apache.spark.rdd.RDD object SampleTokenize { - //tag::DIFFICULT + //tag::DIFFICULT[] def difficultTokenizeRDD(input: RDD[String]) = { input.flatMap(_.split(" ")) } - //end::DIFFICULT + //end::DIFFICULT[] - //tag::EASY + //tag::EASY[] def tokenizeRDD(input: RDD[String]) = { input.flatMap(tokenize) } @@ -17,5 +17,5 @@ object SampleTokenize { protected[tokenize] def tokenize(input: String) = { input.split(" ") } - //end::EASY + //end::EASY[] } diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index d32dce6..8031c42 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -3,7 +3,7 @@ import org.apache.spark.rdd.RDD import org.apache.spark.mllib.random.RandomRDDs object GenerateScalingData { - // tag::MAGIC_PANDA + // tag::MAGIC_PANDA[] /** * Generate a Goldilocks data set. We expect the key to follow an exponential * distribution and the data its self to be normal. @@ -13,5 +13,5 @@ object GenerateScalingData { val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = 500) keyRDD.zip(valuesRDD).map{case (k, v) => List(k.toString) ++ v.toArray.map(_.toString)} } - // end::MAGIC_PANDA + // end::MAGIC_PANDA[] } diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 5eb42fc..94ad333 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -13,7 +13,7 @@ import org.scalatest.exceptions.TestFailedException class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSuiteBase { val inputList = List(PandaInfo("toronto", 2, 1), PandaInfo("san diego", 3, 2)) - //tag::approxEqualDataFrames + //tag::approxEqualDataFrames[] test("verify simple happy pandas") { val sqlCtx = sqlContext import sqlCtx.implicits._ @@ -25,9 +25,9 @@ class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSui val result = HappyPanda.happyPandas(inputDF) approxEqualDataFrames(expectedDf, result, 1E-5) } - //end::approxEqualDataFrames + //end::approxEqualDataFrames[] - //tag::exactEqualDataFrames + //tag::exactEqualDataFrames[] test("verify exact equality") { val sqlCtx = sqlContext import sqlCtx.implicits._ @@ -36,7 +36,7 @@ class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSui val resultRows = result.collect() assert(List(Row("san diego", 3, 2)) === resultRows) } - //end::exactEqualDataFrames + //end::exactEqualDataFrames[] } From ccf4c9d12d8c404e7cb94db736b4d7d0feed66cc Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 15 Oct 2015 16:51:55 -0700 Subject: [PATCH 020/198] refactoring Quantile to be a static class --- .../GoldiLocks/QuantileOnly.scala | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala index fbe57d6..9e92ffa 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala @@ -12,43 +12,46 @@ import scala.reflect.runtime.{universe => ru} * valPairs is an K/V RDD of ((Value, ColumnIndex), OccurenceCount) * colIndexList is the list of column indexes we wish to extract ranks for */ -class QuantileWithHashMap(val valPairs: RDD[((Double, Int), Long)], val colIndexList: List[Int], targetRanks: List[Long]) { +object QuantileWithHashMap { /* * n is value of the last column index in the valPairs. It represents the width of the part of the dataset * that we care about. It is possible that n will be greater than the number * of columns if some columns between 0 and n are not included */ - val n = colIndexList.last+1 /** * @return A map of colIndex -> Array of rank stats for column indices (corresponding to the class param) */ - def findQuantiles( ) = { + def findQuantiles( valPairs: RDD[((Double, Int), Long)], colIndexList: List[Int], targetRanks: List[Long] ) = { + val n = colIndexList.last+1 val sorted = valPairs.sortByKey() sorted.persist(StorageLevel.MEMORY_AND_DISK) val parts : Array[Partition] = sorted.partitions - val map1 = getTotalsForeachPart(sorted, parts.length) - val map2 = getLocationsOfRanksWithinEachPart(map1) + val map1 = getTotalsForeachPart(sorted, parts.length, n ) + val map2 = getLocationsOfRanksWithinEachPart(targetRanks, map1, n) val result = findElementsIteratively(sorted, map2) result.groupByKey().collectAsMap() } - def findQuantilesWithCustomStorage(storageLevel: StorageLevel, checkPoint : Boolean, directory : String = "") = { + def findQuantilesWithCustomStorage(valPairs: RDD[((Double, Int), Long)], + colIndexList: List[Int], + targetRanks: List[Long], + storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK, + checkPoint : Boolean, directory : String = "") = { + + val n = colIndexList.last+1 val sorted = valPairs.sortByKey() if(storageLevel!=StorageLevel.NONE){ sorted.persist(storageLevel) } if(checkPoint){ sorted.sparkContext.setCheckpointDir(directory) - if(storageLevel.equals(StorageLevel.NONE)){ - println("Warning: Checkpointing without storing can be very slow. Consider changing Storage level Param ") - } sorted.checkpoint() } val parts : Array[Partition] = sorted.partitions - val map1 = getTotalsForeachPart(sorted, parts.length) - val map2 = getLocationsOfRanksWithinEachPart(map1) + val map1 = getTotalsForeachPart(sorted, parts.length, n) + val map2 = getLocationsOfRanksWithinEachPart(targetRanks, map1, n) val result = findElementsIteratively(sorted, map2) result.groupByKey().collectAsMap() } @@ -58,7 +61,7 @@ class QuantileWithHashMap(val valPairs: RDD[((Double, Int), Long)], val colIndex * @param numPartitions * @return an RDD the length of the number of partitions, where each row is a triple (partition Index */ - def getTotalsForeachPart(sorted: RDD[((Double, Int), Long)], numPartitions: Int) = { + private def getTotalsForeachPart(sorted: RDD[((Double, Int), Long)], numPartitions: Int, n : Int ) = { val zero = Array.fill[Long](n)(0) sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { val keyPair : Array[Long] = it.aggregate(zero)( @@ -78,7 +81,8 @@ class QuantileWithHashMap(val valPairs: RDD[((Double, Int), Long)], val colIndex * @return and Array, locations where locations(i) = (i, list of each (colIndex, Value) * in that partition value pairs that correspond to one of the target rank statistics for that col */ - def getLocationsOfRanksWithinEachPart(partitionMap : Array[(Int, Array[Long])]) : Array[(Int, List[(Int, Long)])] = { + private def getLocationsOfRanksWithinEachPart(targetRanks : List[Long], + partitionMap : Array[(Int, Array[Long])], n : Int ) : Array[(Int, List[(Int, Long)])] = { val runningTotal = Array.fill[Long](n)(0) partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> { val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() @@ -102,19 +106,19 @@ class QuantileWithHashMap(val valPairs: RDD[((Double, Int), Long)], val colIndex * @return An iterator of columnIndex, value pairs which correspond only to the values at which are * rank statistics. */ - def findElementsIteratively(sorted : RDD[((Double, Int), Long)], locations : Array[(Int, List[(Int, Long)])]) = { + private def findElementsIteratively(sorted : RDD[((Double, Int), Long)], locations : Array[(Int, List[(Int, Long)])]) = { sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { val targetsInThisPart = locations(index)._2 val len = targetsInThisPart.length if(len >0 ) { - val newIt = PartitionProcessingUtil2.getNewValues(it, targetsInThisPart) + val newIt = PartitionProcessingUtil.getNewValues(it, targetsInThisPart) newIt} else Iterator.empty } ) } } -object PartitionProcessingUtil2 extends Serializable { +object PartitionProcessingUtil extends Serializable { def getNewValues(it: Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)]): Iterator[(Int, Double)] = { val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) From 1056ae91845926de0f2a58a7e320b750a93b1823 Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 15 Oct 2015 18:24:01 -0700 Subject: [PATCH 021/198] "fixing the build with refactor" --- .../GoldiLocks/QuantileOnly.scala | 57 ++++++++++++------- .../QuantileOnlyArtisanalTest.scala | 2 +- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala index 9e92ffa..6b5facd 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala @@ -111,31 +111,46 @@ object QuantileWithHashMap { val targetsInThisPart = locations(index)._2 val len = targetsInThisPart.length if(len >0 ) { - val newIt = PartitionProcessingUtil.getNewValues(it, targetsInThisPart) - newIt} - else Iterator.empty - } ) - } -} - -object PartitionProcessingUtil extends Serializable { - - def getNewValues(it: Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)]): Iterator[(Int, Double)] = { - val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) - val keysInThisPart = targetsInThisPart.map(_._1).distinct - val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() - keysInThisPart.foreach(key => runningTotals += ((key, 0L))) - val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() - it.foreach { case ((value, colIndex), count) => { - if (keysInThisPart.contains(colIndex) ) { - val total = runningTotals(colIndex) - val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) - ranksPresent.foreach(r => { + val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) + val keysInThisPart = targetsInThisPart.map(_._1).distinct + val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() + keysInThisPart.foreach(key => runningTotals += ((key, 0L))) + val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + it.foreach { case ((value, colIndex), count) => { + if (keysInThisPart.contains(colIndex) ) { + val total = runningTotals(colIndex) + val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) + ranksPresent.foreach(r => { newIt += ((colIndex, value)) - }) + }) runningTotals.update(colIndex, total + count) } }} newIt.toIterator + } + else Iterator.empty + } ) } } + +// object FindElements extends Serializable { + +// def getNewValues(it: Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)]): Iterator[(Int, Double)] = { +// val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) +// val keysInThisPart = targetsInThisPart.map(_._1).distinct +// val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() +// keysInThisPart.foreach(key => runningTotals += ((key, 0L))) +// val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() +// it.foreach { case ((value, colIndex), count) => { +// if (keysInThisPart.contains(colIndex) ) { +// val total = runningTotals(colIndex) +// val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) +// ranksPresent.foreach(r => { +// newIt += ((colIndex, value)) +// }) +// runningTotals.update(colIndex, total + count) +// } +// }} +// newIt.toIterator +// } +// } diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index 221ac80..fcfc3da 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -20,7 +20,7 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { test("retrieve quantiles") { val input: RDD[((Double, Int), Long)] = sc.parallelize( List(((2.0, 1), 10L), ((1.0, 1), 5L), ((3.0, 1), 4L))) - val result = new QuantileWithHashMap(input, List(1), List(1L, 6L)).findQuantiles() + val result = QuantileWithHashMap.findQuantiles(input, List(1), List(1L, 6L)) val expected = List(1.0, 2.0) assert(expected === result(1).toList) } From 923fb3334c44117d0977bca040c05e48d66f1760 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 17 Oct 2015 17:09:18 -0700 Subject: [PATCH 022/198] Add createHiveContext and createSqlContext --- build.sbt | 2 +- .../dataframe/HappyPandas.scala | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 4dec3b8..a8f4eaf 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,7 @@ sparkVersion := "1.5.1" spDependencies += "holdenk/spark-testing-base:0.1.3" -sparkComponents ++= Seq("core", "streaming", "sql", "mllib") +sparkComponents ++= Seq("core", "streaming", "sql", "hive", "mllib") parallelExecution in Test := false diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 67645f2..754465c 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -1,9 +1,32 @@ /** * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. */ + +import org.apache.spark._ +import org.apache.spark.sql._ +import org.apache.spark.sql.hive._ import org.apache.spark.sql.DataFrame object HappyPanda { + // How to create a HiveContext or SQLContext with an existing SparkContext + def sqlContext(sc: SparkContext): SQLContext = { + //tag::createSQLContext[] + val sqlContext = new SQLContext(sc) + // Import the implicits, unlike in core Spark the implicits are defined on the context + import sqlContext.implicits._ + //end::createSQLContext[] + sqlContext + } + + def hiveContext(sc: SparkContext): HiveContext = { + //tag::createHiveContext[] + val hiveContext = new HiveContext(sc) + // Import the implicits, unlike in core Spark the implicits are defined on the context + import hiveContext.implicits._ + //end::createHiveContext[] + hiveContext + } + def happyPandas(pandaInfo: DataFrame): DataFrame = { pandaInfo.select(pandaInfo("place"), (pandaInfo("happyPandas") / pandaInfo("totalPandas")).as("percentHappy")) From 504ba6f866a622bd6685024fa01d9db7386db47d Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 17 Oct 2015 17:09:53 -0700 Subject: [PATCH 023/198] add a tag around the components --- build.sbt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sbt b/build.sbt index a8f4eaf..e77ca6d 100644 --- a/build.sbt +++ b/build.sbt @@ -16,7 +16,9 @@ sparkVersion := "1.5.1" spDependencies += "holdenk/spark-testing-base:0.1.3" +//tag::sparkComponents[] sparkComponents ++= Seq("core", "streaming", "sql", "hive", "mllib") +//end::sparkComponents[] parallelExecution in Test := false From 8fe754f1f5aeb1d6ea99b8c9a132cca0e8e3252c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 17 Oct 2015 20:25:09 -0700 Subject: [PATCH 024/198] correct name is examples --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index e77ca6d..ec60497 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ organization := "com.highperformancespark" -name := "spark-testing-base" +name := "examples" publishMavenStyle := true From 4fa1643e57de88a12074ed795ddf77f8514e1aca Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 21 Oct 2015 00:03:14 -0700 Subject: [PATCH 025/198] Add a few more spark sql examples --- .../dataframe/HappyPandas.scala | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 754465c..e1867ff 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -6,6 +6,8 @@ import org.apache.spark._ import org.apache.spark.sql._ import org.apache.spark.sql.hive._ import org.apache.spark.sql.DataFrame +import org.apache.spark.sql.catalyst.expressions.aggregate._ +import org.apache.spark.sql.functions._ object HappyPanda { // How to create a HiveContext or SQLContext with an existing SparkContext @@ -31,7 +33,32 @@ object HappyPanda { pandaInfo.select(pandaInfo("place"), (pandaInfo("happyPandas") / pandaInfo("totalPandas")).as("percentHappy")) } + def minHappyPandas(pandaInfo: DataFrame, num: Int): DataFrame = { pandaInfo.filter(pandaInfo("happyPandas") >= num) } + + //tag::maxPandaSizePerZip[] + def maxPandaSizePerZip(pandas: DataFrame): DataFrame = { + pandas.groupBy(pandas("zip")).max("pandasize") + } + //end::maxPandaSizePerZip[] + + //tag::minMaxPandasSizePerZip[] + def minMaxPandaSizePerZip(pandas: DataFrame): DataFrame = { + // List of strings + pandas.groupBy(pandas("zip")).agg(("min", "pandasize"), ("max", "pandasize")) + // Map of column to aggregate + pandas.groupBy(pandas("zip")).agg(Map("pandasize" -> "min", + "pandasize" -> "max")) + // expression literals + } + //end::minMaxPandasSizePerZip[] + + //tag::complexAggPerZip[] + def complexAggPerZip(pandas: DataFrame): DataFrame = { + // Compute the min and mean + pandas.groupBy(pandas("zip")).agg(min(pandas("pandasize")), mean(pandas("pandasize"))) + } + //end::complexAggPerZip[] } From 32653b7585accd2e8896e857cdbc2d9049222b71 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 25 Oct 2015 13:35:25 -0700 Subject: [PATCH 026/198] More SQL examples --- .../dataframe/HappyPandas.scala | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index e1867ff..1bfd8db 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -29,28 +29,43 @@ object HappyPanda { hiveContext } + // Illustrate loading some JSON data + def loadDataSimple(sqlCtx: SQLContext, path: String): DataFrame = { + //tag::loadPandaJSONSimple[] + sqlCtx.read.json(path) + //end::loadPandaJSONSimple[] + } + def happyPandas(pandaInfo: DataFrame): DataFrame = { pandaInfo.select(pandaInfo("place"), (pandaInfo("happyPandas") / pandaInfo("totalPandas")).as("percentHappy")) } + //tag::simpleFilter[] def minHappyPandas(pandaInfo: DataFrame, num: Int): DataFrame = { pandaInfo.filter(pandaInfo("happyPandas") >= num) } + //end::simpleFilter[] + + //tag::complexFilter[] + def minHappyPandas(pandaInfo: DataFrame, num: Int): DataFrame = { + pandaInfo.filter(pandaInfo("happyPandas") >= pandaInfo("totalPandas") * 2) + } + //end::complexFilter[] //tag::maxPandaSizePerZip[] def maxPandaSizePerZip(pandas: DataFrame): DataFrame = { - pandas.groupBy(pandas("zip")).max("pandasize") + pandas.groupBy(pandas("zip")).max("pandaSize") } //end::maxPandaSizePerZip[] //tag::minMaxPandasSizePerZip[] def minMaxPandaSizePerZip(pandas: DataFrame): DataFrame = { // List of strings - pandas.groupBy(pandas("zip")).agg(("min", "pandasize"), ("max", "pandasize")) + pandas.groupBy(pandas("zip")).agg(("min", "pandaSize"), ("max", "pandaSize")) // Map of column to aggregate - pandas.groupBy(pandas("zip")).agg(Map("pandasize" -> "min", - "pandasize" -> "max")) + pandas.groupBy(pandas("zip")).agg(Map("pandaSize" -> "min", + "pandaSize" -> "max")) // expression literals } //end::minMaxPandasSizePerZip[] @@ -58,7 +73,7 @@ object HappyPanda { //tag::complexAggPerZip[] def complexAggPerZip(pandas: DataFrame): DataFrame = { // Compute the min and mean - pandas.groupBy(pandas("zip")).agg(min(pandas("pandasize")), mean(pandas("pandasize"))) + pandas.groupBy(pandas("zip")).agg(min(pandas("pandaSize")), mean(pandas("pandaSize"))) } //end::complexAggPerZip[] } From 9eb0e4f6677ddb8d9c0ebec8bca27dbf3c7838fe Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 25 Oct 2015 17:50:41 -0700 Subject: [PATCH 027/198] Add panda encoding example, fix second min happy panda filter to make sense and have a different name --- .../dataframe/HappyPandas.scala | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 1bfd8db..475152c 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -41,6 +41,16 @@ object HappyPanda { (pandaInfo("happyPandas") / pandaInfo("totalPandas")).as("percentHappy")) } + //tag::encodePandaType[] + def encodePandaType(pandaInfo: DataFrame, num: Int): DataFrame = { + pandaInfo.select(pandaInfo("pandaId"), + when(pandaInfo("pandaType") === "giant", 0). + when(pandaInfo("pandaType") === "red", 1). + otherwise(2) + ) + } + //end::encodePandaType[] + //tag::simpleFilter[] def minHappyPandas(pandaInfo: DataFrame, num: Int): DataFrame = { pandaInfo.filter(pandaInfo("happyPandas") >= num) @@ -48,8 +58,8 @@ object HappyPanda { //end::simpleFilter[] //tag::complexFilter[] - def minHappyPandas(pandaInfo: DataFrame, num: Int): DataFrame = { - pandaInfo.filter(pandaInfo("happyPandas") >= pandaInfo("totalPandas") * 2) + def minHappyPandasComplex(pandaInfo: DataFrame, num: Int): DataFrame = { + pandaInfo.filter(pandaInfo("happyPandas") >= pandaInfo("totalPandas") / 2) } //end::complexFilter[] From 3732712a785a41decb612be583e1ed80576dfdd7 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 25 Oct 2015 18:06:24 -0700 Subject: [PATCH 028/198] build fix --- build.sbt | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sbt b/build.sbt index ec60497..b5e95f8 100644 --- a/build.sbt +++ b/build.sbt @@ -30,6 +30,7 @@ libraryDependencies ++= Seq( "org.scalacheck" %% "scalacheck" % "1.12.4", "junit" % "junit" % "4.10", "org.eclipse.jetty" % "jetty-util" % "9.3.2.v20150730", + "org.codehaus.jackson" % "jackson-mapper-asl" % "1.8.8", "com.novocode" % "junit-interface" % "0.10" % "test->default") From e81c0db7896957ce60121c4aa6a939f225d2ec57 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 25 Oct 2015 18:23:54 -0700 Subject: [PATCH 029/198] Show a quick example of specifying the schema --- .../dataframe/HappyPandas.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 94ad333..5551225 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -2,7 +2,7 @@ * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. */ -import org.apache.spark.sql.{DataFrame, Row} +import org.apache.spark.sql.{SQLContext, DataFrame, Row} import org.apache.spark.sql.types._ import com.holdenkarau.spark.testing._ @@ -38,6 +38,13 @@ class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSui } //end::exactEqualDataFrames[] + + def loadPandaStuffies(sqlCtx: SQLContext): DataFrame = { + val pandaStuffies = List(Row("ikea", null), Row("tube", 6), Row("real", 30)) + val schema = StructType(List(StructField("name", StringType, true), + StructField("age", IntegerType, true))) + sqlCtx.createDataFrame(sc.parallelize(pandaStuffies), schema) + } } case class PandaInfo(place: String, totalPandas: Integer, happyPandas: Integer) From e2231de099ca2646235160fb09d227e4b3cfb623 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 26 Oct 2015 23:47:07 -0700 Subject: [PATCH 030/198] enable fork --- build.sbt | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sbt b/build.sbt index b5e95f8..eed1462 100644 --- a/build.sbt +++ b/build.sbt @@ -21,6 +21,7 @@ sparkComponents ++= Seq("core", "streaming", "sql", "hive", "mllib") //end::sparkComponents[] parallelExecution in Test := false +fork := true javaOptions ++= Seq("-Xms512M", "-Xmx2048M", "-XX:MaxPermSize=2048M", "-XX:+CMSClassUnloadingEnabled") From f8910d1d1caf9d431f2b2e4892e3325eb75f603f Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 27 Oct 2015 00:11:01 -0700 Subject: [PATCH 031/198] Add verify approx by hand example --- .../dataframe/HappyPandas.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 5551225..9f4a61d 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -8,6 +8,7 @@ import org.apache.spark.sql.types._ import com.holdenkarau.spark.testing._ import org.scalatest.FunSuite +import org.scalatest.Matchers._ import org.scalatest.exceptions.TestFailedException class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSuiteBase { @@ -27,6 +28,22 @@ class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSui } //end::approxEqualDataFrames[] + test("verify approx by hand") { + val sqlCtx = sqlContext + import sqlCtx.implicits._ + val expectedRows = List(Row("toronto", 0.5), Row("san diego", 2/3.0)) + val inputDF = sqlCtx.createDataFrame(inputList) + val result = HappyPanda.happyPandas(inputDF) + val resultRows = result.collect() + //tag::approxEqualRow[] + assert(expectedRows.size === resultRows.size) + expectedRows.zip(resultRows).foreach{case (r1, r2) => + assert(r1(0) === r2(0)) + assert(r1.getDouble(1) === (r2.getDouble(1) +- 0.001)) + } + //end::approxEqualRow[] + } + //tag::exactEqualDataFrames[] test("verify exact equality") { val sqlCtx = sqlContext From 927d81ea8afcb49c50158c7c4a80bd9acea17934 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 27 Oct 2015 00:23:55 -0700 Subject: [PATCH 032/198] We don't use sudo anyways, disable it and turn on the cache for ci --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index aa60a1e..a4bcf53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,8 @@ language: scala +sudo: false +cache: + directories: + - $HOME/.ivy2 scala: - 2.10.4 before_install: From a093fc2713acb0d4291226cb8b2c8aeeff192074 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Fri, 30 Oct 2015 00:14:05 -0700 Subject: [PATCH 033/198] Add a SampleData tool --- .../tools/SampleData.scala | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala diff --git a/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala b/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala new file mode 100644 index 0000000..af442c5 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala @@ -0,0 +1,28 @@ +import org.apache.spark._ +import org.apache.spark.rdd.RDD +import org.apache.spark.mllib.random.RandomRDDs + +/** + * Sample our production data to be able to use it for tests + */ +object SampleData { + /** + * Sample the input down to k % for usage in tests + */ + def sampleInput[T](rdd: RDD[T]): RDD[T] = { + // tag::randomSampleInput[] + rdd.sample(withReplacement=false, fraction=0.1) + // end::randomSampleInput[] + } + + /** + * Construct a stratified sample + */ + def stratifiedSample(rdd: RDD[(String, Array[Double])]): RDD[(String, Array[Double])] = { + // tag::stratifiedSample[] + // 5% of the red pandas, and 50% of the giant pandas + val stratas = Map("red" -> 0.05, "giant" -> 0.50) + rdd.sampleByKey(withReplacement=false, fractions = stratas) + // end::stratifiedSample[] + } +} From f1e6a5e9188eafce7abfdea6d1ceda892b4f85f9 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 1 Nov 2015 16:25:21 -0800 Subject: [PATCH 034/198] Start adding more on happy pandas magic --- .../dataframe/HappyPandas.scala | 21 +++++++++++++++++-- .../dataframe/HappyPandas.scala | 19 +++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 475152c..2610f77 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -4,10 +4,11 @@ import org.apache.spark._ import org.apache.spark.sql._ -import org.apache.spark.sql.hive._ -import org.apache.spark.sql.DataFrame import org.apache.spark.sql.catalyst.expressions.aggregate._ +import org.apache.spark.sql.DataFrame +import org.apache.spark.sql.expressions._ import org.apache.spark.sql.functions._ +import org.apache.spark.sql.hive._ object HappyPanda { // How to create a HiveContext or SQLContext with an existing SparkContext @@ -86,4 +87,20 @@ object HappyPanda { pandas.groupBy(pandas("zip")).agg(min(pandas("pandaSize")), mean(pandas("pandaSize"))) } //end::complexAggPerZip[] + + def computeRelativePandaSizes(pandas: DataFrame): DataFrame = { + //tag::relativePandaSizesWindow[] + val windowSpec = Window + .orderBy(pandas("age")) + .partitionBy(pandas("zip")) + .rowsBetween(start = 10, end = 10) // use rangeBetween for range instead + //end::relativePandaSizesWindow[] + //tag::relativePandaSizesQuery[] + val pandaRelativeSizeFunc = (pandas("pandaSize") - + avg(pandas("pandaSize")).over(windowSpec)) + pandas.select(pandas("name"), pandas("zip"), pandas("pandaSize"), + pandaRelativeSizeFunc.as("panda_relative_size")) + //end::relativePandaSizesQuery[] + } + } diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 9f4a61d..4065db1 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -55,11 +55,26 @@ class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSui } //end::exactEqualDataFrames[] + // Make a test once we have hivectx in the base + def futureTestRrelativePandaSize() { + val sqlCtx = sqlContext + import sqlCtx.implicits._ + // TODO: Generate some data instead of using the small static data + val inputDF = loadPandaStuffies(sqlCtx) + val result = HappyPanda.computeRelativePandaSizes(inputDF) + val resultRows = result.collect() + assert(List() === resultRows) + } def loadPandaStuffies(sqlCtx: SQLContext): DataFrame = { - val pandaStuffies = List(Row("ikea", null), Row("tube", 6), Row("real", 30)) + val pandaStuffies = List(Row("ikea", null, 0.2, 94110), + Row("tube", 6, 0.4, 94110), + Row("panda", 6, 0.5, 94110), + Row("real", 30, 77.5, 100000)) val schema = StructType(List(StructField("name", StringType, true), - StructField("age", IntegerType, true))) + StructField("age", IntegerType, true), + StructField("pandaSize", DoubleType, true), + StructField("zip", IntegerType, true))) sqlCtx.createDataFrame(sc.parallelize(pandaStuffies), schema) } } From 53e385bdca7be49b1d79f704e2e8ce62474b8fc7 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 1 Nov 2015 19:39:38 -0800 Subject: [PATCH 035/198] Joins start --- .../dataframe/HappyPandas.scala | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 2610f77..815d2bd 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -88,6 +88,12 @@ object HappyPanda { } //end::complexAggPerZip[] + def orderPandas(pandas: DataFrame): DataFrame = { + //tag::simpleSort[] + pandas.orderBy(asc(pandas("size")), desc(pandas("age"))) + //end::simpleSort[] + } + def computeRelativePandaSizes(pandas: DataFrame): DataFrame = { //tag::relativePandaSizesWindow[] val windowSpec = Window @@ -103,4 +109,17 @@ object HappyPanda { //end::relativePandaSizesQuery[] } + // Join DataFrames of Pandas and Sizes with + def joins(df1: DataFrame, df2: DantaFrame): Unit = { + // Inner join implicit + df1.join(df2, df1("name") === df2("name")) + // Inner join explicit + df1.join(df2, df1("name") === df2("name"), "inner") + // Left outer join explicit + df1.join(df2, df1("name") === df2("name"), "left_outer") + // Right outer join explicit + df1.join(df2, df1("name") === df2("name"), "right_outer") + // Left semi join explicit + df1.join(df2, df1("name") === df2("name"), "leftsemi") + } } From 3a3b0645611cef6a62d2b40433ddc8a747e07f2a Mon Sep 17 00:00:00 2001 From: Rachel Date: Wed, 28 Oct 2015 18:38:52 -0700 Subject: [PATCH 036/198] refactoring goldilocks and adding comments --- .../GoldiLocks/DataFrameStatistics.scala | 201 -------------- .../GoldiLocks/GoldiLocksFirstTry.scala | 123 +++++++++ ...Only.scala => GoldiLocksWithHashMap.scala} | 174 +++++++------ .../GoldiLocks/QuantileAndDistinct.scala | 229 ---------------- .../GoldiLocks/RankStatsByGroup.scala | 246 ------------------ .../QuantileOnlyArtisanalTest.scala | 45 +++- 6 files changed, 251 insertions(+), 767 deletions(-) delete mode 100644 src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala create mode 100644 src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala rename src/main/scala/com/high-performance-spark-examples/GoldiLocks/{QuantileOnly.scala => GoldiLocksWithHashMap.scala} (53%) delete mode 100644 src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala delete mode 100644 src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala deleted file mode 100644 index 730c75c..0000000 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/DataFrameStatistics.scala +++ /dev/null @@ -1,201 +0,0 @@ -package com.highperformancespark.examples.goldilocks - -import org.apache.spark.rdd.RDD -import org.apache.spark.sql.{DataFrame, Row} - -import scala.collection.immutable.HashMap - -/** - * Methods for computing statics on columns in a dataFrame - */ -object DataFrameStatistics { - //toDO: Right now all the keys have to fit in memory... maybe that is a bad idea? - - def findMedianAndDistinctByKey(inputData: DataFrame, medianColumns: List[Int], - distinctColumns: List[Int], groupByIndices: List[Int]) = { - val allColumns = (medianColumns ++ distinctColumns).distinct - val valPairs = DataFrameProcessingUtils.createHashMapBySeveralGroups(inputData, - allColumns, groupByIndices) - val calculator = new RankStatsByGroup(valPairs, allColumns) - val (mapOfMedians, distinct) = calculator.getMedianAndDistinct() - val medianCols = mapMediansToWide(mapOfMedians, medianColumns) - //add the distinct values - medianCols.map{ case ((group, ar )) => - val distinctRow = distinctColumns.map( i => distinct.getOrElse((i, group), -1L ) ) - (group , (ar, distinctRow)) - } - } - - /** - * Maps the result of the median to one row per group - */ - private def mapMediansToWide( - medianMap: RDD[((Int, String), - Double )], medians: List[Int] - ): RDD[(String, Array[Double])] = { - val locationMedianIndexMap = medians.zipWithIndex.toMap - medianMap.sparkContext.broadcast(locationMedianIndexMap) - val zero = Array.fill[Double](medians.length)(Double.NaN) - val useGroupAsKey = medianMap.mapPartitions( - iter => iter.map{ - case (((index, group), median)) => - (group, (locationMedianIndexMap.get(index), median )) - }.filter( - x => x._2._1.isDefined - ).map{ - case ((group, (Some(i), v ))) => (group, (i, v)) - } - ) - useGroupAsKey.aggregateByKey(zero)( - seqOp = - (row, x) => { - val (index, value) = x - row.updated(index, value) - }, - combOp = - (a, b) => a.zip(b).map { case ((x, y)) => - val (xNan, yNan) = (x.isNaN, y.isNaN) - (xNan, yNan) match { - case (true, true) => Double.NaN - case (false, true) => x - case (true, false) => y - //this is a non exhaustive match, if both x and y have values we should throw exception } - } - }) - } -} - -/** - * Methods to process data frames to key/value pairs. - */ -object DataFrameProcessingUtils{ - //this delim is used to seperate the keys, its ugly but it works - val keyDelim : String = "_alpine> { - val hashMap = new collection.mutable.HashMap[(Double, Int), Long]() - it.foreach( row => { - activeCols.foreach( i => { - val v = row.get(i).toString.toDouble - val key = (v, i) - val count = hashMap.getOrElseUpdate(key, 0) - hashMap.update(key, count + 1 ) - }) - }) - val newM = hashMap.toArray - newM.toIterator - }) - map - } - - /** - * @param groupByIndex the index of the groupBy Column - * @return rdd with ((cellValue, (cellIndex, groupByValue)), count) - */ - def createHashMapByGroup(inputData : DataFrame, - activeCols : Seq[Int], groupByIndex : Int - ) : RDD[((Double, (Int, String)), Long)] = { - val map = inputData.rdd.mapPartitions(it => { - val hashMap = new collection.mutable.HashMap[(Double, (Int, String)), Long]() - it.foreach( row => { - val groupKey = row.get(groupByIndex).toString - activeCols.foreach( i => { - val v = row.get(i).toString.toDouble - val key = (v, (i, groupKey)) - val count = hashMap.getOrElseUpdate(key, 0) - hashMap.update(key, count + 1 ) - }) - }) - val newM = hashMap.toArray - newM.toIterator - }) - map - } - - /** - * Creates an - * @param groupByIndices the index of the groupBy Column - * @return rdd with ((cellValue, (cellIndex, groupByValue)), count) - */ - def createHashMapBySeveralGroups(inputData : DataFrame, - activeCols : Seq[Int], groupByIndices : List[Int] - ) : RDD[((Double, (Int, String)), Long)] = { - val map = inputData.rdd.mapPartitions(it => { - val hashMap = new collection.mutable.HashMap[(Double, (Int, String)), Long]() - it.foreach( row => { - val groupKey = buildKey(row, groupByIndices) - activeCols.foreach( i => { - val v = row.get(i).toString.toDouble - val key = (v, (i, groupKey)) - val count = hashMap.getOrElseUpdate(key, 0) - hashMap.update(key, count + 1 ) - }) - }) - val newM = hashMap.toArray - newM.toIterator - }) - map - } - - /** - * * Used in for the MultivariateStatisticalSumerizer methods. - * Maps a data frame to key value pair of (group, vector) - - * @param inputData the data frame - * @param activeCols the columns that will be used in the operation - * @param groupByIndices the indices of the group by columns from which we will build the key - * @return - */ - def mapToKeyedVectors(inputData : DataFrame, activeCols : Array[Int], groupByIndices : List[Int ]) = { - inputData.map( row => { - val key = buildKey(row, groupByIndices) - val vector = org.apache.spark.mllib.linalg.Vectors.dense(activeCols.map( - i => row.get(i).toString.toDouble) - ) - (key, vector) - }) - } - - /** - * Build the key for the vector or HashMap by combining the values of the group by columns - * at each row. - * Uses the special delimiter field of this class. - */ - protected def buildKey(row : Row , groupByKeys : List[Int]) : String = { - val key : StringBuilder = new StringBuilder - key.append(row.get(groupByKeys.head).toString) - groupByKeys.tail.foreach( index => { - key.append(keyDelim + row.get(index).toString ) - }) - key.toString() - } - - /** - * Splits the concatenated key into an array - */ - def getKey(key : String ): Array[String] ={ - val s = key.split(keyDelim) - if(s.isEmpty) Array(key) else s - } -} - -/** - * Useful when using HashMaps as Accumulators - */ -object HashMapUtil extends Serializable { - def mergeMaps[A, B]( - mapA : HashMap[A, B], - mapB : HashMap[A, B], - merge : (B,B)=> B) : HashMap[A,B] = { - var nextMap = mapB - //add thing to map B - mapA.foreach{case ((key, aValue)) => - val bValue = mapB.get(key) - nextMap = bValue match { - case (Some(v)) => nextMap.updated(key, merge(aValue, v)) - case None => nextMap.updated(key, aValue) - } - } - nextMap - } -} diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala new file mode 100644 index 0000000..999bfd3 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala @@ -0,0 +1,123 @@ +package com.highperformancespark.examples.goldilocks + +import org.apache.spark.Partition +import org.apache.spark.rdd.RDD +import org.apache.spark.sql.DataFrame +import org.apache.spark.storage.StorageLevel + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer + + +object GoldiLocksFirstTry { + + + def findQuantiles( dataFrame: DataFrame, targetRanks: List[Long] ) = { + val n = dataFrame.schema.length + val valPairs: RDD[(Double, Int)] = getPairs(dataFrame) + val sorted = valPairs.sortByKey() + sorted.persist(StorageLevel.MEMORY_AND_DISK) + val parts : Array[Partition] = sorted.partitions + val map1 = getTotalsForeachPart(sorted, parts.length, n ) + val map2 = getLocationsOfRanksWithinEachPart(targetRanks, map1, n) + val result = findElementsIteratively(sorted, map2) + result.groupByKey().collectAsMap() + } + + /** + * Step 1. Map the rows to pairs of (value, colIndex) + * @param dataFrame of double columns to compute the rank satistics for + * @return + */ + private def getPairs(dataFrame : DataFrame ): RDD[(Double, Int )] ={ + dataFrame.flatMap( row => row.toSeq.zipWithIndex.map{ case (v, index ) => + (v.toString.toDouble, index )}) + } + + /** + * Step 2. Find the number of elements for each column in each partition + * @param sorted + * @param numPartitions + * @param n the number of columns + * @return an RDD the length of the number of partitions, where each row contains + * - the partition index + * - an array, totalsPerPart where totalsPerPart(i) = the number of elements in column + * i on this partition + */ + private def getTotalsForeachPart(sorted: RDD[(Double, Int)], numPartitions: Int, n : Int ) = { + val zero = Array.fill[Long](n)(0) + sorted.mapPartitionsWithIndex((partitionIndex : Int, it : Iterator[(Double, Int)]) => { + val totalsPerPart : Array[Long] = it.aggregate(zero)( + (a : Array[Long], v : (Double ,Int)) => { + val (value, colIndex) = v + a(colIndex) = a(colIndex) + 1L + a + }, + (a : Array[Long], b : Array[Long]) => { + require(a.length == b.length) + a.zip(b).map{ case(aVal, bVal) => aVal + bVal} + }) + Iterator((partitionIndex, totalsPerPart)) + }).collect() + } + /** + * Step 3: For each Partition determine the index of the elements that are desired rank statistics + * @param partitionMap- the result of the previous method + * @return an Array, the length of the number of partitions where each row contains + * - the partition index + * - a list, relevantIndexList where relevantIndexList(i) = the index of an element on this + * partition that matches one of the target ranks + */ + private def getLocationsOfRanksWithinEachPart(targetRanks : List[Long], + partitionMap : Array[(Int, Array[Long])], n : Int ) : Array[(Int, List[(Int, Long)])] = { + val runningTotal = Array.fill[Long](n)(0) + partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> { + val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() + totals.zipWithIndex.foreach{ case (colCount, colIndex) => { + val runningTotalCol = runningTotal(colIndex) + runningTotal(colIndex) += colCount + val ranksHere = targetRanks.filter(rank => + (runningTotalCol <= rank && runningTotalCol + colCount >= rank) + ) + //for each of the rank statistics present add this column index and the index it will be + //at on this partition (the rank - the running total) + ranksHere.foreach(rank => { + relevantIndexList += ((colIndex, rank-runningTotalCol)) + }) + }} + (partitionIndex, relevantIndexList.toList) + }} + } + + /** + * Step4: Using the results of the previous method, scan the data and return the elements + * which correspond to the rank statistics we are looking for in each column + */ + private def findElementsIteratively(sorted : RDD[(Double, Int)], locations : Array[(Int, List[(Int, Long)])]) = { + sorted.mapPartitionsWithIndex((index : Int, it : Iterator[(Double, Int)]) => { + val targetsInThisPart = locations(index)._2 + val len = targetsInThisPart.length + if (len > 0) { + val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) + val keysInThisPart = targetsInThisPart.map(_._1).distinct + val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() + keysInThisPart.foreach(key => runningTotals+=((key, 0L))) + val newIt : ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + it.foreach{ case( value, colIndex) => { + if(runningTotals isDefinedAt(colIndex)){ + val total = runningTotals(colIndex) + 1L + runningTotals.update(colIndex, total) + if(partMap(colIndex).contains(total)){ + newIt += ((colIndex,value )) + } + } + }} + newIt.toIterator + } + else { + Iterator.empty + } + }) + } + +} diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala similarity index 53% rename from src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala rename to src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala index 6b5facd..8f56531 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnly.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala @@ -2,29 +2,20 @@ package com.highperformancespark.examples.goldilocks import org.apache.spark.Partition import org.apache.spark.rdd.RDD +import org.apache.spark.sql.DataFrame import org.apache.spark.storage.StorageLevel import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.reflect.runtime.{universe => ru} -/** - * valPairs is an K/V RDD of ((Value, ColumnIndex), OccurenceCount) - * colIndexList is the list of column indexes we wish to extract ranks for - */ -object QuantileWithHashMap { - /* - * n is value of the last column index in the valPairs. It represents the width of the part of the dataset - * that we care about. It is possible that n will be greater than the number - * of columns if some columns between 0 and n are not included - */ - /** - * @return A map of colIndex -> Array of rank stats for column indices (corresponding to the class param) - */ - def findQuantiles( valPairs: RDD[((Double, Int), Long)], colIndexList: List[Int], targetRanks: List[Long] ) = { - val n = colIndexList.last+1 - val sorted = valPairs.sortByKey() +object GoldiLocksWithHashMap { + + def findQuantiles( dataFrame: DataFrame , targetRanks: List[Long] ) = { + val valueIndexCountPairs: RDD[((Double, Int), Long)] = createHashMap(dataFrame) + val n = dataFrame.schema.length + val sorted = valueIndexCountPairs.sortByKey() sorted.persist(StorageLevel.MEMORY_AND_DISK) val parts : Array[Partition] = sorted.partitions val map1 = getTotalsForeachPart(sorted, parts.length, n ) @@ -33,38 +24,42 @@ object QuantileWithHashMap { result.groupByKey().collectAsMap() } - - def findQuantilesWithCustomStorage(valPairs: RDD[((Double, Int), Long)], - colIndexList: List[Int], - targetRanks: List[Long], - storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK, - checkPoint : Boolean, directory : String = "") = { - - val n = colIndexList.last+1 - val sorted = valPairs.sortByKey() - if(storageLevel!=StorageLevel.NONE){ - sorted.persist(storageLevel) - } - if(checkPoint){ - sorted.sparkContext.setCheckpointDir(directory) - sorted.checkpoint() - } - val parts : Array[Partition] = sorted.partitions - val map1 = getTotalsForeachPart(sorted, parts.length, n) - val map2 = getLocationsOfRanksWithinEachPart(targetRanks, map1, n) - val result = findElementsIteratively(sorted, map2) - result.groupByKey().collectAsMap() + /** + * Step 1. Map the rows to pairs of ((value, colIndex), count) where count is the number of times + * that value and that pair appear on this partition + * @param dataFrame of double columns to compute the rank statistics for + * @return + */ + def createHashMap(dataFrame : DataFrame) : RDD[((Double, Int), Long)] = { + val map = dataFrame.rdd.mapPartitions(it => { + val hashMap = new mutable.HashMap[(Double, Int), Long]() + it.foreach( row => { + row.toSeq.zipWithIndex.foreach{ case (value, i) => { + val v = value.toString.toDouble + val key = (v, i) + val count = hashMap.getOrElseUpdate(key, 0) + hashMap.update(key, count + 1 ) + }} + }) + val newM = hashMap.toArray + newM.toIterator + }) + map } /** - * @param sorted - * @param numPartitions - * @return an RDD the length of the number of partitions, where each row is a triple (partition Index + * Step 2. Find the number of elements for each column in each partition + * @param sorted rdd of ((value, index), count) pairs which has already been sorted + * @param numPartitions the number of partitions + * @return an RDD the length of the number of partitions, where each row contains + * - the partition index + * - an array, totalsPerPart where totalsPerPart(i) = the number of elements in column + * i on this partition */ private def getTotalsForeachPart(sorted: RDD[((Double, Int), Long)], numPartitions: Int, n : Int ) = { val zero = Array.fill[Long](n)(0) sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { - val keyPair : Array[Long] = it.aggregate(zero)( + val totalsPerPart : Array[Long] = it.aggregate(zero)( (a : Array[Long], v : ((Double ,Int), Long)) => { val ((value, colIndex) , count) = v a(colIndex) = a(colIndex) + count @@ -73,13 +68,17 @@ object QuantileWithHashMap { require(a.length == b.length) a.zip(b).map{ case(aVal, bVal) => aVal + bVal} }) - Iterator((index, keyPair)) + Iterator((index, totalsPerPart)) }).collect() } + /** + * Step 3: For each Partition determine the index of the elements that are desired rank statistics * @param partitionMap- the result of the previous method - * @return and Array, locations where locations(i) = (i, list of each (colIndex, Value) - * in that partition value pairs that correspond to one of the target rank statistics for that col + * @return an Array, the length of the number of partitions where each row contains + * - the partition index + * - a list, relevantIndexList where relevantIndexList(i) = the index of an element on this + * partition that matches one of the target ranks */ private def getLocationsOfRanksWithinEachPart(targetRanks : List[Long], partitionMap : Array[(Int, Array[Long])], n : Int ) : Array[(Int, List[(Int, Long)])] = { @@ -101,12 +100,11 @@ object QuantileWithHashMap { } /** - * @param sorted - * @param locations - * @return An iterator of columnIndex, value pairs which correspond only to the values at which are - * rank statistics. + * Step4: Using the results of the previous method, scan the data and return the elements + * which correspond to the rank statistics we are looking for in each column */ - private def findElementsIteratively(sorted : RDD[((Double, Int), Long)], locations : Array[(Int, List[(Int, Long)])]) = { + private def findElementsIteratively(sorted : RDD[((Double, Int), Long)], + locations : Array[(Int, List[(Int, Long)])]) = { sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { val targetsInThisPart = locations(index)._2 val len = targetsInThisPart.length @@ -114,43 +112,53 @@ object QuantileWithHashMap { val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) val keysInThisPart = targetsInThisPart.map(_._1).distinct val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() - keysInThisPart.foreach(key => runningTotals += ((key, 0L))) - val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() - it.foreach { case ((value, colIndex), count) => { - if (keysInThisPart.contains(colIndex) ) { - val total = runningTotals(colIndex) - val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) - ranksPresent.foreach(r => { - newIt += ((colIndex, value)) - }) - runningTotals.update(colIndex, total + count) + keysInThisPart.foreach(key => runningTotals += ((key, 0L))) + val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + it.foreach { case ((value, colIndex), count) => { + if (keysInThisPart.contains(colIndex) ) { + val total = runningTotals(colIndex) + val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) + ranksPresent.foreach(r => { + newIt += ((colIndex, value)) + }) + runningTotals.update(colIndex, total + count) + } + }} + newIt.toIterator } - }} - newIt.toIterator - } else Iterator.empty } ) } -} -// object FindElements extends Serializable { + /** + * We will want to use this in some chapter where we talk about check pointing + * @param valPairs + * @param colIndexList + * @param targetRanks + * @param storageLevel + * @param checkPoint + * @param directory + * @return + */ + def findQuantilesWithCustomStorage(valPairs: RDD[((Double, Int), Long)], + colIndexList: List[Int], + targetRanks: List[Long], + storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK, + checkPoint : Boolean, directory : String = "") = { -// def getNewValues(it: Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)]): Iterator[(Int, Double)] = { -// val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) -// val keysInThisPart = targetsInThisPart.map(_._1).distinct -// val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() -// keysInThisPart.foreach(key => runningTotals += ((key, 0L))) -// val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() -// it.foreach { case ((value, colIndex), count) => { -// if (keysInThisPart.contains(colIndex) ) { -// val total = runningTotals(colIndex) -// val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) -// ranksPresent.foreach(r => { -// newIt += ((colIndex, value)) -// }) -// runningTotals.update(colIndex, total + count) -// } -// }} -// newIt.toIterator -// } -// } + val n = colIndexList.last+1 + val sorted = valPairs.sortByKey() + if(storageLevel!=StorageLevel.NONE){ + sorted.persist(storageLevel) + } + if(checkPoint){ + sorted.sparkContext.setCheckpointDir(directory) + sorted.checkpoint() + } + val parts : Array[Partition] = sorted.partitions + val map1 = getTotalsForeachPart(sorted, parts.length, n) + val map2 = getLocationsOfRanksWithinEachPart(targetRanks, map1, n) + val result = findElementsIteratively(sorted, map2) + result.groupByKey().collectAsMap() + } +} diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala deleted file mode 100644 index 2868f08..0000000 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/QuantileAndDistinct.scala +++ /dev/null @@ -1,229 +0,0 @@ -package com.highperformancespark.examples.goldilocks - -import org.apache.spark.rdd.RDD -import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer -import scala.reflect.runtime.{universe => ru} - -/** - * Computes Rank statistics and distinct values on data that has been mapped to - * Value, columnIndex, count triples. - *These methods aren't currently used in the Aggregation plugin, - * because the group by key field is required - * @param valPairs - * @param colIndexList - */ -class RankStats(valPairs : RDD[((Double, Int), Long )], - val colIndexList : List[Int]) { - private var reduced : RDD[((Double, Int), Long )] = _ - private var sorted : RDD[((Double, Int), Long )] = _ - val n = colIndexList.last+1 - - def getDistinctByColumn() = { - reduced = valPairs.reduceByKey((a, b) => a + b) - val countsByPartition = getDistinctForeachPart() - val distinct = getTotalDistinct(countsByPartition) - distinct - } - - def getMedianAndDistinct() = { - //toDo: use range partitioner to sort this - reduced = valPairs.reduceByKey((a, b) => a + b) - sorted = reduced.sortByKey() - val countAndSumByPart = getDistinctAndTotalForeachPart() - val (distinct, total) = getDistinctAndTotal(countAndSumByPart) - val half : Long = total.head / 2 - val sumsByPart = countAndSumByPart.sortBy(x => x._1)map{ case(index, (counts, sums ) ) => (index, sums)} - val mapOfLocations = getLocationsOfRanksWithinEachPart(sumsByPart, List(half)) - val medians = findElementsIteratively(mapOfLocations).groupByKey().collectAsMap() - (medians, distinct) - } - - /** - * Median and distinct step 1: - * Gets the number of distinct values and the total values by colIndex and group on each - * partition. Doing this in two steps should prevent large shuffles across the network. - * - * Returns with a triple for each partition with: - * 1. The index of the partition - * 2. A array of (colIndex) -> distinct values - * 3. A array of (colIndex) -> sum of Values - * (this is used in the 'getLocationsOfRanksWithinEachPart' method to calculate the medians) - */ - protected def getDistinctAndTotalForeachPart() = { - val zero = Array.fill[Long](n)(0) - sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { - val (countArray, sumArray) = PartitionProcessingUtil.totalAndDistinct(it, zero) - Iterator((index, (countArray, sumArray))) - }).collect() - } - - /** - * Median and distinct step 2. - * Given the per-partition distinct value and total counts gives us the total number of - * distinct values and the total number of values in the data set. - * Note: Right now, I am allowing for the columns to have different numbers or - * values, say because some of the cells had bad data, so the totals are for - * each column index. In the method that calculates medians, i assume that they are the same. - * - * Note: To just find the median, this step isn't really needed - * - * @param totalForEachPart result of the getDistinctForeachPart method - * - * @return A tupple of: - * 1. a array of (index) -> distinctValues - * 2. a array of (index) -> total values - */ - protected def getDistinctAndTotal(totalForEachPart : - Array[(Int, (Array[Long], Array[Long]))] ) = { - val runningCounts = Array.fill[Long](n)(0L) - val runningSums = Array.fill[Long](n)(0L) - var i = 0 - while (i < totalForEachPart.length){ - val (index, (countAr , sumAr)) = totalForEachPart(i) - countAr.zipWithIndex.foreach{ - case (count, colIndex) => runningCounts(colIndex) += count } - sumAr.zipWithIndex.foreach{ - case (sum, colIndex) => runningSums(colIndex) += sum } - i +=1 - } - (runningCounts, runningSums) - } - - - /** - * Median and distinct step 3: - * @param partitionMap- the result of the previous method - * @return and Array, locations where locations(i) = (i, list of each (colIndex, Value) - * in that partition value pairs that correspond to one of the target rank statistics for that col - */ - def getLocationsOfRanksWithinEachPart( - partitionMap : Array[(Int, Array[Long])], - targetRanks : List[Long ]) : Array[(Int, List[(Int, Long)])] = { - val runningTotal = Array.fill[Long](n)(0) - partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> { - val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() - totals.zipWithIndex.foreach{ case (colCount, colIndex) => { - val runningTotalCol = runningTotal(colIndex) - runningTotal(colIndex) += colCount - val ranksHere = targetRanks.filter(rank => - (runningTotalCol <= rank && runningTotalCol + colCount >= rank) - ) - ranksHere.foreach(rank => { - relevantIndexList += ((colIndex, rank-runningTotalCol)) - }) - }} //end of mapping col counts - (partitionIndex, relevantIndexList.toList) - }} - } - - /** - * Median and distinct step 4: - *Returns an iterator of columnIndex, - * value pairs which correspond only to the values at which are - * rank statistics. - */ - def findElementsIteratively(locations : Array[(Int, List[(Int, Long)])]) = { - sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { - val targetsInThisPart = locations(index)._2 - val len = targetsInThisPart.length - if(len >0 ) { - val newIt = PartitionProcessingUtil.getValuesFromRanks(it, targetsInThisPart) - newIt} - else Iterator.empty - } ) - } - - //Stuff used just to find distinct values bellow this - - /** - * @return an array of each partition and three values - * 1. An array of longs, which is the count for each column of the values on that partition - * 2. The first value of that partition - * 3. the last value on that partition - */ - def getDistinctForeachPart() : - Array[ Array[Long]] = { - val zero = Array.fill[Long](n)(0) - reduced.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { - - //toDo: Would it be better to keep as iterator and loop? - val keyPair : Array[Long] = it.aggregate(zero)( - (a , v : ((Double ,Int), Long)) => { - val ((value, colIndex) , count) = v - //don't add the count, just as one, because we are looking for the distinct values - a(colIndex) += 1 - a}, - (a : Array[Long], b : Array[Long]) => { - require(a.length == b.length) - a.zip(b).map{ case(aVal, bVal) => aVal + bVal} - }) - Iterator( keyPair) - }).collect() - } - - def getTotalDistinct(totalForEachPart : - (Array[Array[Long]]) ) = { - - val runningTotal = totalForEachPart(0) - var i = 1 - while (i < totalForEachPart.length){ - val countAr = totalForEachPart(i) - countAr.zipWithIndex.foreach{ - case (count, colIndex) => runningTotal(colIndex) += count } - - i +=1 - } - runningTotal - } - -} - -object PartitionProcessingUtil extends Serializable { - - def totalAndDistinct(it : Iterator[((Double, Int), Long)], zero : Array[Long] ) : (Array[Long], Array[Long]) = { - // val zero1 = Array.fill[Long](n)(0L) - val keyPair : (Array[Long], Array[Long]) = it.aggregate((Array.fill[Long](zero.length)(0L), Array.fill[Long](zero.length)(0L)))( - (acc : (Array[Long], Array[Long]) , v : ((Double ,Int), Long)) => { - val (a, sumAr ) = acc - val ((value, colIndex) , count) = v - a(colIndex) = a(colIndex) + 1 - sumAr(colIndex) = sumAr(colIndex) + count - (a, sumAr)}, - (a , b ) => { - val (aCounts, aSums) = a - val (bCounts, bSums) = b - require(aCounts.length == bCounts.length) - require(aSums.length == bSums.length) - val nextCounts = aCounts.zip(bCounts).map{ case(aVal, bVal) => aVal + bVal} - val nextSums = aSums.zip(bSums).map{case( aVal, bVal) => aVal + bVal} - (nextCounts, nextSums) - }) - keyPair - } - /** - * @param it iterator from the map partitions step in the find elements iteratively step - * @param targetsInThisPart the results of the 'getLocationsOfRanksWithinEachPart' method - * @return The nth value (or values, if the target ranks list is longer than one) - * for each column, if that nth value is in this partition. - */ - def getValuesFromRanks(it: Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)]): Iterator[(Int, Double)] = { - val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) - val keysInThisPart = targetsInThisPart.map(_._1).distinct - val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() - //toDo: refactor to not add 0L but user getOrElse update instead - keysInThisPart.foreach(key => runningTotals += ((key, 0L))) - val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() - it.foreach { case ((value, colIndex), count) => { - if (keysInThisPart.contains(colIndex) ) { - val total = runningTotals(colIndex) - val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) - ranksPresent.foreach(r => { - newIt += ((colIndex, value)) - }) - runningTotals.update(colIndex, total + count) - } - }} - newIt.toIterator - } -} diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala deleted file mode 100644 index c4058ae..0000000 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RankStatsByGroup.scala +++ /dev/null @@ -1,246 +0,0 @@ -package com.highperformancespark.examples.goldilocks - -import org.apache.spark.rdd.RDD - -import scala.collection.immutable.HashMap -import scala.collection.mutable.ArrayBuffer - -class RankStatsByGroup( valPairs : RDD[((Double, (Int, String)), Long)], - colIndexList : List[Int]){ - val n = colIndexList.last+1 - private var reduced : RDD[((Double, (Int, String)), Long)] = _ - private var sorted : RDD[((Double, (Int, String)), Long)] = _ - - /** - * - * @return A tupples with - * 1. KeyValueRDD of (index, group) -> Median - * 2. Hashmap of (index, group) -> # distinct values - */ - def getMedianAndDistinct () = { - reduced = valPairs.reduceByKey((a, b) => a + b) - sorted = reduced.sortByKey() - val countAndSumByPart = calculateDistinctAndTotalForeachPart() - val (distinct, total) = getTotalAndDistinct(countAndSumByPart) - val half : Long = total.get(valPairs.first()._1._2).get / 2 - val mapOfLocations = getLocationsOfRanksWithinEachPart(countAndSumByPart, List(half)) - val medians = findElementsIteratively( mapOfLocations).groupByKey().mapValues(_.head) - (medians, distinct) - } - - def getDistinct() = { - reduced = valPairs.reduceByKey((a, b) => a + b) - val countsByPartition = calculateDistinctForEachPart() - val distinct = getTotalDistinct(countsByPartition) - distinct - } - /** - * Median and distinct step 1: - * Gets the number of distinct values and the total values by colIndex and group on each - * partition. Doing this in two steps should prevent large shuffles across the network. - * - * Returns with a triple for each partition with: - * 1. The index of the partition - * 2. A HashMap of (colIndex, group) -> distinct values - * 3. A HashMap of (colIndex, group) -> sum of Values - * (this is used in the 'getLocationsOfRanksWithinEachPart' method to calculate the medians) - */ - protected def calculateDistinctAndTotalForeachPart() : - Array[ (Int, HashMap[(Int, String ), Long], HashMap[(Int, String ), Long])] = { - sorted.mapPartitionsWithIndex( - (index : Int, it : Iterator[((Double, (Int , String ) ), Long)]) => { - // val count = n.toLong.toInt - val (countArray, sumArray) = GroupedPartitionProcessingUtil.totalAndDistinctByGroup(it) - Iterator((index, countArray, sumArray)) - }).collect() - } - - /** - * Median and distinct step 2. - * Given the per-partition distinct value and total counts gives us the total number of - * distinct values and the total number of values in the data set. - * Note: Right now, I am allowing for the columns to have different numbers or - * values, say because some of the cells had bad data, so the totals are for - * each column index. In the method that calculates medians, i assume that they are the same. - * - * Note: To just find the median, this step isn't really needed - * - * @param totalForEachPart result of the getDistinctForeachPart method - * - * @return A tupple of: - * 1. a hashmap of (index, group) -> distinctValues - * 2. a hashmap of (index, group) -> total values - */ - protected def getTotalAndDistinct( - totalForEachPart : Array[(Int, HashMap[(Int,String ), Long ], - HashMap[(Int,String ), Long ])]) : (HashMap[(Int,String ), Long ], - HashMap[(Int,String ), Long ]) = { - totalForEachPart.map( - t => (t._2, t._3)).reduce( - (hashMap1, hashMap2) => { - val ( count1, sum1 ) = hashMap1 - val ( count2, sum2) = hashMap2 - val sumFunction = (a: Long , b: Long ) => a + b - val mergedCounts = HashMapUtil.mergeMaps(count1, count2, sumFunction) - val mergedSums = HashMapUtil.mergeMaps(sum1, sum2, sumFunction) - - (mergedCounts, mergedSums) - } ) - } - - /** - * Median and distinct step 3: - * Calculates the location of the desired rank statistics within each partition. - * @param partitionMap the result of the calculateDistinctAndTotalForeachPart method - * @param targetRanks the rank statistics we want to calculate for each index/ group. Median should - * be the size of the dataset/2 - * @return An array with a HashMap for each partition of - * (colIndex, group) -> List of the indices of the iterator on this partition that contain - * the rank statics that we are looking for. - */ - protected def getLocationsOfRanksWithinEachPart( - partitionMap : Array[(Int, HashMap[(Int, String), Long], HashMap[(Int, String ), Long])], - targetRanks : List[Long] - ) : Array[ HashMap[(Int, String ), List[Long] ]] = { - //as we go through the data linearly, keep track of the number of elements we have seen for - //each partition - var runningTotal = HashMap[(Int, String ), Long]() - //sort by partition number - val sortedParts = partitionMap.sortBy(_._1).map( t => (t._2, t._3)) - sortedParts.map { - - case (( countMap, sumsMap )) => - var relevantIndexMap = HashMap[(Int, String), List[Long] ]() - sumsMap.foreach{ - case (((colIndex, group), colCount )) => - //the running totals for this column/group. If we haven't seen it yet, 0L - val runningTotalCol = runningTotal.getOrElse((colIndex, group), 0L) - runningTotal = runningTotal.updated((colIndex, group), runningTotalCol + colCount ) - //if the count for this column/ and group is in the right range, add it to the map - val ranksHere = targetRanks.filter(rank => - runningTotalCol <= rank && runningTotalCol + colCount >= rank ) - - //add ranksHere to the relevant index map - ranksHere.foreach( - rank => { - val location = rank - runningTotalCol - val indicesForThisKey = relevantIndexMap.getOrElse((colIndex, group ), List[Long]()) - relevantIndexMap = relevantIndexMap.updated( - (colIndex, group), location :: indicesForThisKey) - }) - }//end sums map for each - relevantIndexMap - } - } - - /** - * Median and distinct step 4: - * Given the location within each partition of the desired rank static, goes through the sorted - * data an actually gets that information - * @param locations the results of the getLocationsOfRanksWithinEachPart method - * @return - */ - protected def findElementsIteratively( locations : Array[ HashMap[(Int, String ), List[Long] ]]) = { - sorted.mapPartitionsWithIndex((index, iter) => { - val targetsInThisPart = locations(index) - val len = targetsInThisPart.keys.size - if(len >0 ) { - val newIt = GroupedPartitionProcessingUtil.getValuesFromRanks(iter, targetsInThisPart) - newIt - } - else Iterator.empty - } ) - } - - /** - * Calculates the number of distinct values per (index,keyPair) on each partition). - * The per partition counts are important for the distinct median finding - */ - protected def calculateDistinctForEachPart() : - Array[HashMap[(Int,String ), Long ]] = { - val zero = new HashMap[(Int, String), Long]() - - reduced.mapPartitionsWithIndex((index : Int, - it : Iterator[((Double, (Int, String)), Long)]) => { - val hashMap : HashMap[(Int, String ), Long ] = it.aggregate(zero)( - (map : HashMap[(Int, String), Long ], v : ((Double, (Int, String)), Long) ) => { - val ((value, (colIndex, group)) , count) = v - val prevCount = map.getOrElse( (colIndex, group), 0L ) - //don't add the count, just as one, because we are looking for the distinct values - map.updated((colIndex, group), prevCount + 1 ) - }, - (a , b ) => { - val mergeFunction = (a : Long, b : Long ) => a + b - val h : HashMap[(Int, String ), Long ] = HashMapUtil.mergeMaps[(Int, String ), Long](a, b , mergeFunction) - h - }) - Iterator(hashMap) - }).collect() - } - - protected def getTotalDistinct(totalForEachPart : Array[HashMap[(Int,String ), Long ]] - ) = { - totalForEachPart.reduce((hashMap1, hashMap2) => HashMapUtil.mergeMaps(hashMap1, - hashMap2, (a: Long , b: Long ) => a + b)) - } - - -} - -/** - * Serializable methods performed on iterators in the mapPartitions steps - * in the above clasee - */ -object GroupedPartitionProcessingUtil extends Serializable{ - /** - * Calculates counts an sums per group and partition - */ - def totalAndDistinctByGroup(it : Iterator[((Double, (Int, String)), Long)] ) - : ( HashMap[(Int, String), Long], HashMap[(Int, String), Long]) = { - - val keyPair : ( HashMap[(Int, String), Long], HashMap[(Int, String), Long]) = - it.aggregate(( new HashMap[(Int, String), Long](), new HashMap[(Int, String), Long]()))( - (acc , v : ((Double ,(Int, String )), Long)) => { - val (countMap, sumMap ) = acc - val ((_, (colIndex, group )) , count) = v - val prevCount = countMap.getOrElse( (colIndex, group), 0L ) - val prevSum = sumMap.getOrElse((colIndex, group), 0L) - //don't add the count, just as one, because we are looking for the distinct values - val nextCounts = countMap.updated((colIndex, group), prevCount + 1 ) - val nextSums = sumMap.updated((colIndex, group), prevSum + count) - ( nextCounts, nextSums) } , - (a , b ) => { - val (aCounts, aSums) = a - val (bCounts, bSums) = b - - val nextCounts = HashMapUtil.mergeMaps[(Int, String), Long](aCounts, bCounts, (a :Long , b : Long ) => a+b) - val nextSums = HashMapUtil.mergeMaps[(Int, String), Long](aSums, bSums , (a :Long , b : Long ) => a+b) - (nextCounts, nextSums) - }) - keyPair - } - //return iterator of (colIndex,group)-> value, then make it into a map. - /** - * @param it iterator from the map partitions step in the find elements iteratively step - * @param targetsInThisPart the results of the 'getLocationsOfRanksWithinEachPart' method - * @return The nth value (or values, if the target ranks list is longer than one) - * for each column, if that nth value is in this partition. - */ - def getValuesFromRanks(it : Iterator[((Double, (Int, String)), Long)], targetsInThisPart :HashMap[(Int, String), List[Long]] ) = { - val keysInThisPart = targetsInThisPart.keySet - var runningTotals = HashMap[(Int, String), Long]() - - val newIt: ArrayBuffer[((Int, String), Double)] = new scala.collection.mutable.ArrayBuffer() - it.foreach { case (((value, key), count)) => - if (keysInThisPart.contains(key)) { - val total = runningTotals.getOrElse(key, 0L) - val ranksPresent = targetsInThisPart(key).filter(v => (v <= count + total) && (v > total)) - ranksPresent.foreach(r => { - newIt += ((key, value)) - }) - runningTotals = runningTotals.updated(key, total + count) - } - } - newIt.toIterator - } -} diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index fcfc3da..d5303f5 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -1,8 +1,7 @@ package com.highperformancespark.examples.goldilocks import org.apache.spark._ -import org.apache.spark.rdd._ - +import org.apache.spark.sql.SQLContext import org.scalatest.{BeforeAndAfterAll, FunSuite} // tag::MAGIC_PANDA[] @@ -17,12 +16,39 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { super.beforeAll() } - test("retrieve quantiles") { - val input: RDD[((Double, Int), Long)] = sc.parallelize( - List(((2.0, 1), 10L), ((1.0, 1), 5L), ((3.0, 1), 4L))) - val result = QuantileWithHashMap.findQuantiles(input, List(1), List(1L, 6L)) - val expected = List(1.0, 2.0) - assert(expected === result(1).toList) + val inputList = List(GoldiLocksRow(0.0, 4.5, 7.7, 5.0), + GoldiLocksRow(1.0, 5.5, 6.7, 6.0), + GoldiLocksRow(2.0, 5.5, 1.5, 7.0), + GoldiLocksRow(3.0, 5.5, 0.5, 7.0), + GoldiLocksRow(4.0, 5.5, 0.5, 8.0) + ) + + test("Goldi locks first try ") { + val sqlContext = new SQLContext(sc) + val input = sqlContext.createDataFrame(inputList) + val secondAndThird = GoldiLocksFirstTry.findQuantiles(input, targetRanks = List(2L, 3L)) + val expectedResult = Map[Int, Set[Double]]( + 0 -> Set(1.0, 2.0), + 1 -> Set(5.5, 5.5), + 2 -> Set(0.5, 1.5), + 3 -> Set(6.0, 7.0)) + secondAndThird.foreach(x => println( x._1 +"," + x._2.mkString(" "))) + assert(expectedResult.forall{case ((index, expectedRanks)) => + secondAndThird.get(index).get.toSet.equals(expectedRanks)}) + } + + test("GoldiLocks With Hashmap ") { + val sqlContext = new SQLContext(sc) + val input = sqlContext.createDataFrame(inputList) + val secondAndThird = GoldiLocksWithHashMap.findQuantiles(input, targetRanks = List(2L, 3L)) + val expectedResult = Map[Int, Set[Double]]( + 0 -> Set(1.0, 2.0), + 1 -> Set(5.5, 5.5), + 2 -> Set(0.5, 1.5), + 3 -> Set(6.0, 7.0)) + secondAndThird.foreach(x => println( x._1 +"," + x._2.mkString(" "))) + assert(expectedResult.forall{case ((index, expectedRanks)) => + secondAndThird.get(index).get.toSet.equals(expectedRanks)}) } override def afterAll() { @@ -34,3 +60,6 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { } } // end::MAGIC_PANDA[] + + +case class GoldiLocksRow(pandaId : Double, softness : Double, fuzzyness : Double, size : Double ) \ No newline at end of file From c6bd66792518b8f2e0e6c0357dfebc0dd4f42ea7 Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 29 Oct 2015 10:43:46 -0700 Subject: [PATCH 037/198] first implementation of secondary sort with abstract types --- .../GoldiLocks/SecondarySort.scala | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala new file mode 100644 index 0000000..a971fe9 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala @@ -0,0 +1,66 @@ +package com.highperformancespark.examples.goldilocks + +import org.apache.spark.{Partitioner, HashPartitioner} +import org.apache.spark.rdd.RDD + +import scala.collection.mutable.ArrayBuffer +import scala.reflect.ClassTag + +object SecondarySort { + + def sortByTwoKeys[K : Ordering : ClassTag , S, V : ClassTag](pairRDD : RDD[((K, S), V)], partitions : Int ) = { + val colValuePartitioner = new PrimaryKeyPartitioner[K, S](partitions) + implicit val ordering: Ordering[(K, S)] = Ordering.by(_._1) + val sortedWithinParts = pairRDD.repartitionAndSortWithinPartitions( + colValuePartitioner) + sortedWithinParts + } + + def groupByKeyAndSortBySecondaryKey[K : Ordering : ClassTag, S, V](pairRDD : RDD[((K, S), V)], partitions : Int ) = { + val colValuePartitioner = new PrimaryKeyPartitioner[Double, Int](partitions) + implicit val ordering: Ordering[(K, S)] = Ordering.by(_._1) + val sortedWithinParts = pairRDD.repartitionAndSortWithinPartitions( + colValuePartitioner) + sortedWithinParts.mapPartitions( iter => groupSorted[K, S, V](iter) ) + } + + def groupSorted[K,S,V]( + it: Iterator[((K, S), V)]): Iterator[(K, List[(S, V)])] = { + val res = List[(K, ArrayBuffer[(S, V)])]() + it.foldLeft(res)((list, next) => list match { + case Nil => { + val ((firstKey, secondKey), value) = next + List((firstKey, ArrayBuffer((secondKey, value)))) + } + case head :: rest => { + val (curKey, valueBuf) = head + val ((firstKey, secondKey), value) = next + if (!firstKey.equals(curKey) ) { + (firstKey, ArrayBuffer((secondKey, value))) :: list + } else { + valueBuf.append((secondKey, value)) + list + } + } + }).map { case (key, buf) => (key, buf.toList) }.iterator + } + +} + +class PrimaryKeyPartitioner[K, S](partitions: Int) extends Partitioner { + /** + * We create a hash partitioner and use it with the first set of keys. + */ + val delegatePartitioner = new HashPartitioner(partitions) + + override def numPartitions = delegatePartitioner.numPartitions + + /** + * Partition according to the hash value of the first key + */ + override def getPartition(key: Any): Int = { + val k = key.asInstanceOf[(K, S)] + delegatePartitioner.getPartition(k._1) + } + +} From be9e11dc34d8af4dce69ffc03dea66e0049dafac Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 29 Oct 2015 10:51:20 -0700 Subject: [PATCH 038/198] secondary sort tests --- .../GoldiLocks/GoldiLocksFirstTry.scala | 11 +++++------ .../GoldiLocks/SecondarySort.scala | 2 +- .../GoldiLocks/QuantileOnlyArtisanalTest.scala | 6 ++++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala index 999bfd3..786ffe8 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala @@ -11,7 +11,6 @@ import scala.collection.mutable.ArrayBuffer object GoldiLocksFirstTry { - def findQuantiles( dataFrame: DataFrame, targetRanks: List[Long] ) = { val n = dataFrame.schema.length val valPairs: RDD[(Double, Int)] = getPairs(dataFrame) @@ -36,7 +35,7 @@ object GoldiLocksFirstTry { /** * Step 2. Find the number of elements for each column in each partition - * @param sorted + * @param sorted - the sorted (value, colIndex) pairs * @param numPartitions * @param n the number of columns * @return an RDD the length of the number of partitions, where each row contains @@ -71,13 +70,13 @@ object GoldiLocksFirstTry { private def getLocationsOfRanksWithinEachPart(targetRanks : List[Long], partitionMap : Array[(Int, Array[Long])], n : Int ) : Array[(Int, List[(Int, Long)])] = { val runningTotal = Array.fill[Long](n)(0) - partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> { + partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() totals.zipWithIndex.foreach{ case (colCount, colIndex) => { val runningTotalCol = runningTotal(colIndex) runningTotal(colIndex) += colCount val ranksHere = targetRanks.filter(rank => - (runningTotalCol <= rank && runningTotalCol + colCount >= rank) + runningTotalCol <= rank && runningTotalCol + colCount >= rank ) //for each of the rank statistics present add this column index and the index it will be //at on this partition (the rank - the running total) @@ -86,7 +85,7 @@ object GoldiLocksFirstTry { }) }} (partitionIndex, relevantIndexList.toList) - }} + } } /** @@ -104,7 +103,7 @@ object GoldiLocksFirstTry { keysInThisPart.foreach(key => runningTotals+=((key, 0L))) val newIt : ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() it.foreach{ case( value, colIndex) => { - if(runningTotals isDefinedAt(colIndex)){ + if(runningTotals isDefinedAt colIndex){ val total = runningTotals(colIndex) + 1L runningTotals.update(colIndex, total) if(partMap(colIndex).contains(total)){ diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala index a971fe9..d7c7869 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala @@ -16,7 +16,7 @@ object SecondarySort { sortedWithinParts } - def groupByKeyAndSortBySecondaryKey[K : Ordering : ClassTag, S, V](pairRDD : RDD[((K, S), V)], partitions : Int ) = { + def groupByKeyAndSortBySecondaryKey[K : Ordering : ClassTag, S, V : ClassTag](pairRDD : RDD[((K, S), V)], partitions : Int ) = { val colValuePartitioner = new PrimaryKeyPartitioner[Double, Int](partitions) implicit val ordering: Ordering[(K, S)] = Ordering.by(_._1) val sortedWithinParts = pairRDD.repartitionAndSortWithinPartitions( diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index d5303f5..e2dabf4 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -51,6 +51,12 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { secondAndThird.get(index).get.toSet.equals(expectedRanks)}) } + test("Secondary Sort"){ + val data = sc.parallelize(Range.apply(0, 10)).flatMap( i => List(20.0, 30.0 , 40.0 ).map(x => ((x, i), 1L ))) + val r = SecondarySort.groupByKeyAndSortBySecondaryKey(data, 3) + r.collect().foreach( v => println( v)) + } + override def afterAll() { // We clear the driver port so that we don't try and bind to the same port on restart sc.stop() From 341c97a7b9f5e5d1235a2a5cbccc7cb414ba699f Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 29 Oct 2015 11:24:58 -0700 Subject: [PATCH 039/198] creating real unit test for secondary sort --- .../GoldiLocks/QuantileOnlyArtisanalTest.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index e2dabf4..43525c6 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -55,6 +55,11 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { val data = sc.parallelize(Range.apply(0, 10)).flatMap( i => List(20.0, 30.0 , 40.0 ).map(x => ((x, i), 1L ))) val r = SecondarySort.groupByKeyAndSortBySecondaryKey(data, 3) r.collect().foreach( v => println( v)) + val rSorted = r.collect().sortWith( + lt = (a, b) => a._1.toDouble > b._1.toDouble ) + assert(r.collect().zipWithIndex.forall{ + case (((key, list), index )) => rSorted(index)._1.equals(key) + }) } override def afterAll() { From 424d01930785a42b9a14c1ffb3b0626b15c1bbca Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 8 Nov 2015 17:21:13 -0800 Subject: [PATCH 040/198] adding tags to secondary sort --- .../GoldiLocks/SecondarySort.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala index d7c7869..790db4b 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala @@ -8,6 +8,7 @@ import scala.reflect.ClassTag object SecondarySort { + //tag::sortByTwoKeys def sortByTwoKeys[K : Ordering : ClassTag , S, V : ClassTag](pairRDD : RDD[((K, S), V)], partitions : Int ) = { val colValuePartitioner = new PrimaryKeyPartitioner[K, S](partitions) implicit val ordering: Ordering[(K, S)] = Ordering.by(_._1) @@ -15,7 +16,9 @@ object SecondarySort { colValuePartitioner) sortedWithinParts } + //end::sortByTwoKeys + //tag::sortAndGroup def groupByKeyAndSortBySecondaryKey[K : Ordering : ClassTag, S, V : ClassTag](pairRDD : RDD[((K, S), V)], partitions : Int ) = { val colValuePartitioner = new PrimaryKeyPartitioner[Double, Int](partitions) implicit val ordering: Ordering[(K, S)] = Ordering.by(_._1) @@ -44,9 +47,11 @@ object SecondarySort { } }).map { case (key, buf) => (key, buf.toList) }.iterator } + //end::sortAndGroup } +//tag::primaryKeyPartitioner class PrimaryKeyPartitioner[K, S](partitions: Int) extends Partitioner { /** * We create a hash partitioner and use it with the first set of keys. @@ -62,5 +67,5 @@ class PrimaryKeyPartitioner[K, S](partitions: Int) extends Partitioner { val k = key.asInstanceOf[(K, S)] delegatePartitioner.getPartition(k._1) } - } +//end::primaryKeyPartitioner From 9fe50e4662dc2ff69710a755198c6f754c3980b5 Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 8 Nov 2015 17:42:12 -0800 Subject: [PATCH 041/198] fixing dantaFrame --- .../high-performance-spark-examples/dataframe/HappyPandas.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 815d2bd..e1cc7f3 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -110,7 +110,7 @@ object HappyPanda { } // Join DataFrames of Pandas and Sizes with - def joins(df1: DataFrame, df2: DantaFrame): Unit = { + def joins(df1: DataFrame, df2: DataFrame): Unit = { // Inner join implicit df1.join(df2, df1("name") === df2("name")) // Inner join explicit From 575cd926be73ac1f4ae9d54b59b9ff5bac3d4760 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 8 Nov 2015 17:43:30 -0800 Subject: [PATCH 042/198] Add ze tags --- .../dataframe/HappyPandas.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index e1cc7f3..f65fbb6 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -112,7 +112,10 @@ object HappyPanda { // Join DataFrames of Pandas and Sizes with def joins(df1: DataFrame, df2: DataFrame): Unit = { // Inner join implicit + //tag::innerJoin[] df1.join(df2, df1("name") === df2("name")) + //end::innerJoin[] + //tag::joins[] // Inner join explicit df1.join(df2, df1("name") === df2("name"), "inner") // Left outer join explicit @@ -121,5 +124,6 @@ object HappyPanda { df1.join(df2, df1("name") === df2("name"), "right_outer") // Left semi join explicit df1.join(df2, df1("name") === df2("name"), "leftsemi") + //end::joins[] } } From a4eaf78aa37091223dea5b9386f1d0e6da8d57b0 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 8 Nov 2015 18:20:44 -0800 Subject: [PATCH 043/198] Switch our include for spark testing base to new version, stop depending on spDependencies because it doesn't work very well, and fix a syntax error in the SQL examples --- build.sbt | 4 +--- .../dataframe/HappyPandas.scala | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index eed1462..ce31c58 100644 --- a/build.sbt +++ b/build.sbt @@ -14,8 +14,6 @@ javacOptions ++= Seq("-source", "1.7", "-target", "1.7") sparkVersion := "1.5.1" -spDependencies += "holdenk/spark-testing-base:0.1.3" - //tag::sparkComponents[] sparkComponents ++= Seq("core", "streaming", "sql", "hive", "mllib") //end::sparkComponents[] @@ -30,8 +28,8 @@ libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "2.2.1", "org.scalacheck" %% "scalacheck" % "1.12.4", "junit" % "junit" % "4.10", + "com.holdenkarau" % "spark-testing-base_2.10" % "1.5.1_0.2.1", "org.eclipse.jetty" % "jetty-util" % "9.3.2.v20150730", - "org.codehaus.jackson" % "jackson-mapper-asl" % "1.8.8", "com.novocode" % "junit-interface" % "0.10" % "test->default") diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index f65fbb6..47afc1b 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -90,7 +90,7 @@ object HappyPanda { def orderPandas(pandas: DataFrame): DataFrame = { //tag::simpleSort[] - pandas.orderBy(asc(pandas("size")), desc(pandas("age"))) + pandas.orderBy(pandas("size").asc, pandas("age").desc) //end::simpleSort[] } From 71070964f2ab72f28800d06c4565862ab75ddd1b Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 8 Nov 2015 19:05:51 -0800 Subject: [PATCH 044/198] Add a SQL dataframe query example --- .../dataframe/HappyPandas.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 47afc1b..ec05e77 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -88,6 +88,15 @@ object HappyPanda { } //end::complexAggPerZip[] + def simpleSqlExample(pandas: DataFrame): DataFrame = { + val sqlCtx = pandas.sqlContext + //tag::pandasSQLQuery[] + pandas.registerTempTable("pandas") + val miniPandas = sqlCtx.sql("SELECT * FROM pandas WHERE pandaSize < 100") + //end::pandasSQLQuery[] + miniPandas + } + def orderPandas(pandas: DataFrame): DataFrame = { //tag::simpleSort[] pandas.orderBy(pandas("size").asc, pandas("age").desc) From 4bb76d55887a2ea92534c6a6fff9f0436d10fc7d Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 8 Nov 2015 22:42:44 -0800 Subject: [PATCH 045/198] Add []'s to tags in secondary sort --- .../GoldiLocks/SecondarySort.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala index 790db4b..d56c3d8 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala @@ -8,7 +8,7 @@ import scala.reflect.ClassTag object SecondarySort { - //tag::sortByTwoKeys + //tag::sortByTwoKeys[] def sortByTwoKeys[K : Ordering : ClassTag , S, V : ClassTag](pairRDD : RDD[((K, S), V)], partitions : Int ) = { val colValuePartitioner = new PrimaryKeyPartitioner[K, S](partitions) implicit val ordering: Ordering[(K, S)] = Ordering.by(_._1) @@ -16,9 +16,9 @@ object SecondarySort { colValuePartitioner) sortedWithinParts } - //end::sortByTwoKeys + //end::sortByTwoKeys[] - //tag::sortAndGroup + //tag::sortAndGroup[] def groupByKeyAndSortBySecondaryKey[K : Ordering : ClassTag, S, V : ClassTag](pairRDD : RDD[((K, S), V)], partitions : Int ) = { val colValuePartitioner = new PrimaryKeyPartitioner[Double, Int](partitions) implicit val ordering: Ordering[(K, S)] = Ordering.by(_._1) @@ -47,11 +47,11 @@ object SecondarySort { } }).map { case (key, buf) => (key, buf.toList) }.iterator } - //end::sortAndGroup + //end::sortAndGroup[] } -//tag::primaryKeyPartitioner +//tag::primaryKeyPartitioner[] class PrimaryKeyPartitioner[K, S](partitions: Int) extends Partitioner { /** * We create a hash partitioner and use it with the first set of keys. @@ -68,4 +68,4 @@ class PrimaryKeyPartitioner[K, S](partitions: Int) extends Partitioner { delegatePartitioner.getPartition(k._1) } } -//end::primaryKeyPartitioner +//end::primaryKeyPartitioner[] From a62b84e3787ab67088d8186f39ceaf5e4c9608e1 Mon Sep 17 00:00:00 2001 From: Rachel Date: Mon, 9 Nov 2015 10:11:52 -0800 Subject: [PATCH 046/198] fixing tags --- .../GoldiLocks/GoldiLocksFirstTry.scala | 16 +++++++++- .../GoldiLocks/GoldiLocksWithHashMap.scala | 3 +- .../GoldiLocks/SecondarySort.scala | 31 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala index 786ffe8..873898d 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala @@ -9,6 +9,20 @@ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer +object GoldiLocksGroupByKey { + //tag::groupByKey[] + def findRankStatistics( + pairRDD: RDD[(Int, Double)], + ranks: List[Long]): scala.collection.Map[Int, List[Double]] = { + pairRDD.groupByKey().mapValues(iter => { + val ar = iter.toArray.sorted + ranks.map(n => ar(n.toInt)) + }).collectAsMap() + } + //end::groupByKey[] +} + +//tag::firstTry[] object GoldiLocksFirstTry { def findQuantiles( dataFrame: DataFrame, targetRanks: List[Long] ) = { @@ -118,5 +132,5 @@ object GoldiLocksFirstTry { } }) } - } +//end::firstTry[] diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala index 8f56531..c4f4005 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala @@ -9,7 +9,7 @@ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.reflect.runtime.{universe => ru} - +//tag::hashMap[] object GoldiLocksWithHashMap { def findQuantiles( dataFrame: DataFrame , targetRanks: List[Long] ) = { @@ -162,3 +162,4 @@ object GoldiLocksWithHashMap { result.groupByKey().collectAsMap() } } +//end::hashMap[] \ No newline at end of file diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala index d56c3d8..a5eeff3 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala @@ -69,3 +69,34 @@ class PrimaryKeyPartitioner[K, S](partitions: Int) extends Partitioner { } } //end::primaryKeyPartitioner[] + +object CoPartitioningLessons { + + def coLocated[K,V](a : RDD[(K, V)], b : RDD[(K, V)], + partitionerX : Partitioner, partitionerY :Partitioner): Unit = { + + //tag::coLocated + val rddA = a.partitionBy(partitionerX) + rddA.cache() + val rddB = b.partitionBy(partitionerY) + rddB.cache() + val rddC = a.cogroup(b) + rddC.count() + //end::coLocated[] + } + + def notCoLocated[K,V](a : RDD[(K, V)], b : RDD[(K, V)], + partitionerX : Partitioner, partitionerY :Partitioner): Unit = { + + //tag::notCoLocated + val rddA = a.partitionBy(partitionerX) + rddA.cache() + val rddB = b.partitionBy(partitionerY) + rddB.cache() + val rddC = a.cogroup(b) + rddA.count() + rddB.count() + rddC.count() + //end::notCoLocated[] + } +} From 984bbdc123cbd04972a5f609390b4bd37cdce3f8 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 10 Nov 2015 00:00:28 -0800 Subject: [PATCH 047/198] Fix build error --- build.sbt | 2 +- .../dataframe/HappyPandas.scala | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index ce31c58..b3c6dff 100644 --- a/build.sbt +++ b/build.sbt @@ -15,7 +15,7 @@ javacOptions ++= Seq("-source", "1.7", "-target", "1.7") sparkVersion := "1.5.1" //tag::sparkComponents[] -sparkComponents ++= Seq("core", "streaming", "sql", "hive", "mllib") +sparkComponents ++= Seq("core", "streaming", "sql", "hive", "hive-thriftserver", "mllib") //end::sparkComponents[] parallelExecution in Test := false diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index ec05e77..c88ab64 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -9,6 +9,7 @@ import org.apache.spark.sql.DataFrame import org.apache.spark.sql.expressions._ import org.apache.spark.sql.functions._ import org.apache.spark.sql.hive._ +import org.apache.spark.sql.hive.thriftserver._ object HappyPanda { // How to create a HiveContext or SQLContext with an existing SparkContext @@ -97,6 +98,13 @@ object HappyPanda { miniPandas } + def startJDBCServer(sqlContext: HiveContext): Unit = { + //tag::startJDBC[] + sqlContext.setConf("hive.server2.thrift.port", "9090") + HiveThriftServer2.startWithContext(sqlContext) + //end::startJDBC[] + } + def orderPandas(pandas: DataFrame): DataFrame = { //tag::simpleSort[] pandas.orderBy(pandas("size").asc, pandas("age").desc) From f6ff65f29899b3ed7b3d79ec1282a582a69fa635 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 10 Nov 2015 00:00:45 -0800 Subject: [PATCH 048/198] Add a simple wordcount example --- .../wordcount/WordCount.scala | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala diff --git a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala new file mode 100644 index 0000000..0a8e2c2 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala @@ -0,0 +1,23 @@ +/** + * What sort of big data book would this be if we didn't mention wordcount? + */ +import org.apache.spark.rdd._ + +object WordCount { + // bad idea: uses group by key + def badIdea(rdd: RDD[String]): RDD[(String, Int)] = { + val words = rdd.flatMap(_.split(" ")) + val wordPairs = words.map((_, 1)) + val grouped = wordPairs.groupByKey() + val wordCounts = grouped.mapValues(_.sum) + wordCounts + } + + // good idea: doesn't use group by key + def goodIdea(rdd: RDD[String]): RDD[(String, Int)] = { + val words = rdd.flatMap(_.split(" ")) + val wordPairs = words.map((_, 1)) + val wordCounts = wordPairs.reduceByKey(_ + _) + wordCounts + } +} From d7143332add5257c60f185649ba22fbeb15ec67e Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 22 Nov 2015 18:08:24 -0500 Subject: [PATCH 049/198] adding final set of examples and fixing a few problems w SecondarySort class --- build.sbt | 1 + .../GoldiLocks/RDDJoinExamples.scala | 88 +++++++++++++++++++ .../GoldiLocks/SecondarySort.scala | 4 +- 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/main/scala/com/high-performance-spark-examples/GoldiLocks/RDDJoinExamples.scala diff --git a/build.sbt b/build.sbt index b3c6dff..84f8362 100644 --- a/build.sbt +++ b/build.sbt @@ -19,6 +19,7 @@ sparkComponents ++= Seq("core", "streaming", "sql", "hive", "hive-thriftserver", //end::sparkComponents[] parallelExecution in Test := false + fork := true javaOptions ++= Seq("-Xms512M", "-Xmx2048M", "-XX:MaxPermSize=2048M", "-XX:+CMSClassUnloadingEnabled") diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RDDJoinExamples.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RDDJoinExamples.scala new file mode 100644 index 0000000..723ca1d --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RDDJoinExamples.scala @@ -0,0 +1,88 @@ +package com.highperformancespark.examples.goldilocks + +import org.apache.spark.HashPartitioner +import org.apache.spark.rdd.RDD + +object RDDJoinExamples { + + /* For Example, suppose we have one RDD with some data in the form (Panda id, score) + and another RDD with (Panda id, address), and we want to send each Panda some mail + with her best score. We could join the RDDs on ID and then compute the best score + for each address. Like this: + + 'ToDo: Insert Example' + + However, this is slower than first reducing the score data, so that the + //first dataset contains only one row for each Panda with her best score and then + //joining that data with the address data. + + 'ToDO: Insert an example of this' */ + //tag::joinScoresWithAddress[] + def joinScoresWithAddress1( scoreRDD : RDD[(Long, Double)], + addressRDD : RDD[(Long, String )]) : RDD[(Long, (Double, String))]= { + val joinedRDD = scoreRDD.join(addressRDD) + joinedRDD.reduceByKey( (x, y) => if(x._1 > y._1) x else y ) + } + //end::joinScoresWithAddress[] + + //tag::joinScoresWithAddressFast[] + def joinScoresWithAddress2( scoreRDD : RDD[(Long, Double)], + addressRDD : RDD[(Long, String )]) : RDD[(Long, (Double, String))]= { + //stuff + val bestScoreData = scoreRDD.reduceByKey((x, y) => if(x > y) x else y) + bestScoreData.join(addressRDD) + + } + //end::joinScoresWithAddressFast[] +/* + We could make the example in the previous section even faster, + by using the partitioner for the address data as an argument for + the reduce by key step. + 'ToDO: Insert the code to show this here' */ + //tag::joinScoresWithAddress3[] + def joinScoresWithAddress3( scoreRDD : RDD[(Long, Double)], + addressRDD : RDD[(Long, String )]) : RDD[(Long, (Double, String))]= { + //if addressRDD has a known partitioner we should use that, + //otherwise it has a default hash parttioner, which we can reconstrut by getting the umber of + // partitions. + val addressDataPartitioner = addressRDD.partitioner match { + case (Some(p)) => p + case (None) => new HashPartitioner(addressRDD.partitions.length) + } + val bestScoreData = scoreRDD.reduceByKey(addressDataPartitioner, (x, y) => if(x > y) x else y) + bestScoreData.join(addressRDD) + } + //end::joinScoresWithAddress3[] + + def debugString( scoreRDD : RDD[(Long, Double)], + addressRDD : RDD[(Long, String )]) = { + //tag::debugString[] + scoreRDD.join(addressRDD).toDebugString + //end::debugString[] + } + + /* + * Suppose we had two datasets of information about each panda, + * one with the scores, and one with there favorite foods. + * We could use cogroup to associate each Pandas id with an iterator + * of their scores and another iterator of their favorite foods. + */ + + + def coGroupExample( scoreRDD : RDD[(Long, Double)], foodRDD : RDD[(Long, String )], + addressRDD : RDD[(Long, String )]) = { + //tag::coGroupExample1[] + val cogroupedRDD: RDD[(Long, (Iterable[Double], Iterable[String]))] = scoreRDD.cogroup(foodRDD) + //end::coGroupExample1[] + + /* + * For example, if we needed to join the panda score data with both address + * and favorite foods, it would be better to use co group than two + * join operations. + */ + + //tag::coGroupExample2[] + val addressScoreFood = addressRDD.cogroup(scoreRDD, foodRDD) + //end::coGroupExample2[] + } + } diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala index a5eeff3..bd4e134 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala @@ -72,7 +72,7 @@ class PrimaryKeyPartitioner[K, S](partitions: Int) extends Partitioner { object CoPartitioningLessons { - def coLocated[K,V](a : RDD[(K, V)], b : RDD[(K, V)], + def coLocated(a : RDD[(Int, String)], b : RDD[(Int, String)], partitionerX : Partitioner, partitionerY :Partitioner): Unit = { //tag::coLocated @@ -85,7 +85,7 @@ object CoPartitioningLessons { //end::coLocated[] } - def notCoLocated[K,V](a : RDD[(K, V)], b : RDD[(K, V)], + def notCoLocated(a : RDD[(Int, String)], b : RDD[(Int, String )], partitionerX : Partitioner, partitionerY :Partitioner): Unit = { //tag::notCoLocated From 8c52af1d7a99272bfaed4c93b3e748e25a0f7f76 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 29 Nov 2015 00:07:12 -0800 Subject: [PATCH 050/198] Add UDF & UDAF example --- .../dataframe/UDFs.scala | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala new file mode 100644 index 0000000..088f5b6 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala @@ -0,0 +1,63 @@ +/** + * Example UDFs + */ + +import org.apache.spark._ +import org.apache.spark.sql._ +import org.apache.spark.sql.catalyst.expressions.aggregate._ +import org.apache.spark.sql.DataFrame +import org.apache.spark.sql.expressions._ +import org.apache.spark.sql.functions._ +import org.apache.spark.sql.hive._ +import org.apache.spark.sql.hive.thriftserver._ +import org.apache.spark.sql.types._ + +object UDFs { + //tag::setupUDFs[] + def setupUDFs(sqlCtx: SQLContext) = { + sqlCtx.udf.register("strLen", (s: String) => s.length()) + } + //end::setupUDFs[] + + //tag::setupUDAFs[] + def setupUDAFs(sqlCtx: SQLContext) = { + class Avg extends UserDefinedAggregateFunction { + // Input type + def inputSchema: org.apache.spark.sql.types.StructType = + StructType(StructField("value", DoubleType) :: Nil) + + def bufferSchema: StructType = StructType( + StructField("count", LongType) :: + StructField("sum", DoubleType) :: Nil + ) + + // Return type + def dataType: DataType = DoubleType + + def deterministic: Boolean = true + + def initialize(buffer: MutableAggregationBuffer): Unit = { + buffer(0) = 0L + buffer(1) = 0.0 + } + + def update(buffer: MutableAggregationBuffer,input: Row): Unit = { + buffer(0) = buffer.getAs[Long](0) + 1 + buffer(1) = buffer.getAs[Double](1) + input.getAs[Double](0) + } + + def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = { + buffer1(0) = buffer1.getAs[Long](0) + buffer2.getAs[Long](0) + buffer1(1) = buffer1.getAs[Double](1) + buffer2.getAs[Double](1) + } + + def evaluate(buffer: Row): Any = { + math.pow(buffer.getDouble(1), 1.toDouble / buffer.getLong(0)) + } + } + // Optionally register + val avg = new Avg + sqlCtx.udf.register("ourAvg", avg) + } + //end::setupUDAFs[] +} From 3ecc265d663bdf6565165b3fe7be15dfe392b095 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 9 Dec 2015 00:34:29 -0800 Subject: [PATCH 051/198] More json loading examples --- .../dataframe/HappyPandas.scala | 13 +++++++++++-- .../dataframe/UDFs.scala | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index c88ab64..226307f 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -1,6 +1,7 @@ /** * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. */ +package com.highperformancespark.examples.dataframe import org.apache.spark._ import org.apache.spark.sql._ @@ -32,10 +33,18 @@ object HappyPanda { } // Illustrate loading some JSON data - def loadDataSimple(sqlCtx: SQLContext, path: String): DataFrame = { + def loadDataSimple(sc: SparkContext, sqlCtx: SQLContext, path: String): DataFrame = { //tag::loadPandaJSONSimple[] - sqlCtx.read.json(path) + val df = sqlCtx.read.json(path) //end::loadPandaJSONSimple[] + //tag::loadPandaJSONComplex[] + val df2 = sqlCtx.read.format("json").option("samplingRatio", "1.0").load(path) + //end::loadPandaJSONComplex[] + val rdd = sc.textFile(path) + //tag::loadPandaJsonRDD[] + val df3 = sqlCtx.jsonRDD(rdd) + //end::loadPandaJSONRDD[] + df } def happyPandas(pandaInfo: DataFrame): DataFrame = { diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala index 088f5b6..094e69f 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala @@ -1,6 +1,7 @@ /** * Example UDFs */ +package com.highperformancespark.examples.dataframe import org.apache.spark._ import org.apache.spark.sql._ From fe97ea8905b382d402fd9040ffaf01470c954de4 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 9 Dec 2015 09:56:09 -0800 Subject: [PATCH 052/198] Switch to non deprecated API and add package for test --- .../high-performance-spark-examples/dataframe/HappyPandas.scala | 2 +- .../high-performance-spark-examples/dataframe/HappyPandas.scala | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 226307f..b8b88ce 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -42,7 +42,7 @@ object HappyPanda { //end::loadPandaJSONComplex[] val rdd = sc.textFile(path) //tag::loadPandaJsonRDD[] - val df3 = sqlCtx.jsonRDD(rdd) + val df3 = sqlCtx.read.json(rdd) //end::loadPandaJSONRDD[] df } diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 4065db1..8dbc96a 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -1,6 +1,7 @@ /** * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. */ +package com.highperformancespark.examples.dataframe import org.apache.spark.sql.{SQLContext, DataFrame, Row} import org.apache.spark.sql.types._ From 9ffec0835e7c289c9e593e4f475b78bf384f1fe9 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 12 Dec 2015 17:44:14 -0800 Subject: [PATCH 053/198] Add some loadsave and querying sql examples --- .../dataframe/LoadSave.scala | 96 +++++++++++++++++++ .../dataframe/RegularSQL.scala | 39 ++++++++ 2 files changed, 135 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala create mode 100644 src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala new file mode 100644 index 0000000..4d0064b --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -0,0 +1,96 @@ +/** + * Load and save data to/from DataFrames + */ +package com.highperformancespark.examples.dataframe + +import java.util.Properties + +import org.apache.spark._ +import org.apache.spark.rdd._ +import org.apache.spark.sql._ +import org.apache.spark.sql.catalyst.expressions.aggregate._ +import org.apache.spark.sql.DataFrame +import org.apache.spark.sql.expressions._ +import org.apache.spark.sql.functions._ +import org.apache.spark.sql.types._ +import org.apache.spark.sql.hive._ +import org.apache.spark.sql.hive.thriftserver._ + +case class PandaMagic(happy: Boolean, name: String) +case class LoadSave(sqlContext: SQLContext) { + import sqlContext.implicits._ + //tag::createFromRDD[] + def createFromCaseClassRDD(input: RDD[PandaMagic]) = { + // Create DataFrame explicitly using sqlContext and schema inferance + val df1 = sqlContext.createDataFrame(input, classOf[PandaMagic]) + // Create DataFrame using sqlContext implicits and schema inferance + val df2 = input.toDF() + + // Create a Row RDD from our RDD of case classes + val rowRDD = input.map(pm => Row(pm.happy, pm.name)) + // Create DataFrame explicitly with specified schema + val schema = StructType(List(StructField("happy", BooleanType, false), + StructField("name", StringType, true))) + val df3 = sqlContext.createDataFrame(rowRDD, schema) + } + //end::createFromRDD[] + + //tag::createFromLocal[] + def createFromLocal(input: Seq[PandaMagic]) = { + sqlContext.createDataFrame(input) + } + //end::createFromLocal[] + + //tag::collectResults[] + def collectDF(df: DataFrame) = { + val result: Array[Row] = df.collect() + } + //end::collectResults[] + + //tag::toRDD[] + def toRDD(input: DataFrame): RDD[PandaMagic] = { + val rdd: RDD[Row] = input.rdd + rdd.map(row => PandaMagic(row.getAs[Boolean](0), row.getAs[String](1))) + } + //end::toRDD[] + + //tag::partitionedOutput[] + def writeOutByZip(input: DataFrame): Unit = { + input.write.partitionBy("zipcode").format("json").save("output/") + } + //end::partitionedOutput[] + + def createJDBC() = { + //tag::createJDBC[] + sqlContext.read.jdbc("jdbc:dialect:serverName;user=user;password=pass", + "table", new Properties) + sqlContext.read.format("jdbc") + .option("url", "jdbc:dialect:serverName") + .option("dbtable", "table").load() + //end::createJDBC[] + } + + def writeJDBC(df: DataFrame) = { + //tag::writeJDBC[] + df.write.jdbc("jdbc:dialect:serverName;user=user;password=pass", + "table", new Properties) + df.write.format("jdbc") + .option("url", "jdbc:dialect:serverName") + .option("user", "user") + .option("password", "pass") + .option("dbtable", "table").save() + //end::writeJDBC[] + } + + //tag::loadHiveTable[] + def loadHiveTable(name: String): DataFrame = { + sqlContext.read.table("pandas") + } + //end::loadHiveTable[] + + //tag::saveManagedTable[] + def saveManagedTable(df: DataFrame): Unit = { + df.write.saveAsTable("pandas") + } + //end::saveManagedTable[] +} diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala new file mode 100644 index 0000000..6653cb0 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala @@ -0,0 +1,39 @@ +/** + * Using plain-old-sql + */ +package com.highperformancespark.examples.dataframe + +import org.apache.spark._ +import org.apache.spark.rdd._ +import org.apache.spark.sql._ +import org.apache.spark.sql.catalyst.expressions.aggregate._ +import org.apache.spark.sql.DataFrame +import org.apache.spark.sql.expressions._ +import org.apache.spark.sql.functions._ +import org.apache.spark.sql.types._ +import org.apache.spark.sql.hive._ +import org.apache.spark.sql.hive.thriftserver._ + +case class RegularSQL(sqlContext: SQLContext) { + import sqlContext.implicits._ + + //tag::queryTable[] + def querySQL(): DataFrame = { + sqlContext.sql("SELECT * FROM pandas WHERE size > 0") + } + //end::queryTable[] + + // TODO: Holden: include a parquet example file and point this to that. + //tag::queryRawFile[] + def queryRawFile(): DataFrame = { + sqlContext.sql("SELECT * FROM parquet.`path_to_parquer_file`") + } + //end::queryRawFile[] + + //tag::registerTable[] + def registerTable(df: DataFrame): Unit = { + df.registerTempTable("pandas") + df.saveAsTable("perm_pandas") + } + //end::registerTable[] +} From 5b96227a42756cbb4cc9a8c11100797c7324fdb5 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Fri, 18 Dec 2015 23:54:39 -0800 Subject: [PATCH 054/198] Load/save parquet --- .../dataframe/LoadSave.scala | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index 4d0064b..b28770e 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -82,8 +82,26 @@ case class LoadSave(sqlContext: SQLContext) { //end::writeJDBC[] } + //tag::loadParquet[] + def loadParquet(path: String): DataFrame = { + // Configure Spark to read binary data as string, note: must be configured on SQLContext + sqlContext.setConf("spark.sql.parquet.binaryAsString", "true") + // Load parquet data using merge schema (configured through option) + sqlContext.read + .option("mergeSchema", "true") + .format("parquet") + .load(path) + } + //end::loadParquet[] + + //tag::writeParquet[] + def writeParquet(df: DataFrame, path: String) = { + df.write.format("parquet").save(path) + } + //end::writeParquet[] + //tag::loadHiveTable[] - def loadHiveTable(name: String): DataFrame = { + def loadHiveTable(): DataFrame = { sqlContext.read.table("pandas") } //end::loadHiveTable[] From 0b0fc3a93abf1b209f9f4fb19e4b0158e25092ba Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 19 Dec 2015 00:18:10 -0800 Subject: [PATCH 055/198] sometime sbt builds make me sad --- build.sbt | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sbt b/build.sbt index 84f8362..2c3e438 100644 --- a/build.sbt +++ b/build.sbt @@ -31,6 +31,7 @@ libraryDependencies ++= Seq( "junit" % "junit" % "4.10", "com.holdenkarau" % "spark-testing-base_2.10" % "1.5.1_0.2.1", "org.eclipse.jetty" % "jetty-util" % "9.3.2.v20150730", + "org.codehaus.jackson" % "jackson-mapper-asl" % "1.8.8", "com.novocode" % "junit-interface" % "0.10" % "test->default") From 444d8e8d0dcee0e6b620928e22fdf834559c2dcb Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 21 Dec 2015 14:23:32 -0800 Subject: [PATCH 056/198] Switch PandaMagic to PandaInfo/PandaPlace --- .../dataframe/LoadSave.scala | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index b28770e..1ad2c77 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -16,21 +16,29 @@ import org.apache.spark.sql.types._ import org.apache.spark.sql.hive._ import org.apache.spark.sql.hive.thriftserver._ -case class PandaMagic(happy: Boolean, name: String) +case class PandaInfo(name: String, happy: Boolean, pt: String) +case class PandaPlace(name: String, pandas: Array[PandaInfo]) case class LoadSave(sqlContext: SQLContext) { import sqlContext.implicits._ //tag::createFromRDD[] - def createFromCaseClassRDD(input: RDD[PandaMagic]) = { + def createFromCaseClassRDD(input: RDD[PandaPlace]) = { // Create DataFrame explicitly using sqlContext and schema inferance - val df1 = sqlContext.createDataFrame(input, classOf[PandaMagic]) + val df1 = sqlContext.createDataFrame(input) // Create DataFrame using sqlContext implicits and schema inferance val df2 = input.toDF() // Create a Row RDD from our RDD of case classes - val rowRDD = input.map(pm => Row(pm.happy, pm.name)) + val rowRDD = input.map(pm => Row(pm.name, + pm.pandas.map(pi => Row(pi.pandaName, pi.happy, pi.pt)))) // Create DataFrame explicitly with specified schema - val schema = StructType(List(StructField("happy", BooleanType, false), - StructField("name", StringType, true))) + val schema = StructType(List( + StructField("name", StringType, true), + StructField("pandas", ArrayType( + StructType(List( + StructField("name", StringType, true), + StructField("happy", BooleanType, true), + StructField("pt", StringType, true))))))) + )) val df3 = sqlContext.createDataFrame(rowRDD, schema) } //end::createFromRDD[] @@ -48,7 +56,7 @@ case class LoadSave(sqlContext: SQLContext) { //end::collectResults[] //tag::toRDD[] - def toRDD(input: DataFrame): RDD[PandaMagic] = { + def toRDD(input: DataFrame): RDD[PandaPlaces] = { val rdd: RDD[Row] = input.rdd rdd.map(row => PandaMagic(row.getAs[Boolean](0), row.getAs[String](1))) } From fb2eeb474083ce8af2d8f63942b03f660fd5218c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 21 Dec 2015 14:31:32 -0800 Subject: [PATCH 057/198] whoops some old references --- .../dataframe/LoadSave.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index 1ad2c77..b31dc06 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -29,7 +29,7 @@ case class LoadSave(sqlContext: SQLContext) { // Create a Row RDD from our RDD of case classes val rowRDD = input.map(pm => Row(pm.name, - pm.pandas.map(pi => Row(pi.pandaName, pi.happy, pi.pt)))) + pm.pandas.map(pi => Row(pi.name, pi.happy, pi.pt)))) // Create DataFrame explicitly with specified schema val schema = StructType(List( StructField("name", StringType, true), @@ -38,13 +38,12 @@ case class LoadSave(sqlContext: SQLContext) { StructField("name", StringType, true), StructField("happy", BooleanType, true), StructField("pt", StringType, true))))))) - )) val df3 = sqlContext.createDataFrame(rowRDD, schema) } //end::createFromRDD[] //tag::createFromLocal[] - def createFromLocal(input: Seq[PandaMagic]) = { + def createFromLocal(input: Seq[PandaPlace]) = { sqlContext.createDataFrame(input) } //end::createFromLocal[] @@ -56,9 +55,9 @@ case class LoadSave(sqlContext: SQLContext) { //end::collectResults[] //tag::toRDD[] - def toRDD(input: DataFrame): RDD[PandaPlaces] = { + def toRDD(input: DataFrame): RDD[PandaInfo] = { val rdd: RDD[Row] = input.rdd - rdd.map(row => PandaMagic(row.getAs[Boolean](0), row.getAs[String](1))) + rdd.map(row => PandaInfo(row.getAs[String](0), row.getAs[Boolean](1), row.getAs[String](2))) } //end::toRDD[] From 2bc36e088c7d5dcc5b591a88c2d784767ce73cd1 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Thu, 24 Dec 2015 17:07:14 +0200 Subject: [PATCH 058/198] Add pandaInfo test cases --- .../dataframe/HappyPandas.scala | 69 +++++++++++-- .../dataframe/LoadSave.scala | 17 ++-- .../dataframe/HappyPandas.scala | 96 +++++++++++++------ 3 files changed, 136 insertions(+), 46 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index b8b88ce..02f1396 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -13,7 +13,7 @@ import org.apache.spark.sql.hive._ import org.apache.spark.sql.hive.thriftserver._ object HappyPanda { - // How to create a HiveContext or SQLContext with an existing SparkContext + // create SQLContext with an existing SparkContext def sqlContext(sc: SparkContext): SQLContext = { //tag::createSQLContext[] val sqlContext = new SQLContext(sc) @@ -23,6 +23,7 @@ object HappyPanda { sqlContext } + // create HiveContext with an existing SparkContext def hiveContext(sc: SparkContext): HiveContext = { //tag::createHiveContext[] val hiveContext = new HiveContext(sc) @@ -47,33 +48,71 @@ object HappyPanda { df } - def happyPandas(pandaInfo: DataFrame): DataFrame = { + // Here will be some examples on PandaInfo DataFrame + + /** + * @param place name of place + * @param pandaType type of pandas in this place + * @param happyPandas number of happy pandas in this place + * @param totalPandas total number of pandas in this place + */ + case class PandaInfo(place: String, pandaType: String, happyPandas: Integer, totalPandas: Integer) + + /** + * Gets the percentage of happy pandas per place. + * + * @param pandaInfo the input DataFrame + * @return Returns a pair of (place, percentage of happy pandas) + */ + def happyPandasPercentage(pandaInfo: DataFrame): DataFrame = { pandaInfo.select(pandaInfo("place"), (pandaInfo("happyPandas") / pandaInfo("totalPandas")).as("percentHappy")) } //tag::encodePandaType[] - def encodePandaType(pandaInfo: DataFrame, num: Int): DataFrame = { - pandaInfo.select(pandaInfo("pandaId"), - when(pandaInfo("pandaType") === "giant", 0). + /** + * Encodes pandaType to Integer values instead of string values. + * + * @param pandaInfo the input DataFrame + * @return Returns a pair of (pandaId, mapped integer value for pandaType) + */ + def encodePandaType(pandaInfo: DataFrame): DataFrame = { + pandaInfo.select(pandaInfo("place"), + (when(pandaInfo("pandaType") === "giant", 0). when(pandaInfo("pandaType") === "red", 1). - otherwise(2) + otherwise(2)).as("encodedType") ) } //end::encodePandaType[] //tag::simpleFilter[] - def minHappyPandas(pandaInfo: DataFrame, num: Int): DataFrame = { - pandaInfo.filter(pandaInfo("happyPandas") >= num) + /** + * Gets places with happy pandas more than minHappinessBound. + */ + def minHappyPandas(pandaInfo: DataFrame, minHappyPandas: Int): DataFrame = { + pandaInfo.filter(pandaInfo("happyPandas") >= minHappyPandas) } //end::simpleFilter[] //tag::complexFilter[] - def minHappyPandasComplex(pandaInfo: DataFrame, num: Int): DataFrame = { + /** + * Gets places that contains happy pandas more than unhappy pandas. + */ + def happyPandasPlaces(pandaInfo: DataFrame): DataFrame = { pandaInfo.filter(pandaInfo("happyPandas") >= pandaInfo("totalPandas") / 2) } //end::complexFilter[] + + /** + * + * @param name name of panda + * @param zip zip code + * @param pandaSize size of panda in KG + * @param age age of panda + */ + case class Pandas(name: String, zip: String, pandaSize: Integer, age: Integer) + //tag::maxPandaSizePerZip[] def maxPandaSizePerZip(pandas: DataFrame): DataFrame = { pandas.groupBy(pandas("zip")).max("pandaSize") @@ -114,9 +153,17 @@ object HappyPanda { //end::startJDBC[] } + /** + * Orders pandas by size ascending and by age descending. + * Pandas will be sorted by "size" first and if two pandas have the same "size" + * will be sorted by "age". + * + * @param pandas + * @return + */ def orderPandas(pandas: DataFrame): DataFrame = { //tag::simpleSort[] - pandas.orderBy(pandas("size").asc, pandas("age").desc) + pandas.orderBy(pandas("pandaSize").asc, pandas("age").desc) //end::simpleSort[] } @@ -127,9 +174,11 @@ object HappyPanda { .partitionBy(pandas("zip")) .rowsBetween(start = 10, end = 10) // use rangeBetween for range instead //end::relativePandaSizesWindow[] + //tag::relativePandaSizesQuery[] val pandaRelativeSizeFunc = (pandas("pandaSize") - avg(pandas("pandaSize")).over(windowSpec)) + pandas.select(pandas("name"), pandas("zip"), pandas("pandaSize"), pandaRelativeSizeFunc.as("panda_relative_size")) //end::relativePandaSizesQuery[] diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index b31dc06..ec7fdfa 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -5,6 +5,7 @@ package com.highperformancespark.examples.dataframe import java.util.Properties +import com.highperformancespark.examples.dataframe.HappyPanda.PandaInfo import org.apache.spark._ import org.apache.spark.rdd._ import org.apache.spark.sql._ @@ -16,28 +17,31 @@ import org.apache.spark.sql.types._ import org.apache.spark.sql.hive._ import org.apache.spark.sql.hive.thriftserver._ -case class PandaInfo(name: String, happy: Boolean, pt: String) case class PandaPlace(name: String, pandas: Array[PandaInfo]) + case class LoadSave(sqlContext: SQLContext) { import sqlContext.implicits._ //tag::createFromRDD[] def createFromCaseClassRDD(input: RDD[PandaPlace]) = { // Create DataFrame explicitly using sqlContext and schema inferance val df1 = sqlContext.createDataFrame(input) + // Create DataFrame using sqlContext implicits and schema inferance val df2 = input.toDF() // Create a Row RDD from our RDD of case classes val rowRDD = input.map(pm => Row(pm.name, - pm.pandas.map(pi => Row(pi.name, pi.happy, pi.pt)))) + pm.pandas.map(pi => Row(pi.place, pi.pandaType, pi.happyPandas, pi.totalPandas)))) + // Create DataFrame explicitly with specified schema val schema = StructType(List( StructField("name", StringType, true), StructField("pandas", ArrayType( StructType(List( - StructField("name", StringType, true), - StructField("happy", BooleanType, true), - StructField("pt", StringType, true))))))) + StructField("place", StringType, true), + StructField("pandaType", StringType, true), + StructField("happyPandas", IntegerType, true), + StructField("totalPandas", IntegerType, true))))))) val df3 = sqlContext.createDataFrame(rowRDD, schema) } //end::createFromRDD[] @@ -51,13 +55,14 @@ case class LoadSave(sqlContext: SQLContext) { //tag::collectResults[] def collectDF(df: DataFrame) = { val result: Array[Row] = df.collect() + result } //end::collectResults[] //tag::toRDD[] def toRDD(input: DataFrame): RDD[PandaInfo] = { val rdd: RDD[Row] = input.rdd - rdd.map(row => PandaInfo(row.getAs[String](0), row.getAs[Boolean](1), row.getAs[String](2))) + rdd.map(row => PandaInfo(row.getAs[String](0), row.getAs[String](1), row.getAs[Integer](2), row.getAs[Integer](3))) } //end::toRDD[] diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 8dbc96a..bb582ba 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -3,39 +3,41 @@ */ package com.highperformancespark.examples.dataframe -import org.apache.spark.sql.{SQLContext, DataFrame, Row} -import org.apache.spark.sql.types._ - +import com.highperformancespark.examples.dataframe.HappyPanda.PandaInfo import com.holdenkarau.spark.testing._ - -import org.scalatest.FunSuite +import org.apache.spark.sql.types._ +import org.apache.spark.sql.{DataFrame, Row, SQLContext} import org.scalatest.Matchers._ -import org.scalatest.exceptions.TestFailedException -class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSuiteBase { - val inputList = List(PandaInfo("toronto", 2, 1), PandaInfo("san diego", 3, 2)) +class HappyPandasTest extends DataFrameSuiteBase { + val toronto = "toronto" + val sandiego = "san diego" + val virginia = "virginia" + val pandInfoList = List(PandaInfo(toronto, "giant", 1, 2), + PandaInfo(sandiego, "red", 2, 3), + PandaInfo(virginia, "black", 1, 10)) //tag::approxEqualDataFrames[] - test("verify simple happy pandas") { - val sqlCtx = sqlContext - import sqlCtx.implicits._ - val expectedResult = List(Row("toronto", 0.5), Row("san diego", 2/3.0)) - val expectedDf = sqlCtx.createDataFrame(sc.parallelize(expectedResult), - StructType(List(StructField("place", StringType), - StructField("percentHappy", DoubleType)))) - val inputDF = sqlCtx.createDataFrame(inputList) - val result = HappyPanda.happyPandas(inputDF) + + test("verify simple happy pandas Percentage") { + val expectedResult = List(Row(toronto, 0.5), Row(sandiego, 2/3.0), Row(virginia, 1/10.0)) + val expectedDf = createDF(expectedResult, ("place", StringType), + ("percentHappy", DoubleType)) + + val inputDF = sqlContext.createDataFrame(pandInfoList) + val result = HappyPanda.happyPandasPercentage(inputDF) + approxEqualDataFrames(expectedDf, result, 1E-5) } //end::approxEqualDataFrames[] test("verify approx by hand") { - val sqlCtx = sqlContext - import sqlCtx.implicits._ - val expectedRows = List(Row("toronto", 0.5), Row("san diego", 2/3.0)) - val inputDF = sqlCtx.createDataFrame(inputList) - val result = HappyPanda.happyPandas(inputDF) - val resultRows = result.collect() + val inputDF = sqlContext.createDataFrame(pandInfoList) + val resultDF = HappyPanda.happyPandasPercentage(inputDF) + val resultRows = resultDF.collect() + + val expectedRows = List(Row(toronto, 0.5), Row(sandiego, 2/3.0), Row(virginia, 1/10.0)) + //tag::approxEqualRow[] assert(expectedRows.size === resultRows.size) expectedRows.zip(resultRows).foreach{case (r1, r2) => @@ -45,21 +47,43 @@ class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSui //end::approxEqualRow[] } + test("test encode Panda type") { + val inputDF = sqlContext.createDataFrame(pandInfoList) + val resultDF = HappyPanda.encodePandaType(inputDF) + + val expectedRows = List(Row(toronto, 0), Row(sandiego, 1), Row(virginia, 2)) + val expectedDF = createDF3(expectedRows, ("place", StringType, true), + ("encodedType", IntegerType, false)) + + equalDataFrames(expectedDF, resultDF) + } + //tag::exactEqualDataFrames[] test("verify exact equality") { - val sqlCtx = sqlContext - import sqlCtx.implicits._ - val inputDF = sqlCtx.createDataFrame(inputList) + // test minHappyPandas + val inputDF = sqlContext.createDataFrame(pandInfoList) val result = HappyPanda.minHappyPandas(inputDF, 2) val resultRows = result.collect() - assert(List(Row("san diego", 3, 2)) === resultRows) + + val expectedRows = List(Row(sandiego, "red", 2, 3)) + assert(expectedRows === resultRows) } //end::exactEqualDataFrames[] + test("test happyPandasPlaces") { + val inputDF = sqlContext.createDataFrame(pandInfoList) + val resultDF = HappyPanda.happyPandasPlaces(inputDF) + + val expectedRows = List(PandaInfo(toronto, "giant", 1, 2), + PandaInfo(sandiego, "red", 2, 3)) + val expectedDF = sqlContext.createDataFrame(expectedRows) + + equalDataFrames(expectedDF, resultDF) + } + // Make a test once we have hivectx in the base def futureTestRrelativePandaSize() { val sqlCtx = sqlContext - import sqlCtx.implicits._ // TODO: Generate some data instead of using the small static data val inputDF = loadPandaStuffies(sqlCtx) val result = HappyPanda.computeRelativePandaSizes(inputDF) @@ -78,6 +102,18 @@ class HappyPandasTest extends FunSuite with SharedSparkContext with DataFrameSui StructField("zip", IntegerType, true))) sqlCtx.createDataFrame(sc.parallelize(pandaStuffies), schema) } -} -case class PandaInfo(place: String, totalPandas: Integer, happyPandas: Integer) + + private def createDF(list: List[Row], fields: Tuple2[String, DataType]*) = + sqlContext.createDataFrame(sc.parallelize(list), structType2(fields)) + + private def structType2(fields: Seq[(String, DataType)]) = + StructType(fields.map(f => (StructField(f._1, f._2))).toList) + + + private def createDF3(list: List[Row], fields: Tuple3[String, DataType, Boolean]*) = + sqlContext.createDataFrame(sc.parallelize(list), structType3(fields)) + + private def structType3(fields: Seq[(String, DataType, Boolean)]) = + StructType(fields.map(f => (StructField(f._1, f._2, f._3))).toList) +} \ No newline at end of file From e6cd103cea8d7e8f443ac18955724f7f5ea10196 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Mon, 28 Dec 2015 07:52:20 +0200 Subject: [PATCH 059/198] Add pandas test cases --- .../dataframe/HappyPandas.scala | 29 +-- .../dataframe/HappyPandas.scala | 171 +++++++++++++++--- 2 files changed, 161 insertions(+), 39 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 02f1396..92091bd 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -105,7 +105,6 @@ object HappyPanda { /** - * * @param name name of panda * @param zip zip code * @param pandaSize size of panda in KG @@ -121,17 +120,22 @@ object HappyPanda { //tag::minMaxPandasSizePerZip[] def minMaxPandaSizePerZip(pandas: DataFrame): DataFrame = { - // List of strings - pandas.groupBy(pandas("zip")).agg(("min", "pandaSize"), ("max", "pandaSize")) - // Map of column to aggregate - pandas.groupBy(pandas("zip")).agg(Map("pandaSize" -> "min", - "pandaSize" -> "max")) - // expression literals + pandas.groupBy(pandas("zip")).agg(min("pandaSize"), max("pandaSize")) } //end::minMaxPandasSizePerZip[] + def minPandaSizeMaxAgePerZip(pandas: DataFrame): DataFrame = { + // this query can be written in two methods + + // 1 + pandas.groupBy(pandas("zip")).agg(("pandaSize", "min"), ("age", "max")) + + // 2 + pandas.groupBy(pandas("zip")).agg(Map("pandaSize" -> "min", "age" -> "max")) + } + //tag::complexAggPerZip[] - def complexAggPerZip(pandas: DataFrame): DataFrame = { + def minMeanSizePerZip(pandas: DataFrame): DataFrame = { // Compute the min and mean pandas.groupBy(pandas("zip")).agg(min(pandas("pandaSize")), mean(pandas("pandaSize"))) } @@ -141,7 +145,7 @@ object HappyPanda { val sqlCtx = pandas.sqlContext //tag::pandasSQLQuery[] pandas.registerTempTable("pandas") - val miniPandas = sqlCtx.sql("SELECT * FROM pandas WHERE pandaSize < 100") + val miniPandas = sqlCtx.sql("SELECT * FROM pandas WHERE pandaSize < 12") //end::pandasSQLQuery[] miniPandas } @@ -157,9 +161,6 @@ object HappyPanda { * Orders pandas by size ascending and by age descending. * Pandas will be sorted by "size" first and if two pandas have the same "size" * will be sorted by "age". - * - * @param pandas - * @return */ def orderPandas(pandas: DataFrame): DataFrame = { //tag::simpleSort[] @@ -172,14 +173,14 @@ object HappyPanda { val windowSpec = Window .orderBy(pandas("age")) .partitionBy(pandas("zip")) - .rowsBetween(start = 10, end = 10) // use rangeBetween for range instead + .rowsBetween(start = -1, end = 1) // use rangeBetween for range instead //end::relativePandaSizesWindow[] //tag::relativePandaSizesQuery[] val pandaRelativeSizeFunc = (pandas("pandaSize") - avg(pandas("pandaSize")).over(windowSpec)) - pandas.select(pandas("name"), pandas("zip"), pandas("pandaSize"), + pandas.select(pandas("name"), pandas("zip"), pandas("pandaSize"), pandas("age"), pandaRelativeSizeFunc.as("panda_relative_size")) //end::relativePandaSizesQuery[] } diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index bb582ba..18a291e 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -3,7 +3,7 @@ */ package com.highperformancespark.examples.dataframe -import com.highperformancespark.examples.dataframe.HappyPanda.PandaInfo +import com.highperformancespark.examples.dataframe.HappyPanda.{PandaInfo, Pandas} import com.holdenkarau.spark.testing._ import org.apache.spark.sql.types._ import org.apache.spark.sql.{DataFrame, Row, SQLContext} @@ -13,10 +13,16 @@ class HappyPandasTest extends DataFrameSuiteBase { val toronto = "toronto" val sandiego = "san diego" val virginia = "virginia" - val pandInfoList = List(PandaInfo(toronto, "giant", 1, 2), + val pandaInfoList = List(PandaInfo(toronto, "giant", 1, 2), PandaInfo(sandiego, "red", 2, 3), PandaInfo(virginia, "black", 1, 10)) + val pandasList = List(Pandas("bata", "10010", 10, 2), + Pandas("wiza", "10010", 20, 4), + Pandas("dabdob", "11000", 8, 2), + Pandas("hanafy", "11000", 15, 7), + Pandas("hamdi", "11111", 20, 10)) + //tag::approxEqualDataFrames[] test("verify simple happy pandas Percentage") { @@ -24,7 +30,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val expectedDf = createDF(expectedResult, ("place", StringType), ("percentHappy", DoubleType)) - val inputDF = sqlContext.createDataFrame(pandInfoList) + val inputDF = sqlContext.createDataFrame(pandaInfoList) val result = HappyPanda.happyPandasPercentage(inputDF) approxEqualDataFrames(expectedDf, result, 1E-5) @@ -32,7 +38,7 @@ class HappyPandasTest extends DataFrameSuiteBase { //end::approxEqualDataFrames[] test("verify approx by hand") { - val inputDF = sqlContext.createDataFrame(pandInfoList) + val inputDF = sqlContext.createDataFrame(pandaInfoList) val resultDF = HappyPanda.happyPandasPercentage(inputDF) val resultRows = resultDF.collect() @@ -48,7 +54,7 @@ class HappyPandasTest extends DataFrameSuiteBase { } test("test encode Panda type") { - val inputDF = sqlContext.createDataFrame(pandInfoList) + val inputDF = sqlContext.createDataFrame(pandaInfoList) val resultDF = HappyPanda.encodePandaType(inputDF) val expectedRows = List(Row(toronto, 0), Row(sandiego, 1), Row(virginia, 2)) @@ -61,7 +67,7 @@ class HappyPandasTest extends DataFrameSuiteBase { //tag::exactEqualDataFrames[] test("verify exact equality") { // test minHappyPandas - val inputDF = sqlContext.createDataFrame(pandInfoList) + val inputDF = sqlContext.createDataFrame(pandaInfoList) val result = HappyPanda.minHappyPandas(inputDF, 2) val resultRows = result.collect() @@ -71,7 +77,7 @@ class HappyPandasTest extends DataFrameSuiteBase { //end::exactEqualDataFrames[] test("test happyPandasPlaces") { - val inputDF = sqlContext.createDataFrame(pandInfoList) + val inputDF = sqlContext.createDataFrame(pandaInfoList) val resultDF = HappyPanda.happyPandasPlaces(inputDF) val expectedRows = List(PandaInfo(toronto, "giant", 1, 2), @@ -81,26 +87,141 @@ class HappyPandasTest extends DataFrameSuiteBase { equalDataFrames(expectedDF, resultDF) } - // Make a test once we have hivectx in the base - def futureTestRrelativePandaSize() { - val sqlCtx = sqlContext - // TODO: Generate some data instead of using the small static data - val inputDF = loadPandaStuffies(sqlCtx) - val result = HappyPanda.computeRelativePandaSizes(inputDF) - val resultRows = result.collect() - assert(List() === resultRows) + test("test maxPandaSizePerZip") { + val inputDF = sqlContext.createDataFrame(pandasList) + val resultDF = HappyPanda.maxPandaSizePerZip(inputDF) + + val expectedRows = List(Row(pandasList(1).zip, pandasList(1).pandaSize), + Row(pandasList(3).zip, pandasList(3).pandaSize), + Row(pandasList(4).zip, pandasList(4).pandaSize)) + val expectedDF = createDF(expectedRows, ("zip", StringType), + ("max(pandaSize)", IntegerType)) + + equalDataFrames(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) + } + + test("test minMaxPandaSizePerZip"){ + val inputDF = sqlContext.createDataFrame(pandasList) + val resultDF = HappyPanda.minMaxPandaSizePerZip(inputDF) + + val expectedRows = List( + Row(pandasList(1).zip, pandasList(0).pandaSize, pandasList(1).pandaSize), + Row(pandasList(3).zip, pandasList(2).pandaSize, pandasList(3).pandaSize), + Row(pandasList(4).zip, pandasList(4).pandaSize, pandasList(4).pandaSize)) + + val expectedDF = createDF(expectedRows, ("zip", StringType), + ("min(pandaSize)", IntegerType), + ("max(pandaSize)", IntegerType)) + + equalDataFrames(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) + } + + test("test minPandaSizeMaxAgePerZip") { + val inputDF = sqlContext.createDataFrame(pandasList) + val resultDF = HappyPanda.minPandaSizeMaxAgePerZip(inputDF) + + val expectedRows = List( + Row(pandasList(1).zip, pandasList(0).pandaSize, pandasList(1).age), + Row(pandasList(3).zip, pandasList(2).pandaSize, pandasList(3).age), + Row(pandasList(4).zip, pandasList(4).pandaSize, pandasList(4).age)) + + val expectedDF = createDF(expectedRows, ("zip", StringType), + ("min(pandaSize)", IntegerType), + ("max(age)", IntegerType)) + + equalDataFrames(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) + } + + test("test complexAggPerZip") { + val inputDF = sqlContext.createDataFrame(pandasList) + val resultDF = HappyPanda.minMeanSizePerZip(inputDF) + + val expectedRows = List( + Row(pandasList(1).zip, pandasList(0).pandaSize, 15.0), + Row(pandasList(3).zip, pandasList(2).pandaSize, 11.5), + Row(pandasList(4).zip, pandasList(4).pandaSize, 20.0)) + + val expectedDF = createDF(expectedRows, ("zip", StringType), + ("min(pandaSize)", IntegerType), + ("avg(pandaSize)", DoubleType)) + + approxEqualDataFrames(expectedDF.orderBy("zip"), resultDF.orderBy("zip"), 1e-5) + } + + + test("test Simple SQL example") { + val inputDF = sqlContext.createDataFrame(pandasList) + val resultDF = HappyPanda.simpleSqlExample(inputDF) + + val expectedRows = List(pandasList(0), pandasList(2)) + val expectedDF = sqlContext.createDataFrame(expectedRows) + + equalDataFrames(expectedDF, resultDF) + } + + test("test Order Pandas") { + val inputDF = sqlContext.createDataFrame(pandasList) + val resultDF = HappyPanda.orderPandas(inputDF) + + val expectedRows = List(pandasList(2), pandasList(0), pandasList(3), + pandasList(4), pandasList(1)) + val expectedDF = sqlContext.createDataFrame(expectedRows) + + equalDataFrames(expectedDF, resultDF) + } + + + test("test computeRelativePandaSizes") { + val inputDF = loadPandaStuffies() + val resultDF = HappyPanda.computeRelativePandaSizes(inputDF) + + val expectedDF = getExpectedPandasRelativeSize() + + approxEqualDataFrames(expectedDF.orderBy("name"), resultDF.orderBy("name"), 1e-2) + } + + private def getExpectedPandasRelativeSize():DataFrame = { + val expectedRows = List( + Row("name1-1", "zip1", 10, 1, -5.0), + Row("name2-1", "zip1", 20, 2, 5.0), + Row("name3-1", "zip1", 15, 3, 1.6666), + Row("name4-1", "zip1", 5, 4, -5.0), + + Row("name1-2", "zip2", 5, 1, -7.5), + Row("name2-2", "zip2", 20, 2, 4.66666), + Row("name3-2", "zip2", 21, 3, 0.5), + + Row("name1-3", "zip3", 10, 1, 0.0), + Row("name2-3", "zip3", 10, 2, 0.0), + + Row("name1-4", "zip4", 5, 1, 0.0)) + + val expectedDF = createDF(expectedRows, ("name", StringType), + ("zip", StringType), + ("pandaSize", IntegerType), + ("age", IntegerType), + ("panda_relative_size", DoubleType)) + + expectedDF } - def loadPandaStuffies(sqlCtx: SQLContext): DataFrame = { - val pandaStuffies = List(Row("ikea", null, 0.2, 94110), - Row("tube", 6, 0.4, 94110), - Row("panda", 6, 0.5, 94110), - Row("real", 30, 77.5, 100000)) - val schema = StructType(List(StructField("name", StringType, true), - StructField("age", IntegerType, true), - StructField("pandaSize", DoubleType, true), - StructField("zip", IntegerType, true))) - sqlCtx.createDataFrame(sc.parallelize(pandaStuffies), schema) + private def loadPandaStuffies(): DataFrame = { + val pandaStuffies = List( + Pandas("name1-1", "zip1", 10, 1), + Pandas("name2-1", "zip1", 20, 2), + Pandas("name3-1", "zip1", 15, 3), + Pandas("name4-1", "zip1", 5, 4), + + Pandas("name1-2", "zip2", 5, 1), + Pandas("name2-2", "zip2", 20, 2), + Pandas("name3-2", "zip2", 21, 3), + + Pandas("name1-3", "zip3", 10, 1), + Pandas("name2-3", "zip3", 10, 2), + + Pandas("name1-4", "zip4", 5, 1)) + + sqlContext.createDataFrame(sc.parallelize(pandaStuffies)) } From 8c5071ff290a67756bf5136ec64eb55216828e31 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 28 Dec 2015 23:14:23 -0800 Subject: [PATCH 060/198] Tag imports --- .../dataframe/HappyPandas.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index b8b88ce..70b32c2 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -4,13 +4,18 @@ package com.highperformancespark.examples.dataframe import org.apache.spark._ -import org.apache.spark.sql._ +//tag::sparkSQLImports[] +import org.apache.spark.sql.{DataFrame, SQLContext, Row} import org.apache.spark.sql.catalyst.expressions.aggregate._ -import org.apache.spark.sql.DataFrame import org.apache.spark.sql.expressions._ import org.apache.spark.sql.functions._ +import org.apache.spark.sql.types._ +//end::sparkSQLImports[] +//tag::sparkHiveImports[] +// Additional imports for using HiveContext import org.apache.spark.sql.hive._ import org.apache.spark.sql.hive.thriftserver._ +//end::sparkHiveImports[] object HappyPanda { // How to create a HiveContext or SQLContext with an existing SparkContext From 64b5f02f13f805ffd21fd99cbd62eb939fae002f Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Thu, 31 Dec 2015 05:39:46 +0200 Subject: [PATCH 061/198] Create configurable test case for computeRelativePandaSizes --- .../dataframe/HappyPandas.scala | 4 +- .../dataframe/HappyPandas.scala | 82 ++++++++++++------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 92091bd..de5122d 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -74,7 +74,7 @@ object HappyPanda { * Encodes pandaType to Integer values instead of string values. * * @param pandaInfo the input DataFrame - * @return Returns a pair of (pandaId, mapped integer value for pandaType) + * @return Returns a DataFrame of pandaId and integer value for pandaType. */ def encodePandaType(pandaInfo: DataFrame): DataFrame = { pandaInfo.select(pandaInfo("place"), @@ -173,7 +173,7 @@ object HappyPanda { val windowSpec = Window .orderBy(pandas("age")) .partitionBy(pandas("zip")) - .rowsBetween(start = -1, end = 1) // use rangeBetween for range instead + .rowsBetween(start = -10, end = 10) // use rangeBetween for range instead //end::relativePandaSizesWindow[] //tag::relativePandaSizesQuery[] diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 18a291e..280a373 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -9,6 +9,9 @@ import org.apache.spark.sql.types._ import org.apache.spark.sql.{DataFrame, Row, SQLContext} import org.scalatest.Matchers._ +import scala.collection.mutable +import scala.util.Random + class HappyPandasTest extends DataFrameSuiteBase { val toronto = "toronto" val sandiego = "san diego" @@ -172,29 +175,44 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test computeRelativePandaSizes") { - val inputDF = loadPandaStuffies() + val inputPandaList = loadPandaStuffies() + val inputDF = sqlContext.createDataFrame(inputPandaList) + val resultDF = HappyPanda.computeRelativePandaSizes(inputDF) - val expectedDF = getExpectedPandasRelativeSize() + val expectedDF = getExpectedPandasRelativeSize(inputPandaList, -10, 10) - approxEqualDataFrames(expectedDF.orderBy("name"), resultDF.orderBy("name"), 1e-2) + approxEqualDataFrames(expectedDF.orderBy("name"), resultDF.orderBy("name"), 1e-5) } - private def getExpectedPandasRelativeSize():DataFrame = { - val expectedRows = List( - Row("name1-1", "zip1", 10, 1, -5.0), - Row("name2-1", "zip1", 20, 2, 5.0), - Row("name3-1", "zip1", 15, 3, 1.6666), - Row("name4-1", "zip1", 5, 4, -5.0), + private def getExpectedPandasRelativeSize(pandaList: List[Pandas], start: Int, end: Int):DataFrame = { + + val expectedRows = + pandaList + .groupBy(_.zip) + .map(zipPandas => (zipPandas._1, zipPandas._2.sortBy(_.age))) + .flatMap(zipPandas => { + val pandas = zipPandas._2 + val length = pandas.size - 1 + val result = new mutable.MutableList[Row] - Row("name1-2", "zip2", 5, 1, -7.5), - Row("name2-2", "zip2", 20, 2, 4.66666), - Row("name3-2", "zip2", 21, 3, 0.5), + for (i <- 0 to length) { + var totalSum = 0 + val startOffset = math.max(0, i + start) + val endOffset = math.min(length, i + end) - Row("name1-3", "zip3", 10, 1, 0.0), - Row("name2-3", "zip3", 10, 2, 0.0), + for (j <- (startOffset to endOffset)) + totalSum += pandas(j).pandaSize - Row("name1-4", "zip4", 5, 1, 0.0)) + val count = (endOffset - startOffset + 1) + val average = totalSum.toDouble / count + + val panda = pandas(i) + result += Row(panda.name, panda.zip, panda.pandaSize, panda.age, panda.pandaSize - average) + } + + result + }).toList val expectedDF = createDF(expectedRows, ("name", StringType), ("zip", StringType), @@ -205,23 +223,31 @@ class HappyPandasTest extends DataFrameSuiteBase { expectedDF } - private def loadPandaStuffies(): DataFrame = { - val pandaStuffies = List( - Pandas("name1-1", "zip1", 10, 1), - Pandas("name2-1", "zip1", 20, 2), - Pandas("name3-1", "zip1", 15, 3), - Pandas("name4-1", "zip1", 5, 4), + private def loadPandaStuffies(): List[Pandas] = { + val zipCount = 20 + val maxPandasPerZip = 100 + val maxPandaAge = 50 + val maxPandaSize = 500 + val random = new Random() + + val pandas = + (1 to zipCount) + .flatMap(zipId => { + val pandasCount = 1 + random.nextInt(maxPandasPerZip) + val zipName = s"zip($zipId)" - Pandas("name1-2", "zip2", 5, 1), - Pandas("name2-2", "zip2", 20, 2), - Pandas("name3-2", "zip2", 21, 3), + (1 to pandasCount).map(pandaId => { + val name = s"panda($pandaId)($zipId)" + val size = 1 + random.nextInt(maxPandaSize) + val age = 1 + random.nextInt(maxPandaAge) - Pandas("name1-3", "zip3", 10, 1), - Pandas("name2-3", "zip3", 10, 2), + Pandas(name, zipName, size, age) + } + ) - Pandas("name1-4", "zip4", 5, 1)) + }) - sqlContext.createDataFrame(sc.parallelize(pandaStuffies)) + pandas.toList } From 356d7847c8a0081dc2c26f8ba96a421b1cf27870 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Thu, 31 Dec 2015 06:20:02 +0200 Subject: [PATCH 062/198] Reduce computeRelativePandaSizes test case size --- .../dataframe/HappyPandas.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 280a373..ea40bd8 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -224,8 +224,8 @@ class HappyPandasTest extends DataFrameSuiteBase { } private def loadPandaStuffies(): List[Pandas] = { - val zipCount = 20 - val maxPandasPerZip = 100 + val zipCount = 3 + val maxPandasPerZip = 15 val maxPandaAge = 50 val maxPandaSize = 500 val random = new Random() From f4363e96e4e5fa05bf1cf9246b3e24298823885b Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Mon, 4 Jan 2016 07:43:31 +0200 Subject: [PATCH 063/198] Add documentation and examples to GoldiLocksFirstTry https://github.com/high-performance-spark/high-performance-spark-examples/issues/4 --- .../GoldiLocks/GoldiLocksFirstTry.scala | 208 ++++++++++++------ 1 file changed, 141 insertions(+), 67 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala index 873898d..2192302 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala @@ -1,19 +1,19 @@ package com.highperformancespark.examples.goldilocks -import org.apache.spark.Partition import org.apache.spark.rdd.RDD import org.apache.spark.sql.DataFrame import org.apache.spark.storage.StorageLevel import scala.collection.mutable import scala.collection.mutable.ArrayBuffer - +import scala.collection.Map; +import scala.collection.mutable.MutableList; object GoldiLocksGroupByKey { //tag::groupByKey[] def findRankStatistics( pairRDD: RDD[(Int, Double)], - ranks: List[Long]): scala.collection.Map[Int, List[Double]] = { + ranks: List[Long]): Map[Int, List[Double]] = { pairRDD.groupByKey().mapValues(iter => { val ar = iter.toArray.sorted ranks.map(n => ar(n.toInt)) @@ -25,42 +25,93 @@ object GoldiLocksGroupByKey { //tag::firstTry[] object GoldiLocksFirstTry { - def findQuantiles( dataFrame: DataFrame, targetRanks: List[Long] ) = { - val n = dataFrame.schema.length - val valPairs: RDD[(Double, Int)] = getPairs(dataFrame) - val sorted = valPairs.sortByKey() - sorted.persist(StorageLevel.MEMORY_AND_DISK) - val parts : Array[Partition] = sorted.partitions - val map1 = getTotalsForeachPart(sorted, parts.length, n ) - val map2 = getLocationsOfRanksWithinEachPart(targetRanks, map1, n) - val result = findElementsIteratively(sorted, map2) + /** + * Find nth target rank for every column. + * + * For example: + * + * dataframe: + * (0.0, 4.5, 7.7, 5.0) + * (1.0, 5.5, 6.7, 6.0) + * (2.0, 5.5, 1.5, 7.0) + * (3.0, 5.5, 0.5, 7.0) + * (4.0, 5.5, 0.5, 8.0) + * + * targetRanks: + * 1, 3 + * + * The output will be: + * 0 -> (0.0, 2.0) + * 1 -> (4.5, 5.5) + * 2 -> (7.7, 1.5) + * 3 -> (5.0, 7.0) + * + * @param dataFrame dataframe of doubles + * @param targetRanks the required ranks for every column + * + * @return map of (column index, list of target ranks) + */ + def findQuantiles(dataFrame: DataFrame, targetRanks: List[Long]): + Map[Int, Iterable[Double]] = { + + val valueColumnPairs: RDD[(Double, Int)] = getValueColumnIndexPairs(dataFrame) + val sortedValueColumnPairs = valueColumnPairs.sortByKey() + sortedValueColumnPairs.persist(StorageLevel.MEMORY_AND_DISK) + + val numOfColumns = dataFrame.schema.length + val partitionColumnsFreq = getColumnFreqPerPartition(sortedValueColumnPairs, numOfColumns) + val ranksLocations = getLocationsOfRanksWithinEachPart(targetRanks, partitionColumnsFreq, numOfColumns) + val result = findElementsIteratively(sortedValueColumnPairs, ranksLocations) result.groupByKey().collectAsMap() } /** - * Step 1. Map the rows to pairs of (value, colIndex) - * @param dataFrame of double columns to compute the rank satistics for - * @return + * Step 1. Map the rows to pairs of (value, column Index). + * + * For example: + * + * dataFrame: + * 1.5, 1.25, 2.0 + * 5.25, 2.5, 1.5 + * + * The output RDD will be: + * (1.5, 0) (1.25, 1) (2.0, 2) (5.25, 0) (2.5, 1) (1.5, 2) + * + * @param dataFrame dateframe of doubles + * + * @return RDD of pairs (value, column Index) */ - private def getPairs(dataFrame : DataFrame ): RDD[(Double, Int )] ={ - dataFrame.flatMap( row => row.toSeq.zipWithIndex.map{ case (v, index ) => - (v.toString.toDouble, index )}) + private def getValueColumnIndexPairs(dataFrame : DataFrame): RDD[(Double, Int)] = { + dataFrame.flatMap(row => row.toSeq.zipWithIndex.map{ case (v, index) => + (v.toString.toDouble, index)}) } /** - * Step 2. Find the number of elements for each column in each partition - * @param sorted - the sorted (value, colIndex) pairs - * @param numPartitions - * @param n the number of columns - * @return an RDD the length of the number of partitions, where each row contains - * - the partition index - * - an array, totalsPerPart where totalsPerPart(i) = the number of elements in column - * i on this partition + * Step 2. Find the number of elements for each column in each partition. + * + * For Example: + * + * sortedValueColumnPairs: + * Partition 1: (1.5, 0) (1.25, 1) (2.0, 2) (5.25, 0) + * Partition 2: (7.5, 1) (9.5, 2) + * + * numOfColumns: 3 + * + * The output will be: + * [(0, [2, 1, 1]), (1, [0, 1, 1])] + * + * @param sortedValueColumnPairs - sorted RDD of (value, column Index) pairs + * @param numOfColumns the number of columns + * + * @return Array that contains (partition index, number of elements from every column on this partition) */ - private def getTotalsForeachPart(sorted: RDD[(Double, Int)], numPartitions: Int, n : Int ) = { - val zero = Array.fill[Long](n)(0) - sorted.mapPartitionsWithIndex((partitionIndex : Int, it : Iterator[(Double, Int)]) => { - val totalsPerPart : Array[Long] = it.aggregate(zero)( + private def getColumnFreqPerPartition(sortedValueColumnPairs: RDD[(Double, Int)], numOfColumns : Int): + Array[(Int, Array[Long])] = { + + val zero = Array.fill[Long](numOfColumns)(0) + + def aggregateColumnFrequencies (partitionIndex : Int, valueColumnPairs : Iterator[(Double, Int)]) = { + val totalsPerPart : Array[Long] = valueColumnPairs.aggregate(zero)( (a : Array[Long], v : (Double ,Int)) => { val (value, colIndex) = v a(colIndex) = a(colIndex) + 1L @@ -70,62 +121,85 @@ object GoldiLocksFirstTry { require(a.length == b.length) a.zip(b).map{ case(aVal, bVal) => aVal + bVal} }) + Iterator((partitionIndex, totalsPerPart)) - }).collect() + } + + sortedValueColumnPairs.mapPartitionsWithIndex(aggregateColumnFrequencies).collect() } + /** * Step 3: For each Partition determine the index of the elements that are desired rank statistics - * @param partitionMap- the result of the previous method - * @return an Array, the length of the number of partitions where each row contains - * - the partition index - * - a list, relevantIndexList where relevantIndexList(i) = the index of an element on this - * partition that matches one of the target ranks + * + * For Example: + * targetRanks: 5 + * partitionColumnsFreq: [(0, [2, 3]), (1, [4, 1]), (2, [5, 2])] + * numOfColumns: 2 + * + * The output will be: + * [(0, []), (1, [(0, 3)]), (2, [(1, 1)])] + * + * @param partitionColumnsFreq Array of (partition index, columns frequencies per this partition) + * + * @return Array that contains (partition index, relevantIndexList where relevantIndexList(i) = the index + * of an element on this partition that matches one of the target ranks) */ private def getLocationsOfRanksWithinEachPart(targetRanks : List[Long], - partitionMap : Array[(Int, Array[Long])], n : Int ) : Array[(Int, List[(Int, Long)])] = { - val runningTotal = Array.fill[Long](n)(0) - partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> - val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() - totals.zipWithIndex.foreach{ case (colCount, colIndex) => { + partitionColumnsFreq : Array[(Int, Array[Long])], numOfColumns : Int) : Array[(Int, List[(Int, Long)])] = { + + val runningTotal = Array.fill[Long](numOfColumns)(0) + + partitionColumnsFreq.sortBy(_._1).map { case (partitionIndex, columnsFreq) => + val relevantIndexList = new MutableList[(Int, Long)]() + + columnsFreq.zipWithIndex.foreach{ case (colCount, colIndex) => { val runningTotalCol = runningTotal(colIndex) + val ranksHere: List[Long] = targetRanks.filter(rank => (runningTotalCol < rank && runningTotalCol + colCount >= rank)) + + // for each of the rank statistics present add this column index and the index it will be at + // on this partition (the rank - the running total) + relevantIndexList ++= ranksHere.map(rank => (colIndex, rank - runningTotalCol)) + runningTotal(colIndex) += colCount - val ranksHere = targetRanks.filter(rank => - runningTotalCol <= rank && runningTotalCol + colCount >= rank - ) - //for each of the rank statistics present add this column index and the index it will be - //at on this partition (the rank - the running total) - ranksHere.foreach(rank => { - relevantIndexList += ((colIndex, rank-runningTotalCol)) - }) }} + (partitionIndex, relevantIndexList.toList) } } /** - * Step4: Using the results of the previous method, scan the data and return the elements - * which correspond to the rank statistics we are looking for in each column - */ - private def findElementsIteratively(sorted : RDD[(Double, Int)], locations : Array[(Int, List[(Int, Long)])]) = { - sorted.mapPartitionsWithIndex((index : Int, it : Iterator[(Double, Int)]) => { - val targetsInThisPart = locations(index)._2 - val len = targetsInThisPart.length - if (len > 0) { - val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) - val keysInThisPart = targetsInThisPart.map(_._1).distinct + * Finds rank statistics elements using ranksLocations. + * + * @param sortedValueColumnPairs - sorted RDD of (value, colIndex) pairs + * @param ranksLocations Array of (partition Index, list of (column index, rank index of this column at this partition)) + * + * @return + */ + private def findElementsIteratively(sortedValueColumnPairs : RDD[(Double, Int)], + ranksLocations : Array[(Int, List[(Int, Long)])]): RDD[(Int, Double)] = { + + sortedValueColumnPairs.mapPartitionsWithIndex((partitionIndex : Int, valueColumnPairs : Iterator[(Double, Int)]) => { + val targetsInThisPart: List[(Int, Long)] = ranksLocations(partitionIndex)._2 + if (!targetsInThisPart.isEmpty) { + val columnsRelativeIndex: Map[Int, List[Long]] = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) + val columnsInThisPart = targetsInThisPart.map(_._1).distinct + val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() - keysInThisPart.foreach(key => runningTotals+=((key, 0L))) - val newIt : ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() - it.foreach{ case( value, colIndex) => { - if(runningTotals isDefinedAt colIndex){ + runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap + + val result : ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + + valueColumnPairs.foreach{ case(value, colIndex) => { + if (runningTotals isDefinedAt colIndex) { val total = runningTotals(colIndex) + 1L runningTotals.update(colIndex, total) - if(partMap(colIndex).contains(total)){ - newIt += ((colIndex,value )) - } + + if (columnsRelativeIndex(colIndex).contains(total)) + result += ((colIndex, value)) } }} - newIt.toIterator + + result.toIterator } else { Iterator.empty From b9a2413f59cb2998584f660196aea209b6a07408 Mon Sep 17 00:00:00 2001 From: Rachel Date: Mon, 4 Jan 2016 10:07:31 -0800 Subject: [PATCH 064/198] adding one test --- .../QuantileOnlyArtisanalTest.scala | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index 43525c6..c90c1c3 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -5,7 +5,7 @@ import org.apache.spark.sql.SQLContext import org.scalatest.{BeforeAndAfterAll, FunSuite} // tag::MAGIC_PANDA[] -class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { +class QuantileOnlyArtisanalTest extends FunSuite with BeforeAndAfterAll { @transient private var _sc: SparkContext = _ def sc: SparkContext = _sc @@ -23,7 +23,7 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { GoldiLocksRow(4.0, 5.5, 0.5, 8.0) ) - test("Goldi locks first try ") { + test("Goldilocks first try ") { val sqlContext = new SQLContext(sc) val input = sqlContext.createDataFrame(inputList) val secondAndThird = GoldiLocksFirstTry.findQuantiles(input, targetRanks = List(2L, 3L)) @@ -37,6 +37,43 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { secondAndThird.get(index).get.toSet.equals(expectedRanks)}) } + //tests the edge case in which one partition does not contain any of the elements in one column + test("Goldilocks first try multiplePartitions") { + import org.scalatest.PrivateMethodTester._ + val testData = sc.parallelize(List(1.0, 2.0, 3.0, 4.0).map(x => (x, x)), 3) + val mapPartitions = testData.mapPartitionsWithIndex { + case (index, iter) => + val key = if (index == 1) 1 else 0 + iter.map(x => (x._1, key)) + } + + val getColumnFreqPerPartition = PrivateMethod[ Array[(Int, Array[Long])]]('getColumnFreqPerPartition) + val totals = GoldiLocksFirstTry invokePrivate getColumnFreqPerPartition(mapPartitions, 2) + + totals.foreach(x => println(x._1 + " : " + x._2.mkString(" "))) + val getLocationsOfRanksWithinEachPart = + PrivateMethod[Array[(Int, List[(Int, Long)])]]('getLocationsOfRanksWithinEachPart) + + val locations = GoldiLocksFirstTry invokePrivate getLocationsOfRanksWithinEachPart(List(1L), totals, 2) + locations.foreach(x => println(x._1 + " : " + x._2.mkString(" "))) + + //assert that there is nothing in the column with index 1 on the second partition + assert(totals(1)._2(0) == 0 ) + + val firstPartition = locations(0)._2 + //assertFirstPartitionOnlyContains a target rank for the for columnIndex 0, at index 1 + assert(firstPartition.toSet.equals(Set((0,1))) ) + + //assertSecondPartition only contains rank for columnIndex 1, at index 1 + val secondPartition = locations(1)._2 + assert(secondPartition.toSet.equals(Set((1,1))) ) + + //assert ThirdPartition contains no locations + val thirdPartition = locations(2)._2 + assert(thirdPartition.toSet.equals(Set())) + assert(locations.length == 3) + } + test("GoldiLocks With Hashmap ") { val sqlContext = new SQLContext(sc) val input = sqlContext.createDataFrame(inputList) @@ -72,5 +109,4 @@ class QuantileOnlyArtisanallTest extends FunSuite with BeforeAndAfterAll { } // end::MAGIC_PANDA[] - case class GoldiLocksRow(pandaId : Double, softness : Double, fuzzyness : Double, size : Double ) \ No newline at end of file From 7c54c22160eb3f3cab35b030eb30a3237b32bcec Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Wed, 6 Jan 2016 07:29:19 +0200 Subject: [PATCH 065/198] Add documentation and examples to GoldiLocksWithHashMap --- .../GoldiLocks/GoldiLocksFirstTry.scala | 40 +-- .../GoldiLocks/GoldiLocksWithHashMap.scala | 262 +++++++++++------- .../QuantileOnlyArtisanalTest.scala | 14 +- 3 files changed, 197 insertions(+), 119 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala index 2192302..b2a4985 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala @@ -6,8 +6,8 @@ import org.apache.spark.storage.StorageLevel import scala.collection.mutable import scala.collection.mutable.ArrayBuffer -import scala.collection.Map; -import scala.collection.mutable.MutableList; +import scala.collection.Map +import scala.collection.mutable.MutableList object GoldiLocksGroupByKey { //tag::groupByKey[] @@ -51,18 +51,19 @@ object GoldiLocksFirstTry { * * @return map of (column index, list of target ranks) */ - def findQuantiles(dataFrame: DataFrame, targetRanks: List[Long]): + def findRankStatistics(dataFrame: DataFrame, targetRanks: List[Long]): Map[Int, Iterable[Double]] = { - val valueColumnPairs: RDD[(Double, Int)] = getValueColumnIndexPairs(dataFrame) + val valueColumnPairs: RDD[(Double, Int)] = getValueColumnPairs(dataFrame) val sortedValueColumnPairs = valueColumnPairs.sortByKey() sortedValueColumnPairs.persist(StorageLevel.MEMORY_AND_DISK) val numOfColumns = dataFrame.schema.length - val partitionColumnsFreq = getColumnFreqPerPartition(sortedValueColumnPairs, numOfColumns) - val ranksLocations = getLocationsOfRanksWithinEachPart(targetRanks, partitionColumnsFreq, numOfColumns) - val result = findElementsIteratively(sortedValueColumnPairs, ranksLocations) - result.groupByKey().collectAsMap() + val partitionColumnsFreq = getColumnsFreqPerPartition(sortedValueColumnPairs, numOfColumns) + val ranksLocations = getRanksLocationsWithinEachPart(targetRanks, partitionColumnsFreq, numOfColumns) + + val targetRanksValues = findTargetRanksIteratively(sortedValueColumnPairs, ranksLocations) + targetRanksValues.groupByKey().collectAsMap() } /** @@ -81,7 +82,7 @@ object GoldiLocksFirstTry { * * @return RDD of pairs (value, column Index) */ - private def getValueColumnIndexPairs(dataFrame : DataFrame): RDD[(Double, Int)] = { + private def getValueColumnPairs(dataFrame : DataFrame): RDD[(Double, Int)] = { dataFrame.flatMap(row => row.toSeq.zipWithIndex.map{ case (v, index) => (v.toString.toDouble, index)}) } @@ -105,24 +106,23 @@ object GoldiLocksFirstTry { * * @return Array that contains (partition index, number of elements from every column on this partition) */ - private def getColumnFreqPerPartition(sortedValueColumnPairs: RDD[(Double, Int)], numOfColumns : Int): + private def getColumnsFreqPerPartition(sortedValueColumnPairs: RDD[(Double, Int)], numOfColumns : Int): Array[(Int, Array[Long])] = { val zero = Array.fill[Long](numOfColumns)(0) def aggregateColumnFrequencies (partitionIndex : Int, valueColumnPairs : Iterator[(Double, Int)]) = { - val totalsPerPart : Array[Long] = valueColumnPairs.aggregate(zero)( + val columnsFreq : Array[Long] = valueColumnPairs.aggregate(zero)( (a : Array[Long], v : (Double ,Int)) => { val (value, colIndex) = v a(colIndex) = a(colIndex) + 1L a }, (a : Array[Long], b : Array[Long]) => { - require(a.length == b.length) a.zip(b).map{ case(aVal, bVal) => aVal + bVal} }) - Iterator((partitionIndex, totalsPerPart)) + Iterator((partitionIndex, columnsFreq)) } sortedValueColumnPairs.mapPartitionsWithIndex(aggregateColumnFrequencies).collect() @@ -144,8 +144,9 @@ object GoldiLocksFirstTry { * @return Array that contains (partition index, relevantIndexList where relevantIndexList(i) = the index * of an element on this partition that matches one of the target ranks) */ - private def getLocationsOfRanksWithinEachPart(targetRanks : List[Long], - partitionColumnsFreq : Array[(Int, Array[Long])], numOfColumns : Int) : Array[(Int, List[(Int, Long)])] = { + private def getRanksLocationsWithinEachPart(targetRanks : List[Long], + partitionColumnsFreq : Array[(Int, Array[Long])], + numOfColumns : Int) : Array[(Int, List[(Int, Long)])] = { val runningTotal = Array.fill[Long](numOfColumns)(0) @@ -154,7 +155,8 @@ object GoldiLocksFirstTry { columnsFreq.zipWithIndex.foreach{ case (colCount, colIndex) => { val runningTotalCol = runningTotal(colIndex) - val ranksHere: List[Long] = targetRanks.filter(rank => (runningTotalCol < rank && runningTotalCol + colCount >= rank)) + val ranksHere: List[Long] = targetRanks.filter(rank => + (runningTotalCol < rank && runningTotalCol + colCount >= rank)) // for each of the rank statistics present add this column index and the index it will be at // on this partition (the rank - the running total) @@ -173,9 +175,9 @@ object GoldiLocksFirstTry { * @param sortedValueColumnPairs - sorted RDD of (value, colIndex) pairs * @param ranksLocations Array of (partition Index, list of (column index, rank index of this column at this partition)) * - * @return + * @return returns RDD of the target ranks (column index, value) */ - private def findElementsIteratively(sortedValueColumnPairs : RDD[(Double, Int)], + private def findTargetRanksIteratively(sortedValueColumnPairs : RDD[(Double, Int)], ranksLocations : Array[(Int, List[(Int, Long)])]): RDD[(Int, Double)] = { sortedValueColumnPairs.mapPartitionsWithIndex((partitionIndex : Int, valueColumnPairs : Iterator[(Double, Int)]) => { @@ -190,7 +192,7 @@ object GoldiLocksFirstTry { val result : ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() valueColumnPairs.foreach{ case(value, colIndex) => { - if (runningTotals isDefinedAt colIndex) { + if (runningTotals contains colIndex) { val total = runningTotals(colIndex) + 1L runningTotals.update(colIndex, total) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala index c4f4005..3dc2145 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala @@ -1,133 +1,209 @@ package com.highperformancespark.examples.goldilocks -import org.apache.spark.Partition import org.apache.spark.rdd.RDD import org.apache.spark.sql.DataFrame import org.apache.spark.storage.StorageLevel -import scala.collection.mutable +import scala.collection.{mutable, Map} import scala.collection.mutable.ArrayBuffer -import scala.reflect.runtime.{universe => ru} +import scala.collection.mutable.MutableList //tag::hashMap[] object GoldiLocksWithHashMap { - def findQuantiles( dataFrame: DataFrame , targetRanks: List[Long] ) = { - val valueIndexCountPairs: RDD[((Double, Int), Long)] = createHashMap(dataFrame) - val n = dataFrame.schema.length - val sorted = valueIndexCountPairs.sortByKey() - sorted.persist(StorageLevel.MEMORY_AND_DISK) - val parts : Array[Partition] = sorted.partitions - val map1 = getTotalsForeachPart(sorted, parts.length, n ) - val map2 = getLocationsOfRanksWithinEachPart(targetRanks, map1, n) - val result = findElementsIteratively(sorted, map2) - result.groupByKey().collectAsMap() + /** + * Find nth target rank for every column. + * + * For example: + * + * dataframe: + * (0.0, 4.5, 7.7, 5.0) + * (1.0, 5.5, 6.7, 6.0) + * (2.0, 5.5, 1.5, 7.0) + * (3.0, 5.5, 0.5, 7.0) + * (4.0, 5.5, 0.5, 8.0) + * + * targetRanks: + * 1, 3 + * + * The output will be: + * 0 -> (0.0, 2.0) + * 1 -> (4.5, 5.5) + * 2 -> (7.7, 1.5) + * 3 -> (5.0, 7.0) + * + * @param dataFrame dataframe of doubles + * @param targetRanks the required ranks for every column + * + * @return map of (column index, list of target ranks) + */ + def findRankStatistics(dataFrame: DataFrame, targetRanks: List[Long]): + Map[Int, Iterable[Double]] = { + + val aggregatedValueColumnPairs: RDD[((Double, Int), Long)] = getAggregatedValueColumnPairs(dataFrame) + val sortedAggregatedValueColumnPairs = aggregatedValueColumnPairs.sortByKey() + sortedAggregatedValueColumnPairs.persist(StorageLevel.MEMORY_AND_DISK) + + val numOfColumns = dataFrame.schema.length + val partitionColumnsFreq = getColumnsFreqPerPartition(sortedAggregatedValueColumnPairs, numOfColumns) + val ranksLocations = getRanksLocationsWithinEachPart(targetRanks, partitionColumnsFreq, numOfColumns) + + val targetRanksValues = findTargetRanksIteratively(sortedAggregatedValueColumnPairs, ranksLocations) + targetRanksValues.groupByKey().collectAsMap() } /** * Step 1. Map the rows to pairs of ((value, colIndex), count) where count is the number of times * that value and that pair appear on this partition + * + * For example: + * + * dataFrame: + * 1.5, 1.25, 2.0 + * 1.5, 2.5, 2.0 + * + * The output RDD will be: + * ((1.5, 0), 2) ((1.25, 1), 1) ((2.5, 1), 1) ((2.0, 2), 2) + * * @param dataFrame of double columns to compute the rank statistics for - * @return + * + * @return returns RDD of ((value, column index), count) */ - def createHashMap(dataFrame : DataFrame) : RDD[((Double, Int), Long)] = { - val map = dataFrame.rdd.mapPartitions(it => { - val hashMap = new mutable.HashMap[(Double, Int), Long]() - it.foreach( row => { - row.toSeq.zipWithIndex.foreach{ case (value, i) => { - val v = value.toString.toDouble - val key = (v, i) - val count = hashMap.getOrElseUpdate(key, 0) - hashMap.update(key, count + 1 ) + def getAggregatedValueColumnPairs(dataFrame : DataFrame) : RDD[((Double, Int), Long)] = { + val aggregatedValueColumnRDD = dataFrame.rdd.mapPartitions(rows => { + val valueColumnMap = new mutable.HashMap[(Double, Int), Long]() + rows.foreach(row => { + row.toSeq.zipWithIndex.foreach{ case (value, columnIndex) => { + val key = (value.toString.toDouble, columnIndex) + val count = valueColumnMap.getOrElseUpdate(key, 0) + valueColumnMap.update(key, count + 1) }} }) - val newM = hashMap.toArray - newM.toIterator + + valueColumnMap.toIterator }) - map + + aggregatedValueColumnRDD } /** - * Step 2. Find the number of elements for each column in each partition - * @param sorted rdd of ((value, index), count) pairs which has already been sorted - * @param numPartitions the number of partitions - * @return an RDD the length of the number of partitions, where each row contains - * - the partition index - * - an array, totalsPerPart where totalsPerPart(i) = the number of elements in column - * i on this partition - */ - private def getTotalsForeachPart(sorted: RDD[((Double, Int), Long)], numPartitions: Int, n : Int ) = { - val zero = Array.fill[Long](n)(0) - sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { - val totalsPerPart : Array[Long] = it.aggregate(zero)( - (a : Array[Long], v : ((Double ,Int), Long)) => { - val ((value, colIndex) , count) = v + * Step 2. Find the number of elements for each column in each partition. + * + * For Example: + * + * sortedValueColumnPairs: + * Partition 1: ((1.5, 0), 2) ((2.0, 0), 1) + * Partition 2: ((4.0, 0), 3) ((3.0, 1), 1) + * + * numOfColumns: 3 + * + * The output will be: + * [(0, [3, 0]), (1, [3, 1])] + * + * @param sortedAggregatedValueColumnPairs - sortedAggregatedValueColumnPairs RDD of ((value, column index), count) + * @param numOfColumns the number of columns + * + * @return Array that contains (partition index, number of elements from every column on this partition) + */ + private def getColumnsFreqPerPartition(sortedAggregatedValueColumnPairs: RDD[((Double, Int), Long)], + numOfColumns : Int): Array[(Int, Array[Long])] = { + + val zero = Array.fill[Long](numOfColumns)(0) + def aggregateColumnFrequencies(partitionIndex : Int, pairs : Iterator[((Double, Int), Long)]) = { + val columnsFreq : Array[Long] = pairs.aggregate(zero)( + (a : Array[Long], v : ((Double,Int), Long)) => { + val ((value, colIndex), count) = v a(colIndex) = a(colIndex) + count a}, (a : Array[Long], b : Array[Long]) => { - require(a.length == b.length) a.zip(b).map{ case(aVal, bVal) => aVal + bVal} }) - Iterator((index, totalsPerPart)) - }).collect() + + Iterator((partitionIndex, columnsFreq)) + } + + sortedAggregatedValueColumnPairs.mapPartitionsWithIndex(aggregateColumnFrequencies).collect() } /** - * Step 3: For each Partition determine the index of the elements that are desired rank statistics - * @param partitionMap- the result of the previous method - * @return an Array, the length of the number of partitions where each row contains - * - the partition index - * - a list, relevantIndexList where relevantIndexList(i) = the index of an element on this - * partition that matches one of the target ranks - */ - private def getLocationsOfRanksWithinEachPart(targetRanks : List[Long], - partitionMap : Array[(Int, Array[Long])], n : Int ) : Array[(Int, List[(Int, Long)])] = { - val runningTotal = Array.fill[Long](n)(0) - partitionMap.sortBy(_._1).map { case (partitionIndex, totals)=> { - val relevantIndexList = new scala.collection.mutable.MutableList[(Int, Long)]() - totals.zipWithIndex.foreach{ case (colCount, colIndex) => { + * Step 3: For each Partition determine the index of the elements that are desired rank statistics + * + * For Example: + * targetRanks: 5 + * partitionColumnsFreq: [(0, [2, 3]), (1, [4, 1]), (2, [5, 2])] + * numOfColumns: 2 + * + * The output will be: + * [(0, []), (1, [(0, 3)]), (2, [(1, 1)])] + * + * @param partitionColumnsFreq Array of (partition index, columns frequencies per this partition) + * + * @return Array that contains (partition index, relevantIndexList where relevantIndexList(i) = the index + * of an element on this partition that matches one of the target ranks) + */ + private def getRanksLocationsWithinEachPart(targetRanks : List[Long], + partitionColumnsFreq : Array[(Int, Array[Long])], + numOfColumns : Int) : Array[(Int, List[(Int, Long)])] = { + + val runningTotal = Array.fill[Long](numOfColumns)(0) + + partitionColumnsFreq.sortBy(_._1).map { case (partitionIndex, columnsFreq)=> { + val relevantIndexList = new MutableList[(Int, Long)]() + + columnsFreq.zipWithIndex.foreach{ case (colCount, colIndex) => { val runningTotalCol = runningTotal(colIndex) + + val ranksHere: List[Long] = targetRanks.filter(rank => + (runningTotalCol < rank && runningTotalCol + colCount >= rank)) + relevantIndexList ++= ranksHere.map(rank => (colIndex, rank - runningTotalCol)) + runningTotal(colIndex) += colCount - val ranksHere = targetRanks.filter(rank => - (runningTotalCol <= rank && runningTotalCol + colCount >= rank) - ) - ranksHere.foreach(rank => { - relevantIndexList += ((colIndex, rank-runningTotalCol)) - }) - }} //end of mapping col counts + }} + (partitionIndex, relevantIndexList.toList) }} } /** - * Step4: Using the results of the previous method, scan the data and return the elements - * which correspond to the rank statistics we are looking for in each column - */ - private def findElementsIteratively(sorted : RDD[((Double, Int), Long)], - locations : Array[(Int, List[(Int, Long)])]) = { - sorted.mapPartitionsWithIndex((index : Int, it : Iterator[((Double, Int), Long)]) => { - val targetsInThisPart = locations(index)._2 - val len = targetsInThisPart.length - if(len >0 ) { - val partMap = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) - val keysInThisPart = targetsInThisPart.map(_._1).distinct - val runningTotals: mutable.HashMap[Int, Long] = new mutable.HashMap() - keysInThisPart.foreach(key => runningTotals += ((key, 0L))) - val newIt: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() - it.foreach { case ((value, colIndex), count) => { - if (keysInThisPart.contains(colIndex) ) { + * Finds rank statistics elements using ranksLocations. + * + * @param sortedAggregatedValueColumnPairs - sorted RDD of (value, colIndex) pairs + * @param ranksLocations Array of (partition Index, list of (column index, rank index of this column at this partition)) + * + * @return returns RDD of the target ranks (column index, value) + */ + private def findTargetRanksIteratively(sortedAggregatedValueColumnPairs : RDD[((Double, Int), Long)], + ranksLocations : Array[(Int, List[(Int, Long)])]): RDD[(Int, Double)] = { + + sortedAggregatedValueColumnPairs.mapPartitionsWithIndex((partitionIndex : Int, + aggregatedValueColumnPairs : Iterator[((Double, Int), Long)]) => { + + val targetsInThisPart = ranksLocations(partitionIndex)._2 + if (!targetsInThisPart.isEmpty) { + val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) + val columnsInThisPart = targetsInThisPart.map(_._1).distinct + + val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() + runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap + + val result: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + + aggregatedValueColumnPairs.foreach { case ((value, colIndex), count) => { + if (columnsInThisPart contains colIndex) { val total = runningTotals(colIndex) - val ranksPresent = partMap(colIndex).filter(v => (v <= count + total) && (v > total)) - ranksPresent.foreach(r => { - newIt += ((colIndex, value)) - }) + + val ranksPresent = columnsRelativeIndex(colIndex) + .filter(index => (index <= count + total) && (index > total)) + ranksPresent.foreach(r => result += ((colIndex, value))) + runningTotals.update(colIndex, total + count) } }} - newIt.toIterator + + result.toIterator } else Iterator.empty - } ) + }) } /** @@ -148,18 +224,18 @@ object GoldiLocksWithHashMap { val n = colIndexList.last+1 val sorted = valPairs.sortByKey() - if(storageLevel!=StorageLevel.NONE){ + if (storageLevel != StorageLevel.NONE) sorted.persist(storageLevel) - } - if(checkPoint){ + + if (checkPoint) { sorted.sparkContext.setCheckpointDir(directory) sorted.checkpoint() } - val parts : Array[Partition] = sorted.partitions - val map1 = getTotalsForeachPart(sorted, parts.length, n) - val map2 = getLocationsOfRanksWithinEachPart(targetRanks, map1, n) - val result = findElementsIteratively(sorted, map2) - result.groupByKey().collectAsMap() + + val partitionColumnsFreq = getColumnsFreqPerPartition(sorted, n) + val ranksLocations = getRanksLocationsWithinEachPart(targetRanks, partitionColumnsFreq, n) + val targetRanksValues = findTargetRanksIteratively(sorted, ranksLocations) + targetRanksValues.groupByKey().collectAsMap() } } //end::hashMap[] \ No newline at end of file diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index c90c1c3..f67dc01 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -26,7 +26,7 @@ class QuantileOnlyArtisanalTest extends FunSuite with BeforeAndAfterAll { test("Goldilocks first try ") { val sqlContext = new SQLContext(sc) val input = sqlContext.createDataFrame(inputList) - val secondAndThird = GoldiLocksFirstTry.findQuantiles(input, targetRanks = List(2L, 3L)) + val secondAndThird = GoldiLocksFirstTry.findRankStatistics(input, targetRanks = List(2L, 3L)) val expectedResult = Map[Int, Set[Double]]( 0 -> Set(1.0, 2.0), 1 -> Set(5.5, 5.5), @@ -47,14 +47,14 @@ class QuantileOnlyArtisanalTest extends FunSuite with BeforeAndAfterAll { iter.map(x => (x._1, key)) } - val getColumnFreqPerPartition = PrivateMethod[ Array[(Int, Array[Long])]]('getColumnFreqPerPartition) - val totals = GoldiLocksFirstTry invokePrivate getColumnFreqPerPartition(mapPartitions, 2) + val getColumnsFreqPerPartition = PrivateMethod[ Array[(Int, Array[Long])]]('getColumnsFreqPerPartition) + val totals = GoldiLocksFirstTry invokePrivate getColumnsFreqPerPartition(mapPartitions, 2) totals.foreach(x => println(x._1 + " : " + x._2.mkString(" "))) - val getLocationsOfRanksWithinEachPart = - PrivateMethod[Array[(Int, List[(Int, Long)])]]('getLocationsOfRanksWithinEachPart) + val getRanksLocationsWithinEachPart = + PrivateMethod[Array[(Int, List[(Int, Long)])]]('getRanksLocationsWithinEachPart) - val locations = GoldiLocksFirstTry invokePrivate getLocationsOfRanksWithinEachPart(List(1L), totals, 2) + val locations = GoldiLocksFirstTry invokePrivate getRanksLocationsWithinEachPart(List(1L), totals, 2) locations.foreach(x => println(x._1 + " : " + x._2.mkString(" "))) //assert that there is nothing in the column with index 1 on the second partition @@ -77,7 +77,7 @@ class QuantileOnlyArtisanalTest extends FunSuite with BeforeAndAfterAll { test("GoldiLocks With Hashmap ") { val sqlContext = new SQLContext(sc) val input = sqlContext.createDataFrame(inputList) - val secondAndThird = GoldiLocksWithHashMap.findQuantiles(input, targetRanks = List(2L, 3L)) + val secondAndThird = GoldiLocksWithHashMap.findRankStatistics(input, targetRanks = List(2L, 3L)) val expectedResult = Map[Int, Set[Double]]( 0 -> Set(1.0, 2.0), 1 -> Set(5.5, 5.5), From 633f68ff58f0f42d2dfa2f164d0577e8d48bc06f Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 6 Jan 2016 18:25:33 -0800 Subject: [PATCH 066/198] update the tags in the join example so we can split them up in the sql chapter text --- .../dataframe/HappyPandas.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 400a5f4..bcc01f4 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -192,19 +192,23 @@ object HappyPanda { // Join DataFrames of Pandas and Sizes with def joins(df1: DataFrame, df2: DataFrame): Unit = { - // Inner join implicit //tag::innerJoin[] + // Inner join implicit df1.join(df2, df1("name") === df2("name")) - //end::innerJoin[] - //tag::joins[] // Inner join explicit df1.join(df2, df1("name") === df2("name"), "inner") + //end::innerJoin[] + //tag::leftouterJoin[] // Left outer join explicit df1.join(df2, df1("name") === df2("name"), "left_outer") + //end::leftouterJoin[] + //tag::rightouterJoin[] // Right outer join explicit df1.join(df2, df1("name") === df2("name"), "right_outer") + //end::rightouterJoin[] + //tag::leftsemiJoin[] // Left semi join explicit df1.join(df2, df1("name") === df2("name"), "leftsemi") - //end::joins[] + //end::leftsemiJoin[] } } From bd08e45f0e1da97c5871045c03aaedc8337caacd Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 10 Jan 2016 16:44:49 -0800 Subject: [PATCH 067/198] Add a shell script to launch a class with mysql jdbc connector (TODO: create the class and setup CI for it) --- resources/mysql-connector-java-5.1.38.jar | Bin 0 -> 983911 bytes shell-scripts/launch-with-mysql-jdbc | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 resources/mysql-connector-java-5.1.38.jar create mode 100644 shell-scripts/launch-with-mysql-jdbc diff --git a/resources/mysql-connector-java-5.1.38.jar b/resources/mysql-connector-java-5.1.38.jar new file mode 100644 index 0000000000000000000000000000000000000000..be09493c0f4a304a9782232537ee797c55daea66 GIT binary patch literal 983911 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 z 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

ej_(rq$Ry%W7(#XEie$SeKfYSty*{6QR^N% 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
0CrPT7N=RzxKC+0b+Nj}Xv#aH91%7?XB`4@aSA= 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�~7P= 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

ns>p#}ha#x^KL{$8o7ViVY<~>ogRAit-qE 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

K7fjbn6~ekSG|Wonq{3n) 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
  • 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 literal 0 HcmV?d00001 diff --git a/shell-scripts/launch-with-mysql-jdbc b/shell-scripts/launch-with-mysql-jdbc new file mode 100644 index 0000000..90ac352 --- /dev/null +++ b/shell-scripts/launch-with-mysql-jdbc @@ -0,0 +1,5 @@ +ASSEMBLY_JAR=./target/scala-2.10/examples_2.10.jar +CLASS="com.highperformancespark.dataframe.mysqlload" +#tag:[submit] +spark-submit --jars ./resources/mysql-connector-java-5.1.38.jar $ASSEMBLY_JAR $CLASS +#end:[submit] \ No newline at end of file From ca171e18651ff43ac829c5765e322de9d6854bcf Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 10 Jan 2016 16:45:01 -0800 Subject: [PATCH 068/198] Note that mysql connector is GPLd --- LICENSE | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LICENSE b/LICENSE index 8f71f43..80f405b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,6 @@ +Individual components under resources are available under their own licenses. + * MySQL connector is GPL +The source code in this repo is available under the Apache License Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ From 69f9eeec61175734b4e4bf2476e91b7f0054c7c3 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 10 Jan 2016 16:45:17 -0800 Subject: [PATCH 069/198] Update exclusions --- build.sbt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/build.sbt b/build.sbt index 2c3e438..a4b3bc5 100644 --- a/build.sbt +++ b/build.sbt @@ -56,3 +56,16 @@ resolvers ++= Seq( ) licenses := Seq("Apache License 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.html")) + +mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) => + { + case m if m.toLowerCase.endsWith("manifest.mf") => MergeStrategy.discard + case m if m.startsWith("META-INF") => MergeStrategy.discard + case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first + case PathList("org", "apache", xs @ _*) => MergeStrategy.first + case PathList("org", "jboss", xs @ _*) => MergeStrategy.first + case "about.html" => MergeStrategy.rename + case "reference.conf" => MergeStrategy.concat + case _ => MergeStrategy.first + } +} From 24dc5f413666e6ec11739f05245e60a40a341ce2 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 12 Jan 2016 11:17:25 -0800 Subject: [PATCH 070/198] Upgrade to 1.6.0 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 2c3e438..247de8c 100644 --- a/build.sbt +++ b/build.sbt @@ -12,7 +12,7 @@ crossScalaVersions := Seq("2.10.4", "2.11.6") javacOptions ++= Seq("-source", "1.7", "-target", "1.7") -sparkVersion := "1.5.1" +sparkVersion := "1.6.0" //tag::sparkComponents[] sparkComponents ++= Seq("core", "streaming", "sql", "hive", "hive-thriftserver", "mllib") From 04dec461209928a95c4187aaa0ae83baeaec0981 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 12 Jan 2016 13:23:42 -0800 Subject: [PATCH 071/198] Change generate scaling data a bit --- .../dataframe/HappyPandas.scala | 8 ++++++++ .../tools/GenerateScalingData.scala | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index bcc01f4..450d003 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -55,6 +55,14 @@ object HappyPanda { // Here will be some examples on PandaInfo DataFrame + /** + * @param id panda id + * @param zip zip code of panda residence + * @param happy if panda is happy + * @param attributes array of panada attributes + */ + case class RawPanda(id: Long, zip: String, happy: Boolean, attributes: Array[Double]) + /** * @param place name of place * @param pandaType type of pandas in this place diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index 8031c42..b88c63e 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -1,3 +1,7 @@ +package com.highperformancespark.examples.tools + +import com.highperformancespark.examples.dataframe.HappyPanda.PandaInfo + import org.apache.spark._ import org.apache.spark.rdd.RDD import org.apache.spark.mllib.random.RandomRDDs @@ -5,13 +9,15 @@ import org.apache.spark.mllib.random.RandomRDDs object GenerateScalingData { // tag::MAGIC_PANDA[] /** - * Generate a Goldilocks data set. We expect the key to follow an exponential - * distribution and the data its self to be normal. + * Generate a Goldilocks data set. We expect the zip code to follow an exponential + * distribution and the data its self to be normal */ - def generateGoldilocks(sc: SparkContext, size: Long): RDD[List[String]] = { - val keyRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt) - val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = 500) - keyRDD.zip(valuesRDD).map{case (k, v) => List(k.toString) ++ v.toArray.map(_.toString)} + def generateGoldilocks(sc: SparkContext, elements: Long, size: Long): RDD[List[String]] = { + val keyRDD = sc.parallelize(1L.to(size)) + val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt) + val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = size) + keyRDD.zip(valuesRDD, valuesRDD).map{case (k, z, v) => + RawPanda(k, z, v(0) > 0.5, v)} } // end::MAGIC_PANDA[] } From ca843d148f1244ac1ed8c2df99e92b05a15066b2 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 12 Jan 2016 13:23:57 -0800 Subject: [PATCH 072/198] Start adding a simple perf --- .../perf/SimplePerfTest.scala | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala new file mode 100644 index 0000000..7892783 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.highperformancespark.examples.perf + +import org.apache.spark.rdd._ +import org.apache.spark.{SparkContext, SparkConf} +import org.apache.spark.sql.{DataFrame, Dataset, Row} + +/** + * A simple performance test to compare a simple sort between DataFrame, Dataset, and RDD + */ +object SimplePerfTest { + def main(args: Array[Sttring]) = { + val sparkConf = SparkConf().setAppName("simple-perf-test") + val sc = SparkContext(sparkConf) + val scalingFactor = if (args.length > 0) args(0).toInt else 100 + val size = if (args.length > 1) args(1).toInt else 50 + val inputRDD = GenerateScalingData.generateGoldilocks(sc, scalingFactor, size) + } +} From 97b709ffecfd534383bb31bf276c2e73f0de359c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 12 Jan 2016 14:32:22 -0800 Subject: [PATCH 073/198] Add some more bits for a simple perf test --- .../perf/SimplePerfTest.scala | 45 ++++++++++++++++--- .../tools/GenerateScalingData.scala | 37 ++++++++++++--- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index 7892783..88f19d7 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -16,19 +16,54 @@ */ package com.highperformancespark.examples.perf +import com.highperformancespark.examples.dataframe.HappyPanda.RawPanda +import com.highperformancespark.examples.tools._ + import org.apache.spark.rdd._ import org.apache.spark.{SparkContext, SparkConf} import org.apache.spark.sql.{DataFrame, Dataset, Row} +import org.apache.spark.sql.hive.HiveContext /** - * A simple performance test to compare a simple sort between DataFrame, Dataset, and RDD + * A simple performance test to compare a simple sort between DataFrame, and RDD */ object SimplePerfTest { - def main(args: Array[Sttring]) = { - val sparkConf = SparkConf().setAppName("simple-perf-test") - val sc = SparkContext(sparkConf) - val scalingFactor = if (args.length > 0) args(0).toInt else 100 + def main(args: Array[String]) = { + val sparkConf = new SparkConf().setAppName("simple-perf-test") + val sc = new SparkContext(sparkConf) + val sqlCtx = new HiveContext(sc) + val scalingFactor = if (args.length > 0) args(0).toLong else 100L val size = if (args.length > 1) args(1).toInt else 50 + run(sc, sqlCtx, scalingFactor, size) + } + + def run(sc: SparkContext, sqlCtx: HiveContext, scalingFactor: Long, size: Int) = { + import sqlCtx.implicits._ val inputRDD = GenerateScalingData.generateGoldilocks(sc, scalingFactor, size) + inputRDD.cache() + inputRDD.count() + val rddTimeings = 1.to(10).map(x => time(testOnRDD(inputRDD))) + val inputDataFrame = inputRDD.toDF() + inputDataFrame.cache() + inputDataFrame.count() + val dataFrameTimeings = 1.to(10).map(x => time(testOnRDD(inputRDD))) + println(rddTimeings.mkString(",")) + println(dataFrameTimeings.mkString(",")) + } + + def testOnRDD(rdd: RDD[RawPanda]) = { + rdd.map(p => (p.id, p)).sortByKey().foreach{x => ()} + } + + def testOnDataFrame(df: DataFrame) = { + df.orderBy("id").rdd.foreach{x => ()} + } + + def time[R](block: => R): (R, Long) = { + val t0 = System.nanoTime() + val result = block // call-by-name + val t1 = System.nanoTime() + println(s"Time ${t1 - t0}ns") + (result, t1 - t0) } } diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index b88c63e..f284505 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -1,23 +1,48 @@ package com.highperformancespark.examples.tools -import com.highperformancespark.examples.dataframe.HappyPanda.PandaInfo +import com.highperformancespark.examples.dataframe.HappyPanda.RawPanda import org.apache.spark._ import org.apache.spark.rdd.RDD import org.apache.spark.mllib.random.RandomRDDs +import org.apache.spark.mllib.linalg.Vector object GenerateScalingData { - // tag::MAGIC_PANDA[] /** * Generate a Goldilocks data set. We expect the zip code to follow an exponential * distribution and the data its self to be normal */ - def generateGoldilocks(sc: SparkContext, elements: Long, size: Long): RDD[List[String]] = { + def generateFullGoldilocks(sc: SparkContext, elements: Long, size: Int): RDD[RawPanda] = { val keyRDD = sc.parallelize(1L.to(size)) - val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt) + val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt.toString) + val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = size) + keyRDD.zipPartitions(zipRDD, valuesRDD){ + (i1, i2, i3) => + new Iterator[(Long, String, Vector)] { + def hasNext: Boolean = (i1.hasNext, i2.hasNext, i3.hasNext) match { + case (true, true, true) => true + case (false, false, false) => false + case _ => throw new SparkException("Can only zip RDDs with " + + "same number of elements in each partition") + } + def next(): (Long, String, Vector) = (i1.next(), i2.next(), i3.next()) + } + }.map{case (k, z, v) => + RawPanda(k, z, v(0) > 0.5, v.toArray)} + } + + // tag::MAGIC_PANDA[] + /** + * Generate a Goldilocks data set all with the same id. + * We expect the zip code to follow an exponential + * distribution and the data its self to be normal. + * Simplified to avoid a 3-way zip. + */ + def generateGoldilocks(sc: SparkContext, elements: Long, size: Int): RDD[RawPanda] = { + val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt.toString) val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = size) - keyRDD.zip(valuesRDD, valuesRDD).map{case (k, z, v) => - RawPanda(k, z, v(0) > 0.5, v)} + zipRDD.zip(valuesRDD).map{case (z, v) => + RawPanda(1, z, v(0) > 0.5, v.toArray)} } // end::MAGIC_PANDA[] } From e9b6d7f385d4e21642347d958e3543037972a4bb Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 14 Jan 2016 08:40:09 -0800 Subject: [PATCH 074/198] Support making assembly --- build.sbt | 14 ++++++++++++++ .../tools/GenerateScalingData.scala | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 247de8c..d4435bb 100644 --- a/build.sbt +++ b/build.sbt @@ -56,3 +56,17 @@ resolvers ++= Seq( ) licenses := Seq("Apache License 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.html")) + +mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) => + { + case m if m.toLowerCase.endsWith("manifest.mf") => MergeStrategy.discard + case m if m.startsWith("META-INF") => MergeStrategy.discard + case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first + case PathList("org", "apache", xs @ _*) => MergeStrategy.first + case PathList("org", "jboss", xs @ _*) => MergeStrategy.first + case "log4j.properties" => MergeStrategy.discard + case "about.html" => MergeStrategy.rename + case "reference.conf" => MergeStrategy.concat + case _ => MergeStrategy.first + } +} diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index f284505..0557434 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -15,7 +15,7 @@ object GenerateScalingData { def generateFullGoldilocks(sc: SparkContext, elements: Long, size: Int): RDD[RawPanda] = { val keyRDD = sc.parallelize(1L.to(size)) val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt.toString) - val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = size) + val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = size, numCols = elements) keyRDD.zipPartitions(zipRDD, valuesRDD){ (i1, i2, i3) => new Iterator[(Long, String, Vector)] { From 9984ce940a96bee48fe6518187834437d1365a08 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 14 Jan 2016 09:08:36 -0800 Subject: [PATCH 075/198] Fix simple perf test and fix generating data to have same length of partition for keyRDD --- .../perf/SimplePerfTest.scala | 4 ++-- .../tools/GenerateScalingData.scala | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index 88f19d7..60f265e 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -47,8 +47,8 @@ object SimplePerfTest { inputDataFrame.cache() inputDataFrame.count() val dataFrameTimeings = 1.to(10).map(x => time(testOnRDD(inputRDD))) - println(rddTimeings.mkString(",")) - println(dataFrameTimeings.mkString(",")) + println(rddTimeings.map(_._2).mkString(",")) + println(dataFrameTimeings.map(_._2).mkString(",")) } def testOnRDD(rdd: RDD[RawPanda]) = { diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index 0557434..0b8ca1a 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -11,11 +11,13 @@ object GenerateScalingData { /** * Generate a Goldilocks data set. We expect the zip code to follow an exponential * distribution and the data its self to be normal + * @param rows number of rows in the RDD + * @param size number of value elements */ - def generateFullGoldilocks(sc: SparkContext, elements: Long, size: Int): RDD[RawPanda] = { - val keyRDD = sc.parallelize(1L.to(size)) - val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt.toString) - val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = size, numCols = elements) + def generateFullGoldilocks(sc: SparkContext, rows: Long, size: Int): RDD[RawPanda] = { + val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = rows).map(_.toInt.toString) + val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = rows, numCols = size) + val keyRDD = sc.parallelize(1L.to(rows), zipRDD.partitions.size) keyRDD.zipPartitions(zipRDD, valuesRDD){ (i1, i2, i3) => new Iterator[(Long, String, Vector)] { From 832ec576f7a4099310a887a032987647ac1784b5 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 14 Jan 2016 17:23:15 -0800 Subject: [PATCH 076/198] Fix up random number generator and add a simple perf test example --- .../perf/SimplePerfTest.scala | 6 +++--- .../tools/GenerateScalingData.scala | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index 60f265e..5b3bd69 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -39,7 +39,7 @@ object SimplePerfTest { def run(sc: SparkContext, sqlCtx: HiveContext, scalingFactor: Long, size: Int) = { import sqlCtx.implicits._ - val inputRDD = GenerateScalingData.generateGoldilocks(sc, scalingFactor, size) + val inputRDD = GenerateScalingData.generateFullGoldilocks(sc, scalingFactor, size) inputRDD.cache() inputRDD.count() val rddTimeings = 1.to(10).map(x => time(testOnRDD(inputRDD))) @@ -52,11 +52,11 @@ object SimplePerfTest { } def testOnRDD(rdd: RDD[RawPanda]) = { - rdd.map(p => (p.id, p)).sortByKey().foreach{x => ()} + rdd.map(p => (p.id, p)).sortByKey().distinct().count() } def testOnDataFrame(df: DataFrame) = { - df.orderBy("id").rdd.foreach{x => ()} + df.orderBy("id").distinct().count() } def time[R](block: => R): (R, Long) = { diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index 0b8ca1a..d310060 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -7,25 +7,25 @@ import org.apache.spark.rdd.RDD import org.apache.spark.mllib.random.RandomRDDs import org.apache.spark.mllib.linalg.Vector +// TODO: Add tests for this object GenerateScalingData { /** * Generate a Goldilocks data set. We expect the zip code to follow an exponential * distribution and the data its self to be normal - * @param rows number of rows in the RDD + * @param rows number of rows in the RDD (approximate) * @param size number of value elements */ - def generateFullGoldilocks(sc: SparkContext, rows: Long, size: Int): RDD[RawPanda] = { + def generateFullGoldilocks(sc: SparkContext, rows: Long, numCols: Int): RDD[RawPanda] = { val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = rows).map(_.toInt.toString) - val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = rows, numCols = size) - val keyRDD = sc.parallelize(1L.to(rows), zipRDD.partitions.size) + val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = rows, numCols = numCols).repartition(zipRDD.partitions.size) + val keyRDD = sc.parallelize(1L.to(rows), zipRDD.getNumPartitions) keyRDD.zipPartitions(zipRDD, valuesRDD){ (i1, i2, i3) => new Iterator[(Long, String, Vector)] { def hasNext: Boolean = (i1.hasNext, i2.hasNext, i3.hasNext) match { case (true, true, true) => true case (false, false, false) => false - case _ => throw new SparkException("Can only zip RDDs with " + - "same number of elements in each partition") + case _ => false // Note: this is unsafe (we throw away data when one of our partitions has run out). } def next(): (Long, String, Vector) = (i1.next(), i2.next(), i3.next()) } @@ -40,9 +40,9 @@ object GenerateScalingData { * distribution and the data its self to be normal. * Simplified to avoid a 3-way zip. */ - def generateGoldilocks(sc: SparkContext, elements: Long, size: Int): RDD[RawPanda] = { - val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = size).map(_.toInt.toString) - val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = 1, numCols = size) + def generateGoldilocks(sc: SparkContext, rows: Long, numCols: Int): RDD[RawPanda] = { + val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = rows).map(_.toInt.toString) + val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = rows, numCols = numCols) zipRDD.zip(valuesRDD).map{case (z, v) => RawPanda(1, z, v(0) > 0.5, v.toArray)} } From e272d36bead12eb9914c94090d92d63c8ce20efa Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Fri, 22 Jan 2016 23:05:38 -0800 Subject: [PATCH 077/198] minor refactor --- .../dataframe/HappyPandas.scala | 8 -------- .../dataframe/RawPandas.scala | 8 ++++++++ .../perf/SimplePerfTest.scala | 2 +- .../tools/GenerateScalingData.scala | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 450d003..bcc01f4 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -55,14 +55,6 @@ object HappyPanda { // Here will be some examples on PandaInfo DataFrame - /** - * @param id panda id - * @param zip zip code of panda residence - * @param happy if panda is happy - * @param attributes array of panada attributes - */ - case class RawPanda(id: Long, zip: String, happy: Boolean, attributes: Array[Double]) - /** * @param place name of place * @param pandaType type of pandas in this place diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala new file mode 100644 index 0000000..76d55e6 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala @@ -0,0 +1,8 @@ +package com.highperformancespark.examples.dataframe +/** + * @param id panda id + * @param zip zip code of panda residence + * @param happy if panda is happy + * @param attributes array of panada attributes + */ +case class RawPanda(id: Long, zip: String, happy: Boolean, attributes: Array[Double]) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index 5b3bd69..7587277 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -16,7 +16,7 @@ */ package com.highperformancespark.examples.perf -import com.highperformancespark.examples.dataframe.HappyPanda.RawPanda +import com.highperformancespark.examples.dataframe.RawPanda import com.highperformancespark.examples.tools._ import org.apache.spark.rdd._ diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index d310060..08feee5 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -1,6 +1,6 @@ package com.highperformancespark.examples.tools -import com.highperformancespark.examples.dataframe.HappyPanda.RawPanda +import com.highperformancespark.examples.dataframe.RawPanda import org.apache.spark._ import org.apache.spark.rdd.RDD From 106fed21ce5184d6d90ec54a1c5feb764cecbb7c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 14 Jan 2016 17:34:43 -0800 Subject: [PATCH 078/198] Start adding mixed dataset example --- .../dataframe/MixedDataset.scala | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala new file mode 100644 index 0000000..fd06fee --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -0,0 +1,58 @@ +/** + * A sample mixing relational & functional transformations with Datasets. + */ +package com.highperformancespark.examples.dataframe + +import org.apache.spark._ +import org.apache.spark.sql.{Dataset, SQLContext} +import org.apache.spark.sql.catalyst.expressions.aggregate._ +import org.apache.spark.sql.expressions._ +import org.apache.spark.sql.functions._ +import org.apache.spark.sql.types._ +// Additional imports for using HiveContext +import org.apache.spark.sql.hive._ +import org.apache.spark.sql.hive.thriftserver._ + +object MixedDataset { + /** + * A sample function on a Dataset of RawPandas. + * This is contrived, since our reduction could also be done with SQL aggregates, but + * we can see the flexibility of being able to specify arbitrary Scala code. + */ + def happyPandaSums() = { + } + + /** + * A sample function on a Dataset of RawPandas that round trips through a DataFrame for sorting. + * In the future sorting may be available directly on Datasets but this example illustrates how to + * convert from + */ + def topHappyPandasSums() = { + } + + /** + * Illustrate how we make typed queries, using some of the float properties to produce boolean + * values. + */ + def typedQueryExample() = { + } + + /** + * Illustrate converting a Dataset to an RDD + */ + def toRDD() = { + } + + /** + * Illustrate converting a Dataset to a DataFrame + */ + def toDF() = { + } + + /** + * Illustrate DataFrame to Dataset. Its important to note that if the schema does not match what + * is expected by the Dataset this fails fast. + */ + def fromDF() = { + } +} From 93152b176ffd55bcd1dd47708017b083a5215f97 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Fri, 22 Jan 2016 23:07:28 -0800 Subject: [PATCH 079/198] Fill in some stuff --- .../dataframe/MixedDataset.scala | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index fd06fee..cdae7c1 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -4,7 +4,8 @@ package com.highperformancespark.examples.dataframe import org.apache.spark._ -import org.apache.spark.sql.{Dataset, SQLContext} +import org.apache.spark.rdd.RDD +import org.apache.spark.sql._ import org.apache.spark.sql.catalyst.expressions.aggregate._ import org.apache.spark.sql.expressions._ import org.apache.spark.sql.functions._ @@ -13,46 +14,54 @@ import org.apache.spark.sql.types._ import org.apache.spark.sql.hive._ import org.apache.spark.sql.hive.thriftserver._ -object MixedDataset { +class MixedDataset(sqlCtx: SQLContext) { + import sqlCtx.implicits._ + /** * A sample function on a Dataset of RawPandas. * This is contrived, since our reduction could also be done with SQL aggregates, but * we can see the flexibility of being able to specify arbitrary Scala code. */ - def happyPandaSums() = { + def happyPandaSums(ds: Dataset[RawPanda]): Double = { + ds.toDF().filter($"happy" === true).as[RawPanda]. + select($"attributes"(0).as[Double]). + reduce((x, y) => x + y) } /** - * A sample function on a Dataset of RawPandas that round trips through a DataFrame for sorting. - * In the future sorting may be available directly on Datasets but this example illustrates how to - * convert from + * Functional map + Dataset, sums the positive attributes for the pandas */ - def topHappyPandasSums() = { + def funMap(ds: Dataset[RawPanda]): Dataset[Double] = { + ds.map{rp => rp.attributes.filter(_ > 0).sum} } /** * Illustrate how we make typed queries, using some of the float properties to produce boolean * values. */ - def typedQueryExample() = { + def typedQueryExample(ds: Dataset[RawPanda]): Dataset[Double] = { + ds.select($"attributes"(0).as[Double]) } /** * Illustrate converting a Dataset to an RDD */ - def toRDD() = { + def toRDD(ds: Dataset[RawPanda]): RDD[RawPanda] = { + ds.rdd } /** * Illustrate converting a Dataset to a DataFrame */ - def toDF() = { + def toDF(ds: Dataset[RawPanda]): DataFrame = { + ds.toDF() } /** * Illustrate DataFrame to Dataset. Its important to note that if the schema does not match what * is expected by the Dataset this fails fast. */ - def fromDF() = { + def fromDF(df: DataFrame): Dataset[RawPanda] = { + df.as[RawPanda] } } From dbc250766c602c4810c6d136e2c14ea623804fef Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Fri, 22 Jan 2016 23:23:48 -0800 Subject: [PATCH 080/198] Add tags --- .../dataframe/MixedDataset.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index cdae7c1..201818e 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -43,6 +43,7 @@ class MixedDataset(sqlCtx: SQLContext) { ds.select($"attributes"(0).as[Double]) } + //tag::toRDDDF[] /** * Illustrate converting a Dataset to an RDD */ @@ -56,12 +57,15 @@ class MixedDataset(sqlCtx: SQLContext) { def toDF(ds: Dataset[RawPanda]): DataFrame = { ds.toDF() } + //end::toRDDDF[] /** * Illustrate DataFrame to Dataset. Its important to note that if the schema does not match what * is expected by the Dataset this fails fast. */ + //tag::DataFrameAsDataset[] def fromDF(df: DataFrame): Dataset[RawPanda] = { df.as[RawPanda] } + //end::DataFrameAsDataset[] } From d08762490d5bcba055a7d5d01ca2826b5f4f7d2b Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 24 Jan 2016 21:25:05 -0800 Subject: [PATCH 081/198] Add an example of purely relational queries and one of a functional query --- .../dataframe/MixedDataset.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 201818e..0273171 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -28,12 +28,24 @@ class MixedDataset(sqlCtx: SQLContext) { reduce((x, y) => x + y) } + /** + * A sample function on a Dataset of RawPandas. + * Use the first attribute to deterimine if a panda is fuzzy + */ + //tag::basicSelect[] + def fuzzyPandas(ds: Dataset[RawPanda]): Dataset[(Long, Boolean)] = { + ds.select($"id".as[Long], ($"attributes"(0) > 0.5).as[Boolean]) + } + //end::basicSelect[] + /** * Functional map + Dataset, sums the positive attributes for the pandas */ + //tag::functionalQuery[] def funMap(ds: Dataset[RawPanda]): Dataset[Double] = { ds.map{rp => rp.attributes.filter(_ > 0).sum} } + //end::functionalQuery[] /** * Illustrate how we make typed queries, using some of the float properties to produce boolean From a02608efa5a7a26f845e2279e3d828d19f044549 Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 24 Jan 2016 23:52:00 -0800 Subject: [PATCH 082/198] Adding test of evaluation for chapter 4 --- .../GoldiLocks/EvaluationTests.scala | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala new file mode 100644 index 0000000..e4e003a --- /dev/null +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala @@ -0,0 +1,50 @@ +package com.highperformancespark.examples.goldilocks + +import org.apache.spark._ +import org.apache.spark.rdd.RDD +import org.scalatest.{BeforeAndAfterAll, FunSuite} + + +class EvaluationTests extends FunSuite with BeforeAndAfterAll { + @transient private var _sc: SparkContext = _ + def sc: SparkContext = _sc + + val conf = new SparkConf().setMaster("local[4]").setAppName("test") + + override def beforeAll() { + _sc = new SparkContext(conf) + super.beforeAll() + } + + test("MapValues preserves Partitioning "){ + val s = Array(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0).zipWithIndex + val data: RDD[(Double, Int )] = sc.parallelize(s) + + // tag::MapValues[] + val sortedData = data.sortByKey() + val mapValues: RDD[(Double, String)] = sortedData.mapValues(_.toString) + assert(mapValues.partitioner.isDefined, "Using Map Values preserves partitioning") + + val map = sortedData.map( pair => (pair._1, pair._2.toString)) + assert(!map.partitioner.isDefined, "Using map does not preserve partitioning") + // end::MapValues[] + } + + test( "Subtract Behavior "){ + val a = Array(1, 2, 3 ,4 ,4 ,4 ,4 ) + val b = Array(3, 4 ) + val rddA = sc.parallelize(a) + val rddB = sc.parallelize(b) + val rddC = rddA.subtract(rddB) + assert(rddC.count() < rddA.count() - rddB.count()) + } + + override def afterAll() { + // We clear the driver port so that we don't try and bind to the same port on restart + sc.stop() + System.clearProperty("spark.driver.port") + _sc = null + super.afterAll() + } +} + From 200e27d3c96844689f13472423851302db582fb0 Mon Sep 17 00:00:00 2001 From: Rachel Date: Mon, 25 Jan 2016 09:09:42 -0800 Subject: [PATCH 083/198] change evaluation tests to use shared spark context --- .../GoldiLocks/EvaluationTests.scala | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala index e4e003a..481007c 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala @@ -1,20 +1,10 @@ package com.highperformancespark.examples.goldilocks -import org.apache.spark._ +import com.holdenkarau.spark.testing.SharedSparkContext import org.apache.spark.rdd.RDD -import org.scalatest.{BeforeAndAfterAll, FunSuite} +import org.scalatest.FunSuite - -class EvaluationTests extends FunSuite with BeforeAndAfterAll { - @transient private var _sc: SparkContext = _ - def sc: SparkContext = _sc - - val conf = new SparkConf().setMaster("local[4]").setAppName("test") - - override def beforeAll() { - _sc = new SparkContext(conf) - super.beforeAll() - } +class EvaluationTests extends FunSuite with SharedSparkContext { test("MapValues preserves Partitioning "){ val s = Array(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0).zipWithIndex @@ -39,12 +29,5 @@ class EvaluationTests extends FunSuite with BeforeAndAfterAll { assert(rddC.count() < rddA.count() - rddB.count()) } - override def afterAll() { - // We clear the driver port so that we don't try and bind to the same port on restart - sc.stop() - System.clearProperty("spark.driver.port") - _sc = null - super.afterAll() - } } From f62d8f7faaa4a5e989de14f4f70dd0d4776bd568 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 25 Jan 2016 18:19:23 -0800 Subject: [PATCH 084/198] Add a basic union example --- .../dataframe/MixedDataset.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 0273171..8bad83b 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -38,6 +38,15 @@ class MixedDataset(sqlCtx: SQLContext) { } //end::basicSelect[] + /** + * Union happy and sad pandas + */ + //tag::basicUnion[] + def unionPandas(happyPandas: Dataset[RawPanda], sadPandas: Dataset[RawPanda]) = { + happyPandas.union(sadPandas) + } + //end::basicUnion[] + /** * Functional map + Dataset, sums the positive attributes for the pandas */ From cedc0cbee1e22fca12dec68055c320a9f91c9447 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 25 Jan 2016 20:27:29 -0800 Subject: [PATCH 085/198] Include spark-csv in the old fashion maven coordinates way --- build.sbt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.sbt b/build.sbt index d4435bb..5b488d3 100644 --- a/build.sbt +++ b/build.sbt @@ -18,6 +18,7 @@ sparkVersion := "1.6.0" sparkComponents ++= Seq("core", "streaming", "sql", "hive", "hive-thriftserver", "mllib") //end::sparkComponents[] + parallelExecution in Test := false fork := true @@ -29,6 +30,9 @@ libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "2.2.1", "org.scalacheck" %% "scalacheck" % "1.12.4", "junit" % "junit" % "4.10", + //tag::sparkCSV[] + "com.databricks" % "spark-csv_2.10" % "1.3.0", + //end::sparkCSV[] "com.holdenkarau" % "spark-testing-base_2.10" % "1.5.1_0.2.1", "org.eclipse.jetty" % "jetty-util" % "9.3.2.v20150730", "org.codehaus.jackson" % "jackson-mapper-asl" % "1.8.8", From 3f7c92c8b6beba06ab910f89fc3106888bf3ff55 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 25 Jan 2016 20:28:19 -0800 Subject: [PATCH 086/198] s/fuzzy/squishy/ (attribute 0 is squishy in the text) --- .../dataframe/MixedDataset.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 8bad83b..b7a3ca0 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -30,10 +30,10 @@ class MixedDataset(sqlCtx: SQLContext) { /** * A sample function on a Dataset of RawPandas. - * Use the first attribute to deterimine if a panda is fuzzy + * Use the first attribute to deterimine if a panda is squishy. */ //tag::basicSelect[] - def fuzzyPandas(ds: Dataset[RawPanda]): Dataset[(Long, Boolean)] = { + def squishyPandas(ds: Dataset[RawPanda]): Dataset[(Long, Boolean)] = { ds.select($"id".as[Long], ($"attributes"(0) > 0.5).as[Boolean]) } //end::basicSelect[] From 8ca4a756b7ffeed84d56f28c4de579a633825ba9 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 25 Jan 2016 23:48:42 -0800 Subject: [PATCH 087/198] break up the SQLHiveComponent --- build.sbt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 5b488d3..05c6f67 100644 --- a/build.sbt +++ b/build.sbt @@ -15,8 +15,11 @@ javacOptions ++= Seq("-source", "1.7", "-target", "1.7") sparkVersion := "1.6.0" //tag::sparkComponents[] -sparkComponents ++= Seq("core", "streaming", "sql", "hive", "hive-thriftserver", "mllib") +sparkComponents ++= Seq("core", "streaming", "hive-thriftserver", "mllib") //end::sparkComponents[] +//tag::addSQLHiveComponent[] +sparkComponents ++= Seq("sql", "hive") +//end::addSQLHiveComponent[] parallelExecution in Test := false From 2928c935841b2d328a424dbca9502bca7d7f7bae Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 25 Jan 2016 23:58:33 -0800 Subject: [PATCH 088/198] Use RawPanda accross places a bit more --- .../dataframe/LoadSave.scala | 17 +++++++---------- .../dataframe/RawPandas.scala | 6 ++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index ec7fdfa..7750be3 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -5,7 +5,6 @@ package com.highperformancespark.examples.dataframe import java.util.Properties -import com.highperformancespark.examples.dataframe.HappyPanda.PandaInfo import org.apache.spark._ import org.apache.spark.rdd._ import org.apache.spark.sql._ @@ -17,8 +16,6 @@ import org.apache.spark.sql.types._ import org.apache.spark.sql.hive._ import org.apache.spark.sql.hive.thriftserver._ -case class PandaPlace(name: String, pandas: Array[PandaInfo]) - case class LoadSave(sqlContext: SQLContext) { import sqlContext.implicits._ //tag::createFromRDD[] @@ -31,17 +28,17 @@ case class LoadSave(sqlContext: SQLContext) { // Create a Row RDD from our RDD of case classes val rowRDD = input.map(pm => Row(pm.name, - pm.pandas.map(pi => Row(pi.place, pi.pandaType, pi.happyPandas, pi.totalPandas)))) + pm.pandas.map(pi => Row(pi.id, pi.zip, pi.happy, pi.attributes)))) // Create DataFrame explicitly with specified schema val schema = StructType(List( StructField("name", StringType, true), StructField("pandas", ArrayType( StructType(List( - StructField("place", StringType, true), - StructField("pandaType", StringType, true), - StructField("happyPandas", IntegerType, true), - StructField("totalPandas", IntegerType, true))))))) + StructField("id", LongType, true), + StructField("zip", StringType, true), + StructField("happy", BooleanType, true), + StructField("attributes", ArrayType(FloatType), true))))))) val df3 = sqlContext.createDataFrame(rowRDD, schema) } //end::createFromRDD[] @@ -60,9 +57,9 @@ case class LoadSave(sqlContext: SQLContext) { //end::collectResults[] //tag::toRDD[] - def toRDD(input: DataFrame): RDD[PandaInfo] = { + def toRDD(input: DataFrame): RDD[RawPanda] = { val rdd: RDD[Row] = input.rdd - rdd.map(row => PandaInfo(row.getAs[String](0), row.getAs[String](1), row.getAs[Integer](2), row.getAs[Integer](3))) + rdd.map(row => RawPanda(row.getAs[Long](0), row.getAs[String](1), row.getAs[Boolean](2), row.getAs[Array[Double]](3))) } //end::toRDD[] diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala index 76d55e6..529f46c 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala @@ -6,3 +6,9 @@ package com.highperformancespark.examples.dataframe * @param attributes array of panada attributes */ case class RawPanda(id: Long, zip: String, happy: Boolean, attributes: Array[Double]) + +/** + * @param name place name + * @param pandas pandas in that place + */ +case class PandaPlace(name: String, pandas: Array[RawPanda]) From 9e68349da344bf805ff14ab53697111ec86f75ea Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 26 Jan 2016 00:02:07 -0800 Subject: [PATCH 089/198] add a raw panda json --- resources/rawpanda.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 resources/rawpanda.json diff --git a/resources/rawpanda.json b/resources/rawpanda.json new file mode 100644 index 0000000..854fb30 --- /dev/null +++ b/resources/rawpanda.json @@ -0,0 +1 @@ +{"name":"mission","pandas":[{"id":1,"zip":"94110","happy":true,"attributes":[0.4,0.5]}]} From 80ebcbb6c551790d8c564cca6304b6d8f2caad39 Mon Sep 17 00:00:00 2001 From: Rachel Date: Tue, 26 Jan 2016 15:04:51 -0800 Subject: [PATCH 090/198] adding caching example --- .../GoldiLocks/EvaluationTests.scala | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala index 481007c..c949a62 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala @@ -5,11 +5,12 @@ import org.apache.spark.rdd.RDD import org.scalatest.FunSuite class EvaluationTests extends FunSuite with SharedSparkContext { - + val doubleList = Array(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0) + val keyValuePairs = Array(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0).zipWithIndex + val path = "target/testResults" test("MapValues preserves Partitioning "){ - val s = Array(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0).zipWithIndex - val data: RDD[(Double, Int )] = sc.parallelize(s) + val data: RDD[(Double, Int )] = sc.parallelize(keyValuePairs) // tag::MapValues[] val sortedData = data.sortByKey() val mapValues: RDD[(Double, String)] = sortedData.mapValues(_.toString) @@ -29,5 +30,26 @@ class EvaluationTests extends FunSuite with SharedSparkContext { assert(rddC.count() < rddA.count() - rddB.count()) } + test( "Two actions without caching ") { + val rddA: RDD[(Double, Int)] = sc.parallelize(keyValuePairs) + + // tag::TwoActions[] + val sorted = rddA.sortByKey() + val count = sorted.count() + sorted.saveAsHadoopFile(path) + // end::TwoActions[] + } + + test( "Two actions with caching "){ + val rddA: RDD[(Double, Int)] = sc.parallelize(keyValuePairs) + // tag::TwoActionsCache[] + val sorted = rddA.sortByKey() + val count = sorted.count() + rddA.persist() + sorted.saveAsHadoopFile(path) + // end::TwoActionsCache[] + + } + } From 78618ec8ac67409f16b6bd0f4eb6391bdef2a237 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 26 Jan 2016 22:40:19 -0800 Subject: [PATCH 091/198] Update the schema a bit more (include the panda type in the schema so we can illustrate encoding) --- resources/rawpanda.json | 2 +- .../high-performance-spark-examples/dataframe/LoadSave.scala | 3 ++- .../high-performance-spark-examples/dataframe/RawPandas.scala | 3 ++- .../tools/GenerateScalingData.scala | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/resources/rawpanda.json b/resources/rawpanda.json index 854fb30..8e690c6 100644 --- a/resources/rawpanda.json +++ b/resources/rawpanda.json @@ -1 +1 @@ -{"name":"mission","pandas":[{"id":1,"zip":"94110","happy":true,"attributes":[0.4,0.5]}]} +{"name":"mission","pandas":[{"id":1,"zip":"94110","pt":"giant", "happy":true,"attributes":[0.4,0.5]}]} diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index 7750be3..22614e2 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -59,7 +59,8 @@ case class LoadSave(sqlContext: SQLContext) { //tag::toRDD[] def toRDD(input: DataFrame): RDD[RawPanda] = { val rdd: RDD[Row] = input.rdd - rdd.map(row => RawPanda(row.getAs[Long](0), row.getAs[String](1), row.getAs[Boolean](2), row.getAs[Array[Double]](3))) + rdd.map(row => RawPanda(row.getAs[Long](0), row.getAs[String](1), + row.getAs[String](2), row.getAs[Boolean](3), row.getAs[Array[Double]](4))) } //end::toRDD[] diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala index 529f46c..ff2ba69 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala @@ -2,10 +2,11 @@ package com.highperformancespark.examples.dataframe /** * @param id panda id * @param zip zip code of panda residence + * @param pt Type of panda as a string * @param happy if panda is happy * @param attributes array of panada attributes */ -case class RawPanda(id: Long, zip: String, happy: Boolean, attributes: Array[Double]) +case class RawPanda(id: Long, zip: String, pt: String, happy: Boolean, attributes: Array[Double]) /** * @param name place name diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index 08feee5..3ad9b5f 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -30,7 +30,7 @@ object GenerateScalingData { def next(): (Long, String, Vector) = (i1.next(), i2.next(), i3.next()) } }.map{case (k, z, v) => - RawPanda(k, z, v(0) > 0.5, v.toArray)} + RawPanda(k, z, "giant", v(0) > 0.5, v.toArray)} } // tag::MAGIC_PANDA[] @@ -44,7 +44,7 @@ object GenerateScalingData { val zipRDD = RandomRDDs.exponentialRDD(sc, mean = 1000, size = rows).map(_.toInt.toString) val valuesRDD = RandomRDDs.normalVectorRDD(sc, numRows = rows, numCols = numCols) zipRDD.zip(valuesRDD).map{case (z, v) => - RawPanda(1, z, v(0) > 0.5, v.toArray)} + RawPanda(1, z, "giant", v(0) > 0.5, v.toArray)} } // end::MAGIC_PANDA[] } From 75489d10d3cbdacdb377d9ee910f42793c9c51a9 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 26 Jan 2016 22:58:52 -0800 Subject: [PATCH 092/198] make nicer simple examples --- .../dataframe/HappyPandas.scala | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index bcc01f4..2790e1e 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -82,31 +82,46 @@ object HappyPanda { * @return Returns a DataFrame of pandaId and integer value for pandaType. */ def encodePandaType(pandaInfo: DataFrame): DataFrame = { - pandaInfo.select(pandaInfo("place"), - (when(pandaInfo("pandaType") === "giant", 0). - when(pandaInfo("pandaType") === "red", 1). + pandaInfo.select(pandaInfo("id"), + (when(pandaInfo("pt") === "giant", 0). + when(pandaInfo("pt") === "red", 1). otherwise(2)).as("encodedType") ) } //end::encodePandaType[] - //tag::simpleFilter[] /** * Gets places with happy pandas more than minHappinessBound. */ def minHappyPandas(pandaInfo: DataFrame, minHappyPandas: Int): DataFrame = { pandaInfo.filter(pandaInfo("happyPandas") >= minHappyPandas) } - //end::simpleFilter[] - //tag::complexFilter[] + /** + * Find pandas that are sad + */ + def sadPandas(pandaInfo: DataFrame): DataFrame = { + //tag::simpleFilter[] + pandaInfo.filter(!pandaInfo("happy")) + //end::simpleFilter[] + } + + /** + * Find pandas that are happy and fuzzier than squishy + */ + def happyFuzzyPandas(pandaInfo: DataFrame): DataFrame = { + //tag::complexFilter[] + pandaInfo.filter(pandaInfo("happy").and( + pandaInfo("attributes")(0) > pandaInfo("attributes")(1))) + //end::complexFilter[] + } /** * Gets places that contains happy pandas more than unhappy pandas. */ def happyPandasPlaces(pandaInfo: DataFrame): DataFrame = { pandaInfo.filter(pandaInfo("happyPandas") >= pandaInfo("totalPandas") / 2) } - //end::complexFilter[] + /** From 2ec56be6d67c412abe17e92be96783ff25110d1c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 28 Jan 2016 20:26:58 -0800 Subject: [PATCH 093/198] Update test now that the the encoder works on raw pandas --- .../dataframe/HappyPandas.scala | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index ea40bd8..27a0cda 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -16,9 +16,14 @@ class HappyPandasTest extends DataFrameSuiteBase { val toronto = "toronto" val sandiego = "san diego" val virginia = "virginia" - val pandaInfoList = List(PandaInfo(toronto, "giant", 1, 2), - PandaInfo(sandiego, "red", 2, 3), - PandaInfo(virginia, "black", 1, 10)) + val pandaInfoList = List( + PandaInfo(toronto, "giant", 1, 2), + PandaInfo(sandiego, "red", 2, 3), + PandaInfo(virginia, "black", 1, 10)) + + val rawPandaList = List( + RawPanda(10L, "94110", "giant", true, Array(1.0, 0.9)), + RawPanda(11L, "94110", "red", true, Array(1.0, 0.9))) val pandasList = List(Pandas("bata", "10010", 10, 2), Pandas("wiza", "10010", 20, 4), @@ -57,11 +62,11 @@ class HappyPandasTest extends DataFrameSuiteBase { } test("test encode Panda type") { - val inputDF = sqlContext.createDataFrame(pandaInfoList) + val inputDF = sqlContext.createDataFrame(rawPandaList) val resultDF = HappyPanda.encodePandaType(inputDF) - val expectedRows = List(Row(toronto, 0), Row(sandiego, 1), Row(virginia, 2)) - val expectedDF = createDF3(expectedRows, ("place", StringType, true), + val expectedRows = List(Row(10L, 0), Row(11L, 1)) + val expectedDF = createDF3(expectedRows, ("id", LongType, false), ("encodedType", IntegerType, false)) equalDataFrames(expectedDF, resultDF) @@ -263,4 +268,4 @@ class HappyPandasTest extends DataFrameSuiteBase { private def structType3(fields: Seq[(String, DataType, Boolean)]) = StructType(fields.map(f => (StructField(f._1, f._2, f._3))).toList) -} \ No newline at end of file +} From e939fb3765a7b1713f942dab93e37667c7f073ab Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 28 Jan 2016 21:12:09 -0800 Subject: [PATCH 094/198] drop duplicate example --- .../dataframe/HappyPandas.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 2790e1e..19f6a5b 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -123,6 +123,14 @@ object HappyPanda { } + /** + * Remove duplicate pandas by id + */ + def removeDuplicates(pandas: DataFrame): DataFrame = { + //tag::dropDuplicatePandaIds[] + pandas.dropDuplicates(List("id")) + //end::dropDuplicatePandaIds[] + } /** * @param name name of panda From 32dd5be9cbe57d6c84fcaa6a25b6bfe3484e27a0 Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 31 Jan 2016 11:31:44 -0800 Subject: [PATCH 095/198] formatting --- .../GoldiLocks/EvaluationTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala index c949a62..1f988ba 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala @@ -21,7 +21,7 @@ class EvaluationTests extends FunSuite with SharedSparkContext { // end::MapValues[] } - test( "Subtract Behavior "){ + test( "Subtract Behavior"){ val a = Array(1, 2, 3 ,4 ,4 ,4 ,4 ) val b = Array(3, 4 ) val rddA = sc.parallelize(a) From 7b59eb125814178d986d446911336a4800cd1c3b Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 31 Jan 2016 13:03:19 -0800 Subject: [PATCH 096/198] fix biuld --- .../GoldiLocks/EvaluationTests.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala index 1f988ba..700fcb5 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala @@ -21,7 +21,7 @@ class EvaluationTests extends FunSuite with SharedSparkContext { // end::MapValues[] } - test( "Subtract Behavior"){ + test( "Subtract Behavior "){ val a = Array(1, 2, 3 ,4 ,4 ,4 ,4 ) val b = Array(3, 4 ) val rddA = sc.parallelize(a) @@ -36,7 +36,8 @@ class EvaluationTests extends FunSuite with SharedSparkContext { // tag::TwoActions[] val sorted = rddA.sortByKey() val count = sorted.count() - sorted.saveAsHadoopFile(path) + val sample: Long = count / 10 + sorted.take(sample.toInt) // end::TwoActions[] } @@ -45,8 +46,9 @@ class EvaluationTests extends FunSuite with SharedSparkContext { // tag::TwoActionsCache[] val sorted = rddA.sortByKey() val count = sorted.count() + val sample: Long = count / 10 rddA.persist() - sorted.saveAsHadoopFile(path) + sorted.take(sample.toInt) // end::TwoActionsCache[] } From 11b172b5ad03c54e2f66ccc6095bf44163b2dd2d Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 31 Jan 2016 20:59:58 -0800 Subject: [PATCH 097/198] change simple perf test to compute the average --- .../high-performance-spark-examples/perf/SimplePerfTest.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index 7587277..f1c7274 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -52,11 +52,11 @@ object SimplePerfTest { } def testOnRDD(rdd: RDD[RawPanda]) = { - rdd.map(p => (p.id, p)).sortByKey().distinct().count() + rdd.map(p => (p.zip, (p.attributes(0), 1))).reduceByKey{case (x, y) => (x._1 + y._1, x._2 + y._2)}.collect() } def testOnDataFrame(df: DataFrame) = { - df.orderBy("id").distinct().count() + df.select(df("zip"), df("attributes")(0).as("fuzzyness")).groupBy("zip").avg("fuzzyness").collect() } def time[R](block: => R): (R, Long) = { From 6c155fb7ba382f18f9622e57e21722435df5a86c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 31 Jan 2016 20:50:59 -0800 Subject: [PATCH 098/198] Add a sample for chapter 4 to show saving setup overhead (Bad sample example) --- .../tools/SampleData.scala | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala b/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala index af442c5..5930d54 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala @@ -1,3 +1,6 @@ +import scala.util.Random +import scala.reflect.{classTag, ClassTag} + import org.apache.spark._ import org.apache.spark.rdd.RDD import org.apache.spark.mllib.random.RandomRDDs @@ -25,4 +28,30 @@ object SampleData { rdd.sampleByKey(withReplacement=false, fractions = stratas) // end::stratifiedSample[] } + + /** + * Custom random sample with RNG. This is intended as an example of how to save setup overhead. + */ + def slowSampleInput[T: ClassTag](rdd: RDD[T]): RDD[T] = { + rdd.flatMap{x => val r = new Random() + if (r.nextInt(10) == 0) { + Some(x) + } else { + None + }} + } + + /** + * Custom random sample with RNG. This is intended as an example of how to save setup overhead. + */ + def customSampleInput[T: ClassTag](rdd: RDD[T]): RDD[T] = { + rdd.mapPartitions{itr => val r = new Random() + itr.flatMap{x => + if (r.nextInt(10) == 0) { + Some(x) + } else { + None + }} + } + } } From 110567e260b4bd46842b8d63d5f424a7168b7cc7 Mon Sep 17 00:00:00 2001 From: Rachel Date: Sun, 31 Jan 2016 14:57:01 -0800 Subject: [PATCH 099/198] some more tests --- .../GoldiLocks/EvaluationTests.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala index 700fcb5..f1aa9ef 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala @@ -22,12 +22,27 @@ class EvaluationTests extends FunSuite with SharedSparkContext { } test( "Subtract Behavior "){ + // tag::Subtract[] val a = Array(1, 2, 3 ,4 ,4 ,4 ,4 ) val b = Array(3, 4 ) val rddA = sc.parallelize(a) val rddB = sc.parallelize(b) val rddC = rddA.subtract(rddB) assert(rddC.count() < rddA.count() - rddB.count()) + // tag::Subtract[] + } + + test( "Intersection Behavior "){ + // tag::Intersect[] + val a = Array(1, 2, 3 ,4 ,4 ,4 ,4 ) + val b = Array(3, 4 ) + val rddA = sc.parallelize(a) + val rddB = sc.parallelize(b) + val intersection = rddA.intersection(rddB) + val subtraction = rddA.subtract(rddB) + val union = intersection.union(subtraction) + assert(!rddA.collect().sorted.sameElements(union.collect().sorted)) + // tag::Intersect[] } test( "Two actions without caching ") { From c6083c525a07486bfc9ffaa337f69c61e0e19453 Mon Sep 17 00:00:00 2001 From: Rachel Date: Mon, 1 Feb 2016 17:18:51 -0800 Subject: [PATCH 100/198] with tags this time --- .../GoldiLocks/EvaluationTests.scala | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala index f1aa9ef..c635184 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/EvaluationTests.scala @@ -29,7 +29,7 @@ class EvaluationTests extends FunSuite with SharedSparkContext { val rddB = sc.parallelize(b) val rddC = rddA.subtract(rddB) assert(rddC.count() < rddA.count() - rddB.count()) - // tag::Subtract[] + // end::Subtract[] } test( "Intersection Behavior "){ @@ -42,7 +42,28 @@ class EvaluationTests extends FunSuite with SharedSparkContext { val subtraction = rddA.subtract(rddB) val union = intersection.union(subtraction) assert(!rddA.collect().sorted.sameElements(union.collect().sorted)) - // tag::Intersect[] + // end::Intersect[] + } + + test("Itereative Computations "){ + def RMSE(rdd : RDD[(Int, Int )]) = { + val n = rdd.count() + math.sqrt(rdd.map(x => (x._1 - x._2) * (x._1 - x._2)).reduce(_ + _) / n) + } + + val validationSet = sc.parallelize(keyValuePairs) + + // tag::iterativeComp[] + val testSet: Array[RDD[(Double, Int)]] = Array(validationSet.mapValues(_ + 1), validationSet.mapValues(_ + 2), validationSet) + validationSet.persist() //persist since we are using this RDD several times + val errors = testSet.map( rdd => { + RMSE(rdd.join(validationSet).values) + }) + // end::iterativeComp[] + + //the one where we didn't change anything should have the lowest root mean squared error + assert(errors.min == errors(2)) + } test( "Two actions without caching ") { @@ -65,8 +86,9 @@ class EvaluationTests extends FunSuite with SharedSparkContext { rddA.persist() sorted.take(sample.toInt) // end::TwoActionsCache[] - } + + } From b5325b4a8eea402facee8c0a815dde7244ec2eca Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 1 Feb 2016 21:36:11 -0800 Subject: [PATCH 101/198] benchmarking works better when you compare the results :p --- .../high-performance-spark-examples/perf/SimplePerfTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index f1c7274..6368a16 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -46,7 +46,7 @@ object SimplePerfTest { val inputDataFrame = inputRDD.toDF() inputDataFrame.cache() inputDataFrame.count() - val dataFrameTimeings = 1.to(10).map(x => time(testOnRDD(inputRDD))) + val dataFrameTimeings = 1.to(10).map(x => time(testOnDataFrame(inputDataFrame))) println(rddTimeings.map(_._2).mkString(",")) println(dataFrameTimeings.map(_._2).mkString(",")) } From e931747f0ec3b11ce0e9ffcc7af6fff1ac2dfb0f Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 1 Feb 2016 22:30:09 -0800 Subject: [PATCH 102/198] Add grouped timings --- .../perf/SimplePerfTest.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index 6368a16..ae3144e 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -43,18 +43,26 @@ object SimplePerfTest { inputRDD.cache() inputRDD.count() val rddTimeings = 1.to(10).map(x => time(testOnRDD(inputRDD))) + val groupTimeings = 1.to(10).map(x => time(groupOnRDD(inputRDD))) val inputDataFrame = inputRDD.toDF() inputDataFrame.cache() inputDataFrame.count() val dataFrameTimeings = 1.to(10).map(x => time(testOnDataFrame(inputDataFrame))) println(rddTimeings.map(_._2).mkString(",")) + println(groupTimeings.map(_._2).mkString(",")) println(dataFrameTimeings.map(_._2).mkString(",")) } - def testOnRDD(rdd: RDD[RawPanda]) = { + def groupOnRDD(rdd: RDD[RawPanda]) = { rdd.map(p => (p.zip, (p.attributes(0), 1))).reduceByKey{case (x, y) => (x._1 + y._1, x._2 + y._2)}.collect() } + def testOnRDD(rdd: RDD[RawPanda]) = { + rdd.map(p => (p.zip, p.attributes(0))).groupByKey().mapValues{v => + v.aggregate((0.0, 0))({case (x, y) => (x._1 + y, x._2 + 1)}, + {case (x, y) => (x._1 + y._1, x._2 + y._2)})} + } + def testOnDataFrame(df: DataFrame) = { df.select(df("zip"), df("attributes")(0).as("fuzzyness")).groupBy("zip").avg("fuzzyness").collect() } From ee789a2bd73c46d0fee63fa2445584c7f78bbd99 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 1 Feb 2016 22:53:34 -0800 Subject: [PATCH 103/198] do pair of rdds --- .../perf/SimplePerfTest.scala | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index ae3144e..af190f3 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -23,6 +23,7 @@ import org.apache.spark.rdd._ import org.apache.spark.{SparkContext, SparkConf} import org.apache.spark.sql.{DataFrame, Dataset, Row} import org.apache.spark.sql.hive.HiveContext +import org.apache.spark.sql.types._ /** * A simple performance test to compare a simple sort between DataFrame, and RDD @@ -40,11 +41,13 @@ object SimplePerfTest { def run(sc: SparkContext, sqlCtx: HiveContext, scalingFactor: Long, size: Int) = { import sqlCtx.implicits._ val inputRDD = GenerateScalingData.generateFullGoldilocks(sc, scalingFactor, size) - inputRDD.cache() - inputRDD.count() - val rddTimeings = 1.to(10).map(x => time(testOnRDD(inputRDD))) - val groupTimeings = 1.to(10).map(x => time(groupOnRDD(inputRDD))) - val inputDataFrame = inputRDD.toDF() + val pairRDD = inputRDD.map(p => (p.zip.toInt, p.attributes(0))) + pairRDD.cache() + pairRDD.count() + val rddTimeings = 1.to(10).map(x => time(testOnRDD(pairRDD))) + val groupTimeings = 1.to(10).map(x => time(groupOnRDD(pairRDD))) + val df = inputRDD.toDF() + val inputDataFrame = df.select(df("zip").cast(IntegerType), df("attributes")(0).as("fuzzyness")) inputDataFrame.cache() inputDataFrame.count() val dataFrameTimeings = 1.to(10).map(x => time(testOnDataFrame(inputDataFrame))) @@ -53,18 +56,18 @@ object SimplePerfTest { println(dataFrameTimeings.map(_._2).mkString(",")) } - def groupOnRDD(rdd: RDD[RawPanda]) = { - rdd.map(p => (p.zip, (p.attributes(0), 1))).reduceByKey{case (x, y) => (x._1 + y._1, x._2 + y._2)}.collect() + def testOnRDD(rdd: RDD[(Int, Double)]) = { + rdd.map{case (x, y) => (x, (y, 1))}.reduceByKey{case (x, y) => (x._1 + y._1, x._2 + y._2)}.collect() } - def testOnRDD(rdd: RDD[RawPanda]) = { - rdd.map(p => (p.zip, p.attributes(0))).groupByKey().mapValues{v => + def groupOnRDD(rdd: RDD[(Int, Double)]) = { + rdd.groupByKey().mapValues{v => v.aggregate((0.0, 0))({case (x, y) => (x._1 + y, x._2 + 1)}, {case (x, y) => (x._1 + y._1, x._2 + y._2)})} } def testOnDataFrame(df: DataFrame) = { - df.select(df("zip"), df("attributes")(0).as("fuzzyness")).groupBy("zip").avg("fuzzyness").collect() + df.groupBy("zip").avg("fuzzyness").collect() } def time[R](block: => R): (R, Long) = { From 8afc12cf8a45c1beaa4a388531ca9be62c958040 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 1 Feb 2016 22:58:06 -0800 Subject: [PATCH 104/198] switch to count --- .../perf/SimplePerfTest.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index af190f3..6f946ab 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -57,17 +57,17 @@ object SimplePerfTest { } def testOnRDD(rdd: RDD[(Int, Double)]) = { - rdd.map{case (x, y) => (x, (y, 1))}.reduceByKey{case (x, y) => (x._1 + y._1, x._2 + y._2)}.collect() + rdd.map{case (x, y) => (x, (y, 1))}.reduceByKey{case (x, y) => (x._1 + y._1, x._2 + y._2)}.count() } def groupOnRDD(rdd: RDD[(Int, Double)]) = { rdd.groupByKey().mapValues{v => v.aggregate((0.0, 0))({case (x, y) => (x._1 + y, x._2 + 1)}, - {case (x, y) => (x._1 + y._1, x._2 + y._2)})} + {case (x, y) => (x._1 + y._1, x._2 + y._2)})}.count() } def testOnDataFrame(df: DataFrame) = { - df.groupBy("zip").avg("fuzzyness").collect() + df.groupBy("zip").avg("fuzzyness").count() } def time[R](block: => R): (R, Long) = { From a2f6a2391523dc4eabdebac13351421ad8c34e1e Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Mon, 1 Feb 2016 22:37:39 +0200 Subject: [PATCH 105/198] Resolve code inspection issues --- .../GoldiLocks/GoldiLocksFirstTry.scala | 10 ++--- .../GoldiLocks/GoldiLocksWithHashMap.scala | 6 +-- .../GoldiLocks/SecondarySort.scala | 14 +++---- .../dataframe/HappyPandas.scala | 11 +++-- .../dataframe/LoadSave.scala | 17 +++----- .../dataframe/RegularSQL.scala | 10 ----- .../dataframe/UDFs.scala | 6 --- .../tools/SampleData.scala | 4 +- ...appyPandas.scala => HappyPandasTest.scala} | 40 +++++++++---------- 9 files changed, 45 insertions(+), 73 deletions(-) rename src/test/scala/com/high-performance-spark-examples/dataframe/{HappyPandas.scala => HappyPandasTest.scala} (87%) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala index b2a4985..b60b94f 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala @@ -1,14 +1,12 @@ package com.highperformancespark.examples.goldilocks +import scala.collection.{Map, mutable} +import scala.collection.mutable.{ArrayBuffer, MutableList} + import org.apache.spark.rdd.RDD import org.apache.spark.sql.DataFrame import org.apache.spark.storage.StorageLevel -import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer -import scala.collection.Map -import scala.collection.mutable.MutableList - object GoldiLocksGroupByKey { //tag::groupByKey[] def findRankStatistics( @@ -182,7 +180,7 @@ object GoldiLocksFirstTry { sortedValueColumnPairs.mapPartitionsWithIndex((partitionIndex : Int, valueColumnPairs : Iterator[(Double, Int)]) => { val targetsInThisPart: List[(Int, Long)] = ranksLocations(partitionIndex)._2 - if (!targetsInThisPart.isEmpty) { + if (targetsInThisPart.nonEmpty) { val columnsRelativeIndex: Map[Int, List[Long]] = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) val columnsInThisPart = targetsInThisPart.map(_._1).distinct diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala index 3dc2145..6d0c46b 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala @@ -73,11 +73,11 @@ object GoldiLocksWithHashMap { val aggregatedValueColumnRDD = dataFrame.rdd.mapPartitions(rows => { val valueColumnMap = new mutable.HashMap[(Double, Int), Long]() rows.foreach(row => { - row.toSeq.zipWithIndex.foreach{ case (value, columnIndex) => { + row.toSeq.zipWithIndex.foreach{ case (value, columnIndex) => val key = (value.toString.toDouble, columnIndex) val count = valueColumnMap.getOrElseUpdate(key, 0) valueColumnMap.update(key, count + 1) - }} + } }) valueColumnMap.toIterator @@ -179,7 +179,7 @@ object GoldiLocksWithHashMap { aggregatedValueColumnPairs : Iterator[((Double, Int), Long)]) => { val targetsInThisPart = ranksLocations(partitionIndex)._2 - if (!targetsInThisPart.isEmpty) { + if (targetsInThisPart.nonEmpty) { val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) val columnsInThisPart = targetsInThisPart.map(_._1).distinct diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala index bd4e134..d7d92a0 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/SecondarySort.scala @@ -1,11 +1,11 @@ package com.highperformancespark.examples.goldilocks -import org.apache.spark.{Partitioner, HashPartitioner} -import org.apache.spark.rdd.RDD - import scala.collection.mutable.ArrayBuffer import scala.reflect.ClassTag +import org.apache.spark.{HashPartitioner, Partitioner} +import org.apache.spark.rdd.RDD + object SecondarySort { //tag::sortByTwoKeys[] @@ -31,11 +31,11 @@ object SecondarySort { it: Iterator[((K, S), V)]): Iterator[(K, List[(S, V)])] = { val res = List[(K, ArrayBuffer[(S, V)])]() it.foldLeft(res)((list, next) => list match { - case Nil => { + case Nil => val ((firstKey, secondKey), value) = next List((firstKey, ArrayBuffer((secondKey, value)))) - } - case head :: rest => { + + case head :: rest => val (curKey, valueBuf) = head val ((firstKey, secondKey), value) = next if (!firstKey.equals(curKey) ) { @@ -44,7 +44,7 @@ object SecondarySort { valueBuf.append((secondKey, value)) list } - } + }).map { case (key, buf) => (key, buf.toList) }.iterator } //end::sortAndGroup[] diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 19f6a5b..3f0a22f 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -5,19 +5,18 @@ package com.highperformancespark.examples.dataframe import org.apache.spark._ //tag::sparkSQLImports[] -import org.apache.spark.sql.{DataFrame, SQLContext, Row} -import org.apache.spark.sql.catalyst.expressions.aggregate._ +import org.apache.spark.sql.{DataFrame, SQLContext} import org.apache.spark.sql.expressions._ import org.apache.spark.sql.functions._ -import org.apache.spark.sql.types._ //end::sparkSQLImports[] + //tag::sparkHiveImports[] // Additional imports for using HiveContext import org.apache.spark.sql.hive._ import org.apache.spark.sql.hive.thriftserver._ //end::sparkHiveImports[] -object HappyPanda { +object HappyPandas { // create SQLContext with an existing SparkContext def sqlContext(sc: SparkContext): SQLContext = { //tag::createSQLContext[] @@ -205,8 +204,8 @@ object HappyPanda { //end::relativePandaSizesWindow[] //tag::relativePandaSizesQuery[] - val pandaRelativeSizeFunc = (pandas("pandaSize") - - avg(pandas("pandaSize")).over(windowSpec)) + val pandaRelativeSizeFunc = pandas("pandaSize") - + avg(pandas("pandaSize")).over(windowSpec) pandas.select(pandas("name"), pandas("zip"), pandas("pandaSize"), pandas("age"), pandaRelativeSizeFunc.as("panda_relative_size")) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index 22614e2..795100f 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -5,16 +5,9 @@ package com.highperformancespark.examples.dataframe import java.util.Properties -import org.apache.spark._ import org.apache.spark.rdd._ import org.apache.spark.sql._ -import org.apache.spark.sql.catalyst.expressions.aggregate._ -import org.apache.spark.sql.DataFrame -import org.apache.spark.sql.expressions._ -import org.apache.spark.sql.functions._ import org.apache.spark.sql.types._ -import org.apache.spark.sql.hive._ -import org.apache.spark.sql.hive.thriftserver._ case class LoadSave(sqlContext: SQLContext) { import sqlContext.implicits._ @@ -32,13 +25,13 @@ case class LoadSave(sqlContext: SQLContext) { // Create DataFrame explicitly with specified schema val schema = StructType(List( - StructField("name", StringType, true), + StructField("name", StringType, nullable = true), StructField("pandas", ArrayType( StructType(List( - StructField("id", LongType, true), - StructField("zip", StringType, true), - StructField("happy", BooleanType, true), - StructField("attributes", ArrayType(FloatType), true))))))) + StructField("place", StringType, nullable = true), + StructField("pandaType", StringType, nullable = true), + StructField("happyPandas", IntegerType, nullable = true), + StructField("totalPandas", IntegerType, nullable = true))))))) val df3 = sqlContext.createDataFrame(rowRDD, schema) } //end::createFromRDD[] diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala index 6653cb0..aaa27fd 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala @@ -3,19 +3,9 @@ */ package com.highperformancespark.examples.dataframe -import org.apache.spark._ -import org.apache.spark.rdd._ import org.apache.spark.sql._ -import org.apache.spark.sql.catalyst.expressions.aggregate._ -import org.apache.spark.sql.DataFrame -import org.apache.spark.sql.expressions._ -import org.apache.spark.sql.functions._ -import org.apache.spark.sql.types._ -import org.apache.spark.sql.hive._ -import org.apache.spark.sql.hive.thriftserver._ case class RegularSQL(sqlContext: SQLContext) { - import sqlContext.implicits._ //tag::queryTable[] def querySQL(): DataFrame = { diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala index 094e69f..2274781 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala @@ -3,14 +3,8 @@ */ package com.highperformancespark.examples.dataframe -import org.apache.spark._ import org.apache.spark.sql._ -import org.apache.spark.sql.catalyst.expressions.aggregate._ -import org.apache.spark.sql.DataFrame import org.apache.spark.sql.expressions._ -import org.apache.spark.sql.functions._ -import org.apache.spark.sql.hive._ -import org.apache.spark.sql.hive.thriftserver._ import org.apache.spark.sql.types._ object UDFs { diff --git a/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala b/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala index 5930d54..48444dd 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala @@ -1,9 +1,7 @@ import scala.util.Random -import scala.reflect.{classTag, ClassTag} +import scala.reflect.{ClassTag} -import org.apache.spark._ import org.apache.spark.rdd.RDD -import org.apache.spark.mllib.random.RandomRDDs /** * Sample our production data to be able to use it for tests diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala similarity index 87% rename from src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala rename to src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala index 27a0cda..3b9651a 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala @@ -3,7 +3,7 @@ */ package com.highperformancespark.examples.dataframe -import com.highperformancespark.examples.dataframe.HappyPanda.{PandaInfo, Pandas} +import com.highperformancespark.examples.dataframe.HappyPandas.{PandaInfo, Pandas} import com.holdenkarau.spark.testing._ import org.apache.spark.sql.types._ import org.apache.spark.sql.{DataFrame, Row, SQLContext} @@ -39,7 +39,7 @@ class HappyPandasTest extends DataFrameSuiteBase { ("percentHappy", DoubleType)) val inputDF = sqlContext.createDataFrame(pandaInfoList) - val result = HappyPanda.happyPandasPercentage(inputDF) + val result = HappyPandas.happyPandasPercentage(inputDF) approxEqualDataFrames(expectedDf, result, 1E-5) } @@ -47,13 +47,13 @@ class HappyPandasTest extends DataFrameSuiteBase { test("verify approx by hand") { val inputDF = sqlContext.createDataFrame(pandaInfoList) - val resultDF = HappyPanda.happyPandasPercentage(inputDF) + val resultDF = HappyPandas.happyPandasPercentage(inputDF) val resultRows = resultDF.collect() val expectedRows = List(Row(toronto, 0.5), Row(sandiego, 2/3.0), Row(virginia, 1/10.0)) //tag::approxEqualRow[] - assert(expectedRows.size === resultRows.size) + assert(expectedRows.length === resultRows.length) expectedRows.zip(resultRows).foreach{case (r1, r2) => assert(r1(0) === r2(0)) assert(r1.getDouble(1) === (r2.getDouble(1) +- 0.001)) @@ -63,7 +63,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test encode Panda type") { val inputDF = sqlContext.createDataFrame(rawPandaList) - val resultDF = HappyPanda.encodePandaType(inputDF) + val resultDF = HappyPandas.encodePandaType(inputDF) val expectedRows = List(Row(10L, 0), Row(11L, 1)) val expectedDF = createDF3(expectedRows, ("id", LongType, false), @@ -76,7 +76,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("verify exact equality") { // test minHappyPandas val inputDF = sqlContext.createDataFrame(pandaInfoList) - val result = HappyPanda.minHappyPandas(inputDF, 2) + val result = HappyPandas.minHappyPandas(inputDF, 2) val resultRows = result.collect() val expectedRows = List(Row(sandiego, "red", 2, 3)) @@ -86,7 +86,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test happyPandasPlaces") { val inputDF = sqlContext.createDataFrame(pandaInfoList) - val resultDF = HappyPanda.happyPandasPlaces(inputDF) + val resultDF = HappyPandas.happyPandasPlaces(inputDF) val expectedRows = List(PandaInfo(toronto, "giant", 1, 2), PandaInfo(sandiego, "red", 2, 3)) @@ -97,7 +97,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test maxPandaSizePerZip") { val inputDF = sqlContext.createDataFrame(pandasList) - val resultDF = HappyPanda.maxPandaSizePerZip(inputDF) + val resultDF = HappyPandas.maxPandaSizePerZip(inputDF) val expectedRows = List(Row(pandasList(1).zip, pandasList(1).pandaSize), Row(pandasList(3).zip, pandasList(3).pandaSize), @@ -110,7 +110,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test minMaxPandaSizePerZip"){ val inputDF = sqlContext.createDataFrame(pandasList) - val resultDF = HappyPanda.minMaxPandaSizePerZip(inputDF) + val resultDF = HappyPandas.minMaxPandaSizePerZip(inputDF) val expectedRows = List( Row(pandasList(1).zip, pandasList(0).pandaSize, pandasList(1).pandaSize), @@ -126,7 +126,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test minPandaSizeMaxAgePerZip") { val inputDF = sqlContext.createDataFrame(pandasList) - val resultDF = HappyPanda.minPandaSizeMaxAgePerZip(inputDF) + val resultDF = HappyPandas.minPandaSizeMaxAgePerZip(inputDF) val expectedRows = List( Row(pandasList(1).zip, pandasList(0).pandaSize, pandasList(1).age), @@ -142,7 +142,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test complexAggPerZip") { val inputDF = sqlContext.createDataFrame(pandasList) - val resultDF = HappyPanda.minMeanSizePerZip(inputDF) + val resultDF = HappyPandas.minMeanSizePerZip(inputDF) val expectedRows = List( Row(pandasList(1).zip, pandasList(0).pandaSize, 15.0), @@ -159,7 +159,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test Simple SQL example") { val inputDF = sqlContext.createDataFrame(pandasList) - val resultDF = HappyPanda.simpleSqlExample(inputDF) + val resultDF = HappyPandas.simpleSqlExample(inputDF) val expectedRows = List(pandasList(0), pandasList(2)) val expectedDF = sqlContext.createDataFrame(expectedRows) @@ -169,7 +169,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("test Order Pandas") { val inputDF = sqlContext.createDataFrame(pandasList) - val resultDF = HappyPanda.orderPandas(inputDF) + val resultDF = HappyPandas.orderPandas(inputDF) val expectedRows = List(pandasList(2), pandasList(0), pandasList(3), pandasList(4), pandasList(1)) @@ -183,7 +183,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val inputPandaList = loadPandaStuffies() val inputDF = sqlContext.createDataFrame(inputPandaList) - val resultDF = HappyPanda.computeRelativePandaSizes(inputDF) + val resultDF = HappyPandas.computeRelativePandaSizes(inputDF) val expectedDF = getExpectedPandasRelativeSize(inputPandaList, -10, 10) @@ -206,10 +206,10 @@ class HappyPandasTest extends DataFrameSuiteBase { val startOffset = math.max(0, i + start) val endOffset = math.min(length, i + end) - for (j <- (startOffset to endOffset)) + for (j <- startOffset to endOffset) totalSum += pandas(j).pandaSize - val count = (endOffset - startOffset + 1) + val count = endOffset - startOffset + 1 val average = totalSum.toDouble / count val panda = pandas(i) @@ -256,16 +256,16 @@ class HappyPandasTest extends DataFrameSuiteBase { } - private def createDF(list: List[Row], fields: Tuple2[String, DataType]*) = + private def createDF(list: List[Row], fields: (String, DataType)*) = sqlContext.createDataFrame(sc.parallelize(list), structType2(fields)) private def structType2(fields: Seq[(String, DataType)]) = - StructType(fields.map(f => (StructField(f._1, f._2))).toList) + StructType(fields.map(f => StructField(f._1, f._2)).toList) - private def createDF3(list: List[Row], fields: Tuple3[String, DataType, Boolean]*) = + private def createDF3(list: List[Row], fields: (String, DataType, Boolean)*) = sqlContext.createDataFrame(sc.parallelize(list), structType3(fields)) private def structType3(fields: Seq[(String, DataType, Boolean)]) = - StructType(fields.map(f => (StructField(f._1, f._2, f._3))).toList) + StructType(fields.map(f => StructField(f._1, f._2, f._3)).toList) } From 2ef9beadd3ab9e072c18574efb25a1cefb887e90 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 3 Feb 2016 00:15:21 -0800 Subject: [PATCH 106/198] Add accumulator example --- .../transformations/Accumulators.scala | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/transformations/Accumulators.scala diff --git a/src/main/scala/com/high-performance-spark-examples/transformations/Accumulators.scala b/src/main/scala/com/high-performance-spark-examples/transformations/Accumulators.scala new file mode 100644 index 0000000..bbcb688 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/transformations/Accumulators.scala @@ -0,0 +1,44 @@ +/** + * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. + */ +package com.highperformancespark.examples.dataframe + +import org.apache.spark._ +import org.apache.spark.rdd._ + +object Accumulators { + /** + * Compute the total fuzzyness with an accumulator while generating an id and zip pair for sorting + */ + //tag::sumFuzzyAcc[] + def computeTotalFuzzyNess(sc: SparkContext, rdd: RDD[RawPanda]): (RDD[(String, Long)], Double) = { + val acc = sc.accumulator(0.0) // Create an accumulator with the initial value of 0.0 + val transformed = rdd.map{x => acc += x.attributes(0); (x.zip, x.id)} + // accumulator still has zero value + transformed.count() // force evaluation + // Note: This example is dangerous since the transformation may be evaluated multiple times + (transformed, acc.value) + } + //end::sumFuzzyAcc[] + + /** + * Compute the max fuzzyness with an accumulator while generating an id and zip pair for sorting + */ + //tag::maxFuzzyAcc[] + def computeMaxFuzzyNess(sc: SparkContext, rdd: RDD[RawPanda]): (RDD[(String, Long)], Double) = { + object MaxDoubleParam extends AccumulatorParam[Double] { + override def zero(initValue: Double) = initValue + override def addInPlace(r1: Double, r2: Double): Double = { + Math.max(r1, r2) + } + } + // Create an accumulator with the initial value of Double.MinValue + val acc = sc.accumulator(Double.MinValue)(MaxDoubleParam) + val transformed = rdd.map{x => acc += x.attributes(0); (x.zip, x.id)} + // accumulator still has Double.MinValue + transformed.count() // force evaluation + // Note: This example is dangerous since the transformation may be evaluated multiple times + (transformed, acc.value) + } + //end::maxFuzzyAcc[] +} From 784e4827af985c6a323e8c176f9324febd16f143 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 3 Feb 2016 00:39:38 -0800 Subject: [PATCH 107/198] move to the correct package --- .../transformations/Accumulators.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/transformations/Accumulators.scala b/src/main/scala/com/high-performance-spark-examples/transformations/Accumulators.scala index bbcb688..a781ecd 100644 --- a/src/main/scala/com/high-performance-spark-examples/transformations/Accumulators.scala +++ b/src/main/scala/com/high-performance-spark-examples/transformations/Accumulators.scala @@ -1,7 +1,9 @@ /** * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. */ -package com.highperformancespark.examples.dataframe +package com.highperformancespark.examples.transformations + +import com.highperformancespark.examples.dataframe.RawPanda import org.apache.spark._ import org.apache.spark.rdd._ From efa0c9bae6793d8111c4f8c700bf49f9e810b811 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 3 Feb 2016 00:39:48 -0800 Subject: [PATCH 108/198] Add a test --- .../transformations/Accumulators.scala | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/test/scala/com/high-performance-spark-examples/transformations/Accumulators.scala diff --git a/src/test/scala/com/high-performance-spark-examples/transformations/Accumulators.scala b/src/test/scala/com/high-performance-spark-examples/transformations/Accumulators.scala new file mode 100644 index 0000000..ba90abe --- /dev/null +++ b/src/test/scala/com/high-performance-spark-examples/transformations/Accumulators.scala @@ -0,0 +1,24 @@ +/** + * Happy Panda Example for DataFrames. Computes the % of happy pandas. Very contrived. + */ +package com.highperformancespark.examples.transformations + +import com.highperformancespark.examples.dataframe.RawPanda + +import com.holdenkarau.spark.testing._ + +import org.scalatest.FunSuite + +class AccumulatorsTest extends FunSuite with SharedSparkContext { + test("accumulator max should function") { + val input = sc.parallelize(1.to(100)).map(x => RawPanda(1L, "1", "red", true, Array(x.toDouble))) + val (_, max) = Accumulators.computeMaxFuzzyNess(sc, input) + assert(max === 100.0) + } + + test("accumulator sum should function") { + val input = sc.parallelize(1.to(100)).map(x => RawPanda(1L, "1", "red", true, Array(x.toDouble))) + val (_, sum) = Accumulators.computeTotalFuzzyNess(sc, input) + assert(sum === 5050.0) + } +} From d94cffee433c09bffb0de01ed2bec4f272268bc1 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Fri, 5 Feb 2016 06:40:25 +0200 Subject: [PATCH 109/198] Revert pandas schema to original one --- .../dataframe/LoadSave.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index 795100f..6488872 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -24,14 +24,13 @@ case class LoadSave(sqlContext: SQLContext) { pm.pandas.map(pi => Row(pi.id, pi.zip, pi.happy, pi.attributes)))) // Create DataFrame explicitly with specified schema - val schema = StructType(List( - StructField("name", StringType, nullable = true), - StructField("pandas", ArrayType( - StructType(List( - StructField("place", StringType, nullable = true), - StructField("pandaType", StringType, nullable = true), - StructField("happyPandas", IntegerType, nullable = true), - StructField("totalPandas", IntegerType, nullable = true))))))) + val schema = StructType(List(StructField("name", StringType, true), + StructField("pandas", ArrayType(StructType(List( + StructField("id", LongType, true), + StructField("zip", StringType, true), + StructField("happy", BooleanType, true), + StructField("attributes", ArrayType(FloatType), true))))))) + val df3 = sqlContext.createDataFrame(rowRDD, schema) } //end::createFromRDD[] From 5281e5a8c72cbdecf9539374d08c536b534f22bd Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 10 Feb 2016 19:02:00 -0800 Subject: [PATCH 110/198] match description in text --- .../high-performance-spark-examples/dataframe/HappyPandas.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 19f6a5b..1d0c802 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -102,7 +102,7 @@ object HappyPanda { */ def sadPandas(pandaInfo: DataFrame): DataFrame = { //tag::simpleFilter[] - pandaInfo.filter(!pandaInfo("happy")) + pandaInfo.filter(pandaInfo("happy") !== true) //end::simpleFilter[] } From 0bfb4c8d50219b72bcff524085620651a7ce754d Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 10 Feb 2016 20:25:18 -0800 Subject: [PATCH 111/198] Add a select explode example --- .../dataframe/HappyPandas.scala | 15 +++++++++++++++ .../dataframe/HappyPandas.scala | 11 +++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 1d0c802..3e33a1b 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -97,6 +97,21 @@ object HappyPanda { pandaInfo.filter(pandaInfo("happyPandas") >= minHappyPandas) } + /** + * Extra the panda info from panda places and compute the squisheness of the panda + */ + def squishPandaFromPace(pandaPlace: DataFrame): DataFrame = { + //tag::selectExplode[] + val pandaInfo = pandaPlace.explode(pandaPlace("pandas")){ + case Row(pandas: Seq[Row]) => + pandas.map{ + case Row(id: Long, zip: String, pt: String, happy: Boolean, attrs: Seq[Double]) => + RawPanda(id, zip, pt, happy, attrs.toArray) + }} + pandaInfo.select((pandaInfo("attributes")(0) / pandaInfo("attributes")(1)).as("murh")) + //end::selectExplode[] + } + /** * Find pandas that are sad */ diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 27a0cda..175b270 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -31,6 +31,17 @@ class HappyPandasTest extends DataFrameSuiteBase { Pandas("hanafy", "11000", 15, 7), Pandas("hamdi", "11111", 20, 10)) + val pandaPlaces = List(PandaPlace("toronto", rawPandaList.toArray)) + + test("simple explode test") { + val inputDF = sqlContext.createDataFrame(pandaPlaces) + val pandaInfo = sqlContext.createDataFrame(rawPandaList) + val expectedDf = pandaInfo.select((pandaInfo("attributes")(0) / pandaInfo("attributes")(1)).as("murh")) + val result = HappyPanda.squishPandaFromPace(inputDF) + + approxEqualDataFrames(expectedDf, result, 1E-5) + } + //tag::approxEqualDataFrames[] test("verify simple happy pandas Percentage") { From 856fb618953a32fccee7e575d7cf1c3223028d14 Mon Sep 17 00:00:00 2001 From: hanafy Date: Thu, 11 Feb 2016 06:41:35 +0200 Subject: [PATCH 112/198] Keep full list of imports --- .../dataframe/HappyPandas.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 3f0a22f..8175b06 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -5,7 +5,8 @@ package com.highperformancespark.examples.dataframe import org.apache.spark._ //tag::sparkSQLImports[] -import org.apache.spark.sql.{DataFrame, SQLContext} +import org.apache.spark.sql.{DataFrame, SQLContext, Row} +import org.apache.spark.sql.catalyst.expressions.aggregate._ import org.apache.spark.sql.expressions._ import org.apache.spark.sql.functions._ //end::sparkSQLImports[] From 0d7133090b6042f29c18f9e2afd6ba12a28b8c5f Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 10 Feb 2016 23:04:46 -0800 Subject: [PATCH 113/198] Class was renamed in merge --- .../dataframe/HappyPandasTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala index 7ac425b..955eaf4 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala @@ -37,7 +37,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val inputDF = sqlContext.createDataFrame(pandaPlaces) val pandaInfo = sqlContext.createDataFrame(rawPandaList) val expectedDf = pandaInfo.select((pandaInfo("attributes")(0) / pandaInfo("attributes")(1)).as("murh")) - val result = HappyPanda.squishPandaFromPace(inputDF) + val result = HappyPandas.squishPandaFromPace(inputDF) approxEqualDataFrames(expectedDf, result, 1E-5) } From da471b4a8a3f26e74898ca87e8b6d69229d18722 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 10 Feb 2016 23:11:44 -0800 Subject: [PATCH 114/198] Simplify mapPartitions example and add tag --- .../tools/SampleData.scala | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala b/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala index 48444dd..18934f8 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/SampleData.scala @@ -43,13 +43,12 @@ object SampleData { * Custom random sample with RNG. This is intended as an example of how to save setup overhead. */ def customSampleInput[T: ClassTag](rdd: RDD[T]): RDD[T] = { - rdd.mapPartitions{itr => val r = new Random() - itr.flatMap{x => - if (r.nextInt(10) == 0) { - Some(x) - } else { - None - }} + //tag::mapPartitions[] + rdd.mapPartitions{itr => + // Only create once RNG per partitions + val r = new Random() + itr.filter(x => r.nextInt(10) == 0) } + //end::mapPartitions[] } } From bd48249aac76b9b04696f0cde6ee0ed11d73eebc Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 11 Feb 2016 00:14:17 -0800 Subject: [PATCH 115/198] Add a simple broadcast example --- .../tools/FilterInvalidPandas.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/tools/FilterInvalidPandas.scala diff --git a/src/main/scala/com/high-performance-spark-examples/tools/FilterInvalidPandas.scala b/src/main/scala/com/high-performance-spark-examples/tools/FilterInvalidPandas.scala new file mode 100644 index 0000000..c31c8aa --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/tools/FilterInvalidPandas.scala @@ -0,0 +1,17 @@ +package com.highperformancespark.examples.tools + +import scala.collection.immutable.HashSet + +import com.highperformancespark.examples.dataframe.RawPanda + +import org.apache.spark._ +import org.apache.spark.rdd.RDD + +object FilterInvalidPandas { + + def filterInvalidPandas(sc: SparkContext, invalidPandas: List[Long], input: RDD[RawPanda]) = { + val invalid = HashSet() ++ invalidPandas + val invalidBroadcast = sc.broadcast(invalid) + input.filter{panda => !invalidBroadcast.value.contains(panda.id)} + } +} From ae6267f2b39e220c4e92705884f5b2c6b235bed7 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 11 Feb 2016 00:23:28 -0800 Subject: [PATCH 116/198] Add the tag --- .../tools/FilterInvalidPandas.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/tools/FilterInvalidPandas.scala b/src/main/scala/com/high-performance-spark-examples/tools/FilterInvalidPandas.scala index c31c8aa..0fbe944 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/FilterInvalidPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/FilterInvalidPandas.scala @@ -10,8 +10,10 @@ import org.apache.spark.rdd.RDD object FilterInvalidPandas { def filterInvalidPandas(sc: SparkContext, invalidPandas: List[Long], input: RDD[RawPanda]) = { + //tag::broadcast[] val invalid = HashSet() ++ invalidPandas val invalidBroadcast = sc.broadcast(invalid) input.filter{panda => !invalidBroadcast.value.contains(panda.id)} + //end::broadcast[] } } From 25a92d644e2a2d971849b851619094933ad564fb Mon Sep 17 00:00:00 2001 From: Rachel Date: Sat, 6 Feb 2016 14:07:26 -0800 Subject: [PATCH 117/198] [WIP] placeholder for adding iter to iter --- .../GoldiLocks/GoldiLocksWithHashMap.scala | 125 ++++++++++++++---- 1 file changed, 97 insertions(+), 28 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala index 6d0c46b..22856e0 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala @@ -73,11 +73,11 @@ object GoldiLocksWithHashMap { val aggregatedValueColumnRDD = dataFrame.rdd.mapPartitions(rows => { val valueColumnMap = new mutable.HashMap[(Double, Int), Long]() rows.foreach(row => { - row.toSeq.zipWithIndex.foreach{ case (value, columnIndex) => + row.toSeq.zipWithIndex.foreach{ case (value, columnIndex) => { val key = (value.toString.toDouble, columnIndex) val count = valueColumnMap.getOrElseUpdate(key, 0) valueColumnMap.update(key, count + 1) - } + }} }) valueColumnMap.toIterator @@ -172,40 +172,44 @@ object GoldiLocksWithHashMap { * * @return returns RDD of the target ranks (column index, value) */ + //tag::mapPartitionsExample[] private def findTargetRanksIteratively(sortedAggregatedValueColumnPairs : RDD[((Double, Int), Long)], - ranksLocations : Array[(Int, List[(Int, Long)])]): RDD[(Int, Double)] = { + ranksLocations : Array[(Int, List[(Int, Long)])] + ): RDD[(Int, Double)] = { sortedAggregatedValueColumnPairs.mapPartitionsWithIndex((partitionIndex : Int, aggregatedValueColumnPairs : Iterator[((Double, Int), Long)]) => { - val targetsInThisPart = ranksLocations(partitionIndex)._2 - if (targetsInThisPart.nonEmpty) { - val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) - val columnsInThisPart = targetsInThisPart.map(_._1).distinct - - val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() - runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap - - val result: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() - - aggregatedValueColumnPairs.foreach { case ((value, colIndex), count) => { - if (columnsInThisPart contains colIndex) { - val total = runningTotals(colIndex) - - val ranksPresent = columnsRelativeIndex(colIndex) - .filter(index => (index <= count + total) && (index > total)) - ranksPresent.foreach(r => result += ((colIndex, value))) - - runningTotals.update(colIndex, total + count) - } - }} - - result.toIterator + val targetsInThisPart: List[(Int, Long)] = ranksLocations(partitionIndex)._2 + if (!targetsInThisPart.isEmpty) { +// val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) +// val columnsInThisPart = targetsInThisPart.map(_._1).distinct +// +// val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() +// runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap +// +// val result: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() +// +// aggregatedValueColumnPairs.foreach { case ((value, colIndex), count) => { +// if (columnsInThisPart contains colIndex) { +// val total = runningTotals(colIndex) +// +// val ranksPresent = columnsRelativeIndex(colIndex) +// .filter(index => (index <= count + total) && (index > total)) +// ranksPresent.foreach(r => result += ((colIndex, value))) +// +// runningTotals.update(colIndex, total + count) +// } +// }} +// +// result.toIterator + FindTargetsSubRoutine.asIteratorToIteratorTransformation(aggregatedValueColumnPairs, + targetsInThisPart) } else Iterator.empty }) } - + //end::mapPartitionsExample[] /** * We will want to use this in some chapter where we talk about check pointing * @param valPairs @@ -238,4 +242,69 @@ object GoldiLocksWithHashMap { targetRanksValues.groupByKey().collectAsMap() } } -//end::hashMap[] \ No newline at end of file +//end::hashMap[] + + +object FindTargetsSubRoutine extends Serializable { + + //tag::notIter[] + def withHashMap(valueColumnPairsIter : Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)] ) = { + + val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) + val columnsInThisPart = targetsInThisPart.map(_._1).distinct + + val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() + runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap + + val result: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + + valueColumnPairsIter.foreach { + case ((value, colIndex), count) => + + if (columnsInThisPart contains colIndex) { + val total = runningTotals(colIndex) + val ranksPresent = columnsRelativeIndex(colIndex) + .filter(index => (index <= count + total) && (index > total)) + + ranksPresent.foreach(r => result += ((colIndex, value))) + + runningTotals.update(colIndex, total + count) + } + } + + result.toIterator + } + //end::notIter[] + +//The same routine but as an iterator transformation + //ToDO: PlaceHolder for the iterator to iterator transformation which I haven't written yet + //tag::iterToIter[] + def asIteratorToIteratorTransformation(valueColumnPairsIter : Iterator[((Double, Int), Long)], + targetsInThisPart: List[(Int, Long)] ) = { + + val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) + val columnsInThisPart = targetsInThisPart.map(_._1).distinct + + val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() + runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap + + val result: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + + valueColumnPairsIter.foreach { + case ((value, colIndex), count) => + + if (columnsInThisPart contains colIndex) { + val total = runningTotals(colIndex) + val ranksPresent = columnsRelativeIndex(colIndex) + .filter(index => (index <= count + total) && (index > total)) + + ranksPresent.foreach(r => result += ((colIndex, value))) + + runningTotals.update(colIndex, total + count) + } + } + + result.toIterator + } + //end::iterToIter[] +} \ No newline at end of file From dd96701d7ac7fa86e2d8a94a0cffb5842eb66a8e Mon Sep 17 00:00:00 2001 From: Rachel Date: Wed, 10 Feb 2016 19:28:23 -0800 Subject: [PATCH 118/198] adding the iterator to iterator panda --- .../GoldiLocks/GoldiLocksWithHashMap.scala | 91 +++++++++++-------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala index 22856e0..5a37f9a 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala @@ -4,6 +4,7 @@ import org.apache.spark.rdd.RDD import org.apache.spark.sql.DataFrame import org.apache.spark.storage.StorageLevel +import scala.Predef import scala.collection.{mutable, Map} import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.MutableList @@ -182,27 +183,6 @@ object GoldiLocksWithHashMap { val targetsInThisPart: List[(Int, Long)] = ranksLocations(partitionIndex)._2 if (!targetsInThisPart.isEmpty) { -// val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) -// val columnsInThisPart = targetsInThisPart.map(_._1).distinct -// -// val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() -// runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap -// -// val result: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() -// -// aggregatedValueColumnPairs.foreach { case ((value, colIndex), count) => { -// if (columnsInThisPart contains colIndex) { -// val total = runningTotals(colIndex) -// -// val ranksPresent = columnsRelativeIndex(colIndex) -// .filter(index => (index <= count + total) && (index > total)) -// ranksPresent.foreach(r => result += ((colIndex, value))) -// -// runningTotals.update(colIndex, total + count) -// } -// }} -// -// result.toIterator FindTargetsSubRoutine.asIteratorToIteratorTransformation(aggregatedValueColumnPairs, targetsInThisPart) } @@ -248,14 +228,39 @@ object GoldiLocksWithHashMap { object FindTargetsSubRoutine extends Serializable { //tag::notIter[] - def withHashMap(valueColumnPairsIter : Iterator[((Double, Int), Long)], targetsInThisPart: List[(Int, Long)] ) = { + /** + * This sub routine returns an Iterator of (columnIndex, value) that correspond to one of the + desired rank statistics on this partition. + + Because in the original iterator, the pairs are distinct + and include the count, one row of the original iterator could map to multiple elements in the output. + I.e. if we were looking for the 2nd and 3rd element in column index 4 on this partition. And the head + of this partition is ((3249.0, 4), 23) (i.e. the element 3249.0 in the 4 th column appears 23 times), + then we would output (4, 3249.0) twice in the final iterator. Once because 3249.0 is the 2nd element and + once because it is the third element on that partition for that column index + and we are looking for both the second and third element. + + * @param valueColumnPairsIter - passed in from the mapPartitions function. An iterator of the sorted + * ((value, columnIndex), count) tupples. + * @param targetsInThisPart - (columnIndex, index-on-partition pairs). In the above example this would + * include (4, 2) and (4,3) since we desire the 2nd element for column + * index 4 on this partition and the 3rd element. + * @return All of the rank statistics that live in this partition as an iterator of (columnIndex, value pairs) + */ + def withArrayBuffer(valueColumnPairsIter : Iterator[((Double, Int), Long)], + targetsInThisPart: List[(Int, Long)] ): Iterator[(Int, Double)] = { + + val columnsRelativeIndex: Predef.Map[Int, List[Long]] = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) - val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) - val columnsInThisPart = targetsInThisPart.map(_._1).distinct + //the column indices of the pairs that are desired rank statistics that live in this partition. + val columnsInThisPart: List[Int] = targetsInThisPart.map(_._1).distinct + //a HashMap with the running totals of each column index. As we loop through the iterator + // we will update the hashmap as we see elements of each column index. val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap + //we use an array buffer to build the resulting iterator val result: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() valueColumnPairsIter.foreach { @@ -263,24 +268,29 @@ object FindTargetsSubRoutine extends Serializable { if (columnsInThisPart contains colIndex) { val total = runningTotals(colIndex) + //the ranks that are contains by this element of the input iterator. + //get by filtering the val ranksPresent = columnsRelativeIndex(colIndex) .filter(index => (index <= count + total) && (index > total)) ranksPresent.foreach(r => result += ((colIndex, value))) - + //update the running totals. runningTotals.update(colIndex, total + count) } } - + //convert result.toIterator } - //end::notIter[] + //end::notIter[] + -//The same routine but as an iterator transformation - //ToDO: PlaceHolder for the iterator to iterator transformation which I haven't written yet //tag::iterToIter[] + /** + * Same function as above but rather than building the result from an array buffer we use + * a flatMap on the iterator to get the resulting iterator. + */ def asIteratorToIteratorTransformation(valueColumnPairsIter : Iterator[((Double, Int), Long)], - targetsInThisPart: List[(Int, Long)] ) = { + targetsInThisPart: List[(Int, Long)] ): Iterator[(Int, Double)] = { val columnsRelativeIndex = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) val columnsInThisPart = targetsInThisPart.map(_._1).distinct @@ -288,23 +298,28 @@ object FindTargetsSubRoutine extends Serializable { val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap - val result: ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() + //map the valueColumn pairs to a list of (colIndex, value) pairs that correspond to one of the + //desired rank statistics on this partition. + + valueColumnPairsIter.flatMap{ - valueColumnPairsIter.foreach { - case ((value, colIndex), count) => + case (((value, colIndex), count)) => if (columnsInThisPart contains colIndex) { val total = runningTotals(colIndex) - val ranksPresent = columnsRelativeIndex(colIndex) - .filter(index => (index <= count + total) && (index > total)) + val ranksPresent: List[Long] = columnsRelativeIndex(colIndex) + .filter(index => (index <= count + total) && (index > total)) - ranksPresent.foreach(r => result += ((colIndex, value))) + val nextElems: Iterator[(Int, Double)] = ranksPresent.map(r => (colIndex, value)).toIterator + //update the running totals runningTotals.update(colIndex, total + count) + nextElems + } else{ + //if there is nothing here add an empty iterator to the flatMap + Iterator[(Int, Double)]() } } - - result.toIterator } //end::iterToIter[] } \ No newline at end of file From 3ee3dec4fc3c13a80299450e07e958ef2f2c13f5 Mon Sep 17 00:00:00 2001 From: Rachel Date: Wed, 10 Feb 2016 19:56:37 -0800 Subject: [PATCH 119/198] fix indent --- .../GoldiLocks/GoldiLocksWithHashMap.scala | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala index 5a37f9a..0991911 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala @@ -256,7 +256,7 @@ object FindTargetsSubRoutine extends Serializable { val columnsInThisPart: List[Int] = targetsInThisPart.map(_._1).distinct //a HashMap with the running totals of each column index. As we loop through the iterator - // we will update the hashmap as we see elements of each column index. + //we will update the hashmap as we see elements of each column index. val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap @@ -267,6 +267,7 @@ object FindTargetsSubRoutine extends Serializable { case ((value, colIndex), count) => if (columnsInThisPart contains colIndex) { + val total = runningTotals(colIndex) //the ranks that are contains by this element of the input iterator. //get by filtering the @@ -274,15 +275,15 @@ object FindTargetsSubRoutine extends Serializable { .filter(index => (index <= count + total) && (index > total)) ranksPresent.foreach(r => result += ((colIndex, value))) - //update the running totals. + + //update the running totals. runningTotals.update(colIndex, total + count) } } //convert - result.toIterator + result.toIterator } - //end::notIter[] - + //end::notIter[] //tag::iterToIter[] /** @@ -296,16 +297,21 @@ object FindTargetsSubRoutine extends Serializable { val columnsInThisPart = targetsInThisPart.map(_._1).distinct val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() - runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap + runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap + + //filter out the pairs that don't have a column index that is in this part + val pairsWithRanksInThisPart = valueColumnPairsIter.filter{ + case (((value, colIndex), count)) => + columnsInThisPart contains colIndex + } //map the valueColumn pairs to a list of (colIndex, value) pairs that correspond to one of the //desired rank statistics on this partition. - - valueColumnPairsIter.flatMap{ + pairsWithRanksInThisPart.flatMap{ case (((value, colIndex), count)) => - if (columnsInThisPart contains colIndex) { + // if (columnsInThisPart contains colIndex) { val total = runningTotals(colIndex) val ranksPresent: List[Long] = columnsRelativeIndex(colIndex) .filter(index => (index <= count + total) && (index > total)) @@ -315,10 +321,6 @@ object FindTargetsSubRoutine extends Serializable { //update the running totals runningTotals.update(colIndex, total + count) nextElems - } else{ - //if there is nothing here add an empty iterator to the flatMap - Iterator[(Int, Double)]() - } } } //end::iterToIter[] From 0d3b63e34a40ed46023e30b492b79731bb39c31b Mon Sep 17 00:00:00 2001 From: Rachel Date: Wed, 10 Feb 2016 20:24:17 -0800 Subject: [PATCH 120/198] fix last comment --- .../GoldiLocks/GoldiLocksWithHashMap.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala index 0991911..fc37f31 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksWithHashMap.scala @@ -311,7 +311,6 @@ object FindTargetsSubRoutine extends Serializable { case (((value, colIndex), count)) => - // if (columnsInThisPart contains colIndex) { val total = runningTotals(colIndex) val ranksPresent: List[Long] = columnsRelativeIndex(colIndex) .filter(index => (index <= count + total) && (index > total)) From c6ec1453d9b8750ef43bb0a9c89f5f189df8cccb Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 11 Feb 2016 12:27:06 -0800 Subject: [PATCH 121/198] refactoring first try to use iter to iter --- .../GoldiLocks/GoldiLocksFirstTry.scala | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala index b60b94f..737bafa 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala @@ -151,17 +151,17 @@ object GoldiLocksFirstTry { partitionColumnsFreq.sortBy(_._1).map { case (partitionIndex, columnsFreq) => val relevantIndexList = new MutableList[(Int, Long)]() - columnsFreq.zipWithIndex.foreach{ case (colCount, colIndex) => { + columnsFreq.zipWithIndex.foreach{ case (colCount, colIndex) => val runningTotalCol = runningTotal(colIndex) val ranksHere: List[Long] = targetRanks.filter(rank => - (runningTotalCol < rank && runningTotalCol + colCount >= rank)) + runningTotalCol < rank && runningTotalCol + colCount >= rank) // for each of the rank statistics present add this column index and the index it will be at // on this partition (the rank - the running total) relevantIndexList ++= ranksHere.map(rank => (colIndex, rank - runningTotalCol)) runningTotal(colIndex) += colCount - }} + } (partitionIndex, relevantIndexList.toList) } @@ -187,19 +187,21 @@ object GoldiLocksFirstTry { val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap - val result : ArrayBuffer[(Int, Double)] = new scala.collection.mutable.ArrayBuffer() - - valueColumnPairs.foreach{ case(value, colIndex) => { - if (runningTotals contains colIndex) { - val total = runningTotals(colIndex) + 1L - runningTotals.update(colIndex, total) - - if (columnsRelativeIndex(colIndex).contains(total)) - result += ((colIndex, value)) - } - }} - - result.toIterator + //filter this iterator, so that it contains only those (value, columnIndex) that are the ranks statistics on this partition + // I.e. Keep track of the number of elements we have seen for each columnIndex using the + // running total hashMap. Keep those pairs for which value is the nth element for that columnIndex that appears on this partition + // and the map contains (columnIndex, n). + valueColumnPairs.filter{ + case(value, colIndex) => + //rely on lazy evaluation. If we have already seen this column index, then evalute this + // block in which we increment the running totals and return if this element's count appears in the map. + lazy val thisPairIsTheRankStatistic: Boolean = { + val total = runningTotals(colIndex) + 1L + runningTotals.update(colIndex, total) + columnsRelativeIndex(colIndex).contains(total) + } + (runningTotals contains colIndex) && thisPairIsTheRankStatistic + }.map(_.swap) } else { Iterator.empty From bbf51e1c54a0585c03974e13d9d8508756937497 Mon Sep 17 00:00:00 2001 From: Rachel Date: Tue, 16 Feb 2016 16:05:28 -0500 Subject: [PATCH 122/198] code examples to go with diagrams --- .../transformations/NarrowAndWide.scala | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/scala/com/high-performance-spark-examples/transformations/NarrowAndWide.scala diff --git a/src/main/scala/com/high-performance-spark-examples/transformations/NarrowAndWide.scala b/src/main/scala/com/high-performance-spark-examples/transformations/NarrowAndWide.scala new file mode 100644 index 0000000..6f9020d --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/transformations/NarrowAndWide.scala @@ -0,0 +1,39 @@ + +package com.highperformancespark.examples.transformations + +import org.apache.spark.rdd.RDD + + +object NarrowAndWide { + + //toDO: Probably should write some sort of test for this. + //this is used in chapter 4 for the stage diagram + def sillySparkProgram( rdd1 : RDD[Int]) = { + + //tag::narrowWide[] + + //Narrow dependency. Map the rdd to tuples of (x, 1) + val rdd2 = rdd1.map((_, 1)) + //wide dependency groupByKey + val rdd3 = rdd2.groupByKey() + //end::narrowWide[] + + rdd3 + } + //this is used in chapter two for the stage diagram. + + //tag::stageDiagram[] + def simpleSparkProgram( rdd : RDD[Double]): Long ={ + + rdd.filter(_< 1000.0) + .map(x => (x , x) ) + + .groupByKey() + .map{ case(value, groups) => (groups.sum, value)} + + .sortByKey() + .count() + } + //end::stageDiagram[] + +} From 42335565c6440b015212cc5d0ac012f37dd8142f Mon Sep 17 00:00:00 2001 From: Rachel Date: Tue, 16 Feb 2016 16:42:02 -0500 Subject: [PATCH 123/198] spacing --- .../transformations/NarrowAndWide.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/transformations/NarrowAndWide.scala b/src/main/scala/com/high-performance-spark-examples/transformations/NarrowAndWide.scala index 6f9020d..d341cb8 100644 --- a/src/main/scala/com/high-performance-spark-examples/transformations/NarrowAndWide.scala +++ b/src/main/scala/com/high-performance-spark-examples/transformations/NarrowAndWide.scala @@ -8,7 +8,7 @@ object NarrowAndWide { //toDO: Probably should write some sort of test for this. //this is used in chapter 4 for the stage diagram - def sillySparkProgram( rdd1 : RDD[Int]) = { + def sillySparkProgram(rdd1 : RDD[Int]) = { //tag::narrowWide[] @@ -23,14 +23,14 @@ object NarrowAndWide { //this is used in chapter two for the stage diagram. //tag::stageDiagram[] - def simpleSparkProgram( rdd : RDD[Double]): Long ={ - + def simpleSparkProgram(rdd : RDD[Double]): Long ={ + //stage1 rdd.filter(_< 1000.0) .map(x => (x , x) ) - + //stage2 .groupByKey() .map{ case(value, groups) => (groups.sum, value)} - + //stage 3 .sortByKey() .count() } From 066ef72af205fd990391b0643bfaae07159ba792 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 21 Feb 2016 17:16:03 -0800 Subject: [PATCH 124/198] Capitalize the string in comments --- .../high-performance-spark-examples/dataframe/HappyPandas.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 3e33a1b..a2a9a4d 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -76,7 +76,7 @@ object HappyPanda { //tag::encodePandaType[] /** - * Encodes pandaType to Integer values instead of string values. + * Encodes pandaType to Integer values instead of String values. * * @param pandaInfo the input DataFrame * @return Returns a DataFrame of pandaId and integer value for pandaType. From 53d6a73f8b88c0b1556d686d51963ca13aaf0ec1 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sat, 27 Feb 2016 22:13:50 -0800 Subject: [PATCH 125/198] Show a coffee shop join --- .../dataframe/MixedDataset.scala | 12 ++++++++++++ .../dataframe/RawPandas.scala | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index b7a3ca0..9b2eab4 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -64,6 +64,18 @@ class MixedDataset(sqlCtx: SQLContext) { ds.select($"attributes"(0).as[Double]) } + /** + * Illustrate Dataset joins + */ + def joinSample(pandas: Dataset[RawPanda], coffeeShops: Dataset[CoffeeShop]): + Dataset[(RawPanda, CoffeeShop)] = { + //tag::joinWith[] + val result: Dataset[(RawPanda, CoffeeShop)] = pandas.joinWith(coffeeShops, + $"zip" === $"zip") + //end::joinWith[] + result + } + //tag::toRDDDF[] /** * Illustrate converting a Dataset to an RDD diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala index ff2ba69..d118130 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/RawPandas.scala @@ -13,3 +13,5 @@ case class RawPanda(id: Long, zip: String, pt: String, happy: Boolean, attribute * @param pandas pandas in that place */ case class PandaPlace(name: String, pandas: Array[RawPanda]) + +case class CoffeeShop(zip: String, name: String) From d59e730fc179582ee6c4710655f18896abdf2192 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 1 Mar 2016 11:56:14 -0800 Subject: [PATCH 126/198] Add max panda sizes on datasets in two ways --- .../dataframe/MixedDataset.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 9b2eab4..75f3eb7 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -56,6 +56,21 @@ class MixedDataset(sqlCtx: SQLContext) { } //end::functionalQuery[] + //tag::maxPandaSizePerZip[] + def maxPandaSizePerZip(ds: Dataset[RawPanda]): Dataset[(String, Double)] = { + ds.groupBy($"zip").keyAs[String].agg(max("attributes(2)").as[Double]) + } + //end::maxPandaSizePerZip[] + + //tag::maxPandaSizePerZipScala[] + def maxPandaSizePerZipScala(ds: Dataset[RawPanda]): Dataset[(String, Double)] = { + ds.groupBy($"zip").keyAs[String].mapGroups{ case (g, iter) => + (g, iter.map(_.attributes(2)).reduceLeft(Math.max(_, _))) + } + } + //end::maxPandaSizePerZipScala[] + + /** * Illustrate how we make typed queries, using some of the float properties to produce boolean * values. From bfcd99def26b365da95aa179dc3df864c2d74e20 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 1 Mar 2016 11:59:12 -0800 Subject: [PATCH 127/198] kill extra blank line --- .../high-performance-spark-examples/dataframe/MixedDataset.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 75f3eb7..912ac04 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -70,7 +70,6 @@ class MixedDataset(sqlCtx: SQLContext) { } //end::maxPandaSizePerZipScala[] - /** * Illustrate how we make typed queries, using some of the float properties to produce boolean * values. From bd2d985f4945a75bb79d49623a678a19d0910a82 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 3 Mar 2016 17:13:34 -0800 Subject: [PATCH 128/198] Add a sample of describe --- .../dataframe/HappyPandas.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index f9485bd..aeb9656 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -155,6 +155,12 @@ object HappyPandas { */ case class Pandas(name: String, zip: String, pandaSize: Integer, age: Integer) + def describePandas(pandas: DataFrame): DataFrame = { + //tag::pandaSizeRangeVarDescribe[] + pandas.describe() + //end::pandaSizeRangeVarDescribe[] + } + //tag::maxPandaSizePerZip[] def maxPandaSizePerZip(pandas: DataFrame): DataFrame = { pandas.groupBy(pandas("zip")).max("pandaSize") From 4142543de8f7a2c7de5f71d20b729e9df439e1df Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 3 Mar 2016 17:37:59 -0800 Subject: [PATCH 129/198] Add a self join example --- .../dataframe/MixedDataset.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 912ac04..2fca18f 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -90,6 +90,19 @@ class MixedDataset(sqlCtx: SQLContext) { result } + /** + * Illustrate a self join to compare pandas in the same zip code + */ + def selfJoin(pandas: Dataset[RawPanda]): + Dataset[(RawPanda, RawPanda)] = { + //tag::joinWith[] + val result: Dataset[(RawPanda, RawPanda)] = pandas.joinWith(pandas, + $"zip" === $"zip") + //end::joinWith[] + result + } + + //tag::toRDDDF[] /** * Illustrate converting a Dataset to an RDD From dc3531627549b45ff6993d32c3f708391dc54925 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 3 Mar 2016 17:38:21 -0800 Subject: [PATCH 130/198] fix tag --- .../dataframe/MixedDataset.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 2fca18f..4d20d81 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -95,10 +95,10 @@ class MixedDataset(sqlCtx: SQLContext) { */ def selfJoin(pandas: Dataset[RawPanda]): Dataset[(RawPanda, RawPanda)] = { - //tag::joinWith[] + //tag::slefJoin[] val result: Dataset[(RawPanda, RawPanda)] = pandas.joinWith(pandas, $"zip" === $"zip") - //end::joinWith[] + //end::selfJoin[] result } From 8cb222f92cb790ddc5489d035d1cbe8666f2d324 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 3 Mar 2016 17:55:50 -0800 Subject: [PATCH 131/198] oops swap char in tag --- .../dataframe/MixedDataset.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 4d20d81..4b9b910 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -95,7 +95,7 @@ class MixedDataset(sqlCtx: SQLContext) { */ def selfJoin(pandas: Dataset[RawPanda]): Dataset[(RawPanda, RawPanda)] = { - //tag::slefJoin[] + //tag::selfJoin[] val result: Dataset[(RawPanda, RawPanda)] = pandas.joinWith(pandas, $"zip" === $"zip") //end::selfJoin[] From affa2d2b43f8e9052aa5adab2df4c63dd3d9ff3f Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 3 Mar 2016 21:37:28 -0800 Subject: [PATCH 132/198] add word count thing with stop words --- .../wordcount/WordCount.scala | 17 +++++++++++++ .../wordcount/WordCountTest.scala | 24 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/test/scala/com/high-performance-spark-examples/wordcount/WordCountTest.scala diff --git a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala index 0a8e2c2..60f4961 100644 --- a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala +++ b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala @@ -1,3 +1,5 @@ +package com.highperformancespark.examples.wordcount + /** * What sort of big data book would this be if we didn't mention wordcount? */ @@ -20,4 +22,19 @@ object WordCount { val wordCounts = wordPairs.reduceByKey(_ + _) wordCounts } + + /** + * Come up with word counts but filter out the illegal tokens and stop words + */ + //tag::wordCountStopwords[] + def withStopWordsFiltered(rdd : RDD[String], illegalTokens : Array[Char], + stopWords : Set[String]): RDD[(String, Int)] = { + val tokens: RDD[String] = rdd.flatMap(_.split(illegalTokens ++ Array[Char](' ')).map(_.trim.toLowerCase)) + val words = tokens.filter(token => + !stopWords.contains(token) && (token.length > 0) ) + val wordPairs = words.map((_, 1)) + val wordCounts = wordPairs.reduceByKey(_ + _) + wordCounts + } + //end::wordCountStopwords[] } diff --git a/src/test/scala/com/high-performance-spark-examples/wordcount/WordCountTest.scala b/src/test/scala/com/high-performance-spark-examples/wordcount/WordCountTest.scala new file mode 100644 index 0000000..6d8edb9 --- /dev/null +++ b/src/test/scala/com/high-performance-spark-examples/wordcount/WordCountTest.scala @@ -0,0 +1,24 @@ +package com.highperformancespark.examples.wordcount + + +import com.holdenkarau.spark.testing.SharedSparkContext +import org.scalatest.FunSuite + +class WordCountTest extends FunSuite with SharedSparkContext { + test("word count with Stop Words Removed"){ + val wordRDD = sc.parallelize(Seq( + "How happy was the panda? You ask.", + "Panda is the most happy panda in all the #$!?ing land!")) + + val stopWords: Set[String] = Set("a", "the", "in", "was", "there", "she", "he") + val illegalTokens: Array[Char] = "#$%?!.".toCharArray + + val wordCounts = WordCount.withStopWordsFiltered(wordRDD, illegalTokens, stopWords) + val wordCountsAsMap = wordCounts.collectAsMap() + assert(!wordCountsAsMap.contains("the")) + assert(!wordCountsAsMap.contains("?")) + assert(!wordCountsAsMap.contains("#$!?ing")) + assert(wordCountsAsMap.contains("ing")) + assert(wordCountsAsMap.get("panda").get.equals(3)) + } +} From ea7c99457527d30a9801dd0e96f973a9abafa7ec Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Fri, 4 Mar 2016 22:31:51 -0800 Subject: [PATCH 133/198] Add self join example --- .../dataframe/HappyPandas.scala | 10 ++++++++++ .../dataframe/HappyPandasTest.scala | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index aeb9656..a7cbb1e 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -255,4 +255,14 @@ object HappyPandas { df1.join(df2, df1("name") === df2("name"), "leftsemi") //end::leftsemiJoin[] } + + // Self join + def selfJoin(df: DataFrame): DataFrame = { + val sqlCtx = df.sqlContext + import sqlCtx.implicits._ + //tag::selfJoin[] + val joined = df.as("a").join(df.as("b")).where($"a.name" === $"b.name") + //end::selfJoin[] + joined + } } diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala index 955eaf4..2dca442 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala @@ -33,6 +33,15 @@ class HappyPandasTest extends DataFrameSuiteBase { val pandaPlaces = List(PandaPlace("toronto", rawPandaList.toArray)) + test("simple self join test") { + val sqlCtx = sqlContext + import sqlCtx.implicits._ + val inputDF = sqlCtx.createDataFrame(pandasList) + val result = HappyPandas.selfJoin(inputDF).select($"a.name", $"b.name") + val rez = result.collect() + rez.foreach{x => assert(x(0) == x(1))} + } + test("simple explode test") { val inputDF = sqlContext.createDataFrame(pandaPlaces) val pandaInfo = sqlContext.createDataFrame(rawPandaList) From 6cbb262887e1f8a26e290d7b7d4cd22abc829a7b Mon Sep 17 00:00:00 2001 From: Rachel Date: Fri, 4 Mar 2016 22:48:22 -0800 Subject: [PATCH 134/198] add tag to original --- .../high-performance-spark-examples/wordcount/WordCount.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala index 60f4961..afa345d 100644 --- a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala +++ b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala @@ -16,12 +16,14 @@ object WordCount { } // good idea: doesn't use group by key - def goodIdea(rdd: RDD[String]): RDD[(String, Int)] = { + //tag::simpleWordCount[] + def simpleWordCount(rdd: RDD[String]): RDD[(String, Int)] = { val words = rdd.flatMap(_.split(" ")) val wordPairs = words.map((_, 1)) val wordCounts = wordPairs.reduceByKey(_ + _) wordCounts } + //end::simpleWordCount /** * Come up with word counts but filter out the illegal tokens and stop words From 81520ee221828294828587e95a37490b870934a2 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 6 Mar 2016 13:56:25 -0800 Subject: [PATCH 135/198] Show a load example --- .../dataframe/HappyPandas.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index a7cbb1e..edfc3ee 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -4,6 +4,7 @@ package com.highperformancespark.examples.dataframe import org.apache.spark._ +import org.apache.spark.rdd.RDD //tag::sparkSQLImports[] import org.apache.spark.sql.{DataFrame, SQLContext, Row} import org.apache.spark.sql.catalyst.expressions.aggregate._ @@ -53,6 +54,14 @@ object HappyPandas { df } + def jsonLoadFromRDD(sc: SparkContext, sqlCtx: SQLContext, input: RDD[String]): DataFrame = { + //tag::loadPandaJSONRDD[] + val rdd: RDD[String] = input.filter(_.contains("panda")) + val df = sqlCtx.read.json(rdd) + //end::loadPandaJSONRDD[] + df + } + // Here will be some examples on PandaInfo DataFrame /** From e3ebd0e97a20a31898ade06bb8c9d83f2575fe1c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 6 Mar 2016 22:04:10 -0800 Subject: [PATCH 136/198] Fix indentation add leftouterjoin example --- .../GoldiLocks/RDDJoinExamples.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RDDJoinExamples.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RDDJoinExamples.scala index 723ca1d..9578ea5 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RDDJoinExamples.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/RDDJoinExamples.scala @@ -22,9 +22,17 @@ object RDDJoinExamples { addressRDD : RDD[(Long, String )]) : RDD[(Long, (Double, String))]= { val joinedRDD = scoreRDD.join(addressRDD) joinedRDD.reduceByKey( (x, y) => if(x._1 > y._1) x else y ) - } + } //end::joinScoresWithAddress[] + //tag::leftOuterJoinScoresWithAddress[] + def outerJoinScoresWithAddress( scoreRDD : RDD[(Long, Double)], + addressRDD : RDD[(Long, String )]) : RDD[(Long, (Double, Option[String]))]= { + val joinedRDD = scoreRDD.leftOuterJoin(addressRDD) + joinedRDD.reduceByKey( (x, y) => if(x._1 > y._1) x else y ) + } + //end::leftOuterJoinScoresWithAddress[] + //tag::joinScoresWithAddressFast[] def joinScoresWithAddress2( scoreRDD : RDD[(Long, Double)], addressRDD : RDD[(Long, String )]) : RDD[(Long, (Double, String))]= { From ca7981e861c1e26747d650d7c0087fc6240fdeb9 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 7 Mar 2016 17:42:04 -0800 Subject: [PATCH 137/198] Cast to double for fuzzyness --- .../high-performance-spark-examples/perf/SimplePerfTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala index 6f946ab..9629451 100644 --- a/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala +++ b/src/main/scala/com/high-performance-spark-examples/perf/SimplePerfTest.scala @@ -47,7 +47,7 @@ object SimplePerfTest { val rddTimeings = 1.to(10).map(x => time(testOnRDD(pairRDD))) val groupTimeings = 1.to(10).map(x => time(groupOnRDD(pairRDD))) val df = inputRDD.toDF() - val inputDataFrame = df.select(df("zip").cast(IntegerType), df("attributes")(0).as("fuzzyness")) + val inputDataFrame = df.select(df("zip").cast(IntegerType), df("attributes")(0).as("fuzzyness").cast(DoubleType)) inputDataFrame.cache() inputDataFrame.count() val dataFrameTimeings = 1.to(10).map(x => time(testOnDataFrame(inputDataFrame))) From 6f5e613d4d00b6fb18b7743f58ce41a0a0de544b Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 7 Mar 2016 17:42:18 -0800 Subject: [PATCH 138/198] Add an append example --- .../dataframe/LoadSave.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index 6488872..0bba6cf 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -62,6 +62,12 @@ case class LoadSave(sqlContext: SQLContext) { } //end::partitionedOutput[] + //tag::saveAppend[] + def writeAppend(input: DataFrame): Unit = { + input.write.mode(SaveMode.Append).save("output/") + } + //end::saveAppend[] + def createJDBC() = { //tag::createJDBC[] sqlContext.read.jdbc("jdbc:dialect:serverName;user=user;password=pass", From 841fec5cc86e238b5135c357f55379d9bd0bf300 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 7 Mar 2016 18:05:14 -0800 Subject: [PATCH 139/198] change murh to squishyness --- .../dataframe/HappyPandas.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index edfc3ee..ff4c5bb 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -117,7 +117,9 @@ object HappyPandas { case Row(id: Long, zip: String, pt: String, happy: Boolean, attrs: Seq[Double]) => RawPanda(id, zip, pt, happy, attrs.toArray) }} - pandaInfo.select((pandaInfo("attributes")(0) / pandaInfo("attributes")(1)).as("murh")) + pandaInfo.select( + (pandaInfo("attributes")(0) / pandaInfo("attributes")(1)) + .as("squishyness")) //end::selectExplode[] } From 177348bf56708ebb9b52f66879568b015df70f96 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 7 Mar 2016 18:12:08 -0800 Subject: [PATCH 140/198] specify squishyness --- .../dataframe/HappyPandasTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala index 2dca442..daf80e2 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala @@ -45,7 +45,7 @@ class HappyPandasTest extends DataFrameSuiteBase { test("simple explode test") { val inputDF = sqlContext.createDataFrame(pandaPlaces) val pandaInfo = sqlContext.createDataFrame(rawPandaList) - val expectedDf = pandaInfo.select((pandaInfo("attributes")(0) / pandaInfo("attributes")(1)).as("murh")) + val expectedDf = pandaInfo.select((pandaInfo("attributes")(0) / pandaInfo("attributes")(1)).as("squishyness")) val result = HappyPandas.squishPandaFromPace(inputDF) approxEqualDataFrames(expectedDf, result, 1E-5) From e1069261938a16cb3e4bc4aeb1451a3840c36950 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 9 Mar 2016 13:11:31 -0800 Subject: [PATCH 141/198] Comment typo --- .../high-performance-spark-examples/dataframe/LoadSave.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index 0bba6cf..ec63e12 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -13,10 +13,10 @@ case class LoadSave(sqlContext: SQLContext) { import sqlContext.implicits._ //tag::createFromRDD[] def createFromCaseClassRDD(input: RDD[PandaPlace]) = { - // Create DataFrame explicitly using sqlContext and schema inferance + // Create DataFrame explicitly using sqlContext and schema inference val df1 = sqlContext.createDataFrame(input) - // Create DataFrame using sqlContext implicits and schema inferance + // Create DataFrame using sqlContext implicits and schema inference val df2 = input.toDF() // Create a Row RDD from our RDD of case classes From 9e01fc7e1dad3eb2553aff8523e2f47feee8ff81 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 16 Mar 2016 21:28:22 -0700 Subject: [PATCH 142/198] Long line break --- .../high-performance-spark-examples/wordcount/WordCount.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala index afa345d..89746b6 100644 --- a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala +++ b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala @@ -31,7 +31,8 @@ object WordCount { //tag::wordCountStopwords[] def withStopWordsFiltered(rdd : RDD[String], illegalTokens : Array[Char], stopWords : Set[String]): RDD[(String, Int)] = { - val tokens: RDD[String] = rdd.flatMap(_.split(illegalTokens ++ Array[Char](' ')).map(_.trim.toLowerCase)) + val tokens: RDD[String] = rdd.flatMap(_.split(illegalTokens ++ Array[Char](' ')). + map(_.trim.toLowerCase)) val words = tokens.filter(token => !stopWords.contains(token) && (token.length > 0) ) val wordPairs = words.map((_, 1)) From 63e69ab3ee8292f851af01f7cf386fe54c09c7ee Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 16 Mar 2016 21:29:44 -0700 Subject: [PATCH 143/198] long line break --- resources/rawpanda.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/rawpanda.json b/resources/rawpanda.json index 8e690c6..1d9940d 100644 --- a/resources/rawpanda.json +++ b/resources/rawpanda.json @@ -1 +1,2 @@ -{"name":"mission","pandas":[{"id":1,"zip":"94110","pt":"giant", "happy":true,"attributes":[0.4,0.5]}]} +{"name":"mission","pandas":[{"id":1,"zip":"94110","pt":"giant", "happy":true, + "attributes":[0.4,0.5]}]} From 52a7176fa74d454d6b3a5ca4539820943d2e6d58 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Fri, 18 Mar 2016 17:38:08 -0700 Subject: [PATCH 144/198] typo --- .../high-performance-spark-examples/dataframe/RegularSQL.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala index aaa27fd..a25a97f 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/RegularSQL.scala @@ -16,7 +16,7 @@ case class RegularSQL(sqlContext: SQLContext) { // TODO: Holden: include a parquet example file and point this to that. //tag::queryRawFile[] def queryRawFile(): DataFrame = { - sqlContext.sql("SELECT * FROM parquet.`path_to_parquer_file`") + sqlContext.sql("SELECT * FROM parquet.`path_to_parquet_file`") } //end::queryRawFile[] From ba15f697db3bda6fda493aadce6821d57ad55e28 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 28 Apr 2016 11:16:35 -0700 Subject: [PATCH 145/198] Add a really basic note to the README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index a7f4184..22cad61 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ # high-performance-spark-examples Examples for High Performance Spark + +# Building + +Most of the examples can be built with sbt, the C and Fortran components depend on gcc and g77 respectively. + +# Tests + +The full test suite depends on having the C and Fortran components built as well as a local R installation available. From f2b88979a92f631b81b1f28407e6beeed7e5e792 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 28 Apr 2016 11:19:49 -0700 Subject: [PATCH 146/198] Bump scala version to 2.11.6 andd gfortran, binutils, and R toolcahin to required packages --- .travis.yml | 10 +++++++++- build.sbt | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a4bcf53..b41400e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,15 @@ cache: directories: - $HOME/.ivy2 scala: - - 2.10.4 + - 2.11.6 +apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gfortran + - gcc + - binutils + - python-pip before_install: - pip install --user codecov after_success: diff --git a/build.sbt b/build.sbt index 05c6f67..8e3b67b 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,7 @@ publishMavenStyle := true version := "0.0.1" -scalaVersion := "2.10.4" +scalaVersion := "2.11.6" crossScalaVersions := Seq("2.10.4", "2.11.6") From cbab77602671c3c54207cd6be59d61c1c0a0b050 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 28 Apr 2016 11:22:20 -0700 Subject: [PATCH 147/198] Add Imap package --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index b41400e..6ade4ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ apt: - gcc - binutils - python-pip +r_packages: + - Imap before_install: - pip install --user codecov after_success: From a5a8091d8c981026cd4a1cb652bbf5c8cdf41414 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 28 Apr 2016 13:03:26 -0700 Subject: [PATCH 148/198] Add the two sum files --- src/main/c/sum.c | 9 +++++++++ src/main/c/sum.h | 1 + 2 files changed, 10 insertions(+) create mode 100644 src/main/c/sum.c create mode 100644 src/main/c/sum.h diff --git a/src/main/c/sum.c b/src/main/c/sum.c new file mode 100644 index 0000000..29bdf71 --- /dev/null +++ b/src/main/c/sum.c @@ -0,0 +1,9 @@ +#include "sum.h" + +int sum(int[] input, int num_elem) { + int ret = 0; + for (int c = 0; c < num_elem; c++) { + ret += input[c]; + } + return ret; +} diff --git a/src/main/c/sum.h b/src/main/c/sum.h new file mode 100644 index 0000000..d5c4167 --- /dev/null +++ b/src/main/c/sum.h @@ -0,0 +1 @@ +int sum(int[] input, int num_elem); From 5666b4567fb1a70dddad23f171ca99e78cc8a86a Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Thu, 28 Apr 2016 13:07:07 -0700 Subject: [PATCH 149/198] Add JniNative plugin from Jakob Odersky --- build.sbt | 4 ++++ project/plugins.sbt | 2 ++ 2 files changed, 6 insertions(+) diff --git a/build.sbt b/build.sbt index 8e3b67b..3f99b3d 100644 --- a/build.sbt +++ b/build.sbt @@ -77,3 +77,7 @@ mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) => case _ => MergeStrategy.first } } + +// JNI + +enablePlugins(JniNative) diff --git a/project/plugins.sbt b/project/plugins.sbt index b81ac59..0e3cce7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -9,3 +9,5 @@ addSbtPlugin("org.spark-packages" % "sbt-spark-package" % "0.2.2") //addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.3") + +addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.4.4") From 60baf28154a7577c66f4bcc10abac5a3d8d6d731 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Fri, 29 Apr 2016 12:31:43 -0700 Subject: [PATCH 150/198] Go back to 2.10.4 for hive thriftserver JAR :( --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 3f99b3d..5d1eccb 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,7 @@ publishMavenStyle := true version := "0.0.1" -scalaVersion := "2.11.6" +scalaVersion := "2.10.4" crossScalaVersions := Seq("2.10.4", "2.11.6") From 710299ce09611142587188c3a2dabb0e570d7177 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 1 May 2016 14:07:31 -0700 Subject: [PATCH 151/198] Start adding sum wrapper example --- src/main/c/sum_wrapper.c | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/c/sum_wrapper.c diff --git a/src/main/c/sum_wrapper.c b/src/main/c/sum_wrapper.c new file mode 100644 index 0000000..9419ef0 --- /dev/null +++ b/src/main/c/sum_wrapper.c @@ -0,0 +1,4 @@ +#include "sum.h" +#include "JavaCode.h" + +.... From 79162cdc8442e9c49ea14b42f0893afaef745d6f Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 1 May 2016 22:40:22 -0700 Subject: [PATCH 152/198] Update JNI build using Jakob's library (thnx\!) --- README.md | 2 +- build.sbt | 4 ++ src/CMakeLists.txt | 45 +++++++++++++ ...ghperformancespark_example_SumJNI_SumJNI.h | 21 ++++++ src/main/c/sum.c | 6 +- src/main/c/sum.h | 2 +- src/main/c/sum_wrapper.c | 5 +- .../dataframe/MixedDataset.scala_back | 67 +++++++++++++++++++ .../native/SumJNI.scala | 5 ++ 9 files changed, 149 insertions(+), 8 deletions(-) create mode 100644 src/CMakeLists.txt create mode 100644 src/main/c/include/com_highperformancespark_example_SumJNI_SumJNI.h create mode 100644 src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala_back create mode 100644 src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala diff --git a/README.md b/README.md index 22cad61..551928f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Examples for High Performance Spark # Building -Most of the examples can be built with sbt, the C and Fortran components depend on gcc and g77 respectively. +Most of the examples can be built with sbt, the C and Fortran components depend on gcc, g77, and cmake. # Tests diff --git a/build.sbt b/build.sbt index 5d1eccb..8f76cd9 100644 --- a/build.sbt +++ b/build.sbt @@ -81,3 +81,7 @@ mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) => // JNI enablePlugins(JniNative) + +sourceDirectory in nativeCompile in Compile := sourceDirectory.value + +nativeLibraryPath in Compile := "com/highperformancespark/examples" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..568fbae --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 2.6) + +# Define project and related variables +# +project (high-performance-spark) + +# Set versions and library name +# Note: major version will be appended to library name +# +set (VERSION_MAJOR 0) +set (VERSION_MINOR 1) +set (VERSION_PATCH 0) +set (LIB_NAME flow${VERSION_MAJOR}) + +# Command-line options +# +# required by sbt-jni to install binaries to correct places +set (LIB_INSTALL_DIR lib CACHE PATH "Path in which to install libraries (Autoconf equivalent to --libdir).") +# required by sbt-jni to disable versioned libraries +set (ENABLE_VERSIONED_LIB ON CACHE BOOLEAN "Generate versioned library files and symlinks.") + +# Setup JNI +find_package(JNI REQUIRED) +if (JNI_FOUND) + message (STATUS "JNI include directories: ${JNI_INCLUDE_DIRS}") +endif() + +# Include directories +include_directories(include) +include_directories(${JNI_INCLUDE_DIRS}) + +# Setup main shared library +# Note: major version is appended to library name +add_library(${LIB_NAME} SHARED main/c/sum.c main/c/sum_wrapper.c) +if (ENABLE_VERSIONED_LIB) + set_target_properties( + ${LIB_NAME} + PROPERTIES + VERSION 0.${VERSION_MINOR}.${VERSION_PATCH} # major version always 0, it is included in name + SOVERSION 0 + ) +endif() + +# Installation targets +install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR}) diff --git a/src/main/c/include/com_highperformancespark_example_SumJNI_SumJNI.h b/src/main/c/include/com_highperformancespark_example_SumJNI_SumJNI.h new file mode 100644 index 0000000..9c587b2 --- /dev/null +++ b/src/main/c/include/com_highperformancespark_example_SumJNI_SumJNI.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_highperformancespark_example_SumJNI_SumJNI */ + +#ifndef _Included_com_highperformancespark_example_SumJNI_SumJNI +#define _Included_com_highperformancespark_example_SumJNI_SumJNI +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_highperformancespark_example_SumJNI_SumJNI + * Method: sum + * Signature: ([I)I + */ +JNIEXPORT jint JNICALL Java_com_highperformancespark_example_SumJNI_SumJNI_sum + (JNIEnv *, jobject, jintArray); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/main/c/sum.c b/src/main/c/sum.c index 29bdf71..f571aad 100644 --- a/src/main/c/sum.c +++ b/src/main/c/sum.c @@ -1,8 +1,8 @@ #include "sum.h" -int sum(int[] input, int num_elem) { - int ret = 0; - for (int c = 0; c < num_elem; c++) { +int sum(int input[], int num_elem) { + int c, ret = 0; + for (c = 0; c < num_elem; c++) { ret += input[c]; } return ret; diff --git a/src/main/c/sum.h b/src/main/c/sum.h index d5c4167..66531ce 100644 --- a/src/main/c/sum.h +++ b/src/main/c/sum.h @@ -1 +1 @@ -int sum(int[] input, int num_elem); +int sum(int input[], int num_elem); diff --git a/src/main/c/sum_wrapper.c b/src/main/c/sum_wrapper.c index 9419ef0..1aeb89b 100644 --- a/src/main/c/sum_wrapper.c +++ b/src/main/c/sum_wrapper.c @@ -1,4 +1,3 @@ #include "sum.h" -#include "JavaCode.h" - -.... +#include "include/com_highperformancespark_example_SumJNI_SumJNI.h" +#include diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala_back b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala_back new file mode 100644 index 0000000..cdae7c1 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala_back @@ -0,0 +1,67 @@ +/** + * A sample mixing relational & functional transformations with Datasets. + */ +package com.highperformancespark.examples.dataframe + +import org.apache.spark._ +import org.apache.spark.rdd.RDD +import org.apache.spark.sql._ +import org.apache.spark.sql.catalyst.expressions.aggregate._ +import org.apache.spark.sql.expressions._ +import org.apache.spark.sql.functions._ +import org.apache.spark.sql.types._ +// Additional imports for using HiveContext +import org.apache.spark.sql.hive._ +import org.apache.spark.sql.hive.thriftserver._ + +class MixedDataset(sqlCtx: SQLContext) { + import sqlCtx.implicits._ + + /** + * A sample function on a Dataset of RawPandas. + * This is contrived, since our reduction could also be done with SQL aggregates, but + * we can see the flexibility of being able to specify arbitrary Scala code. + */ + def happyPandaSums(ds: Dataset[RawPanda]): Double = { + ds.toDF().filter($"happy" === true).as[RawPanda]. + select($"attributes"(0).as[Double]). + reduce((x, y) => x + y) + } + + /** + * Functional map + Dataset, sums the positive attributes for the pandas + */ + def funMap(ds: Dataset[RawPanda]): Dataset[Double] = { + ds.map{rp => rp.attributes.filter(_ > 0).sum} + } + + /** + * Illustrate how we make typed queries, using some of the float properties to produce boolean + * values. + */ + def typedQueryExample(ds: Dataset[RawPanda]): Dataset[Double] = { + ds.select($"attributes"(0).as[Double]) + } + + /** + * Illustrate converting a Dataset to an RDD + */ + def toRDD(ds: Dataset[RawPanda]): RDD[RawPanda] = { + ds.rdd + } + + /** + * Illustrate converting a Dataset to a DataFrame + */ + def toDF(ds: Dataset[RawPanda]): DataFrame = { + ds.toDF() + } + + /** + * Illustrate DataFrame to Dataset. Its important to note that if the schema does not match what + * is expected by the Dataset this fails fast. + */ + def fromDF(df: DataFrame): Dataset[RawPanda] = { + df.as[RawPanda] + } +} diff --git a/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala b/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala new file mode 100644 index 0000000..9a027e4 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala @@ -0,0 +1,5 @@ +package com.highperformancespark.example.SumJNI + +class SumJNI { + @native def sum(n: Array[Int]): Int +} From 92b7c3bb099c9ea0f209bba11a847181a82a2bdc Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Sun, 1 May 2016 23:00:47 -0700 Subject: [PATCH 153/198] Plumb through the call --- src/main/c/sum_wrapper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/c/sum_wrapper.c b/src/main/c/sum_wrapper.c index 1aeb89b..0137a8f 100644 --- a/src/main/c/sum_wrapper.c +++ b/src/main/c/sum_wrapper.c @@ -1,3 +1,15 @@ #include "sum.h" #include "include/com_highperformancespark_example_SumJNI_SumJNI.h" #include + +/* + * Class: com_highperformancespark_example_SumJNI_SumJNI + * Method: sum + * Signature: ([I)I + */ +JNIEXPORT jint JNICALL Java_com_highperformancespark_example_SumJNI_SumJNI_sum +(JNIEnv *env, jobject obj, jintArray ja) { + jsize size = (*env)->GetArrayLength(env, ja); + jint *a = (*env)->GetIntArrayElements(env, ja, 0); + return sum(a, size); +} From 1a5014421c0fdf8f1d6c7796d36541b874d4379a Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 9 May 2016 15:10:21 -0700 Subject: [PATCH 154/198] Add fromRDD example for datasets --- .../dataframe/MixedDataset.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala index 4b9b910..83e4d86 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/MixedDataset.scala @@ -102,6 +102,15 @@ class MixedDataset(sqlCtx: SQLContext) { result } + //tag::fromRDD[] + /** + * Illustrate converting an RDD to DS + */ + def fromRDD(rdd: RDD[RawPanda]): Dataset[RawPanda] = { + rdd.toDS + } + + //end::fromRDD[] //tag::toRDDDF[] /** From e1ebf785f850309c94db6bd650e80d0c4af82229 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 9 May 2016 18:01:24 -0700 Subject: [PATCH 155/198] More JNI stuff - but jodersky is updating his JNI wrapper so lets chill :) --- src/CMakeLists.txt | 2 +- ...ghperformancespark_example_SumJNI_SumJNI.h | 21 ------------------- src/main/c/sum_wrapper.c | 7 ++++--- .../native/NativeExample.scala | 9 ++++++++ .../native/StandAlone.scala | 8 +++++++ .../native/SumJNI.scala | 2 +- 6 files changed, 23 insertions(+), 26 deletions(-) delete mode 100644 src/main/c/include/com_highperformancespark_example_SumJNI_SumJNI.h create mode 100644 src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala create mode 100644 src/main/scala/com/high-performance-spark-examples/native/StandAlone.scala diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 568fbae..a352b1c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,7 +10,7 @@ project (high-performance-spark) set (VERSION_MAJOR 0) set (VERSION_MINOR 1) set (VERSION_PATCH 0) -set (LIB_NAME flow${VERSION_MAJOR}) +set (LIB_NAME highPerformanceSpark${VERSION_MAJOR}) # Command-line options # diff --git a/src/main/c/include/com_highperformancespark_example_SumJNI_SumJNI.h b/src/main/c/include/com_highperformancespark_example_SumJNI_SumJNI.h deleted file mode 100644 index 9c587b2..0000000 --- a/src/main/c/include/com_highperformancespark_example_SumJNI_SumJNI.h +++ /dev/null @@ -1,21 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class com_highperformancespark_example_SumJNI_SumJNI */ - -#ifndef _Included_com_highperformancespark_example_SumJNI_SumJNI -#define _Included_com_highperformancespark_example_SumJNI_SumJNI -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: com_highperformancespark_example_SumJNI_SumJNI - * Method: sum - * Signature: ([I)I - */ -JNIEXPORT jint JNICALL Java_com_highperformancespark_example_SumJNI_SumJNI_sum - (JNIEnv *, jobject, jintArray); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/main/c/sum_wrapper.c b/src/main/c/sum_wrapper.c index 0137a8f..a499d3e 100644 --- a/src/main/c/sum_wrapper.c +++ b/src/main/c/sum_wrapper.c @@ -1,13 +1,14 @@ #include "sum.h" -#include "include/com_highperformancespark_example_SumJNI_SumJNI.h" +#include "include/com_highperformancespark_examples_ffi_SumJNI.h" +#include #include /* - * Class: com_highperformancespark_example_SumJNI_SumJNI + * Class: com_highperformancespark_examples_ffi_SumJNI * Method: sum * Signature: ([I)I */ -JNIEXPORT jint JNICALL Java_com_highperformancespark_example_SumJNI_SumJNI_sum +JNIEXPORT jint JNICALL Java_com_highperformancespark_examples_ffi_SumJNI_sum (JNIEnv *env, jobject obj, jintArray ja) { jsize size = (*env)->GetArrayLength(env, ja); jint *a = (*env)->GetIntArrayElements(env, ja, 0); diff --git a/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala b/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala new file mode 100644 index 0000000..198518d --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala @@ -0,0 +1,9 @@ +package com.highperformancespark.examples.ffi + +import org.apache.spark.rdd.RDD + +object NativeExample { + def jniSum(input: RDD[(String, Array[Int])]): RDD[(String, Int)] = { + input.mapValues(values => new SumJNI().sum(values)) + } +} diff --git a/src/main/scala/com/high-performance-spark-examples/native/StandAlone.scala b/src/main/scala/com/high-performance-spark-examples/native/StandAlone.scala new file mode 100644 index 0000000..7a83aa4 --- /dev/null +++ b/src/main/scala/com/high-performance-spark-examples/native/StandAlone.scala @@ -0,0 +1,8 @@ +package com.highperformancespark.examples.ffi + +object StandAlone { + def main(args: Array[String]) { + System.loadLibrary("highPerformanceSpark0") + println(new SumJNI().sum(Array(1,2,3))) + } +} diff --git a/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala b/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala index 9a027e4..fae79d4 100644 --- a/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala +++ b/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala @@ -1,4 +1,4 @@ -package com.highperformancespark.example.SumJNI +package com.highperformancespark.examples.ffi class SumJNI { @native def sum(n: Array[Int]): Int From 77b53a206df0b65f03264020b5845d49b65b69bf Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 18:30:34 -0700 Subject: [PATCH 156/198] Add more test magic --- ...highperformancespark_examples_ffi_SumJNI.h | 21 +++++++++++ .../native/NativeExample.scala | 37 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/main/c/include/com_highperformancespark_examples_ffi_SumJNI.h create mode 100644 src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala diff --git a/src/main/c/include/com_highperformancespark_examples_ffi_SumJNI.h b/src/main/c/include/com_highperformancespark_examples_ffi_SumJNI.h new file mode 100644 index 0000000..75be264 --- /dev/null +++ b/src/main/c/include/com_highperformancespark_examples_ffi_SumJNI.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_highperformancespark_examples_ffi_SumJNI */ + +#ifndef _Included_com_highperformancespark_examples_ffi_SumJNI +#define _Included_com_highperformancespark_examples_ffi_SumJNI +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_highperformancespark_examples_ffi_SumJNI + * Method: sum + * Signature: ([I)I + */ +JNIEXPORT jint JNICALL Java_com_highperformancespark_examples_ffi_SumJNI_sum + (JNIEnv *, jobject, jintArray); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala b/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala new file mode 100644 index 0000000..6d1c712 --- /dev/null +++ b/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala @@ -0,0 +1,37 @@ +/** + * Test our simple JNI + */ +package com.highperformancespark.examples.ffi + +import com.holdenkarau.spark.testing._ +import org.scalacheck.{Arbitrary, Gen} +import org.scalacheck.Prop.forAll +import org.scalatest.FunSuite +import org.scalatest.prop.Checkers +import org.scalatest.Matchers._ + +class NativeExampleSuite extends FunSuite with SharedSparkContext with Checkers { + test("local sum") { + //def magic2() { + val input = Array(1, 2, 3) + val sumMagic = new SumJNI() + val result = sumMagic.sum(input) + val expected = 6 + result === expected + } + + // test("super simple test") { + def magic() { + val input = sc.parallelize(List(("hi", Array(1, 2, 3)))) + val result = NativeExample.jniSum(input).collect() + val expected = List(("hi", 6)) + result === expected + } + + test("native call should find sum correctly") { + val property = forAll(RDDGenerator.genRDD[(String, Array[Int])](sc)) { + rdd => rdd.mapValues(_.sum).collect() === NativeExample.jniSum(rdd) + } + //check(property) + } +} From fabc1add5e8eb038f6f756de46b6d01b5024c8ec Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 19:31:42 -0700 Subject: [PATCH 157/198] Start adding a PySpark example --- .travis.yml | 11 ++++++++- high_performance_pyspark/SQLLineage.py | 33 ++++++++++++++++++++++++++ high_performance_pyspark/__init__.py | 25 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 high_performance_pyspark/SQLLineage.py create mode 100644 high_performance_pyspark/__init__.py diff --git a/.travis.yml b/.travis.yml index a4bcf53..988a182 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,19 @@ sudo: false cache: directories: - $HOME/.ivy2 + - $HOME/spark scala: - 2.10.4 before_install: - - pip install --user codecov + - pip install --user codecov unittest2 +script: + - "export SPARK_CONF_DIR=./log4j/" + - sbt compile test + - "[ -f spark] || mkdir spark && wget http://d3kbcqa49mib13.cloudfront.net/spark-1.6.1-bin-hadoop2.6.tgz && cd .." + - "tar -xvf ./spark/spark-1.6.1-bin-hadoop2.6.tgz" + - "export SPARK_HOME=`pwd`/spark-1.6.1-bin-hadoop2.6/" + - "export PYTHONPATH=$SPARK_HOME/python:`ls -1 $SPARK_HOME/python/lib/py4j-*-src.zip`:$PYTHONPATH" + - "nosetests --with-doctest --doctest-options=+ELLIPSIS --logging-level=INFO --detailed-errors --verbosity=2 --with-coverage --cover-html-dir=./htmlcov" after_success: # For now no coverage report - codecov \ No newline at end of file diff --git a/high_performance_pyspark/SQLLineage.py b/high_performance_pyspark/SQLLineage.py new file mode 100644 index 0000000..b3f86a9 --- /dev/null +++ b/high_performance_pyspark/SQLLineage.py @@ -0,0 +1,33 @@ +""" +>>> from pyspark.context import SparkContext +>>> from pyspark.sql import SQLContext, Row, DataFrame +>>> sc = SparkContext('local', 'test') +... +>>> sc.setLogLevel("ERROR") +>>> sqlCtx = SQLContext(sc) +... +>>> rdd = sc.parallelize(range(1, 100)).map(lambda x: Row(i = x)) +>>> df = rdd.toDF() +>>> df2 = cutLineage(df) +>>> df.head() == df2.head() +True +>>> df.schema == df2.schema +True +""" + +from pyspark.sql import DataFrame + +def cutLineage(df): + """ + Cut the lineage of a DataFrame - used for iterative algorithms + + .. Note: This uses internal members and may break between versions + """ + jRDD = df._jdf.toJavaRDD() + jSchema = df._jdf.schema() + jRDD.cache() + sqlCtx = df.sql_ctx + javaSqlCtx = sqlCtx._jsqlContext + newJavaDF = javaSqlCtx.createDataFrame(jRDD, jSchema) + newDF = DataFrame(newJavaDF, sqlCtx) + return newDF diff --git a/high_performance_pyspark/__init__.py b/high_performance_pyspark/__init__.py new file mode 100644 index 0000000..7741593 --- /dev/null +++ b/high_performance_pyspark/__init__.py @@ -0,0 +1,25 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +""" +Python version of selected examples from High Performance Spark +""" + +import os +import sys + From cc6230a97205a16b7c787854052c5202ff93f745 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 19:37:47 -0700 Subject: [PATCH 158/198] Add tags --- high_performance_pyspark/SQLLineage.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/high_performance_pyspark/SQLLineage.py b/high_performance_pyspark/SQLLineage.py index b3f86a9..4a4a9f8 100644 --- a/high_performance_pyspark/SQLLineage.py +++ b/high_performance_pyspark/SQLLineage.py @@ -17,6 +17,7 @@ from pyspark.sql import DataFrame +#tag::cutLineage[] def cutLineage(df): """ Cut the lineage of a DataFrame - used for iterative algorithms @@ -31,3 +32,4 @@ def cutLineage(df): newJavaDF = javaSqlCtx.createDataFrame(jRDD, jSchema) newDF = DataFrame(newJavaDF, sqlCtx) return newDF +#end::cutLineage[] From 70815349764d2e7739e4ebcdd993d3a531c268cd Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 19:47:20 -0700 Subject: [PATCH 159/198] Add more packages and cache them --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 988a182..6e47319 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,11 @@ cache: directories: - $HOME/.ivy2 - $HOME/spark + - $HOME/.cache/pip scala: - 2.10.4 before_install: - - pip install --user codecov unittest2 + - pip install --user codecov unittest2 nose pep8 pylint scipy pandas script: - "export SPARK_CONF_DIR=./log4j/" - sbt compile test From d7fd8783353fee700fd262bd17fa10e81c3b4bfd Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 20:01:20 -0700 Subject: [PATCH 160/198] urgh pip install pandas is sad pandas --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6e47319..4154952 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ language: scala sudo: false +apt: + - pandas + - numpy cache: directories: - $HOME/.ivy2 @@ -8,7 +11,7 @@ cache: scala: - 2.10.4 before_install: - - pip install --user codecov unittest2 nose pep8 pylint scipy pandas + - pip install --user codecov unittest2 nose pep8 pylint --download-cache $HOME/.pip-cache script: - "export SPARK_CONF_DIR=./log4j/" - sbt compile test From ed5380161789c79d2337540fd1d8e1bdf0f13640 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 20:03:47 -0700 Subject: [PATCH 161/198] Cache sbt launchers --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 4154952..626b694 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ cache: - $HOME/.ivy2 - $HOME/spark - $HOME/.cache/pip + - $HOME/.sbt/launchers scala: - 2.10.4 before_install: From 4b065ac003c220da51c5940ef96bea12812e4d0f Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 20:49:03 -0700 Subject: [PATCH 162/198] Remove trailing slash --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 626b694..6f995d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ script: - sbt compile test - "[ -f spark] || mkdir spark && wget http://d3kbcqa49mib13.cloudfront.net/spark-1.6.1-bin-hadoop2.6.tgz && cd .." - "tar -xvf ./spark/spark-1.6.1-bin-hadoop2.6.tgz" - - "export SPARK_HOME=`pwd`/spark-1.6.1-bin-hadoop2.6/" + - "export SPARK_HOME=`pwd`/spark-1.6.1-bin-hadoop2.6" - "export PYTHONPATH=$SPARK_HOME/python:`ls -1 $SPARK_HOME/python/lib/py4j-*-src.zip`:$PYTHONPATH" - "nosetests --with-doctest --doctest-options=+ELLIPSIS --logging-level=INFO --detailed-errors --verbosity=2 --with-coverage --cover-html-dir=./htmlcov" after_success: From 37527b5ef993c43cc45afb4b179c0af09152e973 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 21:29:38 -0700 Subject: [PATCH 163/198] fix fetch --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6f995d2..8569ccd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,9 @@ before_install: script: - "export SPARK_CONF_DIR=./log4j/" - sbt compile test - - "[ -f spark] || mkdir spark && wget http://d3kbcqa49mib13.cloudfront.net/spark-1.6.1-bin-hadoop2.6.tgz && cd .." + - "[ -f spark] || mkdir spark && cd spark && wget http://d3kbcqa49mib13.cloudfront.net/spark-1.6.1-bin-hadoop2.6.tgz && cd .." - "tar -xvf ./spark/spark-1.6.1-bin-hadoop2.6.tgz" - - "export SPARK_HOME=`pwd`/spark-1.6.1-bin-hadoop2.6" + - "export SPARK_HOME=`pwd`/spark-1.6.1-bin-hadoop2.6.tgz" - "export PYTHONPATH=$SPARK_HOME/python:`ls -1 $SPARK_HOME/python/lib/py4j-*-src.zip`:$PYTHONPATH" - "nosetests --with-doctest --doctest-options=+ELLIPSIS --logging-level=INFO --detailed-errors --verbosity=2 --with-coverage --cover-html-dir=./htmlcov" after_success: From 97ac4e7f3cddeede528a711f59cfb29c6c46365c Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 21:45:29 -0700 Subject: [PATCH 164/198] Wait crap I'm an idiot --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8569ccd..67be809 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,8 @@ script: - "export SPARK_CONF_DIR=./log4j/" - sbt compile test - "[ -f spark] || mkdir spark && cd spark && wget http://d3kbcqa49mib13.cloudfront.net/spark-1.6.1-bin-hadoop2.6.tgz && cd .." - - "tar -xvf ./spark/spark-1.6.1-bin-hadoop2.6.tgz" - - "export SPARK_HOME=`pwd`/spark-1.6.1-bin-hadoop2.6.tgz" + - "tar -xf ./spark/spark-1.6.1-bin-hadoop2.6.tgz" + - "export SPARK_HOME=`pwd`/spark-1.6.1-bin-hadoop2.6" - "export PYTHONPATH=$SPARK_HOME/python:`ls -1 $SPARK_HOME/python/lib/py4j-*-src.zip`:$PYTHONPATH" - "nosetests --with-doctest --doctest-options=+ELLIPSIS --logging-level=INFO --detailed-errors --verbosity=2 --with-coverage --cover-html-dir=./htmlcov" after_success: From a143793ea559628ce3a55c58fb09c06bc5a0d64a Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 16 May 2016 21:58:08 -0700 Subject: [PATCH 165/198] pre 2.0 support --- high_performance_pyspark/SQLLineage.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/high_performance_pyspark/SQLLineage.py b/high_performance_pyspark/SQLLineage.py index 4a4a9f8..c9d77a2 100644 --- a/high_performance_pyspark/SQLLineage.py +++ b/high_performance_pyspark/SQLLineage.py @@ -28,7 +28,10 @@ def cutLineage(df): jSchema = df._jdf.schema() jRDD.cache() sqlCtx = df.sql_ctx - javaSqlCtx = sqlCtx._jsqlContext + try: + javaSqlCtx = sqlCtx._jsqlContext + except: + javaSqlCtx = sqlCtx._ssql_ctx newJavaDF = javaSqlCtx.createDataFrame(jRDD, jSchema) newDF = DataFrame(newJavaDF, sqlCtx) return newDF From 53dc82adb5acadc9efd90102b8d88a3687bbeb0e Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 17 May 2016 11:15:02 -0700 Subject: [PATCH 166/198] Add log4j.properties file --- conf/log4j.properties | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 conf/log4j.properties diff --git a/conf/log4j.properties b/conf/log4j.properties new file mode 100644 index 0000000..e90a817 --- /dev/null +++ b/conf/log4j.properties @@ -0,0 +1,40 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Set everything to be logged to the console +log4j.rootCategory=ERROR, console +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.target=System.err +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n + +# Set the default spark-shell log level to WARN. When running the spark-shell, the +# log level for this class is used to overwrite the root logger's log level, so that +# the user can have different defaults for the shell and regular Spark apps. +log4j.logger.org.apache.spark.repl.Main=ERROR + +# Settings to quiet third party logs that are too verbose +log4j.logger.org.spark-project.jetty=ERROR +log4j.logger.org.spark-project.jetty.util.component.AbstractLifeCycle=ERROR +log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=INFO +log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO +log4j.logger.org.apache.parquet=ERROR +log4j.logger.parquet=ERROR + +# SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support +log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL +log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR From ba4e0d4121ca6a339aa9c77ec09e95dc63f2bb41 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 17 May 2016 12:14:50 -0700 Subject: [PATCH 167/198] Somewhat hacky update to Scala 2.11 (will be better once 2.0 comes out) and update jodersky's macro package --- build.sbt | 21 +++++++++++-------- project/plugins.sbt | 10 ++++++++- .../native/NativeExample.scala | 1 + 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/build.sbt b/build.sbt index 8f76cd9..e2328f7 100644 --- a/build.sbt +++ b/build.sbt @@ -6,16 +6,17 @@ publishMavenStyle := true version := "0.0.1" -scalaVersion := "2.10.4" +scalaVersion := "2.11.6" -crossScalaVersions := Seq("2.10.4", "2.11.6") +crossScalaVersions := Seq("2.11.6") javacOptions ++= Seq("-source", "1.7", "-target", "1.7") -sparkVersion := "1.6.0" +sparkVersion := "1.6.1" //tag::sparkComponents[] -sparkComponents ++= Seq("core", "streaming", "hive-thriftserver", "mllib") +// TODO(Holden): re-add hive-thriftserver post Spark 2.0 +sparkComponents ++= Seq("core", "streaming", "mllib") //end::sparkComponents[] //tag::addSQLHiveComponent[] sparkComponents ++= Seq("sql", "hive") @@ -33,10 +34,12 @@ libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % "2.2.1", "org.scalacheck" %% "scalacheck" % "1.12.4", "junit" % "junit" % "4.10", + // Temporary hack until Spark 2.0 + "org.apache.spark" % "spark-hive-thriftserver_2.10" % "1.6.1" intransitive(), //tag::sparkCSV[] "com.databricks" % "spark-csv_2.10" % "1.3.0", //end::sparkCSV[] - "com.holdenkarau" % "spark-testing-base_2.10" % "1.5.1_0.2.1", + "com.holdenkarau" % "spark-testing-base_2.11" % "1.6.1_0.3.2", "org.eclipse.jetty" % "jetty-util" % "9.3.2.v20150730", "org.codehaus.jackson" % "jackson-mapper-asl" % "1.8.8", "com.novocode" % "junit-interface" % "0.10" % "test->default") @@ -59,7 +62,9 @@ resolvers ++= Seq( "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/", "Second Typesafe repo" at "http://repo.typesafe.com/typesafe/maven-releases/", "Mesosphere Public Repository" at "http://downloads.mesosphere.io/maven", - Resolver.sonatypeRepo("public") + Resolver.sonatypeRepo("public"), + Resolver.bintrayRepo("jodersky", "sbt-jni-macros"), + "jodersky" at "https://dl.bintray.com/jodersky/maven/" ) licenses := Seq("Apache License 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.html")) @@ -82,6 +87,4 @@ mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) => enablePlugins(JniNative) -sourceDirectory in nativeCompile in Compile := sourceDirectory.value - -nativeLibraryPath in Compile := "com/highperformancespark/examples" +sourceDirectory in nativeCompile := sourceDirectory.value diff --git a/project/plugins.sbt b/project/plugins.sbt index 0e3cce7..a14c726 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,12 +2,20 @@ addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.6.0") resolvers += "sonatype-releases" at "https://oss.sonatype.org/content/repositories/releases/" +resolvers += "sonatype-snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/" + + resolvers += "Spark Package Main Repo" at "https://dl.bintray.com/spark-packages/maven" +// Temporary hack for bintray being sad + +resolvers += Resolver.bintrayRepo("jodersky", "sbt-jni-macros") +resolvers += "jodersky" at "https://dl.bintray.com/jodersky/maven/" + addSbtPlugin("org.spark-packages" % "sbt-spark-package" % "0.2.2") //addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.3") -addSbtPlugin("ch.jodersky" % "sbt-jni" % "0.4.4") +addSbtPlugin("ch.jodersky" % "sbt-jni" % "1.0.0-RC2") diff --git a/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala b/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala index 198518d..c601467 100644 --- a/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala +++ b/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala @@ -2,6 +2,7 @@ package com.highperformancespark.examples.ffi import org.apache.spark.rdd.RDD +@nativeLoader("libhighPerformanceSpark0") object NativeExample { def jniSum(input: RDD[(String, Array[Int])]): RDD[(String, Int)] = { input.mapValues(values => new SumJNI().sum(values)) From 0c94c7dfddeb4f70ba4570eb71f27db701bd223a Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 17 May 2016 12:18:26 -0700 Subject: [PATCH 168/198] Fix sample scala code --- .../high-performance-spark-examples/native/NativeExample.scala | 2 ++ .../high-performance-spark-examples/wordcount/WordCount.scala | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala b/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala index c601467..c8e100e 100644 --- a/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala +++ b/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala @@ -2,6 +2,8 @@ package com.highperformancespark.examples.ffi import org.apache.spark.rdd.RDD +import ch.jodersky.jni.nativeLoader + @nativeLoader("libhighPerformanceSpark0") object NativeExample { def jniSum(input: RDD[(String, Array[Int])]): RDD[(String, Int)] = { diff --git a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala index 89746b6..c89653a 100644 --- a/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala +++ b/src/main/scala/com/high-performance-spark-examples/wordcount/WordCount.scala @@ -31,7 +31,8 @@ object WordCount { //tag::wordCountStopwords[] def withStopWordsFiltered(rdd : RDD[String], illegalTokens : Array[Char], stopWords : Set[String]): RDD[(String, Int)] = { - val tokens: RDD[String] = rdd.flatMap(_.split(illegalTokens ++ Array[Char](' ')). + val seperators = illegalTokens ++ Array[Char](' ') + val tokens: RDD[String] = rdd.flatMap(_.split(seperators). map(_.trim.toLowerCase)) val words = tokens.filter(token => !stopWords.contains(token) && (token.length > 0) ) From 8938e28cda848c0e19fc6eda6254b01916e4694d Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 17 May 2016 12:22:03 -0700 Subject: [PATCH 169/198] Update to new base makefile using nativeInit CMake --- src/CMakeLists.txt | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a352b1c..4d3442b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,10 @@ +################################################################ +# A minimal CMake file that is compatible with sbt-jni # +# # +# All settings required by sbt-jni have been marked so, please # +# add/modify/remove settings to build your specific library. # +################################################################ + cmake_minimum_required(VERSION 2.6) # Define project and related variables @@ -5,19 +12,20 @@ cmake_minimum_required(VERSION 2.6) project (high-performance-spark) # Set versions and library name -# Note: major version will be appended to library name +# (required by sbt-jni) please use semantic versioning # set (VERSION_MAJOR 0) -set (VERSION_MINOR 1) +set (VERSION_MINOR 0) set (VERSION_PATCH 0) -set (LIB_NAME highPerformanceSpark${VERSION_MAJOR}) +# (required by sbt-jni) major version will always be appended to library name +set (LIB_NAME ${CMAKE_PROJECT_NAME}${VERSION_MAJOR}) # Command-line options # -# required by sbt-jni to install binaries to correct places -set (LIB_INSTALL_DIR lib CACHE PATH "Path in which to install libraries (Autoconf equivalent to --libdir).") -# required by sbt-jni to disable versioned libraries -set (ENABLE_VERSIONED_LIB ON CACHE BOOLEAN "Generate versioned library files and symlinks.") +# (set by sbt-jni) +set (LIB_INSTALL_DIR lib CACHE PATH "Path in which to install libraries (equivalent to Autoconf --libdir).") +# (set by sbt-jni) +set (LIB_ENABLE_MINOR_VERSIONS ON CACHE BOOLEAN "Build libraries with minor and patch versions appended.") # Setup JNI find_package(JNI REQUIRED) @@ -26,20 +34,31 @@ if (JNI_FOUND) endif() # Include directories +include_directories(.) +include_directories(./main/c) include_directories(include) include_directories(${JNI_INCLUDE_DIRS}) # Setup main shared library -# Note: major version is appended to library name -add_library(${LIB_NAME} SHARED main/c/sum.c main/c/sum_wrapper.c) -if (ENABLE_VERSIONED_LIB) +file(GLOB LIB_SRC + "*.c" + "*.cpp" + "./main/c/*.c" + "./main/c/*.cpp" +) +add_library(${LIB_NAME} SHARED ${LIB_SRC}) + +# By default, in a regular build, minor and patch versions are added to the generated files. +# When built through sbt-jni however, LIB_ENABLE_MINOR_VERSIONS is deactivated and only a +# major-versioned library file is built. +if (LIB_ENABLE_MINOR_VERSIONS) set_target_properties( ${LIB_NAME} PROPERTIES - VERSION 0.${VERSION_MINOR}.${VERSION_PATCH} # major version always 0, it is included in name + VERSION 0.${VERSION_MINOR}.${VERSION_PATCH} # major version always 0, it is included in library name SOVERSION 0 ) endif() # Installation targets -install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR}) +install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${LIB_INSTALL_DIR}) From 229af6102f49022f1a23837a8d0b37441dbad138 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 17 May 2016 13:03:28 -0700 Subject: [PATCH 170/198] Update to new Arbitraryt generator --- .../high-performance-spark-examples/native/NativeExample.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala b/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala index 6d1c712..99ad773 100644 --- a/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala +++ b/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala @@ -29,7 +29,7 @@ class NativeExampleSuite extends FunSuite with SharedSparkContext with Checkers } test("native call should find sum correctly") { - val property = forAll(RDDGenerator.genRDD[(String, Array[Int])](sc)) { + val property = forAll(RDDGenerator.genRDD(sc)(Arbitrary.arbitrary[(String, Array[Int])])) { rdd => rdd.mapValues(_.sum).collect() === NativeExample.jniSum(rdd) } //check(property) From fc3332c76079d2974e046235936c6cc5de0084a9 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 17 May 2016 14:26:57 -0700 Subject: [PATCH 171/198] Use native loader magics --- project/plugins.sbt | 2 +- src/main/c/sum.h | 5 +++++ .../native/NativeExample.scala | 3 --- .../com/high-performance-spark-examples/native/SumJNI.scala | 3 +++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index a14c726..253c5a6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -18,4 +18,4 @@ addSbtPlugin("org.spark-packages" % "sbt-spark-package" % "0.2.2") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.3") -addSbtPlugin("ch.jodersky" % "sbt-jni" % "1.0.0-RC2") +addSbtPlugin("ch.jodersky" % "sbt-jni" % "1.0.0-RC3") diff --git a/src/main/c/sum.h b/src/main/c/sum.h index 66531ce..d04be96 100644 --- a/src/main/c/sum.h +++ b/src/main/c/sum.h @@ -1 +1,6 @@ +#ifndef _SUM_H +#define _SUM_H + int sum(int input[], int num_elem); + +#endif /* _SUM_H */ diff --git a/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala b/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala index c8e100e..198518d 100644 --- a/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala +++ b/src/main/scala/com/high-performance-spark-examples/native/NativeExample.scala @@ -2,9 +2,6 @@ package com.highperformancespark.examples.ffi import org.apache.spark.rdd.RDD -import ch.jodersky.jni.nativeLoader - -@nativeLoader("libhighPerformanceSpark0") object NativeExample { def jniSum(input: RDD[(String, Array[Int])]): RDD[(String, Int)] = { input.mapValues(values => new SumJNI().sum(values)) diff --git a/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala b/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala index fae79d4..de848bb 100644 --- a/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala +++ b/src/main/scala/com/high-performance-spark-examples/native/SumJNI.scala @@ -1,5 +1,8 @@ package com.highperformancespark.examples.ffi +import ch.jodersky.jni.nativeLoader + +@nativeLoader("high-performance-spark0") class SumJNI { @native def sum(n: Array[Int]): Int } From 2abc06f0731e32822d9ae315ae3aacfe8c0eadce Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 17 May 2016 15:25:16 -0700 Subject: [PATCH 172/198] Upgrade to latest test library and enable property check --- build.sbt | 2 +- .../dataframe/HappyPandasTest.scala | 22 +++++++++---------- .../native/NativeExample.scala | 12 +++++----- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/build.sbt b/build.sbt index e2328f7..fa10c8e 100644 --- a/build.sbt +++ b/build.sbt @@ -39,7 +39,7 @@ libraryDependencies ++= Seq( //tag::sparkCSV[] "com.databricks" % "spark-csv_2.10" % "1.3.0", //end::sparkCSV[] - "com.holdenkarau" % "spark-testing-base_2.11" % "1.6.1_0.3.2", + "com.holdenkarau" % "spark-testing-base_2.11" % "1.6.1_0.3.3", "org.eclipse.jetty" % "jetty-util" % "9.3.2.v20150730", "org.codehaus.jackson" % "jackson-mapper-asl" % "1.8.8", "com.novocode" % "junit-interface" % "0.10" % "test->default") diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala index daf80e2..f46a2f3 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala @@ -48,7 +48,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val expectedDf = pandaInfo.select((pandaInfo("attributes")(0) / pandaInfo("attributes")(1)).as("squishyness")) val result = HappyPandas.squishPandaFromPace(inputDF) - approxEqualDataFrames(expectedDf, result, 1E-5) + assertDataFrameApproximateEquals(expectedDf, result, 1E-5) } //tag::approxEqualDataFrames[] @@ -61,7 +61,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val inputDF = sqlContext.createDataFrame(pandaInfoList) val result = HappyPandas.happyPandasPercentage(inputDF) - approxEqualDataFrames(expectedDf, result, 1E-5) + assertDataFrameApproximateEquals(expectedDf, result, 1E-5) } //end::approxEqualDataFrames[] @@ -89,7 +89,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val expectedDF = createDF3(expectedRows, ("id", LongType, false), ("encodedType", IntegerType, false)) - equalDataFrames(expectedDF, resultDF) + assertDataFrameEquals(expectedDF, resultDF) } //tag::exactEqualDataFrames[] @@ -112,7 +112,7 @@ class HappyPandasTest extends DataFrameSuiteBase { PandaInfo(sandiego, "red", 2, 3)) val expectedDF = sqlContext.createDataFrame(expectedRows) - equalDataFrames(expectedDF, resultDF) + assertDataFrameEquals(expectedDF, resultDF) } test("test maxPandaSizePerZip") { @@ -125,7 +125,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val expectedDF = createDF(expectedRows, ("zip", StringType), ("max(pandaSize)", IntegerType)) - equalDataFrames(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) + assertDataFrameEquals(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) } test("test minMaxPandaSizePerZip"){ @@ -141,7 +141,7 @@ class HappyPandasTest extends DataFrameSuiteBase { ("min(pandaSize)", IntegerType), ("max(pandaSize)", IntegerType)) - equalDataFrames(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) + assertDataFrameEquals(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) } test("test minPandaSizeMaxAgePerZip") { @@ -157,7 +157,7 @@ class HappyPandasTest extends DataFrameSuiteBase { ("min(pandaSize)", IntegerType), ("max(age)", IntegerType)) - equalDataFrames(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) + assertDataFrameEquals(expectedDF.orderBy("zip"), resultDF.orderBy("zip")) } test("test complexAggPerZip") { @@ -173,7 +173,7 @@ class HappyPandasTest extends DataFrameSuiteBase { ("min(pandaSize)", IntegerType), ("avg(pandaSize)", DoubleType)) - approxEqualDataFrames(expectedDF.orderBy("zip"), resultDF.orderBy("zip"), 1e-5) + assertDataFrameApproximateEquals(expectedDF.orderBy("zip"), resultDF.orderBy("zip"), 1e-5) } @@ -184,7 +184,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val expectedRows = List(pandasList(0), pandasList(2)) val expectedDF = sqlContext.createDataFrame(expectedRows) - equalDataFrames(expectedDF, resultDF) + assertDataFrameEquals(expectedDF, resultDF) } test("test Order Pandas") { @@ -195,7 +195,7 @@ class HappyPandasTest extends DataFrameSuiteBase { pandasList(4), pandasList(1)) val expectedDF = sqlContext.createDataFrame(expectedRows) - equalDataFrames(expectedDF, resultDF) + assertDataFrameEquals(expectedDF, resultDF) } @@ -207,7 +207,7 @@ class HappyPandasTest extends DataFrameSuiteBase { val expectedDF = getExpectedPandasRelativeSize(inputPandaList, -10, 10) - approxEqualDataFrames(expectedDF.orderBy("name"), resultDF.orderBy("name"), 1e-5) + assertDataFrameApproximateEquals(expectedDF.orderBy("name"), resultDF.orderBy("name"), 1e-5) } private def getExpectedPandasRelativeSize(pandaList: List[Pandas], start: Int, end: Int):DataFrame = { diff --git a/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala b/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala index 99ad773..3a50e47 100644 --- a/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala +++ b/src/test/scala/com/high-performance-spark-examples/native/NativeExample.scala @@ -20,8 +20,7 @@ class NativeExampleSuite extends FunSuite with SharedSparkContext with Checkers result === expected } - // test("super simple test") { - def magic() { + test("super simple test") { val input = sc.parallelize(List(("hi", Array(1, 2, 3)))) val result = NativeExample.jniSum(input).collect() val expected = List(("hi", 6)) @@ -29,9 +28,12 @@ class NativeExampleSuite extends FunSuite with SharedSparkContext with Checkers } test("native call should find sum correctly") { - val property = forAll(RDDGenerator.genRDD(sc)(Arbitrary.arbitrary[(String, Array[Int])])) { - rdd => rdd.mapValues(_.sum).collect() === NativeExample.jniSum(rdd) + val property = forAll(RDDGenerator.genRDD[(String, Array[Int])](sc)(Arbitrary.arbitrary[(String, Array[Int])])) { + rdd => + val expected = rdd.mapValues(_.sum) + val result = NativeExample.jniSum(rdd) + RDDComparisons.compareWithOrder(expected, result).isEmpty } - //check(property) + check(property) } } From 6a85e157e77b8813888c6c87f6bd8589f7ce3e50 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 17 May 2016 22:53:29 -0700 Subject: [PATCH 173/198] Add cut lineage Scala example --- .../dataframe/HappyPandas.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index ff4c5bb..39271a9 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -267,6 +267,18 @@ object HappyPandas { //end::leftsemiJoin[] } + /** + * Cut the lineage of a DataFrame which has too long a query plan. + */ + def cutLineage(df: DataFrame): DataFrame = { + val sqlCtx = df.sqlContext + //tag::cutLineage[] + val rdd = df.rdd + rdd.cache() + sqlCtx.createDataFrame(rdd, df.schema) + //end::cutLineage[] + } + // Self join def selfJoin(df: DataFrame): DataFrame = { val sqlCtx = df.sqlContext From 7ad749d9b0d8fca0437544d83da8bce774d99654 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 18 May 2016 13:28:32 -0700 Subject: [PATCH 174/198] Add a simple version of the Python perf test --- build.sbt | 2 +- high_performance_pyspark/simple_perf_test.py | 87 +++++++++++++++++++ .../tools/GenerateScalingData.scala | 15 ++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 high_performance_pyspark/simple_perf_test.py diff --git a/build.sbt b/build.sbt index fa10c8e..297b18f 100644 --- a/build.sbt +++ b/build.sbt @@ -35,7 +35,7 @@ libraryDependencies ++= Seq( "org.scalacheck" %% "scalacheck" % "1.12.4", "junit" % "junit" % "4.10", // Temporary hack until Spark 2.0 - "org.apache.spark" % "spark-hive-thriftserver_2.10" % "1.6.1" intransitive(), + "org.apache.spark" % "spark-hive-thriftserver_2.10" % "1.6.1" % "provided" intransitive(), //tag::sparkCSV[] "com.databricks" % "spark-csv_2.10" % "1.3.0", //end::sparkCSV[] diff --git a/high_performance_pyspark/simple_perf_test.py b/high_performance_pyspark/simple_perf_test.py new file mode 100644 index 0000000..9baf981 --- /dev/null +++ b/high_performance_pyspark/simple_perf_test.py @@ -0,0 +1,87 @@ +# When running this example make sure to include the built Scala jar : +# $SPARK_HOME/bin/pyspark --jars ./target/examples-0.0.1.jar --driver-class-path ./target/examples-0.0.1.jar +# This example illustrates how to interface Scala and Python code, but caution +# should be taken as it depends on many private members that may change in +# future releases of Spark. + +from pyspark.sql.types import * +from pyspark.sql import DataFrame +import timeit + +def generate_scale_data(sqlCtx, rows, numCols): + """ + Generate scale data for the performance test. + + This also illustrates calling custom Scala code from the driver. + + .. Note: This depends on many internal methods and may break between versions. + """ + sc = sqlCtx._csc + # Get the SQL Context, 2.0 and pre-2.0 syntax + try: + javaSqlCtx = sqlCtx._jsqlContext + except: + javaSqlCtx = sqlCtx._ssql_ctx + jsc = sc._jsc + scalasc = jsc.sc() + gateway = sc._gateway + # Call a java method that gives us back an RDD of JVM Rows (Int, Double) + # While Python RDDs are wrapped Java RDDs (even of Rows) the contents are different, so we + # can't directly wrap this. + # This returns a Java RDD of Rows - normally it would better to + # return a DataFrame directly, but for illustration we will work with an RDD + # of Rows. + java_rdd = gateway.jvm.com.highperformancespark.examples.tools.GenerateScalingData. \ + generateMiniScaleRows(scalasc, rows, numCols) + # Schemas are serialized to JSON and sent back and forth + # Construct a Python Schema and turn it into a Java Schema + schema = StructType([StructField("zip", IntegerType()), StructField("fuzzyness", DoubleType())]) + jschema = javaSqlCtx.parseDataType(schema.json()) + # Convert the Java RDD to Java DataFrame + java_dataframe = javaSqlCtx.createDataFrame(java_rdd, jschema) + # Wrap the Java DataFrame into a Python DataFrame + python_dataframe = DataFrame(java_dataframe, sqlCtx) + # Convert the Python DataFrame into an RDD + pairRDD = python_dataframe.rdd.map(lambda row: (row[0], row[1])) + return (python_dataframe, pairRDD) + +def testOnDF(df): + result = df.groupBy("zip").avg("fuzzyness").count() + return result + +def testOnRDD(rdd): + result = rdd.map(lambda (x, y): (x, (y, 1))). \ + reduceByKey(lambda (x, y): (x[0] + y [0], x[1] + y[1])). \ + count() + return result + +def groupOnRDD(rdd): + return rdd.groupByKey().mapValues(lambda v: sum(v) / float(len(v))).count() + +def run(sc, sqlCtx, scalingFactor, size): + (input_df, input_rdd) = generate_scale_data(sqlCtx, scalingFactor, size) + input_rdd.cache().count() + rddTimeings = timeit.repeat(stmt=lambda: testOnRDD(input_rdd), repeat=10, number=1) + groupTimeings = timeit.repeat(stmt=lambda: groupOnRDD(input_rdd), repeat=10, number=1) + input_df.cache().count() + dfTimeings = timeit.repeat(stmt=lambda: testOnRDF(input_df), repeat=10, number=1) + print "RDD:" + print rddTimeings + print "group:" + print groupTimeings + print "df:" + print dfTimeings + print "yay" + +if __name__ == "__main__": + + """ + Usage: simple_perf_test scalingFactor size + """ + scalingFactor = int(sys.argv[1]) + size = int(sys.argv[2]) + sc = SparkContext(appName="SimplePythonPerf") + sqlCtx = SQLContext(sc) + run(sc, sqlCtx, scalingFactor, size) + + sc.stop() diff --git a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala index 3ad9b5f..66d01d4 100644 --- a/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala +++ b/src/main/scala/com/high-performance-spark-examples/tools/GenerateScalingData.scala @@ -4,6 +4,7 @@ import com.highperformancespark.examples.dataframe.RawPanda import org.apache.spark._ import org.apache.spark.rdd.RDD +import org.apache.spark.sql.Row import org.apache.spark.mllib.random.RandomRDDs import org.apache.spark.mllib.linalg.Vector @@ -33,6 +34,20 @@ object GenerateScalingData { RawPanda(k, z, "giant", v(0) > 0.5, v.toArray)} } + /** + * Transform it down to just the data used for the benchmark + */ + def generateMiniScale(sc: SparkContext, rows: Long, numCols: Int): RDD[(Int, Double)] = { + generateFullGoldilocks(sc, rows, numCols).map(p => (p.zip.toInt, p.attributes(0))) + } + + /** + * Transform it down to just the data used for the benchmark + */ + def generateMiniScaleRows(sc: SparkContext, rows: Long, numCols: Int): RDD[Row] = { + generateMiniScale(sc, rows, numCols).map{case (zip, fuzzy) => Row(zip, fuzzy)} + } + // tag::MAGIC_PANDA[] /** * Generate a Goldilocks data set all with the same id. From f7bc3e5dc3ad53d57af1033da4b8934f78d96029 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 18 May 2016 14:14:12 -0700 Subject: [PATCH 175/198] Fix a bunch of the timing code --- high_performance_pyspark/simple_perf_test.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/high_performance_pyspark/simple_perf_test.py b/high_performance_pyspark/simple_perf_test.py index 9baf981..c9f346b 100644 --- a/high_performance_pyspark/simple_perf_test.py +++ b/high_performance_pyspark/simple_perf_test.py @@ -7,6 +7,7 @@ from pyspark.sql.types import * from pyspark.sql import DataFrame import timeit +import time def generate_scale_data(sqlCtx, rows, numCols): """ @@ -16,7 +17,7 @@ def generate_scale_data(sqlCtx, rows, numCols): .. Note: This depends on many internal methods and may break between versions. """ - sc = sqlCtx._csc + sc = sqlCtx._sc # Get the SQL Context, 2.0 and pre-2.0 syntax try: javaSqlCtx = sqlCtx._jsqlContext @@ -51,7 +52,7 @@ def testOnDF(df): def testOnRDD(rdd): result = rdd.map(lambda (x, y): (x, (y, 1))). \ - reduceByKey(lambda (x, y): (x[0] + y [0], x[1] + y[1])). \ + reduceByKey(lambda x, y: (x[0] + y [0], x[1] + y[1])). \ count() return result @@ -61,10 +62,10 @@ def groupOnRDD(rdd): def run(sc, sqlCtx, scalingFactor, size): (input_df, input_rdd) = generate_scale_data(sqlCtx, scalingFactor, size) input_rdd.cache().count() - rddTimeings = timeit.repeat(stmt=lambda: testOnRDD(input_rdd), repeat=10, number=1) - groupTimeings = timeit.repeat(stmt=lambda: groupOnRDD(input_rdd), repeat=10, number=1) + rddTimeings = timeit.repeat(stmt=lambda: testOnRDD(input_rdd), repeat=10, number=1, timer=time.time) + groupTimeings = timeit.repeat(stmt=lambda: groupOnRDD(input_rdd), repeat=10, number=1, timer=time.time) input_df.cache().count() - dfTimeings = timeit.repeat(stmt=lambda: testOnRDF(input_df), repeat=10, number=1) + dfTimeings = timeit.repeat(stmt=lambda: testOnDF(input_df), repeat=10, number=1, timer=time.time) print "RDD:" print rddTimeings print "group:" @@ -78,6 +79,9 @@ def run(sc, sqlCtx, scalingFactor, size): """ Usage: simple_perf_test scalingFactor size """ + import sys + from pyspark import SparkContext + from pyspark.sql import SQLContext scalingFactor = int(sys.argv[1]) size = int(sys.argv[2]) sc = SparkContext(appName="SimplePythonPerf") From c3195d92ea1410a7f13f42ba099c3d267fcb2136 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 18 May 2016 18:22:16 -0700 Subject: [PATCH 176/198] Enable GC for timing --- high_performance_pyspark/simple_perf_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/high_performance_pyspark/simple_perf_test.py b/high_performance_pyspark/simple_perf_test.py index c9f346b..9e9f1e6 100644 --- a/high_performance_pyspark/simple_perf_test.py +++ b/high_performance_pyspark/simple_perf_test.py @@ -62,10 +62,10 @@ def groupOnRDD(rdd): def run(sc, sqlCtx, scalingFactor, size): (input_df, input_rdd) = generate_scale_data(sqlCtx, scalingFactor, size) input_rdd.cache().count() - rddTimeings = timeit.repeat(stmt=lambda: testOnRDD(input_rdd), repeat=10, number=1, timer=time.time) - groupTimeings = timeit.repeat(stmt=lambda: groupOnRDD(input_rdd), repeat=10, number=1, timer=time.time) + rddTimeings = timeit.repeat(stmt=lambda: testOnRDD(input_rdd), repeat=10, number=1, timer=time.time, setup='gc.enable()') + groupTimeings = timeit.repeat(stmt=lambda: groupOnRDD(input_rdd), repeat=10, number=1, timer=time.time, setup='gc.enable()') input_df.cache().count() - dfTimeings = timeit.repeat(stmt=lambda: testOnDF(input_df), repeat=10, number=1, timer=time.time) + dfTimeings = timeit.repeat(stmt=lambda: testOnDF(input_df), repeat=10, number=1, timer=time.time, setup='gc.enable()') print "RDD:" print rddTimeings print "group:" From 90c1b66f0d688f7c364ce35339188be08a97c635 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 18 May 2016 23:18:54 -0700 Subject: [PATCH 177/198] test functions were being picked up by nose --- high_performance_pyspark/simple_perf_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/high_performance_pyspark/simple_perf_test.py b/high_performance_pyspark/simple_perf_test.py index 9e9f1e6..e5d6cbb 100644 --- a/high_performance_pyspark/simple_perf_test.py +++ b/high_performance_pyspark/simple_perf_test.py @@ -46,11 +46,11 @@ def generate_scale_data(sqlCtx, rows, numCols): pairRDD = python_dataframe.rdd.map(lambda row: (row[0], row[1])) return (python_dataframe, pairRDD) -def testOnDF(df): +def runOnDF(df): result = df.groupBy("zip").avg("fuzzyness").count() return result -def testOnRDD(rdd): +def runOnRDD(rdd): result = rdd.map(lambda (x, y): (x, (y, 1))). \ reduceByKey(lambda x, y: (x[0] + y [0], x[1] + y[1])). \ count() @@ -62,10 +62,10 @@ def groupOnRDD(rdd): def run(sc, sqlCtx, scalingFactor, size): (input_df, input_rdd) = generate_scale_data(sqlCtx, scalingFactor, size) input_rdd.cache().count() - rddTimeings = timeit.repeat(stmt=lambda: testOnRDD(input_rdd), repeat=10, number=1, timer=time.time, setup='gc.enable()') + rddTimeings = timeit.repeat(stmt=lambda: runOnRDD(input_rdd), repeat=10, number=1, timer=time.time, setup='gc.enable()') groupTimeings = timeit.repeat(stmt=lambda: groupOnRDD(input_rdd), repeat=10, number=1, timer=time.time, setup='gc.enable()') input_df.cache().count() - dfTimeings = timeit.repeat(stmt=lambda: testOnDF(input_df), repeat=10, number=1, timer=time.time, setup='gc.enable()') + dfTimeings = timeit.repeat(stmt=lambda: runOnDF(input_df), repeat=10, number=1, timer=time.time, setup='gc.enable()') print "RDD:" print rddTimeings print "group:" From 193a6f7d6ea0de2742b2f7b45bf8ad89af2b0f06 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Wed, 18 May 2016 23:37:47 -0700 Subject: [PATCH 178/198] try and add coverage --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d9a0793..b7d1a81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ before_install: - pip install --user codecov unittest2 nose pep8 pylint --download-cache $HOME/.pip-cache script: - "export SPARK_CONF_DIR=./log4j/" - - sbt compile test + - sbt clean coverage compile test - "[ -f spark] || mkdir spark && cd spark && wget http://d3kbcqa49mib13.cloudfront.net/spark-1.6.1-bin-hadoop2.6.tgz && cd .." - "tar -xf ./spark/spark-1.6.1-bin-hadoop2.6.tgz" - "export SPARK_HOME=`pwd`/spark-1.6.1-bin-hadoop2.6" From 6fcfcf3a948270f6c0f15cde79fdc18ba6e6f8a1 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Mon, 16 May 2016 08:43:03 +0200 Subject: [PATCH 179/198] Upgrade java version to 8 to use lambda expressions --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 297b18f..a9e9098 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,7 @@ scalaVersion := "2.11.6" crossScalaVersions := Seq("2.11.6") -javacOptions ++= Seq("-source", "1.7", "-target", "1.7") +javacOptions ++= Seq("-source", "1.8", "-target", "1.8") sparkVersion := "1.6.1" From da2acab99801021d96ec9a230735925651f541cc Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Mon, 16 May 2016 08:43:38 +0200 Subject: [PATCH 180/198] Port HappyPandas to Java --- .../examples/dataframe/JavaHappyPandas.java | 365 ++++++++++++++++++ .../dataframe/HappyPandas.scala | 47 ++- 2 files changed, 394 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java new file mode 100644 index 0000000..5b242ba --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java @@ -0,0 +1,365 @@ +package com.highperformancespark.examples.dataframe; + +import com.highperformancespark.examples.objects.JavaRawPanda; +import org.apache.spark.api.java.JavaRDD; +import org.apache.spark.api.java.JavaSparkContext; +import org.apache.spark.sql.Column; +import org.apache.spark.sql.DataFrame; +import org.apache.spark.sql.Row; +import org.apache.spark.sql.SQLContext; +import org.apache.spark.sql.expressions.Window; +import org.apache.spark.sql.expressions.WindowSpec; +import org.apache.spark.sql.hive.HiveContext; +import org.apache.spark.sql.hive.thriftserver.HiveThriftServer2; +import scala.collection.JavaConversions; +import scala.collection.Seq; +import scala.collection.mutable.Buffer; +import scala.reflect.ClassTag$; +import scala.reflect.api.TypeTags; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import scala.reflect.runtime.*; + +import static org.apache.spark.sql.functions.*; + +public class JavaHappyPandas { + + /** + * Creates SQLContext with an existing SparkContext. + */ + public SQLContext sqlContext(JavaSparkContext jsc) { + SQLContext sqlContext = new SQLContext(jsc); + return sqlContext; + } + + /** + * Creates HiveContext with an existing SparkContext. + */ + public HiveContext hiveContext(JavaSparkContext jsc) { + HiveContext hiveContext = new HiveContext(jsc); + return hiveContext; + } + + /** + * Illustrate loading some JSON data. + */ + public DataFrame loadDataSimple(JavaSparkContext jsc, SQLContext sqlContext, String path) { + DataFrame df1 = sqlContext.read().json(path); + + DataFrame df2 = sqlContext.read().format("json").option("samplingRatio", "1.0").load(path); + + JavaRDD jsonRDD = jsc.textFile(path); + DataFrame df3 = sqlContext.read().json(jsonRDD); + + return df1; + } + + public DataFrame jsonLoadFromRDD(SQLContext sqlContext, JavaRDD input) { + JavaRDD rdd = input.filter(e -> e.contains("panda")); + DataFrame df = sqlContext.read().json(rdd); + return df; + } + + // Here will be some examples on PandaInfo DataFrame + + /** + * Gets the percentage of happy pandas per place. + * + * @param pandaInfo the input DataFrame + * @return Returns DataFrame of (place, percentage of happy pandas) + */ + public DataFrame happyPandasPercentage(DataFrame pandaInfo) { + DataFrame happyPercentage = pandaInfo.select(pandaInfo.col("place"), + pandaInfo.col("happyPandas").divide(pandaInfo.col("totalPandas")).as("percentHappy")); + return happyPercentage; + } + + /** + * Encodes pandaType to Integer values instead of String values. + * + * @param pandaInfo the input DataFrame + * @return Returns a DataFrame of pandaId and integer value for pandaType. + */ + public DataFrame encodePandaType(DataFrame pandaInfo) { + DataFrame encodedDF = pandaInfo.select(pandaInfo.col("id"), + when(pandaInfo.col("pt").equalTo("giant"), 0). + when(pandaInfo.col("pt").equalTo("red"), 1). + otherwise(2).as("encodedType")); + + return encodedDF; + } + + /** + * Gets places with happy pandas more than minHappinessBound. + */ + public DataFrame minHappyPandas(DataFrame pandaInfo, int minHappyPandas) { + return pandaInfo.filter(pandaInfo.col("happyPandas").geq(minHappyPandas)); + } + + /** + * Extra the panda info from panda places and compute the squisheness of the panda + */ + public DataFrame squishPandaFromPace(DataFrame pandaPlace) { + Buffer inputCols = JavaConversions.asScalaBuffer(Arrays.asList(pandaPlace.col("pandas"))); + + TypeTags.TypeTag tag = null; // TODO don't know how to create Type Tag in java ?? + + DataFrame pandaInfo = pandaPlace.explode(inputCols.toList(), r -> { + List pandas = r.getList(0); + List rawPandasList = pandas + .stream() + .map(a -> { + long id = a.getLong(0); + String zip = a.getString(1); + String pt = a.getString(2); + boolean happy = a.getBoolean(3); + List attrs = a.getList(4); + return new JavaRawPanda(id, zip, pt, happy, attrs); + }).collect(Collectors.toList()); + + return JavaConversions.asScalaBuffer(rawPandasList); + }, tag); + + DataFrame squishyness = + pandaInfo.select((pandaInfo.col("attributes").apply(0).divide(pandaInfo.col("attributes")).apply(1)) + .as("squishyness")); + + return squishyness; + } + + /** + * Find pandas that are sad. + */ + public DataFrame sadPandas(DataFrame pandaInfo) { + return pandaInfo.filter(pandaInfo.col("happy").notEqual(true)); + } + + /** + * Find pandas that are happy and fuzzier than squishy. + */ + public DataFrame happyFuzzyPandas(DataFrame pandaInfo) { + DataFrame df = pandaInfo.filter( + pandaInfo.col("happy").and(pandaInfo.col("attributes").apply(0)).gt(pandaInfo.col("attributes").apply(1)) + ); + + return df; + } + + /** + * Gets places that contains happy pandas more than unhappy pandas. + */ + public DataFrame happyPandasPlaces(DataFrame pandaInfo) { + return pandaInfo.filter(pandaInfo.col("happyPandas").geq(pandaInfo.col("totalPandas").divide(2))); + } + + /** + * Remove duplicate pandas by id. + */ + public DataFrame removeDuplicates(DataFrame pandas) { + DataFrame df = pandas.dropDuplicates(new String[]{"id"}); + return df; + } + + public DataFrame describePandas(DataFrame pandas) { + return pandas.describe(); + } + + public DataFrame maxPandaSizePerZip(DataFrame pandas) { + return pandas.groupBy(pandas.col("zip")).max("pandaSize"); + } + + public DataFrame minMaxPandaSizePerZip(DataFrame pandas) { + return pandas.groupBy(pandas.col("zip")).agg(min("pandaSize"), max("pandaSize")); + } + + public DataFrame minPandaSizeMaxAgePerZip(DataFrame pandas) { + Map map = new HashMap<>(); + map.put("pandaSize", "min"); + map.put("age", "max"); + + DataFrame df = pandas.groupBy(pandas.col("zip")).agg(map); + return df; + } + + public DataFrame minMeanSizePerZip(DataFrame pandas) { + return pandas.groupBy(pandas.col("zip")).agg(min(pandas.col("pandaSize")), mean(pandas.col("pandaSize"))); + } + + public DataFrame simpleSqlExample(DataFrame pandas) { + SQLContext sqlContext = pandas.sqlContext(); + pandas.registerTempTable("pandas"); + + DataFrame miniPandas = sqlContext.sql("SELECT * FROM pandas WHERE pandaSize < 12"); + return miniPandas; + } + + public void startJDBCServer(HiveContext sqlContext) { + sqlContext.setConf("hive.server2.thrift.port", "9090"); + HiveThriftServer2.startWithContext(sqlContext); + } + + /** + * Orders pandas by size ascending and by age descending. + * Pandas will be sorted by "size" first and if two pandas have the same "size" + * will be sorted by "age". + */ + public DataFrame orderPandas(DataFrame pandas) { + return pandas.orderBy(pandas.col("pandaSize").asc(), pandas.col("age").desc()); + } + + public DataFrame computeRelativePandaSizes(DataFrame pandas) { + //tag::relativePandaSizesWindow[] + WindowSpec windowSpec = Window + .orderBy(pandas.col("age")) + .partitionBy(pandas.col("zip")) + .rowsBetween(-10, 10); // can use rangeBetween for range instead + //end::relativePandaSizesWindow[] + + //tag::relativePandaSizesQuery[] + Column pandaRelativeSizeCol = pandas.col("pandaSize").minus(avg(pandas.col("pandaSize")).over(windowSpec)); + + return pandas.select(pandas.col("name"), pandas.col("zip"), pandas.col("pandaSize"), + pandas.col("age"), pandaRelativeSizeCol.as("panda_relative_size")); + //end::relativePandaSizesQuery[] + } + + public void joins(DataFrame df1, DataFrame df2) { + //tag::innerJoin[] + // Inner join implicit + df1.join(df2, df1.col("name").equalTo(df2.col("name"))); + // Inner join explicit + df1.join(df2, df1.col("name").equalTo(df2.col("name")), "inner"); + //end::innerJoin[] + + //tag::leftouterJoin[] + // Left outer join explicit + df1.join(df2, df1.col("name").equalTo(df2.col("name")), "left_outer"); + //end::leftouterJoin[] + + //tag::rightouterJoin[] + // Right outer join explicit + df1.join(df2, df1.col("name").equalTo(df2.col("name")), "right_outer"); + //end::rightouterJoin[] + + //tag::leftsemiJoin[] + // Left semi join explicit + df1.join(df2, df1.col("name").equalTo(df2.col("name")), "leftsemi"); + //end::leftsemiJoin[] + } + + public DataFrame selfJoin(DataFrame df) { + return df.as("a").join(df.as("b")).where(df.col("name").equalTo(df.col("name"))); + } + + class JavaPandaInfo { + private String place; + private String pandaType; + private int happyPandas; + private int totalPandas; + + /** + * @param place name of place + * @param pandaType type of pandas in this place + * @param happyPandas number of happy pandas in this place + * @param totalPandas total number of pandas in this place + */ + public JavaPandaInfo(String place, String pandaType, int happyPandas, int totalPandas) { + this.place = place; + this.pandaType = pandaType; + this.happyPandas = happyPandas; + this.totalPandas = totalPandas; + } + + public String getPlace() { + return place; + } + + public void setPlace(String place) { + this.place = place; + } + + public String getPandaType() { + return pandaType; + } + + public void setPandaType(String pandaType) { + this.pandaType = pandaType; + } + + public int getHappyPandas() { + return happyPandas; + } + + public void setHappyPandas(int happyPandas) { + this.happyPandas = happyPandas; + } + + public int getTotalPandas() { + return totalPandas; + } + + public void setTotalPandas(int totalPandas) { + this.totalPandas = totalPandas; + } + + } + + class JavaPandas { + private String name; + private String zip; + private int pandaSize; + private int age; + + /** + * @param name name of panda + * @param zip zip code + * @param pandaSize size of panda in KG + * @param age age of panda + */ + public JavaPandas(String name, String zip, int pandaSize, int age) { + this.name = name; + this.zip = zip; + this.pandaSize = pandaSize; + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public int getPandaSize() { + return pandaSize; + } + + public void setPandaSize(int pandaSize) { + this.pandaSize = pandaSize; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + } + +} diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala index 39271a9..f91728d 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/HappyPandas.scala @@ -19,7 +19,9 @@ import org.apache.spark.sql.hive.thriftserver._ //end::sparkHiveImports[] object HappyPandas { - // create SQLContext with an existing SparkContext + /** + * Creates SQLContext with an existing SparkContext. + */ def sqlContext(sc: SparkContext): SQLContext = { //tag::createSQLContext[] val sqlContext = new SQLContext(sc) @@ -29,7 +31,9 @@ object HappyPandas { sqlContext } - // create HiveContext with an existing SparkContext + /** + * Creates HiveContext with an existing SparkContext. + */ def hiveContext(sc: SparkContext): HiveContext = { //tag::createHiveContext[] val hiveContext = new HiveContext(sc) @@ -39,22 +43,24 @@ object HappyPandas { hiveContext } - // Illustrate loading some JSON data + /** + * Illustrate loading some JSON data. + */ def loadDataSimple(sc: SparkContext, sqlCtx: SQLContext, path: String): DataFrame = { //tag::loadPandaJSONSimple[] - val df = sqlCtx.read.json(path) + val df1 = sqlCtx.read.json(path) //end::loadPandaJSONSimple[] //tag::loadPandaJSONComplex[] val df2 = sqlCtx.read.format("json").option("samplingRatio", "1.0").load(path) //end::loadPandaJSONComplex[] - val rdd = sc.textFile(path) + val jsonRDD = sc.textFile(path) //tag::loadPandaJsonRDD[] - val df3 = sqlCtx.read.json(rdd) + val df3 = sqlCtx.read.json(jsonRDD) //end::loadPandaJSONRDD[] - df + df1 } - def jsonLoadFromRDD(sc: SparkContext, sqlCtx: SQLContext, input: RDD[String]): DataFrame = { + def jsonLoadFromRDD(sqlCtx: SQLContext, input: RDD[String]): DataFrame = { //tag::loadPandaJSONRDD[] val rdd: RDD[String] = input.filter(_.contains("panda")) val df = sqlCtx.read.json(rdd) @@ -76,11 +82,10 @@ object HappyPandas { * Gets the percentage of happy pandas per place. * * @param pandaInfo the input DataFrame - * @return Returns a pair of (place, percentage of happy pandas) + * @return Returns DataFrame of (place, percentage of happy pandas) */ def happyPandasPercentage(pandaInfo: DataFrame): DataFrame = { - pandaInfo.select(pandaInfo("place"), - (pandaInfo("happyPandas") / pandaInfo("totalPandas")).as("percentHappy")) + pandaInfo.select(pandaInfo("place"), (pandaInfo("happyPandas") / pandaInfo("totalPandas")).as("percentHappy")) } //tag::encodePandaType[] @@ -133,14 +138,16 @@ object HappyPandas { } /** - * Find pandas that are happy and fuzzier than squishy + * Find pandas that are happy and fuzzier than squishy. */ def happyFuzzyPandas(pandaInfo: DataFrame): DataFrame = { //tag::complexFilter[] - pandaInfo.filter(pandaInfo("happy").and( - pandaInfo("attributes")(0) > pandaInfo("attributes")(1))) + pandaInfo.filter( + pandaInfo("happy").and(pandaInfo("attributes")(0) > pandaInfo("attributes")(1)) + ) //end::complexFilter[] } + /** * Gets places that contains happy pandas more than unhappy pandas. */ @@ -150,7 +157,7 @@ object HappyPandas { /** - * Remove duplicate pandas by id + * Remove duplicate pandas by id. */ def removeDuplicates(pandas: DataFrame): DataFrame = { //tag::dropDuplicatePandaIds[] @@ -233,34 +240,38 @@ object HappyPandas { val windowSpec = Window .orderBy(pandas("age")) .partitionBy(pandas("zip")) - .rowsBetween(start = -10, end = 10) // use rangeBetween for range instead + .rowsBetween(start = -10, end = 10) // can use rangeBetween for range instead //end::relativePandaSizesWindow[] //tag::relativePandaSizesQuery[] - val pandaRelativeSizeFunc = pandas("pandaSize") - + val pandaRelativeSizeCol = pandas("pandaSize") - avg(pandas("pandaSize")).over(windowSpec) pandas.select(pandas("name"), pandas("zip"), pandas("pandaSize"), pandas("age"), - pandaRelativeSizeFunc.as("panda_relative_size")) + pandaRelativeSizeCol.as("panda_relative_size")) //end::relativePandaSizesQuery[] } // Join DataFrames of Pandas and Sizes with def joins(df1: DataFrame, df2: DataFrame): Unit = { + //tag::innerJoin[] // Inner join implicit df1.join(df2, df1("name") === df2("name")) // Inner join explicit df1.join(df2, df1("name") === df2("name"), "inner") //end::innerJoin[] + //tag::leftouterJoin[] // Left outer join explicit df1.join(df2, df1("name") === df2("name"), "left_outer") //end::leftouterJoin[] + //tag::rightouterJoin[] // Right outer join explicit df1.join(df2, df1("name") === df2("name"), "right_outer") //end::rightouterJoin[] + //tag::leftsemiJoin[] // Left semi join explicit df1.join(df2, df1("name") === df2("name"), "leftsemi") From d27673381e8e1fd3f181295ef98b3220a18e4f5c Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Wed, 18 May 2016 05:24:02 +0200 Subject: [PATCH 181/198] Create separate package for java beans --- .../examples/dataframe/JavaHappyPandas.java | 110 ------------------ .../examples/objects/JavaCoffeeShop.java | 27 +++++ .../examples/objects/JavaPandaInfo.java | 54 +++++++++ .../examples/objects/JavaPandaPlace.java | 33 ++++++ .../examples/objects/JavaPandas.java | 54 +++++++++ .../examples/objects/JavaRawPanda.java | 66 +++++++++++ 6 files changed, 234 insertions(+), 110 deletions(-) create mode 100644 src/main/java/com/highperformancespark/examples/objects/JavaCoffeeShop.java create mode 100644 src/main/java/com/highperformancespark/examples/objects/JavaPandaInfo.java create mode 100644 src/main/java/com/highperformancespark/examples/objects/JavaPandaPlace.java create mode 100644 src/main/java/com/highperformancespark/examples/objects/JavaPandas.java create mode 100644 src/main/java/com/highperformancespark/examples/objects/JavaRawPanda.java diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java index 5b242ba..c9f7fd6 100644 --- a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java @@ -12,9 +12,7 @@ import org.apache.spark.sql.hive.HiveContext; import org.apache.spark.sql.hive.thriftserver.HiveThriftServer2; import scala.collection.JavaConversions; -import scala.collection.Seq; import scala.collection.mutable.Buffer; -import scala.reflect.ClassTag$; import scala.reflect.api.TypeTags; import java.util.Arrays; @@ -23,8 +21,6 @@ import java.util.Map; import java.util.stream.Collectors; -import scala.reflect.runtime.*; - import static org.apache.spark.sql.functions.*; public class JavaHappyPandas { @@ -256,110 +252,4 @@ public DataFrame selfJoin(DataFrame df) { return df.as("a").join(df.as("b")).where(df.col("name").equalTo(df.col("name"))); } - class JavaPandaInfo { - private String place; - private String pandaType; - private int happyPandas; - private int totalPandas; - - /** - * @param place name of place - * @param pandaType type of pandas in this place - * @param happyPandas number of happy pandas in this place - * @param totalPandas total number of pandas in this place - */ - public JavaPandaInfo(String place, String pandaType, int happyPandas, int totalPandas) { - this.place = place; - this.pandaType = pandaType; - this.happyPandas = happyPandas; - this.totalPandas = totalPandas; - } - - public String getPlace() { - return place; - } - - public void setPlace(String place) { - this.place = place; - } - - public String getPandaType() { - return pandaType; - } - - public void setPandaType(String pandaType) { - this.pandaType = pandaType; - } - - public int getHappyPandas() { - return happyPandas; - } - - public void setHappyPandas(int happyPandas) { - this.happyPandas = happyPandas; - } - - public int getTotalPandas() { - return totalPandas; - } - - public void setTotalPandas(int totalPandas) { - this.totalPandas = totalPandas; - } - - } - - class JavaPandas { - private String name; - private String zip; - private int pandaSize; - private int age; - - /** - * @param name name of panda - * @param zip zip code - * @param pandaSize size of panda in KG - * @param age age of panda - */ - public JavaPandas(String name, String zip, int pandaSize, int age) { - this.name = name; - this.zip = zip; - this.pandaSize = pandaSize; - this.age = age; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getZip() { - return zip; - } - - public void setZip(String zip) { - this.zip = zip; - } - - public int getPandaSize() { - return pandaSize; - } - - public void setPandaSize(int pandaSize) { - this.pandaSize = pandaSize; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - } - } diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaCoffeeShop.java b/src/main/java/com/highperformancespark/examples/objects/JavaCoffeeShop.java new file mode 100644 index 0000000..997f202 --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/objects/JavaCoffeeShop.java @@ -0,0 +1,27 @@ +package com.highperformancespark.examples.objects; + +public class JavaCoffeeShop { + private String zip; + private String name; + + public JavaCoffeeShop(String zip, String name) { + this.zip = zip; + this.name = name; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaPandaInfo.java b/src/main/java/com/highperformancespark/examples/objects/JavaPandaInfo.java new file mode 100644 index 0000000..c734e64 --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/objects/JavaPandaInfo.java @@ -0,0 +1,54 @@ +package com.highperformancespark.examples.objects; + +public class JavaPandaInfo { + private String place; + private String pandaType; + private int happyPandas; + private int totalPandas; + + /** + * @param place name of place + * @param pandaType type of pandas in this place + * @param happyPandas number of happy pandas in this place + * @param totalPandas total number of pandas in this place + */ + public JavaPandaInfo(String place, String pandaType, int happyPandas, int totalPandas) { + this.place = place; + this.pandaType = pandaType; + this.happyPandas = happyPandas; + this.totalPandas = totalPandas; + } + + public String getPlace() { + return place; + } + + public void setPlace(String place) { + this.place = place; + } + + public String getPandaType() { + return pandaType; + } + + public void setPandaType(String pandaType) { + this.pandaType = pandaType; + } + + public int getHappyPandas() { + return happyPandas; + } + + public void setHappyPandas(int happyPandas) { + this.happyPandas = happyPandas; + } + + public int getTotalPandas() { + return totalPandas; + } + + public void setTotalPandas(int totalPandas) { + this.totalPandas = totalPandas; + } + +} diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaPandaPlace.java b/src/main/java/com/highperformancespark/examples/objects/JavaPandaPlace.java new file mode 100644 index 0000000..9cb851a --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/objects/JavaPandaPlace.java @@ -0,0 +1,33 @@ +package com.highperformancespark.examples.objects; + +import com.highperformancespark.examples.dataframe.RawPanda; + +public class JavaPandaPlace { + private String name; + private RawPanda[] pandas; + + /** + * @param name place name + * @param pandas pandas in that place + */ + public JavaPandaPlace(String name, RawPanda[] pandas) { + this.name = name; + this.pandas = pandas; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RawPanda[] getPandas() { + return pandas; + } + + public void setPandas(RawPanda[] pandas) { + this.pandas = pandas; + } +} \ No newline at end of file diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaPandas.java b/src/main/java/com/highperformancespark/examples/objects/JavaPandas.java new file mode 100644 index 0000000..edc8e52 --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/objects/JavaPandas.java @@ -0,0 +1,54 @@ +package com.highperformancespark.examples.objects; + +public class JavaPandas { + private String name; + private String zip; + private int pandaSize; + private int age; + + /** + * @param name name of panda + * @param zip zip code + * @param pandaSize size of panda in KG + * @param age age of panda + */ + public JavaPandas(String name, String zip, int pandaSize, int age) { + this.name = name; + this.zip = zip; + this.pandaSize = pandaSize; + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public int getPandaSize() { + return pandaSize; + } + + public void setPandaSize(int pandaSize) { + this.pandaSize = pandaSize; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + +} diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaRawPanda.java b/src/main/java/com/highperformancespark/examples/objects/JavaRawPanda.java new file mode 100644 index 0000000..0c404fa --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/objects/JavaRawPanda.java @@ -0,0 +1,66 @@ +package com.highperformancespark.examples.objects; + +import java.util.List; + +public class JavaRawPanda { + private long id; + private String zip; + private String pt; + private boolean happy; + private List attributes; + + /** + * @param id panda id + * @param zip zip code of panda residence + * @param pt Type of panda as a string + * @param happy if panda is happy + * @param attributes array of panada attributes + */ + public JavaRawPanda(long id, String zip, String pt, boolean happy, List attributes) { + this.attributes = attributes; + this.id = id; + this.zip = zip; + this.pt = pt; + this.happy = happy; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getPt() { + return pt; + } + + public void setPt(String pt) { + this.pt = pt; + } + + public boolean isHappy() { + return happy; + } + + public void setHappy(boolean happy) { + this.happy = happy; + } + + public List getAttributes() { + return attributes; + } + + public void setAttributes(List attributes) { + this.attributes = attributes; + } +} \ No newline at end of file From c40b69dd0517cebca10310da6be6ba828c423f57 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Wed, 18 May 2016 05:49:47 +0200 Subject: [PATCH 182/198] Remove squishPandaFromPace example --- .../examples/dataframe/JavaHappyPandas.java | 43 +------------------ 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java index c9f7fd6..ac0a6a3 100644 --- a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java @@ -1,25 +1,17 @@ package com.highperformancespark.examples.dataframe; -import com.highperformancespark.examples.objects.JavaRawPanda; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.sql.Column; import org.apache.spark.sql.DataFrame; -import org.apache.spark.sql.Row; import org.apache.spark.sql.SQLContext; import org.apache.spark.sql.expressions.Window; import org.apache.spark.sql.expressions.WindowSpec; import org.apache.spark.sql.hive.HiveContext; import org.apache.spark.sql.hive.thriftserver.HiveThriftServer2; -import scala.collection.JavaConversions; -import scala.collection.mutable.Buffer; -import scala.reflect.api.TypeTags; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import static org.apache.spark.sql.functions.*; @@ -97,37 +89,6 @@ public DataFrame minHappyPandas(DataFrame pandaInfo, int minHappyPandas) { return pandaInfo.filter(pandaInfo.col("happyPandas").geq(minHappyPandas)); } - /** - * Extra the panda info from panda places and compute the squisheness of the panda - */ - public DataFrame squishPandaFromPace(DataFrame pandaPlace) { - Buffer inputCols = JavaConversions.asScalaBuffer(Arrays.asList(pandaPlace.col("pandas"))); - - TypeTags.TypeTag tag = null; // TODO don't know how to create Type Tag in java ?? - - DataFrame pandaInfo = pandaPlace.explode(inputCols.toList(), r -> { - List pandas = r.getList(0); - List rawPandasList = pandas - .stream() - .map(a -> { - long id = a.getLong(0); - String zip = a.getString(1); - String pt = a.getString(2); - boolean happy = a.getBoolean(3); - List attrs = a.getList(4); - return new JavaRawPanda(id, zip, pt, happy, attrs); - }).collect(Collectors.toList()); - - return JavaConversions.asScalaBuffer(rawPandasList); - }, tag); - - DataFrame squishyness = - pandaInfo.select((pandaInfo.col("attributes").apply(0).divide(pandaInfo.col("attributes")).apply(1)) - .as("squishyness")); - - return squishyness; - } - /** * Find pandas that are sad. */ @@ -201,8 +162,8 @@ public void startJDBCServer(HiveContext sqlContext) { /** * Orders pandas by size ascending and by age descending. - * Pandas will be sorted by "size" first and if two pandas have the same "size" - * will be sorted by "age". + * Pandas will be sorted by "size" first and if two pandas + * have the same "size" will be sorted by "age". */ public DataFrame orderPandas(DataFrame pandas) { return pandas.orderBy(pandas.col("pandaSize").asc(), pandas.col("age").desc()); From 1e58afdf351ce3db1afd6303d95cd8bada66ef89 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Thu, 19 May 2016 07:16:13 +0200 Subject: [PATCH 183/198] Add Java8 to .travis.yml --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index b7d1a81..520a5bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,8 @@ cache: - $HOME/.sbt/launchers scala: - 2.11.6 +jdk: + - oraclejdk8 apt: sources: - ubuntu-toolchain-r-test From e792bb3300cab5e00f289a471133acec612467cb Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Fri, 20 May 2016 00:18:48 +0200 Subject: [PATCH 184/198] Remove startJDBCServer example --- .../examples/dataframe/JavaHappyPandas.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java index ac0a6a3..73636de 100644 --- a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java @@ -8,7 +8,6 @@ import org.apache.spark.sql.expressions.Window; import org.apache.spark.sql.expressions.WindowSpec; import org.apache.spark.sql.hive.HiveContext; -import org.apache.spark.sql.hive.thriftserver.HiveThriftServer2; import java.util.HashMap; import java.util.Map; @@ -155,11 +154,6 @@ public DataFrame simpleSqlExample(DataFrame pandas) { return miniPandas; } - public void startJDBCServer(HiveContext sqlContext) { - sqlContext.setConf("hive.server2.thrift.port", "9090"); - HiveThriftServer2.startWithContext(sqlContext); - } - /** * Orders pandas by size ascending and by age descending. * Pandas will be sorted by "size" first and if two pandas From a252514e685ebabfdb53b83d415cb8ea3a4620ff Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Fri, 20 May 2016 17:09:59 +0200 Subject: [PATCH 185/198] Convert java methods to static --- .../examples/dataframe/JavaHappyPandas.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java index 73636de..09b5e2f 100644 --- a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java @@ -19,7 +19,7 @@ public class JavaHappyPandas { /** * Creates SQLContext with an existing SparkContext. */ - public SQLContext sqlContext(JavaSparkContext jsc) { + public static SQLContext sqlContext(JavaSparkContext jsc) { SQLContext sqlContext = new SQLContext(jsc); return sqlContext; } @@ -27,7 +27,7 @@ public SQLContext sqlContext(JavaSparkContext jsc) { /** * Creates HiveContext with an existing SparkContext. */ - public HiveContext hiveContext(JavaSparkContext jsc) { + public static HiveContext hiveContext(JavaSparkContext jsc) { HiveContext hiveContext = new HiveContext(jsc); return hiveContext; } @@ -35,7 +35,7 @@ public HiveContext hiveContext(JavaSparkContext jsc) { /** * Illustrate loading some JSON data. */ - public DataFrame loadDataSimple(JavaSparkContext jsc, SQLContext sqlContext, String path) { + public static DataFrame loadDataSimple(JavaSparkContext jsc, SQLContext sqlContext, String path) { DataFrame df1 = sqlContext.read().json(path); DataFrame df2 = sqlContext.read().format("json").option("samplingRatio", "1.0").load(path); @@ -46,7 +46,7 @@ public DataFrame loadDataSimple(JavaSparkContext jsc, SQLContext sqlContext, Str return df1; } - public DataFrame jsonLoadFromRDD(SQLContext sqlContext, JavaRDD input) { + public static DataFrame jsonLoadFromRDD(SQLContext sqlContext, JavaRDD input) { JavaRDD rdd = input.filter(e -> e.contains("panda")); DataFrame df = sqlContext.read().json(rdd); return df; @@ -60,7 +60,7 @@ public DataFrame jsonLoadFromRDD(SQLContext sqlContext, JavaRDD input) { * @param pandaInfo the input DataFrame * @return Returns DataFrame of (place, percentage of happy pandas) */ - public DataFrame happyPandasPercentage(DataFrame pandaInfo) { + public static DataFrame happyPandasPercentage(DataFrame pandaInfo) { DataFrame happyPercentage = pandaInfo.select(pandaInfo.col("place"), pandaInfo.col("happyPandas").divide(pandaInfo.col("totalPandas")).as("percentHappy")); return happyPercentage; @@ -72,7 +72,7 @@ public DataFrame happyPandasPercentage(DataFrame pandaInfo) { * @param pandaInfo the input DataFrame * @return Returns a DataFrame of pandaId and integer value for pandaType. */ - public DataFrame encodePandaType(DataFrame pandaInfo) { + public static DataFrame encodePandaType(DataFrame pandaInfo) { DataFrame encodedDF = pandaInfo.select(pandaInfo.col("id"), when(pandaInfo.col("pt").equalTo("giant"), 0). when(pandaInfo.col("pt").equalTo("red"), 1). @@ -84,21 +84,21 @@ public DataFrame encodePandaType(DataFrame pandaInfo) { /** * Gets places with happy pandas more than minHappinessBound. */ - public DataFrame minHappyPandas(DataFrame pandaInfo, int minHappyPandas) { + public static DataFrame minHappyPandas(DataFrame pandaInfo, int minHappyPandas) { return pandaInfo.filter(pandaInfo.col("happyPandas").geq(minHappyPandas)); } /** * Find pandas that are sad. */ - public DataFrame sadPandas(DataFrame pandaInfo) { + public static DataFrame sadPandas(DataFrame pandaInfo) { return pandaInfo.filter(pandaInfo.col("happy").notEqual(true)); } /** * Find pandas that are happy and fuzzier than squishy. */ - public DataFrame happyFuzzyPandas(DataFrame pandaInfo) { + public static DataFrame happyFuzzyPandas(DataFrame pandaInfo) { DataFrame df = pandaInfo.filter( pandaInfo.col("happy").and(pandaInfo.col("attributes").apply(0)).gt(pandaInfo.col("attributes").apply(1)) ); @@ -109,31 +109,31 @@ public DataFrame happyFuzzyPandas(DataFrame pandaInfo) { /** * Gets places that contains happy pandas more than unhappy pandas. */ - public DataFrame happyPandasPlaces(DataFrame pandaInfo) { + public static DataFrame happyPandasPlaces(DataFrame pandaInfo) { return pandaInfo.filter(pandaInfo.col("happyPandas").geq(pandaInfo.col("totalPandas").divide(2))); } /** * Remove duplicate pandas by id. */ - public DataFrame removeDuplicates(DataFrame pandas) { + public static DataFrame removeDuplicates(DataFrame pandas) { DataFrame df = pandas.dropDuplicates(new String[]{"id"}); return df; } - public DataFrame describePandas(DataFrame pandas) { + public static DataFrame describePandas(DataFrame pandas) { return pandas.describe(); } - public DataFrame maxPandaSizePerZip(DataFrame pandas) { + public static DataFrame maxPandaSizePerZip(DataFrame pandas) { return pandas.groupBy(pandas.col("zip")).max("pandaSize"); } - public DataFrame minMaxPandaSizePerZip(DataFrame pandas) { + public static DataFrame minMaxPandaSizePerZip(DataFrame pandas) { return pandas.groupBy(pandas.col("zip")).agg(min("pandaSize"), max("pandaSize")); } - public DataFrame minPandaSizeMaxAgePerZip(DataFrame pandas) { + public static DataFrame minPandaSizeMaxAgePerZip(DataFrame pandas) { Map map = new HashMap<>(); map.put("pandaSize", "min"); map.put("age", "max"); @@ -142,11 +142,11 @@ public DataFrame minPandaSizeMaxAgePerZip(DataFrame pandas) { return df; } - public DataFrame minMeanSizePerZip(DataFrame pandas) { + public static DataFrame minMeanSizePerZip(DataFrame pandas) { return pandas.groupBy(pandas.col("zip")).agg(min(pandas.col("pandaSize")), mean(pandas.col("pandaSize"))); } - public DataFrame simpleSqlExample(DataFrame pandas) { + public static DataFrame simpleSqlExample(DataFrame pandas) { SQLContext sqlContext = pandas.sqlContext(); pandas.registerTempTable("pandas"); @@ -159,11 +159,11 @@ public DataFrame simpleSqlExample(DataFrame pandas) { * Pandas will be sorted by "size" first and if two pandas * have the same "size" will be sorted by "age". */ - public DataFrame orderPandas(DataFrame pandas) { + public static DataFrame orderPandas(DataFrame pandas) { return pandas.orderBy(pandas.col("pandaSize").asc(), pandas.col("age").desc()); } - public DataFrame computeRelativePandaSizes(DataFrame pandas) { + public static DataFrame computeRelativePandaSizes(DataFrame pandas) { //tag::relativePandaSizesWindow[] WindowSpec windowSpec = Window .orderBy(pandas.col("age")) @@ -179,7 +179,7 @@ public DataFrame computeRelativePandaSizes(DataFrame pandas) { //end::relativePandaSizesQuery[] } - public void joins(DataFrame df1, DataFrame df2) { + public static void joins(DataFrame df1, DataFrame df2) { //tag::innerJoin[] // Inner join implicit df1.join(df2, df1.col("name").equalTo(df2.col("name"))); @@ -203,7 +203,7 @@ public void joins(DataFrame df1, DataFrame df2) { //end::leftsemiJoin[] } - public DataFrame selfJoin(DataFrame df) { + public static DataFrame selfJoin(DataFrame df) { return df.as("a").join(df.as("b")).where(df.col("name").equalTo(df.col("name"))); } From b887283a1f97d0f90b41c5b7ebb342c882b469ed Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Sat, 21 May 2016 00:46:50 +0200 Subject: [PATCH 186/198] Add simple test cases for JavaHappyPandas --- .../examples/dataframe/JavaHappyPandas.java | 4 +- .../dataframe/JavaHappyPandasTest.java | 151 ++++++++++++++++++ .../dataframe/HappyPandasTest.scala | 8 +- 3 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/highperformancespark/examples/dataframe/JavaHappyPandasTest.java diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java index 09b5e2f..bc93163 100644 --- a/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaHappyPandas.java @@ -62,7 +62,7 @@ public static DataFrame jsonLoadFromRDD(SQLContext sqlContext, JavaRDD i */ public static DataFrame happyPandasPercentage(DataFrame pandaInfo) { DataFrame happyPercentage = pandaInfo.select(pandaInfo.col("place"), - pandaInfo.col("happyPandas").divide(pandaInfo.col("totalPandas")).as("percentHappy")); + (pandaInfo.col("happyPandas").divide(pandaInfo.col("totalPandas"))).as("percentHappy")); return happyPercentage; } @@ -204,7 +204,7 @@ public static void joins(DataFrame df1, DataFrame df2) { } public static DataFrame selfJoin(DataFrame df) { - return df.as("a").join(df.as("b")).where(df.col("name").equalTo(df.col("name"))); + return (df.as("a")).join(df.as("b")).where("a.name = b.name"); } } diff --git a/src/test/java/com/highperformancespark/examples/dataframe/JavaHappyPandasTest.java b/src/test/java/com/highperformancespark/examples/dataframe/JavaHappyPandasTest.java new file mode 100644 index 0000000..b0d4bdc --- /dev/null +++ b/src/test/java/com/highperformancespark/examples/dataframe/JavaHappyPandasTest.java @@ -0,0 +1,151 @@ +package com.highperformancespark.examples.dataframe; + +import com.highperformancespark.examples.objects.JavaPandaInfo; +import com.highperformancespark.examples.objects.JavaPandas; +import com.highperformancespark.examples.objects.JavaRawPanda; +import com.holdenkarau.spark.testing.JavaDataFrameSuiteBase; +import org.apache.spark.sql.DataFrame; +import org.apache.spark.sql.Row; +import org.apache.spark.sql.RowFactory; +import org.apache.spark.sql.types.*; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +public class JavaHappyPandasTest extends JavaDataFrameSuiteBase { + String toronto = "toronto"; + String sandiego = "san diego"; + String virginia = "virginia"; + + List pandaInfoList = Arrays.asList( + new JavaPandaInfo(toronto, "giant", 1, 2), + new JavaPandaInfo(sandiego, "red", 2, 3), + new JavaPandaInfo(virginia, "black", 1, 10) + ); + + List rawPandaList = Arrays.asList( + new JavaRawPanda(10L, "94110", "giant", true, Arrays.asList(1.0, 0.9)), + new JavaRawPanda(11L, "94110", "red", true, Arrays.asList(1.0, 0.9))); + + List pandasList = Arrays.asList( + new JavaPandas("bata", "10010", 10, 2), + new JavaPandas("wiza", "10010", 20, 4), + new JavaPandas("dabdob", "11000", 8, 2), + new JavaPandas("hanafy", "11000", 15, 7), + new JavaPandas("hamdi", "11111", 20, 10) + ); + + @Test + public void simpleSelfJoinTest() { + DataFrame inputDF = sqlContext().createDataFrame(pandasList, JavaPandas.class); + DataFrame result = JavaHappyPandas.selfJoin(inputDF).select("a.name", "b.name"); + List resultList = result.collectAsList(); + + resultList.stream().forEach(row -> assertEquals(row.getString(0), row.getString(1))); + } + + @Test + public void verifyhappyPandasPercentage() { + List expectedList = Arrays.asList(RowFactory.create(toronto, 0.5), + RowFactory.create(sandiego, 2 / 3.0), RowFactory.create(virginia, 1/10.0)); + DataFrame expectedDF = sqlContext().createDataFrame( + expectedList, new StructType( + new StructField[]{ + new StructField("place", DataTypes.StringType, true, Metadata.empty()), + new StructField("percentHappy", DataTypes.DoubleType, true, Metadata.empty()) + })); + + DataFrame inputDF = sqlContext().createDataFrame(pandaInfoList, JavaPandaInfo.class); + DataFrame resultDF = JavaHappyPandas.happyPandasPercentage(inputDF); + + assertDataFrameApproximateEquals(expectedDF, resultDF, 1E-5); + } + + @Test + public void encodePandaType() { + DataFrame inputDF = sqlContext().createDataFrame(rawPandaList, JavaRawPanda.class); + DataFrame resultDF = JavaHappyPandas.encodePandaType(inputDF); + + List expectedRows = Arrays.asList(RowFactory.create(10L, 0), RowFactory.create(11L, 1)); + DataFrame expectedDF = sqlContext().createDataFrame(expectedRows, new StructType(new StructField[]{ + new StructField("id", DataTypes.LongType, false, Metadata.empty()), + new StructField("encodedType", DataTypes.IntegerType, false, Metadata.empty()) + })); + + assertDataFrameEquals(expectedDF, resultDF); + } + + @Test + public void happyPandasPlaces() { + DataFrame inputDF = sqlContext().createDataFrame(pandaInfoList, JavaPandaInfo.class); + DataFrame resultDF = JavaHappyPandas.happyPandasPlaces(inputDF); + + List expectedRows = Arrays.asList( + new JavaPandaInfo(toronto, "giant", 1, 2), + new JavaPandaInfo(sandiego, "red", 2, 3)); + DataFrame expectedDF = sqlContext().createDataFrame(expectedRows, JavaPandaInfo.class); + + assertDataFrameEquals(expectedDF, resultDF); + } + + @Test + public void maxPandaSizePerZip() { + DataFrame inputDF = sqlContext().createDataFrame(pandasList, JavaPandas.class); + DataFrame resultDF = JavaHappyPandas.maxPandaSizePerZip(inputDF); + + List expectedRows = Arrays.asList( + RowFactory.create(pandasList.get(1).getZip(), pandasList.get(1).getPandaSize()), + RowFactory.create(pandasList.get(3).getZip(), pandasList.get(3).getPandaSize()), + RowFactory.create(pandasList.get(4).getZip(), pandasList.get(4).getPandaSize()) + ); + DataFrame expectedDF = sqlContext().createDataFrame(expectedRows, + new StructType( + new StructField[]{ + new StructField("zip", DataTypes.StringType, true, Metadata.empty()), + new StructField("max(pandaSize)", DataTypes.IntegerType, true, Metadata.empty()) + } + )); + + assertDataFrameEquals(expectedDF.orderBy("zip"), resultDF.orderBy("zip")); + } + + @Test + public void complexAggPerZip() { + DataFrame inputDF = sqlContext().createDataFrame(pandasList, JavaPandas.class); + DataFrame resultDF = JavaHappyPandas.minMeanSizePerZip(inputDF); + + List expectedRows = Arrays.asList( + RowFactory.create(pandasList.get(1).getZip(), pandasList.get(0).getPandaSize(), 15.0), + RowFactory.create(pandasList.get(3).getZip(), pandasList.get(2).getPandaSize(), 11.5), + RowFactory.create(pandasList.get(4).getZip(), pandasList.get(4).getPandaSize(), 20.0)); + + DataFrame expectedDF = sqlContext().createDataFrame(expectedRows, + new StructType( + new StructField[]{ + new StructField("zip", DataTypes.StringType, true, Metadata.empty()), + new StructField("min(pandaSize)", DataTypes.IntegerType, true, Metadata.empty()), + new StructField("avg(pandaSize)", DataTypes.DoubleType, true, Metadata.empty()) + } + )); + + assertDataFrameApproximateEquals(expectedDF.orderBy("zip"), resultDF.orderBy("zip"), 1E-5); + } + + @Test + public void simpleSQLExample() { + DataFrame inputDF = sqlContext().createDataFrame(pandasList, JavaPandas.class); + DataFrame resultDF = JavaHappyPandas.simpleSqlExample(inputDF); + + List expectedList = Arrays.asList( + pandasList.get(0), pandasList.get(2) + ); + DataFrame expectedDF = sqlContext().createDataFrame(expectedList, JavaPandas.class); + + assertDataFrameEquals(expectedDF, resultDF); + } + +} \ No newline at end of file diff --git a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala index f46a2f3..c6d64fe 100644 --- a/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/dataframe/HappyPandasTest.scala @@ -54,14 +54,14 @@ class HappyPandasTest extends DataFrameSuiteBase { //tag::approxEqualDataFrames[] test("verify simple happy pandas Percentage") { - val expectedResult = List(Row(toronto, 0.5), Row(sandiego, 2/3.0), Row(virginia, 1/10.0)) - val expectedDf = createDF(expectedResult, ("place", StringType), + val expectedList = List(Row(toronto, 0.5), Row(sandiego, 2/3.0), Row(virginia, 1/10.0)) + val expectedDf = createDF(expectedList, ("place", StringType), ("percentHappy", DoubleType)) val inputDF = sqlContext.createDataFrame(pandaInfoList) - val result = HappyPandas.happyPandasPercentage(inputDF) + val resultDF = HappyPandas.happyPandasPercentage(inputDF) - assertDataFrameApproximateEquals(expectedDf, result, 1E-5) + assertDataFrameApproximateEquals(expectedDf, resultDF, 1E-5) } //end::approxEqualDataFrames[] From 6ce7baff36e3ba6aca59c67316abf767d1dd8562 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Sat, 21 May 2016 09:03:21 +0200 Subject: [PATCH 187/198] Implements Serializable to all Java Beans --- .../examples/objects/JavaCoffeeShop.java | 4 +++- .../examples/objects/JavaPandaInfo.java | 4 +++- .../examples/objects/JavaPandaPlace.java | 13 +++++++------ .../examples/objects/JavaPandas.java | 4 +++- .../examples/objects/JavaRawPanda.java | 3 ++- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaCoffeeShop.java b/src/main/java/com/highperformancespark/examples/objects/JavaCoffeeShop.java index 997f202..e3f5325 100644 --- a/src/main/java/com/highperformancespark/examples/objects/JavaCoffeeShop.java +++ b/src/main/java/com/highperformancespark/examples/objects/JavaCoffeeShop.java @@ -1,6 +1,8 @@ package com.highperformancespark.examples.objects; -public class JavaCoffeeShop { +import java.io.Serializable; + +public class JavaCoffeeShop implements Serializable { private String zip; private String name; diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaPandaInfo.java b/src/main/java/com/highperformancespark/examples/objects/JavaPandaInfo.java index c734e64..c2b7847 100644 --- a/src/main/java/com/highperformancespark/examples/objects/JavaPandaInfo.java +++ b/src/main/java/com/highperformancespark/examples/objects/JavaPandaInfo.java @@ -1,6 +1,8 @@ package com.highperformancespark.examples.objects; -public class JavaPandaInfo { +import java.io.Serializable; + +public class JavaPandaInfo implements Serializable { private String place; private String pandaType; private int happyPandas; diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaPandaPlace.java b/src/main/java/com/highperformancespark/examples/objects/JavaPandaPlace.java index 9cb851a..dc33d9c 100644 --- a/src/main/java/com/highperformancespark/examples/objects/JavaPandaPlace.java +++ b/src/main/java/com/highperformancespark/examples/objects/JavaPandaPlace.java @@ -1,16 +1,17 @@ package com.highperformancespark.examples.objects; -import com.highperformancespark.examples.dataframe.RawPanda; +import java.io.Serializable; +import java.util.List; -public class JavaPandaPlace { +public class JavaPandaPlace implements Serializable { private String name; - private RawPanda[] pandas; + private List pandas; /** * @param name place name * @param pandas pandas in that place */ - public JavaPandaPlace(String name, RawPanda[] pandas) { + public JavaPandaPlace(String name, List pandas) { this.name = name; this.pandas = pandas; } @@ -23,11 +24,11 @@ public void setName(String name) { this.name = name; } - public RawPanda[] getPandas() { + public List getPandas() { return pandas; } - public void setPandas(RawPanda[] pandas) { + public void setPandas(List pandas) { this.pandas = pandas; } } \ No newline at end of file diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaPandas.java b/src/main/java/com/highperformancespark/examples/objects/JavaPandas.java index edc8e52..f73e93f 100644 --- a/src/main/java/com/highperformancespark/examples/objects/JavaPandas.java +++ b/src/main/java/com/highperformancespark/examples/objects/JavaPandas.java @@ -1,6 +1,8 @@ package com.highperformancespark.examples.objects; -public class JavaPandas { +import java.io.Serializable; + +public class JavaPandas implements Serializable { private String name; private String zip; private int pandaSize; diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaRawPanda.java b/src/main/java/com/highperformancespark/examples/objects/JavaRawPanda.java index 0c404fa..7d2be17 100644 --- a/src/main/java/com/highperformancespark/examples/objects/JavaRawPanda.java +++ b/src/main/java/com/highperformancespark/examples/objects/JavaRawPanda.java @@ -1,8 +1,9 @@ package com.highperformancespark.examples.objects; +import java.io.Serializable; import java.util.List; -public class JavaRawPanda { +public class JavaRawPanda implements Serializable { private long id; private String zip; private String pt; From d7de259efaef0773f71a470ff1713ce1eddda7b6 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Sat, 21 May 2016 09:28:08 +0200 Subject: [PATCH 188/198] Port LoadSave to Java --- .../examples/dataframe/JavaLoadSave.java | 140 ++++++++++++++++++ .../dataframe/LoadSave.scala | 15 +- 2 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/highperformancespark/examples/dataframe/JavaLoadSave.java diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaLoadSave.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaLoadSave.java new file mode 100644 index 0000000..9d36dd8 --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaLoadSave.java @@ -0,0 +1,140 @@ +package com.highperformancespark.examples.dataframe; + +import com.highperformancespark.examples.objects.JavaPandaPlace; +import com.highperformancespark.examples.objects.JavaRawPanda; +import org.apache.spark.api.java.JavaRDD; +import org.apache.spark.sql.*; +import org.apache.spark.sql.types.*; + +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; + +public class JavaLoadSave { + private SQLContext sqlContext; + + public JavaLoadSave(SQLContext sqlContext) { + this.sqlContext = sqlContext; + } + + //tag::createFromRDD[] + public DataFrame createFromJavaBean(JavaRDD input) { + // Create DataFrame using Java Bean + DataFrame df1 = sqlContext.createDataFrame(input, JavaPandaPlace.class); + + // Create DataFrame using JavaRDD + JavaRDD rowRDD = input.map(pm -> RowFactory.create(pm.getName(), + pm.getPandas().stream() + .map(pi -> RowFactory.create(pi.getId(), pi.getZip(), pi.isHappy(), pi.getAttributes())) + .collect(Collectors.toList()))); + + ArrayType pandasType = DataTypes.createArrayType(new StructType( + new StructField[]{ + new StructField("id", DataTypes.LongType, true, Metadata.empty()), + new StructField("zip", DataTypes.StringType, true, Metadata.empty()), + new StructField("happy", DataTypes.BooleanType, true, Metadata.empty()), + new StructField("attributes", DataTypes.createArrayType(DataTypes.FloatType), true, Metadata.empty()) + } + )); + + StructType schema = new StructType(new StructField[]{ + new StructField("name", DataTypes.StringType, true, Metadata.empty()), + new StructField("pandas", pandasType, true, Metadata.empty()) + }); + + DataFrame df2 = sqlContext.createDataFrame(rowRDD, schema); + return df2; + } + //end::createFromRDD[] + + //tag::createFromLocal[] + public DataFrame createFromLocal(List input) { + return sqlContext.createDataFrame(input, PandaPlace.class); + } + //end::createFromLocal[] + + //tag::collectResults[] + public Row[] collectDF(DataFrame df) { + return df.collect(); + } + //end::collectResults[] + + //tag::toRDD[] + public JavaRDD toRDD(DataFrame input) { + JavaRDD rdd = input.javaRDD().map(row -> new JavaRawPanda(row.getLong(0), row.getString(1), + row.getString(2), row.getBoolean(3), row.getList(4))); + return rdd; + } + //end::toRDD[] + + //tag::partitionedOutput[] + public void writeOutByZip(DataFrame input) { + input.write().partitionBy("zipcode").format("json").save("output/"); + } + //end::partitionedOutput[] + + //tag::saveAppend[] + public void writeAppend(DataFrame input) { + input.write().mode(SaveMode.Append).save("output/"); + } + //end::saveAppend[] + + public DataFrame createJDBC() { + //tag::createJDBC[] + DataFrame df1 = sqlContext.read().jdbc("jdbc:dialect:serverName;user=user;password=pass", + "table", new Properties()); + + DataFrame df2 = sqlContext.read().format("jdbc") + .option("url", "jdbc:dialect:serverName") + .option("dbtable", "table").load(); + + return df2; + //end::createJDBC[] + } + + public void writeJDBC(DataFrame df) { + //tag::writeJDBC[] + df.write().jdbc("jdbc:dialect:serverName;user=user;password=pass", + "table", new Properties()); + + df.write().format("jdbc") + .option("url", "jdbc:dialect:serverName") + .option("user", "user") + .option("password", "pass") + .option("dbtable", "table").save(); + //end::writeJDBC[] + } + + //tag::loadParquet[] + public DataFrame loadParquet(String path) { + // Configure Spark to read binary data as string, note: must be configured on SQLContext + sqlContext.setConf("spark.sql.parquet.binaryAsString", "true"); + + // Load parquet data using merge schema (configured through option) + DataFrame df = sqlContext.read() + .option("mergeSchema", "true") + .format("parquet") + .load(path); + + return df; + } + //end::loadParquet[] + + //tag::writeParquet[] + public void writeParquet(DataFrame df, String path) { + df.write().format("parquet").save(path); + } + //end::writeParquet[] + + //tag::loadHiveTable[] + public DataFrame loadHiveTable() { + return sqlContext.read().table("pandas"); + } + //end::loadHiveTable[] + + //tag::saveManagedTable[] + public void saveManagedTable(DataFrame df) { + df.write().saveAsTable("pandas"); + } + //end::saveManagedTable[] +} diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala index ec63e12..2c865f8 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/LoadSave.scala @@ -23,13 +23,15 @@ case class LoadSave(sqlContext: SQLContext) { val rowRDD = input.map(pm => Row(pm.name, pm.pandas.map(pi => Row(pi.id, pi.zip, pi.happy, pi.attributes)))) + val pandasType = ArrayType(StructType(List( + StructField("id", LongType, true), + StructField("zip", StringType, true), + StructField("happy", BooleanType, true), + StructField("attributes", ArrayType(FloatType), true)))) + // Create DataFrame explicitly with specified schema val schema = StructType(List(StructField("name", StringType, true), - StructField("pandas", ArrayType(StructType(List( - StructField("id", LongType, true), - StructField("zip", StringType, true), - StructField("happy", BooleanType, true), - StructField("attributes", ArrayType(FloatType), true))))))) + StructField("pandas", pandasType))) val df3 = sqlContext.createDataFrame(rowRDD, schema) } @@ -72,6 +74,7 @@ case class LoadSave(sqlContext: SQLContext) { //tag::createJDBC[] sqlContext.read.jdbc("jdbc:dialect:serverName;user=user;password=pass", "table", new Properties) + sqlContext.read.format("jdbc") .option("url", "jdbc:dialect:serverName") .option("dbtable", "table").load() @@ -82,6 +85,7 @@ case class LoadSave(sqlContext: SQLContext) { //tag::writeJDBC[] df.write.jdbc("jdbc:dialect:serverName;user=user;password=pass", "table", new Properties) + df.write.format("jdbc") .option("url", "jdbc:dialect:serverName") .option("user", "user") @@ -94,6 +98,7 @@ case class LoadSave(sqlContext: SQLContext) { def loadParquet(path: String): DataFrame = { // Configure Spark to read binary data as string, note: must be configured on SQLContext sqlContext.setConf("spark.sql.parquet.binaryAsString", "true") + // Load parquet data using merge schema (configured through option) sqlContext.read .option("mergeSchema", "true") From f3387148dadb3b5e820d14a03036dc873a0b6e96 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Mon, 23 May 2016 07:53:58 +0200 Subject: [PATCH 189/198] Port UDFs to Java --- .../examples/dataframe/JavaUDFs.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java new file mode 100644 index 0000000..467b8b7 --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java @@ -0,0 +1,74 @@ +package com.highperformancespark.examples.dataframe; + +import org.apache.spark.sql.Row; +import org.apache.spark.sql.SQLContext; +import org.apache.spark.sql.expressions.MutableAggregationBuffer; +import org.apache.spark.sql.expressions.UserDefinedAggregateFunction; +import org.apache.spark.sql.types.*; + +public class JavaUDFs { + + public static void setupUDFs(SQLContext sqlContext) { + sqlContext.udf().register("strlen", (String s) -> s.length(), DataTypes.StringType); + } + + public static void setupUDAFs(SQLContext sqlContext) { + + class Avg extends UserDefinedAggregateFunction { + + @Override + public StructType inputSchema() { + StructType inputSchema = + new StructType(new StructField[]{new StructField("value", DataTypes.DoubleType, true, Metadata.empty())}); + return inputSchema; + } + + @Override + public StructType bufferSchema() { + StructType bufferSchema = + new StructType(new StructField[]{ + new StructField("count", DataTypes.LongType, true, Metadata.empty()), + new StructField("sum", DataTypes.DoubleType, true, Metadata.empty()) + }); + + return bufferSchema; + } + + @Override + public DataType dataType() { + return DataTypes.DoubleType; + } + + @Override + public boolean deterministic() { + return true; + } + + @Override + public void initialize(MutableAggregationBuffer buffer) { + buffer.update(0, 0L); + buffer.update(1, 0.0); + } + + @Override + public void update(MutableAggregationBuffer buffer, Row input) { + buffer.update(0, buffer.getLong(0) + 1); + buffer.update(1, buffer.getDouble(1) + input.getDouble(0)); + } + + @Override + public void merge(MutableAggregationBuffer buffer1, Row buffer2) { + buffer1.update(0, buffer1.getLong(0) + buffer2.getLong(0)); + buffer1.update(1, buffer1.getDouble(1) + buffer2.getDouble(1)); + } + + @Override + public Object evaluate(Row buffer) { + return Math.pow(buffer.getDouble(1), 1.0 / buffer.getLong(0)); + } + } + + Avg average = new Avg(); + sqlContext.udf().register("ourAvg", average); + } +} From 6e4ea731d557dcc0bd986122ec9124883b7da34f Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Mon, 23 May 2016 10:14:17 +0200 Subject: [PATCH 190/198] Fix evaluate average UDF --- .../com/highperformancespark/examples/dataframe/JavaUDFs.java | 2 +- .../com/high-performance-spark-examples/dataframe/UDFs.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java index 467b8b7..6866e4b 100644 --- a/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java @@ -64,7 +64,7 @@ public void merge(MutableAggregationBuffer buffer1, Row buffer2) { @Override public Object evaluate(Row buffer) { - return Math.pow(buffer.getDouble(1), 1.0 / buffer.getLong(0)); + return buffer.getDouble(1) / buffer.getLong(0); } } diff --git a/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala b/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala index 2274781..56d4beb 100644 --- a/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala +++ b/src/main/scala/com/high-performance-spark-examples/dataframe/UDFs.scala @@ -47,7 +47,7 @@ object UDFs { } def evaluate(buffer: Row): Any = { - math.pow(buffer.getDouble(1), 1.toDouble / buffer.getLong(0)) + buffer.getDouble(1) / buffer.getLong(0) } } // Optionally register From 0b0ea92a558235c3c5dc3f6cb222261a963090ab Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 23 May 2016 15:42:59 -0700 Subject: [PATCH 191/198] Add tag for basicUDF to JavaUDFs.java --- .../com/highperformancespark/examples/dataframe/JavaUDFs.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java b/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java index 6866e4b..dd23616 100644 --- a/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java +++ b/src/main/java/com/highperformancespark/examples/dataframe/JavaUDFs.java @@ -9,7 +9,9 @@ public class JavaUDFs { public static void setupUDFs(SQLContext sqlContext) { + //tag::basicUDF[] sqlContext.udf().register("strlen", (String s) -> s.length(), DataTypes.StringType); + //end::basicUDF[] } public static void setupUDAFs(SQLContext sqlContext) { From 5662b36fd6487575746ef2d814d2980b4ba81e92 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Mon, 23 May 2016 17:36:25 -0700 Subject: [PATCH 192/198] Start adding Java interop example. TODO test and make sure the non-casted fake class tag works as expected. --- .../examples/JavaInterop.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/com/highperformancespark/examples/JavaInterop.java diff --git a/src/main/java/com/highperformancespark/examples/JavaInterop.java b/src/main/java/com/highperformancespark/examples/JavaInterop.java new file mode 100644 index 0000000..0e525ea --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/JavaInterop.java @@ -0,0 +1,31 @@ +package com.highperformancespark.examples; + +import scala.reflect.*; +import scala.Tuple2; + +import org.apache.spark.rdd.RDD; +import org.apache.spark.api.java.JavaRDD; +import org.apache.spark.api.java.JavaPairRDD; +import org.apache.spark.api.java.JavaSparkContext; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.spark.sql.functions.*; + +public class JavaInterop { + public static JavaPairRDD wrapPairRDD(RDD> rdd) { + // Construct the class tags + ClassTag strCt = ClassTag$.MODULE$.apply(String.class); + ClassTag longCt = ClassTag$.MODULE$.apply(Long.class); + return new JavaPairRDD(rdd, strCt, longCt); + } + + public static JavaPairRDD wrapPairRDDFakeCt(RDD> rdd) { + // Construct the class tags by casting AnyRef - this would be more commonly done with + // generic or templated code where we can't explicitly construct the correct class tag + // as using fake class tags may result in degraded performance. + ClassTag fake = ClassTag$.MODULE$.AnyRef(); + return new JavaPairRDD(rdd, fake, fake); + } +} From 6bd9e65c0457c9b7fdfd0b604fd29ad9b39ce60a Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 24 May 2016 19:51:29 -0700 Subject: [PATCH 193/198] Add some tests for the JavaInterop component --- .../examples/JavaInterop.java | 6 +-- .../examples/JavaInteropTest.java | 43 +++++++++++++++++++ .../examples/JavaInteropHelper.scala | 11 +++++ 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/highperformancespark/examples/JavaInteropTest.java create mode 100644 src/test/scala/com/highperformancespark/examples/JavaInteropHelper.scala diff --git a/src/main/java/com/highperformancespark/examples/JavaInterop.java b/src/main/java/com/highperformancespark/examples/JavaInterop.java index 0e525ea..0bfe91c 100644 --- a/src/main/java/com/highperformancespark/examples/JavaInterop.java +++ b/src/main/java/com/highperformancespark/examples/JavaInterop.java @@ -14,14 +14,14 @@ import static org.apache.spark.sql.functions.*; public class JavaInterop { - public static JavaPairRDD wrapPairRDD(RDD> rdd) { + public static JavaPairRDD wrapPairRDD(RDD> rdd) { // Construct the class tags ClassTag strCt = ClassTag$.MODULE$.apply(String.class); - ClassTag longCt = ClassTag$.MODULE$.apply(Long.class); + ClassTag longCt = ClassTag$.MODULE$.apply(scala.Long.class); return new JavaPairRDD(rdd, strCt, longCt); } - public static JavaPairRDD wrapPairRDDFakeCt(RDD> rdd) { + public static JavaPairRDD wrapPairRDDFakeCt(RDD> rdd) { // Construct the class tags by casting AnyRef - this would be more commonly done with // generic or templated code where we can't explicitly construct the correct class tag // as using fake class tags may result in degraded performance. diff --git a/src/test/java/com/highperformancespark/examples/JavaInteropTest.java b/src/test/java/com/highperformancespark/examples/JavaInteropTest.java new file mode 100644 index 0000000..66318f7 --- /dev/null +++ b/src/test/java/com/highperformancespark/examples/JavaInteropTest.java @@ -0,0 +1,43 @@ +package com.highperformancespark.examples; + +import com.holdenkarau.spark.testing.SharedJavaSparkContext; + +import scala.Tuple2; + +import org.apache.spark.rdd.RDD; +import org.apache.spark.api.java.JavaRDD; +import org.apache.spark.api.java.JavaPairRDD; +import org.apache.spark.api.java.JavaSparkContext; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class JavaInteropTest extends SharedJavaSparkContext { + + @Test + public void wrapPairRDDTest() { + JavaInteropTestHelper helper = new JavaInteropTestHelper(sc()); + JavaInterop ji = new JavaInterop(); + RDD> rdd = helper.generateMiniPairRDD(); + JavaPairRDD prdd = ji.wrapPairRDD(rdd); + List> expected = Arrays.asList(new Tuple2("panda", 12L)); + assertEquals(expected, prdd.collect()); + } + + @Test + public void wrapPairRDDFakeCtTest() { + JavaInteropTestHelper helper = new JavaInteropTestHelper(sc()); + JavaInterop ji = new JavaInterop(); + RDD> rdd = helper.generateMiniPairRDD(); + JavaPairRDD prdd = ji.wrapPairRDDFakeCt(rdd); + List> expected = Arrays.asList(new Tuple2("panda", 12L)); + assertEquals(expected, prdd.collect()); + } +} diff --git a/src/test/scala/com/highperformancespark/examples/JavaInteropHelper.scala b/src/test/scala/com/highperformancespark/examples/JavaInteropHelper.scala new file mode 100644 index 0000000..4d983a6 --- /dev/null +++ b/src/test/scala/com/highperformancespark/examples/JavaInteropHelper.scala @@ -0,0 +1,11 @@ +package com.highperformancespark.examples + + +import org.apache.spark.SparkContext +import org.apache.spark.rdd.RDD + +class JavaInteropTestHelper(sc: SparkContext) { + def generateMiniPairRDD(): RDD[(String, Long)] = { + sc.parallelize(List(("panda", 12L))) + } +} From 2393c59b0352c7201cc6cd860a0bd3b507c2ab21 Mon Sep 17 00:00:00 2001 From: Holden Karau Date: Tue, 24 May 2016 19:58:32 -0700 Subject: [PATCH 194/198] Add tags so we can include it --- .../java/com/highperformancespark/examples/JavaInterop.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/highperformancespark/examples/JavaInterop.java b/src/main/java/com/highperformancespark/examples/JavaInterop.java index 0bfe91c..3b37093 100644 --- a/src/main/java/com/highperformancespark/examples/JavaInterop.java +++ b/src/main/java/com/highperformancespark/examples/JavaInterop.java @@ -14,13 +14,17 @@ import static org.apache.spark.sql.functions.*; public class JavaInterop { + + //tag::realClassTag[] public static JavaPairRDD wrapPairRDD(RDD> rdd) { // Construct the class tags ClassTag strCt = ClassTag$.MODULE$.apply(String.class); ClassTag longCt = ClassTag$.MODULE$.apply(scala.Long.class); return new JavaPairRDD(rdd, strCt, longCt); } + //end::realClassTag[] + //tag::fakeClassTag[] public static JavaPairRDD wrapPairRDDFakeCt(RDD> rdd) { // Construct the class tags by casting AnyRef - this would be more commonly done with // generic or templated code where we can't explicitly construct the correct class tag @@ -28,4 +32,5 @@ public static JavaPairRDD wrapPairRDDFakeCt(RDD> rdd) { ClassTag fake = ClassTag$.MODULE$.AnyRef(); return new JavaPairRDD(rdd, fake, fake); } + //end::fakeClassTag[] } From 9fd5d78294d516f571d92a5505241f6ebba30f88 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Sat, 21 May 2016 10:39:44 +0200 Subject: [PATCH 195/198] Port GoldiLocksGroupByKey to Java --- .../goldilocks/JavaGoldiLocksGroupByKey.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksGroupByKey.java diff --git a/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksGroupByKey.java b/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksGroupByKey.java new file mode 100644 index 0000000..f5f72ce --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksGroupByKey.java @@ -0,0 +1,32 @@ +package com.highperformancespark.examples.goldilocks; + +import org.apache.spark.api.java.JavaPairRDD; + +import java.util.*; +import java.util.stream.Collectors; + +public class JavaGoldiLocksGroupByKey { + //tag::groupByKey[] + public Map> findRankStatistics( + JavaPairRDD pairRDD, List ranks) { + + Map> element_ranks = pairRDD.groupByKey().mapValues(iter -> { + List values = new ArrayList<>(); + Iterator iterator = iter.iterator(); + while (iterator.hasNext()) + values.add(iterator.next()); + Collections.sort(values); + + List result = + ranks.stream() + .map(n -> values.get(new Long(n).intValue())) + .collect(Collectors.toList()); + + return result; + }).collectAsMap(); + + return element_ranks; + } + //end::groupByKey[] + +} From abd4365e31fad192662d2b866583ea4f9069f869 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Sun, 22 May 2016 11:27:25 +0200 Subject: [PATCH 196/198] Port GoldiLocksFirstTry to Java --- .../goldilocks/JavaGoldiLocksFirstTry.java | 264 ++++++++++++++++++ .../GoldiLocks/GoldiLocksFirstTry.scala | 21 +- 2 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksFirstTry.java diff --git a/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksFirstTry.java b/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksFirstTry.java new file mode 100644 index 0000000..df79a34 --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksFirstTry.java @@ -0,0 +1,264 @@ +package com.highperformancespark.examples.goldilocks; + +import org.apache.spark.api.java.JavaPairRDD; +import org.apache.spark.api.java.JavaRDD; +import org.apache.spark.api.java.function.Function2; +import org.apache.spark.api.java.function.PairFlatMapFunction; +import org.apache.spark.api.java.function.PairFunction; +import org.apache.spark.sql.DataFrame; +import org.apache.spark.sql.Row; +import org.apache.spark.storage.StorageLevel; +import scala.Tuple2; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class JavaGoldiLocksFirstTry { + + /** + * Find nth target rank for every column. + * + * For example: + * + * dataframe: + * (0.0, 4.5, 7.7, 5.0) + * (1.0, 5.5, 6.7, 6.0) + * (2.0, 5.5, 1.5, 7.0) + * (3.0, 5.5, 0.5, 7.0) + * (4.0, 5.5, 0.5, 8.0) + * + * targetRanks: + * 1, 3 + * + * The output will be: + * 0 -> (0.0, 2.0) + * 1 -> (4.5, 5.5) + * 2 -> (7.7, 1.5) + * 3 -> (5.0, 7.0) + * + * @param dataframe dataframe of doubles + * @param targetRanks the required ranks for every column + * + * @return map of (column index, list of target ranks) + */ + public Map> findRankStatistics(DataFrame dataframe, List targetRanks) { + JavaPairRDD valueColumnPairs = getValueColumnPairs(dataframe); + + JavaPairRDD sortedValueColumnPairs = valueColumnPairs.sortByKey(); + sortedValueColumnPairs.persist(StorageLevel.MEMORY_AND_DISK()); + + int numOfColumns = dataframe.schema().length(); + List>> partitionColumnsFreq = + getColumnsFreqPerPartition(sortedValueColumnPairs, numOfColumns); + + List>>> ranksLocations = + getRanksLocationsWithinEachPart(targetRanks, partitionColumnsFreq, numOfColumns); + + findTargetRanksIteratively(sortedValueColumnPairs, ranksLocations); + + return null; + } + + /** + * Step 1. Map the rows to pairs of (value, column Index). + * + * For example: + * + * dataFrame: + * 1.5, 1.25, 2.0 + * 5.25, 2.5, 1.5 + * + * The output RDD will be: + * (1.5, 0) (1.25, 1) (2.0, 2) (5.25, 0) (2.5, 1) (1.5, 2) + * + * @param dataframe dateframe of doubles + * + * @return RDD of pairs (value, column Index) + */ + private JavaPairRDD getValueColumnPairs(DataFrame dataframe) { + JavaPairRDD value_ColIndex = + dataframe.javaRDD().flatMapToPair((PairFlatMapFunction) row -> { + List rowList = (List) (Object) toList(row.toSeq()); + List> list = zipWithIndex(rowList); + return list; + }); + + return value_ColIndex; + } + + /** + * Step 2. Find the number of elements for each column in each partition. + * + * For Example: + * + * sortedValueColumnPairs: + * Partition 1: (1.5, 0) (1.25, 1) (2.0, 2) (5.25, 0) + * Partition 2: (7.5, 1) (9.5, 2) + * + * numOfColumns: 3 + * + * The output will be: + * [(0, [2, 1, 1]), (1, [0, 1, 1])] + * + * @param sortedValueColumnPairs - sorted RDD of (value, column Index) pairs + * @param numOfColumns the number of columns + * + * @return Array that contains (partition index, number of elements from every column on this partition) + */ + private List>> getColumnsFreqPerPartition(JavaPairRDD sortedValueColumnPairs, int numOfColumns) { + List>> columsFreqPerPartition = + sortedValueColumnPairs.mapPartitionsWithIndex((partitionIndex, valueColumnPairs) -> { + Long[] freq = new Long[numOfColumns]; + Arrays.fill(freq, 0); + + while(valueColumnPairs.hasNext()) { + int colIndex = valueColumnPairs.next()._2; + freq[colIndex] = freq[colIndex] + 1; + } + + List freqList = Arrays.asList(freq); + List>> partitionList = Arrays.asList(new Tuple2<>(partitionIndex, freqList)); + return partitionList.iterator(); + }, false).collect(); + + return columsFreqPerPartition; + } + + /** + * Step 3: For each Partition determine the index of the elements that are desired rank statistics + * + * For Example: + * targetRanks: 5 + * partitionColumnsFreq: [(0, [2, 3]), (1, [4, 1]), (2, [5, 2])] + * numOfColumns: 2 + * + * The output will be: + * [(0, []), (1, [(colIdx=0, rankLocation=3)]), (2, [(colIndex=1, rankLocation=1)])] + * + * @param partitionColumnsFreq Array of (partition index, columns frequencies per this partition) + * + * @return Array that contains (partition index, relevantIndexList where relevantIndexList(i) = the index + * of an element on this partition that matches one of the target ranks) + */ + private List>>> getRanksLocationsWithinEachPart(List targetRanks, + List>> partitionColumnsFreq, int numOfColumns) { + + long[] runningTotal = new long[numOfColumns]; + + List>>> ranksLocations = + partitionColumnsFreq + .stream() + .sorted((o1, o2) -> o1._1.compareTo(o2._1)) + .map(partitionIndex_columnsFreq -> { + int partitionIndex = partitionIndex_columnsFreq._1; + List columnsFreq = partitionIndex_columnsFreq._2; + + List> relevantIndexList = new ArrayList<>(); + + zipWithIndex(columnsFreq).stream().forEach(colCount_colIndex -> { + long colCount = colCount_colIndex._1; + int colIndex = colCount_colIndex._2; + + long runningTotalCol = runningTotal[colIndex]; + Stream ranksHere = + targetRanks.stream().filter(rank -> runningTotalCol < rank && runningTotalCol + colCount >= rank); + + // for each of the rank statistics present add this column index and the index it will be at + // on this partition (the rank - the running total) + relevantIndexList.addAll( + ranksHere.map(rank -> new Tuple2<>(colIndex, rank - runningTotalCol)).collect(Collectors.toList())); + + runningTotal[colIndex] += colCount; + }); + + + return new Tuple2<>(partitionIndex, relevantIndexList); + }).collect(Collectors.toList()); + + return ranksLocations; + } + + /** + * Finds rank statistics elements using ranksLocations. + * + * @param sortedValueColumnPairs - sorted RDD of (value, colIndex) pairs + * @param ranksLocations Array of (partition Index, list of (column index, rank index of this column at this partition)) + * + * @return returns RDD of the target ranks (column index, value) + */ + private JavaPairRDD findTargetRanksIteratively(JavaPairRDD sortedValueColumnPairs, + List>>> ranksLocations) { + + JavaRDD> targetRanks = sortedValueColumnPairs.mapPartitionsWithIndex( + (partitionIndex, valueColumnPairs) -> { + List> targetsInThisPart = ranksLocations.get(partitionIndex)._2; + List> result = new ArrayList<>(); + + if (!targetsInThisPart.isEmpty()) { + Map> columnsRelativeIndex = groupByKey(targetsInThisPart); + Set columnsInThisPart = columnsRelativeIndex.keySet(); + + Map runningTotals = toMap(columnsInThisPart); + + // filter this iterator, so that it contains only those (value, columnIndex) that are the ranks statistics on this partition + // I.e. Keep track of the number of elements we have seen for each columnIndex using the + // running total hashMap. Keep those pairs for which value is the nth element for that columnIndex that appears on this partition + // and the map contains (columnIndex, n). + + while (valueColumnPairs.hasNext()) { + Tuple2 value_colIndex = valueColumnPairs.next(); + double value = value_colIndex._1; + int colIndex = value_colIndex._2; + + if (columnsInThisPart.contains(colIndex)) { + long total = runningTotals.get(colIndex) + 1L; + runningTotals.put(colIndex, total); + if (columnsRelativeIndex.get(colIndex).contains(total)) { + result.add(value_colIndex.swap()); + } + } + } + } + + return result.iterator(); + }, false); + + return targetRanks.mapToPair((PairFunction, Integer, Double>) t -> t); + } + + private Map toMap(Set set) { + Map map = new HashMap<>(); + for (int k: set) + map.put(k, 0L); + + return map; + } + + private Map> groupByKey(List> list) { + Map> map = new HashMap<>(); + for (int i = 0; i < list.size(); i++) { + Tuple2 curr = list.get(i); + if (!map.containsKey(curr._1)) + map.put(curr._1, new ArrayList<>()); + + map.get(curr._1).add(curr._2); + } + + return map; + } + + private List toList(scala.collection.Seq seq) { + return scala.collection.JavaConversions.seqAsJavaList(seq); + } + + private List> zipWithIndex(List list) { + List> indexedList = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) + indexedList.add(new Tuple2<>(list.get(i), i)); + + return indexedList; + } + +} + diff --git a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala index 737bafa..3d9eb94 100644 --- a/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala +++ b/src/main/scala/com/high-performance-spark-examples/GoldiLocks/GoldiLocksFirstTry.scala @@ -44,19 +44,18 @@ object GoldiLocksFirstTry { * 2 -> (7.7, 1.5) * 3 -> (5.0, 7.0) * - * @param dataFrame dataframe of doubles + * @param dataframe dataframe of doubles * @param targetRanks the required ranks for every column - * * @return map of (column index, list of target ranks) */ - def findRankStatistics(dataFrame: DataFrame, targetRanks: List[Long]): + def findRankStatistics(dataframe: DataFrame, targetRanks: List[Long]): Map[Int, Iterable[Double]] = { - val valueColumnPairs: RDD[(Double, Int)] = getValueColumnPairs(dataFrame) + val valueColumnPairs: RDD[(Double, Int)] = getValueColumnPairs(dataframe) val sortedValueColumnPairs = valueColumnPairs.sortByKey() sortedValueColumnPairs.persist(StorageLevel.MEMORY_AND_DISK) - val numOfColumns = dataFrame.schema.length + val numOfColumns = dataframe.schema.length val partitionColumnsFreq = getColumnsFreqPerPartition(sortedValueColumnPairs, numOfColumns) val ranksLocations = getRanksLocationsWithinEachPart(targetRanks, partitionColumnsFreq, numOfColumns) @@ -76,12 +75,12 @@ object GoldiLocksFirstTry { * The output RDD will be: * (1.5, 0) (1.25, 1) (2.0, 2) (5.25, 0) (2.5, 1) (1.5, 2) * - * @param dataFrame dateframe of doubles + * @param dataframe dateframe of doubles * * @return RDD of pairs (value, column Index) */ - private def getValueColumnPairs(dataFrame : DataFrame): RDD[(Double, Int)] = { - dataFrame.flatMap(row => row.toSeq.zipWithIndex.map{ case (v, index) => + private def getValueColumnPairs(dataframe : DataFrame): RDD[(Double, Int)] = { + dataframe.flatMap(row => row.toSeq.zipWithIndex.map{ case (v, index) => (v.toString.toDouble, index)}) } @@ -135,7 +134,7 @@ object GoldiLocksFirstTry { * numOfColumns: 2 * * The output will be: - * [(0, []), (1, [(0, 3)]), (2, [(1, 1)])] + * [(0, []), (1, [(colIdx=0, rankLocation=3)]), (2, [(colIndex=1, rankLocation=1)])] * * @param partitionColumnsFreq Array of (partition index, columns frequencies per this partition) * @@ -182,12 +181,12 @@ object GoldiLocksFirstTry { val targetsInThisPart: List[(Int, Long)] = ranksLocations(partitionIndex)._2 if (targetsInThisPart.nonEmpty) { val columnsRelativeIndex: Map[Int, List[Long]] = targetsInThisPart.groupBy(_._1).mapValues(_.map(_._2)) - val columnsInThisPart = targetsInThisPart.map(_._1).distinct + val columnsInThisPart = targetsInThisPart.map(_._1) val runningTotals : mutable.HashMap[Int, Long]= new mutable.HashMap() runningTotals ++= columnsInThisPart.map(columnIndex => (columnIndex, 0L)).toMap - //filter this iterator, so that it contains only those (value, columnIndex) that are the ranks statistics on this partition + // filter this iterator, so that it contains only those (value, columnIndex) that are the ranks statistics on this partition // I.e. Keep track of the number of elements we have seen for each columnIndex using the // running total hashMap. Keep those pairs for which value is the nth element for that columnIndex that appears on this partition // and the map contains (columnIndex, n). From 512c6162986ddac21f95da36fe1ec43c5fcf8e99 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Wed, 25 May 2016 07:46:44 +0200 Subject: [PATCH 197/198] Use SharedSparkContext at QuantileOnlyArtisanalTest --- .../QuantileOnlyArtisanalTest.scala | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index f67dc01..9ece5a5 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -1,20 +1,11 @@ package com.highperformancespark.examples.goldilocks -import org.apache.spark._ +import com.holdenkarau.spark.testing.SharedSparkContext import org.apache.spark.sql.SQLContext -import org.scalatest.{BeforeAndAfterAll, FunSuite} +import org.scalatest.FunSuite // tag::MAGIC_PANDA[] -class QuantileOnlyArtisanalTest extends FunSuite with BeforeAndAfterAll { - @transient private var _sc: SparkContext = _ - def sc: SparkContext = _sc - - val conf = new SparkConf().setMaster("local[4]").setAppName("test") - - override def beforeAll() { - _sc = new SparkContext(conf) - super.beforeAll() - } +class QuantileOnlyArtisanalTest extends FunSuite with SharedSparkContext { val inputList = List(GoldiLocksRow(0.0, 4.5, 7.7, 5.0), GoldiLocksRow(1.0, 5.5, 6.7, 6.0), @@ -99,13 +90,6 @@ class QuantileOnlyArtisanalTest extends FunSuite with BeforeAndAfterAll { }) } - override def afterAll() { - // We clear the driver port so that we don't try and bind to the same port on restart - sc.stop() - System.clearProperty("spark.driver.port") - _sc = null - super.afterAll() - } } // end::MAGIC_PANDA[] From 40b8f69a84a4f5f1d64e2e1ba7381d6fec655015 Mon Sep 17 00:00:00 2001 From: Mahmoud Hanafy Date: Wed, 25 May 2016 09:11:37 +0200 Subject: [PATCH 198/198] Add test case for JavaGoldiLocksFirstTry --- .../goldilocks/JavaGoldiLocksFirstTry.java | 24 ++++----- .../examples/objects/JavaGoldiLocksRow.java | 49 +++++++++++++++++++ .../JavaQuantileOnlyArtisanalTest.java | 42 ++++++++++++++++ .../QuantileOnlyArtisanalTest.scala | 5 +- 4 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/highperformancespark/examples/objects/JavaGoldiLocksRow.java create mode 100644 src/test/java/com/highperformancespark/examples/goldilocks/JavaQuantileOnlyArtisanalTest.java diff --git a/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksFirstTry.java b/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksFirstTry.java index df79a34..d8ffcc9 100644 --- a/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksFirstTry.java +++ b/src/main/java/com/highperformancespark/examples/goldilocks/JavaGoldiLocksFirstTry.java @@ -42,7 +42,7 @@ public class JavaGoldiLocksFirstTry { * * @return map of (column index, list of target ranks) */ - public Map> findRankStatistics(DataFrame dataframe, List targetRanks) { + public static Map> findRankStatistics(DataFrame dataframe, List targetRanks) { JavaPairRDD valueColumnPairs = getValueColumnPairs(dataframe); JavaPairRDD sortedValueColumnPairs = valueColumnPairs.sortByKey(); @@ -55,9 +55,9 @@ public Map> findRankStatistics(DataFrame dataframe, Li List>>> ranksLocations = getRanksLocationsWithinEachPart(targetRanks, partitionColumnsFreq, numOfColumns); - findTargetRanksIteratively(sortedValueColumnPairs, ranksLocations); + JavaPairRDD targetRanksValues = findTargetRanksIteratively(sortedValueColumnPairs, ranksLocations); - return null; + return targetRanksValues.groupByKey().collectAsMap(); } /** @@ -76,7 +76,7 @@ public Map> findRankStatistics(DataFrame dataframe, Li * * @return RDD of pairs (value, column Index) */ - private JavaPairRDD getValueColumnPairs(DataFrame dataframe) { + private static JavaPairRDD getValueColumnPairs(DataFrame dataframe) { JavaPairRDD value_ColIndex = dataframe.javaRDD().flatMapToPair((PairFlatMapFunction) row -> { List rowList = (List) (Object) toList(row.toSeq()); @@ -106,11 +106,11 @@ private JavaPairRDD getValueColumnPairs(DataFrame dataframe) { * * @return Array that contains (partition index, number of elements from every column on this partition) */ - private List>> getColumnsFreqPerPartition(JavaPairRDD sortedValueColumnPairs, int numOfColumns) { + private static List>> getColumnsFreqPerPartition(JavaPairRDD sortedValueColumnPairs, int numOfColumns) { List>> columsFreqPerPartition = sortedValueColumnPairs.mapPartitionsWithIndex((partitionIndex, valueColumnPairs) -> { Long[] freq = new Long[numOfColumns]; - Arrays.fill(freq, 0); + Arrays.fill(freq, 0L); while(valueColumnPairs.hasNext()) { int colIndex = valueColumnPairs.next()._2; @@ -141,7 +141,7 @@ private List>> getColumnsFreqPerPartition(JavaPairRDD * @return Array that contains (partition index, relevantIndexList where relevantIndexList(i) = the index * of an element on this partition that matches one of the target ranks) */ - private List>>> getRanksLocationsWithinEachPart(List targetRanks, + private static List>>> getRanksLocationsWithinEachPart(List targetRanks, List>> partitionColumnsFreq, int numOfColumns) { long[] runningTotal = new long[numOfColumns]; @@ -187,7 +187,7 @@ private List>>> getRanksLocationsWith * * @return returns RDD of the target ranks (column index, value) */ - private JavaPairRDD findTargetRanksIteratively(JavaPairRDD sortedValueColumnPairs, + private static JavaPairRDD findTargetRanksIteratively(JavaPairRDD sortedValueColumnPairs, List>>> ranksLocations) { JavaRDD> targetRanks = sortedValueColumnPairs.mapPartitionsWithIndex( @@ -227,7 +227,7 @@ private JavaPairRDD findTargetRanksIteratively(JavaPairRDD, Integer, Double>) t -> t); } - private Map toMap(Set set) { + private static Map toMap(Set set) { Map map = new HashMap<>(); for (int k: set) map.put(k, 0L); @@ -235,7 +235,7 @@ private Map toMap(Set set) { return map; } - private Map> groupByKey(List> list) { + private static Map> groupByKey(List> list) { Map> map = new HashMap<>(); for (int i = 0; i < list.size(); i++) { Tuple2 curr = list.get(i); @@ -248,11 +248,11 @@ private Map> groupByKey(List> list) { return map; } - private List toList(scala.collection.Seq seq) { + private static List toList(scala.collection.Seq seq) { return scala.collection.JavaConversions.seqAsJavaList(seq); } - private List> zipWithIndex(List list) { + private static List> zipWithIndex(List list) { List> indexedList = new ArrayList<>(); for (int i = 0; i < list.size(); i++) indexedList.add(new Tuple2<>(list.get(i), i)); diff --git a/src/main/java/com/highperformancespark/examples/objects/JavaGoldiLocksRow.java b/src/main/java/com/highperformancespark/examples/objects/JavaGoldiLocksRow.java new file mode 100644 index 0000000..82cafe9 --- /dev/null +++ b/src/main/java/com/highperformancespark/examples/objects/JavaGoldiLocksRow.java @@ -0,0 +1,49 @@ +package com.highperformancespark.examples.objects; + +import java.io.Serializable; + +public class JavaGoldiLocksRow implements Serializable { + private double a; + private double b; + private double c; + private double d; + + public JavaGoldiLocksRow(double a, double b, double c, double d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } + + public double getA() { + return a; + } + + public void setA(double a) { + this.a = a; + } + + public double getB() { + return b; + } + + public void setB(double b) { + this.b = b; + } + + public double getC() { + return c; + } + + public void setC(double c) { + this.c = c; + } + + public double getD() { + return d; + } + + public void setD(double d) { + this.d = d; + } +} \ No newline at end of file diff --git a/src/test/java/com/highperformancespark/examples/goldilocks/JavaQuantileOnlyArtisanalTest.java b/src/test/java/com/highperformancespark/examples/goldilocks/JavaQuantileOnlyArtisanalTest.java new file mode 100644 index 0000000..98110d0 --- /dev/null +++ b/src/test/java/com/highperformancespark/examples/goldilocks/JavaQuantileOnlyArtisanalTest.java @@ -0,0 +1,42 @@ +package com.highperformancespark.examples.goldilocks; + +import com.google.common.collect.Sets; +import com.highperformancespark.examples.objects.JavaGoldiLocksRow; +import com.holdenkarau.spark.testing.SharedJavaSparkContext; +import org.apache.spark.sql.DataFrame; +import org.apache.spark.sql.SQLContext; +import org.junit.Test; + +import java.util.*; + +import static junit.framework.Assert.assertEquals; + +public class JavaQuantileOnlyArtisanalTest extends SharedJavaSparkContext { + + private List inputList = Arrays.asList( + new JavaGoldiLocksRow(0.0, 4.5, 7.7, 5.0), + new JavaGoldiLocksRow(1.0, 5.5, 6.7, 6.0), + new JavaGoldiLocksRow(2.0, 5.5, 1.5, 7.0), + new JavaGoldiLocksRow(3.0, 5.5, 0.5, 7.0), + new JavaGoldiLocksRow(4.0, 5.5, 0.5, 8.0)); + + @Test + public void goldiLocksFirstTry() { + SQLContext sqlContext = new SQLContext(jsc()); + DataFrame input = sqlContext.createDataFrame(inputList, JavaGoldiLocksRow.class); + Map> secondAndThird = JavaGoldiLocksFirstTry.findRankStatistics(input, Arrays.asList(2L, 3L)); + + Map> expectedResult = new HashMap<>(); + expectedResult.put(0, new HashSet<>(Arrays.asList(1.0, 2.0))); + expectedResult.put(1, new HashSet<>(Arrays.asList(5.5, 5.5))); + expectedResult.put(2, new HashSet<>(Arrays.asList(0.5, 1.5))); + expectedResult.put(3, new HashSet<>(Arrays.asList(6.0, 7.0))); + + for (Map.Entry> entry: secondAndThird.entrySet()) { + Set resultSet = Sets.newHashSet(entry.getValue()); + Set expectedSet = expectedResult.get(entry.getKey()); + + assertEquals(expectedSet, resultSet); + } + } +} diff --git a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala index 9ece5a5..5126ce4 100644 --- a/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala +++ b/src/test/scala/com/high-performance-spark-examples/GoldiLocks/QuantileOnlyArtisanalTest.scala @@ -7,7 +7,8 @@ import org.scalatest.FunSuite // tag::MAGIC_PANDA[] class QuantileOnlyArtisanalTest extends FunSuite with SharedSparkContext { - val inputList = List(GoldiLocksRow(0.0, 4.5, 7.7, 5.0), + val inputList = List( + GoldiLocksRow(0.0, 4.5, 7.7, 5.0), GoldiLocksRow(1.0, 5.5, 6.7, 6.0), GoldiLocksRow(2.0, 5.5, 1.5, 7.0), GoldiLocksRow(3.0, 5.5, 0.5, 7.0), @@ -93,4 +94,4 @@ class QuantileOnlyArtisanalTest extends FunSuite with SharedSparkContext { } // end::MAGIC_PANDA[] -case class GoldiLocksRow(pandaId : Double, softness : Double, fuzzyness : Double, size : Double ) \ No newline at end of file +case class GoldiLocksRow(pandaId: Double, softness: Double, fuzziness: Double, size: Double) \ No newline at end of file
  • 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%0D2ss%a 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