我正在尝试在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,因此_符号之类的东西会让我的生活混乱。
感谢您的时间。
答案 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"))