คำนวณการกระจายจากคอลเล็กชันใน Java

การเปลี่ยนคอลเลกชันของตัวเลข (หรือออบเจ็กต์ที่เป็นฟิลด์ที่คุณต้องการตรวจสอบ) เป็นการแจกแจงตัวเลขเหล่านี้เป็นเทคนิคทางสถิติทั่วไป และใช้ในบริบทต่างๆ ในการรายงานและแอปพลิเคชันที่อิงตามข้อมูล

ให้คอลเลกชัน:

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

คุณสามารถตรวจสอบการแจกแจงเป็นจำนวน (ความถี่ของแต่ละองค์ประกอบ) และเก็บผลลัพธ์ไว้ในแผนที่:

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

หรือคุณสามารถ ทำให้ปกติ ค่าตามจำนวนค่าทั้งหมด – ซึ่งแสดงเป็นเปอร์เซ็นต์:

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

หรือแม้แต่แสดงเปอร์เซ็นต์เหล่านี้ใน a 0..100 รูปแบบแทนa 0..1 จัดรูปแบบ

ในคู่มือนี้ เราจะมาดูกันว่าคุณสามารถคำนวณการกระจายจากคอลเลกชันได้อย่างไร ทั้งการใช้ประเภทดั้งเดิมและวัตถุที่เป็นฟิลด์ที่คุณอาจต้องการรายงานในแอปพลิเคชันของคุณ

ด้วยการเพิ่มการรองรับการทำงานโปรแกรมใน Java การคำนวณการแจกแจงจะง่ายกว่าที่เคย เราจะทำงานกับคอลเลกชันของตัวเลขและคอลเลกชันของ Books:

public class Book {

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

   
}

คำนวณการกระจายของคอลเลกชันใน Java

อันดับแรก มาดูวิธีคำนวณการแจกแจงสำหรับประเภทดั้งเดิมกันก่อน การทำงานกับอ็อบเจ็กต์ทำให้คุณสามารถเรียกใช้เมธอดแบบกำหนดเองจากคลาสโดเมนของคุณ เพื่อให้การคำนวณมีความยืดหยุ่นมากขึ้น

โดยค่าเริ่มต้น เราจะแสดงเปอร์เซ็นต์เป็นสองเท่าจาก 0.00 ไปยัง 100.00.

ประเภทดั้งเดิม

มาสร้างรายการจำนวนเต็มและพิมพ์การแจกแจงกัน:

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

การกระจายคำนวณด้วย:

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

วิธีนี้ยอมรับรายการและสตรีม ขณะสตรีม ค่าคือ จัดกลุ่มโดย ค่าจำนวนเต็ม – และค่าของมันคือ นับ การใช้ Collectors.counting()ก่อนรวบรวมเป็น Map โดยที่คีย์แสดงค่าอินพุตและคู่แสดงแทนเปอร์เซ็นต์ในการแจกแจง

วิธีการหลักที่นี่คือ collect() ซึ่งยอมรับ นักสะสมสองคน. key-collector รวบรวมโดยเพียงแค่จัดกลุ่มตามค่าคีย์ (องค์ประกอบอินพุต) นักสะสมมูลค่ารวบรวมผ่าน collectingAndThen() ซึ่งช่วยให้เราสามารถ นับค่า แล้วจัดรูปแบบในรูปแบบอื่น เช่น count * 100.00 / list.size() ซึ่งช่วยให้เราแสดงองค์ประกอบที่นับเป็นเปอร์เซ็นต์:

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

จัดเรียงการกระจายตามค่าหรือคีย์

เมื่อสร้างการแจกแจง คุณมักจะต้องการเรียงลำดับค่า บ่อยกว่านี้จะเป็นโดย สำคัญ. ชวา HashMaps ไม่รับประกันว่าจะรักษาลำดับการแทรกดังนั้นเราจะต้องใช้ a LinkedHashMap ซึ่งไม่ นอกจากนี้ วิธีที่ง่ายที่สุดในการสตรีมแผนที่ซ้ำและรวบรวมใหม่ในขณะนี้ เนื่องจากมีขนาดเล็กกว่ามากและสามารถจัดการได้มากขึ้น

การดำเนินการก่อนหน้านี้สามารถยุบระเบียนหลายพันรายการลงในแผนที่ขนาดเล็กได้อย่างรวดเร็ว ทั้งนี้ขึ้นอยู่กับจำนวนคีย์ที่คุณกำลังติดต่อด้วย ดังนั้นการสตรีมซ้ำจึงไม่แพง:

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

วัตถุ

สิ่งนี้สามารถทำได้สำหรับวัตถุ? ใช้ตรรกะเดียวกัน! แทนที่จะเป็นฟังก์ชันระบุ (Integer::intValue) เราจะใช้ฟิลด์ที่ต้องการแทน เช่น ปีที่จัดพิมพ์สำหรับหนังสือของเรา มาสร้างหนังสือสองสามเล่ม เก็บไว้ในรายการ แล้วคำนวณการแจกแจงปีที่พิมพ์:

ดูคู่มือเชิงปฏิบัติสำหรับการเรียนรู้ Git ที่มีแนวทางปฏิบัติที่ดีที่สุด มาตรฐานที่ยอมรับในอุตสาหกรรม และเอกสารสรุปรวม หยุดคำสั่ง Googling Git และจริงๆ แล้ว เรียน มัน!

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

ลองคำนวณการกระจายของ publishedYear สนาม:

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

ปรับตัว "%.2f" เพื่อกำหนดความแม่นยำของจุดลอยตัว ส่งผลให้:

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

50% ของหนังสือที่กำหนด (2/4) ตีพิมพ์ในปี 2011, 25% (1/4) ตีพิมพ์ในปี 2014 และ 25% (1/4) ในปี 2017 จะเป็นอย่างไรถ้าคุณต้องการจัดรูปแบบผลลัพธ์นี้ให้แตกต่างออกไปและทำให้เป็นมาตรฐาน ช่วงใน 0..1?

คำนวณการกระจายแบบปกติ (เปอร์เซ็นต์) ของคอลเล็กชันใน Java

เพื่อทำให้เปอร์เซ็นต์ปกติจาก a 0.0...100.0 ช่วงถึง a 0..1 ช่วง – เราเพียงแค่ปรับ collectingAndThen() เรียกร้องให้ ไม่ คูณการนับด้วย 100.0 ก่อนจะหารด้วยขนาดของสะสม

ก่อนหน้านี้ Long นับส่งคืนโดย Collectors.counting() ถูกแปลงเป็นทวีคูณโดยปริยาย (การคูณด้วยค่าสองเท่า) ดังนั้นคราวนี้ เราจึงต้องการได้ค่า doubleValue() ของ 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));
}

ปรับตัว "%.4f" เพื่อกำหนดความแม่นยำของจุดลอยตัว ส่งผลให้:

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

คำนวณจำนวนองค์ประกอบ (ความถี่) ของคอลเลกชัน

สุดท้าย – เราสามารถรับการนับองค์ประกอบ (ความถี่ขององค์ประกอบทั้งหมด) ในคอลเล็กชัน โดยเพียงแค่ไม่หารการนับด้วยขนาดของคอลเล็กชัน! นี่คือการนับที่ไม่ได้ทำให้เป็นมาตรฐานโดยสมบูรณ์:

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

ผลลัพธ์นี้ใน:

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

อันที่จริงมีหนังสือสองเล่มจากปี 2011 และหนึ่งเล่มจาก 2014 และ 2017 แต่ละเล่ม

สรุป

การคำนวณการกระจายข้อมูลเป็นงานทั่วไปในแอปพลิเคชันที่มีข้อมูลจำนวนมาก และไม่จำเป็นต้องใช้ไลบรารีภายนอกหรือโค้ดที่ซับซ้อน ด้วยการสนับสนุนการเขียนโปรแกรมที่ใช้งานได้ Java ทำให้การทำงานกับคอลเลกชันเป็นเรื่องง่าย!

ในฉบับร่างสั้นๆ นี้ เราได้พิจารณาถึงวิธีที่คุณสามารถคำนวณการนับความถี่ขององค์ประกอบทั้งหมดในคอลเลกชั่น ตลอดจนวิธีการคำนวณแผนที่การกระจายที่ปรับให้เป็นเปอร์เซ็นต์ระหว่าง 0 และ 1 และ 0 และ 100 ใน Java

ประทับเวลา:

เพิ่มเติมจาก สแต็ค