Chart ~~glätten~~ stauchen, um Datenmenge zu reduzieren, und um Übersichtlichkeit beizubehalten



  • Yaay, ich habe es hinbekommen. 🙂 Zumindest etwas ...

    Erstmal vielen Dank an euch.

    @john-0 sagte in Chart glätten, um Datenmenge zu reduzieren, und Übersichtlichkeit beizubehalten:

    den Mittelwert (da gibt es zig Möglichkeiten) berechnet

    Das habe ich versucht. Habe die x-Achse in Blöcke von 3 Stunden unterteilt, anstatt wie vorher ca. 1 Stunde, und dann den Mittelwert für jeden Block berechnet.

    Ergebnis: https://i.postimg.cc/ncGc8w22/grafik.png

    import jetbrains.exodus.entitystore.Entity;
    import jetbrains.exodus.entitystore.EntityIterable;
    import jetbrains.exodus.entitystore.PersistentEntityStore;
    import jetbrains.exodus.entitystore.PersistentEntityStores;
    
    import java.util.*;
    
    public class StatisticCollectionShrink {
        public StatisticCollectionShrink() {
            ArrayList<Entity> oldEntities = new ArrayList<>();
            try (PersistentEntityStore store = PersistentEntityStores.newInstance("rowsData")) {
                store.executeInTransaction(txn -> {
                    EntityIterable measurements = txn.sort("Measurement", "time", true);
                    for (final Entity measurement : measurements) {
                        oldEntities.add(measurement);
                    }
                    System.out.println("size() = " + measurements.size());
    
                    Entity firstEntity = oldEntities.stream().findFirst().orElseThrow();
                    Long t = (Long) firstEntity.getProperty("time");
                    assert t != null;
                    long t1 = t;
                    long t2 = t1 + 1000 * 60 * 60 * 3;
                    double[] ys = new double[(int) firstEntity.getLinks("asset").size() + 1];
                    int n = 0;
                    for (int i = 0; i < oldEntities.size(); i++) {
                        Entity entity = oldEntities.get(i);
                        Long time = (Long) entity.getProperty("time");
                        assert time != null;
                        if (time <= t2) {
                            Double s = (Double) entity.getProperty("sum");
                            assert s != null;
                            ys[0] += s;
                            EntityIterable assets = entity.getLinks("asset");
                            int j = 1;
                            for (final Entity asset : assets) {
                                String sd = (String) asset.getProperty("sum");
                                assert sd != null;
                                ys[j++] += Double.parseDouble(sd);
                            }
    
                            n++;
                        } else {
                            System.out.println("(i-n) = " + (i - n));
                            if (n < 3) {
                                break;
                            }
                            Entity newEntity = txn.newEntity("Measurement");
                            newEntity.setProperty("time", t1);
                            newEntity.setProperty("sum", ys[0] / n);
                            int j = 1;
                            for (final Entity oldAsset : oldEntities.get(i - n).getLinks("asset")) {
                                Entity newAsset = txn.newEntity("Asset");
                                newAsset.setProperty("name", Objects.requireNonNull(oldAsset.getProperty("name")));
                                newAsset.setProperty("amount", Objects.requireNonNull(oldAsset.getProperty("amount")));
                                newAsset.setProperty("sum", String.valueOf(ys[j++] / n));
                                newAsset.setLink("measurement", newEntity);
                                newEntity.addLink("asset", newAsset);
                            }
    
                            t1 = t2;
                            t2 = t1 + 1000 * 60 * 60 * 3;
                            ys = new double[(int) firstEntity.getLinks("asset").size() + 1];
                            n = 0;
                            i--;
                        }
                    }
    
                    for (final Entity oldEntity : oldEntities) {
                        oldEntity.getLinks("asset").forEach(Entity::delete);
                        oldEntity.delete();
                    }
                    System.out.println("size() = " + txn.sort("Measurement", "time", true).size());
                });
            }
        }
    }
    
    

    13-17: Bisherige Datenpunkte sammeln,
    19-67: Datenpunkte zusammenfassen, Mittelwerte berechnen und einfügen,
    69-73: alte Datenpunkte löschen

    Vorher hatte ich 86, jetzt nur noch 21 Datenpunkte, also ca. 2/3 weniger.

    Aber mir gefallen ein paar Sachen noch nicht:

    1. 45-47: Wenn es in 3 Stunden weniger als 3 Datensätze gibt/gab, dann bricht die Iteration einfach ab
    2. 65: i-- ist, glaube ich, nicht immer sinnvoll
    3. 37-39 und 56: Aus irgendeinen Grund hatte ich "sum" als String gespeichert, anstatt double


  • Hab es noch einmal überarbeitet. Jetzt sieht es schon "geglätteter" aus und mehr so in die Richtung, wie ich dachte, dass es aussehen soll:

    https://i.postimg.cc/VkMBnhyj/grafik.png

    Reduzierung von 89 auf 26 Datenpunkte.

    import jetbrains.exodus.entitystore.Entity;
    import jetbrains.exodus.entitystore.EntityIterable;
    import jetbrains.exodus.entitystore.PersistentEntityStore;
    import jetbrains.exodus.entitystore.PersistentEntityStores;
    import org.jetbrains.annotations.NotNull;
    
    import java.util.*;
    
    public class StatisticCollectionShrink {
        private static class ShrinkBlock {
            private final long t1;
            private final long t2;
            private final ArrayList<Entity> entities = new ArrayList<>();
    
            public ShrinkBlock(final long t1) {
                this.t1 = t1;
                this.t2 = t1 + 1000 * 60 * 60 * 3;
            }
    
            private boolean contains(final Entity e) {
                return contains((long) Objects.requireNonNull(e.getProperty("time")));
            }
    
            private boolean contains(final long t) {
                return t >= t1 && t <= t2;
            }
    
            private double getAverage1() {
                int n1 = entities.size();
                double sum1 = 0;
                for (final Entity entity : entities) {
                    sum1 += (double) Objects.requireNonNull(entity.getProperty("sum"));
                }
                return sum1 / n1;
            }
    
            private double[] getAverage2() {
                int n1 = entities.size();
                int n2 = (int) entities.get(0).getLinks("asset").size();
                double[] sum2 = new double[n2];
                for (final Entity entity : entities) {
                    int i = 0;
                    for (final Entity asset : entity.getLinks("asset")) {
                        sum2[i++] += Double.parseDouble((String) Objects.requireNonNull(asset.getProperty("sum")));
                    }
                }
                for (int i = 0; i < n2; i++) {
                    sum2[i] /= n1;
                }
                return sum2;
            }
    
            private long getMidTime() {
                return t1 + (t2 - t1) / 2;
            }
    
            private @NotNull EntityIterable getFirstAssets() {
                return entities.get(0).getLinks("asset");
            }
        }
    
        public StatisticCollectionShrink() {
            try (PersistentEntityStore store = PersistentEntityStores.newInstance("rowsData")) {
                store.executeInTransaction(txn -> {
                    ArrayList<Entity> oldEntities = new ArrayList<>();
                    EntityIterable measurements = txn.sort("Measurement", "time", true);
                    for (final Entity measurement : measurements) {
                        oldEntities.add(measurement);
                    }
                    System.out.println("size() = " + measurements.size());
    
                    ArrayList<ShrinkBlock> blocks = new ArrayList<>();
                    blocks.add(new ShrinkBlock((Long) Objects.requireNonNull(oldEntities.get(0).getProperty("time"))));
                    long endTime = (Long) Objects.requireNonNull(oldEntities.get(oldEntities.size() - 1).getProperty("time"));
                    while (blocks.get(blocks.size() - 1).t2 < endTime) {
                        blocks.add(new ShrinkBlock(blocks.get(blocks.size() - 1).t2));
                    }
                    for (final Entity entity : oldEntities) {
                        for (final ShrinkBlock block : blocks) {
                            if (block.contains(entity)) {
                                block.entities.add(entity);
                                break;
                            }
                        }
                    }
                    for (final ShrinkBlock block : blocks) {
                        if (block.entities.isEmpty()) {
                            continue;
                        }
                        double sum1 = block.getAverage1();
                        double[] sum2 = block.getAverage2();
                        long midTime = block.getMidTime();
                        Entity newEntity = txn.newEntity("Measurement");
                        newEntity.setProperty("time", midTime);
                        newEntity.setProperty("sum", sum1);
                        int i = 0;
                        for (final Entity oldAsset : block.getFirstAssets()) {
                            Entity newAsset = txn.newEntity("Asset");
                            newAsset.setProperty("name", Objects.requireNonNull(oldAsset.getProperty("name")));
                            newAsset.setProperty("amount", Objects.requireNonNull(oldAsset.getProperty("amount")));
                            newAsset.setProperty("sum", String.valueOf(sum2[i++]));
                            newAsset.setLink("measurement", newEntity);
                            newEntity.addLink("asset", newAsset);
                        }
                    }
    
                    for (final Entity oldEntity : oldEntities) {
                        oldEntity.getLinks("asset").forEach(Entity::delete);
                        oldEntity.delete();
                    }
                    System.out.println("size() = " + txn.sort("Measurement", "time", true).size());
                });
            }
        }
    }
    
    


  • @Fragender
    Mal eine Frage. Deine Datenreihen scheinen ja fast symmetrisch zu sein. Und ferner erscheinen mir die Werte ein wenig zu diskret. Wenn ich z.B. die obere violette Kurve anschaue, so scheinen sich nachfolgende Werte entweder nur minimal zu ändern oder immer in 170 +- Epsilon Schritte. Und ferner scheint diese symmetrisch zu der gelben Datenreihe zu sein.

    Wäre es da nicht mal besser einfach eine Aufzeichnung mit größerer Periode zu machen?



  • Wir haben ein ähnliches Problem und gehen diesen Weg (Sonderfälle mal außer Acht gelassen):

    Wir extrahieren aus den Ausgangsdaten eine Anzahl von Blöcken und aus jedem Block wird das Minimum und Maximum benutzt. Damit verlieren wir die Extrema nicht. Funktioniert natürlich nur, wenn die Ausgangsdatenmenge mind. doppelt so groß ist wie die Zieldatenmenge.



  • @Quiche-Lorraine sagte in Chart glätten, um Datenmenge zu reduzieren, und Übersichtlichkeit beizubehalten:

    Mal eine Frage. Deine Datenreihen scheinen ja fast symmetrisch zu sein. Und ferner erscheinen mir die Werte ein wenig zu diskret. Wenn ich z.B. die obere violette Kurve anschaue, so scheinen sich nachfolgende Werte entweder nur minimal zu ändern oder immer in 170 +- Epsilon Schritte. Und ferner scheint diese symmetrisch zu der gelben Datenreihe zu sein.

    Das Verhalten ist in etwa fast symmetrisch, aber es ist nicht exakt symmetrisch! Was ist zu sehen?: Das sind USD-Beträge von Assets (auf der y-Achse). Dabei wird an der Börse immer ein Asset gegen ein anderes getauscht. Geringe Fees fallen dabei an.

    Folgendes ist dabei ideal: Häufige, kleine Anpassungen der Summen und Summen nahe der Mitte. Sprich, wenn es "out-of-bounds" läuft (eine Asset-Summe ist nahe 0 und die andere nahe Max), wäre das suboptimal. Am besten wäre, wenn sich die Asset-Pairs häufig kreuzen würden. So viel zu der konkreten Problemdomäne. 😉

    @DocShoe sagte in Chart glätten, um Datenmenge zu reduzieren, und Übersichtlichkeit beizubehalten:

    eine Anzahl von Blöcken und aus jedem Block wird das Minimum und Maximum benutzt. Damit verlieren wir die Extrema nicht.

    Das klingt wirklich sinnvoller, als immer den Mittelwert aus 3 oder mehr Summen zu bilden... Aber rechne ich dann (Min + Max) / 2.0?



  • @Fragender sagte in Chart glätten, um Datenmenge zu reduzieren, und Übersichtlichkeit beizubehalten:

    Das klingt wirklich sinnvoller, als immer den Mittelwert aus 3 oder mehr Summen zu bilden... Aber rechne ich dann (Min + Max) / 2.0?

    Ne, aus jedem Block fallen 2 Datenpunkte raus, das Minimum und das Maximum. Und die werden dann im Diagramm angezeigt. Angenommen, du hast 2000 Datenpunkte und möchtest du 500 anzeigen. Dann bildest du 250 Blöcke á 8 Datenpunkte und bestimmst aus jedem Block das Minimum und Maximum. Daraus bildest du dann den Graphen mit 500 Datenpunkten.



  • @DocShoe sagte in Chart glätten, um Datenmenge zu reduzieren, und Übersichtlichkeit beizubehalten:

    @Fragender sagte in Chart glätten, um Datenmenge zu reduzieren, und Übersichtlichkeit beizubehalten:

    Das klingt wirklich sinnvoller, als immer den Mittelwert aus 3 oder mehr Summen zu bilden... Aber rechne ich dann (Min + Max) / 2.0?

    Ne, aus jedem Block fallen 2 Datenpunkte raus, das Minimum und das Maximum. Und die werden dann im Diagramm angezeigt. Angenommen, du hast 2000 Datenpunkte und möchtest du 500 anzeigen. Dann bildest du 250 Blöcke á 8 Datenpunkte und bestimmst aus jedem Block das Minimum und Maximum. Daraus bildest du dann den Graphen mit 500 Datenpunkten.

    Coole Methode. Kannte ich noch gar nicht. Danke dafür 👍



  • Aha, und die anderen fliegen raus, das klingt gut. 😊 Werde mich gleich mal an die Umsetzung wagen.



  • @DocShoe Ich komme damit nicht weiter. Ich/wir haben etwas übersehen. Ich habe ja nicht nur eine Chartlinie, sondern eine Linienschar von 12 Linien, wenn man so will. Ich kann zwar jeweils die Mini- und Maxima bestimmen, aber wie wähle ich dann zwei Datenpunkte aus, (~) zu denen alle Chartlinien dann quasi auch ein Mini- bzw. Maxima bilden? Hoffentlich kann man mich einigermaßen verstehen ... 😃 (ich bin kein Mathe-Fuchs)

    Versuch bisher:

            private Entity[] getSignificantAssetEntities() {
                if (entities.isEmpty())
                    throw new NullPointerException();
                if (entities.size() == 1)
                    return new Entity[]{entities.get(0)};
                int n2 = (int) entities.get(0).getLinks("asset").size();
                Entity[] maxYs = new Entity[n2];
                Entity[] minYs = new Entity[n2];
                for (final Entity entity : entities) {
                    int i = 0;
                    for (final Entity asset : entity.getLinks("asset")) {
                        double sum = Double.parseDouble((String) Objects.requireNonNull(asset.getProperty("sum")));
                        double maxSum = maxYs[i] == null ? Double.MIN_VALUE : Double.parseDouble((String) Objects.requireNonNull(maxYs[i].getProperty("sum")));
                        double minSum = minYs[i] == null ? Double.MAX_VALUE : Double.parseDouble((String) Objects.requireNonNull(minYs[i].getProperty("sum")));
                        if (sum > maxSum) {
                            maxYs[i] = asset;
                        }
                        if (sum < minSum) {
                            minYs[i] = asset;
                        }
                        i++;
                    }
                }
                
                
                
                // return ???
            }
    


  • Glaube, hab 'ne Idee ...

    Ansatz 1: Den RMS bzw. RMSD berechnen: https://www.statisticshowto.com/quadratic-mean/ und https://en.wikipedia.org/wiki/Root-mean-square_deviation_of_atomic_positions

    oder
    Ansatz 2: Jeweils den einfachen Mittelwert sowie Minima und Maxima ermitteln, dann schauen, ob der Mittelwert näher am Minima oder Maxima liegt, und dementsprechend ersteres oder zweiteres verwenden; wodurch die Extrema auch beibehalten werden sollten. (Kenne den Namen dafür net)

    Das Ganze sollte auch stabil sein ... das heißt, die Mehrfachhintereinanderausführung des Verfahrens sollte nach dem zweiten Mal das Ergebnis nicht mehr ändern.

    Was haltet ihr davon?



  • So, habe es, Dank @DocShoe , jetzt endlich hinbekommen.

    Reduzierung von 116 auf 52 Datenpunkte und das Verfahren ist stabil, das Ergebnis verändert sich nicht durch mehrmalige Aufrufe.

    Vorher (oben) und nachher (unten): https://i.postimg.cc/K8F7HNgj/grafik.png

    Danke, dass ihr mir geholfen habt. Anbei noch der Code.

    import jetbrains.exodus.entitystore.Entity;
    import jetbrains.exodus.entitystore.EntityIterable;
    import jetbrains.exodus.entitystore.PersistentEntityStore;
    import jetbrains.exodus.entitystore.PersistentEntityStores;
    
    import java.util.ArrayList;
    import java.util.Objects;
    
    public class StatisticCollectionShrink {
        private static class ShrinkBlock {
            private static final long t0 = 1000 * 60 * 60 * 4;
            private final long t1;
            private final long t2;
            private final long t3;
            private final ArrayList<Entity> entities = new ArrayList<>();
    
            public ShrinkBlock(final long t1) {
                this.t1 = t1;
                this.t2 = t1 + t0;
                this.t3 = t1 + t0 / 2;
            }
    
            public ShrinkBlock getNext() {
                return new ShrinkBlock(t2);
            }
    
            private boolean contains(final Entity e) {
                return contains((long) Objects.requireNonNull(e.getProperty("time")));
            }
    
            private boolean contains(final long t) {
                return t >= t1 && t < t2;
            }
    
            private Entity[] getSignificantAssetEntities() {
                if (entities.size() < 2)
                    return null;
                int n = (int) entities.get(0).getLinks("asset").size();
                Entity[] maxYs = new Entity[n];
                Entity[] minYs = new Entity[n];
                for (final Entity entity : entities) {
                    int i = 0;
                    for (final Entity asset : entity.getLinks("asset")) {
                        if (maxYs[i] == null) {
                            maxYs[i] = asset;
                        }
                        if (minYs[i] == null) {
                            minYs[i] = asset;
                        }
                        double sum = Double.parseDouble((String) Objects.requireNonNull(asset.getProperty("sum")));
                        double maxSum = Double.parseDouble((String) Objects.requireNonNull(maxYs[i].getProperty("sum")));
                        double minSum = Double.parseDouble((String) Objects.requireNonNull(minYs[i].getProperty("sum")));
                        if (sum > maxSum) {
                            maxYs[i] = asset;
                        }
                        if (sum < minSum) {
                            minYs[i] = asset;
                        }
                        i++;
                    }
                }
                Entity maxY = maxYs[0];
                Entity minY = minYs[0];
                for (int i = 0; i < n; i++) {
                    double sum1 = Double.parseDouble((String) Objects.requireNonNull(maxY.getProperty("sum")));
                    double sum2 = Double.parseDouble((String) Objects.requireNonNull(maxYs[i].getProperty("sum")));
                    double sum3 = Double.parseDouble((String) Objects.requireNonNull(minY.getProperty("sum")));
                    double sum4 = Double.parseDouble((String) Objects.requireNonNull(minYs[i].getProperty("sum")));
                    if (sum1 < sum2) {
                        maxY = maxYs[i];
                    }
                    if (sum3 > sum4) {
                        minY = minYs[i];
                    }
                }
                return new Entity[]{maxY, minY};
            }
        }
    
        public StatisticCollectionShrink() {
            try (PersistentEntityStore store = PersistentEntityStores.newInstance("rowsData")) {
                store.executeInTransaction(txn -> {
                    // collect old entities
                    ArrayList<Entity> oldEntities = new ArrayList<>();
                    EntityIterable measurements = txn.sort("Measurement", "time", true);
                    for (final Entity measurement : measurements) {
                        oldEntities.add(measurement);
                    }
                    System.out.println("size() = " + measurements.size());
    
                    // build blocks
                    ArrayList<ShrinkBlock> blocks = new ArrayList<>();
                    blocks.add(new ShrinkBlock((long) Objects.requireNonNull(oldEntities.get(0).getProperty("time"))));
                    long endTime = (long) Objects.requireNonNull(oldEntities.get(oldEntities.size() - 1).getProperty("time"));
                    while (blocks.get(blocks.size() - 1).t2 <= endTime) {
                        blocks.add(blocks.get(blocks.size() - 1).getNext());
                    }
                    for (final Entity entity : oldEntities) {
                        for (final ShrinkBlock block : blocks) {
                            if (block.contains(entity)) {
                                block.entities.add(entity);
                                break;
                            }
                        }
                    }
    
                    // insert new entities
                    for (final ShrinkBlock block : blocks) {
                        if (block.entities.size() < 2) {
                            continue;
                        }
                        Entity[] oldAssets = Objects.requireNonNull(block.getSignificantAssetEntities());
                        long t1 = block.t1;
                        long t3 = block.t3;
                        Entity oldMaxEntity = Objects.requireNonNull(oldAssets[0].getLink("measurement"));
                        Entity oldMinEntity = Objects.requireNonNull(oldAssets[1].getLink("measurement"));
                        long d1 = (long) Objects.requireNonNull(oldMaxEntity.getProperty("time")) - t1;
                        long d2 = (long) Objects.requireNonNull(oldMinEntity.getProperty("time")) - t1;
                        if (d1 <= d2) {
                            {
                                Entity newEntity = txn.newEntity("Measurement");
                                newEntity.setProperty("time", t1);
                                newEntity.setProperty("sum", Objects.requireNonNull(oldMaxEntity.getProperty("sum")));
                                for (final Entity oldAsset : oldMaxEntity.getLinks("asset")) {
                                    Entity newAsset = txn.newEntity("Asset");
                                    newAsset.setProperty("name", Objects.requireNonNull(oldAsset.getProperty("name")));
                                    newAsset.setProperty("amount", Objects.requireNonNull(oldAsset.getProperty("amount")));
                                    newAsset.setProperty("sum", Objects.requireNonNull(oldAsset.getProperty("sum")));
                                    newAsset.setLink("measurement", newEntity);
                                    newEntity.addLink("asset", newAsset);
                                }
                            }
                            {
                                Entity newEntity = txn.newEntity("Measurement");
                                newEntity.setProperty("time", t3);
                                newEntity.setProperty("sum", Objects.requireNonNull(oldMinEntity.getProperty("sum")));
                                for (final Entity oldAsset : oldMinEntity.getLinks("asset")) {
                                    Entity newAsset = txn.newEntity("Asset");
                                    newAsset.setProperty("name", Objects.requireNonNull(oldAsset.getProperty("name")));
                                    newAsset.setProperty("amount", Objects.requireNonNull(oldAsset.getProperty("amount")));
                                    newAsset.setProperty("sum", Objects.requireNonNull(oldAsset.getProperty("sum")));
                                    newAsset.setLink("measurement", newEntity);
                                    newEntity.addLink("asset", newAsset);
                                }
                            }
                        } else {
                            {
                                Entity newEntity = txn.newEntity("Measurement");
                                newEntity.setProperty("time", t1);
                                newEntity.setProperty("sum", Objects.requireNonNull(oldMinEntity.getProperty("sum")));
                                for (final Entity oldAsset : oldMinEntity.getLinks("asset")) {
                                    Entity newAsset = txn.newEntity("Asset");
                                    newAsset.setProperty("name", Objects.requireNonNull(oldAsset.getProperty("name")));
                                    newAsset.setProperty("amount", Objects.requireNonNull(oldAsset.getProperty("amount")));
                                    newAsset.setProperty("sum", Objects.requireNonNull(oldAsset.getProperty("sum")));
                                    newAsset.setLink("measurement", newEntity);
                                    newEntity.addLink("asset", newAsset);
                                }
                            }
                            {
                                Entity newEntity = txn.newEntity("Measurement");
                                newEntity.setProperty("time", t3);
                                newEntity.setProperty("sum", Objects.requireNonNull(oldMaxEntity.getProperty("sum")));
                                for (final Entity oldAsset : oldMaxEntity.getLinks("asset")) {
                                    Entity newAsset = txn.newEntity("Asset");
                                    newAsset.setProperty("name", Objects.requireNonNull(oldAsset.getProperty("name")));
                                    newAsset.setProperty("amount", Objects.requireNonNull(oldAsset.getProperty("amount")));
                                    newAsset.setProperty("sum", Objects.requireNonNull(oldAsset.getProperty("sum")));
                                    newAsset.setLink("measurement", newEntity);
                                    newEntity.addLink("asset", newAsset);
                                }
                            }
                        }
                    }
    
                    // delete old entities
                    for (final Entity oldEntity : oldEntities) {
                        oldEntity.getLinks("asset").forEach(Entity::delete);
                        oldEntity.delete();
                    }
                    System.out.println("size() = " + txn.sort("Measurement", "time", true).size());
                });
            }
        }
    }
    
    

Anmelden zum Antworten