Izračunajte distribucijo iz zbirke v Javi

Pretvarjanje zbirke števil (ali objektov, katerih polja bi radi pregledali) v porazdelitev teh števil je pogosta statistična tehnika in se uporablja v različnih kontekstih v aplikacijah za poročanje in podatke.

Podana zbirka:

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

Njihovo porazdelitev lahko pregledate kot štetje (pogostost vsakega elementa) in rezultate shranite v zemljevid:

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

Ali pa lahko normalizirati vrednosti na podlagi skupnega števila vrednosti – torej izražene v odstotkih:

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

Ali celo izrazite te odstotke v a 0..100 format namesto a 0..1 format.

V tem priročniku si bomo ogledali, kako lahko izračunate porazdelitev iz zbirke – z uporabo primitivnih tipov in objektov, katerih polja bi morda želeli poročati v svoji aplikaciji.

Z dodatkom podpore za funkcionalno programiranje v Javi je izračun distribucij lažji kot kdaj koli prej. Delali bomo z zbirko številk in zbirko Books:

public class Book {

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

   
}

Izračunajte porazdelitev zbirke v Javi

Najprej si poglejmo, kako lahko izračunate porazdelitev za primitivne tipe. Delo s predmeti vam preprosto omogoča klicanje metod po meri iz vaših domenskih razredov, da zagotovite večjo prilagodljivost pri izračunih.

Privzeto bomo odstotke predstavili kot dvojnik od 0.00 do 100.00.

Primitivne vrste

Ustvarimo seznam celih števil in natisnemo njihovo porazdelitev:

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

Porazdelitev se izračuna z:

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

Ta metoda sprejme seznam in ga pretaka. Med pretakanjem so vrednosti razvrščeni po njihova cela vrednost – in njihove vrednosti so štetje uporabo Collectors.counting(), preden se zbere v a Map kjer ključi predstavljajo vhodne vrednosti, podvojitve pa njihove odstotke v porazdelitvi.

Ključne metode tukaj so collect() ki sprejema dva zbiralca. Zbiralnik ključev zbira s preprostim združevanjem po vrednostih ključev (vhodni elementi). Zbiralec vrednosti zbira prek collectingAndThen() metoda, ki nam omogoča preštejte vrednosti in jih nato formatirajte v drugi obliki, kot npr count * 100.00 / list.size() ki nam omogoča, da izrazimo preštete elemente v odstotkih:

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

Razvrsti porazdelitev po vrednosti ali ključu

Ko ustvarjate distribucije – boste običajno želeli razvrstiti vrednosti. Pogosteje bo to mimo ključ. Java HashMaps ne zagotavljajo ohranitve vrstnega reda vstavljanja, zato bomo morali uporabiti a LinkedHashMap ki ne. Poleg tega je najlažje ponovno pretočiti zemljevid in ga znova zbrati zdaj, ko je veliko manjše velikosti in veliko bolj vodljiv.

Prejšnja operacija lahko hitro strne več tisoč zapisov v majhne zemljevide, odvisno od števila ključev, s katerimi imate opravka, tako da ponovno pretakanje ni drago:

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

predmeti

Kako je to mogoče storiti za predmete? Velja ista logika! Namesto identifikacijske funkcije (Integer::intValue), namesto tega bomo uporabili želeno polje – na primer leto izdaje naših knjig. Ustvarimo nekaj knjig, jih shranimo na seznam in nato izračunamo porazdelitev let založbe:

Oglejte si naš praktični, praktični vodnik za učenje Gita z najboljšimi praksami, standardi, sprejetimi v panogi, in priloženo goljufijo. Nehajte Googlati ukaze Git in pravzaprav naučiti it!

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

Izračunajmo porazdelitev publishedYear polje:

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

Prilagodite "%.2f" za nastavitev natančnosti plavajoče vejice. Rezultat tega je:

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

50 % danih knjig (2/4) je bilo izdanih leta 2011, 25 % (1/4) je bilo izdanih leta 2014 in 25 % (1/4) leta 2017. Kaj pa, če želite ta rezultat drugače oblikovati in normalizirati obseg v 0..1?

Izračunajte normalizirano (odstotek) porazdelitev zbirke v Javi

Za normalizacijo odstotkov iz a 0.0...100.0 razpon do a 0..1 ponudbo – preprosto prilagodimo collectingAndThen() pokliči na ne pomnožite štetje s 100.0 pred delitvijo z velikostjo zbirke.

Prej je Long štetje vrnil Collectors.counting() je bilo implicitno pretvorjeno v dvojno (množenje z dvojno vrednostjo) – zato bomo tokrat želeli eksplicitno pridobiti doubleValue() od 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));
}

Prilagodite "%.4f" za nastavitev natančnosti plavajoče vejice. Rezultat tega je:

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

Izračunajte število elementov (pogostost) zbiranja

Končno – število elementov (pogostost vseh elementov) v zbirki lahko dobimo tako, da preprosto ne delimo števila z velikostjo zbirke! To je popolnoma nenormalizirano število:

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

Rezultat tega je:

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

Dejansko sta dve knjigi iz leta 2011 ter po ena iz let 2014 in 2017.

zaključek

Izračun porazdelitev podatkov je običajna naloga v aplikacijah, bogatih s podatki, in ne zahteva uporabe zunanjih knjižnic ali kompleksne kode. S podporo za funkcionalno programiranje je Java olajšala delo z zbirkami!

V tem kratkem osnutku smo si ogledali, kako lahko izračunate število frekvenc vseh elementov v zbirki, pa tudi, kako izračunate porazdelitvene karte, normalizirane na odstotke med 0 in 1 tako dobro, kot 0 in 100 na Javi.

Časovni žig:

Več od Stackabuse