在Scala中合并数据表

时间:2013-07-19 14:46:29

标签: scala functional-programming

我正在研究一个小型数据分析工具,并在此过程中练习/学习Scala。但是我遇到了一个小问题。

假设数据类型为:

X   Gr1     x_11    ... x_1n
X   Gr2     x_21    ... x_2n
..
X   GrK     x_k1    ... x_kn
Y   Gr1     y_11    ... y_1n
Y   Gr3     y_31    ... y_3n
..
Y   Gr(K-1)     ...

这里我有条目(X,Y ......),在最多K组中可能存在或不存在,每组有一系列值。我想要做的是非常简单(理论上),我想在不同的组中合并属于同一“实体”的行。因此,我希望在列中包含从Xx_11的所有值的一行,而不是以x_kn开头的多行。

然而,使事情变得复杂的是,并非所有实体都存在于所有组中。因此,无论哪里有“缺失数据”,我都希望使用例如零或一些表示缺失值的字符串。因此,如果我有(X,Y,Z)最多3组,我想要的类型I表如下:

X   x_11    x_12    x_21    x_22    x_31    x_32
Y   y_11    y_12    N/A     N/A     y_31    y_32
Z   N/A     N/A     z_21    z_22    N/A     N/A

我一直试图解决这个问题,有没有一种聪明的方法来使用List函数来解决这个问题?


我写了这个简单的循环:

for {
  (id, hitlist) <- hits.groupBy(_.acc)
  h <- hitlist
} println(id + "\t" + h.sampleId + "\t" + h.ratios.mkString("\t"))

能够生成看起来像上面示例的表。请注意,我的原始数据具有不同的格式和布局,但这与手头的问题几乎没有关系,因此我跳过了有关解析的所有步骤。我应该能够以更好的方式使用groupBy来实际解决这个问题,但我似乎无法实现这一目标。

然后我修改了我的循环,将hits映射到ratios并将它们相互追加:

for ((id, hitlist) <- hits.groupBy(_.acc)){
  val l = hitlist.map(_.ratios).foldRight(List[Double]()){
    (l1: List[Double], l2: List[Double]) => l1 ::: l2
  }
  println(id + "\t" + l.mkString("\t"))
  //println(id + "\t" + h.sampleId + "\t" + h.ratios.mkString("\t"))
}

这让我更近了一步,但仍然没有雪茄!而不是一个完全填充的“矩阵”,我得到一个锯齿状的表。以上面的例子为例:

X   x_11    x_12    x_21    x_22    x_31    x_32
Y   y_11    y_12    y_31    y_32
Z   z_21    z_22

关于如何填充表格以使各个组的值彼此对齐的任何想法?我应该可以使用_.sampleId,它为每个“点击”保留“组成员”,但我不确定如何。 'hit'是一个类型Hit的列表,实际上是每行的包装器,为获取单个值提供了方便的方法,因此基本上是一个具有“命名索引”的元组(例如.acc,{ {1}} ..)

(我想解决这个问题而不用硬编码组的数量,因为它可能会因情况而异)

谢谢!

2 个答案:

答案 0 :(得分:2)

这是一个人为的例子,但我认为你可以看到它的发展方向:

  case class Hit(acc:String, subAcc:String, value:Int)

  val hits = List(Hit("X", "x_11", 1), Hit("X", "x_21", 2), Hit("X", "x_31", 3))
  val kMax = 4
  val nMax = 2

  for {
    (id, hitlist) <- hits.groupBy(_.acc)
    k <- 1 to kMax
    n <- 1 to nMax
  } yield {
    val subId = "x_%s%s".format(k, n)
    val row = hitlist.find(h => h.subAcc == subId).getOrElse(Hit(id, subId, 0))

    println(row)
  }

//Prints
Hit(X,x_11,1)
Hit(X,x_12,0)
Hit(X,x_21,2)
Hit(X,x_22,0)
Hit(X,x_31,3)
Hit(X,x_32,0)
Hit(X,x_41,0)
Hit(X,x_42,0)

如果您提供有关hits列表的更多信息,那么我们可能会提供一些更准确的信息。

答案 1 :(得分:0)

我已经设法使用以下代码解决了这个问题,我将其作为答案,以防其他人遇到类似问题并需要一些帮助。使用诺亚答案的find()肯定非常有用,所以如果这段代码片段可以帮助你,请给他一个+1。

val samples = hits.groupBy(_.sampleId).keys.toList.sorted
for ((id, hitlist) <- hits.groupBy(_.acc)) {
  val ratios =
    for (sample <- samples)
      yield hitlist.find(h => h.sampleId == sample).map(_.ratios)
      .getOrElse(List(Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN))
  println(id + "\t" + ratios.flatten.mkString("\t"))
}

我认为这不是一个非常优雅或有效的解决方案,因为我有两次调用groupBy,我有兴趣看到更好的解决方案来解决这个问题。