Scala只读取文件的某些部分

时间:2016-12-16 04:02:50

标签: arrays scala apache-spark mapreduce rdd

我正在尝试在Scala中读取一个我知道其结构的输入文件,但是我只需要每第9个条目。到目前为止,我已经设法使用以下方式阅读整个内容:

val lines = sc.textFile("hdfs://moonshot-ha-nameservice/" + args(0))
val fields = lines.map(line => line.split(","))

问题是,这给我留下了一个巨大的数组(我们说的是20GB的数据)。我不仅为了在RDD [Array [String]]和Array [String]之间进行转换而不得不编写一些非常难看的代码,但它实际上使我的代码变得毫无用处。

我尝试了不同的方法,并在使用

之间进行了混合
.map()
.flatMap() and
.reduceByKey()
然而,实际上并没有把我收集的“细胞”放到我需要它的格式中。

这是应该发生的事情:从我们的服务器读取文本文件的文件夹,代码应该以下列格式读取文本的每个“行”:

*---------*
| NASDAQ: |
*---------*
exchange, stock_symbol, date, stock_price_open, stock_price_high, stock_price_low, stock_price_close, stock_volume, stock_price_adj_close

并且只保留stock_symbol,因为那是我正在计算的标识符。到目前为止,我的尝试是将整个事物变成一个数组,只将第一个第9个索引从第一个索引收集到gather_cells var中。问题是,基于我的计算和现实生活结果,该代码需要335天才能运行(不是开玩笑)。

这是我目前的参考代码:

import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf

object SparkNum {


  def main(args: Array[String]) {

    // Do some Scala voodoo
    val sc = new SparkContext(new SparkConf().setAppName("Spark Numerical"))

    // Set input file as per HDFS structure + input args
    val lines = sc.textFile("hdfs://moonshot-ha-nameservice/" + args(0))
    val fields = lines.map(line => line.split(","))
    var collected_cells:Array[String] = new Array[String](0)

    //println("[MESSAGE] Length of CC: " + collected_cells.length)

    val divider:Long = 9
    val array_length = fields.count / divider
    val casted_length = array_length.toInt

    val indexedFields = fields.zipWithIndex
    val indexKey = indexedFields.map{case (k,v) => (v,k)}

    println("[MESSAGE] Number of lines: " + array_length)
    println("[MESSAGE] Casted lenght of: " + casted_length)



    for( i <- 1 to casted_length ) {

      println("[URGENT DEBUG] Processin line " + i + " of " + casted_length)

      var index = 9 * i - 8

      println("[URGENT DEBUG] Index defined to be " + index)

      collected_cells :+ indexKey.lookup(index)

    }



    println("[MESSAGE] collected_cells size: " + collected_cells.length)



    val single_cells = collected_cells.flatMap(collected_cells => collected_cells);
    val counted_cells = single_cells.map(cell => (cell, 1).reduceByKey{case (x, y) => x + y})
    // val result = counted_cells.reduceByKey((a,b) => (a+b))

    // val inmem = counted_cells.persist()
    //
    // // Collect driver into file to be put into user archive
    // inmem.saveAsTextFile("path to server location")

    // ==> Not necessary to save the result as processing time is recorded, not output


  }

}

当我尝试调试它时,底部部分当前被注释掉了,但它充当了伪代码,让我知道我需要做什么。我可能想要指出,我几乎不熟悉Scala,因此_符号之类的东西会让我的生活混乱。

感谢您的时间。

1 个答案:

答案 0 :(得分:2)

在这个问题中有一些概念需要澄清:

执行此代码时:

val lines = sc.textFile("hdfs://moonshot-ha-nameservice/" + args(0))
val fields = lines.map(line => line.split(",")) 

这不会导致大量数据的大小。该表达式表示基础数据的转换。它可以进一步转换,直到我们将数据减少到我们想要的信息集。

在这种情况下,我们希望记录的stock_symbol字段编码为csv:

exchange, stock_symbol, date, stock_price_open, stock_price_high, stock_price_low, stock_price_close, stock_volume, stock_price_adj_close

我还假设数据文件包含这样的横幅:

*---------*
| NASDAQ: |
*---------*

我们要做的第一件事就是删除任何看起来像这个横幅的东西。事实上,我将假设第一个字段是以字母数字字符开头的证券交易所的名称。我们将在进行任何拆分之前执行此操作,从而产生:

val lines = sc.textFile("hdfs://moonshot-ha-nameservice/" + args(0))
val validLines = lines.filter(line => !line.isEmpty && line.head.isLetter)
val fields = validLines.map(line => line.split(","))

它有助于编写变量的类型,让我们高枕无忧,我们拥有我们期望的数据类型。随着我们Scala技能的进步,这些技能可能会变得不那么重要。让我们用类型重写上面的表达式:

val lines: RDD[String] = sc.textFile("hdfs://moonshot-ha-nameservice/" + args(0))
val validLines: RDD[String] = lines.filter(line => !line.isEmpty && line.head.isLetter)
val fields: RDD[Array[String]] = validLines.map(line => line.split(","))

我们感兴趣的是stock_symbol字段,它在位置上是基于0的数组中的元素#1:

val stockSymbols:RDD[String] = fields.map(record => record(1))

如果我们想要计算符号,剩下的就是发出一个计数:

val totalSymbolCount = stockSymbols.count()

这不是很有用,因为我们每条记录都有一个条目。稍微有趣的问题是:

我们有多少个不同的股票代码?

val uniqueStockSymbols = stockSymbols.distinct.count()

我们每个符号有多少条记录?

val countBySymbol = stockSymbols.map(s => (s,1)).reduceByKey(_+_)

在Spark 2.0中,可以直接使用对Dataframes和Datasets的CSV支持 鉴于我们的数据没有带字段名称的标题行(在大型数据集中通常是这样),我们需要提供列名称:

val stockDF = sparkSession.read.csv("/tmp/quotes_clean.csv").toDF("exchange", "symbol", "date", "open", "close", "volume", "price") 

我们现在可以很容易地回答我们的问题:

val uniqueSymbols =  stockDF.select("symbol").distinct().count
val recordsPerSymbol = stockDF.groupBy($"symbol").agg(count($"symbol"))