Java'da Koleksiyondan Dağıtımı Hesapla

Bir sayı koleksiyonunu (veya incelemek istediğiniz alanları olan nesneleri) bu sayıların dağılımına dönüştürmek yaygın bir istatistiksel tekniktir ve raporlama ve veri odaklı uygulamalarda çeşitli bağlamlarda kullanılır.

Bir koleksiyon verildi:

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

Dağıtımlarını sayım (her öğenin sıklığı) olarak inceleyebilir ve sonuçları bir haritada saklayabilirsiniz:

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

Veya normalleştirmek değerlerin toplam sayısına dayalı değerler – dolayısıyla yüzde olarak ifade edilir:

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

Hatta bu yüzdeleri bir şekilde ifade edin. 0..100 yerine format 0..1 biçimi.

Bu kılavuzda, hem ilkel türleri hem de uygulamanızda alanlarını raporlamak isteyebileceğiniz nesneleri kullanarak bir koleksiyondan dağılımı nasıl hesaplayabileceğinize bir göz atacağız.

Java'ya işlevsel programlama desteğinin eklenmesiyle dağılımları hesaplamak her zamankinden daha kolay. Bir sayı koleksiyonuyla ve bir sayı koleksiyonuyla çalışacağız. Books:

public class Book {

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

   
}

Java'da Koleksiyonun Dağıtımını Hesaplama

Öncelikle ilkel türler için bir dağılımı nasıl hesaplayabileceğinize bir göz atalım. Nesnelerle çalışmak, hesaplamalarda daha fazla esneklik sağlamak için etki alanı sınıflarınızdan özel yöntemleri çağırmanıza olanak tanır.

Varsayılan olarak yüzdeleri çift olarak temsil edeceğiz 0.00 için 100.00.

İlkel Tipler

Tam sayıların bir listesini oluşturalım ve dağılımlarını yazdıralım:

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

Dağıtım şu şekilde hesaplanır:

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

Bu yöntem bir listeyi kabul eder ve yayınlar. Akış sırasında değerler tarafından gruplandırılmış tamsayı değerleri – ve değerleri sayılır kullanma Collectors.counting()bir ortamda toplanmadan önce Map burada anahtarlar giriş değerlerini temsil eder ve çiftler bunların dağılımdaki yüzdelerini temsil eder.

Buradaki temel yöntemler collect() hangisi kabul eder iki koleksiyoncu. Anahtar toplayıcı, anahtar değerlerine (giriş öğeleri) göre basitçe gruplayarak toplar. Değer toplayıcı, collectingAndThen() yapmamızı sağlayan yöntem değerleri say ve ardından bunları başka bir biçimde biçimlendirin; count * 100.00 / list.size() sayılan unsurları yüzde cinsinden ifade etmemizi sağlar:

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

Dağıtımı Değere veya Anahtara Göre Sıralayın

Dağıtımları oluştururken genellikle değerleri sıralamak istersiniz. Çoğu zaman bu olur anahtar. Java HashMaps ekleme sırasını koruyacağınızı garanti etmeyin, bu yüzden bir kullanmamız gerekecek LinkedHashMap bu yapar. Ek olarak, artık çok daha küçük boyutta ve çok daha kolay yönetilebilir olduğundan, haritayı yeniden yayınlamak ve yeniden toplamak en kolay yoldur.

Önceki işlem, uğraştığınız anahtar sayısına bağlı olarak binlerce kaydı hızlı bir şekilde küçük haritalara daraltabilir, dolayısıyla yeniden akış pahalı değildir:

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

nesneler

Bu nesneler için nasıl yapılabilir? Aynı mantık geçerli! Bir tanımlama işlevi yerine (Integer::intValue), bunun yerine kitaplarımızın yayınlanma yılı gibi istenen alanı kullanacağız. Birkaç kitap oluşturalım, bunları bir listede saklayalım ve ardından yayın yıllarına göre dağılımlarını hesaplayalım:

En iyi uygulamalar, endüstri tarafından kabul edilen standartlar ve dahil edilen hile sayfası ile Git'i öğrenmek için uygulamalı, pratik kılavuzumuza göz atın. Googling Git komutlarını durdurun ve aslında öğrenmek o!

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

dağılımını hesaplayalım. publishedYear alan:

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

Ayarlamak "%.2f" kayan nokta hassasiyetini ayarlamak için. Bunun sonuçları:

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

Verilen kitapların %50'si (2/4) 2011'de, %25'i (1/4) 2014'te ve %25'i (1/4) 2017'de basıldı. Peki ya bu sonucu farklı formatlayıp normalleştirmek isterseniz? aralık 0..1?

Java'da Koleksiyonun Normalleştirilmiş (Yüzde) Dağılımını Hesaplama

Yüzdeleri normalleştirmek için 0.0...100.0 aralığı 0..1 aralığı – yalnızca collectingAndThen() seslenmek değil sayımı şununla çarpın 100.0 koleksiyonun boyutuna bölmeden önce.

Daha önce Long tarafından döndürülen sayım Collectors.counting() örtülü olarak double'a (çift değerli çarpma) dönüştürüldü - bu sefer açıkça almak isteyeceğiz doubleValue() arasında 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));
}

Ayarlamak "%.4f" kayan nokta hassasiyetini ayarlamak için. Bunun sonuçları:

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

Koleksiyonun Eleman Sayısını (Frekansını) Hesaplayın

Son olarak, sayıyı koleksiyonun boyutuna bölmeyerek koleksiyondaki öğe sayısını (tüm öğelerin sıklığını) elde edebiliriz! Bu tamamen normalleştirilmemiş bir sayıdır:

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

Bunun sonucu:

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

Aslında 2011'den iki, 2014 ve 2017'den birer kitap var.

Sonuç

Veri dağıtımlarını hesaplamak, veri açısından zengin uygulamalarda yaygın bir görevdir ve harici kitaplıkların veya karmaşık kodların kullanılmasını gerektirmez. İşlevsel programlama desteğiyle Java, koleksiyonlarla çalışmayı çocuk oyuncağı haline getirdi!

Bu kısa taslakta, bir koleksiyondaki tüm öğelerin sıklık sayımlarını nasıl hesaplayabileceğinizin yanı sıra aralarındaki yüzdelere göre normalleştirilmiş dağıtım haritalarını nasıl hesaplayabileceğinize bir göz attık. 0 ve 1 yanısıra 0 ve 100 Java.

Zaman Damgası:

Den fazla Yığın kötüye kullanımı