Java Spark查找连续的时间段

Java Spark查找连续的时间段,java,apache-spark,Java,Apache Spark,我想在一个很大的数据集中找到时间戳。这需要使用Java在Spark中完成(Scala中的代码示例也非常受欢迎) 每行看起来如下所示: ID,开始时间,结束时间 例如,数据集: [[1,10,15],[1,15,20],[2,10,13],[1,22,33],[2,13,16] 预期结果是相同id的所有连续时间帧,对于每个连续时间帧,只有开始和结束时间: [[1,10,20],[1,22,33],[2,10,16]] 我已经尝试了以下方法,但是没有成功,因为没有维护订单。因此,我希望有一种更有效的

我想在一个很大的数据集中找到时间戳。这需要使用Java在Spark中完成(Scala中的代码示例也非常受欢迎)

每行看起来如下所示:

ID,开始时间,结束时间

例如,数据集:

[[1,10,15],[1,15,20],[2,10,13],[1,22,33],[2,13,16]

预期结果是相同id的所有连续时间帧,对于每个连续时间帧,只有开始和结束时间:

[[1,10,20],[1,22,33],[2,10,16]]

我已经尝试了以下方法,但是没有成功,因为没有维护订单。因此,我希望有一种更有效的方法来做到这一点

textFile.mapToPair(x -> new Tuple2<>(x[0],new Tuple2<>(x[1], x[2])
    .mapValues(x -> new LinkedList<>(Arrays.asList(x)))
    .reduceByKey((x,y) -> {
         Tuple2<Long, Long> v1 = x.getLast();
         Tuple2<Long, Long> v2 = y.getFirst();
         Tuple2<Long, Long> v3 = v2;
         if(v2._1().equals(v1._2())) {
              v3 = new Tuple2<>(v1._1(), v2._2());
              x.removeLast();
         }
         x.addLast(v3);
         return x;
    })
    .flatMapValues(x -> x);
textFile.mapToPair(x->new Tuple2(x[0],new Tuple2(x[1],x[2]))
.mapValues(x->新建链接列表(Arrays.asList(x)))
.reduceByKey((x,y)->{
tuple2v1=x.getLast();
Tuple2 v2=y.getFirst();
tuple2v3=v2;
如果(v2._1()等于(v1._2()){
v3=新元组2(v1._1(),v2._2());
x、 removeLast();
}
x、 addLast(v3);
返回x;
})
.flatmap值(x->x);

我认为这不是火花问题,而是逻辑问题。 您应该考虑使用几个独立函数的选项:

  • 将两个间隔绑定在一起(我们将其命名为
    bindinentries()
  • 将新间隔添加到间隔的间隔累加器中(设为
    insertEntry()
建议使用模拟数据
mockData

+---+-----+---+
| id|start|end|
+---+-----+---+
|  1|   22| 33|
|  1|   15| 20|
|  1|   10| 15|
|  2|   13| 16|
|  2|   10| 13|
+---+-----+---+
借助这些函数,我对您问题的解决方案如下:

+---+-----+---+
| id|start|end|
+---+-----+---+
|  1|   10| 20|
|  1|   22| 33|
|  2|   10| 16|
+---+-----+---+
val processed=mockData
.groupByKey(u.id)
.flatMapGroups{(id:Int,it:Iterator[Entry])=>
processEntries(it)
}
processEntries()
的唯一目标是将每个id的所有条目折叠到非相交间隔的集合中。 这是它的签名:

def processEntries(it:Iterator[Entry]):List[Entry]=
it.foldLeft(Nil:List[Entry])(insertEntry)
此函数用于从分组条目中逐个获取元素,并将它们逐个推入累加器

函数
insertEntry()
处理此类插入:

def插入(acc:List[Entry],e:Entry):List[Entry]=acc匹配{
案例Nil=>e::Nil
案例a::as=>
val组合=分录(a,e)
组合赛{
案例x::y::Nil=>x::insertEntry(as,y)
案例x::Nil=>insertEntry(as,x)
case=>a::as
}
}
bindEntries()
函数应该为您处理条目的顺序:

def bindEntries(x:Entry,y:Entry):列表[条目]=
(x.start>y.end,x.endy::x::Nil
大小写(u,true)=>x::y::Nil
案例=>x.copy(开始=x.start最小y.start,结束=x.end最大y.end)::Nil
}
bindEntries()
将返回正确排序的一个或两个条目的列表。 这就是它背后的理念:

insertEntry()
将在插入期间为您对所有条目进行排序

毕竟,生成的数据集如下所示:

+---+-----+---+
| id|start|end|
+---+-----+---+
|  1|   10| 20|
|  1|   22| 33|
|  2|   10| 16|
+---+-----+---+
注意:函数
insertEntry()
不是尾部递归函数。 进一步优化有一个很好的起点

还有一个完整的解决方案:

import org.apache.log4j.{Level,Logger}
导入org.apache.spark.sql.{Dataset,Encoder,Encoder,SparkSession}
对象临时性{
Logger.getLogger(“org”).setLevel(Level.OFF)
def main(参数:数组[字符串]):单位={
导入spark.implicits_
val处理=模拟数据
.groupByKey(u.id)
.flatMapGroups{(id,it)=>
processEntries(it)
}
mockData.show()
已处理。show()
}
def processEntries(it:Iterator[Entry]):List[Entry]=
it.foldLeft(Nil:List[Entry])(insertEntry)
def插入入口(acc:List[Entry],e:Entry):List[Entry]=acc匹配{
案例Nil=>e::Nil
案例a::as=>
val组合=分录(a,e)
组合赛{
案例x::y::Nil=>x::insertEntry(as,y)
案例x::Nil=>insertEntry(as,x)
case=>a::as
}
}
def bindEntries(x:条目,y:条目):列表[条目]=
(x.start>y.end,x.endy::x::Nil
大小写(u,true)=>x::y::Nil
案例=>x.copy(开始=x.start最小y.start,结束=x.end最大y.end)::Nil
}
lazy val mockData:Dataset[Entry]=spark.createDataset(Seq(
条目(1、22、33),
条目(1、15、20),
条目(1、10、15),
条目(2、13、16),
条目(2、10、13)
))
案例类条目(id:Int,开始:Int,结束:Int)
隐式lazy val entryEncoder:Encoder[Entry]=Encoders.product[Entry]
lazy val spark:SparkSession=SparkSession.builder()
.master(“本地”)
.getOrCreate()
}

您肯定可以排序吗?其他方法可能也会有同样的问题。但这并不重要"在我看来,这不是一个好方法,ID的数量非常有限,而每个ID的时间框架非常大,因此使用这种方法会在同一个节点上运行大量长时间的计算reduce函数甚至要求函数具有可交换性和关联性,这不是这里的情况仍然建议大家谈论的排序交换性和关联性。这对字数和其他方面来说是可以的。这里你只需要在1个分区上进行所有操作,以避免混乱。我会用SQL方法解决这个问题,所以我希望看到一个替代方法。好东西。在假期。以前做过类似的事情,但更多SQL。好的,我知道它是如何完成的。提问者关心的是没有几个ID,但我看到你在这方面的想法和我一样。我认为没有必要发布替代方案,只要它在功能上有效。