Java 8 vários fluxos de agrupamento por

user2578909:

Eu tenho um registro de temperatura algo como isto

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

Eu tenho que analisar isso em um POJO e calcular o delta média de acordo com a declaração do problema seguinte:

Use as Streams API para calcular o delta temperatura média anual para cada país. Para calcular delta A temperatura média em 1900 iria ser subtraída da temperatura média em 1901 para se obter o delta 1900-1901 para uma determinada cidade. A média de todos esses deltas é o delta temperatura média anual para uma cidade. A média de todas as cidades em um país é a média de um país.

Meu Temperado POJO parece seguir com getters e setters

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

Tenho mantido uma lista de temperaturas como este problema deve ser alcançado usando correntes.

Para calcular o delta Eu estou tentando usar os seguintes fluxos mas eu ainda sou incapaz de calcular o delta real, como eu tenho que calcular o delta média do país, já cumpri agrupando mais de país, cidade e data.

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

Para calcular o delta teremos que calcular as diferenças para a Map<Integer, Double>.

Para calcular a diferença eu vim com o seguinte código, mas não pôde se conectar seguinte código com o descrito acima

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

Como posso calcular o delta usando fluxos de uma só vez ou a executar operações de fluxo ao longo countriesMap, a fim de delta calcular e acheive o statment problema mencionado.?

também:

Para reduzir a definição do problema em um bloco menor, uma outra abordagem que você pode olhar para é analisar através da yeartemperatura ly e calcular o delta para eles, ainda averageing-lo. Isto embora tem que ser feito para todos os valores do tipo Map<Integer, Double>dentro do interior Mapem sua pergunta. Seria algo como:

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

Tomar nota adicional, esta é apenas uma média de um City's registro de <Year, AverageTemperature>, você teria que percorrer todo o seu Cityconjunto de chaves e da mesma forma para todos os seu Countryconjunto de chaves para exaustivamente descobrir essas médias.

Além disso mover esta unidade de trabalho em um método, iterar o mapa completo de mapas, isso pode ser realizado como:

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

onde averagePerCityé o método que se segue

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

Nota : O código acima pode estar faltando validações, é apenas para dar uma ideia de como poderia o problema completo ser dividido em partes menores e, em seguida, resolvido.

Edit1 : Qual poderia ser melhorado ainda mais como :

// 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 : E ainda mais para

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

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=165661&siteId=1
Recomendado
Clasificación