によってグループ化Javaの8ストリームの複数

user2578909:

私はこのような温度レコード何かを持っています

dt        |AverageTemperature |AverageTemperatureUncertainty|City   |Country |Latitude|Longitude
----------+-------------------+-----------------------------+-------+--------+--------+---------
1963-01-01|-5.417000000000002 |0.5                          |Karachi|Pakistan|57.05N  |10.33E  
1963-02-01|-4.7650000000000015|0.328                        |Karachi|Pakistan|57.05N  |10.33E  
1964-01-01|-5.417000000000002 |0.5                          |Karachi|Pakistan|57.05N  |10.33E  
1964-02-01|-4.7650000000000015|0.328                        |Karachi|Pakistan|57.05N  |10.33E  
1965-01-01|11.417000000000002 |0.5                          |Karachi|Pakistan|57.05N  |10.33E 
1965-02-01|12.7650000000000015|0.328                        |Karachi|Pakistan|57.05N  |10.33E

私はPOJOにこれを解析し、次のような問題文のあたり、平均デルタを計算する必要があります。

各国の年間平均気温デルタを計算するストリームAPIを使用してください。計算デルタ1900の平均温度は、特定の都市のために1900年から1901年までのデルタを得るために、1901年に平均温度から減算されることになります。これらすべてのデルタの平均は、都市のための年間平均気温デルタです。国のすべての都市の平均は、国の平均です。

私の温帯POJOを持つゲッターとセッターを次のようになります。

public class Temperature {
    private java.util.Date date;
    private double averageTemperature;
    private double averageTemperatureUncertainty;
    private String city;
    private String country;
    private String latitude;
    private String longitude;
}

この問題は、ストリームを使用して達成されるように私は温度のリストを維持しています。

私は、次のストリームを使用しようとしていますが、私は平均的な国のデルタを計算する必要があるとして、私は、まだ実際のデルタを計算することができませんデルタを計算するために、私は、国、都市と日付の上にグループ分けを行っています。

Map<String, Map<String, Map<Integer, Double>>> countriesMap = this.getTemperatures().stream()
                .sorted(Comparator.comparing(Temperature::getDate))
                .collect(Collectors.groupingBy(Temperature::getCountry,
                        Collectors.groupingBy(Temperature::getCity,
                        Collectors.groupingBy
                                (t -> {
                                            Calendar calendar = Calendar.getInstance();
                                            calendar.setTime(t.getDate());
                                            return calendar.get(Calendar.YEAR);
                                        }, 
                        Collectors.averagingDouble(Temperature::getAverageTemperature)))));

デルタを計算するために、我々はのための差異を計算する必要がありますMap<Integer, Double>

差を計算するために私は次のコードを思いついたが、上記のいずれかで、次のコードを接続することができませんでした

Stream.of(10d, 20d, 10d) //this is sample data that I that I get in `Map<Integer, Double>` of countriesMap
        .map(new Function<Double, Optional<Double>>() {
            Optional<Double> previousValue = Optional.empty();
            @Override
            public Optional<Double> apply(Double current) {
                Optional<Double> value = previousValue.map(previous -> current - previous);
                previousValue = Optional.of(current);
                return value;
            }
        })
        .filter(Optional::isPresent)
        .map(Optional::get)
        .forEach(System.out::println);

どのように私は一度に、またはどのように上のストリーム操作を実行するためのストリームを使用してデルタを計算することができますcountriesMap計算デルタに順番にし、上記の問題なステートメントをacheive。?

また:

小さなブロックに問題文を削減するためには、あなたを介して解析しているに見ることができると別のアプローチyearさらに、LY温度およびそれらのデルタを計算しaverage、それをINGの。これは、しかしタイプのすべての値のために行わなければならないMap<Integer, Double>内部内のMapあなたの質問に。それは次のようになります。

Map<Integer, Double> unitOfWork = new HashMap<>(); // innermost map you've attained ('yearToAverageTemperature' map)
unitOfWork = unitOfWork.entrySet()
        .stream()
        .sorted(Map.Entry.comparingByKey())
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
// the values sorted based on the year from a sorted map
List<Double> srtedValPerYear = new ArrayList<>(unitOfWork.values());
// average of deltas from the complete list 
double avg = IntStream.range(0, srtedVal.size() - 1)
        .mapToDouble(i -> (srtedVal.get(i + 1) - srtedVal.get(i)))
        .average().orElse(Double.NaN);

さらに注意することは、これはただ一つの平均であるCityのの記録<Year, AverageTemperature>、すべてのあなたを反復処理する必要がありますCityキーセットと同様にすべてのあなたのためにCountry徹底的にキーセットなど平均を見つけます。

さらに、メソッドにこの作業単位の移動マップの完全なマップを反復処理する、これはのように達成されることがあります。

// The average of all cities in a country is the average of a country.
AtomicReference<Double> countryValAvg = new AtomicReference<>(0.0);
countriesMap.forEach((country, cityMap) -> {
    // The average of all these deltas is the average annual temperature delta for a city.
    AtomicReference<Double> cityAvgTemp = new AtomicReference<>((double) 0);
    cityMap.forEach((city, yearMap) -> cityAvgTemp.set(cityAvgTemp.get() + averagePerCity(yearMap)));
    double avgAnnualTempDeltaPerCity = cityAvgTemp.get() / cityMap.size();

    countryValAvg.set(countryValAvg.get() + avgAnnualTempDeltaPerCity);
});
System.out.println(countryValAvg.get() / countriesMap.size());

どこaveragePerCity次ない方法があります

double averagePerCity(Map<Integer, Double> unitOfWork) {
    unitOfWork = unitOfWork.entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    List<Double> srtedVal = new ArrayList<>(unitOfWork.values());
    return IntStream.range(0, srtedVal.size() - 1)
            .mapToDouble(i -> (srtedVal.get(i + 1) - srtedVal.get(i)))
            .average().orElse(Double.NaN);
}

:上記のコードは欠落している可能性があります検証は、そのちょうど完全問題は小さな部分に分割して、解決できる方法のアイデアを提供します。

EDIT1としてより一層向上させることができ

// The average of all cities in a country is the average of a country.
AtomicReference<Double> countryValAvg = new AtomicReference<>(0.0);
countriesMap.forEach((country, cityMap) -> {
    // The average of all these deltas is the average annual temperature delta for a city.
    double avgAnnualTempDeltaPerCity = cityMap.values()
            .stream()
            .mapToDouble(Quick::averagePerCity) // Quick is my class name
            .average()
            .orElse(Double.NaN);
    countryValAvg.set(countryValAvg.get() + avgAnnualTempDeltaPerCity);
});
System.out.println(countryValAvg.get() / countriesMap.size());

EDIT2:さらなるへ

double avgAnnualTempDeltaPerCity = countriesMap.values().stream()
        .mapToDouble(cityMap -> cityMap.values()
                .stream()
                .mapToDouble(Quick::averagePerCity) // Quick is my class name
                .average()
                .orElse(Double.NaN))
        .average().orElse(Double.NaN);

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=165663&siteId=1