A számok gyűjteményének (vagy olyan objektumoknak, amelyeknek a mezőit ellenőrizni szeretné) alakítása e számok eloszlására egy általános statisztikai technika, amelyet különféle kontextusokban alkalmaznak jelentéskészítésben és adatvezérelt alkalmazásokban.
Adott egy gyűjtemény:
1, 1, 2, 1, 2, 3, 1, 4, 5, 1, 3
Megtekintheti eloszlásukat számként (az egyes elemek gyakorisága), és az eredményeket térképen tárolhatja:
{
"1": 5,
"2": 2,
"3": 2,
"4": 1,
"5": 1
}
Vagy tudsz szabványosít az értékek az összes érték alapján – százalékban kifejezve:
{
"1": 0.45,
"2": 0.18,
"3": 0.18,
"4": 0.09,
"5": 0.09
}
Vagy akár kifejezni ezeket a százalékokat a 0..100
formátum helyett a 0..1
formátumban.
Ebben az útmutatóban megvizsgáljuk, hogyan számíthat ki eloszlást egy gyűjteményből – mind primitív típusok, mind objektumok használatával, amelyek mezőiről érdemes jelentést készíteni az alkalmazásban.
A Java funkcionális programozási támogatásával a disztribúciók kiszámítása egyszerűbb, mint valaha. Számgyűjteményével és egy gyűjteményével fogunk dolgozni Book
s:
public class Book {
private String id;
private String name;
private String author;
private long pageNumber;
private long publishedYear;
}
Számítsa ki a gyűjtemény eloszlását Java nyelven
Először nézzük meg, hogyan számítható ki a primitív típusok eloszlása. Az objektumokkal végzett munka egyszerűen lehetővé teszi egyéni metódusok meghívását a tartományosztályaiból, hogy nagyobb rugalmasságot biztosítson a számításokban.
Alapértelmezés szerint a százalékokat duplájaként jelenítjük meg 0.00
nak nek 100.00
.
Primitív típusok
Hozzuk létre az egész számok listáját, és nyomtassuk ki az eloszlásukat:
List integerList = List.of(1, 1, 2, 1, 2, 3, 1, 4, 5, 1, 3);
System.out.println(calculateIntegerDistribution(integerList));
Az eloszlás kiszámítása a következőképpen történik:
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()))))));
}
Ez a módszer elfogad egy listát és streamel. Streamelés közben az értékek a következők által csoportosított egész értékük – és értékeik azok számítani segítségével Collectors.counting()
, mielőtt összegyűjtik a Map
ahol a kulcsok a bemeneti értékeket, a kettősök pedig azok százalékos arányát jelentik az eloszlásban.
A legfontosabb módszerek itt a következők collect()
amely elfogadja két gyűjtő. A kulcsgyűjtő egyszerűen a kulcsértékek (bemeneti elemek) szerinti csoportosítással gyűjt. Az értékgyűjtő a collectingAndThen()
módszer, amely lehetővé teszi számunkra számolja meg az értékeket majd formázza őket más formátumba, mint pl count * 100.00 / list.size()
amely lehetővé teszi, hogy a megszámlált elemeket százalékban fejezzük ki:
{1=45.45, 2=18.18, 3=18.18, 4=9.09, 5=9.09}
Rendezze az eloszlást érték vagy kulcs szerint
A disztribúciók létrehozásakor általában érdemes rendezni az értékeket. Leggyakrabban ez lesz kulcs. Jáva HashMap
s nem garantálja a beillesztési sorrend megőrzését, ezért használnunk kell a LinkedHashMap
ami igen. Ezen túlmenően a legegyszerűbb a térkép újbóli streamelése és újragyűjtése, mivel az sokkal kisebb méretű és sokkal könnyebben kezelhető.
Az előző művelet gyorsan összecsukhat több ezer rekordot kis térképekké, a kezelt kulcsok számától függően, így az újrastreamelés nem drága:
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));
}
tárgyak
Hogyan lehet ezt megtenni tárgyaknál? Ugyanez a logika érvényesül! Azonosító függvény helyett (Integer::intValue
), helyette a kívánt mezőt használjuk – például könyveink kiadási évét. Hozzunk létre néhány könyvet, tároljuk egy listában, majd számítsuk ki a kiadási évek eloszlását:
Tekintse meg gyakorlatias, gyakorlati útmutatónkat a Git tanulásához, amely tartalmazza a bevált gyakorlatokat, az iparág által elfogadott szabványokat és a mellékelt csalólapot. Hagyd abba a guglizást a Git parancsokkal, és valójában tanulni meg!
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);
Számítsuk ki az eloszlását publishedYear
terület:
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));
}
Állítsa be a "%.2f"
a lebegőpontos pontosság beállításához. Ennek eredménye:
{2011=50.0, 2014=25.0, 2017=25.0}
Az adott könyvek 50%-a (2/4) 2011-ben, 25%-a (1/4) 2014-ben, 25%-a (1/4) 2017-ben jelent meg. Mi van, ha ezt az eredményt másképp szeretné formázni, normalizálni a tartomány 0..1
?
A gyűjtemény normalizált (százalékos) eloszlásának kiszámítása Java nyelven
A százalékok normalizálásához a 0.0...100.0
tartomány a 0..1
tartomány – egyszerűen adaptáljuk a collectingAndThen()
hívja nem szorozzuk meg a számlálást 100.0
mielőtt elosztaná a gyűjtemény méretével.
Korábban a Long
által visszaadott szám Collectors.counting()
implicit módon duplává lett konvertálva (dupla értékkel való szorzás) – ezért ezúttal explicit módon szeretnénk megkapni a doubleValue()
az 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));
}
Állítsa be a "%.4f"
a lebegőpontos pontosság beállításához. Ennek eredménye:
{2011=0.5, 2014=0.25, 2017=0.25}
Számítsa ki a gyűjtemény elemszámát (gyakoriságát).
Végül – úgy kaphatjuk meg a gyűjtemény elemszámát (az összes elem gyakoriságát), hogy egyszerűen nem osztjuk el a számot a gyűjtemény méretével! Ez egy teljesen nem normalizált szám:
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));
}
Ennek eredményeként:
{2011=2, 2014=1, 2017=1}
Valóban, két könyv van 2011-ből, és egy-egy 2014-ből és 2017-ből.
Következtetés
Az adatok eloszlásának kiszámítása gyakori feladat az adatban gazdag alkalmazásokban, és nincs szükség külső könyvtárak vagy összetett kód használatára. A funkcionális programozási támogatással a Java gyerekjáték tette a gyűjteményekkel való munkát!
Ebben a rövid vázlatban megvizsgáltuk, hogyan számíthatja ki a gyűjtemény összes elemének gyakorisági számát, valamint hogyan lehet kiszámítani a százalékos arányokra normalizált eloszlási térképeket. 0
és a 1
szintén 0
és a 100
Java-ban.