我得到测量数据列表。该列表中的一个条目包含一个timstamp和数据本身。每15分钟就有一个条目或多或少-但是可能还会丢失数据点或抖动很大。 我需要建立一个标准化的数据列表,每15分钟就有一个准确的条目。作为数据,我可以进行之前的测量。
输入:
A B C D E F
|----|---------|-----|--|-----------------------|--> t
输出:
|----|----|----|----|----|----|----|----|----|----|--> t
A B B C C E E E E E F
如何用Java 8中的流以优雅而有效的方式实现这一点?
它不能是data.stream().filter([...]).findFirst()
,因为可能有很多数据点-始终从头开始搜索就太昂贵了。
我对输入数据已经对齐15分钟进行了相同的测试,所以我能够做到
public NormalizedData normalizeData(List<MeasurementData> data, Instant t) {
return data.stream()
.filter(d -> Objects.equals(d.getTimestamp().getEpochSecond(), t.getEpochSecond()))
.map(d -> new NormalizedData(t, d))
.findFirst()
.orElse(...);
}
对于所有Instant t
来说太慢了。
有什么主意吗?我们应该能够以某种方式将搜索位置存储在流中,并在下一轮继续。或完全不同的方法。
如果存在第三方标准库兼容标准流(例如StreamEx
)的解决方案,那么这也是一个选择。
答案 0 :(得分:3)
下面是一些示例代码,以显示如何实现填充丢失的数据点。
下面的许多代码不是必需的,因为它们只是设置数据以显示代码实际的工作方式。
代码要做的是使用Stream API的收集功能,并将最后收集的data.frame(
x = rnorm(1000),
spoke = factor(sample(1:6, 1000, replace=T))
) %>%
ggplot(aes(x = spoke, fill=spoke, y = x)) +
geom_violin() +
coord_polar() +
theme(
plot.background = element_rect(fill = "darkblue"),
panel.background = element_rect(fill = "lightblue",
colour = "lightblue"))
与当前的DataPoint
进行比较,如果两者之间的时间戳差异大于15分钟,则插入一个新条目。
从测试数据中可以看到,C和D以及E和F之间相差30分钟。这意味着将复制C和E的数据。
代码
DataPoint
输出
private static final long FIFTEEN_MINS_IN_MILLI_SECONDS = 900_000L;
public static void main(String[] args) {
//This is just to get some realistic times
long now = System.currentTimeMillis();
List<DataPoint> data = getDataPoints(now);
ArrayList<DataPoint> newDataPoints = data.stream().collect(Collector.of(
ArrayList<DataPoint>::new,
(ArrayList<DataPoint> dataPoints, DataPoint nextDataPoint) -> {
if (!dataPoints.isEmpty()) {
addPointIfRequired(dataPoints, nextDataPoint);
}
dataPoints.add(nextDataPoint);
},
(dataPoints, dataPoints2) -> {
if (dataPoints.isEmpty()) return dataPoints2;
if (!dataPoints2.isEmpty()) {
addPointIfRequired(dataPoints, dataPoints2.get(0));
dataPoints.addAll(dataPoints2);
}
return dataPoints;
}
));
newDataPoints.forEach(System.out::println);
}
private static void addPointIfRequired(ArrayList<DataPoint> dataPoints, DataPoint nextDataPoint) {
DataPoint previousDataPoint = dataPoints.get(dataPoints.size() - 1);
long timestampDiff = nextDataPoint.timestamp - previousDataPoint.timestamp;
if (timestampDiff > FIFTEEN_MINS_IN_MILLI_SECONDS) {
long fifteenMinIncrement = previousDataPoint.timestamp + FIFTEEN_MINS_IN_MILLI_SECONDS;
DataPoint newEntry = new DataPoint(previousDataPoint.data, fifteenMinIncrement);
dataPoints.add(newEntry);
}
}
private static List<DataPoint> getDataPoints(long now) {
return Arrays.asList(
//initial time
new DataPoint("A", now),
//15 minute increment
new DataPoint("B", now + FIFTEEN_MINS_IN_MILLI_SECONDS),
//15 minute increment
new DataPoint("C", now + (FIFTEEN_MINS_IN_MILLI_SECONDS * 2)),
//30 minute increment
new DataPoint("D", now + (FIFTEEN_MINS_IN_MILLI_SECONDS * 4)),
//15 minute increment
new DataPoint("E", now + (FIFTEEN_MINS_IN_MILLI_SECONDS * 5)),
//30 minute increment
new DataPoint("F", now + (FIFTEEN_MINS_IN_MILLI_SECONDS * 7))
);
}
private static class DataPoint {
private final String data;
private final long timestamp;
private DataPoint(String data, long timestamp) {
this.data = data;
this.timestamp = timestamp;
}
@Override
public String toString() {
return data + " " + Instant.ofEpochMilli(timestamp);
}
}
答案 1 :(得分:0)
如果我正确理解OP,以下是StreamEx进行的尝试:
// assume the data is sorted by time
final List<Pair<Integer, String>> data = N.asList(Pair.of(1, "A"), Pair.of(16, "B"), Pair.of(46, "C"),
Pair.of(60, "D"), Pair.of(76, "E"), Pair.of(151, "F"));
final int startTime = data.get(0).left();
final int interval = 15;
final Map<Integer, Pair<Integer, String>> map = StreamEx.of(data).filter(p -> (p.left() - startTime) % interval == 0).toMap(p -> p.left());
IntStreamEx.rangeClosed(startTime, data.get(data.size() - 1).left(), interval)
.forEach(t -> map.computeIfAbsent(t, k -> Pair.of(t, map.get(t - interval).right())));
final List<Pair<Integer, String>> result = StreamEx.of(map).sortedBy(e -> e.getKey()).map(e -> e.getValue()).toList();
System.out.println(result.stream().map(p -> p.right).collect(Collectors.joining("--")));