{"action":"created","comment":{"url":"https://api.github.com/repos/apache/spark/pulls/comments/36240984","id":36240984,"diff_hunk":"@@ -139,200 +202,308 @@ class PrefixSpan private (\n     run(data.rdd.map(_.asScala.map(_.asScala.toArray).toArray))\n   }\n \n+}\n+\n+@Experimental\n+object PrefixSpan extends Logging {\n+\n   /**\n-   * Find the complete set of sequential patterns in the input sequences. This method utilizes\n-   * the internal representation of itemsets as Array[Int] where each itemset is represented by\n-   * a contiguous sequence of non-negative integers and delimiters represented by [[DELIMITER]].\n-   * @param data ordered sequences of itemsets. Items are represented by non-negative integers.\n-   *             Each itemset has one or more items and is delimited by [[DELIMITER]].\n-   * @return a set of sequential pattern pairs,\n-   *         the key of pair is pattern (a list of elements),\n-   *         the value of pair is the pattern's count.\n+   * Find the complete set of frequent sequential patterns in the input sequences.\n+   * @param data ordered sequences of itemsets. We represent a sequence internally as Array[Int],\n+   *             where each itemset is represented by a contiguous sequence of distinct and ordered\n+   *             positive integers. We use 0 as the delimiter at itemset boundaries, including the\n+   *             first and the last position.\n+   * @return an RDD of (frequent sequential pattern, count) pairs,\n+   * @see [[Postfix]]\n    */\n-  private[fpm] def run(data: RDD[Array[Int]]): RDD[(Array[Int], Long)] = {\n+  private[fpm] def genFreqPatterns(\n+      data: RDD[Array[Int]],\n+      minCount: Long,\n+      maxPatternLength: Int,\n+      maxLocalProjDBSize: Long): RDD[(Array[Int], Long)] = {\n     val sc = data.sparkContext\n \n     if (data.getStorageLevel == StorageLevel.NONE) {\n       logWarning(\"Input data is not cached.\")\n     }\n \n-    // Use List[Set[Item]] for internal computation\n-    val sequences = data.map { seq => splitSequence(seq.toList) }\n-\n-    // Convert min support to a min number of transactions for this dataset\n-    val minCount = if (minSupport == 0) 0L else math.ceil(sequences.count() * minSupport).toLong\n-\n-    // (Frequent items -> number of occurrences, all items here satisfy the `minSupport` threshold\n-    val freqItemCounts = sequences\n-      .flatMap(seq => seq.flatMap(nonemptySubsets(_)).distinct.map(item => (item, 1L)))\n-      .reduceByKey(_ + _)\n-      .filter { case (item, count) => (count >= minCount) }\n-      .collect()\n-      .toMap\n-\n-    // Pairs of (length 1 prefix, suffix consisting of frequent items)\n-    val itemSuffixPairs = {\n-      val freqItemSets = freqItemCounts.keys.toSet\n-      val freqItems = freqItemSets.flatten\n-      sequences.flatMap { seq =>\n-        val filteredSeq = seq.map(item => freqItems.intersect(item)).filter(_.nonEmpty)\n-        freqItemSets.flatMap { item =>\n-          val candidateSuffix = LocalPrefixSpan.getSuffix(item, filteredSeq)\n-          candidateSuffix match {\n-            case suffix if !suffix.isEmpty => Some((List(item), suffix))\n-            case _ => None\n+    val postfixes = data.map(items => new Postfix(items))\n+\n+    // Local frequent patterns (prefixes) and their counts.\n+    val localFreqPatterns = mutable.ArrayBuffer.empty[(Array[Int], Long)]\n+    // Prefixes whose projected databases are small.\n+    val smallPrefixes = mutable.Map.empty[Int, Prefix]\n+    val emptyPrefix = Prefix.empty\n+    // Prefixes whose projected databases are large.\n+    var largePrefixes = mutable.Map(emptyPrefix.id -> emptyPrefix)\n+    while (largePrefixes.nonEmpty) {\n+      val numLocalFreqPatterns = localFreqPatterns.length\n+      logInfo(s\"number of local frequent patterns: $numLocalFreqPatterns\")\n+      if (localFreqPatterns.length > 1000000) {\n+        logWarning(\n+          s\"\"\"\n+             | Collected $numLocalFreqPatterns local frequent patterns. You may want to consider:\n+             |   1. increase minSupport,\n+             |   2. decrease maxPatternLength,\n+             |   3. increase maxLocalProjDBSize.\n+           \"\"\".stripMargin)\n+      }\n+      logInfo(s\"number of small prefixes: ${smallPrefixes.size}\")\n+      logInfo(s\"number of large prefixes: ${largePrefixes.size}\")\n+      val largePrefixArray = largePrefixes.values.toArray\n+      val freqPrefixes = postfixes.flatMap { postfix =>\n+          largePrefixArray.flatMap { prefix =>\n+            postfix.project(prefix).genPrefixItems.map { case (item, postfixSize) =>\n+              ((prefix.id, item), (1L, postfixSize))\n+            }\n+          }\n+        }.reduceByKey { case ((c0, s0), (c1, s1)) =>\n+          (c0 + c1, s0 + s1)\n+        }.filter { case (_, (c, _)) => c >= minCount }\n+        .collect()\n+      val newLargePrefixes = mutable.Map.empty[Int, Prefix]\n+      freqPrefixes.foreach { case ((id, item), (count, projDBSize)) =>\n+        val newPrefix = largePrefixes(id) :+ item\n+        localFreqPatterns += ((newPrefix.items :+ 0, count))\n+        if (newPrefix.length < maxPatternLength) {\n+          if (projDBSize > maxLocalProjDBSize) {\n+            newLargePrefixes += newPrefix.id -> newPrefix\n+          } else {\n+            smallPrefixes += newPrefix.id -> newPrefix\n           }\n         }\n       }\n+      largePrefixes = newLargePrefixes\n     }\n \n-    // Accumulator for the computed results to be returned, initialized to the frequent items (i.e.\n-    // frequent length-one prefixes)\n-    var resultsAccumulator = freqItemCounts.map { case (item, count) => (List(item), count) }.toList\n-\n-    // Remaining work to be locally and distributively processed respectfully\n-    var (pairsForLocal, pairsForDistributed) = partitionByProjDBSize(itemSuffixPairs)\n-\n-    // Continue processing until no pairs for distributed processing remain (i.e. all prefixes have\n-    // projected database sizes <= `maxLocalProjDBSize`) or `maxPatternLength` is reached\n-    var patternLength = 1\n-    while (pairsForDistributed.count() != 0 && patternLength < maxPatternLength) {\n-      val (nextPatternAndCounts, nextPrefixSuffixPairs) =\n-        extendPrefixes(minCount, pairsForDistributed)\n-      pairsForDistributed.unpersist()\n-      val (smallerPairsPart, largerPairsPart) = partitionByProjDBSize(nextPrefixSuffixPairs)\n-      pairsForDistributed = largerPairsPart\n-      pairsForDistributed.persist(StorageLevel.MEMORY_AND_DISK)\n-      pairsForLocal ++= smallerPairsPart\n-      resultsAccumulator ++= nextPatternAndCounts.collect()\n-      patternLength += 1 // pattern length grows one per iteration\n+    // Switch to local processing.\n+    val bcSmallPrefixes = sc.broadcast(smallPrefixes)\n+    val distributedFreqPattern = postfixes.flatMap { postfix =>\n+      bcSmallPrefixes.value.values.map { prefix =>\n+        (prefix.id, postfix.project(prefix).compressed)\n+      }.filter(_._2.nonEmpty)\n+    }.groupByKey().flatMap { case (id, projPostfixes) =>\n+      val prefix = bcSmallPrefixes.value(id)\n+      val localPrefixSpan = new LocalPrefixSpan(minCount, maxPatternLength - prefix.length)\n+      // TODO: We collect projected postfixes into memory. We should also compare the performance\n+      // TODO: of keeping them on shuffle files.\n+      localPrefixSpan.run(projPostfixes.toArray).map { case (pattern, count) =>\n+        (prefix.items ++ pattern, count)\n+      }\n     }\n \n-    // Process the small projected databases locally\n-    val remainingResults = getPatternsInLocal(\n-      minCount, sc.parallelize(pairsForLocal, 1).groupByKey())\n-\n-    (sc.parallelize(resultsAccumulator, 1) ++ remainingResults)\n-      .map { case (pattern, count) => (flattenSequence(pattern.reverse).toArray, count) }\n+    // Union local frequent patterns and distributed ones.\n+    val freqPatterns = (sc.parallelize(localFreqPatterns, 1) ++ distributedFreqPattern)\n+      .persist(StorageLevel.MEMORY_AND_DISK)\n+    freqPatterns\n   }\n \n-\n   /**\n-   * Partitions the prefix-suffix pairs by projected database size.\n-   * @param prefixSuffixPairs prefix (length n) and suffix pairs,\n-   * @return prefix-suffix pairs partitioned by whether their projected database size is <= or\n-   *         greater than [[maxLocalProjDBSize]]\n+   * Represents a prefix.\n+   * @param items: items in this prefix, using the internal format\n+   * @param length: length of this prefix, not counting 0\n    */\n-  private def partitionByProjDBSize(prefixSuffixPairs: RDD[(List[Set[Int]], List[Set[Int]])])\n-    : (List[(List[Set[Int]], List[Set[Int]])], RDD[(List[Set[Int]], List[Set[Int]])]) = {\n-    val prefixToSuffixSize = prefixSuffixPairs\n-      .aggregateByKey(0)(\n-        seqOp = { case (count, suffix) => count + suffix.length },\n-        combOp = { _ + _ })\n-    val smallPrefixes = prefixToSuffixSize\n-      .filter(_._2 <= maxLocalProjDBSize)\n-      .keys\n-      .collect()\n-      .toSet\n-    val small = prefixSuffixPairs.filter { case (prefix, _) => smallPrefixes.contains(prefix) }\n-    val large = prefixSuffixPairs.filter { case (prefix, _) => !smallPrefixes.contains(prefix) }\n-    (small.collect().toList, large)\n+  private[fpm] class Prefix private (val items: Array[Int], val length: Int) extends Serializable {\n+\n+    /** A unique id for this prefix. */\n+    val id: Int = Prefix.nextId\n+\n+    /** Expands this prefix by the input item. */\n+    def :+(item: Int): Prefix = {\n+      require(item != 0)\n+      if (item < 0) {\n+        new Prefix(items :+ -item, length + 1)\n+      } else {\n+        new Prefix(items ++ Array(0, item), length + 1)\n+      }\n+    }\n   }\n \n-  /**\n-   * Extends all prefixes by one itemset from their suffix and computes the resulting frequent\n-   * prefixes and remaining work.\n-   * @param minCount minimum count\n-   * @param prefixSuffixPairs prefix (length N) and suffix pairs,\n-   * @return (frequent length N+1 extended prefix, count) pairs and (frequent length N+1 extended\n-   *         prefix, corresponding suffix) pairs.\n-   */\n-  private def extendPrefixes(\n-      minCount: Long,\n-      prefixSuffixPairs: RDD[(List[Set[Int]], List[Set[Int]])])\n-    : (RDD[(List[Set[Int]], Long)], RDD[(List[Set[Int]], List[Set[Int]])]) = {\n-\n-    // (length N prefix, itemset from suffix) pairs and their corresponding number of occurrences\n-    // Every (prefix :+ suffix) is guaranteed to have support exceeding `minSupport`\n-    val prefixItemPairAndCounts = prefixSuffixPairs\n-      .flatMap { case (prefix, suffix) =>\n-      suffix.flatMap(nonemptySubsets(_)).distinct.map(y => ((prefix, y), 1L)) }\n-      .reduceByKey(_ + _)\n-      .filter { case (item, count) => (count >= minCount) }\n-\n-    // Map from prefix to set of possible next items from suffix\n-    val prefixToNextItems = prefixItemPairAndCounts\n-      .keys\n-      .groupByKey()\n-      .mapValues(_.toSet)\n-      .collect()\n-      .toMap\n-\n-    // Frequent patterns with length N+1 and their corresponding counts\n-    val extendedPrefixAndCounts = prefixItemPairAndCounts\n-      .map { case ((prefix, item), count) => (item :: prefix, count) }\n-\n-    // Remaining work, all prefixes will have length N+1\n-    val extendedPrefixAndSuffix = prefixSuffixPairs\n-      .filter(x => prefixToNextItems.contains(x._1))\n-      .flatMap { case (prefix, suffix) =>\n-        val frequentNextItemSets = prefixToNextItems(prefix)\n-        val frequentNextItems = frequentNextItemSets.flatten\n-        val filteredSuffix = suffix\n-          .map(item => frequentNextItems.intersect(item))\n-          .filter(_.nonEmpty)\n-        frequentNextItemSets.flatMap { item =>\n-          LocalPrefixSpan.getSuffix(item, filteredSuffix) match {\n-            case suffix if !suffix.isEmpty => Some(item :: prefix, suffix)\n-            case _ => None\n-          }\n-        }\n-      }\n+  private[fpm] object Prefix {\n+    /** Internal counter to generate unique IDs. */\n+    private val counter: AtomicInteger = new AtomicInteger(-1)\n+\n+    /** Gets the next unique ID. */\n+    private def nextId: Int = counter.incrementAndGet()\n \n-    (extendedPrefixAndCounts, extendedPrefixAndSuffix)\n+    /** An empty [[Prefix]] instance. */\n+    val empty: Prefix = new Prefix(Array.empty, 0)\n   }\n \n   /**\n-   * Calculate the patterns in local.\n-   * @param minCount the absolute minimum count\n-   * @param data prefixes and projected sequences data data\n-   * @return patterns\n+   * An internal representation of a postfix from some projection.\n+   * We use one int array to store the items, which might also contains other items from the\n+   * original sequence.\n+   * Items are represented by positive integers, and items in each itemset must be distinct and\n+   * ordered.\n+   * we use 0 as the delimiter between itemsets.\n+   * For example, a sequence `<(12)(31)1>` is represented by `[0, 1, 2, 0, 1, 3, 0, 1, 0]`.\n+   * The postfix of this sequence w.r.t. to prefix `<1>` is `<(_2)(13)1>`.\n+   * We may reuse the original items array `[0, 1, 2, 0, 1, 3, 0, 1, 0]` to represent the postfix,\n+   * and mark the start index of the postfix, which is `2` in this example.\n+   * So the active items in this postfix are `[2, 0, 1, 3, 0, 1, 0]`.\n+   * We also remember the start indices of partial projections, the ones that split an itemset.\n+   * For example, another possible partial projection w.r.t. `<1>` is <(_3)1>`.\n+   * We remember the start indices of partial projections, which is `[2, 5]` in this example.\n+   * This data structure makes it easier to do projections.\n+   *\n+   * @param items an int array containing this postfix with 0 as the delimiter","path":"mllib/src/main/scala/org/apache/spark/mllib/fpm/PrefixSpan.scala","position":486,"original_position":486,"commit_id":"65f90e85cbde21cebe0bbf1f54d0bfb3f3e58cce","original_commit_id":"65f90e85cbde21cebe0bbf1f54d0bfb3f3e58cce","user":{"login":"feynmanliang","id":990069,"avatar_url":"https://avatars.githubusercontent.com/u/990069?v=3","gravatar_id":"","url":"https://api.github.com/users/feynmanliang","html_url":"https://github.com/feynmanliang","followers_url":"https://api.github.com/users/feynmanliang/followers","following_url":"https://api.github.com/users/feynmanliang/following{/other_user}","gists_url":"https://api.github.com/users/feynmanliang/gists{/gist_id}","starred_url":"https://api.github.com/users/feynmanliang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/feynmanliang/subscriptions","organizations_url":"https://api.github.com/users/feynmanliang/orgs","repos_url":"https://api.github.com/users/feynmanliang/repos","events_url":"https://api.github.com/users/feynmanliang/events{/privacy}","received_events_url":"https://api.github.com/users/feynmanliang/received_events","type":"User","site_admin":false},"body":"document `start` param and how it differs from `partialStarts`","created_at":"2015-08-04T20:58:49Z","updated_at":"2015-08-04T20:58:49Z","html_url":"https://github.com/apache/spark/pull/7937#discussion_r36240984","pull_request_url":"https://api.github.com/repos/apache/spark/pulls/7937","_links":{"self":{"href":"https://api.github.com/repos/apache/spark/pulls/comments/36240984"},"html":{"href":"https://github.com/apache/spark/pull/7937#discussion_r36240984"},"pull_request":{"href":"https://api.github.com/repos/apache/spark/pulls/7937"}}},"pull_request":{"url":"https://api.github.com/repos/apache/spark/pulls/7937","id":41594166,"html_url":"https://github.com/apache/spark/pull/7937","diff_url":"https://github.com/apache/spark/pull/7937.diff","patch_url":"https://github.com/apache/spark/pull/7937.patch","issue_url":"https://api.github.com/repos/apache/spark/issues/7937","number":7937,"state":"open","locked":false,"title":"[SPARK-9540] [MLLIB] optimize PrefixSpan implementation","user":{"login":"mengxr","id":829644,"avatar_url":"https://avatars.githubusercontent.com/u/829644?v=3","gravatar_id":"","url":"https://api.github.com/users/mengxr","html_url":"https://github.com/mengxr","followers_url":"https://api.github.com/users/mengxr/followers","following_url":"https://api.github.com/users/mengxr/following{/other_user}","gists_url":"https://api.github.com/users/mengxr/gists{/gist_id}","starred_url":"https://api.github.com/users/mengxr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mengxr/subscriptions","organizations_url":"https://api.github.com/users/mengxr/orgs","repos_url":"https://api.github.com/users/mengxr/repos","events_url":"https://api.github.com/users/mengxr/events{/privacy}","received_events_url":"https://api.github.com/users/mengxr/received_events","type":"User","site_admin":false},"body":"This is a major refactoring of the PrefixSpan implementation. It contains the following changes:\r\n\r\n1. Expand prefix with one item at a time. The existing implementation generates all subsets for each itemset, which might have scalability issue when the itemset is large.\r\n2. Use a new internal format. `<(12)(31)>` is represented by `[0, 1, 2, 0, 1, 3, 0]` internally. We use `0` because negative numbers are used to indicates partial prefix items, e.g., `_2` is represented by `-2`.\r\n3. Remember the start indices of all partial projections in the projected postfix to help next projection.\r\n4. Reuse the original sequence array for projected postfixes.\r\n5. Use `Prefix` IDs in aggregation rather than its content.\r\n6. Use `ArrayBuilder` for building primitive arrays.\r\n7. Expose `maxLocalProjDBSize`.\r\n8. Tests are not changed except using `0` instead of `-1` as the delimiter.\r\n\r\n`Postfix`'s API doc should be a good place to start.\r\n\r\nCloses #7594 \r\n\r\n@feynmanliang @zhangjiajin ","created_at":"2015-08-04T17:16:37Z","updated_at":"2015-08-04T20:58:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"fc4edec0533e49d8bd298f322146797840b49a9b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/apache/spark/pulls/7937/commits","review_comments_url":"https://api.github.com/repos/apache/spark/pulls/7937/comments","review_comment_url":"https://api.github.com/repos/apache/spark/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/spark/issues/7937/comments","statuses_url":"https://api.github.com/repos/apache/spark/statuses/65f90e85cbde21cebe0bbf1f54d0bfb3f3e58cce","head":{"label":"mengxr:SPARK-9540","ref":"SPARK-9540","sha":"65f90e85cbde21cebe0bbf1f54d0bfb3f3e58cce","user":{"login":"mengxr","id":829644,"avatar_url":"https://avatars.githubusercontent.com/u/829644?v=3","gravatar_id":"","url":"https://api.github.com/users/mengxr","html_url":"https://github.com/mengxr","followers_url":"https://api.github.com/users/mengxr/followers","following_url":"https://api.github.com/users/mengxr/following{/other_user}","gists_url":"https://api.github.com/users/mengxr/gists{/gist_id}","starred_url":"https://api.github.com/users/mengxr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mengxr/subscriptions","organizations_url":"https://api.github.com/users/mengxr/orgs","repos_url":"https://api.github.com/users/mengxr/repos","events_url":"https://api.github.com/users/mengxr/events{/privacy}","received_events_url":"https://api.github.com/users/mengxr/received_events","type":"User","site_admin":false},"repo":{"id":17278743,"name":"spark","full_name":"mengxr/spark","owner":{"login":"mengxr","id":829644,"avatar_url":"https://avatars.githubusercontent.com/u/829644?v=3","gravatar_id":"","url":"https://api.github.com/users/mengxr","html_url":"https://github.com/mengxr","followers_url":"https://api.github.com/users/mengxr/followers","following_url":"https://api.github.com/users/mengxr/following{/other_user}","gists_url":"https://api.github.com/users/mengxr/gists{/gist_id}","starred_url":"https://api.github.com/users/mengxr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mengxr/subscriptions","organizations_url":"https://api.github.com/users/mengxr/orgs","repos_url":"https://api.github.com/users/mengxr/repos","events_url":"https://api.github.com/users/mengxr/events{/privacy}","received_events_url":"https://api.github.com/users/mengxr/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mengxr/spark","description":"Mirror of Apache Spark","fork":true,"url":"https://api.github.com/repos/mengxr/spark","forks_url":"https://api.github.com/repos/mengxr/spark/forks","keys_url":"https://api.github.com/repos/mengxr/spark/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mengxr/spark/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mengxr/spark/teams","hooks_url":"https://api.github.com/repos/mengxr/spark/hooks","issue_events_url":"https://api.github.com/repos/mengxr/spark/issues/events{/number}","events_url":"https://api.github.com/repos/mengxr/spark/events","assignees_url":"https://api.github.com/repos/mengxr/spark/assignees{/user}","branches_url":"https://api.github.com/repos/mengxr/spark/branches{/branch}","tags_url":"https://api.github.com/repos/mengxr/spark/tags","blobs_url":"https://api.github.com/repos/mengxr/spark/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mengxr/spark/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mengxr/spark/git/refs{/sha}","trees_url":"https://api.github.com/repos/mengxr/spark/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mengxr/spark/statuses/{sha}","languages_url":"https://api.github.com/repos/mengxr/spark/languages","stargazers_url":"https://api.github.com/repos/mengxr/spark/stargazers","contributors_url":"https://api.github.com/repos/mengxr/spark/contributors","subscribers_url":"https://api.github.com/repos/mengxr/spark/subscribers","subscription_url":"https://api.github.com/repos/mengxr/spark/subscription","commits_url":"https://api.github.com/repos/mengxr/spark/commits{/sha}","git_commits_url":"https://api.github.com/repos/mengxr/spark/git/commits{/sha}","comments_url":"https://api.github.com/repos/mengxr/spark/comments{/number}","issue_comment_url":"https://api.github.com/repos/mengxr/spark/issues/comments{/number}","contents_url":"https://api.github.com/repos/mengxr/spark/contents/{+path}","compare_url":"https://api.github.com/repos/mengxr/spark/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mengxr/spark/merges","archive_url":"https://api.github.com/repos/mengxr/spark/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mengxr/spark/downloads","issues_url":"https://api.github.com/repos/mengxr/spark/issues{/number}","pulls_url":"https://api.github.com/repos/mengxr/spark/pulls{/number}","milestones_url":"https://api.github.com/repos/mengxr/spark/milestones{/number}","notifications_url":"https://api.github.com/repos/mengxr/spark/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mengxr/spark/labels{/name}","releases_url":"https://api.github.com/repos/mengxr/spark/releases{/id}","created_at":"2014-02-28T07:27:59Z","updated_at":"2015-07-09T23:08:59Z","pushed_at":"2015-08-04T17:20:07Z","git_url":"git://github.com/mengxr/spark.git","ssh_url":"git@github.com:mengxr/spark.git","clone_url":"https://github.com/mengxr/spark.git","svn_url":"https://github.com/mengxr/spark","homepage":null,"size":119808,"stargazers_count":0,"watchers_count":0,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"apache:master","ref":"master","sha":"5a23213c148bfe362514f9c71f5273ebda0a848a","user":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":17165658,"name":"spark","full_name":"apache/spark","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/spark","description":"Mirror of Apache Spark","fork":false,"url":"https://api.github.com/repos/apache/spark","forks_url":"https://api.github.com/repos/apache/spark/forks","keys_url":"https://api.github.com/repos/apache/spark/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/spark/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/spark/teams","hooks_url":"https://api.github.com/repos/apache/spark/hooks","issue_events_url":"https://api.github.com/repos/apache/spark/issues/events{/number}","events_url":"https://api.github.com/repos/apache/spark/events","assignees_url":"https://api.github.com/repos/apache/spark/assignees{/user}","branches_url":"https://api.github.com/repos/apache/spark/branches{/branch}","tags_url":"https://api.github.com/repos/apache/spark/tags","blobs_url":"https://api.github.com/repos/apache/spark/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/spark/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/spark/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/spark/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/spark/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/spark/languages","stargazers_url":"https://api.github.com/repos/apache/spark/stargazers","contributors_url":"https://api.github.com/repos/apache/spark/contributors","subscribers_url":"https://api.github.com/repos/apache/spark/subscribers","subscription_url":"https://api.github.com/repos/apache/spark/subscription","commits_url":"https://api.github.com/repos/apache/spark/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/spark/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/spark/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/spark/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/spark/contents/{+path}","compare_url":"https://api.github.com/repos/apache/spark/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/spark/merges","archive_url":"https://api.github.com/repos/apache/spark/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/spark/downloads","issues_url":"https://api.github.com/repos/apache/spark/issues{/number}","pulls_url":"https://api.github.com/repos/apache/spark/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/spark/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/spark/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/spark/labels{/name}","releases_url":"https://api.github.com/repos/apache/spark/releases{/id}","created_at":"2014-02-25T08:00:08Z","updated_at":"2015-08-04T17:17:53Z","pushed_at":"2015-08-04T20:51:55Z","git_url":"git://github.com/apache/spark.git","ssh_url":"git@github.com:apache/spark.git","clone_url":"https://github.com/apache/spark.git","svn_url":"https://github.com/apache/spark","homepage":null,"size":1656787,"stargazers_count":4961,"watchers_count":4961,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":4503,"mirror_url":"git://git.apache.org/spark.git","open_issues_count":330,"forks":4503,"open_issues":330,"watchers":4961,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/spark/pulls/7937"},"html":{"href":"https://github.com/apache/spark/pull/7937"},"issue":{"href":"https://api.github.com/repos/apache/spark/issues/7937"},"comments":{"href":"https://api.github.com/repos/apache/spark/issues/7937/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/spark/pulls/7937/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/spark/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/spark/pulls/7937/commits"},"statuses":{"href":"https://api.github.com/repos/apache/spark/statuses/65f90e85cbde21cebe0bbf1f54d0bfb3f3e58cce"}}}}