数値のコレクション (または検査したいフィールドであるオブジェクト) をそれらの数値の分布に変換することは、一般的な統計手法であり、レポートやデータ駆動型アプリケーションのさまざまなコンテキストで採用されています。
与えられたコレクション:
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
}
または、これらのパーセンテージを 0..100
の代わりにフォーマットする 0..1
形式でダウンロードすることができます。
このガイドでは、コレクションから分布を計算する方法を見ていきます。プリミティブ型と、アプリケーションでレポートするフィールドであるオブジェクトの両方を使用します。
Java での関数型プログラミングのサポートが追加されたことで、分布の計算がこれまでになく簡単になりました。 数値のコレクションと Book
s:
public class Book {
private String id;
private String name;
private String author;
private long pageNumber;
private long publishedYear;
}
Java でコレクションの分布を計算する
まず、プリミティブ型の分布を計算する方法を見てみましょう。 オブジェクトを操作すると、ドメイン クラスからカスタム メソッドを呼び出して、計算の柔軟性を高めることができます。
デフォルトでは、パーセンテージを double から 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
ここで、キーは入力値を表し、double は分布内のパーセンテージを表します。
ここでのキーメソッドは collect()
受け入れる 二人のコレクター. キーコレクターは、キー値 (入力要素) でグループ化するだけで収集します。 value-collector は、 collectingAndThen()
メソッドにより、 値を数える 次に、次のような別の形式でフォーマットします count * 100.00 / list.size()
これにより、カウントされた要素をパーセンテージで表現できます。
{1=45.45, 2=18.18, 3=18.18, 4=9.09, 5=9.09}
分布を値またはキーでソート
ディストリビューションを作成するときは、通常、値を並べ替える必要があります。 多くの場合、これは キー。 Java HashMap
s 挿入の順序を維持することを保証するものではありません、したがって、使用する必要があります 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()
は暗黙的に double に変換されました (double 値を使用した乗算) – したがって、今回は明示的に取得する必要があります。 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 年と XNUMX 年からそれぞれ XNUMX 冊の本があります。
まとめ
データの分布を計算することは、データが豊富なアプリケーションでは一般的なタスクであり、外部ライブラリや複雑なコードを使用する必要はありません。 関数型プログラミングのサポートにより、Java はコレクションを簡単に操作できるようになりました。
この短いドラフトでは、コレクション内のすべての要素の頻度カウントを計算する方法と、要素間のパーセンテージに正規化された分布マップを計算する方法を調べました。 0
& 1
と同様 0
& 100
Javaで。