Laske jakelu kokoelmasta Javassa

Lukukokoelman (tai objektien, joiden kenttiä haluat tarkastaa) muuttaminen näiden lukujen jakaumaksi on yleinen tilastotekniikka, ja sitä käytetään useissa yhteyksissä raportoinnissa ja tietopohjaisissa sovelluksissa.

Annettu kokoelma:

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

Voit tarkastaa niiden jakautumisen laskentana (kunkin elementin tiheys) ja tallentaa tulokset karttaan:

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

Tai voit normalisoitua arvot, jotka perustuvat arvojen kokonaismäärään eli ilmaistaan ​​ne prosentteina:

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

Tai jopa ilmaista nämä prosenttiosuudet muodossa a 0..100 muoto a sijasta 0..1 -muodossa.

Tässä oppaassa tarkastellaan, kuinka voit laskea jakauman kokoelmasta – sekä käyttämällä primitiivityyppejä että objekteja, joiden kentät saatat haluta raportoida sovelluksessasi.

Kun Javaan on lisätty toiminnallinen ohjelmointituki, jakelujen laskeminen on helpompaa kuin koskaan. Työskentelemme numerokokoelman ja kokoelman kanssa Books:

public class Book {

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

   
}

Laske kokoelman jakautuminen Javassa

Katsotaanpa ensin, kuinka voit laskea jakauman primitiivisille tyypeille. Objektien kanssa työskentely antaa sinun kutsua mukautettuja menetelmiä toimialueluokistasi, mikä lisää laskelmien joustavuutta.

Oletuksena esitämme prosenttiosuudet kaksinkertaisena arvosta 0.00 että 100.00.

Primitiiviset tyypit

Luodaan luettelo kokonaisluvuista ja tulostetaan niiden jakauma:

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

Jakauma lasketaan seuraavasti:

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

Tämä menetelmä hyväksyy luettelon ja suoratoistaa sen. Suoratoiston aikana arvot ovat ryhmitelty niiden kokonaislukuarvo – ja niiden arvot ovat lasketaan käyttämällä Collectors.counting(), ennen kuin se kerätään a Map jossa avaimet edustavat syötearvoja ja tuplaukset niiden prosenttiosuuksia jakaumassa.

Tärkeimmät menetelmät tässä ovat collect() joka hyväksyy kaksi keräilijää. Avaimenkerääjä kerää yksinkertaisesti ryhmittelemällä avainarvojen (syöttöelementtien) mukaan. Arvonkerääjä kerää collectingAndThen() menetelmä, jonka avulla voimme laske arvot ja muotoile ne sitten toiseen muotoon, kuten count * 100.00 / list.size() jonka avulla voimme ilmaista lasketut elementit prosentteina:

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

Lajittele jakelu arvon tai avaimen mukaan

Jakaumia luotaessa – arvot kannattaa yleensä lajitella. Useimmiten tämä menee ohi avain. Java HashMaps et takaa lisäysjärjestyksen säilymistä, joten meidän on käytettävä a LinkedHashMap joka tekee. Lisäksi on helpointa suoratoistaa kartta uudelleen ja kerätä se uudelleen nyt, kun se on paljon pienempi koko ja paljon helpompi hallita.

Edellinen toiminto voi nopeasti kutistaa useita tuhansia tietueita pieniksi kartoiksi riippuen käyttämiesi avainten määrästä, joten uudelleensuoraus ei ole kallista:

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

Esineet

Miten tämä voidaan tehdä esineille? Sama logiikka pätee! Tunnusfunktion sijaan (Integer::intValue), käytämme sen sijaan haluttua kenttää – kuten kirjojen julkaisuvuotta. Luodaan muutama kirja, tallennetaan ne luetteloon ja lasketaan sitten julkaisuvuosien jakautumat:

Tutustu käytännönläheiseen, käytännölliseen Gitin oppimisoppaaseemme, jossa on parhaat käytännöt, alan hyväksymät standardit ja mukana tuleva huijauslehti. Lopeta Git-komentojen googlailu ja oikeastaan oppia se!

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

Lasketaan jakauma publishedYear ala:

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

Säädä "%.2f" asettaaksesi liukulukutarkkuuden. Tästä seuraa:

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

50 % annetuista kirjoista (2/4) julkaistiin vuonna 2011, 25 % (1/4) julkaistiin vuonna 2014 ja 25 % (1/4) vuonna 2017. Mitä jos haluat muotoilla tämän tuloksen eri tavalla ja normalisoida alue sisään 0..1?

Laske kokoelman normalisoitu (prosenttiosuus) jakautuminen Javassa

Prosenttien normalisoimiseksi a:sta 0.0...100.0 alueelta a 0..1 valikoima – mukautamme yksinkertaisesti collectingAndThen() soita emme kerro luku luvulla 100.0 ennen jakamista kokoelman koolla.

Aikaisemmin Long palauttanut määrän Collectors.counting() muunnettiin implisiittisesti kaksoisarvoksi (kertominen kaksinkertaisella arvolla) – joten tällä kertaa haluamme saada eksplisiittisesti doubleValue() että 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));
}

Säädä "%.4f" asettaaksesi liukulukutarkkuuden. Tästä seuraa:

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

Laske kokoelman elementtimäärä (taajuus).

Lopuksi – voimme saada elementtimäärän (kaikkien elementtien tiheys) kokoelmassa yksinkertaisesti olematta jakamatta määrää kokoelman koolla! Tämä on täysin normalisoimaton luku:

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

Tämä johtaa:

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

Itse asiassa on kaksi kirjaa vuodelta 2011 ja yksi vuosilta 2014 ja 2017.

Yhteenveto

Tietojen jakautumien laskeminen on yleinen tehtävä runsaasti dataa sisältävissä sovelluksissa, eikä se vaadi ulkoisten kirjastojen tai monimutkaisen koodin käyttöä. Toiminnallisen ohjelmointituen ansiosta Java teki kokoelmien kanssa työskentelystä helppoa!

Tässä lyhyessä luonnoksessa olemme tarkastelleet, kuinka voit laskea kokoelman kaikkien elementtien esiintymistiheysluvut sekä kuinka laskea jakautumakartat, jotka on normalisoitu prosentteiksi välillä 0 ja 1 sekä 0 ja 100 Java.

Aikaleima:

Lisää aiheesta Stackabus