Berechnen Sie die Verteilung aus der Sammlung in Java

Das Umwandeln einer Sammlung von Zahlen (oder Objekten, deren Felder Sie untersuchen möchten) in eine Verteilung dieser Zahlen ist eine gängige statistische Technik und wird in verschiedenen Kontexten in Berichterstellungs- und datengesteuerten Anwendungen eingesetzt.

Gegeben eine Sammlung:

1, 1, 2, 1, 2, 3, 1, 4, 5, 1, 3

Sie können ihre Verteilung als Anzahl (Häufigkeit jedes Elements) untersuchen und die Ergebnisse in einer Karte speichern:

{
"1": 5,
"2": 2,
"3": 2,
"4": 1,
"5": 1
}

Oder du kannst normalisieren die Werte bezogen auf die Gesamtzahl der Werte – also in Prozent ausgedrückt:

{
"1": 0.45,
"2": 0.18,
"3": 0.18,
"4": 0.09,
"5": 0.09
}

Oder drücken Sie diese Prozentsätze sogar in a aus 0..100 Format statt a 0..1 Format.

In diesem Leitfaden werfen wir einen Blick darauf, wie Sie eine Verteilung aus einer Sammlung berechnen können – sowohl mit primitiven Typen als auch mit Objekten, deren Felder Sie möglicherweise in Ihrer Anwendung melden möchten.

Mit der zusätzlichen funktionalen Programmierunterstützung in Java ist die Berechnung von Verteilungen einfacher als je zuvor. Wir werden mit einer Sammlung von Zahlen und einer Sammlung von Zahlen arbeiten Books:

public class Book {

    private String id;
    private String name;
    private String author;
    private long pageNumber;
    private long publishedYear;

   
}

Berechnen Sie die Verteilung der Sammlung in Java

Schauen wir uns zunächst an, wie Sie eine Verteilung für primitive Typen berechnen können. Das Arbeiten mit Objekten ermöglicht es Ihnen einfach, benutzerdefinierte Methoden aus Ihren Domänenklassen aufzurufen, um mehr Flexibilität bei den Berechnungen bereitzustellen.

Standardmäßig stellen wir die Prozentsätze als Doppel von dar 0.00 zu 100.00.

Primitive Typen

Lassen Sie uns eine Liste von ganzen Zahlen erstellen und ihre Verteilung drucken:

List integerList = List.of(1, 1, 2, 1, 2, 3, 1, 4, 5, 1, 3);
System.out.println(calculateIntegerDistribution(integerList));

Die Verteilung errechnet sich mit:

public static Map calculateIntegerDistribution(List list) {
    return list.stream()
            .collect(Collectors.groupingBy(Integer::intValue,
                    Collectors.collectingAndThen(Collectors.counting(),
                            count -> (Double.parseDouble(String.format("%.2f", count * 100.00 / list.size()))))));
}

Diese Methode akzeptiert eine Liste und streamt sie. Während des Streamings sind die Werte gruppiert nach ihren ganzzahligen Wert – und ihre Werte sind gezählt Verwendung von Collectors.counting(), bevor sie in a gesammelt werden Map wobei die Schlüssel die Eingabewerte darstellen und die Doubles ihre Prozentsätze in der Verteilung darstellen.

Die Schlüsselmethoden hier sind collect() was akzeptiert zwei Sammler. Der Schlüsselsammler sammelt durch einfaches Gruppieren nach Schlüsselwerten (Eingabeelementen). Der Werte-Sammler sammelt über die collectingAndThen() Methode, die es uns ermöglicht zählen die Werte und formatieren Sie sie dann in einem anderen Format, z count * 100.00 / list.size() wodurch wir die gezählten Elemente in Prozent ausdrücken können:

{1=45.45, 2=18.18, 3=18.18, 4=9.09, 5=9.09}

Verteilung nach Wert oder Schlüssel sortieren

Beim Erstellen von Verteilungen möchten Sie die Werte normalerweise sortieren. Meistens wird dies vorbei sein Schlüssel. Java HashMaps garantieren nicht, dass die Reihenfolge des Einfügens erhalten bleibt, also müssen wir a verwenden LinkedHashMap was tut. Außerdem ist es am einfachsten, die Karte erneut zu streamen und neu zu sammeln, da sie jetzt viel kleiner und viel besser zu handhaben ist.

Der vorherige Vorgang kann je nach Anzahl der Schlüssel, mit denen Sie es zu tun haben, schnell mehrere tausend Datensätze in kleine Karten zusammenfassen, sodass das erneute Streamen nicht teuer ist:

public static Map calculateIntegerDistribution(List list) {
    return list.stream()
            .collect(Collectors.groupingBy(Integer::intValue,
                    Collectors.collectingAndThen(Collectors.counting(),
                            count -> (Double.parseDouble(String.format("%.2f", count.doubleValue() / list.size()))))))
            
            
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(e -> Integer.parseInt(e.getKey().toString()),
                    Map.Entry::getValue,
                    (a, b) -> {
                        throw new AssertionError();
                    },
                    LinkedHashMap::new));
}

Objekte

Wie kann dies für Objekte durchgeführt werden? Es gilt die gleiche Logik! Anstelle einer Identifizierungsfunktion (Integer::intValue), verwenden wir stattdessen das gewünschte Feld – beispielsweise das Erscheinungsjahr für unsere Bücher. Lassen Sie uns ein paar Bücher erstellen, in einer Liste speichern und dann die Verteilungen der Erscheinungsjahre berechnen:

Sehen Sie sich unseren praxisnahen, praktischen Leitfaden zum Erlernen von Git an, mit Best Practices, branchenweit akzeptierten Standards und einem mitgelieferten Spickzettel. Hören Sie auf, Git-Befehle zu googeln und tatsächlich in Verbindung, um es!

Book book1 = new Book("001", "Our Mathematical Universe", "Max Tegmark", 432, 2014);
Book book2 = new Book("002", "Life 3.0", "Max Tegmark", 280, 2017);
Book book3 = new Book("003", "Sapiens", "Yuval Noah Harari", 443, 2011);
Book book4 = new Book("004", "Steve Jobs", "Water Isaacson", 656, 2011);

List books = Arrays.asList(book1, book2, book3, book4);

Lassen Sie uns die Verteilung der berechnen publishedYear Feld:

public static Map calculateDistribution(List books) {
    return books.stream()
            .collect(Collectors.groupingBy(Book::getPublishedYear,
                    Collectors.collectingAndThen(Collectors.counting(),
                            count -> (Double.parseDouble(String.format("%.2f", count * 100.00 / books.size()))))))
            
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(e -> Integer.parseInt(e.getKey().toString()),
                    Map.Entry::getValue,
                    (a, b) -> {
                        throw new AssertionError();
                    },
                    LinkedHashMap::new));
}

Verstelle die "%.2f" um die Fließkommagenauigkeit einzustellen. Das führt zu:

{2011=50.0, 2014=25.0, 2017=25.0}

50 % der angegebenen Bücher (2/4) wurden 2011 veröffentlicht, 25 % (1/4) wurden 2014 und 25 % (1/4) 2017 veröffentlicht. Was ist, wenn Sie dieses Ergebnis anders formatieren und normalisieren möchten die Reichweite hinein 0..1?

Berechnen Sie die normalisierte (prozentuale) Verteilung der Sammlung in Java

Um die Prozentsätze von a zu normalisieren 0.0...100.0 reichen bis a 0..1 Sortiment – ​​wir passen das einfach an collectingAndThen() Aufruf nicht multiplizieren Sie die Anzahl mit 100.0 vor dem Teilen durch die Größe der Sammlung.

Zuvor war die Long Zählung zurückgegeben von Collectors.counting() wurde implizit in ein Double umgewandelt (Multiplikation mit einem Double-Wert) – also wollen wir dieses Mal explizit das erhalten doubleValue() dauert ebenfalls 3 Jahre. Das erste Jahr ist das sog. count:

    public static Map calculateDistributionNormalized(List books) {
        return books.stream()
            .collect(Collectors.groupingBy(Book::getPublishedYear,
                    Collectors.collectingAndThen(Collectors.counting(),
                            count -> (Double.parseDouble(String.format("%.4f", count.doubleValue() / books.size()))))))
            
            .entrySet()
            .stream()
            .sorted(comparing(e -> e.getKey()))
            .collect(Collectors.toMap(e -> Integer.parseInt(e.getKey().toString()),
                    Map.Entry::getValue,
                    (a, b) -> {
                        throw new AssertionError();
                    },
                    LinkedHashMap::new));
}

Verstelle die "%.4f" um die Fließkommagenauigkeit einzustellen. Das führt zu:

{2011=0.5, 2014=0.25, 2017=0.25}

Berechnen Sie die Elementanzahl (Häufigkeit) der Sammlung

Schließlich können wir die Elementanzahl (Häufigkeit aller Elemente) in der Sammlung erhalten, indem wir die Anzahl einfach nicht durch die Größe der Sammlung teilen! Dies ist eine vollständig nicht normalisierte Zählung:

   public static Map calculateDistributionCount(List books) {
        return books
            .stream()
            .collect(Collectors.groupingBy(Book::getPublishedYear,
                    Collectors.collectingAndThen(Collectors.counting(),
                            count -> (Integer.parseInt(String.format("%s", count.intValue()))))))
            
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(e -> Integer.parseInt(e.getKey().toString()),
                    Map.Entry::getValue,
                    (a, b) -> {
                        throw new AssertionError();
                    },
                    LinkedHashMap::new));
}

Das führt zu:

{2011=2, 2014=1, 2017=1}

Tatsächlich gibt es zwei Bücher aus dem Jahr 2011 und jeweils eines aus den Jahren 2014 und 2017.

Zusammenfassung

Die Berechnung von Datenverteilungen ist eine häufige Aufgabe in datenintensiven Anwendungen und erfordert keine Verwendung externer Bibliotheken oder komplexen Codes. Mit funktionaler Programmierunterstützung machte Java die Arbeit mit Sammlungen zum Kinderspiel!

In diesem kurzen Entwurf haben wir uns angesehen, wie Sie Häufigkeitszählungen aller Elemente in einer Sammlung berechnen können und wie Sie Verteilungskarten berechnen können, die auf Prozentsätze dazwischen normalisiert sind 0 und 1 und auch der 0 und 100 auf Java.

Zeitstempel:

Mehr von Stapelmissbrauch