חשב הפצה מאוסף ב-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 פורמט במקום א 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() שמקבל שני אספנים. אוסף המפתחות אוסף על ידי קיבוץ פשוט לפי ערכי המפתח (רכיבי קלט). אוסף הערכים אוסף דרך ה 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, עם שיטות עבודה מומלצות, סטנדרטים מקובלים בתעשייה ודף רמאות כלול. תפסיק לגוגל פקודות 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

כדי לנרמל את האחוזים מא 0.0...100.0 טווח עד א 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 בג'אווה.

בול זמן:

עוד מ Stackabuse