我有一个List<对象[]> Object []中的一列是LocalDateTime。其他列是位置(String)和项目价格(Double)。
基本上,我的列表如下所示:
2017-01-01 02:05:00 NEWYORK 26.89
2017-01-01 02:10:00 NEWYORK 72.00
2017-01-01 02:15:00 NEWYORK 73.10
2017-01-01 02:20:00 NEWYORK 70.11
2017-01-01 02:25:00 NEWYORK 79.90
2017-01-01 02:30:00 NEWYORK 72.33
2017-01-01 02:35:00 NEWYORK 75.69
2017-01-01 02:40:00 NEWYORK 72.12
2017-01-01 02:45:00 NEWYORK 73.09
2017-01-01 02:50:00 NEWYORK 72.67
2017-01-01 02:55:00 NEWYORK 72.56
2017-01-01 03:00:00 NEWYORK 72.76
2017-01-01 02:05:00 BOSTON 26.89
2017-01-01 02:10:00 BOSTON 42.00
2017-01-01 02:15:00 BOSTON 23.10
2017-01-01 02:20:00 BOSTON 77.11
2017-01-01 02:25:00 BOSTON 49.92
2017-01-01 02:30:00 BOSTON 72.63
2017-01-01 02:35:00 BOSTON 73.19
2017-01-01 02:40:00 BOSTON 76.18
2017-01-01 02:45:00 BOSTON 83.59
2017-01-01 02:50:00 BOSTON 76.67
2017-01-01 02:55:00 BOSTON 52.06
2017-01-01 03:00:00 BOSTON 76.06
我需要做的是每个城市15分钟间隔的价格的时间加权平均值。与间隔关联的DateTime是最新的。因此,在上面的列表中运行我的算法会产生另一个看起来像这样的List:
01-01-2017 02:15:00 NEWYORK 57.33 (average of 2:05, 2:10 and 2:15)
01-01-2017 02:30:00 NEWYORK 74.11 (average of 2:20, 2:25 and 2:30)
01-01-2017 02:45:00 NEWYORK 73.63 (...)
01-01-2017 03:00:00 NEWYORK 72.60
01-01-2017 02:15:00 BOSTON 30.66 (average of 2:05, 2:10 and 2:15)
01-01-2017 02:30:00 BOSTON 66.55 (average of 2:20, 2:25 and 2:30)
01-01-2017 02:45:00 BOSTON 77.65 (...)
01-01-2017 03:00:00 BOSTON 68.26
我认为这样做的第一步是按15分钟的间隔和按城市划分记录。其余的只是迭代群体并得到平均值,我可以自己弄清楚。
我不知道如何对每个LocalDateTime进行分组,甚至在15分钟内进行分组。最后要提到的是可能缺少行。一些间隔可能是空的,在这种情况下我们可以完全忽略该间隔。非常感谢任何帮助。
UPDATE1 :我假设有一种更好的方法来对它们进行分组,而不是对它们进行排序,然后迭代每一个并比较时间戳。像这篇文章的第一个答案: How to Group Objects in a List into other Lists by Attribute using streams & Java 8?
UPDATE2 :此外,时间戳不一定每5分钟一次。它们可能是随机的,有些间隔可能有3或5行。
UPDATE3 :这不是重复,因为这个问题是关于分组而不是四舍五入。我知道如何向下舍入到15分钟是一种方法,但之后,我必须保持实时时间戳以执行时间加权平均值。这绝对不是这样做的方式。
答案 0 :(得分:1)
设计一个类来保存数据而不是Object
数组。您班级的每个对象都会包含时间戳,位置和商品价格。它也可以将时间戳四舍五入到整个15分钟;或者只是一种方法来实现这一目标。据我所知,你可以进行四舍五入(否则有this question: Round time by seconds中的灵感。)
使用此类,您可以使用the answer you linked to中的流进行分组和平均。如果您愿意,您甚至可以从List <Object[]>
启动流,并在进一步处理之前将每个数组映射到一个对象中。
编辑我似乎明白,您更喜欢在没有行的类的情况下执行此操作。当然可以这样做:
private static List<Object[]> averageByQuarterOfHour(final int indexOfTime,
int otherGroupingIndex, int indexToAverage, List<Object[]> myList) {
return myList.stream()
.collect(Collectors.groupingBy(arr
-> Arrays.asList(roundUpToWholeQuarterOfHour((LocalDateTime) arr[indexOfTime]),
arr[otherGroupingIndex])))
.entrySet()
.stream()
.map(e -> new Object[] { e.getKey().get(0),
e.getKey().get(1),
e.getValue().stream()
.map(arr -> (Number) arr[indexToAverage])
.mapToDouble(Number::doubleValue)
.average()
.getAsDouble() })
.collect(Collectors.toList());
}
static LocalDateTime roundUpToWholeQuarterOfHour(LocalDateTime timeToRound) {
LocalDateTime truncated = timeToRound.truncatedTo(ChronoUnit.MINUTES);
int minute = truncated.getMinute();
if (truncated.isEqual(timeToRound) && minute % 15 == 0) { // already on a whole quarter
return timeToRound;
}
int minutesToAdd = 15 - (minute % 15);
return truncated.plusMinutes(minutesToAdd);
}
将您的列表提交到averageByQuarterOfHour()
会给出:
[2017-01-01T03:00, NEWYORK, 72.66333333333334]
[2017-01-01T02:15, NEWYORK, 57.330000000000005]
[2017-01-01T02:30, NEWYORK, 74.11333333333333]
[2017-01-01T02:45, NEWYORK, 73.63333333333334]
[2017-01-01T02:45, BOSTON, 77.65333333333334]
[2017-01-01T03:00, BOSTON, 68.26333333333334]
[2017-01-01T02:15, BOSTON, 30.663333333333338]
[2017-01-01T02:30, BOSTON, 66.55333333333333]
我会把排序留给你。
你可能想要三思而后行。行的类可能具有更好的建模优势,这会影响整个应用程序,而不仅仅是平均计算,但另一方面,它可能需要您分别为每个实体建模,以及实体的数据成立。在本地,一个类,甚至只是一个包含数组的辅助类和一个获取四分之一小时的方法,可能仍然使上面的代码更具可读性。
答案 1 :(得分:1)
这就是我最终做到的方式:
HashMap<LocalDateTime, HashMap<Object, ArrayList<Object[]>>> map = new HashMap<LocalDateTime, HashMap<Object, ArrayList<Object[]>>>();
for(int i = 0; i < data.size(); i++) {
Object[] row = data.get(i);
int minutes = ((LocalDateTime) row[timeFieldIndex]).getMinute();
int minutesToAdd = ( newIntervalMinutes - minutes % newIntervalMinutes) % newIntervalMinutes;
LocalDateTime roundedTime = ((LocalDateTime)row[timeFieldIndex]).plusMinutes(minutesToAdd).withSecond(0);
HashMap<Object, ArrayList<Object[]>> sortFieldMap = map.get(roundedTime);
if(sortFieldMap == null) {
sortFieldMap = new HashMap<Object, ArrayList<Object[]>>();
map.put(roundedTime, sortFieldMap);
}
ArrayList<Object[]> rowList = sortFieldMap.get(row[sortFieldIndex]);
if(rowList == null) {
rowList = new ArrayList<Object[]>();
sortFieldMap.put(row[sortFieldIndex], rowList);
}
rowList.add(row);
}
所以步骤是:
现在,变量映射包含我的所有行,按时间间隔和位置分组。
答案 2 :(得分:0)
虽然有这个问题的解决方案,我试着自己写。 也许是另一种解决方案?
public static void main(String[] args) {
List<Demo> list = new ArrayList<>();
LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES);
Demo demo = new Demo(Timestamp.valueOf(now),"Beijing",66.40);
list.add(demo);
demo = new Demo(Timestamp.valueOf(now.plusMinutes(10)),"Beijing",60.40);
list.add(demo);
demo = new Demo(Timestamp.valueOf(now.plusMinutes(16)),"Beijing",80.40);
list.add(demo);
Map map = list.stream().collect(Collectors.groupingBy(e -> roundUp(e.timestamp.toLocalDateTime())));
System.out.println(map);
}
public static String roundUp(LocalDateTime time){
LocalDateTime temp;
int minutesSinceLastWhole15 = time.getMinute() % 15;
if (minutesSinceLastWhole15 >= 8) { // round up
temp = time.plusMinutes(15 - minutesSinceLastWhole15);
} else { // round down
temp = time.minusMinutes(minutesSinceLastWhole15);
}
return temp.toString();
}
static class Demo{
Timestamp timestamp;
String city;
Double price;
public Demo(Timestamp timestamp, String city, Double price){
this.timestamp = timestamp;
this.city = city;
this.price = price;
}
@Override
public String toString() {
return "{ timestamp:"+timestamp+" city:"+city+" price:"+price+" }";
}
}
答案 3 :(得分:-1)
为了简化演示,我将你的Object []转换为CityValue
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class HelloWorld{
public static void main(String []args) throws Exception {
makeList(900).values().stream().forEach(cityValues -> {
cityValues.forEach(cityValue -> {
System.out.println(cityValue.toString());
});
System.out.println(" ------- ");
});
}
private static Map<Double, List<CityValue>> makeList(int second) throws ParseException {
List<CityValue> CityValues = new ArrayList<>();
CityValues.add(new CityValue("2017-01-01 02:05:00", "NEWYORK", 26.89));
CityValues.add(new CityValue("2017-01-01 02:10:00", "NEWYORK", 72.00));
CityValues.add(new CityValue("2017-01-01 02:15:00", "NEWYORK", 73.10));
CityValues.add(new CityValue("2017-01-01 02:20:00", "NEWYORK", 70.11));
CityValues.add(new CityValue("2017-01-01 02:25:00", "NEWYORK", 79.90));
CityValues.add(new CityValue("2017-01-01 02:30:00", "NEWYORK", 72.33));
CityValues.add(new CityValue("2017-01-01 02:35:00", "NEWYORK", 75.69));
CityValues.add(new CityValue("2017-01-01 02:40:00", "NEWYORK", 72.12));
CityValues.add(new CityValue("2017-01-01 02:45:00", "NEWYORK", 73.09));
CityValues.add(new CityValue("2017-01-01 02:50:00", "NEWYORK", 72.67));
CityValues.add(new CityValue("2017-01-01 02:55:00", "NEWYORK", 72.56));
CityValues.add(new CityValue("2017-01-01 03:00:00", "NEWYORK", 72.76));
CityValues.add(new CityValue("2017-01-01 02:05:00", "BOSTON", 26.89));
CityValues.add(new CityValue("2017-01-01 02:10:00", "BOSTON", 42.00));
CityValues.add(new CityValue("2017-01-01 02:15:00", "BOSTON", 23.10));
CityValues.add(new CityValue("2017-01-01 02:20:00", "BOSTON", 77.11));
CityValues.add(new CityValue("2017-01-01 02:25:00", "BOSTON", 49.92));
CityValues.add(new CityValue("2017-01-01 02:30:00", "BOSTON", 72.63));
CityValues.add(new CityValue("2017-01-01 02:35:00", "BOSTON", 73.19));
CityValues.add(new CityValue("2017-01-01 02:40:00", "BOSTON", 76.18));
CityValues.add(new CityValue("2017-01-01 02:45:00", "BOSTON", 83.59));
CityValues.add(new CityValue("2017-01-01 02:50:00", "BOSTON", 76.67));
CityValues.add(new CityValue("2017-01-01 02:55:00", "BOSTON", 52.06));
CityValues.add(new CityValue("2017-01-01 03:00:00", "BOSTON", 76.06));
return CityValues.stream().collect(Collectors.groupingBy(cityValue -> Math.ceil(cityValue.getDate().getTime() / (second * 1000))));
}
}
class CityValue {
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD hh:mm:ss");
Date date;
String cityName;
float price;
public CityValue(String date, String cityName, double price) throws ParseException {
this.cityName = cityName;
this.price = (float) price;
this.date = sdf.parse(date);
}
public Date getDate() {
return this.date;
}
@Override
public String toString() {
return sdf.format(this.date) + " " + this.cityName + " " + this.price;
}
}