从Scala / Spark中的RDD中提取数据

时间:2016-12-19 16:56:15

标签: scala hadoop apache-spark

所以我有一个大型数据集,它是stackoverflow用户库的一个示例。该数据集中的一行如下:

<row Id="42" Reputation="11849" CreationDate="2008-08-01T13:00:11.640" DisplayName="Coincoin" LastAccessDate="2014-01-18T20:32:32.443" WebsiteUrl="" Location="Montreal, Canada" AboutMe="A guy with the attention span of a dead goldfish who has been having a blast in the industry for more than 10 years.&#xD;&#xA;&#xD;&#xA;Mostly specialized in game and graphics programming, from custom software 3D renderers to accelerated hardware pipeline programming." Views="648" UpVotes="337" DownVotes="40" Age="35" AccountId="33" />

我想从声誉中提取数字,在这种情况下,它是&#34; 11849&#34;和年龄的数字,在这个例子中它是&#34; 35&#34;我想将它们作为花车。

该文件位于HDFS中,因此它的格式为RDD

 val linesWithAge = lines.filter(line => line.contains("Age="))    //This is filtering data which doesnt have age
    val repSplit = linesWithAge.flatMap(line => line.split("\"")) //Here I am trying to split the data where there is a "

所以当我用引号将它拆分时,声誉在索引3中,而年龄在索引23中,但是如何将它们分配给地图或变量,以便我可以将它们用作浮点数。 此外,我需要它为RDD上的每一行执行此操作。

编辑:

   val linesWithAge = lines.filter(line => line.contains("Age="))    //transformations from the original input data
   val repSplit = linesWithAge.flatMap(line => line.split("\""))
    val withIndex = repSplit.zipWithIndex
    val indexKey = withIndex.map{case (k,v) => (v,k)}
    val b = indexKey.lookup(3)
    println(b)

因此,如果为数组添加索引,现在我已成功设法将其分配给变量,但我只能将其用于RDD中的一个项目,是否有人知道如何对所有项目执行此操作?

2 个答案:

答案 0 :(得分:1)

我们要做的是将原始数据集中的每个元素(表示为RDD)转换为包含(Reputation, Age)的元组作为数值。

一种可能的方法是使用String操作转换RDD的每个元素,以便提取元素“Age”和“Reputation”的值,如下所示:

// define a function to extract the value of an element, given the name
def findElement(src: Array[String], name:String):Option[String] = {
  for {
    entry <- src.find(_.startsWith(name))
    value <- entry.split("\"").lift(1)
  } yield value
}

然后我们使用该函数从每条记录中提取有趣的值:

val reputationByAge = lines.flatMap{line => 
    val elements = line.split(" ")
    for {
        age <- findElement(elements, "Age")
        rep <- findElement(elements, "Reputation")
    } yield (rep.toInt, age.toInt)
}

请注意我们在执行此操作之前不需要过滤“年龄”。如果我们处理的记录没有“年龄”或“声望”,findElement将返回None。此后,for-comprehension的结果将为NoneflatMap操作将使记录展平

解决此问题的更好方法是意识到我们正在处理结构化XML数据。 Scala提供对XML的内置支持,因此我们可以这样做:

import scala.xml.XML
import scala.xml.XML._

// help function to map Strings to Option where empty strings become None 
def emptyStrToNone(str:String):Option[String] = if (str.isEmpty) None else Some(str)

val xmlReputationByAge = lines.flatMap{line => 
    val record = XML.loadString(line)
    for {          
      rep <- emptyStrToNone((record \ "@Reputation").text)
      age <- emptyStrToNone((record \ "@Age").text)
    } yield (rep.toInt, age.toInt)
}

此方法依赖于XML记录的结构来提取正确的属性。和以前一样,我们使用Option值和flatMap的组合来删除不包含我们需要的所有信息的记录。

答案 1 :(得分:-1)

首先,您需要一个函数来提取行(getValueForKeyAs[T])的给定键的值,然后执行:

val rdd = linesWithAge.map(line => (getValueForKeyAs[Float](line,"Age"), getValueForKeyAs[Float](line,"Reputation")))

这应该为您提供RDD[(Float,Float)]

类型的rdd

getValueForKeyAs可以像这样实现:

def getValueForKeyAs[A](line:String, key:String) : A = {
    val res = line.split(key+"=")
    if(res.size==1) throw new RuntimeException(s"no value for key $key")
    val value = res(1).split("\"")(1)
    return value.asInstanceOf[A]
}