Arvutage jaotus Java kogust

Arvude kogumi (või objektide, mille välju soovite kontrollida) muutmine nende arvude jaotuseks on levinud statistiline tehnika ja seda kasutatakse aruandluses ja andmepõhistes rakendustes erinevates kontekstides.

Antud kollektsioon:

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

Saate vaadata nende jaotust loendina (iga elemendi sagedus) ja salvestada tulemused kaardile:

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

Või saate normaliseerida väärtused, mis põhinevad väärtuste koguarvul, väljendades neid protsentides:

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

Või isegi väljendada neid protsente a 0..100 vormingus a asemel 0..1 formaadis.

Selles juhendis vaatleme, kuidas saate arvutada jaotust kogust – nii primitiivsete tüüpide kui ka objektide abil, mille välju võiksite oma rakenduses raporteerida.

Java funktsionaalse programmeerimise toe lisamisega on distributsioonide arvutamine lihtsam kui kunagi varem. Töötame numbrite ja kogumiga Books:

public class Book {

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

   
}

Arvutage kogu jaotus Javas

Vaatame esmalt, kuidas saab arvutada primitiivsete tüüpide jaotust. Objektidega töötamine võimaldab teil lihtsalt kutsuda kohandatud meetodeid oma domeeniklassidest, et pakkuda arvutustes suuremat paindlikkust.

Vaikimisi esitame protsente kahekordse väärtusena 0.00 et 100.00.

Primitiivsed tüübid

Loome täisarvude loendi ja trükime nende jaotuse:

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

Jaotus arvutatakse järgmiselt:

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()))))));
}

See meetod aktsepteerib loendit ja voogesitab selle. Voogesituse ajal on väärtused poolt rühmitatud nende täisarv väärtus – ja nende väärtused on loendatud kasutamine Collectors.counting(), enne kui kogutakse a Map kus võtmed tähistavad sisendväärtusi ja kahekohalised nende protsente jaotuses.

Peamised meetodid on siin collect() mis aktsepteerib kaks kollektsionääri. Võtmekoguja kogub lihtsalt võtmeväärtuste (sisendelementide) järgi rühmitades. Väärtusekoguja kogub kaudu collectingAndThen() meetod, mis võimaldab meil seda teha loe väärtusi ja seejärel vormindage need muus vormingus, näiteks count * 100.00 / list.size() mis võimaldab meil väljendada loendatud elemente protsentides:

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

Sorteeri jaotus väärtuse või võtme järgi

Distributsioonide loomisel soovite tavaliselt väärtusi sortida. Enamasti läheb see mööda võti. Java HashMaps ei garanteeri sisestamisjärjestuse säilimist, seega peame kasutama a LinkedHashMap mis teeb. Lisaks on kõige lihtsam kaarti uuesti voogesitada ja uuesti koguda, kuna see on palju väiksem ja paremini hallatav.

Eelmine toiming võib olenevalt kasutatavate võtmete arvust mitu tuhat kirjet kiiresti väikesteks kaartideks ahendada, nii et uuesti voogesitamine pole kallis:

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));
}

Objektid

Kuidas seda objektide puhul teha? Sama loogika kehtib! Identifitseerimisfunktsiooni asemel (Integer::intValue), kasutame selle asemel soovitud välja – näiteks oma raamatute avaldamisaastat. Loome mõned raamatud, salvestame need nimekirja ja arvutame välja ilmumisaastate jaotused:

Tutvuge meie praktilise ja praktilise Giti õppimise juhendiga, mis sisaldab parimaid tavasid, tööstusharus aktsepteeritud standardeid ja kaasas olevat petulehte. Lõpetage Giti käskude guugeldamine ja tegelikult õppima seda!

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);

Arvutame välja jaotuse publishedYear väli:

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));
}

Reguleeri "%.2f" ujukoma täpsuse määramiseks. Selle tulemuseks on:

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

50% antud raamatutest (2/4) ilmus 2011. aastal, 25% (1/4) ilmus 2014. aastal ja 25% (1/4) 2017. aastal. Mis siis, kui soovite seda tulemust teistmoodi vormistada ja normaliseerida vahemik sisse 0..1?

Arvutage Java kogu normaliseeritud (protsentuaalne) jaotus

Protsentide normaliseerimiseks alates a 0.0...100.0 ulatus kuni a 0..1 vahemik – me lihtsalt kohandame collectingAndThen() helistama mitte korrutage arv arvuga 100.0 enne jagamist kollektsiooni suurusega.

Varem Long poolt tagastatud arv Collectors.counting() teisendati kaudselt kahekordseks (korrutamine topeltväärtusega) – nii et seekord tahame selgesõnaliselt hankida doubleValue() Euroopa 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));
}

Reguleeri "%.4f" ujukoma täpsuse määramiseks. Selle tulemuseks on:

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

Arvutage kogumise elementide arv (sagedus).

Lõpetuseks – elementide arvu (kõikide elementide sageduse) kogus saame lihtsalt mitte jagada kogumi suurusega! See on täielikult normaliseerimata arv:

   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));
}

Selle tulemuseks on:

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

Tõepoolest, 2011. aastast on kaks raamatut ning kumbki üks 2014. ja 2017. aastast.

Järeldus

Andmete jaotuste arvutamine on andmerikastes rakendustes tavaline ülesanne ja see ei nõua väliste teekide või keeruka koodi kasutamist. Funktsionaalse programmeerimise toega muutis Java kogudega töötamise imelihtsaks!

Selles lühikeses mustandis oleme vaatlenud, kuidas saate arvutada kogumi kõigi elementide sagedusloendeid, samuti kuidas arvutada jaotuskaarte, mis on normaliseeritud protsentides 0 ja 1 sama hästi kui 0 ja 100 Java keeles.

Ajatempel:

Veel alates Stackabus