访问全局查找Apache Spark

时间:2015-01-30 20:55:50

标签: apache-spark bigdata

我有一个csv文件列表,每个文件都有一堆类别名称作为标题列。每行都是具有布尔值(0,1)的用户列表,无论它们是否属于该类别。每个csv文件都没有相同的标题类别集。

我想在所有具有以下输出的文件中创建复合csv:

  1. 标题是所有标题的联合
  2. 每一行都是唯一的用户,其布尔值对应于类别列
  3. 我想解决这个问题的方法是为每个单元格创建一个user_id的元组和一个唯一的category_id,其值为'1'。然后为每个用户减少所有这些列以获得最终输出。

    如何创建元组开头?我可以对所有类别进行全局查找吗?

    示例数据:

    File 1
    user_id,cat1,cat2,cat3
    21321,,,1,
    21322,1,1,1,
    21323,1,,,
    

    文件2

    user_id,cat4,cat5
    21321,1,,,
    21323,,1,,
    

    输出

    user_id,cat1,cat2,cat3,cat4,cat5
    21321,,1,1,,,
    21322,1,1,1,,,
    21323,1,1,,,,
    

2 个答案:

答案 0 :(得分:0)

问题的标题可能具有误导性,因为它传达了某种实现选择,因为不需要全局查找来解决手头的问题。

在大数据中,指导大多数解决方案的基本原则是:分而治之。在这种情况下,输入的CSV文件可以分为(用户,类别)元组。 包含任意数量类别的任意数量的CSV文件都可以转换为这种简单格式。生成的CSV结果是上一步的并集,提取了所有类别的nr,以及一些数据转换,以获得所需的格式。

在代码中,此算法如下所示:

import org.apache.spark.SparkContext._

val file1 = """user_id,cat1,cat2,cat3|21321,,,1|21322,1,1,1|21323,1,,""".split("\\|")
val file2 = """user_id,cat4,cat5|21321,1,|21323,,1""".split("\\|")
val csv1 = sparkContext.parallelize(file1)
val csv2 = sparkContext.parallelize(file2)

import org.apache.spark.rdd.RDD
def toTuples(csv:RDD[String]):RDD[(String, String)] = {
  val headerLine = csv.first
  val header = headerLine.split(",")
  val data = csv.filter(_ != headerLine).map(line => line.split(","))
  data.flatMap{elem => 
    val merged = elem.zip(header)
    val id = elem.head
    merged.tail.collect{case (v,cat) if v == "1" => (id, cat)}
  }
}

val data1 = toTuples(csv1)
val data2 = toTuples(csv2)
val union = data1.union(data2)
val categories = union.map{case (id, cat) => cat}.distinct.collect.sorted //sorted category names
val categoriesByUser = union.groupByKey.mapValues(v=>v.toSet)
val numericCategoriesByUser = categoriesByUser.mapValues{catSet => categories.map(cat=> if (catSet(cat)) "1" else "")}
  val asCsv = numericCategoriesByUser.collect.map{case (id, cats)=> id + "," + cats.mkString(",")}

结果:

21321,,,1,1,
21322,1,1,1,,
21323,1,,,,1

(生成标题很简单,留给读者练习)

答案 1 :(得分:-1)

如果您需要的只是结果值,则不需要将此作为两步过程。 可能的设计: 1 /解析你的csv。你没有提到你的数据是否在分布式FS上,所以我认为不是。 2 /将(K,V)对输入可变并行化(利用Spark)映射。 伪代码:

val directory = ..
mutable.ParHashMap map = new mutable.ParHashMap()
while (files[i] != null)
{
  val file = directory.spark.textFile("/myfile...")
  val cols = file.map(_.split(","))

  map.put(col[0], col[i++])
}

然后你可以通过地图上的迭代器访问你的(K / V)元组。