RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1450111
Accepted
Pavel
Pavel
Asked:2022-09-20 01:12:24 +0000 UTC2022-09-20 01:12:24 +0000 UTC 2022-09-20 01:12:24 +0000 UTC

按时间分组对象

  • 772

Item将工作表中的对象分组items为对象列表Group,每个组存储与一分钟相关的数据。

组内的数据value必须分组为给定间隔的算术平均值(以秒为单位)。如果源工作表中没有当前间隔 N 的数据,则重复上一个间隔的值。如果组中的第一个区间没有足够的数据,则复制前一组的最后一个区间。

间隔始终小于或等于 60 秒。并将一分钟分成相等的秒数,没有余数。

public class Item {
    private int id;
    private String time;
    private double value;
    // Getters, setters, constructors...
}
public class Group {
    private int id;
    private List<Item> items;
    // Getters, setters, constructors...
}

对于原始列表,持续时间间隔 = 30:

List<Item> items = List.of(
        new Item(1, "19/09/2020 1:03:00 AM", 1.0),
        new Item(2, "19/09/2020 1:03:03 AM", 1.3),
        new Item(3, "19/09/2020 1:03:15 AM", 1.1),
        new Item(4, "19/09/2020 1:03:47 AM", 1.2),
        new Item(5, "19/09/2020 1:03:57 AM", 1.6),
        new Item(6, "19/09/2020 1:04:04 AM", 1.8),
        new Item(7, "19/09/2020 1:04:43 AM", 1.9),
        new Item(8, "19/09/2020 1:04:44 AM", 2.1),
        new Item(9, "19/09/2020 1:05:30 AM", 1.8),
        new Item(10, "19/09/2020 1:05:46 AM", 2.3)
);

结果应该是:

List.of(
        new Group(1, List.of(
                new Item(1, "19/09/2020 1:03:00 AM", 1.13), // первые 30 сек value = (1.0 + 1.3 + 1.1) / 3
                new Item(2, "19/09/2020 1:03:30 AM", 1.4) // вторые 30 сек value = (1.2 + 1.6) / 2
        )),
        new Group(2, List.of(
                new Item(1, "19/09/2020 1:04:00 AM", 1.8), // первые 30 сек
                new Item(2, "19/09/2020 1:04:30 AM", 1.5) // вторые 30 сек
        )),
        new Group(2, List.of(
                new Item(1, "19/09/2020 1:05:00 AM", 1.5), // для первых 30 сек данных нет, в результат пойдет предыдущее значение
                new Item(2, "19/09/2020 1:05:30 AM", 2.05) // вторые 30 сек
        )));

签名List<Group> transform(List<Item> src, int intervalSize)

到目前为止,我所做的只是创建一个空的组列表

public class Transformer {

    SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss a", Locale.ENGLISH);

    @SneakyThrows
    public List<Group> transform(List<Item> source, int intervalSize) {
        List<Group> target = getEmptyGroups(source);

        return target;
    }

    @SneakyThrows
    private List<Group> getEmptyGroups(List<Item> source) {
        Item start = source.get(0);
        Calendar startTime = Calendar.getInstance();
        startTime.setTime(formatter.parse(start.getTime()));
        Item end = source.get(source.size() - 1);
        Calendar endTime = Calendar.getInstance();
        endTime.setTime(formatter.parse(end.getTime()));
        long groupTotal = ChronoUnit.MINUTES.between(startTime.toInstant(), endTime.toInstant()) + 1;
        List<Group> groups = new ArrayList<>();
        IntStream.iterate(0, i -> i < groupTotal, i -> i + 1)
                .forEachOrdered(i -> {
                    Group group = new Group();
                    group.setId(i + 1);
                    groups.add(group);
                });
        return groups;
    }
}
java
  • 0 0 个回答
  • 0 Views

0 个回答

  • Voted
  1. Best Answer
    Pak Uula
    2022-09-22T16:21:45Z2022-09-22T16:21:45Z

    首先,您的预期值有问题。对象 2.2 有一个时间戳 "19/09/2020 1:04:00 AM",它应该 "19/09/2020 1:04:30 AM",对吧?对象 3.2 也是如此。顺便说一句,最后一个组号应该是 3,而不是 2,对吧?

    对象 2.2 的平均值尚不清楚。你1.5,可是应该的(1.9+2.1)/2 -> 2.0。

    现在做决定。

    如果您以一种简单的方式直接决定,而不进行内存优化,那么我会这样做。

    将期间拆分为不重叠的窗口。例如,在您的情况下,60 秒的时间段分为两个窗口。进一步在一个无限循环中——如果对象属于当前区间,则将其添加到相应的窗口中,否则,关闭区间并开始下一个,直到对象在区间内。

    关闭窗口意味着Item使用与窗口开始相对应的时间戳和等于窗口时间平均值的值。

    窗口代码。

    package org.example.groups;
    
    
    import java.text.ParseException;
    
    public class Window {
        public static final long PERIOD = 60000;
        
        public final int windowId;
        public long start;
        public long end;
        public int count = 0;
        public double sum = 0.0;
        public double lastValue = 0.0;
        
        public Window(long start, long duration, int windowId) {
            this.windowId = windowId;
            this.start = start;
            this.end = start + duration;
        }
        
        public double add(double v) throws ParseException {
            count +=1;
            sum += v;
            return value();
        }
        
        public double value() {
            if (count == 0) {
                return lastValue;
            }
            return sum / count;
        }
        
        public Item shift()  {
            return shift(1);
        }
        
        public Item shift(int n) {
            Item result = toItem();
            
            lastValue = value();        
            sum = 0.0;
            count = 0;
            start += n*PERIOD;
            end += n*PERIOD;
            
            return result;
        }
        
        public Item toItem() {
            return new Item(
                    windowId,
                    Util.toDateString(start),
                    value()
                    );
        }
        
        public boolean isIn(long t) throws ParseException {
            return (start <= t && t < end); 
        }
    }
    

    计算平均值的代码:

    package org.example.groups;
    
    import java.text.ParseException;
    import java.util.LinkedList;
    import java.util.List;
    
    public class Average {
        public List<Item> items;
    
        private Window[] windows;
        private double lastValue = 0.0;
    
        private final long duration;
    
        public Average(long start, int numOfWindows) {
            items = new LinkedList<Item>();
            long intervalStart = Util.periodStart(start);
            long intervalEnd = intervalStart + Window.PERIOD;
            
            duration = Window.PERIOD / numOfWindows;
            windows = new Window[numOfWindows];
            
            for (int i = 0; i < numOfWindows; i++) {
                windows[i] = new Window(intervalStart + duration*i, duration, i+1);
            }
            windows[numOfWindows-1].end = intervalEnd;
        }
        
        private long intervalStart() {
            return windows[0].start;
        }
        
        private long intervalEnd() {
            return intervalStart() + Window.PERIOD;
        }
    
        private boolean isIn(long t) {
            return (intervalStart() <= t && t < intervalEnd());
        }
    
        private void finishPeriod() {
            for (Window w : windows) {
                w.lastValue = lastValue;
                Item it = w.shift();
                lastValue = it.getValue();
                items.add(it);
            }
        }
    
        public void add(Item it) throws ParseException {
            long t = Util.timeInMillis(it);
            while (!isIn(t)) {
                finishPeriod();
            }
            int wIdx = (int) ((t - intervalStart()) / duration);
            assert(wIdx < windows.length);
            windows[wIdx].add(it.getValue());
        }
        
        public List<Item> finish() {
            finishPeriod();
            return items;
        }
        
        public List<Group> groupify() {
            LinkedList<Group> result = new LinkedList<Group>();
            
            int itemsPerGroup = windows.length;
            int groupId = 1;
            for (int i = 0; i < items.size(); i+=itemsPerGroup) {
                Group g = new Group(groupId++, items.subList(i, i+itemsPerGroup));
                result.add(g);
            }
            return result;
        }
    }
    

    该类Average由间隔的开始和时段内的窗口数参数化。Window.PERIOD周期的持续时间由以毫秒为单位的常数给出。如果需要,该方法public void add(Item it)会移动间距,然后将对象添加到适当的窗口。当间隔移动时,平均值被添加到列表中items。

    为简单起见,我将对象作为一个整体写在一个列表中,并且仅在必要时将它们分组收集,但可以在finishPeriod.

    这个解决方案有什么问题。

    1. 可以通过在间隔内滑动的单个窗口来解决问题。

    2. 在数据中的时间戳之间存在较大差距的情况下,可以不保存 "empty" Item,而只保存有数据的那些间隔。

    完整代码:https ://github.com/pakuula/StackOverflow/tree/main/java/1450111

    一个周期内运行两个区间的示例:

    package org.example.groups;
    
    import java.text.ParseException;
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) throws ParseException {
            List<Item> items = List.of(
                    new Item(1, "19/09/2020 1:03:00 AM", 1.0),
                    new Item(2, "19/09/2020 1:03:03 AM", 1.3),
                    new Item(3, "19/09/2020 1:03:15 AM", 1.1),
                    new Item(4, "19/09/2020 1:03:47 AM", 1.2),
                    new Item(5, "19/09/2020 1:03:57 AM", 1.6),
                    new Item(6, "19/09/2020 1:04:04 AM", 1.8),
                    new Item(7, "19/09/2020 1:04:43 AM", 1.9),
                    new Item(8, "19/09/2020 1:04:44 AM", 2.1),
                    new Item(9, "19/09/2020 1:05:30 AM", 1.8),
                    new Item(10, "19/09/2020 1:05:46 AM", 2.3)
            );
            long start = Util.timeInMillis(items.get(0));
            start = Util.periodStart(start);
            
            Average av = new Average(start, 2);
    
            for (Item it : items) {
                av.add(it);
            }
            av.finish()
        
            for (Group g : av.groupify()) {
                System.out.println(g.toString());
            }
        }
    }
    

    结论:

    Group [id=1, items=[Item [id=1, time=Sat Sep 19 01:03:00 KST 2020, value=1.13], Item [id=2, time=Sat Sep 19 01:03:30 KST 2020, value=1.40]]]
    Group [id=2, items=[Item [id=1, time=Sat Sep 19 01:04:00 KST 2020, value=1.80], Item [id=2, time=Sat Sep 19 01:04:30 KST 2020, value=2.00]]]
    Group [id=3, items=[Item [id=1, time=Sat Sep 19 01:05:00 KST 2020, value=2.00], Item [id=2, time=Sat Sep 19 01:05:30 KST 2020, value=2.05]]]
    
    • 1

相关问题

  • wpcap 找不到指定的模块

  • 如何以编程方式从桌面应用程序打开 HTML 页面?

  • Android Studio 中的 R.java 文件在哪里?

  • HashMap 初始化

  • 如何使用 lambda 表达式通过增加与原点的距离来对点进行排序?

  • 最大化窗口时如何调整元素大小?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5