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.?
Para reduzir a definição do problema em um bloco menor, uma outra abordagem que você pode olhar para é analisar através da year
temperatura ly e calcular o delta para eles, ainda average
ing-lo. Isto embora tem que ser feito para todos os valores do tipo Map<Integer, Double>
dentro do interior Map
em 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 City
conjunto de chaves e da mesma forma para todos os seu Country
conjunto 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);