wip refactor

This commit is contained in:
giuseppenuc committed 2024-11-08 18:48:13 +01:00
1 parent 549ced1aea
commit d092c0967c
74 files changed
+2008 -1095015

No files matched your search

-1
View File
@@ -1 +0,0 @@
data/simulated_data.parquet
+1
View File
@@ -2,6 +2,7 @@
<module type="PYTHON_MODULE" version="4"> <module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/models" /> <excludeFolder url="file://$MODULE_DIR$/models" />
</content> </content>
<orderEntry type="jdk" jdkName="ml_env" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="ml_env" jdkType="Python SDK" />
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-22
View File
@@ -1,22 +0,0 @@
Unnamed: 0,Varietà di Olive,Produzione (tonnellate/ettaro),Tecnica di Coltivazione,Produzione Olio (tonnellate/ettaro),Produzione Olio (litri/ettaro),Min % Resa,Max % Resa,Min Produzione Olio (litri/ettaro),Max Produzione Olio (litri/ettaro),Media Produzione Olio (litri/ettaro),Litri per Tonnellata,Min Litri per Tonnellata,Max Litri per Tonnellata,Media Litri per Tonnellata
0,Nocellara dell'Etna,3.5,Tradizionale,0.7000000000000001,687.7729257641921,0.18,0.22,687.7729257641921,840.6113537117903,764.1921397379913,196.5065502183406,196.5065502183406,240.17467248908295,218.34061135371178
1,Nocellara dell'Etna,5.0,Intensiva,1.0,1091.703056768559,0.18,0.22,982.532751091703,1200.8733624454148,1091.7030567685588,218.3406113537118,196.50655021834058,240.17467248908298,218.34061135371178
2,Nocellara dell'Etna,6.5,Superintensiva,1.3,1561.135371179039,0.18,0.22,1277.292576419214,1561.135371179039,1419.2139737991265,240.17467248908295,196.5065502183406,240.17467248908295,218.34061135371178
6,Leccino,4.0,Tradizionale,0.8,698.6899563318777,0.16,0.2,698.6899563318777,873.3624454148471,786.0262008733624,174.67248908296943,174.67248908296943,218.34061135371178,196.5065502183406
7,Leccino,6.0,Intensiva,1.2000000000000002,1179.0393013100436,0.16,0.2,1048.0349344978165,1310.043668122271,1179.0393013100438,196.5065502183406,174.6724890829694,218.3406113537118,196.5065502183406
8,Leccino,7.5,Superintensiva,1.5,1637.5545851528384,0.16,0.2,1310.043668122271,1637.5545851528384,1473.7991266375543,218.34061135371178,174.67248908296946,218.34061135371178,196.50655021834064
9,Frantoio,5.0,Tradizionale,1.0,1091.703056768559,0.2,0.25,1091.703056768559,1364.6288209606987,1228.165938864629,218.3406113537118,218.3406113537118,272.92576419213975,245.6331877729258
10,Frantoio,7.5,Intensiva,1.5,1883.187772925764,0.2,0.25,1637.5545851528384,2046.943231441048,1842.2489082969432,251.09170305676852,218.34061135371178,272.92576419213975,245.63318777292577
11,Frantoio,9.0,Superintensiva,1.8,2456.331877729257,0.2,0.25,1965.065502183406,2456.331877729257,2210.698689956332,272.9257641921397,218.34061135371178,272.9257641921397,245.63318777292574
15,Coratina,4.5,Tradizionale,0.9,1080.7860262008733,0.22,0.25,1080.7860262008733,1228.1659388646287,1154.475982532751,240.17467248908295,240.17467248908295,272.9257641921397,256.5502183406113
16,Coratina,6.0,Intensiva,1.2000000000000002,1572.0524017467249,0.22,0.25,1441.0480349344978,1637.5545851528384,1539.301310043668,262.0087336244541,240.17467248908295,272.92576419213975,256.55021834061137
17,Coratina,7.0,Superintensiva,1.4,1910.480349344978,0.22,0.25,1681.2227074235807,1910.480349344978,1795.8515283842794,272.92576419213975,240.17467248908295,272.92576419213975,256.55021834061137
21,Taggiasca,2.5,Tradizionale,0.5,463.9737991266376,0.17,0.2,463.9737991266376,545.8515283842795,504.91266375545854,185.58951965065503,185.58951965065503,218.3406113537118,201.96506550218342
22,Taggiasca,4.0,Intensiva,0.8,829.6943231441047,0.17,0.2,742.35807860262,873.3624454148471,807.8602620087336,207.42358078602618,185.589519650655,218.34061135371178,201.9650655021834
23,Taggiasca,6.0,Superintensiva,1.2000000000000002,1310.043668122271,0.17,0.2,1113.53711790393,1310.043668122271,1211.7903930131006,218.3406113537118,185.589519650655,218.3406113537118,201.9650655021834
24,Pendolino,4.0,Tradizionale,0.8,655.0218340611353,0.15,0.18,655.0218340611353,786.0262008733624,720.5240174672489,163.75545851528383,163.75545851528383,196.5065502183406,180.13100436681222
25,Pendolino,5.0,Intensiva,1.0,927.9475982532751,0.15,0.18,818.7772925764192,982.532751091703,900.655021834061,185.58951965065503,163.75545851528383,196.50655021834058,180.1310043668122
26,Pendolino,7.0,Superintensiva,1.4,1375.5458515283842,0.15,0.18,1146.2882096069868,1375.5458515283842,1260.9170305676855,196.5065502183406,163.75545851528383,196.5065502183406,180.13100436681222
27,Moraiolo,3.2,Tradizionale,0.6400000000000001,628.82096069869,0.18,0.22,628.82096069869,768.5589519650656,698.6899563318777,196.5065502183406,196.5065502183406,240.17467248908298,218.34061135371178
28,Moraiolo,4.8,Intensiva,0.96,1048.0349344978165,0.18,0.22,943.2314410480348,1152.838427947598,1048.0349344978165,218.34061135371178,196.50655021834058,240.1746724890829,218.34061135371172
29,Moraiolo,6.5,Superintensiva,1.3,1561.135371179039,0.18,0.22,1277.292576419214,1561.135371179039,1419.2139737991265,240.17467248908295,196.5065502183406,240.17467248908295,218.34061135371178
1 Unnamed: 0 Varietà di Olive Produzione (tonnellate/ettaro) Tecnica di Coltivazione Produzione Olio (tonnellate/ettaro) Produzione Olio (litri/ettaro) Min % Resa Max % Resa Min Produzione Olio (litri/ettaro) Max Produzione Olio (litri/ettaro) Media Produzione Olio (litri/ettaro) Litri per Tonnellata Min Litri per Tonnellata Max Litri per Tonnellata Media Litri per Tonnellata
2 0 Nocellara dell'Etna 3.5 Tradizionale 0.7000000000000001 687.7729257641921 0.18 0.22 687.7729257641921 840.6113537117903 764.1921397379913 196.5065502183406 196.5065502183406 240.17467248908295 218.34061135371178
3 1 Nocellara dell'Etna 5.0 Intensiva 1.0 1091.703056768559 0.18 0.22 982.532751091703 1200.8733624454148 1091.7030567685588 218.3406113537118 196.50655021834058 240.17467248908298 218.34061135371178
4 2 Nocellara dell'Etna 6.5 Superintensiva 1.3 1561.135371179039 0.18 0.22 1277.292576419214 1561.135371179039 1419.2139737991265 240.17467248908295 196.5065502183406 240.17467248908295 218.34061135371178
5 6 Leccino 4.0 Tradizionale 0.8 698.6899563318777 0.16 0.2 698.6899563318777 873.3624454148471 786.0262008733624 174.67248908296943 174.67248908296943 218.34061135371178 196.5065502183406
6 7 Leccino 6.0 Intensiva 1.2000000000000002 1179.0393013100436 0.16 0.2 1048.0349344978165 1310.043668122271 1179.0393013100438 196.5065502183406 174.6724890829694 218.3406113537118 196.5065502183406
7 8 Leccino 7.5 Superintensiva 1.5 1637.5545851528384 0.16 0.2 1310.043668122271 1637.5545851528384 1473.7991266375543 218.34061135371178 174.67248908296946 218.34061135371178 196.50655021834064
8 9 Frantoio 5.0 Tradizionale 1.0 1091.703056768559 0.2 0.25 1091.703056768559 1364.6288209606987 1228.165938864629 218.3406113537118 218.3406113537118 272.92576419213975 245.6331877729258
9 10 Frantoio 7.5 Intensiva 1.5 1883.187772925764 0.2 0.25 1637.5545851528384 2046.943231441048 1842.2489082969432 251.09170305676852 218.34061135371178 272.92576419213975 245.63318777292577
10 11 Frantoio 9.0 Superintensiva 1.8 2456.331877729257 0.2 0.25 1965.065502183406 2456.331877729257 2210.698689956332 272.9257641921397 218.34061135371178 272.9257641921397 245.63318777292574
11 15 Coratina 4.5 Tradizionale 0.9 1080.7860262008733 0.22 0.25 1080.7860262008733 1228.1659388646287 1154.475982532751 240.17467248908295 240.17467248908295 272.9257641921397 256.5502183406113
12 16 Coratina 6.0 Intensiva 1.2000000000000002 1572.0524017467249 0.22 0.25 1441.0480349344978 1637.5545851528384 1539.301310043668 262.0087336244541 240.17467248908295 272.92576419213975 256.55021834061137
13 17 Coratina 7.0 Superintensiva 1.4 1910.480349344978 0.22 0.25 1681.2227074235807 1910.480349344978 1795.8515283842794 272.92576419213975 240.17467248908295 272.92576419213975 256.55021834061137
14 21 Taggiasca 2.5 Tradizionale 0.5 463.9737991266376 0.17 0.2 463.9737991266376 545.8515283842795 504.91266375545854 185.58951965065503 185.58951965065503 218.3406113537118 201.96506550218342
15 22 Taggiasca 4.0 Intensiva 0.8 829.6943231441047 0.17 0.2 742.35807860262 873.3624454148471 807.8602620087336 207.42358078602618 185.589519650655 218.34061135371178 201.9650655021834
16 23 Taggiasca 6.0 Superintensiva 1.2000000000000002 1310.043668122271 0.17 0.2 1113.53711790393 1310.043668122271 1211.7903930131006 218.3406113537118 185.589519650655 218.3406113537118 201.9650655021834
17 24 Pendolino 4.0 Tradizionale 0.8 655.0218340611353 0.15 0.18 655.0218340611353 786.0262008733624 720.5240174672489 163.75545851528383 163.75545851528383 196.5065502183406 180.13100436681222
18 25 Pendolino 5.0 Intensiva 1.0 927.9475982532751 0.15 0.18 818.7772925764192 982.532751091703 900.655021834061 185.58951965065503 163.75545851528383 196.50655021834058 180.1310043668122
19 26 Pendolino 7.0 Superintensiva 1.4 1375.5458515283842 0.15 0.18 1146.2882096069868 1375.5458515283842 1260.9170305676855 196.5065502183406 163.75545851528383 196.5065502183406 180.13100436681222
20 27 Moraiolo 3.2 Tradizionale 0.6400000000000001 628.82096069869 0.18 0.22 628.82096069869 768.5589519650656 698.6899563318777 196.5065502183406 196.5065502183406 240.17467248908298 218.34061135371178
21 28 Moraiolo 4.8 Intensiva 0.96 1048.0349344978165 0.18 0.22 943.2314410480348 1152.838427947598 1048.0349344978165 218.34061135371178 196.50655021834058 240.1746724890829 218.34061135371172
22 29 Moraiolo 6.5 Superintensiva 1.3 1561.135371179039 0.18 0.22 1277.292576419214 1561.135371179039 1419.2139737991265 240.17467248908295 196.5065502183406 240.17467248908295 218.34061135371178
-1
View File
@@ -1 +0,0 @@
{"queryCost":17544,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"1984-01-01","datetimeEpoch":441759600,"tempmax":13.3,"tempmin":1.5,"temp":6.9,"feelslikemax":13.3,"feelslikemin":1.5,"feelslike":6.9,"dew":5.0,"humidity":88.4,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":null,"snow":null,"snowdepth":3.8,"windgust":null,"windspeed":2.0,"winddir":44.6,"pressure":1022.0,"cloudcover":33.5,"visibility":3.4,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:53","sunriseEpoch":441787073,"sunset":"16:48:48","sunsetEpoch":441820128,"moonphase":0.94,"conditions":"Partially cloudy","description":"Partly cloudy throughout the day.","icon":"partly-cloudy-day","stations":["16243099999","16235099999","16245099999","16242099999","16216099999","16239099999","16219099999","16234099999","16244099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":441759600,"temp":5.6,"feelslike":5.6,"humidity":93.28,"dew":4.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":1.9,"winddir":350.0,"pressure":null,"visibility":2.6,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16239099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":441763200,"temp":5.4,"feelslike":5.4,"humidity":90.74,"dew":4.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":4.0,"preciptype":null,"windgust":null,"windspeed":0.2,"winddir":30.0,"pressure":1022.0,"visibility":3.4,"cloudcover":29.2,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":441766800,"temp":3.9,"feelslike":3.9,"humidity":94.59,"dew":3.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":0.0,"winddir":null,"pressure":null,"visibility":1.5,"cloudcover":40.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16239099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":441770400,"temp":4.2,"feelslike":4.2,"humidity":95.74,"dew":3.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":0.9,"winddir":20.0,"pressure":null,"visibility":2.2,"cloudcover":37.3,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16239099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":441774000,"temp":4.1,"feelslike":4.1,"humidity":98.57,"dew":3.9,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":4.0,"preciptype":null,"windgust":null,"windspeed":2.0,"winddir":51.0,"pressure":null,"visibility":4.0,"cloudcover":29.2,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":441777600,"temp":3.8,"feelslike":3.8,"humidity":94.99,"dew":3.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":0.0,"winddir":null,"pressure":null,"visibility":2.9,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16239099999","16234099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":441781200,"temp":3.1,"feelslike":3.1,"humidity":94.85,"dew":2.4,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":1.9,"winddir":90.0,"pressure":null,"visibility":2.7,"cloudcover":15.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16242099999","16239099999","16234099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":441784800,"temp":1.5,"feelslike":1.5,"humidity":98.19,"dew":1.2,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":4.0,"preciptype":null,"windgust":null,"windspeed":0.0,"winddir":null,"pressure":null,"visibility":1.0,"cloudcover":13.8,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16235099999","16245099999","16242099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":441788400,"temp":1.6,"feelslike":1.6,"humidity":98.28,"dew":1.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":0.0,"winddir":null,"pressure":null,"visibility":0.6,"cloudcover":67.9,"solarradiatioLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"1986-01-01","datetimeEpoch":504918000,"tempmax":12.6,"tempmin":5.1,"temp":9.0,"feelslikemax":12.6,"feelslikemin":5.1,"feelslike":8.3,"dew":6.8,"humidity":86.8,"precip":0.074,"precipprob":100.0,"precipcover":4.17,"preciptype":["rain"],"snow":null,"snowdepth":22.8,"windgust":null,"windspeed":15.9,"winddir":163.9,"pressure":1000.8,"cloudcover":59.6,"visibility":10.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:56","sunriseEpoch":504945476,"sunset":"16:49:14","sunsetEpoch":504978554,"moonphase":0.68,"conditions":"Rain, Partially cloudy","description":"Partly cloudy throughout the day with afternoon rain.","icon":"rain","stations":["16243099999","16235099999","16245099999","16242099999","16216099999","16239099999","16219099999","16234099999","16244099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":504918000,"temp":8.4,"feelslike":8.4,"humidity":93.1,"dew":7.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":2.4,"winddir":90.0,"pressure":null,"visibility":11.0,"cloudcover":80.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16239099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":504921600,"temp":7.9,"feelslike":7.9,"humidity":93.69,"dew":6.9,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":20.0,"preciptype":null,"windgust":null,"windspeed":3.1,"winddir":75.0,"pressure":1000.8,"visibility":12.0,"cloudcover":79.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":504925200,"temp":8.3,"feelslike":7.9,"humidity":93.42,"dew":7.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":5.0,"winddir":90.0,"pressure":null,"visibility":8.8,"cloudcover":80.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":504928800,"temp":8.3,"feelslike":8.3,"humidity":93.42,"dew":7.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":2.1,"winddir":120.0,"pressure":null,"visibility":9.6,"cloudcover":85.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":504932400,"temp":8.7,"feelslike":8.7,"humidity":89.16,"dew":7.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":20.0,"preciptype":null,"windgust":null,"windspeed":3.9,"winddir":80.0,"pressure":null,"visibility":9.4,"cloudcover":85.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":504936000,"temp":8.3,"feelslike":6.1,"humidity":92.0,"dew":7.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":12.8,"winddir":115.0,"pressure":null,"visibility":9.7,"cloudcover":78.1,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16234099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":504939600,"temp":8.3,"feelslike":5.9,"humidity":95.18,"dew":7.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":14.8,"winddir":117.0,"pressure":null,"visibility":10.2,"cloudcover":58.2,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":504943200,"temp":9.0,"feelslike":6.9,"humidity":88.05,"dew":7.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":22.0,"preciptype":null,"windgust":null,"windspeed":13.4,"winddir":104.0,"pressure":null,"visibility":10.7,"cloudcover":52.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16235099999","16245099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":504946800,"temp":9.1,"feelslike":6.7,"humidity":92.06,"dew":7.9,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":15.7,"winddir":122.0,"pressure":null,"visibility":8.5,"cloudLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17544,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"1988-01-01","datetimeEpoch":567990000,"tempmax":13.9,"tempmin":3.4,"temp":7.8,"feelslikemax":13.9,"feelslikemin":3.4,"feelslike":7.3,"dew":5.9,"humidity":88.4,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":null,"snow":null,"snowdepth":null,"windgust":null,"windspeed":11.5,"winddir":38.3,"pressure":1015.0,"cloudcover":33.8,"visibility":5.9,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:53","sunriseEpoch":568017473,"sunset":"16:48:49","sunsetEpoch":568050529,"moonphase":0.41,"conditions":"Partially cloudy","description":"Partly cloudy throughout the day.","icon":"partly-cloudy-day","stations":["16243099999","16235099999","16245099999","16242099999","16216099999","16239099999","16219099999","16234099999","16244099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":567990000,"temp":7.3,"feelslike":6.3,"humidity":93.37,"dew":6.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":6.5,"winddir":87.0,"pressure":null,"visibility":3.5,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":567993600,"temp":6.7,"feelslike":5.4,"humidity":92.57,"dew":5.5,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":6.8,"winddir":78.0,"pressure":1015.0,"visibility":4.2,"cloudcover":35.6,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":567997200,"temp":5.6,"feelslike":3.9,"humidity":100.0,"dew":5.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":8.0,"winddir":31.0,"pressure":null,"visibility":2.5,"cloudcover":40.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":568000800,"temp":5.9,"feelslike":5.1,"humidity":96.27,"dew":5.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":4.9,"winddir":45.0,"pressure":null,"visibility":2.5,"cloudcover":52.7,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":568004400,"temp":5.9,"feelslike":4.5,"humidity":91.2,"dew":4.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":6.9,"winddir":82.0,"pressure":null,"visibility":5.0,"cloudcover":53.3,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":568008000,"temp":6.6,"feelslike":5.5,"humidity":93.33,"dew":5.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":6.2,"winddir":16.0,"pressure":null,"visibility":2.5,"cloudcover":52.7,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":568011600,"temp":5.9,"feelslike":3.8,"humidity":91.53,"dew":4.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":9.5,"winddir":45.0,"pressure":null,"visibility":3.3,"cloudcover":51.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":568015200,"temp":3.6,"feelslike":3.6,"humidity":98.26,"dew":3.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":1.1,"winddir":47.0,"pressure":null,"visibility":2.8,"cloudcover":39.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16235099999","16242099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":568018800,"temp":3.7,"feelslike":3.7,"humidity":98.06,"dew":3.4,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":1.8,"winddir":29.0,"pressure":null,"visibility":1.9,"cloudcover":23.6,"solarradiation":null,"solarenergy":null,"uvindex":nLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"1990-01-01","datetimeEpoch":631148400,"tempmax":8.4,"tempmin":0.1,"temp":3.4,"feelslikemax":7.2,"feelslikemin":-3.8,"feelslike":0.9,"dew":1.0,"humidity":84.9,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":null,"snow":null,"snowdepth":null,"windgust":null,"windspeed":13.5,"winddir":49.0,"pressure":1016.8,"cloudcover":43.7,"visibility":7.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:56","sunriseEpoch":631175876,"sunset":"16:49:15","sunsetEpoch":631208955,"moonphase":0.15,"conditions":"Partially cloudy","description":"Partly cloudy throughout the day.","icon":"partly-cloudy-day","stations":["16240099999","16245099999","16242099999","16216099999","16239099999","16219099999","16234099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":631148400,"temp":1.1,"feelslike":-2.3,"humidity":88.25,"dew":-0.7,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":11.2,"winddir":50.0,"pressure":1016.7,"visibility":6.4,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16240099999","16242099999","16239099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":631152000,"temp":0.8,"feelslike":-2.3,"humidity":87.68,"dew":-1.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":9.9,"winddir":48.0,"pressure":1016.9,"visibility":6.8,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":631155600,"temp":1.1,"feelslike":-2.0,"humidity":93.04,"dew":0.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.0,"winddir":50.0,"pressure":null,"visibility":5.6,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":631159200,"temp":0.8,"feelslike":-2.6,"humidity":94.89,"dew":0.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.8,"winddir":57.0,"pressure":null,"visibility":5.6,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":631162800,"temp":0.2,"feelslike":-3.2,"humidity":89.33,"dew":-1.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.7,"winddir":55.0,"pressure":null,"visibility":6.3,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":631166400,"temp":0.3,"feelslike":-3.0,"humidity":98.04,"dew":0.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.3,"winddir":55.0,"pressure":null,"visibility":5.6,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":631170000,"temp":0.1,"feelslike":-3.8,"humidity":98.03,"dew":-0.2,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":12.1,"winddir":47.0,"pressure":null,"visibility":5.6,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":631173600,"temp":0.8,"feelslike":-2.4,"humidity":94.18,"dew":0.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.4,"winddir":51.0,"pressure":null,"visibility":8.6,"cloudcover":10.8,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16245099999","16242099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":631177200,"temp":1.1,"feelslike":-2.8,"humidity":91.2,"dew":-0.2,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":13.5,"winddir":60.0,"pressure":null,"visibility":5.5,"cloudcover":44.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-day","stations":["16242099999","16239099999"],"source":"obs"},{"datetime":"09:00:00","datetimeEpoch":631180800,"temLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17544,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"1992-01-01","datetimeEpoch":694220400,"tempmax":11.1,"tempmin":-1.9,"temp":4.7,"feelslikemax":11.1,"feelslikemin":-3.2,"feelslike":3.0,"dew":2.0,"humidity":83.5,"precip":1.582,"precipprob":100.0,"precipcover":8.33,"preciptype":["rain","snow"],"snow":null,"snowdepth":15.8,"windgust":null,"windspeed":22.2,"winddir":56.1,"pressure":1035.3,"cloudcover":22.2,"visibility":6.8,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:52","sunriseEpoch":694247872,"sunset":"16:48:51","sunsetEpoch":694280931,"moonphase":0.88,"conditions":"Snow, Rain, Partially cloudy","description":"Partly cloudy throughout the day with rain or snow.","icon":"rain","stations":["16240099999","16243099999","16235099999","16245099999","16242099999","16216099999","16239099999","16219099999","16234099999","16244099999","16224099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":694220400,"temp":2.5,"feelslike":-1.8,"humidity":90.72,"dew":1.2,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":18.3,"winddir":60.0,"pressure":1031.1,"visibility":5.6,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16240099999","16243099999","16242099999","16216099999","16239099999","16219099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":694224000,"temp":2.9,"feelslike":1.0,"humidity":87.06,"dew":0.9,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":16.0,"preciptype":null,"windgust":null,"windspeed":6.8,"winddir":80.0,"pressure":1031.2,"visibility":6.3,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16242099999","16216099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":694227600,"temp":2.3,"feelslike":-2.1,"humidity":90.36,"dew":0.9,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":18.3,"winddir":70.0,"pressure":1031.7,"visibility":3.2,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16240099999","16243099999","16242099999","16216099999","16239099999","16219099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":694231200,"temp":2.9,"feelslike":-1.8,"humidity":88.67,"dew":1.2,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":22.2,"winddir":60.0,"pressure":1032.1,"visibility":3.2,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16240099999","16243099999","16242099999","16216099999","16239099999","16219099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":694234800,"temp":1.6,"feelslike":-1.4,"humidity":95.73,"dew":1.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.0,"winddir":62.0,"pressure":1032.5,"visibility":5.6,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16242099999","16216099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":694238400,"temp":1.6,"feelslike":-1.3,"humidity":94.66,"dew":0.8,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":9.4,"winddir":62.0,"pressure":1032.5,"visibility":5.7,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16242099999","16216099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":694242000,"temp":1.4,"feelslike":-3.2,"humidity":84.12,"dew":-1.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":18.4,"winddir":70.0,"pressure":1033.1,"visibility":5.5,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16240099999","16243099999","16242099999","16216099999","16219099999","16244099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":694245600,"temp":2.0,"feelslike":-0.6,"humidity":89.97,"dew":0.5,"precip":0.805,"precipprob":100.0,"snow":null,"snowdepth":16.0,"preciptype":["rain","snow"],"windgust":null,"windspeed":8.9,"winddir":60.0,"pressure":1034.5,"visibility":7.5,"cloudcover":35.1,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Snow, Rain, Partially cloudy","icon":"rain","stations":["16245099999","16242099999","16239099999","16219099999","16224099999","16234099999"],"souLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"1994-01-01","datetimeEpoch":757378800,"tempmax":11.9,"tempmin":4.1,"temp":8.4,"feelslikemax":11.9,"feelslikemin":0.4,"feelslike":6.6,"dew":6.3,"humidity":86.7,"precip":2.382,"precipprob":100.0,"precipcover":8.33,"preciptype":["rain"],"snow":null,"snowdepth":106.0,"windgust":null,"windspeed":37.3,"winddir":167.7,"pressure":1008.1,"cloudcover":70.1,"visibility":8.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:54","sunriseEpoch":757406274,"sunset":"16:49:17","sunsetEpoch":757439357,"moonphase":0.62,"conditions":"Rain, Partially cloudy","description":"Partly cloudy throughout the day with rain in the morning and afternoon.","icon":"rain","stations":["16243099999","16235099999","16245099999","16242099999","16216099999","16239099999","16219099999","16214099999","16234099999","16244099999","16224099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":757378800,"temp":6.5,"feelslike":3.3,"humidity":85.84,"dew":4.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":17.5,"winddir":142.0,"pressure":1019.5,"visibility":6.0,"cloudcover":46.9,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16216099999","16239099999","16219099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":757382400,"temp":8.2,"feelslike":5.2,"humidity":85.43,"dew":5.9,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":105.0,"preciptype":null,"windgust":null,"windspeed":19.6,"winddir":137.0,"pressure":1018.5,"visibility":6.4,"cloudcover":48.6,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16216099999","16239099999","16219099999","16214099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":757386000,"temp":8.7,"feelslike":5.9,"humidity":82.24,"dew":5.8,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":18.7,"winddir":163.0,"pressure":null,"visibility":6.9,"cloudcover":32.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16216099999","16239099999","16219099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":757389600,"temp":6.2,"feelslike":3.1,"humidity":95.34,"dew":5.5,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":16.4,"winddir":152.0,"pressure":null,"visibility":7.4,"cloudcover":65.4,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16216099999","16219099999","16214099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":757393200,"temp":10.0,"feelslike":10.0,"humidity":83.76,"dew":7.4,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":34.5,"winddir":154.0,"pressure":1015.0,"visibility":9.8,"cloudcover":87.2,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16242099999","16216099999","16239099999","16219099999","16214099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":757396800,"temp":10.1,"feelslike":10.1,"humidity":86.64,"dew":8.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":26.6,"winddir":149.0,"pressure":null,"visibility":9.8,"cloudcover":82.9,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16216099999","16219099999","16234099999","16244099999","16214099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":757400400,"temp":10.4,"feelslike":10.4,"humidity":72.46,"dew":5.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":21.3,"winddir":155.0,"pressure":null,"visibility":9.2,"cloudcover":82.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16216099999","16219099999","16234099999","16244099999","16214099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":757404000,"temp":9.9,"feelslike":6.3,"humidity":87.47,"dew":7.9,"precip":1.583,"precipprob":100.0,"snow":null,"snowdepth":105.0,"preciptype":["rain"],"windgust":null,"windspeed":31.9,"winddir":138.0,"pressure":1010.0,"visibility":9.7,"cloudcover":92.8,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Rain, Overcast","icon":"rain","stations":["16243099999","16245099999","16242099999","16219099999","16224099999","1Line truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17544,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"1996-01-01","datetimeEpoch":820450800,"tempmax":15.8,"tempmin":6.1,"temp":11.2,"feelslikemax":15.8,"feelslikemin":3.5,"feelslike":10.9,"dew":9.2,"humidity":87.7,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":null,"snow":null,"snowdepth":null,"windgust":null,"windspeed":14.0,"winddir":154.3,"pressure":1001.4,"cloudcover":45.9,"visibility":9.9,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:50","sunriseEpoch":820478270,"sunset":"16:48:52","sunsetEpoch":820511332,"moonphase":0.35,"conditions":"Partially cloudy","description":"Partly cloudy throughout the day.","icon":"partly-cloudy-day","stations":["16243099999","16235099999","16245099999","16216099999","16242099999","16239099999","16219099999","16244099999","16234099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":820450800,"temp":9.0,"feelslike":7.8,"humidity":93.43,"dew":8.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":8.2,"winddir":249.0,"pressure":null,"visibility":10.7,"cloudcover":86.9,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16216099999","16239099999","16219099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":820454400,"temp":9.5,"feelslike":8.3,"humidity":91.73,"dew":8.2,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":8.3,"winddir":159.0,"pressure":998.8,"visibility":10.4,"cloudcover":24.1,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16242099999","16216099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":820458000,"temp":6.1,"feelslike":3.5,"humidity":96.16,"dew":5.5,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":12.5,"winddir":208.0,"pressure":null,"visibility":8.4,"cloudcover":52.3,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16216099999","16219099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":820461600,"temp":8.6,"feelslike":8.1,"humidity":90.41,"dew":7.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":5.3,"winddir":356.0,"pressure":null,"visibility":10.0,"cloudcover":36.7,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16216099999","16219099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":820465200,"temp":8.2,"feelslike":6.8,"humidity":92.22,"dew":7.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":8.5,"winddir":158.0,"pressure":null,"visibility":10.7,"cloudcover":31.8,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16216099999","16239099999","16219099999","16244099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":820468800,"temp":9.0,"feelslike":7.9,"humidity":89.2,"dew":7.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":7.5,"winddir":208.5,"pressure":null,"visibility":10.6,"cloudcover":48.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":null},{"datetime":"06:00:00","datetimeEpoch":820472400,"temp":9.7,"feelslike":9.0,"humidity":86.16,"dew":7.5,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":6.5,"winddir":259.0,"pressure":null,"visibility":10.5,"cloudcover":65.1,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16216099999","16219099999","16234099999","16244099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":820476000,"temp":10.9,"feelslike":10.9,"humidity":93.5,"dew":9.8,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":2.0,"winddir":157.0,"pressure":null,"visibility":10.2,"cloudcover":76.6,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16216099999","16219099999","16234099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":820479600,"temp":9.9,"feelslike":9.9,"humidity":96.07,"dew":9.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepthLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"1998-01-01","datetimeEpoch":883609200,"tempmax":15.0,"tempmin":-0.3,"temp":8.2,"feelslikemax":15.0,"feelslikemin":-0.3,"feelslike":8.1,"dew":5.5,"humidity":84.5,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":null,"snow":null,"snowdepth":null,"windgust":null,"windspeed":7.5,"winddir":56.2,"pressure":1021.0,"cloudcover":39.7,"visibility":7.7,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:54","sunriseEpoch":883636674,"sunset":"16:49:18","sunsetEpoch":883669758,"moonphase":0.1,"conditions":"Partially cloudy","description":"Partly cloudy throughout the day.","icon":"partly-cloudy-day","stations":["16240099999","16243099999","16235099999","16245099999","16242099999","16216099999","16219099999","16239099999","16244099999","16234099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":883609200,"temp":9.3,"feelslike":9.3,"humidity":84.7,"dew":6.9,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":4.8,"winddir":99.0,"pressure":1019.5,"visibility":8.9,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16245099999","16242099999","16219099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":883612800,"temp":6.7,"feelslike":6.7,"humidity":94.87,"dew":6.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":3.7,"winddir":35.0,"pressure":1019.9,"visibility":6.4,"cloudcover":43.4,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":883616400,"temp":6.9,"feelslike":6.9,"humidity":91.81,"dew":5.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":3.3,"winddir":13.0,"pressure":null,"visibility":3.4,"cloudcover":49.3,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16239099999","16219099999","16244099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":883620000,"temp":6.3,"feelslike":6.3,"humidity":96.65,"dew":5.8,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":3.7,"winddir":38.0,"pressure":null,"visibility":4.6,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16239099999","16219099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":883623600,"temp":5.3,"feelslike":3.6,"humidity":97.73,"dew":5.0,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":7.5,"winddir":38.0,"pressure":null,"visibility":6.8,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16245099999","16239099999","16219099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":883627200,"temp":4.9,"feelslike":4.9,"humidity":86.26,"dew":2.8,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":3.8,"winddir":35.0,"pressure":null,"visibility":10.0,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16245099999","16219099999","16234099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":883630800,"temp":5.4,"feelslike":5.4,"humidity":92.75,"dew":4.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":3.4,"winddir":41.0,"pressure":1019.5,"visibility":6.6,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16245099999","16242099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":883634400,"temp":4.8,"feelslike":4.8,"humidity":95.28,"dew":4.1,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":2.4,"winddir":32.0,"pressure":null,"visibility":7.6,"cloudcover":30.3,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16216099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":883638000,"temp":3.4,"feelslike":3.4,"humidity":88.25,"dew":1.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":0.2,"winddir":30.0,"pressure":1020.5,"visibility":3.1,"cloudcover":56.Line truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17544,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2000-01-01","datetimeEpoch":946681200,"tempmax":8.4,"tempmin":-1.6,"temp":2.2,"feelslikemax":7.0,"feelslikemin":-5.5,"feelslike":-0.2,"dew":-3.3,"humidity":69.8,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":null,"snow":null,"snowdepth":16.0,"windgust":null,"windspeed":11.4,"winddir":25.2,"pressure":1023.4,"cloudcover":0.0,"visibility":10.2,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:50","sunriseEpoch":946708670,"sunset":"16:48:53","sunsetEpoch":946741733,"moonphase":0.82,"conditions":"Clear","description":"Clear conditions throughout the day.","icon":"clear-day","stations":["16243099999","16245099999","16242099999","16216099999","16239099999","16219099999","16234099999","16244099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":946681200,"temp":0.9,"feelslike":-2.6,"humidity":72.11,"dew":-3.6,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":11.4,"winddir":37.0,"pressure":null,"visibility":10.0,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":946684800,"temp":0.3,"feelslike":-2.9,"humidity":74.27,"dew":-3.7,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":9.9,"winddir":30.0,"pressure":1021.7,"visibility":12.6,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":946688400,"temp":0.1,"feelslike":-3.3,"humidity":75.83,"dew":-3.7,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.4,"winddir":37.0,"pressure":1022.0,"visibility":10.0,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":946692000,"temp":-0.3,"feelslike":-3.7,"humidity":79.44,"dew":-3.4,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":9.9,"winddir":36.0,"pressure":1022.0,"visibility":10.0,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":946695600,"temp":-0.3,"feelslike":-3.8,"humidity":76.75,"dew":-3.9,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.6,"winddir":5.0,"pressure":1022.0,"visibility":10.4,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":946699200,"temp":-0.6,"feelslike":-3.9,"humidity":82.19,"dew":-3.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":9.5,"winddir":41.0,"pressure":1022.0,"visibility":10.0,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":946702800,"temp":-1.1,"feelslike":-4.8,"humidity":83.44,"dew":-3.5,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windgust":null,"windspeed":10.6,"winddir":30.0,"pressure":1022.0,"visibility":10.0,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":946706400,"temp":-1.4,"feelslike":-4.9,"humidity":87.31,"dew":-3.3,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":16.0,"preciptype":null,"windgust":null,"windspeed":9.6,"winddir":19.0,"pressure":1022.2,"visibility":10.3,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16239099999","16219099999","16234099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":946710000,"temp":-1.3,"feelslike":-4.5,"humidity":86.52,"dew":-3.2,"precip":0.0,"precipprob":0.0,"snow":null,"snowdepth":null,"preciptype":null,"windLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2002-01-01","datetimeEpoch":1009839600,"tempmax":8.3,"tempmin":1.3,"temp":4.4,"feelslikemax":5.8,"feelslikemin":-2.1,"feelslike":0.6,"dew":-10.0,"humidity":35.3,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":["rain"],"snow":0.3,"snowdepth":18.9,"windgust":null,"windspeed":32.9,"winddir":21.5,"pressure":1028.1,"cloudcover":24.5,"visibility":11.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:54","sunriseEpoch":1009867074,"sunset":"16:49:19","sunsetEpoch":1009900159,"moonphase":0.57,"conditions":"Partially cloudy","description":"Partly cloudy throughout the day.","icon":"partly-cloudy-day","stations":["16243099999","16235099999","16245099999","16242099999","16216099999","16239099999","16219099999","16234099999","16224099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1009839600,"temp":4.2,"feelslike":-0.6,"humidity":57.94,"dew":-3.3,"precip":0.0,"precipprob":0.0,"snow":0.04,"snowdepth":18.67,"preciptype":["rain"],"windgust":null,"windspeed":26.2,"winddir":355.0,"pressure":1018.0,"visibility":10.2,"cloudcover":34.3,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16242099999","16239099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1009843200,"temp":4.0,"feelslike":-1.5,"humidity":44.24,"dew":-7.1,"precip":0.0,"precipprob":0.0,"snow":0.04,"snowdepth":18.71,"preciptype":["rain"],"windgust":null,"windspeed":32.9,"winddir":8.0,"pressure":1009.1,"visibility":13.2,"cloudcover":4.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16235099999","16245099999","16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1009846800,"temp":4.5,"feelslike":-0.3,"humidity":39.56,"dew":-8.1,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":18.75,"preciptype":["rain"],"windgust":null,"windspeed":26.0,"winddir":10.0,"pressure":1020.6,"visibility":10.4,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16242099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1009850400,"temp":3.9,"feelslike":-0.8,"humidity":43.14,"dew":-7.5,"precip":0.0,"precipprob":0.0,"snow":0.04,"snowdepth":18.79,"preciptype":["rain"],"windgust":null,"windspeed":23.7,"winddir":8.0,"pressure":1021.6,"visibility":10.4,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16242099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1009854000,"temp":3.4,"feelslike":-1.5,"humidity":43.97,"dew":-7.7,"precip":0.0,"precipprob":0.0,"snow":0.04,"snowdepth":18.83,"preciptype":["rain"],"windgust":null,"windspeed":25.2,"winddir":13.0,"pressure":1022.8,"visibility":10.6,"cloudcover":20.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16219099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1009857600,"temp":4.1,"feelslike":0.0,"humidity":39.52,"dew":-8.4,"precip":0.0,"precipprob":0.0,"snow":0.04,"snowdepth":18.88,"preciptype":["rain"],"windgust":null,"windspeed":19.7,"winddir":12.0,"pressure":1023.6,"visibility":10.1,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16242099999","16234099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1009861200,"temp":3.8,"feelslike":-0.1,"humidity":36.39,"dew":-9.8,"precip":0.0,"precipprob":0.0,"snow":0.04,"snowdepth":18.92,"preciptype":["rain"],"windgust":null,"windspeed":17.7,"winddir":39.0,"pressure":1024.3,"visibility":10.1,"cloudcover":50.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16243099999","16245099999","16242099999","16234099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1009864800,"temp":3.2,"feelslike":-2.0,"humidity":36.68,"dew":-10.2,"precip":0.0,"precipprob":0.0,"snow":0.04,"snowdepth":18.96,"preciptype":["rain"],"windgust":null,"windspeed":26.8,"winddir":23.0,"pressure":1025.4,"visibility":22.5,"cloudcover":2.7,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16243099999","16245099999","16242099999","16216099999","16219099999","16234099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1009868400,"temp":3.0,"feelslike":-1.3,"humidity":36.86,"dew":-10.4,"precip":0.0,"precipprob":0.0,"snow":0.04,"snowdepth":1Line truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17544,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2004-01-01","datetimeEpoch":1072911600,"tempmax":13.9,"tempmin":3.8,"temp":7.1,"feelslikemax":13.9,"feelslikemin":0.9,"feelslike":6.2,"dew":3.7,"humidity":81.1,"precip":4.711,"precipprob":100.0,"precipcover":4.17,"preciptype":["rain"],"snow":0.0,"snowdepth":1.9,"windgust":null,"windspeed":12.7,"winddir":20.8,"pressure":1005.6,"cloudcover":47.3,"visibility":8.7,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:51","sunriseEpoch":1072939071,"sunset":"16:48:54","sunsetEpoch":1072972134,"moonphase":0.3,"conditions":"Rain, Partially cloudy","description":"Partly cloudy throughout the day with morning rain.","icon":"rain","stations":["16221099999","16243099999","16235099999","16245099999","16242099999","16216099999","16239099999","16219099999","16224099999","16234099999","16244099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1072911600,"temp":4.8,"feelslike":3.4,"humidity":94.21,"dew":4.0,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.87,"preciptype":null,"windgust":null,"windspeed":6.4,"winddir":22.0,"pressure":1006.0,"visibility":6.0,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16219099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1072915200,"temp":4.9,"feelslike":3.5,"humidity":92.08,"dew":3.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.87,"preciptype":null,"windgust":null,"windspeed":6.4,"winddir":34.0,"pressure":null,"visibility":4.7,"cloudcover":54.9,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16219099999","16224099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1072918800,"temp":4.5,"feelslike":2.0,"humidity":95.41,"dew":3.8,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.87,"preciptype":null,"windgust":null,"windspeed":10.1,"winddir":37.0,"pressure":1005.8,"visibility":5.4,"cloudcover":50.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16239099999","16219099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1072922400,"temp":4.5,"feelslike":2.0,"humidity":97.09,"dew":4.1,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.87,"preciptype":null,"windgust":null,"windspeed":10.1,"winddir":38.0,"pressure":1005.3,"visibility":5.3,"cloudcover":null,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16245099999","16239099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1072926000,"temp":4.4,"feelslike":1.8,"humidity":96.47,"dew":3.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.87,"preciptype":null,"windgust":null,"windspeed":10.5,"winddir":44.0,"pressure":1005.1,"visibility":4.5,"cloudcover":30.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16239099999","16219099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1072929600,"temp":3.8,"feelslike":0.9,"humidity":99.75,"dew":3.8,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.87,"preciptype":null,"windgust":null,"windspeed":11.8,"winddir":41.0,"pressure":1004.9,"visibility":3.9,"cloudcover":50.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16239099999","16219099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1072933200,"temp":4.5,"feelslike":1.5,"humidity":97.09,"dew":4.1,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.87,"preciptype":null,"windgust":null,"windspeed":12.7,"winddir":36.0,"pressure":1005.1,"visibility":5.3,"cloudcover":90.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16239099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1072936800,"temp":4.9,"feelslike":2.5,"humidity":92.96,"dew":3.8,"precip":4.711,"precipprob":100.0,"snow":0.0,"snowdepth":1.87,"preciptype":["rain"],"windgust":null,"windspeed":10.0,"winddir":41.0,"pressure":1005.1,"visibility":7.9,"cloudcover":80.2,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Rain, Partially cloudy","icon":"rain","stations":["16245099999","16216099999","16239099999","16219099999","16224099999","16234099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1072940400,"temp":4.2,"feelslike":4.2,"humidity":82.84,"dew":1.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.87,"preciptype":null,"windgust":null,"windspeed":2.0,"winddir":41.0,"pressure":1005.9,"visibilitLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2006-01-01","datetimeEpoch":1136070000,"tempmax":13.0,"tempmin":8.9,"temp":11.9,"feelslikemax":13.0,"feelslikemin":8.9,"feelslike":11.8,"dew":10.0,"humidity":88.7,"precip":5.108,"precipprob":100.0,"precipcover":12.5,"preciptype":["rain"],"snow":0.0,"snowdepth":0.0,"windgust":null,"windspeed":21.1,"winddir":198.5,"pressure":1008.1,"cloudcover":83.0,"visibility":7.3,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:54","sunriseEpoch":1136097474,"sunset":"16:49:19","sunsetEpoch":1136130559,"moonphase":0.05,"conditions":"Rain, Partially cloudy","description":"Partly cloudy throughout the day with a chance of rain throughout the day.","icon":"rain","stations":["16233099999","16235099999","16243099999","16245099999","16242099999","16238099999","16239099999","16224099999","16234099999","16244099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1136070000,"temp":10.3,"feelslike":10.3,"humidity":91.16,"dew":8.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":8.1,"winddir":146.0,"pressure":1013.7,"visibility":7.2,"cloudcover":83.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16233099999","16235099999","16245099999","16242099999","16238099999","16239099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1136073600,"temp":11.3,"feelslike":11.3,"humidity":93.62,"dew":10.3,"precip":3.047,"precipprob":100.0,"snow":0.0,"snowdepth":0.0,"preciptype":["rain"],"windgust":null,"windspeed":17.7,"winddir":169.0,"pressure":null,"visibility":5.3,"cloudcover":100.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Rain, Overcast","icon":"rain","stations":["16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1136077200,"temp":11.7,"feelslike":11.7,"humidity":92.13,"dew":10.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":9.3,"winddir":188.0,"pressure":1011.7,"visibility":7.8,"cloudcover":90.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16233099999","16235099999","16245099999","16242099999","16238099999","16239099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1136080800,"temp":13.0,"feelslike":13.0,"humidity":91.55,"dew":11.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":12.8,"winddir":206.0,"pressure":1011.8,"visibility":8.3,"cloudcover":80.8,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16233099999","16235099999","16245099999","16242099999","16238099999","16239099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1136084400,"temp":12.9,"feelslike":12.9,"humidity":91.8,"dew":11.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":10.6,"winddir":190.0,"pressure":1010.7,"visibility":7.2,"cloudcover":91.6,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Overcast","icon":"cloudy","stations":["16235099999","16245099999","16242099999","16238099999","16239099999","16224099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1136088000,"temp":12.9,"feelslike":12.9,"humidity":91.62,"dew":11.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":11.1,"winddir":218.0,"pressure":1010.1,"visibility":8.3,"cloudcover":56.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16233099999","16235099999","16245099999","16242099999","16238099999","16239099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1136091600,"temp":13.0,"feelslike":13.0,"humidity":91.44,"dew":11.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":13.7,"winddir":201.0,"pressure":1010.0,"visibility":8.3,"cloudcover":56.5,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16233099999","16235099999","16245099999","16242099999","16238099999","16239099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1136095200,"temp":12.9,"feelslike":12.9,"humidity":84.37,"dew":10.3,"precip":0.303,"precipprob":100.0,"snow":0.0,"snowdepth":0.0,"preciptype":["rain"],"windgust":null,"windspeed":19.6,"winddir":217.0,"pressure":1010.1,"visibility":6.2,"cloudcover":63.9,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Rain, Partially cloudy","icon":"rain","stations":["16235099999","16245099999","16242099999","1623909Line truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17544,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2008-01-01","datetimeEpoch":1199142000,"tempmax":9.0,"tempmin":1.9,"temp":4.6,"feelslikemax":5.9,"feelslikemin":-2.0,"feelslike":1.4,"dew":-3.2,"humidity":60.0,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":null,"snow":0.0,"snowdepth":1.3,"windgust":null,"windspeed":24.9,"winddir":29.8,"pressure":1017.6,"cloudcover":27.7,"visibility":11.2,"solarradiation":null,"solarenergy":null,"uvindex":null,"sunrise":"07:37:51","sunriseEpoch":1199169471,"sunset":"16:48:55","sunsetEpoch":1199202535,"moonphase":0.77,"conditions":"Partially cloudy","description":"Partly cloudy throughout the day.","icon":"partly-cloudy-day","stations":["16217099999","16215099999","16245099999","16242099999","16238099999","16239099999","16219099999","16224099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1199142000,"temp":4.3,"feelslike":2.6,"humidity":80.83,"dew":1.3,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.31,"preciptype":null,"windgust":null,"windspeed":7.1,"winddir":54.0,"pressure":1018.2,"visibility":10.0,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16217099999","16245099999","16242099999","16238099999","16239099999","16224099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1199145600,"temp":3.6,"feelslike":0.3,"humidity":73.31,"dew":-0.8,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.31,"preciptype":null,"windgust":null,"windspeed":13.2,"winddir":43.0,"pressure":1018.0,"visibility":8.7,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16217099999","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1199149200,"temp":2.9,"feelslike":0.6,"humidity":85.16,"dew":0.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.31,"preciptype":null,"windgust":null,"windspeed":8.2,"winddir":57.0,"pressure":1017.2,"visibility":10.0,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16217099999","16245099999","16242099999","16238099999","16239099999","16224099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1199152800,"temp":2.6,"feelslike":0.6,"humidity":86.99,"dew":0.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.3,"preciptype":null,"windgust":null,"windspeed":7.1,"winddir":47.0,"pressure":1016.9,"visibility":10.0,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16217099999","16215099999","16245099999","16238099999","16239099999","16224099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1199156400,"temp":2.5,"feelslike":-1.3,"humidity":77.73,"dew":-1.0,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.3,"preciptype":null,"windgust":null,"windspeed":14.7,"winddir":42.0,"pressure":1017.2,"visibility":8.9,"cloudcover":27.4,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16217099999","16215099999","16245099999","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1199160000,"temp":2.2,"feelslike":0.6,"humidity":88.34,"dew":0.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.3,"preciptype":null,"windgust":null,"windspeed":5.9,"winddir":56.0,"pressure":1017.2,"visibility":10.7,"cloudcover":39.1,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16217099999","16245099999","16242099999","16238099999","16239099999","16224099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1199163600,"temp":2.5,"feelslike":0.5,"humidity":86.77,"dew":0.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.3,"preciptype":null,"windgust":null,"windspeed":6.9,"winddir":54.0,"pressure":1016.9,"visibility":10.9,"cloudcover":25.6,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16217099999","16215099999","16245099999","16238099999","16239099999","16224099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1199167200,"temp":1.9,"feelslike":-1.8,"humidity":75.82,"dew":-1.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":1.29,"preciptype":null,"windgust":null,"windspeed":14.0,"winddir":57.0,"pressure":1017.5,"visibility":10.2,"cloudcover":0.0,"solarradiation":null,"solarenergy":null,"uvindex":null,"conditions":"Clear","icon":"clear-night","stations":["16217099999","16242099999","16239099999","16219099999","16224099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1199170800,"temp":2.3,"feelslike":-2.0,"humidity":71.06,"dew":-2.4,"prLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2010-01-01","datetimeEpoch":1262300400,"tempmax":13.4,"tempmin":10.1,"temp":11.8,"feelslikemax":13.4,"feelslikemin":10.1,"feelslike":11.8,"dew":8.2,"humidity":78.5,"precip":5.599,"precipprob":100.0,"precipcover":12.5,"preciptype":["rain"],"snow":0.0,"snowdepth":0.0,"windgust":77.2,"windspeed":43.1,"winddir":234.7,"pressure":994.8,"cloudcover":70.8,"visibility":9.7,"solarradiation":47.8,"solarenergy":4.0,"uvindex":2.0,"sunrise":"07:37:53","sunriseEpoch":1262327873,"sunset":"16:49:21","sunsetEpoch":1262360961,"moonphase":0.52,"conditions":"Rain, Partially cloudy","description":"Partly cloudy throughout the day with a chance of rain throughout the day.","icon":"rain","stations":["LIRF","LIRE","16243099999","16245099999","16242099999","16239099999","16244099999","16224099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1262300400,"temp":12.8,"feelslike":12.8,"humidity":89.87,"dew":11.2,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":16.5,"winddir":227.0,"pressure":997.0,"visibility":10.5,"cloudcover":90.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16245099999","16242099999","16239099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1262304000,"temp":12.8,"feelslike":12.8,"humidity":86.27,"dew":10.5,"precip":3.261,"precipprob":100.0,"snow":0.0,"snowdepth":0.0,"preciptype":["rain"],"windgust":null,"windspeed":17.8,"winddir":207.0,"pressure":997.2,"visibility":10.2,"cloudcover":46.7,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Rain, Partially cloudy","icon":"rain","stations":["LIRF","16242099999","16239099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1262307600,"temp":12.8,"feelslike":12.8,"humidity":87.64,"dew":10.8,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":14.2,"winddir":210.0,"pressure":996.1,"visibility":10.7,"cloudcover":51.9,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","LIRE","16245099999","16242099999","16239099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1262311200,"temp":13.4,"feelslike":13.4,"humidity":82.79,"dew":10.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":17.7,"winddir":209.0,"pressure":995.7,"visibility":10.7,"cloudcover":63.2,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","LIRE","16245099999","16242099999","16239099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1262314800,"temp":13.2,"feelslike":13.2,"humidity":83.71,"dew":10.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":15.0,"winddir":196.0,"pressure":995.0,"visibility":10.5,"cloudcover":57.1,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","LIRE","16245099999","16242099999","16239099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1262318400,"temp":13.1,"feelslike":13.1,"humidity":82.28,"dew":10.2,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":19.5,"winddir":195.0,"pressure":994.7,"visibility":10.7,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","LIRE","16245099999","16242099999","16239099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1262322000,"temp":11.2,"feelslike":11.2,"humidity":87.5,"dew":9.2,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":68.7,"windspeed":35.9,"winddir":246.0,"pressure":994.3,"visibility":6.2,"cloudcover":80.6,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","LIRE","16245099999","16242099999","16239099999","16244099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1262325600,"temp":11.0,"feelslike":11.0,"humidity":83.5,"dew":8.3,"precip":1.892,"precipprob":100.0,"snow":0.0,"snowdepth":0.0,"preciptype":["rain"],"windgust":null,"windspeed":16.7,"winddir":200.0,"pressure":994.1,"visibility":10.4,"cloudcover":73.6,"solarradiation":1.3,"solarenergy":0.0,"uvindex":0.0,"conditions":"Rain, Partially cloudy","icon":"rain","stations":["LIRF","16245099999","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1262329200,"temp":11.7,"feelslike":11.7,"humidity":79.77,"dew":8.3,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"wiLine truncated
File diff suppressed because it is too large. Load diff
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2014-01-01","datetimeEpoch":1388530800,"tempmax":11.8,"tempmin":4.6,"temp":7.4,"feelslikemax":11.8,"feelslikemin":2.9,"feelslike":6.7,"dew":5.3,"humidity":87.1,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":["rain"],"snow":0.0,"snowdepth":2.2,"windgust":null,"windspeed":8.6,"winddir":39.9,"pressure":1018.8,"cloudcover":39.4,"visibility":9.6,"solarradiation":95.4,"solarenergy":8.1,"uvindex":4.0,"sunrise":"07:37:52","sunriseEpoch":1388558272,"sunset":"16:49:23","sunsetEpoch":1388591363,"moonphase":0.0,"conditions":"Partially cloudy","description":"Clearing in the afternoon.","icon":"partly-cloudy-day","stations":["LIRF","LIRE","16243099999","16245099999","16242099999","16239099999","16219099999","16224099999"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1388530800,"temp":6.2,"feelslike":5.4,"humidity":96.38,"dew":5.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":2.08,"preciptype":null,"windgust":null,"windspeed":5.3,"winddir":62.0,"pressure":1018.1,"visibility":10.4,"cloudcover":52.2,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16219099999","16224099999"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1388534400,"temp":5.8,"feelslike":5.8,"humidity":96.31,"dew":5.2,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":2.07,"preciptype":null,"windgust":null,"windspeed":3.1,"winddir":47.0,"pressure":1019.0,"visibility":8.4,"cloudcover":48.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16219099999","16224099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1388538000,"temp":5.6,"feelslike":4.2,"humidity":95.52,"dew":5.0,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":2.07,"preciptype":null,"windgust":null,"windspeed":6.8,"winddir":56.0,"pressure":1018.2,"visibility":4.2,"cloudcover":37.2,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16219099999","16224099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1388541600,"temp":5.1,"feelslike":3.9,"humidity":95.83,"dew":4.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":2.06,"preciptype":null,"windgust":null,"windspeed":5.8,"winddir":43.0,"pressure":1018.3,"visibility":4.9,"cloudcover":28.2,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16219099999","16224099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1388545200,"temp":5.0,"feelslike":3.6,"humidity":96.04,"dew":4.4,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":2.06,"preciptype":null,"windgust":null,"windspeed":6.3,"winddir":340.0,"pressure":1018.3,"visibility":4.9,"cloudcover":26.4,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","LIRE","16245099999","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1388548800,"temp":4.6,"feelslike":2.9,"humidity":96.93,"dew":4.1,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":2.05,"preciptype":null,"windgust":null,"windspeed":7.0,"winddir":50.0,"pressure":1018.0,"visibility":4.9,"cloudcover":20.1,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1388552400,"temp":4.9,"feelslike":3.8,"humidity":93.36,"dew":3.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":2.05,"preciptype":null,"windgust":null,"windspeed":5.7,"winddir":60.0,"pressure":1017.5,"visibility":7.3,"cloudcover":18.7,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Clear","icon":"clear-night","stations":["LIRF","16243099999","16242099999","16239099999","16219099999","16224099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1388556000,"temp":5.9,"feelslike":4.7,"humidity":91.42,"dew":4.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":2.04,"preciptype":null,"windgust":null,"windspeed":6.2,"winddir":352.0,"pressure":1018.3,"visibility":8.9,"cloudcover":80.8,"solarradiation":4.2,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16219099999","16224099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1388559600,"temp":5.9,"feelslike":5.1,"humidity":91.05,"dew":4.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdLine truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17544,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2016-01-01","datetimeEpoch":1451602800,"tempmax":10.7,"tempmin":6.9,"temp":8.3,"feelslikemax":10.7,"feelslikemin":5.6,"feelslike":7.7,"dew":4.9,"humidity":79.1,"precip":0.115,"precipprob":100.0,"precipcover":8.33,"preciptype":["rain"],"snow":0.0,"snowdepth":0.0,"windgust":12.6,"windspeed":11.9,"winddir":49.8,"pressure":1024.3,"cloudcover":69.9,"visibility":6.5,"solarradiation":43.4,"solarenergy":3.8,"uvindex":2.0,"sunrise":"07:37:48","sunriseEpoch":1451630268,"sunset":"16:48:58","sunsetEpoch":1451663338,"moonphase":0.72,"conditions":"Rain, Partially cloudy","description":"Partly cloudy throughout the day with rain.","icon":"rain","stations":["LIRF","LIRE","16245099999","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1451602800,"temp":7.9,"feelslike":7.4,"humidity":79.48,"dew":4.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":9.4,"windspeed":4.9,"winddir":20.0,"pressure":1025.9,"visibility":8.5,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1451606400,"temp":7.0,"feelslike":7.0,"humidity":84.74,"dew":4.6,"precip":0.007,"precipprob":100.0,"snow":0.0,"snowdepth":0.0,"preciptype":["rain"],"windgust":null,"windspeed":3.2,"winddir":27.0,"pressure":1025.8,"visibility":8.0,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Rain, Partially cloudy","icon":"rain","stations":["16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1451610000,"temp":7.1,"feelslike":5.6,"humidity":79.71,"dew":3.8,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":9.7,"windspeed":8.0,"winddir":2.0,"pressure":1024.9,"visibility":7.8,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1451613600,"temp":8.3,"feelslike":6.5,"humidity":74.44,"dew":4.0,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":11.2,"windspeed":10.6,"winddir":64.0,"pressure":1025.0,"visibility":8.3,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","LIRE","16245099999","16242099999","16219099999","16224099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1451617200,"temp":8.3,"feelslike":6.3,"humidity":74.48,"dew":4.1,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":10.4,"windspeed":11.9,"winddir":77.0,"pressure":1024.4,"visibility":8.3,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","LIRE","16245099999","16242099999","16219099999","16224099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1451620800,"temp":7.1,"feelslike":7.1,"humidity":79.77,"dew":3.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":9.7,"windspeed":3.1,"winddir":57.0,"pressure":1024.2,"visibility":7.8,"cloudcover":44.8,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1451624400,"temp":7.1,"feelslike":7.1,"humidity":79.84,"dew":3.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":9.7,"windspeed":3.5,"winddir":70.0,"pressure":1024.2,"visibility":7.8,"cloudcover":44.8,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1451628000,"temp":7.1,"feelslike":6.1,"humidity":80.2,"dew":3.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":9.7,"windspeed":6.1,"winddir":34.0,"pressure":1010.9,"visibility":7.9,"cloudcover":46.8,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1451631600,"temp":6.9,"feelslike":6.3,"humidity":81.39,"dew":3.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"Line truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2018-01-01","datetimeEpoch":1514761200,"tempmax":13.6,"tempmin":8.0,"temp":11.8,"feelslikemax":13.6,"feelslikemin":6.8,"feelslike":11.7,"dew":8.8,"humidity":81.9,"precip":0.0,"precipprob":0.0,"precipcover":0.0,"preciptype":null,"snow":0.0,"snowdepth":0.0,"windgust":44.6,"windspeed":24.7,"winddir":173.7,"pressure":1014.7,"cloudcover":61.3,"visibility":9.8,"solarradiation":26.8,"solarenergy":2.3,"uvindex":2.0,"sunrise":"07:37:51","sunriseEpoch":1514788671,"sunset":"16:49:23","sunsetEpoch":1514821763,"moonphase":0.48,"conditions":"Partially cloudy","description":"Partly cloudy throughout the day.","icon":"partly-cloudy-day","stations":["LIRF","E7225","E2310","16245099999","16242099999","16239099999","AT416","16224099999","LIRA"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1514761200,"temp":12.0,"feelslike":12.0,"humidity":85.66,"dew":9.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":19.4,"windspeed":15.8,"winddir":145.0,"pressure":1020.2,"visibility":10.0,"cloudcover":39.2,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1514764800,"temp":12.7,"feelslike":12.7,"humidity":82.82,"dew":9.8,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":12.6,"winddir":146.0,"pressure":1020.0,"visibility":8.7,"cloudcover":84.8,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1514768400,"temp":11.9,"feelslike":11.9,"humidity":85.18,"dew":9.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":15.3,"winddir":141.0,"pressure":1019.0,"visibility":10.0,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1514772000,"temp":12.8,"feelslike":12.8,"humidity":76.05,"dew":8.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":16.8,"winddir":135.0,"pressure":1018.2,"visibility":10.0,"cloudcover":90.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1514775600,"temp":12.4,"feelslike":12.4,"humidity":77.52,"dew":8.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":18.2,"winddir":133.0,"pressure":1017.2,"visibility":10.0,"cloudcover":33.6,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1514779200,"temp":12.7,"feelslike":12.7,"humidity":80.61,"dew":9.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":23.9,"winddir":142.0,"pressure":1016.2,"visibility":10.0,"cloudcover":90.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1514782800,"temp":12.8,"feelslike":12.8,"humidity":80.19,"dew":9.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":null,"windspeed":24.2,"winddir":146.0,"pressure":1015.2,"visibility":10.0,"cloudcover":29.5,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1514786400,"temp":13.0,"feelslike":13.0,"humidity":80.22,"dew":9.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":44.6,"windspeed":24.7,"winddir":146.0,"pressure":1015.2,"visibility":10.0,"cloudcover":37.1,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["LIRF","16245099999","16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1514790000,"temp":13.0,"feelslike":13.0,"humidity":80.22,"dew":9.7,"precip":0.0,"pLine truncated
File diff suppressed because it is too large. Load diff
-1
View File
@@ -1 +0,0 @@
{"queryCost":17520,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2022-01-01","datetimeEpoch":1640991600,"tempmax":13.2,"tempmin":7.8,"temp":9.9,"feelslikemax":13.2,"feelslikemin":6.3,"feelslike":9.5,"dew":8.5,"humidity":91.1,"precip":0.126,"precipprob":100.0,"precipcover":4.17,"preciptype":["rain"],"snow":0.0,"snowdepth":21.8,"windgust":14.0,"windspeed":8.7,"winddir":48.3,"pressure":1026.7,"cloudcover":15.2,"visibility":4.2,"solarradiation":80.8,"solarenergy":7.1,"uvindex":4.0,"sunrise":"07:37:54","sunriseEpoch":1641019074,"sunset":"16:49:26","sunsetEpoch":1641052166,"moonphase":0.95,"conditions":"Rain","description":"Clear conditions throughout the day with morning rain.","icon":"rain","stations":["16217099999","E2310","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1640991600,"temp":10.9,"feelslike":10.9,"humidity":97.84,"dew":10.6,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":23.33,"preciptype":null,"windgust":7.9,"windspeed":5.0,"winddir":64.0,"pressure":1026.0,"visibility":1.1,"cloudcover":97.8,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Overcast","icon":"cloudy","stations":["16217099999","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1640995200,"temp":10.9,"feelslike":10.9,"humidity":96.2,"dew":10.3,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":23.17,"preciptype":null,"windgust":null,"windspeed":4.0,"winddir":327.0,"pressure":1026.1,"visibility":0.0,"cloudcover":30.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1640998800,"temp":10.7,"feelslike":10.7,"humidity":98.48,"dew":10.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":23.0,"preciptype":null,"windgust":8.3,"windspeed":4.1,"winddir":240.0,"pressure":1026.5,"visibility":2.2,"cloudcover":21.3,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1641002400,"temp":10.6,"feelslike":10.6,"humidity":98.93,"dew":10.5,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":22.83,"preciptype":null,"windgust":8.3,"windspeed":4.9,"winddir":10.0,"pressure":1026.4,"visibility":2.2,"cloudcover":11.8,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Clear","icon":"clear-night","stations":["16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1641006000,"temp":9.0,"feelslike":8.0,"humidity":99.6,"dew":8.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":22.67,"preciptype":null,"windgust":8.3,"windspeed":7.1,"winddir":40.0,"pressure":1025.2,"visibility":1.1,"cloudcover":57.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16242099999","16239099999","16224099999","LIRA"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1641009600,"temp":9.1,"feelslike":8.8,"humidity":97.52,"dew":8.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":22.5,"preciptype":null,"windgust":8.6,"windspeed":4.9,"winddir":360.0,"pressure":1025.6,"visibility":0.0,"cloudcover":0.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Clear","icon":"clear-night","stations":["16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1641013200,"temp":9.1,"feelslike":9.1,"humidity":97.79,"dew":8.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":22.33,"preciptype":null,"windgust":8.6,"windspeed":3.7,"winddir":354.0,"pressure":1025.6,"visibility":0.3,"cloudcover":0.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Clear","icon":"fog","stations":["16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1641016800,"temp":8.4,"feelslike":7.8,"humidity":99.36,"dew":8.3,"precip":0.126,"precipprob":100.0,"snow":0.0,"snowdepth":22.17,"preciptype":["rain"],"windgust":9.0,"windspeed":5.4,"winddir":40.0,"pressure":1025.6,"visibility":1.4,"cloudcover":0.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Rain","icon":"rain","stations":["16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1641020400,"temp":8.3,"feelslike":7.7,"humidity":99.27,"dew":8.2,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":22.0,"preciptype":null,"windgust":11.2,"windspeed":5.7,"winddir":47.0,"pressure":1026.5,"visibility":0.3,"cloudcover":23.7,"solarradiation":9.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"fog","stations":["E2310","16242099999","16Line truncated
-1
View File
@@ -1 +0,0 @@
{"queryCost":7079,"latitude":41.9032,"longitude":12.4957,"resolvedAddress":"Italia","address":"Italy","timezone":"Europe/Rome","tzoffset":1.0,"days":[{"datetime":"2024-01-01","datetimeEpoch":1704063600,"tempmax":14.1,"tempmin":10.9,"temp":12.9,"feelslikemax":14.1,"feelslikemin":10.9,"feelslike":12.9,"dew":10.0,"humidity":83.0,"precip":0.676,"precipprob":100.0,"precipcover":4.17,"preciptype":["rain"],"snow":0.0,"snowdepth":0.0,"windgust":33.5,"windspeed":16.1,"winddir":185.1,"pressure":1014.8,"cloudcover":72.3,"visibility":9.8,"solarradiation":54.7,"solarenergy":4.7,"uvindex":2.0,"sunrise":"07:37:52","sunriseEpoch":1704091072,"sunset":"16:49:02","sunsetEpoch":1704124142,"moonphase":0.68,"conditions":"Rain, Partially cloudy","description":"Partly cloudy throughout the day with morning rain.","icon":"rain","stations":["16217099999","E2310","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs","hours":[{"datetime":"00:00:00","datetimeEpoch":1704063600,"temp":13.6,"feelslike":13.6,"humidity":84.46,"dew":11.0,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":27.7,"windspeed":16.1,"winddir":159.0,"pressure":1015.9,"visibility":10.0,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["16217099999","E2310","16242099999","16239099999","16219099999","16224099999","LIRA"],"source":"obs"},{"datetime":"01:00:00","datetimeEpoch":1704067200,"temp":13.4,"feelslike":13.4,"humidity":90.64,"dew":11.9,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":33.5,"windspeed":12.5,"winddir":163.0,"pressure":1015.4,"visibility":7.3,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["E2310","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"02:00:00","datetimeEpoch":1704070800,"temp":13.5,"feelslike":13.5,"humidity":95.73,"dew":12.8,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":31.3,"windspeed":13.7,"winddir":182.0,"pressure":1014.8,"visibility":7.3,"cloudcover":79.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["E2310","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"03:00:00","datetimeEpoch":1704074400,"temp":13.7,"feelslike":13.7,"humidity":91.06,"dew":12.3,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":29.5,"windspeed":13.6,"winddir":201.0,"pressure":1014.9,"visibility":10.0,"cloudcover":72.5,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["E2310","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"04:00:00","datetimeEpoch":1704078000,"temp":12.8,"feelslike":12.8,"humidity":90.75,"dew":11.3,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":21.2,"windspeed":9.2,"winddir":166.0,"pressure":1014.8,"visibility":10.0,"cloudcover":88.8,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["E2310","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"05:00:00","datetimeEpoch":1704081600,"temp":12.0,"feelslike":12.0,"humidity":91.48,"dew":10.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":21.2,"windspeed":9.4,"winddir":147.0,"pressure":1014.1,"visibility":10.0,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["E2310","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"06:00:00","datetimeEpoch":1704085200,"temp":12.5,"feelslike":12.5,"humidity":90.57,"dew":11.0,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":20.9,"windspeed":9.6,"winddir":198.0,"pressure":1014.1,"visibility":10.0,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Partially cloudy","icon":"partly-cloudy-night","stations":["E2310","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"07:00:00","datetimeEpoch":1704088800,"temp":13.3,"feelslike":13.3,"humidity":85.2,"dew":10.9,"precip":0.676,"precipprob":100.0,"snow":0.0,"snowdepth":0.0,"preciptype":["rain"],"windgust":13.3,"windspeed":12.7,"winddir":202.0,"pressure":1014.1,"visibility":10.0,"cloudcover":50.0,"solarradiation":0.0,"solarenergy":0.0,"uvindex":0.0,"conditions":"Rain, Partially cloudy","icon":"rain","stations":["E2310","16242099999","16239099999","16224099999"],"source":"obs"},{"datetime":"08:00:00","datetimeEpoch":1704092400,"temp":13.0,"feelslike":13.0,"humidity":85.48,"dew":10.7,"precip":0.0,"precipprob":0.0,"snow":0.0,"snowdepth":0.0,"preciptype":null,"windgust":17.6,"windspeed":Line truncated
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,22 +0,0 @@
Unnamed: 0,Varietà di Olive,Produzione (tonnellate/ettaro),Tecnica di Coltivazione,Produzione Olio (tonnellate/ettaro),Produzione Olio (litri/ettaro),Min % Resa,Max % Resa,Min Produzione Olio (litri/ettaro),Max Produzione Olio (litri/ettaro),Media Produzione Olio (litri/ettaro),Litri per Tonnellata,Min Litri per Tonnellata,Max Litri per Tonnellata,Media Litri per Tonnellata
0,Nocellara dell'Etna,3.5,Tradizionale,0.7000000000000001,687.7729257641921,0.18,0.22,687.7729257641921,840.6113537117903,764.1921397379913,196.5065502183406,196.5065502183406,240.17467248908295,218.34061135371178
1,Nocellara dell'Etna,5.0,Intensiva,1.0,1091.703056768559,0.18,0.22,982.532751091703,1200.8733624454148,1091.7030567685588,218.3406113537118,196.50655021834058,240.17467248908298,218.34061135371178
2,Nocellara dell'Etna,6.5,Superintensiva,1.3,1561.135371179039,0.18,0.22,1277.292576419214,1561.135371179039,1419.2139737991265,240.17467248908295,196.5065502183406,240.17467248908295,218.34061135371178
6,Leccino,4.0,Tradizionale,0.8,698.6899563318777,0.16,0.2,698.6899563318777,873.3624454148471,786.0262008733624,174.67248908296943,174.67248908296943,218.34061135371178,196.5065502183406
7,Leccino,6.0,Intensiva,1.2000000000000002,1179.0393013100436,0.16,0.2,1048.0349344978165,1310.043668122271,1179.0393013100438,196.5065502183406,174.6724890829694,218.3406113537118,196.5065502183406
8,Leccino,7.5,Superintensiva,1.5,1637.5545851528384,0.16,0.2,1310.043668122271,1637.5545851528384,1473.7991266375543,218.34061135371178,174.67248908296946,218.34061135371178,196.50655021834064
9,Frantoio,5.0,Tradizionale,1.0,1091.703056768559,0.2,0.25,1091.703056768559,1364.6288209606987,1228.165938864629,218.3406113537118,218.3406113537118,272.92576419213975,245.6331877729258
10,Frantoio,7.5,Intensiva,1.5,1883.187772925764,0.2,0.25,1637.5545851528384,2046.943231441048,1842.2489082969432,251.09170305676852,218.34061135371178,272.92576419213975,245.63318777292577
11,Frantoio,9.0,Superintensiva,1.8,2456.331877729257,0.2,0.25,1965.065502183406,2456.331877729257,2210.698689956332,272.9257641921397,218.34061135371178,272.9257641921397,245.63318777292574
15,Coratina,4.5,Tradizionale,0.9,1080.7860262008733,0.22,0.25,1080.7860262008733,1228.1659388646287,1154.475982532751,240.17467248908295,240.17467248908295,272.9257641921397,256.5502183406113
16,Coratina,6.0,Intensiva,1.2000000000000002,1572.0524017467249,0.22,0.25,1441.0480349344978,1637.5545851528384,1539.301310043668,262.0087336244541,240.17467248908295,272.92576419213975,256.55021834061137
17,Coratina,7.0,Superintensiva,1.4,1910.480349344978,0.22,0.25,1681.2227074235807,1910.480349344978,1795.8515283842794,272.92576419213975,240.17467248908295,272.92576419213975,256.55021834061137
21,Taggiasca,2.5,Tradizionale,0.5,463.9737991266376,0.17,0.2,463.9737991266376,545.8515283842795,504.91266375545854,185.58951965065503,185.58951965065503,218.3406113537118,201.96506550218342
22,Taggiasca,4.0,Intensiva,0.8,829.6943231441047,0.17,0.2,742.35807860262,873.3624454148471,807.8602620087336,207.42358078602618,185.589519650655,218.34061135371178,201.9650655021834
23,Taggiasca,6.0,Superintensiva,1.2000000000000002,1310.043668122271,0.17,0.2,1113.53711790393,1310.043668122271,1211.7903930131006,218.3406113537118,185.589519650655,218.3406113537118,201.9650655021834
24,Pendolino,4.0,Tradizionale,0.8,655.0218340611353,0.15,0.18,655.0218340611353,786.0262008733624,720.5240174672489,163.75545851528383,163.75545851528383,196.5065502183406,180.13100436681222
25,Pendolino,5.0,Intensiva,1.0,927.9475982532751,0.15,0.18,818.7772925764192,982.532751091703,900.655021834061,185.58951965065503,163.75545851528383,196.50655021834058,180.1310043668122
26,Pendolino,7.0,Superintensiva,1.4,1375.5458515283842,0.15,0.18,1146.2882096069868,1375.5458515283842,1260.9170305676855,196.5065502183406,163.75545851528383,196.5065502183406,180.13100436681222
27,Moraiolo,3.2,Tradizionale,0.6400000000000001,628.82096069869,0.18,0.22,628.82096069869,768.5589519650656,698.6899563318777,196.5065502183406,196.5065502183406,240.17467248908298,218.34061135371178
28,Moraiolo,4.8,Intensiva,0.96,1048.0349344978165,0.18,0.22,943.2314410480348,1152.838427947598,1048.0349344978165,218.34061135371178,196.50655021834058,240.1746724890829,218.34061135371172
29,Moraiolo,6.5,Superintensiva,1.3,1561.135371179039,0.18,0.22,1277.292576419214,1561.135371179039,1419.2139737991265,240.17467248908295,196.5065502183406,240.17467248908295,218.34061135371178
1 Unnamed: 0 Varietà di Olive Produzione (tonnellate/ettaro) Tecnica di Coltivazione Produzione Olio (tonnellate/ettaro) Produzione Olio (litri/ettaro) Min % Resa Max % Resa Min Produzione Olio (litri/ettaro) Max Produzione Olio (litri/ettaro) Media Produzione Olio (litri/ettaro) Litri per Tonnellata Min Litri per Tonnellata Max Litri per Tonnellata Media Litri per Tonnellata
2 0 Nocellara dell'Etna 3.5 Tradizionale 0.7000000000000001 687.7729257641921 0.18 0.22 687.7729257641921 840.6113537117903 764.1921397379913 196.5065502183406 196.5065502183406 240.17467248908295 218.34061135371178
3 1 Nocellara dell'Etna 5.0 Intensiva 1.0 1091.703056768559 0.18 0.22 982.532751091703 1200.8733624454148 1091.7030567685588 218.3406113537118 196.50655021834058 240.17467248908298 218.34061135371178
4 2 Nocellara dell'Etna 6.5 Superintensiva 1.3 1561.135371179039 0.18 0.22 1277.292576419214 1561.135371179039 1419.2139737991265 240.17467248908295 196.5065502183406 240.17467248908295 218.34061135371178
5 6 Leccino 4.0 Tradizionale 0.8 698.6899563318777 0.16 0.2 698.6899563318777 873.3624454148471 786.0262008733624 174.67248908296943 174.67248908296943 218.34061135371178 196.5065502183406
6 7 Leccino 6.0 Intensiva 1.2000000000000002 1179.0393013100436 0.16 0.2 1048.0349344978165 1310.043668122271 1179.0393013100438 196.5065502183406 174.6724890829694 218.3406113537118 196.5065502183406
7 8 Leccino 7.5 Superintensiva 1.5 1637.5545851528384 0.16 0.2 1310.043668122271 1637.5545851528384 1473.7991266375543 218.34061135371178 174.67248908296946 218.34061135371178 196.50655021834064
8 9 Frantoio 5.0 Tradizionale 1.0 1091.703056768559 0.2 0.25 1091.703056768559 1364.6288209606987 1228.165938864629 218.3406113537118 218.3406113537118 272.92576419213975 245.6331877729258
9 10 Frantoio 7.5 Intensiva 1.5 1883.187772925764 0.2 0.25 1637.5545851528384 2046.943231441048 1842.2489082969432 251.09170305676852 218.34061135371178 272.92576419213975 245.63318777292577
10 11 Frantoio 9.0 Superintensiva 1.8 2456.331877729257 0.2 0.25 1965.065502183406 2456.331877729257 2210.698689956332 272.9257641921397 218.34061135371178 272.9257641921397 245.63318777292574
11 15 Coratina 4.5 Tradizionale 0.9 1080.7860262008733 0.22 0.25 1080.7860262008733 1228.1659388646287 1154.475982532751 240.17467248908295 240.17467248908295 272.9257641921397 256.5502183406113
12 16 Coratina 6.0 Intensiva 1.2000000000000002 1572.0524017467249 0.22 0.25 1441.0480349344978 1637.5545851528384 1539.301310043668 262.0087336244541 240.17467248908295 272.92576419213975 256.55021834061137
13 17 Coratina 7.0 Superintensiva 1.4 1910.480349344978 0.22 0.25 1681.2227074235807 1910.480349344978 1795.8515283842794 272.92576419213975 240.17467248908295 272.92576419213975 256.55021834061137
14 21 Taggiasca 2.5 Tradizionale 0.5 463.9737991266376 0.17 0.2 463.9737991266376 545.8515283842795 504.91266375545854 185.58951965065503 185.58951965065503 218.3406113537118 201.96506550218342
15 22 Taggiasca 4.0 Intensiva 0.8 829.6943231441047 0.17 0.2 742.35807860262 873.3624454148471 807.8602620087336 207.42358078602618 185.589519650655 218.34061135371178 201.9650655021834
16 23 Taggiasca 6.0 Superintensiva 1.2000000000000002 1310.043668122271 0.17 0.2 1113.53711790393 1310.043668122271 1211.7903930131006 218.3406113537118 185.589519650655 218.3406113537118 201.9650655021834
17 24 Pendolino 4.0 Tradizionale 0.8 655.0218340611353 0.15 0.18 655.0218340611353 786.0262008733624 720.5240174672489 163.75545851528383 163.75545851528383 196.5065502183406 180.13100436681222
18 25 Pendolino 5.0 Intensiva 1.0 927.9475982532751 0.15 0.18 818.7772925764192 982.532751091703 900.655021834061 185.58951965065503 163.75545851528383 196.50655021834058 180.1310043668122
19 26 Pendolino 7.0 Superintensiva 1.4 1375.5458515283842 0.15 0.18 1146.2882096069868 1375.5458515283842 1260.9170305676855 196.5065502183406 163.75545851528383 196.5065502183406 180.13100436681222
20 27 Moraiolo 3.2 Tradizionale 0.6400000000000001 628.82096069869 0.18 0.22 628.82096069869 768.5589519650656 698.6899563318777 196.5065502183406 196.5065502183406 240.17467248908298 218.34061135371178
21 28 Moraiolo 4.8 Intensiva 0.96 1048.0349344978165 0.18 0.22 943.2314410480348 1152.838427947598 1048.0349344978165 218.34061135371178 196.50655021834058 240.1746724890829 218.34061135371172
22 29 Moraiolo 6.5 Superintensiva 1.3 1561.135371179039 0.18 0.22 1277.292576419214 1561.135371179039 1419.2139737991265 240.17467248908295 196.5065502183406 240.17467248908295 218.34061135371178
Binary file not shown.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,669 +0,0 @@
{
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.14",
"mimetype": "text/x-python",
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"pygments_lexer": "ipython3",
"nbconvert_exporter": "python",
"file_extension": ".py"
},
"kaggle": {
"accelerator": "gpu",
"dataSources": [
{
"sourceId": 9725208,
"sourceType": "datasetVersion",
"datasetId": 5950719
},
{
"sourceId": 9730815,
"sourceType": "datasetVersion",
"datasetId": 5954901
}
],
"dockerImageVersionId": 30787,
"isInternetEnabled": true,
"language": "python",
"sourceType": "notebook",
"isGpuEnabled": true
}
},
"nbformat_minor": 4,
"nbformat": 4,
"cells": [
{
"cell_type": "markdown",
"source": "# Analisi e Previsione della Produzione di Olio d'Oliva\n\nQuesto notebook esplora la relazione tra i dati meteorologici e la produzione annuale di olio d'oliva, con l'obiettivo di creare un modello predittivo.",
"metadata": {}
},
{
"cell_type": "code",
"source": "import os\n\n# Rimuove il file se esiste\nif os.path.exists('output.zip'):\n os.remove('output.zip')\n \n!zip -r output.zip /kaggle/working/",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T22:02:37.712657Z",
"iopub.execute_input": "2024-10-28T22:02:37.713572Z",
"iopub.status.idle": "2024-10-28T22:05:13.962589Z",
"shell.execute_reply.started": "2024-10-28T22:02:37.713526Z",
"shell.execute_reply": "2024-10-28T22:05:13.961548Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "import tensorflow as tf\n\nprint(f\"Keras version: {tf.keras.__version__}\")\nprint(f\"TensorFlow version: {tf.__version__}\")\n\n# GPU configuration\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n try:\n for gpu in gpus:\n tf.config.experimental.set_memory_growth(gpu, True)\n logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n print(len(gpus), \"Physical GPUs,\", len(logical_gpus), \"Logical GPUs\")\n except RuntimeError as e:\n print(e)",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T18:17:11.580753Z",
"iopub.execute_input": "2024-10-28T18:17:11.581122Z",
"iopub.status.idle": "2024-10-28T18:17:11.883020Z",
"shell.execute_reply.started": "2024-10-28T18:17:11.581083Z",
"shell.execute_reply": "2024-10-28T18:17:11.881838Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "# Test semplice per verificare che la GPU funzioni\ndef test_gpu():\n print(\"TensorFlow version:\", tf.__version__)\n print(\"\\nDispositivi disponibili:\")\n print(tf.config.list_physical_devices())\n\n # Creiamo e moltiplichiamo due tensori sulla GPU\n with tf.device('/GPU:0'):\n a = tf.random.normal([10000, 10000])\n b = tf.random.normal([10000, 10000])\n c = tf.matmul(a, b)\n\n print(\"\\nShape del risultato:\", c.shape)\n print(\"Device del tensore:\", c.device)\n return \"Test completato con successo!\"\n\n\ntest_gpu()",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T18:17:14.607427Z",
"iopub.execute_input": "2024-10-28T18:17:14.608081Z",
"iopub.status.idle": "2024-10-28T18:17:14.758117Z",
"shell.execute_reply.started": "2024-10-28T18:17:14.608043Z",
"shell.execute_reply": "2024-10-28T18:17:14.757247Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "!pip install numpy\n!pip install pandas\n\n!pip install keras\n!pip install scikit-learn\n!pip install matplotlib\n!pip install joblib\n!pip install pyarrow\n!pip install fastparquet\n!pip install scipy\n!pip install seaborn\n!pip install tqdm",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T06:41:20.930792Z",
"iopub.execute_input": "2024-10-28T06:41:20.931152Z",
"iopub.status.idle": "2024-10-28T06:43:20.309151Z",
"shell.execute_reply.started": "2024-10-28T06:41:20.931111Z",
"shell.execute_reply": "2024-10-28T06:43:20.308064Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import MinMaxScaler, StandardScaler\nfrom tensorflow.keras.layers import Input, Dense, Dropout, Bidirectional, LSTM, LayerNormalization, Add, Activation, BatchNormalization, MultiHeadAttention, MaxPooling1D\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.regularizers import l2\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nfrom sklearn.metrics import mean_squared_error, mean_absolute_error\nfrom datetime import datetime\nimport os\nimport json\nimport joblib\nimport re\nimport pyarrow as pa\nimport pyarrow.parquet as pq\nfrom tqdm import tqdm\n\nrandom_state_value = 42",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T18:17:19.003246Z",
"iopub.execute_input": "2024-10-28T18:17:19.004112Z",
"iopub.status.idle": "2024-10-28T18:17:19.020415Z",
"shell.execute_reply.started": "2024-10-28T18:17:19.004072Z",
"shell.execute_reply": "2024-10-28T18:17:19.019550Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## Funzioni di Plot",
"metadata": {}
},
{
"cell_type": "code",
"source": "def save_plot(plt, title, output_dir='/kaggle/working/plots'):\n os.makedirs(output_dir, exist_ok=True)\n filename = \"\".join(x for x in title if x.isalnum() or x in [' ', '-', '_']).rstrip()\n filename = filename.replace(' ', '_').lower()\n filepath = os.path.join(output_dir, f\"{filename}.png\")\n plt.savefig(filepath, bbox_inches='tight', dpi=300)\n print(f\"Plot salvato come: {filepath}\")",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T18:17:22.785465Z",
"iopub.execute_input": "2024-10-28T18:17:22.785841Z",
"iopub.status.idle": "2024-10-28T18:17:22.792190Z",
"shell.execute_reply.started": "2024-10-28T18:17:22.785803Z",
"shell.execute_reply": "2024-10-28T18:17:22.791093Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## 1. Caricamento e preparazione dei Dati Meteo",
"metadata": {}
},
{
"cell_type": "code",
"source": "# Function to convert csv to parquet\ndef csv_to_parquet(csv_file, parquet_file, chunksize=100000):\n writer = None\n\n for chunk in pd.read_csv(csv_file, chunksize=chunksize):\n if writer is None:\n\n table = pa.Table.from_pandas(chunk)\n writer = pq.ParquetWriter(parquet_file, table.schema)\n else:\n table = pa.Table.from_pandas(chunk)\n\n writer.write_table(table)\n\n if writer:\n writer.close()\n\n print(f\"File conversion completed : {csv_file} -> {parquet_file}\")\n\n\ndef read_json_files(folder_path):\n all_data = []\n\n file_list = sorted(os.listdir(folder_path))\n\n for filename in file_list:\n if filename.endswith('.json'):\n file_path = os.path.join(folder_path, filename)\n try:\n with open(file_path, 'r') as file:\n data = json.load(file)\n all_data.extend(data['days'])\n except Exception as e:\n print(f\"Error processing file '{filename}': {str(e)}\")\n\n return all_data\n\n\ndef create_weather_dataset(data):\n dataset = []\n seen_datetimes = set()\n\n for day in data:\n date = day['datetime']\n for hour in day['hours']:\n datetime_str = f\"{date} {hour['datetime']}\"\n\n # Verifico se questo datetime è già stato visto\n if datetime_str in seen_datetimes:\n continue\n\n seen_datetimes.add(datetime_str)\n\n if isinstance(hour['preciptype'], list):\n preciptype = \"__\".join(hour['preciptype'])\n else:\n preciptype = hour['preciptype'] if hour['preciptype'] else \"\"\n\n conditions = hour['conditions'].replace(', ', '__').replace(' ', '_').lower()\n\n row = {\n 'datetime': datetime_str,\n 'temp': hour['temp'],\n 'feelslike': hour['feelslike'],\n 'humidity': hour['humidity'],\n 'dew': hour['dew'],\n 'precip': hour['precip'],\n 'snow': hour['snow'],\n 'preciptype': preciptype.lower(),\n 'windspeed': hour['windspeed'],\n 'winddir': hour['winddir'],\n 'pressure': hour['pressure'],\n 'cloudcover': hour['cloudcover'],\n 'visibility': hour['visibility'],\n 'solarradiation': hour['solarradiation'],\n 'solarenergy': hour['solarenergy'],\n 'uvindex': hour['uvindex'],\n 'conditions': conditions,\n 'tempmax': day['tempmax'],\n 'tempmin': day['tempmin'],\n 'precipprob': day['precipprob'],\n 'precipcover': day['precipcover']\n }\n dataset.append(row)\n\n dataset.sort(key=lambda x: datetime.strptime(x['datetime'], \"%Y-%m-%d %H:%M:%S\"))\n\n return pd.DataFrame(dataset)\n\n\nfolder_path = './data/weather'\n#raw_data = read_json_files(folder_path)\n#weather_data = create_weather_dataset(raw_data)\n#weather_data['datetime'] = pd.to_datetime(weather_data['datetime'], errors='coerce')\n#weather_data['date'] = weather_data['datetime'].dt.date\n#weather_data = weather_data.dropna(subset=['datetime'])\n#weather_data['datetime'] = pd.to_datetime(weather_data['datetime'])\n#weather_data['year'] = weather_data['datetime'].dt.year\n#weather_data['month'] = weather_data['datetime'].dt.month\n#weather_data['day'] = weather_data['datetime'].dt.day\n#weather_data.head()\n\n#weather_data.to_parquet('./data/weather_data.parquet')",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T06:43:20.340958Z",
"iopub.execute_input": "2024-10-28T06:43:20.341378Z",
"iopub.status.idle": "2024-10-28T06:43:20.358973Z",
"shell.execute_reply.started": "2024-10-28T06:43:20.341335Z",
"shell.execute_reply": "2024-10-28T06:43:20.358037Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "",
"metadata": {}
},
{
"cell_type": "code",
"source": "# Crea le sequenze per LSTM\ndef create_sequences(timesteps, X, y=None):\n \"\"\"\n Crea sequenze temporali dai dati.\n \n Parameters:\n -----------\n X : array-like\n Dati di input\n timesteps : int\n Numero di timestep per ogni sequenza\n y : array-like, optional\n Target values. Se None, crea sequenze solo per X\n \n Returns:\n --------\n tuple o array\n Se y è fornito: (X_sequences, y_sequences)\n Se y è None: X_sequences\n \"\"\"\n Xs = []\n for i in range(len(X) - timesteps):\n Xs.append(X[i:i + timesteps])\n\n if y is not None:\n ys = []\n for i in range(len(X) - timesteps):\n ys.append(y[i + timesteps])\n return np.array(Xs), np.array(ys)\n\n return np.array(Xs)\n\n\n# Funzioni per costruire il modello LSTM avanzato\ndef create_residual_lstm_layer(x, units, dropout_rate, l2_reg=0.01, return_sequences=True):\n residual = x\n x = Bidirectional(LSTM(units, return_sequences=return_sequences, kernel_regularizer=l2(l2_reg)))(x)\n x = LayerNormalization()(x)\n x = Dropout(dropout_rate)(x)\n # Adjust residual dimension and handle return_sequences\n if return_sequences:\n if int(residual.shape[-1]) != 2 * units:\n residual = Dense(2 * units, activation='linear')(residual)\n x = Add()([x, residual])\n return x\n\n\ndef attention_block(x, units, num_heads=8):\n attention = MultiHeadAttention(num_heads=num_heads, key_dim=units)(x, x)\n x = Add()([x, attention])\n x = LayerNormalization()(x)\n return x\n\n\ndef build_advanced_model(input_shape, l2_lambda=0.005):\n inputs = Input(shape=input_shape)\n\n # Primi due layer LSTM con sequenze\n x = create_residual_lstm_layer(inputs, 64, 0.2, l2_lambda, return_sequences=True)\n x = create_residual_lstm_layer(x, 32, 0.2, l2_lambda, return_sequences=True)\n\n # Attention e MaxPooling mentre abbiamo ancora la sequenza\n x = attention_block(x, 32, num_heads=8)\n x = MaxPooling1D()(x)\n\n # Ultimo layer LSTM senza sequenze\n x = create_residual_lstm_layer(x, 16, 0.1, l2_lambda, return_sequences=False)\n\n # Dense layers\n x = Dense(32, kernel_regularizer=l2(l2_lambda))(x)\n x = BatchNormalization()(x)\n x = Activation('swish')(x)\n x = Dropout(0.1)(x)\n\n x = Dense(16, kernel_regularizer=l2(l2_lambda))(x)\n x = BatchNormalization()(x)\n x = Activation('swish')(x)\n x = Dropout(0.1)(x)\n\n outputs = Dense(1, kernel_regularizer=l2(l2_lambda))(x)\n\n model = Model(inputs=inputs, outputs=outputs)\n return model\n\n\ndef get_season(date):\n month = date.month\n day = date.day\n if (month == 12 and day >= 21) or (month <= 3 and day < 20):\n return 'Winter'\n elif (month == 3 and day >= 20) or (month <= 6 and day < 21):\n return 'Spring'\n elif (month == 6 and day >= 21) or (month <= 9 and day < 23):\n return 'Summer'\n elif (month == 9 and day >= 23) or (month <= 12 and day < 21):\n return 'Autumn'\n else:\n return 'Unknown'\n\n\ndef get_time_period(hour):\n if 5 <= hour < 12:\n return 'Morning'\n elif 12 <= hour < 17:\n return 'Afternoon'\n elif 17 <= hour < 21:\n return 'Evening'\n else:\n return 'Night'\n\n\ndef add_time_features(df):\n df['datetime'] = pd.to_datetime(df['datetime'])\n df['timestamp'] = df['datetime'].astype(np.int64) // 10 ** 9\n df['year'] = df['datetime'].dt.year\n df['month'] = df['datetime'].dt.month\n df['day'] = df['datetime'].dt.day\n df['hour'] = df['datetime'].dt.hour\n df['minute'] = df['datetime'].dt.minute\n df['hour_sin'] = np.sin(df['hour'] * (2 * np.pi / 24))\n df['hour_cos'] = np.cos(df['hour'] * (2 * np.pi / 24))\n df['day_of_week'] = df['datetime'].dt.dayofweek\n df['day_of_year'] = df['datetime'].dt.dayofyear\n df['week_of_year'] = df['datetime'].dt.isocalendar().week.astype(int)\n df['quarter'] = df['datetime'].dt.quarter\n df['is_month_end'] = df['datetime'].dt.is_month_end.astype(int)\n df['is_quarter_end'] = df['datetime'].dt.is_quarter_end.astype(int)\n df['is_year_end'] = df['datetime'].dt.is_year_end.astype(int)\n df['month_sin'] = np.sin(df['month'] * (2 * np.pi / 12))\n df['month_cos'] = np.cos(df['month'] * (2 * np.pi / 12))\n df['day_of_year_sin'] = np.sin(df['day_of_year'] * (2 * np.pi / 365.25))\n df['day_of_year_cos'] = np.cos(df['day_of_year'] * (2 * np.pi / 365.25))\n df['season'] = df['datetime'].apply(get_season)\n df['time_period'] = df['hour'].apply(get_time_period)\n return df\n\n\n# Carica il dataset\nweather_data = pd.read_parquet('/kaggle/input/olive-oil/weather_data.parquet')\n\n# Aggiungi le caratteristiche temporali\nweather_data = add_time_features(weather_data)\n\n# Encoding delle variabili categoriali\nweather_data = pd.get_dummies(weather_data, columns=['season', 'time_period'], drop_fiLine truncated
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T06:43:20.360560Z",
"iopub.execute_input": "2024-10-28T06:43:20.361353Z",
"iopub.status.idle": "2024-10-28T06:43:23.932182Z",
"shell.execute_reply.started": "2024-10-28T06:43:20.361319Z",
"shell.execute_reply": "2024-10-28T06:43:23.931121Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "# numero di timesteps (utilizziamo le ultime 24 ore)\ntimesteps = 24\n\n# Costruisci il modello per ogni variabile target\nmodels = {}\nhistories = {}\nfor i, target in enumerate(target_variables):\n target_data = datasets[target]\n target_scaler = scalers[target]\n\n X_train = target_data['X_train']\n y_train = target_data['y_train']\n X_val = target_data['X_val']\n y_val = target_data['y_val']\n X_test = target_data['X_test']\n y_test = target_data['y_test']\n\n num_features = X_train.shape[1]\n\n X_train_seq, y_train_seq = create_sequences(timesteps, X_train, y_train)\n X_val_seq, y_val_seq = create_sequences(timesteps, X_val, y_val)\n X_test_seq, y_test_seq = create_sequences(timesteps, X_test, y_test)\n\n print(X_train_seq.shape, y_train_seq.shape)\n print(X_val_seq.shape, y_val_seq.shape)\n print(X_test_seq.shape, y_test_seq.shape)\n\n print(f\"Addestramento del modello per: {target}\")\n model = build_advanced_model((timesteps, num_features), l2_lambda=0.001)\n optimizer = Adam(learning_rate=0.001, clipnorm=1.0)\n model.compile(optimizer=optimizer, loss='mse', metrics=['mae'])\n early_stopping = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)\n\n reduce_lr = ReduceLROnPlateau(\n monitor='val_loss',\n factor=0.5,\n patience=5,\n min_lr=1e-6\n )\n \n \n history = model.fit(\n X_train_seq, y_train_seq,\n validation_data=(X_val_seq, y_val_seq),\n epochs=50,\n batch_size=180,\n callbacks=[\n early_stopping,\n reduce_lr,\n # Model Checkpoint\n tf.keras.callbacks.ModelCheckpoint(\n filepath='/kaggle/working/{target}/best_model_{epoch:02d}_{val_loss:.4f}.keras',\n monitor='val_loss',\n save_best_only=True,\n mode='min'\n ),\n # TensorBoard logging\n tf.keras.callbacks.TensorBoard(\n log_dir='/kaggle/working/{target}/logs',\n histogram_freq=1,\n write_graph=True,\n update_freq='epoch'\n )],\n verbose=1\n )\n test_loss = model.evaluate(X_test_seq, y_test_seq)\n mse, mae = test_loss\n print(f'Test MSE per {target}: {mse:.4f}')\n print(f'Test MAE per {target}: {mae:.4f}')\n models[target] = model\n histories[target] = history\n",
"metadata": {
"jupyter": {
"is_executing": true
},
"execution": {
"iopub.status.busy": "2024-10-28T06:43:23.933505Z",
"iopub.execute_input": "2024-10-28T06:43:23.933839Z",
"iopub.status.idle": "2024-10-28T07:14:03.118634Z",
"shell.execute_reply.started": "2024-10-28T06:43:23.933806Z",
"shell.execute_reply": "2024-10-28T07:14:03.117747Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "def save_models_and_scalers(models, scaler_X, scalers_y, target_variables, base_path='/kaggle/working/models'):\n \"\"\"\n Salva i modelli e gli scaler nella cartella models.\n \n Parameters:\n -----------\n models : dict\n Dizionario contenente i modelli per ogni variabile target\n scaler_X : MinMaxScaler\n Scaler unico per tutte le feature di input\n scalers_y : dict\n Dizionario contenente gli scaler per le variabili target\n target_variables : list\n Lista delle variabili target\n base_path : str\n Percorso base dove salvare i modelli (default: 'models')\n \"\"\"\n\n # Crea la cartella se non esiste\n os.makedirs(base_path, exist_ok=True)\n\n # Salva lo scaler X generale\n scaler_x_path = os.path.join(base_path, 'scaler_x.joblib')\n joblib.dump(scaler_X, scaler_x_path)\n\n # Salva i modelli e gli scaler Y per ogni variabile target\n for target in target_variables:\n # Crea una sottocartella per ogni target\n target_path = os.path.join(base_path, target)\n os.makedirs(target_path, exist_ok=True)\n\n # Salva il modello\n model_path = os.path.join(target_path, 'model.joblib')\n joblib.dump(models[target], model_path)\n\n # Salva lo scaler Y\n scaler_y_path = os.path.join(target_path, 'scaler_y.joblib')\n joblib.dump(scalers_y[target], scaler_y_path)\n\n # Salva la lista delle variabili target\n target_vars_path = os.path.join(base_path, 'target_variables.joblib')\n joblib.dump(target_variables, target_vars_path)\n\n print(f\"Modelli e scaler salvati in: {base_path}\")\n\n\ndef load_models_and_scalers(base_path='/kaggle/working/models'):\n \"\"\"\n Carica i modelli e gli scaler dalla cartella models.\n \n Parameters:\n -----------\n base_path : str\n Percorso della cartella contenente i modelli salvati (default: 'models')\n \n Returns:\n --------\n tuple\n (models, scaler_X, scalers_y, target_variables)\n \"\"\"\n\n # Carica la lista delle variabili target\n target_vars_path = os.path.join(base_path, 'target_variables.joblib')\n target_variables = joblib.load(target_vars_path)\n\n # Carica lo scaler X generale\n scaler_x_path = os.path.join(base_path, 'scaler_x.joblib')\n scaler_X = joblib.load(scaler_x_path)\n\n # Inizializza i dizionari\n models = {}\n scalers_y = {}\n\n # Carica i modelli e gli scaler per ogni variabile target\n for target in target_variables:\n target_path = os.path.join(base_path, target)\n\n # Carica il modello\n model_path = os.path.join(target_path, 'model.joblib')\n models[target] = joblib.load(model_path)\n\n # Carica lo scaler Y\n scaler_y_path = os.path.join(target_path, 'scaler_y.joblib')\n scalers_y[target] = joblib.load(scaler_y_path)\n\n print(f\"Modelli e scaler caricati da: {base_path}\")\n return models, scaler_X, scalers_y, target_variables\n\n\n",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T18:17:50.886746Z",
"iopub.execute_input": "2024-10-28T18:17:50.887388Z",
"iopub.status.idle": "2024-10-28T18:17:50.899266Z",
"shell.execute_reply.started": "2024-10-28T18:17:50.887349Z",
"shell.execute_reply": "2024-10-28T18:17:50.898193Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "save_models_and_scalers(models, scaler_X, scalers, target_variables)",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T07:14:03.136051Z",
"iopub.execute_input": "2024-10-28T07:14:03.136333Z",
"iopub.status.idle": "2024-10-28T07:14:03.558701Z",
"shell.execute_reply.started": "2024-10-28T07:14:03.136303Z",
"shell.execute_reply": "2024-10-28T07:14:03.557717Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "# Previsione delle variabili mancanti per data_before_2010\n# Prepara data_before_2010\ndata_before_2010 = data_before_2010.sort_values('datetime')\ndata_before_2010.set_index('datetime', inplace=True)\n\n# Assicurati che le features non abbiano valori mancanti\ndata_before_2010[features] = data_before_2010[features].ffill()\ndata_before_2010[features] = data_before_2010[features].bfill()",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T07:14:03.559889Z",
"iopub.execute_input": "2024-10-28T07:14:03.560236Z",
"iopub.status.idle": "2024-10-28T07:14:03.690402Z",
"shell.execute_reply.started": "2024-10-28T07:14:03.560193Z",
"shell.execute_reply": "2024-10-28T07:14:03.689615Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "models, scaler_X, scalers_y, target_variables = load_models_and_scalers()\n\ntimesteps = 24\n# Crea X per data_before_2010\nX_before = data_before_2010[features].values\nX_before_scaled = scaler_X.transform(X_before)\n\n# Crea le sequenze per LSTM\nX_before_seq = create_sequences(timesteps, X_before_scaled)\n\n# Prevedi le variabili mancanti\nfor i, target in enumerate(target_variables):\n print(\"Shape di X_before_seq:\", X_before_seq.shape)\n print(f\"Previsione di {target} per data_before_2010\")\n y_pred_scaled = models[target].predict(X_before_seq)\n print(\"Shape delle predizioni:\", y_pred_scaled.shape)\n # Ricostruisci i valori originali\n scaler = scalers_y[target]\n y_pred = scaler.inverse_transform(y_pred_scaled)\n\n # Allinea le previsioni con le date corrette\n dates = data_before_2010.index[timesteps:]\n data_before_2010.loc[dates, target] = y_pred\n\n# Gestisci eventuali valori iniziali mancanti\ndata_before_2010[target_variables] = data_before_2010[target_variables].bfill()\n\n# Combina data_before_2010 e data_after_2010\nweather_data_complete = pd.concat([data_before_2010, data_after_2010], axis=0)\nweather_data_complete = weather_data_complete.sort_index()\n\n# Salva il dataset completo\nweather_data_complete.reset_index(inplace=True)\nweather_data_complete.to_parquet('/kaggle/working/weather_data_complete.parquet', index=False)\n",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T07:14:03.691544Z",
"iopub.execute_input": "2024-10-28T07:14:03.691857Z",
"iopub.status.idle": "2024-10-28T07:16:07.983275Z",
"shell.execute_reply.started": "2024-10-28T07:14:03.691824Z",
"shell.execute_reply": "2024-10-28T07:16:07.981975Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## 2. Esplorazione dei Dati Meteo",
"metadata": {}
},
{
"cell_type": "code",
"source": "weather_data = pd.read_parquet('/kaggle/working/weather_data_complete.parquet')",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T07:16:07.984920Z",
"iopub.execute_input": "2024-10-28T07:16:07.985367Z",
"iopub.status.idle": "2024-10-28T07:16:08.113149Z",
"shell.execute_reply.started": "2024-10-28T07:16:07.985319Z",
"shell.execute_reply": "2024-10-28T07:16:08.112118Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "# Visualizzazione delle tendenze temporali\nfig, axes = plt.subplots(5, 1, figsize=(15, 20))\nweather_data.set_index('date')['temp'].plot(ax=axes[0], title='Temperatura Media Giornaliera')\nweather_data.set_index('date')['humidity'].plot(ax=axes[1], title='Umidità Media Giornaliera')\nweather_data.set_index('date')['solarradiation'].plot(ax=axes[2], title='Radiazione Solare Giornaliera')\nweather_data.set_index('date')['solarenergy'].plot(ax=axes[3], title='Radiazione Solare Giornaliera')\nweather_data.set_index('date')['precip'].plot(ax=axes[4], title='Precipitazioni Giornaliere')\nplt.tight_layout()\nplt.show()\nsave_plot(plt, 'weather_trends', '/kaggle/working/plots')\nplt.close()",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T07:16:08.114826Z",
"iopub.execute_input": "2024-10-28T07:16:08.115235Z",
"iopub.status.idle": "2024-10-28T07:16:13.275984Z",
"shell.execute_reply.started": "2024-10-28T07:16:08.115192Z",
"shell.execute_reply": "2024-10-28T07:16:13.275040Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## 3. Simulazione dei Dati di Produzione Annuale",
"metadata": {}
},
{
"cell_type": "code",
"source": "\n# Esempio di utilizzo\nolive_varieties = pd.read_csv('/kaggle/input/olive-oil/variety_olive_oil_production.csv')\n\n\ndef add_olive_water_consumption_correlation(dataset):\n # Dati simulati per il fabbisogno d'acqua e la correlazione con la temperatura\n fabbisogno_acqua = {\n \"Nocellara dell'Etna\": {\"Primavera\": 1200, \"Estate\": 2000, \"Autunno\": 1000, \"Inverno\": 500, \"Temperatura Ottimale\": 18, \"Resistenza\": \"Media\"},\n \"Leccino\": {\"Primavera\": 1000, \"Estate\": 1800, \"Autunno\": 800, \"Inverno\": 400, \"Temperatura Ottimale\": 20, \"Resistenza\": \"Alta\"},\n \"Frantoio\": {\"Primavera\": 1100, \"Estate\": 1900, \"Autunno\": 900, \"Inverno\": 450, \"Temperatura Ottimale\": 19, \"Resistenza\": \"Alta\"},\n \"Coratina\": {\"Primavera\": 1300, \"Estate\": 2200, \"Autunno\": 1100, \"Inverno\": 550, \"Temperatura Ottimale\": 17, \"Resistenza\": \"Media\"},\n \"Moraiolo\": {\"Primavera\": 1150, \"Estate\": 2100, \"Autunno\": 900, \"Inverno\": 480, \"Temperatura Ottimale\": 18, \"Resistenza\": \"Media\"},\n \"Pendolino\": {\"Primavera\": 1050, \"Estate\": 1850, \"Autunno\": 850, \"Inverno\": 430, \"Temperatura Ottimale\": 20, \"Resistenza\": \"Alta\"},\n \"Taggiasca\": {\"Primavera\": 1000, \"Estate\": 1750, \"Autunno\": 800, \"Inverno\": 400, \"Temperatura Ottimale\": 19, \"Resistenza\": \"Alta\"},\n \"Canino\": {\"Primavera\": 1100, \"Estate\": 1900, \"Autunno\": 900, \"Inverno\": 450, \"Temperatura Ottimale\": 18, \"Resistenza\": \"Media\"},\n \"Itrana\": {\"Primavera\": 1200, \"Estate\": 2000, \"Autunno\": 1000, \"Inverno\": 500, \"Temperatura Ottimale\": 17, \"Resistenza\": \"Media\"},\n \"Ogliarola\": {\"Primavera\": 1150, \"Estate\": 1950, \"Autunno\": 900, \"Inverno\": 480, \"Temperatura Ottimale\": 18, \"Resistenza\": \"Media\"},\n \"Biancolilla\": {\"Primavera\": 1050, \"Estate\": 1800, \"Autunno\": 850, \"Inverno\": 430, \"Temperatura Ottimale\": 19, \"Resistenza\": \"Alta\"}\n }\n\n # Calcola il fabbisogno idrico annuale per ogni varietà\n for varieta in fabbisogno_acqua:\n fabbisogno_acqua[varieta][\"Annuale\"] = sum([fabbisogno_acqua[varieta][stagione] for stagione in [\"Primavera\", \"Estate\", \"Autunno\", \"Inverno\"]])\n\n # Aggiungiamo le nuove colonne al dataset\n dataset[\"Fabbisogno Acqua Primavera (m³/ettaro)\"] = dataset[\"Varietà di Olive\"].apply(lambda x: fabbisogno_acqua[x][\"Primavera\"])\n dataset[\"Fabbisogno Acqua Estate (m³/ettaro)\"] = dataset[\"Varietà di Olive\"].apply(lambda x: fabbisogno_acqua[x][\"Estate\"])\n dataset[\"Fabbisogno Acqua Autunno (m³/ettaro)\"] = dataset[\"Varietà di Olive\"].apply(lambda x: fabbisogno_acqua[x][\"Autunno\"])\n dataset[\"Fabbisogno Acqua Inverno (m³/ettaro)\"] = dataset[\"Varietà di Olive\"].apply(lambda x: fabbisogno_acqua[x][\"Inverno\"])\n dataset[\"Fabbisogno Idrico Annuale (m³/ettaro)\"] = dataset[\"Varietà di Olive\"].apply(lambda x: fabbisogno_acqua[x][\"Annuale\"])\n dataset[\"Temperatura Ottimale\"] = dataset[\"Varietà di Olive\"].apply(lambda x: fabbisogno_acqua[x][\"Temperatura Ottimale\"])\n dataset[\"Resistenza alla Siccità\"] = dataset[\"Varietà di Olive\"].apply(lambda x: fabbisogno_acqua[x][\"Resistenza\"])\n\n return dataset\n\n\nolive_varieties = add_olive_water_consumption_correlation(olive_varieties)\n\nolive_varieties.to_parquet(\"/kaggle/working/olive_varieties.parquet\")",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T07:16:13.277678Z",
"iopub.execute_input": "2024-10-28T07:16:13.278023Z",
"iopub.status.idle": "2024-10-28T07:16:13.321438Z",
"shell.execute_reply.started": "2024-10-28T07:16:13.277988Z",
"shell.execute_reply": "2024-10-28T07:16:13.320689Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "def preprocess_weather_data(weather_df):\n # Calcola statistiche mensili per ogni anno\n monthly_weather = weather_df.groupby(['year', 'month']).agg({\n 'temp': ['mean', 'min', 'max'],\n 'humidity': 'mean',\n 'precip': 'sum',\n 'windspeed': 'mean',\n 'cloudcover': 'mean',\n 'solarradiation': 'sum',\n 'solarenergy': 'sum',\n 'uvindex': 'max'\n }).reset_index()\n\n monthly_weather.columns = ['year', 'month'] + [f'{col[0]}_{col[1]}' for col in monthly_weather.columns[2:]]\n return monthly_weather\n\n\ndef get_growth_phase(month):\n if month in [12, 1, 2]:\n return 'dormancy'\n elif month in [3, 4, 5]:\n return 'flowering'\n elif month in [6, 7, 8]:\n return 'fruit_set'\n else:\n return 'ripening'\n\n\ndef calculate_weather_effect(row, optimal_temp):\n # Effetti base\n temp_effect = -0.1 * (row['temp_mean'] - optimal_temp) ** 2\n rain_effect = -0.05 * (row['precip_sum'] - 600) ** 2 / 10000\n sun_effect = 0.1 * row['solarenergy_sum'] / 1000\n\n # Fattori di scala basati sulla fase di crescita\n if row['growth_phase'] == 'dormancy':\n temp_scale = 0.5\n rain_scale = 0.2\n sun_scale = 0.1\n elif row['growth_phase'] == 'flowering':\n temp_scale = 2.0\n rain_scale = 1.5\n sun_scale = 1.0\n elif row['growth_phase'] == 'fruit_set':\n temp_scale = 1.5\n rain_scale = 1.0\n sun_scale = 0.8\n else: # ripening\n temp_scale = 1.0\n rain_scale = 0.5\n sun_scale = 1.2\n\n # Calcolo dell'effetto combinato\n combined_effect = (\n temp_scale * temp_effect +\n rain_scale * rain_effect +\n sun_scale * sun_effect\n )\n\n # Aggiustamenti specifici per fase\n if row['growth_phase'] == 'flowering':\n combined_effect -= 0.5 * max(0, row['precip_sum'] - 50) # Penalità per pioggia eccessiva durante la fioritura\n elif row['growth_phase'] == 'fruit_set':\n combined_effect += 0.3 * max(0, row['temp_mean'] - (optimal_temp + 5)) # Bonus per temperature più alte durante la formazione dei frutti\n\n return combined_effect\n\n\ndef calculate_water_need(weather_data, base_need, optimal_temp):\n # Calcola il fabbisogno idrico basato su temperatura e precipitazioni\n temp_factor = 1 + 0.05 * (weather_data['temp_mean'] - optimal_temp) # Aumenta del 5% per ogni grado sopra l'ottimale\n rain_factor = 1 - 0.001 * weather_data['precip_sum'] # Diminuisce leggermente con l'aumentare delle precipitazioni\n return base_need * temp_factor * rain_factor\n\n\ndef clean_column_name(name):\n # Rimuove caratteri speciali e spazi, converte in snake_case e abbrevia\n name = re.sub(r'[^a-zA-Z0-9\\s]', '', name) # Rimuove caratteri speciali\n name = name.lower().replace(' ', '_') # Converte in snake_case\n\n # Abbreviazioni comuni\n abbreviations = {\n 'production': 'prod',\n 'percentage': 'pct',\n 'hectare': 'ha',\n 'tonnes': 't',\n 'litres': 'l',\n 'minimum': 'min',\n 'maximum': 'max',\n 'average': 'avg'\n }\n\n for full, abbr in abbreviations.items():\n name = name.replace(full, abbr)\n\n return name\n\n\ndef create_technique_mapping(olive_varieties, mapping_path='models/technique_mapping.joblib'):\n # Estrai tutte le tecniche uniche dal dataset e convertile in lowercase\n all_techniques = olive_varieties['Tecnica di Coltivazione'].str.lower().unique()\n\n # Crea il mapping partendo da 1\n technique_mapping = {tech: i + 1 for i, tech in enumerate(sorted(all_techniques))}\n\n # Salva il mapping\n os.makedirs(os.path.dirname(mapping_path), exist_ok=True)\n joblib.dump(technique_mapping, mapping_path)\n\n return technique_mapping\n\n\ndef encode_techniques(df, mapping_path='models/technique_mapping.joblib'):\n if not os.path.exists(mapping_path):\n raise FileNotFoundError(f\"Mapping not found at {mapping_path}. Run create_technique_mapping first.\")\n\n technique_mapping = joblib.load(mapping_path)\n\n # Trova tutte le colonne delle tecniche\n tech_columns = [col for col in df.columns if col.endswith('_tech')]\n\n # Applica il mapping a tutte le colonne delle tecniche\n for col in tech_columns:\n df[col] = df[col].str.lower().map(technique_mapping).fillna(0).astype(int)\n\n return df\n\n\ndef decode_techniques(df, mapping_path='models/technique_mapping.joblib'):\n if not os.path.exists(mapping_path):\n raise FileNotFoundError(f\"Mapping not found at {mapping_path}\")\n\n technique_mapping = joblib.load(mapping_path)\n reverse_mapping = {v: k for k, v in technique_mapping.items()}\n reverse_mapping[0] = '' # Aggiungi un mapping per 0 a stringa vuota\n\n # Trova tutte le colonne delle tecniche\n tech_columns = [col for col in df.columns if col.endswith('_tech')]\n\n # Applica Line truncated
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T16:45:14.154883Z",
"iopub.execute_input": "2024-10-28T16:45:14.155261Z",
"iopub.status.idle": "2024-10-28T16:45:14.179174Z",
"shell.execute_reply.started": "2024-10-28T16:45:14.155223Z",
"shell.execute_reply": "2024-10-28T16:45:14.178223Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "def simulate_olive_production(weather_data, olive_varieties, num_simulations=5, random_seed=None):\n \"\"\"\n Simula la produzione di olive per diverse zone e varietà, considerando variazioni meteo specifiche per zona.\n Include barre di avanzamento per monitorare il progresso.\n \n Args:\n weather_data: DataFrame con dati meteorologici storici\n olive_varieties: DataFrame con informazioni sulle varietà di olive\n num_simulations: Numero di simulazioni/zone da generare\n random_seed: Seme per la riproducibilità dei risultati\n \n Returns:\n DataFrame con i risultati delle simulazioni per tutte le zone\n \"\"\"\n if random_seed is not None:\n np.random.seed(random_seed)\n\n create_technique_mapping(olive_varieties)\n monthly_weather = preprocess_weather_data(weather_data)\n all_results = []\n\n # Preparazione dati varietà\n all_varieties = olive_varieties['Varietà di Olive'].unique()\n variety_techniques = {\n variety: olive_varieties[olive_varieties['Varietà di Olive'] == variety]['Tecnica di Coltivazione'].unique()\n for variety in all_varieties\n }\n\n # Barra di avanzamento principale per le simulazioni\n with tqdm(total=num_simulations*num_simulations, desc=\"Simulazioni completate\") as sim_pbar:\n # Per ogni simulazione (anno)\n for sim in range(num_simulations):\n # Seleziona anno di base per questa simulazione\n selected_year = np.random.choice(monthly_weather['year'].unique())\n base_weather = monthly_weather[monthly_weather['year'] == selected_year].copy()\n base_weather.loc[:, 'growth_phase'] = base_weather['month'].apply(get_growth_phase)\n\n # Per ogni zona nella simulazione\n for zone in range(num_simulations):\n # Crea una copia dei dati meteo per questa zona specifica\n zone_weather = base_weather.copy()\n\n # Genera variazioni meteorologiche specifiche per questa zona\n zone_weather['temp_mean'] *= np.random.uniform(0.95, 1.05, len(zone_weather))\n zone_weather['precip_sum'] *= np.random.uniform(0.9, 1.1, len(zone_weather))\n zone_weather['solarenergy_sum'] *= np.random.uniform(0.95, 1.05, len(zone_weather))\n\n # Genera caratteristiche specifiche della zona\n num_varieties = np.random.randint(1, 4) # 1-3 varietà per zona\n selected_varieties = np.random.choice(all_varieties, size=num_varieties, replace=False)\n hectares = np.random.uniform(1, 10) # Dimensione del terreno\n percentages = np.random.dirichlet(np.ones(num_varieties)) # Distribuzione delle varietà\n\n # Inizializzazione contatori annuali\n annual_production = 0\n annual_min_oil = 0\n annual_max_oil = 0\n annual_avg_oil = 0\n annual_water_need = 0\n\n # Inizializzazione dizionario dati varietà\n variety_data = {clean_column_name(variety): {\n 'tech': '',\n 'pct': 0,\n 'prod_t_ha': 0,\n 'oil_prod_t_ha': 0,\n 'oil_prod_l_ha': 0,\n 'min_yield_pct': 0,\n 'max_yield_pct': 0,\n 'min_oil_prod_l_ha': 0,\n 'max_oil_prod_l_ha': 0,\n 'avg_oil_prod_l_ha': 0,\n 'l_per_t': 0,\n 'min_l_per_t': 0,\n 'max_l_per_t': 0,\n 'avg_l_per_t': 0,\n 'olive_prod': 0,\n 'min_oil_prod': 0,\n 'max_oil_prod': 0,\n 'avg_oil_prod': 0,\n 'water_need': 0\n } for variety in all_varieties}\n\n # Simula produzione per ogni varietà selezionata\n for i, variety in enumerate(selected_varieties):\n # Seleziona tecnica di coltivazione casuale per questa varietà\n technique = np.random.choice(variety_techniques[variety])\n percentage = percentages[i]\n\n # Ottieni informazioni specifiche della varietà\n variety_info = olive_varieties[\n (olive_varieties['Varietà di Olive'] == variety) &\n (olive_varieties['Tecnica di Coltivazione'] == technique)\n ].iloc[0]\n\n # Calcola produzione base con variabilità\n base_production = variety_info['Produzione (tonnellate/ettaro)'] * 1000 * percentage * hectares / 12\n base_production *= np.random.uniform(0.9, 1.1) # Aggiungi variabilità alla produzione base\n\n # Calcola effetti meteo sulla produzione\nLine truncated
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T07:16:13.322975Z",
"iopub.execute_input": "2024-10-28T07:16:13.323323Z",
"iopub.status.idle": "2024-10-28T09:33:50.129328Z",
"shell.execute_reply.started": "2024-10-28T07:16:13.323289Z",
"shell.execute_reply": "2024-10-28T09:33:50.127974Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "simulated_data = pd.read_parquet(\"/kaggle/working/simulated_data.parquet\")\n\n\ndef clean_column_names(df):\n # Funzione per pulire i nomi delle colonne\n new_columns = []\n for col in df.columns:\n # Usa regex per separare le varietà\n varieties = re.findall(r'([a-z]+)_([a-z_]+)', col)\n if varieties:\n new_columns.append(f\"{varieties[0][0]}_{varieties[0][1]}\")\n else:\n new_columns.append(col)\n return new_columns\n\n\ndef prepare_comparison_data(simulated_data, olive_varieties):\n # Pulisci i nomi delle colonne\n df = simulated_data.copy()\n\n df.columns = clean_column_names(df)\n df = encode_techniques(df)\n\n all_varieties = olive_varieties['Varietà di Olive'].unique()\n varieties = [clean_column_name(variety) for variety in all_varieties]\n comparison_data = []\n\n for variety in varieties:\n olive_prod_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_olive_prod')), None)\n oil_prod_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_avg_oil_prod')), None)\n tech_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_tech')), None)\n water_need_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_water_need')), None)\n\n if olive_prod_col and oil_prod_col and tech_col and water_need_col:\n variety_data = df[[olive_prod_col, oil_prod_col, tech_col, water_need_col]]\n variety_data = variety_data[variety_data[tech_col] != 0] # Esclude le righe dove la tecnica è 0\n\n if not variety_data.empty:\n avg_olive_prod = pd.to_numeric(variety_data[olive_prod_col], errors='coerce').mean()\n avg_oil_prod = pd.to_numeric(variety_data[oil_prod_col], errors='coerce').mean()\n avg_water_need = pd.to_numeric(variety_data[water_need_col], errors='coerce').mean()\n efficiency = avg_oil_prod / avg_olive_prod if avg_olive_prod > 0 else 0\n water_efficiency = avg_oil_prod / avg_water_need if avg_water_need > 0 else 0\n\n comparison_data.append({\n 'Variety': variety,\n 'Avg Olive Production (kg/ha)': avg_olive_prod,\n 'Avg Oil Production (L/ha)': avg_oil_prod,\n 'Avg Water Need (m³/ha)': avg_water_need,\n 'Oil Efficiency (L/kg)': efficiency,\n 'Water Efficiency (L oil/m³ water)': water_efficiency\n })\n\n return pd.DataFrame(comparison_data)\n\n\ndef plot_variety_comparison(comparison_data, metric):\n plt.figure(figsize=(12, 6))\n bars = plt.bar(comparison_data['Variety'], comparison_data[metric])\n plt.title(f'Comparison of {metric} across Olive Varieties')\n plt.xlabel('Variety')\n plt.ylabel(metric)\n plt.xticks(rotation=45, ha='right')\n\n for bar in bars:\n height = bar.get_height()\n plt.text(bar.get_x() + bar.get_width() / 2., height,\n f'{height:.2f}',\n ha='center', va='bottom')\n\n plt.tight_layout()\n plt.show()\n save_plot(plt, f'variety_comparison_{metric.lower().replace(\" \", \"_\").replace(\"/\", \"_\").replace(\"(\", \"\").replace(\")\", \"\")}', '/kaggle/working/plots')\n plt.close()\n\n\ndef plot_efficiency_vs_production(comparison_data):\n plt.figure(figsize=(10, 6))\n\n plt.scatter(comparison_data['Avg Olive Production (kg/ha)'],\n comparison_data['Oil Efficiency (L/kg)'],\n s=100)\n\n for i, row in comparison_data.iterrows():\n plt.annotate(row['Variety'],\n (row['Avg Olive Production (kg/ha)'], row['Oil Efficiency (L/kg)']),\n xytext=(5, 5), textcoords='offset points')\n\n plt.title('Oil Efficiency vs Olive Production by Variety')\n plt.xlabel('Average Olive Production (kg/ha)')\n plt.ylabel('Oil Efficiency (L oil / kg olives)')\n plt.tight_layout()\n save_plot(plt, 'efficiency_vs_production', '/kaggle/working/plots')\n plt.close()\n\n\ndef plot_water_efficiency_vs_production(comparison_data):\n plt.figure(figsize=(10, 6))\n\n plt.scatter(comparison_data['Avg Olive Production (kg/ha)'],\n comparison_data['Water Efficiency (L oil/m³ water)'],\n s=100)\n\n for i, row in comparison_data.iterrows():\n plt.annotate(row['Variety'],\n (row['Avg Olive Production (kg/ha)'], row['Water Efficiency (L oil/m³ water)']),\n xytext=(5, 5), textcoords='offset points')\n\n plt.title('Water Efficiency vs Olive Production by Variety')\n plt.xlabel('Average Olive Production (kg/ha)')\n plt.ylabel('Water Efficiency (L oil / m³ water)')\n plt.tight_layout()\n plt.show()\n save_plot(plt, 'water_efficLine truncated
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T18:18:34.945646Z",
"iopub.execute_input": "2024-10-28T18:18:34.946022Z",
"iopub.status.idle": "2024-10-28T18:18:47.185796Z",
"shell.execute_reply.started": "2024-10-28T18:18:34.945985Z",
"shell.execute_reply": "2024-10-28T18:18:47.184864Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## 4. Analisi della Relazione tra Meteo e Produzione",
"metadata": {}
},
{
"cell_type": "code",
"source": "def get_full_data(simulated_data, olive_varieties):\n # Assumiamo che simulated_data contenga già tutti i dati necessari\n # Includiamo solo le colonne rilevanti\n relevant_columns = ['year', 'temp_mean', 'precip_sum', 'solar_energy_sum', 'ha', 'zone', 'olive_prod']\n\n # Aggiungiamo le colonne specifiche per varietà\n all_varieties = olive_varieties['Varietà di Olive'].unique()\n varieties = [clean_column_name(variety) for variety in all_varieties]\n for variety in varieties:\n relevant_columns.extend([f'{variety}_olive_prod', f'{variety}_tech'])\n\n return simulated_data[relevant_columns].copy()\n\n\ndef analyze_correlations(full_data, variety):\n # Filtra i dati per la varietà specifica\n variety_data = full_data[[col for col in full_data.columns if not col.startswith('_') or col.startswith(f'{variety}_')]]\n\n # Rinomina le colonne per chiarezza\n variety_data = variety_data.rename(columns={\n f'{variety}_olive_prod': 'olive_production',\n f'{variety}_tech': 'technique'\n })\n\n # Matrice di correlazione\n plt.figure(figsize=(12, 10))\n corr_matrix = variety_data[['temp_mean', 'precip_sum', 'solar_energy_sum', 'olive_production']].corr()\n sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')\n plt.title(f'Matrice di Correlazione - {variety}')\n plt.tight_layout()\n plt.show()\n save_plot(plt, f'correlation_matrix_{variety}', '/kaggle/working/plots')\n plt.close()\n\n # Scatter plots\n fig, axes = plt.subplots(2, 2, figsize=(20, 20))\n fig.suptitle(f'Relazione tra Fattori Meteorologici e Produzione di Olive - {variety}', fontsize=16)\n\n for ax, var in zip(axes.flat, ['temp_mean', 'precip_sum', 'solar_energy_sum', 'ha']):\n sns.scatterplot(data=variety_data, x=var, y='olive_production', hue='technique', ax=ax)\n ax.set_title(f'{var.capitalize()} vs Produzione Olive')\n ax.set_xlabel(var.capitalize())\n ax.set_ylabel('Produzione Olive (kg/ettaro)')\n\n plt.tight_layout()\n plt.show()\n save_plot(plt, f'meteorological_factors_{variety}', '/kaggle/working/plots')\n plt.close()\n\n\n# Uso delle funzioni\nfull_data = get_full_data(simulated_data, olive_varieties)\n\n# Assumiamo che 'selected_variety' sia definito altrove nel codice\n# Per esempio:\nselected_variety = 'nocellara_delletna'\n\nanalyze_correlations(full_data, selected_variety)",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T09:34:03.394039Z",
"iopub.execute_input": "2024-10-28T09:34:03.394321Z",
"iopub.status.idle": "2024-10-28T09:37:17.482691Z",
"shell.execute_reply.started": "2024-10-28T09:34:03.394290Z",
"shell.execute_reply": "2024-10-28T09:37:17.481712Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## 5. Preparazione del Modello di Machine Learning",
"metadata": {}
},
{
"cell_type": "code",
"source": [
"def prepare_data(df, olive_varieties_df):\n",
" # Crea una copia del DataFrame per evitare modifiche all'originale\n",
" df = df.copy()\n",
"\n",
" # Ordina per zona e anno\n",
" df = df.sort_values(['zone', 'year'])\n",
"\n",
" # Definisci le feature\n",
" temporal_features = ['temp_mean', 'precip_sum', 'solar_energy_sum']\n",
" static_features = ['ha'] # Feature statiche base\n",
" target_features = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']\n",
"\n",
" # Ottieni le varietà pulite\n",
" all_varieties = olive_varieties_df['Varietà di Olive'].unique()\n",
" varieties = [clean_column_name(variety) for variety in all_varieties]\n",
"\n",
" # Crea la struttura delle feature per ogni varietà\n",
" variety_features = [\n",
" 'tech', 'pct', 'prod_t_ha', 'oil_prod_t_ha', 'oil_prod_l_ha',\n",
" 'min_yield_pct', 'max_yield_pct', 'min_oil_prod_l_ha', 'max_oil_prod_l_ha',\n",
" 'avg_oil_prod_l_ha', 'l_per_t', 'min_l_per_t', 'max_l_per_t', 'avg_l_per_t'\n",
" ]\n",
"\n",
" # Prepara dizionari per le nuove colonne\n",
" new_columns = {}\n",
"\n",
" # Prepara le feature per ogni varietà\n",
" for variety in varieties:\n",
" # Feature esistenti\n",
" for feature in variety_features:\n",
" col_name = f\"{variety}_{feature}\"\n",
" if col_name in df.columns:\n",
" if feature != 'tech': # Non includere la colonna tech direttamente\n",
" static_features.append(col_name)\n",
"\n",
" # Feature binarie per le tecniche di coltivazione\n",
" for technique in ['tradizionale', 'intensiva', 'superintensiva']:\n",
" col_name = f\"{variety}_{technique}\"\n",
" new_columns[col_name] = df[f\"{variety}_tech\"].notna() & (\n",
" df[f\"{variety}_tech\"].str.lower() == technique\n",
" ).fillna(False)\n",
" static_features.append(col_name)\n",
"\n",
" # Aggiungi tutte le nuove colonne in una volta sola\n",
" new_df = pd.concat([df] + [pd.Series(v, name=k) for k, v in new_columns.items()], axis=1)\n",
"\n",
" # Ordiniamo per zona e anno per mantenere la continuità temporale\n",
" df_sorted = new_df.sort_values(['zone', 'year'])\n",
"\n",
" # Definiamo la dimensione della finestra temporale\n",
" window_size = 41\n",
"\n",
" # Liste per raccogliere i dati\n",
" temporal_sequences = []\n",
" static_features_list = []\n",
" targets_list = []\n",
"\n",
" # Iteriamo per ogni zona\n",
" for zone in df_sorted['zone'].unique():\n",
" zone_data = df_sorted[df_sorted['zone'] == zone].reset_index(drop=True)\n",
"\n",
" if len(zone_data) >= window_size: # Verifichiamo che ci siano abbastanza dati\n",
" # Creiamo sequenze temporali scorrevoli\n",
" for i in range(len(zone_data) - window_size + 1):\n",
" # Sequenza temporale\n",
" temporal_window = zone_data.iloc[i:i + window_size][temporal_features].values\n",
" # Verifichiamo che non ci siano valori NaN\n",
" if not np.isnan(temporal_window).any():\n",
" temporal_sequences.append(temporal_window)\n",
"\n",
" # Feature statiche (prendiamo quelle dell'ultimo timestep della finestra)\n",
" static_features_list.append(zone_data.iloc[i + window_size - 1][static_features].values)\n",
"\n",
" # Target (prendiamo quelli dell'ultimo timestep della finestra)\n",
" targets_list.append(zone_data.iloc[i + window_size - 1][target_features].values)\n",
"\n",
" # Convertiamo in array numpy\n",
" X_temporal = np.array(temporal_sequences)\n",
" X_static = np.array(static_features_list)\n",
" y = np.array(targets_list)\n",
"\n",
" print(f\"Dataset completo - Temporal: {X_temporal.shape}, Static: {X_static.shape}, Target: {y.shape}\")\n",
"\n",
" # Split dei dati (usando indici casuali per una migliore distribuzione)\n",
" indices = np.random.permutation(len(X_temporal))\n",
" #train_idx = int(len(indices) * 0.7)\n",
" #val_idx = int(len(indices) * 0.85)\n",
" \n",
" train_idx = int(len(indices) * 0.65) # 65% training\n",
" val_idx = int(len(indices) * 0.85) # 20% validation\n",
" # Il resto rimane 15% test\n",
"\n",
" # Oppure versione con 25% validation:\n",
" #train_idx = int(len(indices) * 0.60) # 60% training\n",
" #val_idx = int(len(indices) * 0.85) # 25% validation\n",
"\n",
" train_indices = indices[:train_idx]\n",
" val_indices = indices[train_idx:val_idx]\n",
" test_indices = indices[val_idx:]\n",
"\n",
" # Split dei dati\n",
" X_temporal_train = X_temporal[train_indices]\n",
" X_temporal_val = X_temporal[val_indices]\n",
" X_temporal_test = X_temporal[test_indices]\n",
"\n",
" X_static_train = X_static[train_indices]\n",
" X_static_val = X_static[val_indices]\n",
" X_static_test = X_static[test_indices]\n",
"\n",
" y_train = y[train_indices]\n",
" y_val = y[val_indices]\n",
" y_test = y[test_indices]\n",
"\n",
" # Standardizzazione\n",
" scaler_temporal = StandardScaler()\n",
" scaler_static = StandardScaler()\n",
" scaler_y = StandardScaler()\n",
"\n",
" # Standardizzazione dei dati temporali\n",
" X_temporal_train = scaler_temporal.fit_transform(X_temporal_train.reshape(-1, len(temporal_features))).reshape(X_temporal_train.shape)\n",
" X_temporal_val = scaler_temporal.transform(X_temporal_val.reshape(-1, len(temporal_features))).reshape(X_temporal_val.shape)\n",
" X_temporal_test = scaler_temporal.transform(X_temporal_test.reshape(-1, len(temporal_features))).reshape(X_temporal_test.shape)\n",
"\n",
" # Standardizzazione dei dati statici\n",
" X_static_train = scaler_static.fit_transform(X_static_train)\n",
" X_static_val = scaler_static.transform(X_static_val)\n",
" X_static_test = scaler_static.transform(X_static_test)\n",
"\n",
" # Standardizzazione dei target\n",
" y_train = scaler_y.fit_transform(y_train)\n",
" y_val = scaler_y.transform(y_val)\n",
" y_test = scaler_y.transform(y_test)\n",
"\n",
" print(\"\\nShape dopo lo split e standardizzazione:\")\n",
" print(f\"Train - Temporal: {X_temporal_train.shape}, Static: {X_static_train.shape}, Target: {y_train.shape}\")\n",
" print(f\"Val - Temporal: {X_temporal_val.shape}, Static: {X_static_val.shape}, Target: {y_val.shape}\")\n",
" print(f\"Test - Temporal: {X_temporal_test.shape}, Static: {X_static_test.shape}, Target: {y_test.shape}\")\n",
"\n",
" # Prepara i dizionari di input\n",
" train_data = {'temporal': X_temporal_train, 'static': X_static_train}\n",
" val_data = {'temporal': X_temporal_val, 'static': X_static_val}\n",
" test_data = {'temporal': X_temporal_test, 'static': X_static_test}\n",
"\n",
" return (train_data, y_train), (val_data, y_val), (test_data, y_test), (scaler_temporal, scaler_static, scaler_y)"
],
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T18:19:04.089809Z",
"iopub.execute_input": "2024-10-28T18:19:04.090188Z",
"iopub.status.idle": "2024-10-28T18:19:04.139332Z",
"shell.execute_reply.started": "2024-10-28T18:19:04.090151Z",
"shell.execute_reply": "2024-10-28T18:19:04.138344Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## Divisione train/validation/test:\n",
"metadata": {}
},
{
"cell_type": "code",
"source": "simulated_data = pd.read_parquet(\"/kaggle/working/simulated_data.parquet\")\nolive_varieties = pd.read_parquet(\"/kaggle/working/olive_varieties.parquet\")\n\n(train_data, train_targets), (val_data, val_targets), (test_data, test_targets), scalers = prepare_data(simulated_data, olive_varieties)\n\nscaler_temporal, scaler_static, scaler_y = scalers\n\nprint(\"Temporal data shape:\", train_data['temporal'].shape)\nprint(\"Static data shape:\", train_data['static'].shape)\nprint(\"Target shape:\", train_targets.shape)",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T18:19:10.442984Z",
"iopub.execute_input": "2024-10-28T18:19:10.443717Z",
"iopub.status.idle": "2024-10-28T18:53:11.893811Z",
"shell.execute_reply.started": "2024-10-28T18:19:10.443670Z",
"shell.execute_reply": "2024-10-28T18:53:11.892633Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## OliveOilTransformer",
"metadata": {}
},
{
"cell_type": "code",
"source": "import tensorflow as tf\nimport numpy as np\n\nclass PositionalEncoding(tf.keras.layers.Layer):\n def __init__(self, position, d_model):\n super(PositionalEncoding, self).__init__()\n self.pos_encoding = self.positional_encoding(position, d_model)\n\n def get_angles(self, position, i, d_model):\n angles = 1 / tf.pow(10000, (2 * (i // 2)) / tf.cast(d_model, tf.float32))\n return position * angles\n\n def positional_encoding(self, position, d_model):\n angle_rads = self.get_angles(\n position=tf.range(position, dtype=tf.float32)[:, tf.newaxis],\n i=tf.range(d_model, dtype=tf.float32)[tf.newaxis, :],\n d_model=d_model)\n\n sines = tf.math.sin(angle_rads[:, 0::2])\n cosines = tf.math.cos(angle_rads[:, 1::2])\n\n pos_encoding = tf.concat([sines, cosines], axis=-1)\n pos_encoding = pos_encoding[tf.newaxis, ...]\n return tf.cast(pos_encoding, tf.float32)\n\n def call(self, inputs):\n return inputs + self.pos_encoding[:, :tf.shape(inputs)[1], :]\n\nclass TemporalAugmentation(tf.keras.layers.Layer):\n def __init__(self, noise_factor=0.03, **kwargs):\n super().__init__(**kwargs)\n self.noise_factor = noise_factor\n\n def call(self, inputs, training=None):\n if training:\n noise = tf.random.normal(\n shape=tf.shape(inputs), \n mean=0.0, \n stddev=self.noise_factor\n )\n return inputs + noise\n return inputs\n\nclass EnhancedTransformerBlock(tf.keras.layers.Layer):\n def __init__(self, d_model, num_heads, ff_dim, dropout=0.1):\n super().__init__()\n self.att = tf.keras.layers.MultiHeadAttention(\n num_heads=num_heads, \n key_dim=d_model // num_heads,\n value_dim=d_model // num_heads\n )\n self.ffn = tf.keras.Sequential([\n tf.keras.layers.Dense(ff_dim, activation=\"gelu\"),\n tf.keras.layers.Dropout(dropout),\n tf.keras.layers.Dense(d_model)\n ])\n self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6)\n self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6)\n self.dropout1 = tf.keras.layers.Dropout(dropout)\n self.dropout2 = tf.keras.layers.Dropout(dropout)\n self.residual_attention = tf.keras.layers.Dense(d_model, activation='sigmoid')\n\n def call(self, inputs, training):\n # Self-attention con residual connection\n attn_output = self.att(inputs, inputs)\n attn_output = self.dropout1(attn_output, training=training)\n residual_weights = self.residual_attention(inputs)\n out1 = self.layernorm1(inputs + residual_weights * attn_output)\n \n # Feed-forward con residual connection\n ffn_output = self.ffn(out1)\n ffn_output = self.dropout2(ffn_output, training=training)\n return self.layernorm2(out1 + ffn_output)\n\nclass TemporalPoolingLayer(tf.keras.layers.Layer):\n def __init__(self, num_heads, key_dim, **kwargs):\n super().__init__(**kwargs)\n self.attention_pooling = tf.keras.layers.MultiHeadAttention(\n num_heads=num_heads, \n key_dim=key_dim\n )\n self.temporal_pooling = tf.keras.layers.GlobalAveragePooling1D()\n self.max_pooling = tf.keras.layers.GlobalMaxPooling1D()\n self.concat = tf.keras.layers.Concatenate(axis=-1)\n \n def call(self, inputs, training=None):\n # Attention pooling\n att_output = self.attention_pooling(inputs, inputs)\n \n # Global average e max pooling\n avg_output = self.temporal_pooling(inputs)\n max_output = self.max_pooling(inputs)\n \n # Reshape attention output\n att_output = tf.reduce_mean(att_output, axis=1)\n \n # Concatena tutti i tipi di pooling\n return self.concat([att_output, avg_output, max_output])\n\nclass OliveOilTransformer(tf.keras.Model):\n def __init__(self, temporal_shape, static_shape, num_outputs,\n d_model=128, num_heads=8, ff_dim=256, num_transformer_blocks=4,\n mlp_units=[256, 128, 64], dropout=0.2):\n super(OliveOilTransformer, self).__init__()\n \n # Input layers\n self.temporal_input = tf.keras.layers.Input(shape=temporal_shape, name='temporal_input')\n self.static_input = tf.keras.layers.Input(shape=static_shape, name='static_input')\n \n # Input normalization\n self.temporal_normalization = tf.keras.layers.LayerNormalization(epsilon=1e-6)\n self.static_normalization = tf.keras.layers.LayerNormalization(epsilon=1e-6)\n \n # Data Augmentation\n self.temporal_augmentation = TemporalAugmentation(noise_factor=0.03)\n \n # Temporal path\n self.temporal_projection = tf.keras.Sequential([\n Line truncated
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T20:35:41.836445Z",
"iopub.execute_input": "2024-10-28T20:35:41.836806Z",
"iopub.status.idle": "2024-10-28T20:35:43.296587Z",
"shell.execute_reply.started": "2024-10-28T20:35:41.836772Z",
"shell.execute_reply": "2024-10-28T20:35:43.295751Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## Model Training",
"metadata": {}
},
{
"cell_type": "code",
"source": "# Esegui il training\nhistory = model.fit(\n x=train_data,\n y=train_targets,\n validation_data=(val_data, val_targets),\n epochs=150,\n batch_size=64,\n callbacks=callbacks,\n verbose=1,\n shuffle=True\n)",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T20:36:16.203049Z",
"iopub.execute_input": "2024-10-28T20:36:16.203987Z",
"iopub.status.idle": "2024-10-28T21:38:49.845072Z",
"shell.execute_reply.started": "2024-10-28T20:36:16.203943Z",
"shell.execute_reply": "2024-10-28T21:38:49.844267Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "# Per denormalizzare e calcolare l'errore reale\ndef calculate_real_error(model, test_data, test_targets, scaler_y):\n # Fare predizioni\n predictions = model.predict(test_data)\n \n # Denormalizzare predizioni e target\n predictions_real = scaler_y.inverse_transform(predictions)\n targets_real = scaler_y.inverse_transform(test_targets)\n \n # Calcolare errore percentuale per ogni target\n percentage_errors = []\n absolute_errors = []\n \n for i in range(predictions_real.shape[1]):\n mae = np.mean(np.abs(predictions_real[:, i] - targets_real[:, i]))\n mape = np.mean(np.abs((predictions_real[:, i] - targets_real[:, i]) / targets_real[:, i])) * 100\n percentage_errors.append(mape)\n absolute_errors.append(mae)\n \n # Stampa risultati per ogni target\n target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']\n \n print(\"\\nErrori per target:\")\n print(\"-\" * 50)\n for i, target in enumerate(target_names):\n print(f\"{target}:\")\n print(f\"MAE assoluto: {absolute_errors[i]:.2f}\")\n print(f\"Errore percentuale medio: {percentage_errors[i]:.2f}%\")\n print(f\"Precisione: {100 - percentage_errors[i]:.2f}%\")\n print(\"-\" * 50)\n \n return percentage_errors, absolute_errors\n\n# Calcola gli errori reali\npercentage_errors, absolute_errors = calculate_real_error(model, val_data, val_targets, scaler_y)",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T21:38:49.848936Z",
"iopub.execute_input": "2024-10-28T21:38:49.849275Z",
"iopub.status.idle": "2024-10-28T21:39:06.761178Z",
"shell.execute_reply.started": "2024-10-28T21:38:49.849237Z",
"shell.execute_reply": "2024-10-28T21:39:06.759965Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "def save_transformer_model(model, scaler_temporal, scaler_static, scaler_y, base_path='/kaggle/working/models/oli_transformer'):\n \"\"\"\n Salva il modello transformer e i suoi scaler.\n \n Parameters:\n -----------\n model : OliveOilTransformer\n Il modello transformer\n scaler_temporal : StandardScaler\n Scaler per i dati temporali\n scaler_static : StandardScaler\n Scaler per i dati statici\n scaler_y : StandardScaler\n Scaler per i target\n base_path : str\n Percorso base dove salvare il modello e gli scaler\n \"\"\"\n # Crea la cartella se non esiste\n os.makedirs(base_path, exist_ok=True)\n \n # Salva il modello\n model_path = os.path.join(base_path, 'olive_transformer.keras')\n model.save(model_path)\n \n # Salva gli scaler\n joblib.dump(scaler_temporal, os.path.join(base_path, 'scaler_temporal.joblib'))\n joblib.dump(scaler_static, os.path.join(base_path, 'scaler_static.joblib'))\n joblib.dump(scaler_y, os.path.join(base_path, 'scaler_y.joblib'))\n \n print(f\"Modello transformer e scaler salvati in: {base_path}\")\n\ndef load_transformer_model(base_path='/kaggle/working/models/oli_transformer'):\n \"\"\"\n Carica il modello transformer e i suoi scaler.\n \n Parameters:\n -----------\n base_path : str\n Percorso dove sono salvati il modello e gli scaler\n \n Returns:\n --------\n tuple\n (model, scaler_temporal, scaler_static, scaler_y)\n \"\"\"\n # Carica il modello\n model_path = os.path.join(base_path, 'olive_transformer.keras')\n model = tf.keras.models.load_model(model_path, custom_objects={\n 'WarmUpLearningRateSchedule': WarmUpLearningRateSchedule\n })\n \n # Carica gli scaler\n scaler_temporal = joblib.load(os.path.join(base_path, 'scaler_temporal.joblib'))\n scaler_static = joblib.load(os.path.join(base_path, 'scaler_static.joblib'))\n scaler_y = joblib.load(os.path.join(base_path, 'scaler_y.joblib'))\n \n print(f\"Modello transformer e scaler caricati da: {base_path}\")\n return model, scaler_temporal, scaler_static, scaler_y\n\n# Esempio di utilizzo:\n\n# Per salvare:\nsave_transformer_model(\n model=model,\n scaler_temporal=scaler_temporal,\n scaler_static=scaler_static,\n scaler_y=scaler_y,\n)\n\n# Per caricare:\n#model, scaler_temporal, scaler_static, scaler_y = load_transformer_model()\n",
"metadata": {
"execution": {
"iopub.status.busy": "2024-10-28T21:39:06.762613Z",
"iopub.execute_input": "2024-10-28T21:39:06.763155Z",
"iopub.status.idle": "2024-10-28T21:39:07.109425Z",
"shell.execute_reply.started": "2024-10-28T21:39:06.763103Z",
"shell.execute_reply": "2024-10-28T21:39:07.108352Z"
},
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "## 8. Conclusioni e Prossimi Passi\n\nIn questo notebook, abbiamo:\n1. Caricato e analizzato i dati meteorologici\n2. Simulato la produzione annuale di olive basata sui dati meteo\n3. Esplorato le relazioni tra variabili meteorologiche e produzione di olive\n4. Creato e valutato un modello di machine learning per prevedere la produzione\n5. Utilizzato ARIMA per fare previsioni meteo\n6. Previsto la produzione di olive per il prossimo anno\n\nProssimi passi:\n- Raccogliere dati reali sulla produzione di olive per sostituire i dati simulati\n- Esplorare modelli più avanzati, come le reti neurali o i modelli di ensemble\n- Incorporare altri fattori che potrebbero influenzare la produzione, come le pratiche agricole o l'età degli alberi\n- Sviluppare una dashboard interattiva basata su questo modello",
"metadata": {}
}
]
}
-36
View File
@@ -1,36 +0,0 @@
{
"oliveto": {
"hectares": 10,
"varieties": [
{
"variety": "Nocellara dell'Etna",
"technique": "Tradizionale",
"percentage": 70
},
{
"variety": "Frantoio",
"technique": "Tradizionale",
"percentage": 30
}
]
},
"costs": {
"fixed": {
"ammortamento": 2000,
"assicurazione": 500,
"manutenzione": 800
},
"variable": {
"raccolta": 0.35,
"potatura": 600,
"fertilizzanti": 400
},
"transformation": {
"molitura": 0.15,
"stoccaggio": 0.2,
"bottiglia": 1.2,
"etichettatura": 0.3
},
"selling_price": 12
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
View File
File renamed without changes.
Binary file not shown.
+179
View File
@@ -0,0 +1,179 @@
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
import plotly.graph_objects as go
from plotly.subplots import make_subplots
class EnvironmentalSimulator:
def __init__(self):
# Parametri base per la crescita delle olive
self.optimal_temp_range = (15, 25) # °C
self.optimal_humidity = 60 # %
self.optimal_rainfall = 50 # mm/mese
self.optimal_radiation = 250 # W/m²
# Fasi fenologiche dell'olivo
self.growth_phases = {
'Dormienza': {'duration': 60, 'sensitivity': 0.3},
'Germogliamento': {'duration': 30, 'sensitivity': 0.7},
'Fioritura': {'duration': 30, 'sensitivity': 1.0},
'Allegagione': {'duration': 45, 'sensitivity': 0.8},
'Sviluppo Frutti': {'duration': 90, 'sensitivity': 0.6},
'Maturazione': {'duration': 60, 'sensitivity': 0.5}
}
def calculate_stress_index(self, temp, humidity, rainfall, radiation):
"""Calcola l'indice di stress ambientale"""
# Stress temperatura
temp_avg = np.mean(temp)
temp_stress = abs(temp_avg - np.mean(self.optimal_temp_range)) / 10
# Stress idrico
humidity_stress = abs(humidity - self.optimal_humidity) / 100
rainfall_stress = abs(rainfall - self.optimal_rainfall) / self.optimal_rainfall
# Stress radiazione
radiation_stress = abs(radiation - self.optimal_radiation) / self.optimal_radiation
# Indice di stress composito
stress_index = (temp_stress * 0.4 +
humidity_stress * 0.2 +
rainfall_stress * 0.2 +
radiation_stress * 0.2)
return min(1.0, stress_index)
def simulate_growth(self, temp_range, humidity, rainfall, radiation, days=365):
"""Simula la crescita dell'olivo nel tempo"""
results = []
current_date = datetime.now()
for day in range(days):
# Calcola la fase corrente
day_of_year = (current_date + timedelta(days=day)).timetuple().tm_yday
phase = self.get_growth_phase(day_of_year)
# Simula temperatura giornaliera
temp = np.random.uniform(temp_range[0], temp_range[1])
# Calcola stress giornaliero
stress = self.calculate_stress_index(temp_range, humidity, rainfall, radiation)
# Calcola crescita giornaliera (0-100%)
growth_rate = self.calculate_growth_rate(phase, stress)
results.append({
'date': current_date + timedelta(days=day),
'phase': phase,
'temperature': temp,
'stress_index': stress,
'growth_rate': growth_rate
})
return pd.DataFrame(results)
def get_growth_phase(self, day_of_year):
"""Determina la fase di crescita in base al giorno dell'anno"""
total_days = 0
for phase, details in self.growth_phases.items():
total_days += details['duration']
if day_of_year % 365 <= total_days:
return phase
return list(self.growth_phases.keys())[0]
def calculate_growth_rate(self, phase, stress):
"""Calcola il tasso di crescita giornaliero"""
base_rate = self.growth_phases[phase]['sensitivity']
return base_rate * (1 - stress) * 100
def calculate_production_impact(self, stress_history):
"""Calcola l'impatto sulla produzione"""
base_production = 100 # kg/albero
stress_impact = np.mean(stress_history)
return base_production * (1 - stress_impact)
def create_growth_simulation_figure(sim_data: pd.DataFrame) -> go.Figure:
"""Crea il grafico della simulazione di crescita"""
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Aggiunge la linea di crescita
fig.add_trace(
go.Scatter(
x=sim_data['date'],
y=sim_data['growth_rate'],
name="Tasso di Crescita",
line=dict(color='#2E86C1', width=2)
),
secondary_y=False
)
# Aggiunge l'indice di stress
fig.add_trace(
go.Scatter(
x=sim_data['date'],
y=sim_data['stress_index'],
name="Indice di Stress",
line=dict(color='#E74C3C', width=2)
),
secondary_y=True
)
# Aggiungi indicatori delle fasi
for phase in sim_data['phase'].unique():
phase_data = sim_data[sim_data['phase'] == phase]
fig.add_trace(
go.Scatter(
x=[phase_data['date'].iloc[0]],
y=[0],
name=phase,
mode='markers+text',
text=[phase],
textposition='top center',
marker=dict(size=10)
),
secondary_y=False
)
# Configurazione layout
fig.update_layout(
title='Simulazione Crescita e Stress Ambientale',
xaxis_title='Data',
yaxis_title='Tasso di Crescita (%)',
yaxis2_title='Indice di Stress',
hovermode='x unified',
showlegend=True,
height=500
)
return fig
def create_production_impact_figure(sim_data: pd.DataFrame) -> go.Figure:
"""Crea il grafico dell'impatto sulla produzione"""
# Calcola medie mensili
monthly_data = sim_data.set_index('date').resample('M').mean()
fig = go.Figure()
# Aggiunge il grafico a barre della produzione stimata
fig.add_trace(
go.Bar(
x=monthly_data.index,
y=100 * (1 - monthly_data['stress_index']),
name='Produzione Stimata (%)',
marker_color='#27AE60'
)
)
# Configurazione layout
fig.update_layout(
title='Impatto Stimato sulla Produzione',
xaxis_title='Mese',
yaxis_title='Produzione Stimata (%)',
hovermode='x unified',
showlegend=True,
height=500
)
return fig
-441
View File
@@ -1,441 +0,0 @@
import os
import json
import numpy as np
import pandas as pd
import joblib
import tensorflow as tf
from src.models.solar_models import create_uv_model, create_energy_model, create_radiation_model
from typing import Tuple, Optional
import datetime
def read_json_files(folder_path):
all_data = []
file_list = sorted(os.listdir(folder_path))
for filename in file_list:
if filename.endswith('.json'):
file_path = os.path.join(folder_path, filename)
try:
with open(file_path, 'r') as file:
data = json.load(file)
all_data.extend(data['days'])
except Exception as e:
print(f"Error processing file '{filename}': {str(e)}")
return all_data
def save_single_model_and_scalers(model, model_name, scalers=None, base_path='./kaggle/working/models'):
"""
Salva un singolo modello con tutti i suoi artefatti associati e multipli scaler.
Parameters:
-----------
model : keras.Model
Il modello da salvare
model_name : str
Nome del modello (es. 'solarradiation', 'solarenergy', 'uvindex')
scalers : dict, optional
Dizionario degli scaler associati al modello (es. {'X': x_scaler, 'y': y_scaler})
base_path : str
Percorso base dove salvare il modello
"""
if isinstance(base_path, list):
base_path = './kaggle/working/models'
# Crea la cartella base se non esiste
os.makedirs(base_path, exist_ok=True)
# Crea la sottocartella per il modello specifico
model_path = os.path.join(base_path, model_name)
os.makedirs(model_path, exist_ok=True)
try:
print(f"\nSalvataggio modello {model_name}...")
# 1. Salva il modello completo
model_file = os.path.join(model_path, 'model.keras')
model.save(model_file, save_format='keras')
print(f"- Salvato modello completo: {model_file}")
# 2. Salva i pesi separatamente
weights_path = os.path.join(model_path, 'weights')
os.makedirs(weights_path, exist_ok=True)
weight_file = os.path.join(weights_path, 'weights')
model.save_weights(weight_file)
print(f"- Salvati pesi: {weight_file}")
# 3. Salva il plot del modello
plot_path = os.path.join(model_path, f'{model_name}_architecture.png')
tf.keras.utils.plot_model(
model,
to_file=plot_path,
show_shapes=True,
show_layer_names=True,
rankdir='TB',
expand_nested=True,
dpi=150
)
print(f"- Salvato plot architettura: {plot_path}")
# 4. Salva il summary del modello
summary_path = os.path.join(model_path, f'{model_name}_summary.txt')
with open(summary_path, 'w') as f:
model.summary(print_fn=lambda x: f.write(x + '\n'))
print(f"- Salvato summary modello: {summary_path}")
# 5. Salva gli scaler se forniti
if scalers is not None:
scaler_path = os.path.join(model_path, 'scalers')
os.makedirs(scaler_path, exist_ok=True)
for scaler_name, scaler in scalers.items():
scaler_file = os.path.join(scaler_path, f'{scaler_name}_scaler.joblib')
joblib.dump(scaler, scaler_file)
print(f"- Salvato scaler {scaler_name}: {scaler_file}")
# 6. Salva la configurazione del modello
model_config = {
'has_solar_params': True if model_name == 'solarradiation' else False,
'scalers': list(scalers.keys()) if scalers else []
}
config_path = os.path.join(model_path, 'model_config.joblib')
joblib.dump(model_config, config_path)
print(f"- Salvata configurazione: {config_path}")
# 7. Crea un README specifico per il modello
readme_path = os.path.join(model_path, 'README.txt')
with open(readme_path, 'w') as f:
f.write(f"{model_name.upper()} Model Artifacts\n")
f.write("=" * (len(model_name) + 15) + "\n\n")
f.write("Directory structure:\n")
f.write("- model.keras: Complete model\n")
f.write("- weights/: Model weights\n")
f.write(f"- {model_name}_architecture.png: Visual representation of model architecture\n")
f.write(f"- {model_name}_summary.txt: Detailed model summary\n")
f.write("- model_config.joblib: Model configuration\n")
if scalers:
f.write("- scalers/: Directory containing model scalers\n")
for scaler_name in scalers.keys():
f.write(f" - {scaler_name}_scaler.joblib: {scaler_name} scaler\n")
print(f"\nTutti gli artefatti per {model_name} salvati in: {model_path}")
print(f"Consulta {readme_path} per i dettagli sulla struttura")
except Exception as e:
print(f"Errore nel salvataggio degli artefatti per {model_name}: {str(e)}")
raise
return model_path
def load_single_model_and_scalers(model_name, base_path='./kaggle/working/models'):
"""
Carica un singolo modello con tutti i suoi artefatti e scaler associati.
Parameters:
-----------
model_name : str
Nome del modello da caricare (es. 'solarradiation', 'solarenergy', 'uvindex')
base_path : str
Percorso base dove sono salvati i modelli
Returns:
--------
tuple
(model, scalers, model_config)
"""
model_path = os.path.join(base_path, model_name)
if not os.path.exists(model_path):
print(f"Directory del modello non trovata: {model_path}")
return None, None, None
try:
print(f"\nCaricamento modello {model_name}...")
# 1. Carica la configurazione del modello
config_path = os.path.join(model_path, 'model_config.joblib')
try:
model_config = joblib.load(config_path)
print("- Configurazione modello caricata")
except:
print("! Configurazione modello non trovata, usando configurazione di default")
model_config = {
'has_solar_params': True if model_name == 'solarradiation' else False,
'scalers': ['X', 'y']
}
# 2. Carica il modello
try:
# Prima prova a caricare il modello completo
model_file = os.path.join(model_path, 'model.keras')
model = tf.keras.models.load_model(model_file)
print(f"- Modello caricato da: {model_file}")
# Verifica i pesi
weights_path = os.path.join(model_path, 'weights', 'weights')
if os.path.exists(weights_path + '.index'):
model.load_weights(weights_path)
print("- Pesi verificati con successo")
except Exception as e:
print(f"! Errore nel caricamento del modello: {str(e)}")
print("Tentativo di ricostruzione del modello...")
try:
# Ricostruzione del modello
if model_name == 'solarradiation':
model = create_radiation_model(input_shape=(24, 8))
elif model_name == 'solarenergy':
model = create_energy_model(input_shape=(24, 8))
elif model_name == 'uvindex':
model = create_uv_model(input_shape=(24, 8))
else:
raise ValueError(f"Tipo di modello non riconosciuto: {model_name}")
# Carica i pesi
model.load_weights(weights_path)
print("- Modello ricostruito dai pesi con successo")
except Exception as e:
print(f"! Errore nella ricostruzione del modello: {str(e)}")
return None, None, None
# 3. Carica gli scaler
scalers = {}
scaler_path = os.path.join(model_path, 'scalers')
if os.path.exists(scaler_path):
print("\nCaricamento scaler:")
for scaler_file in os.listdir(scaler_path):
if scaler_file.endswith('_scaler.joblib'):
scaler_name = scaler_file.replace('_scaler.joblib', '')
scaler_file_path = os.path.join(scaler_path, scaler_file)
try:
scalers[scaler_name] = joblib.load(scaler_file_path)
print(f"- Caricato scaler {scaler_name}")
except Exception as e:
print(f"! Errore nel caricamento dello scaler {scaler_name}: {str(e)}")
else:
print("! Directory degli scaler non trovata")
# 4. Verifica integrità del modello
try:
# Verifica che il modello possa fare predizioni
if model_name == 'solarradiation':
dummy_input = [np.zeros((1, 24, 8)), np.zeros((1, 3))]
else:
dummy_input = np.zeros((1, 24, 8))
model.predict(dummy_input, verbose=0)
print("\n✓ Verifica integrità modello completata con successo")
except Exception as e:
print(f"\n! Attenzione: il modello potrebbe non funzionare correttamente: {str(e)}")
# 5. Carica e verifica il summary del modello
summary_path = os.path.join(model_path, f'{model_name}_summary.txt')
if os.path.exists(summary_path):
print("\nSummary del modello disponibile in:", summary_path)
# 6. Verifica il plot dell'architettura
plot_path = os.path.join(model_path, f'{model_name}_architecture.png')
if os.path.exists(plot_path):
print("Plot dell'architettura disponibile in:", plot_path)
print(f"\nCaricamento di {model_name} completato con successo!")
return model, scalers, model_config
except Exception as e:
print(f"\nErrore critico nel caricamento del modello {model_name}: {str(e)}")
return None, None, None
def load_weather_data(
data_path: str,
start_year: Optional[int] = None,
end_year: Optional[int] = None
) -> pd.DataFrame:
"""
Carica e preprocessa i dati meteorologici da file JSON o Parquet.
Parameters
----------
data_path : str
Percorso al file dei dati (può essere .json o .parquet)
start_year : int, optional
Anno di inizio per filtrare i dati
end_year : int, optional
Anno di fine per filtrare i dati
Returns
-------
pd.DataFrame
DataFrame contenente i dati meteo preprocessati
Examples
--------
>>> weather_data = load_weather_data('./data/weather_data.parquet', start_year=2010)
"""
try:
# Determina il tipo di file e carica di conseguenza
if data_path.endswith('.parquet'):
weather_data = pd.read_parquet(data_path)
elif data_path.endswith('.json'):
# Se è un file JSON, prima lo convertiamo in DataFrame
with open(data_path, 'r') as f:
raw_data = json.load(f)
weather_data = create_weather_dataset(raw_data)
else:
raise ValueError(f"Formato file non supportato: {data_path}")
# Converti la colonna datetime
weather_data['datetime'] = pd.to_datetime(weather_data['datetime'], errors='coerce')
# Filtra per anno se specificato
if start_year is not None:
weather_data = weather_data[weather_data['datetime'].dt.year >= start_year]
if end_year is not None:
weather_data = weather_data[weather_data['datetime'].dt.year <= end_year]
# Aggiungi colonne di data
weather_data['date'] = weather_data['datetime'].dt.date
weather_data['year'] = weather_data['datetime'].dt.year
weather_data['month'] = weather_data['datetime'].dt.month
weather_data['day'] = weather_data['datetime'].dt.day
# Rimuovi righe con datetime nullo
weather_data = weather_data.dropna(subset=['datetime'])
# Ordina per datetime
weather_data = weather_data.sort_values('datetime')
# Gestione valori mancanti nelle colonne principali
numeric_columns = weather_data.select_dtypes(include=[np.number]).columns
for col in numeric_columns:
if weather_data[col].isnull().any():
# Interpolazione lineare per i valori mancanti
weather_data[col] = weather_data[col].interpolate(method='linear')
# Rimuovi eventuali duplicati
weather_data = weather_data.drop_duplicates(subset=['datetime'])
# Verifica la completezza dei dati
print(f"Dati caricati dal {weather_data['datetime'].min()} al {weather_data['datetime'].max()}")
print(f"Numero totale di records: {len(weather_data)}")
return weather_data
except Exception as e:
print(f"Errore nel caricamento dei dati meteo: {str(e)}")
raise
def create_weather_dataset(raw_data: list) -> pd.DataFrame:
"""
Converte i dati JSON grezzi in un DataFrame strutturato.
Parameters
----------
raw_data : list
Lista di dizionari contenenti i dati meteo
Returns
-------
pd.DataFrame
DataFrame strutturato con i dati meteo
"""
dataset = []
seen_datetimes = set()
for day in raw_data:
date = day['datetime']
for hour in day['hours']:
datetime_str = f"{date} {hour['datetime']}"
# Verifica duplicati
if datetime_str in seen_datetimes:
continue
seen_datetimes.add(datetime_str)
# Gestione preciptype
if isinstance(hour['preciptype'], list):
preciptype = "__".join(hour['preciptype'])
else:
preciptype = hour['preciptype'] if hour['preciptype'] else ""
# Gestione conditions
conditions = hour['conditions'].replace(', ', '__').replace(' ', '_').lower()
# Crea la riga
row = {
'datetime': datetime_str,
'temp': hour['temp'],
'feelslike': hour['feelslike'],
'humidity': hour['humidity'],
'dew': hour['dew'],
'precip': hour['precip'],
'snow': hour['snow'],
'preciptype': preciptype.lower(),
'windspeed': hour['windspeed'],
'winddir': hour['winddir'],
'pressure': hour['pressure'],
'cloudcover': hour['cloudcover'],
'visibility': hour['visibility'],
'solarradiation': hour['solarradiation'],
'solarenergy': hour['solarenergy'],
'uvindex': hour['uvindex'],
'conditions': conditions,
'tempmax': day['tempmax'],
'tempmin': day['tempmin'],
'precipprob': day['precipprob'],
'precipcover': day['precipcover']
}
dataset.append(row)
# Ordina per datetime
dataset.sort(key=lambda x: datetime.strptime(x['datetime'], "%Y-%m-%d %H:%M:%S"))
return pd.DataFrame(dataset)
def load_olive_varieties(
data_path: str,
add_water_features: bool = True
) -> pd.DataFrame:
"""
Carica e preprocessa i dati delle varietà di olive.
Parameters
----------
data_path : str
Percorso al file dei dati
add_water_features : bool
Se True, aggiunge feature relative al consumo d'acqua
Returns
-------
pd.DataFrame
DataFrame contenente i dati delle varietà di olive
"""
try:
if data_path.endswith('.csv'):
olive_varieties = pd.read_csv(data_path)
elif data_path.endswith('.parquet'):
olive_varieties = pd.read_parquet(data_path)
else:
raise ValueError(f"Formato file non supportato: {data_path}")
# Se richiesto, aggiungi feature sul consumo d'acqua
if add_water_features and 'Fabbisogno Acqua Primavera (m³/ettaro)' not in olive_varieties.columns:
from src.data.data_simulator import add_olive_water_consumption_correlation
olive_varieties = add_olive_water_consumption_correlation(olive_varieties)
print(f"Dati varietà olive caricati: {len(olive_varieties)} varietà")
return olive_varieties
except Exception as e:
print(f"Errore nel caricamento dei dati delle varietà: {str(e)}")
raise
-324
View File
@@ -1,324 +0,0 @@
# src/data/data_processor.py
import pandas as pd
import numpy as np
import tensorflow as tf
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.model_selection import train_test_split
import joblib
import os
from typing import Tuple, List, Dict, Optional, Union
def preprocess_weather_data(weather_df: pd.DataFrame) -> pd.DataFrame:
"""
Calcola statistiche mensili per ogni anno dai dati meteo.
Parameters
----------
weather_df : pd.DataFrame
DataFrame contenente i dati meteorologici
Returns
-------
pd.DataFrame
DataFrame con statistiche mensili
"""
# Calcola statistiche mensili per ogni anno
monthly_weather = weather_df.groupby(['year', 'month']).agg({
'temp': ['mean', 'min', 'max'],
'humidity': 'mean',
'precip': 'sum',
'windspeed': 'mean',
'cloudcover': 'mean',
'solarradiation': 'sum',
'solarenergy': 'sum',
'uvindex': 'max'
}).reset_index()
# Rinomina le colonne
monthly_weather.columns = ['year', 'month'] + [
f'{col[0]}_{col[1]}' for col in monthly_weather.columns[2:]
]
return monthly_weather
def create_sequences(timesteps: int, X: np.ndarray, y: Optional[np.ndarray] = None) -> Union[
np.ndarray, Tuple[np.ndarray, np.ndarray]]:
"""
Crea sequenze temporali dai dati.
Parameters
----------
timesteps : int
Numero di timestep per ogni sequenza
X : array-like
Dati di input
y : array-like, optional
Target values
Returns
-------
tuple o array
Se y è fornito: (X_sequences, y_sequences)
Se y è None: X_sequences
"""
Xs = []
for i in range(len(X) - timesteps):
Xs.append(X[i:i + timesteps])
if y is not None:
ys = []
for i in range(len(X) - timesteps):
ys.append(y[i + timesteps])
return np.array(Xs), np.array(ys)
return np.array(Xs)
def prepare_solar_data(weather_data: pd.DataFrame, features: List[str]) -> Tuple:
"""
Prepara i dati per i modelli solari.
Parameters
----------
weather_data : pd.DataFrame
DataFrame contenente i dati meteorologici
features : list
Lista delle feature da utilizzare
Returns
-------
tuple
(X_scaled, scaler_X, y_scaled, scaler_y, data_after_2010)
"""
# Aggiunge le caratteristiche temporali
weather_data = add_advanced_features(weather_data)
weather_data = pd.get_dummies(weather_data, columns=['season', 'time_period'], drop_first=True)
# Filtra dati dopo 2010
data_after_2010 = weather_data[weather_data['year'] >= 2010].copy()
data_after_2010 = data_after_2010.sort_values('datetime')
data_after_2010.set_index('datetime', inplace=True)
# Interpola valori mancanti
target_variables = ['solarradiation', 'solarenergy', 'uvindex']
for column in target_variables:
data_after_2010[column] = data_after_2010[column].interpolate(method='time')
# Rimuovi righe con valori mancanti
data_after_2010.dropna(subset=features + target_variables, inplace=True)
# Prepara X e y
X = data_after_2010[features].values
y = data_after_2010[target_variables].values
# Normalizza features
scaler_X = MinMaxScaler()
X_scaled = scaler_X.fit_transform(X)
scaler_y = MinMaxScaler()
y_scaled = scaler_y.fit_transform(y)
return X_scaled, scaler_X, y_scaled, scaler_y, data_after_2010
def prepare_transformer_data(df: pd.DataFrame, olive_varieties_df: pd.DataFrame) -> Tuple:
"""
Prepara i dati per il modello transformer.
"""
# Copia del DataFrame
df = df.copy()
# Ordina per zona e anno
df = df.sort_values(['zone', 'year'])
# Feature definition
temporal_features = ['temp_mean', 'precip_sum', 'solar_energy_sum']
static_features = ['ha']
target_features = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']
# Get clean varieties
all_varieties = olive_varieties_df['Varietà di Olive'].unique()
varieties = [clean_column_name(variety) for variety in all_varieties]
# Variety features structure
variety_features = [
'tech', 'pct', 'prod_t_ha', 'oil_prod_t_ha', 'oil_prod_l_ha',
'min_yield_pct', 'max_yield_pct', 'min_oil_prod_l_ha', 'max_oil_prod_l_ha',
'avg_oil_prod_l_ha', 'l_per_t', 'min_l_per_t', 'max_l_per_t', 'avg_l_per_t'
]
# Prepare columns
new_columns = {}
# Prepare features for each variety
for variety in varieties:
for feature in variety_features:
col_name = f"{variety}_{feature}"
if col_name in df.columns:
if feature != 'tech':
static_features.append(col_name)
# Binary features for cultivation techniques
for technique in ['tradizionale', 'intensiva', 'superintensiva']:
col_name = f"{variety}_{technique}"
new_columns[col_name] = df[f"{variety}_tech"].notna() & (
df[f"{variety}_tech"].str.lower() == technique
).fillna(False)
static_features.append(col_name)
# Add all new columns at once
new_df = pd.concat([df] + [pd.Series(v, name=k) for k, v in new_columns.items()], axis=1)
# Sort by zone and year
df_sorted = new_df.sort_values(['zone', 'year'])
# Window size definition
window_size = 41
# Prepare lists for data collection
temporal_sequences = []
static_features_list = []
targets_list = []
# Process data by zone
for zone in df_sorted['zone'].unique():
zone_data = df_sorted[df_sorted['zone'] == zone].reset_index(drop=True)
if len(zone_data) >= window_size:
for i in range(len(zone_data) - window_size + 1):
temporal_window = zone_data.iloc[i:i + window_size][temporal_features].values
if not np.isnan(temporal_window).any():
temporal_sequences.append(temporal_window)
static_features_list.append(zone_data.iloc[i + window_size - 1][static_features].values)
targets_list.append(zone_data.iloc[i + window_size - 1][target_features].values)
# Convert to numpy arrays
X_temporal = np.array(temporal_sequences)
X_static = np.array(static_features_list)
y = np.array(targets_list)
# Split data
indices = np.random.permutation(len(X_temporal))
train_idx = int(len(indices) * 0.65)
val_idx = int(len(indices) * 0.85)
train_indices = indices[:train_idx]
val_indices = indices[train_idx:val_idx]
test_indices = indices[val_idx:]
# Split datasets
X_temporal_train = X_temporal[train_indices]
X_temporal_val = X_temporal[val_indices]
X_temporal_test = X_temporal[test_indices]
X_static_train = X_static[train_indices]
X_static_val = X_static[val_indices]
X_static_test = X_static[test_indices]
y_train = y[train_indices]
y_val = y[val_indices]
y_test = y[test_indices]
# Standardization
scaler_temporal = StandardScaler()
scaler_static = StandardScaler()
scaler_y = StandardScaler()
# Apply standardization
X_temporal_train = scaler_temporal.fit_transform(X_temporal_train.reshape(-1, len(temporal_features))).reshape(
X_temporal_train.shape)
X_temporal_val = scaler_temporal.transform(X_temporal_val.reshape(-1, len(temporal_features))).reshape(
X_temporal_val.shape)
X_temporal_test = scaler_temporal.transform(X_temporal_test.reshape(-1, len(temporal_features))).reshape(
X_temporal_test.shape)
X_static_train = scaler_static.fit_transform(X_static_train)
X_static_val = scaler_static.transform(X_static_val)
X_static_test = scaler_static.transform(X_static_test)
y_train = scaler_y.fit_transform(y_train)
y_val = scaler_y.transform(y_val)
y_test = scaler_y.transform(y_test)
# Prepare input dictionaries
train_data = {'temporal': X_temporal_train, 'static': X_static_train}
val_data = {'temporal': X_temporal_val, 'static': X_static_val}
test_data = {'temporal': X_temporal_test, 'static': X_static_test}
# Save scalers
base_path = './kaggle/working/models/oil_transformer/'
os.makedirs(base_path, exist_ok=True)
joblib.dump(scaler_temporal, os.path.join(base_path, 'scaler_temporal.joblib'))
joblib.dump(scaler_static, os.path.join(base_path, 'scaler_static.joblib'))
joblib.dump(scaler_y, os.path.join(base_path, 'scaler_y.joblib'))
return (train_data, y_train), (val_data, y_val), (test_data, y_test), (scaler_temporal, scaler_static, scaler_y)
def encode_techniques(df: pd.DataFrame,
mapping_path: str = './kaggle/working/models/technique_mapping.joblib') -> pd.DataFrame:
"""
Codifica le tecniche di coltivazione usando un mapping salvato.
Parameters
----------
df : pd.DataFrame
DataFrame contenente le colonne delle tecniche
mapping_path : str
Percorso al file di mapping
Returns
-------
pd.DataFrame
DataFrame con le tecniche codificate
"""
if not os.path.exists(mapping_path):
raise FileNotFoundError(f"Mapping not found at {mapping_path}. Run create_technique_mapping first.")
technique_mapping = joblib.load(mapping_path)
# Trova tutte le colonne delle tecniche
tech_columns = [col for col in df.columns if col.endswith('_tech')]
# Applica il mapping a tutte le colonne delle tecniche
for col in tech_columns:
df[col] = df[col].str.lower().map(technique_mapping).fillna(0).astype(int)
return df
def decode_techniques(df: pd.DataFrame,
mapping_path: str = './kaggle/working/models/technique_mapping.joblib') -> pd.DataFrame:
"""
Decodifica le tecniche di coltivazione usando un mapping salvato.
Parameters
----------
df : pd.DataFrame
DataFrame contenente le colonne delle tecniche codificate
mapping_path : str
Percorso al file di mapping
Returns
-------
pd.DataFrame
DataFrame con le tecniche decodificate
"""
if not os.path.exists(mapping_path):
raise FileNotFoundError(f"Mapping not found at {mapping_path}")
technique_mapping = joblib.load(mapping_path)
reverse_mapping = {v: k for k, v in technique_mapping.items()}
reverse_mapping[0] = '' # Mapping per 0 a stringa vuota
# Trova tutte le colonne delle tecniche
tech_columns = [col for col in df.columns if col.endswith('_tech')]
# Applica il reverse mapping
for col in tech_columns:
df[col] = df[col].map(reverse_mapping)
return df
-332
View File
@@ -1,332 +0,0 @@
import pandas as pd
import numpy as np
from typing import Dict
from src.utils.helpers import clean_column_name
def calculate_weather_effect(row: pd.Series, optimal_temp: float) -> float:
"""
Calcola l'effetto delle condizioni meteorologiche sulla produzione.
Parameters
----------
row : pd.Series
Serie contenente i dati meteorologici
optimal_temp : float
Temperatura ottimale per la varietà
Returns
-------
float
Effetto combinato delle condizioni meteo
"""
# Effetti base
temp_effect = -0.1 * (row['temp_mean'] - optimal_temp) ** 2
rain_effect = -0.05 * (row['precip_sum'] - 600) ** 2 / 10000
sun_effect = 0.1 * row['solarenergy_sum'] / 1000
# Fattori di scala basati sulla fase di crescita
if row['growth_phase'] == 'dormancy':
temp_scale = 0.5
rain_scale = 0.2
sun_scale = 0.1
elif row['growth_phase'] == 'flowering':
temp_scale = 2.0
rain_scale = 1.5
sun_scale = 1.0
elif row['growth_phase'] == 'fruit_set':
temp_scale = 1.5
rain_scale = 1.0
sun_scale = 0.8
else: # ripening
temp_scale = 1.0
rain_scale = 0.5
sun_scale = 1.2
# Calcolo dell'effetto combinato
combined_effect = (
temp_scale * temp_effect +
rain_scale * rain_effect +
sun_scale * sun_effect
)
# Aggiustamenti specifici per fase
if row['growth_phase'] == 'flowering':
combined_effect -= 0.5 * max(0, row['precip_sum'] - 50) # Penalità per pioggia eccessiva
elif row['growth_phase'] == 'fruit_set':
combined_effect += 0.3 * max(0, row['temp_mean'] - (optimal_temp + 5)) # Bonus temperature alte
return combined_effect
def calculate_water_need(weather_data: pd.Series, base_need: float, optimal_temp: float) -> float:
"""
Calcola il fabbisogno idrico basato su temperatura e precipitazioni.
Parameters
----------
weather_data : pd.Series
Serie contenente i dati meteorologici
base_need : float
Fabbisogno idrico base
optimal_temp : float
Temperatura ottimale per la varietà
Returns
-------
float
Fabbisogno idrico calcolato
"""
temp_factor = 1 + 0.05 * (weather_data['temp_mean'] - optimal_temp)
rain_factor = 1 - 0.001 * weather_data['precip_sum']
return base_need * temp_factor * rain_factor
def add_olive_water_consumption_correlation(dataset: pd.DataFrame) -> pd.DataFrame:
"""
Aggiunge dati correlati al consumo d'acqua per ogni varietà di oliva.
Parameters
----------
dataset : pd.DataFrame
DataFrame contenente i dati delle varietà di olive
Returns
-------
pd.DataFrame
DataFrame con dati aggiuntivi sul consumo d'acqua
"""
# Dati simulati per il fabbisogno d'acqua e correlazione con temperatura
fabbisogno_acqua = {
"Nocellara dell'Etna": {"Primavera": 1200, "Estate": 2000, "Autunno": 1000, "Inverno": 500,
"Temperatura Ottimale": 18, "Resistenza": "Media"},
"Leccino": {"Primavera": 1000, "Estate": 1800, "Autunno": 800, "Inverno": 400, "Temperatura Ottimale": 20,
"Resistenza": "Alta"},
"Frantoio": {"Primavera": 1100, "Estate": 1900, "Autunno": 900, "Inverno": 450, "Temperatura Ottimale": 19,
"Resistenza": "Alta"},
"Coratina": {"Primavera": 1300, "Estate": 2200, "Autunno": 1100, "Inverno": 550, "Temperatura Ottimale": 17,
"Resistenza": "Media"},
"Moraiolo": {"Primavera": 1150, "Estate": 2100, "Autunno": 900, "Inverno": 480, "Temperatura Ottimale": 18,
"Resistenza": "Media"},
"Pendolino": {"Primavera": 1050, "Estate": 1850, "Autunno": 850, "Inverno": 430, "Temperatura Ottimale": 20,
"Resistenza": "Alta"},
"Taggiasca": {"Primavera": 1000, "Estate": 1750, "Autunno": 800, "Inverno": 400, "Temperatura Ottimale": 19,
"Resistenza": "Alta"},
"Canino": {"Primavera": 1100, "Estate": 1900, "Autunno": 900, "Inverno": 450, "Temperatura Ottimale": 18,
"Resistenza": "Media"},
"Itrana": {"Primavera": 1200, "Estate": 2000, "Autunno": 1000, "Inverno": 500, "Temperatura Ottimale": 17,
"Resistenza": "Media"},
"Ogliarola": {"Primavera": 1150, "Estate": 1950, "Autunno": 900, "Inverno": 480, "Temperatura Ottimale": 18,
"Resistenza": "Media"},
"Biancolilla": {"Primavera": 1050, "Estate": 1800, "Autunno": 850, "Inverno": 430, "Temperatura Ottimale": 19,
"Resistenza": "Alta"}
}
# Calcola fabbisogno idrico annuale
for varieta in fabbisogno_acqua:
fabbisogno_acqua[varieta]["Annuale"] = sum(
fabbisogno_acqua[varieta][stagione]
for stagione in ["Primavera", "Estate", "Autunno", "Inverno"]
)
# Aggiungi colonne al dataset
dataset["Fabbisogno Acqua Primavera (m³/ettaro)"] = dataset["Varietà di Olive"].apply(
lambda x: fabbisogno_acqua[x]["Primavera"])
dataset["Fabbisogno Acqua Estate (m³/ettaro)"] = dataset["Varietà di Olive"].apply(
lambda x: fabbisogno_acqua[x]["Estate"])
dataset["Fabbisogno Acqua Autunno (m³/ettaro)"] = dataset["Varietà di Olive"].apply(
lambda x: fabbisogno_acqua[x]["Autunno"])
dataset["Fabbisogno Acqua Inverno (m³/ettaro)"] = dataset["Varietà di Olive"].apply(
lambda x: fabbisogno_acqua[x]["Inverno"])
dataset["Fabbisogno Idrico Annuale (m³/ettaro)"] = dataset["Varietà di Olive"].apply(
lambda x: fabbisogno_acqua[x]["Annuale"])
dataset["Temperatura Ottimale"] = dataset["Varietà di Olive"].apply(
lambda x: fabbisogno_acqua[x]["Temperatura Ottimale"])
dataset["Resistenza alla Siccità"] = dataset["Varietà di Olive"].apply(
lambda x: fabbisogno_acqua[x]["Resistenza"])
return dataset
def simulate_zone(base_weather: pd.DataFrame,
olive_varieties: pd.DataFrame,
year: int,
zone: int,
all_varieties: np.ndarray,
variety_techniques: Dict) -> Dict:
"""
Simula la produzione di olive per una singola zona.
Parameters
----------
base_weather : pd.DataFrame
DataFrame contenente i dati meteo di base
olive_varieties : pd.DataFrame
DataFrame con le informazioni sulle varietà
year : int
Anno della simulazione
zone : int
ID della zona
all_varieties : np.ndarray
Array con tutte le varietà disponibili
variety_techniques : Dict
Dizionario con le tecniche disponibili per ogni varietà
Returns
-------
Dict
Dizionario con i risultati della simulazione
"""
# Crea una copia dei dati meteo per questa zona
zone_weather = base_weather.copy()
# Genera variazioni meteorologiche specifiche per questa zona
zone_weather['temp_mean'] *= np.random.uniform(0.95, 1.05, len(zone_weather))
zone_weather['precip_sum'] *= np.random.uniform(0.9, 1.1, len(zone_weather))
zone_weather['solarenergy_sum'] *= np.random.uniform(0.95, 1.05, len(zone_weather))
# Genera caratteristiche specifiche della zona
num_varieties = np.random.randint(1, 4) # 1-3 varietà per zona
selected_varieties = np.random.choice(all_varieties, size=num_varieties, replace=False)
hectares = np.random.uniform(1, 10) # Dimensione del terreno
percentages = np.random.dirichlet(np.ones(num_varieties)) # Distribuzione delle varietà
# Inizializzazione contatori annuali
annual_production = 0
annual_min_oil = 0
annual_max_oil = 0
annual_avg_oil = 0
annual_water_need = 0
# Inizializzazione dizionario dati varietà
variety_data = {clean_column_name(variety): {
'tech': '',
'pct': 0,
'prod_t_ha': 0,
'oil_prod_t_ha': 0,
'oil_prod_l_ha': 0,
'min_yield_pct': 0,
'max_yield_pct': 0,
'min_oil_prod_l_ha': 0,
'max_oil_prod_l_ha': 0,
'avg_oil_prod_l_ha': 0,
'l_per_t': 0,
'min_l_per_t': 0,
'max_l_per_t': 0,
'avg_l_per_t': 0,
'olive_prod': 0,
'min_oil_prod': 0,
'max_oil_prod': 0,
'avg_oil_prod': 0,
'water_need': 0
} for variety in all_varieties}
# Simula produzione per ogni varietà selezionata
for i, variety in enumerate(selected_varieties):
# Seleziona tecnica di coltivazione casuale per questa varietà
technique = np.random.choice(variety_techniques[variety])
percentage = percentages[i]
# Ottieni informazioni specifiche della varietà
variety_info = olive_varieties[
(olive_varieties['Varietà di Olive'] == variety) &
(olive_varieties['Tecnica di Coltivazione'] == technique)
].iloc[0]
# Calcola produzione base con variabilità
base_production = variety_info['Produzione (tonnellate/ettaro)'] * 1000 * percentage * hectares / 12
base_production *= np.random.uniform(0.9, 1.1)
# Calcola effetti meteo sulla produzione
weather_effect = zone_weather.apply(
lambda row: calculate_weather_effect(row, variety_info['Temperatura Ottimale']),
axis=1
)
monthly_production = base_production * (1 + weather_effect / 10000)
monthly_production *= np.random.uniform(0.95, 1.05, len(zone_weather))
# Calcola produzione annuale per questa varietà
annual_variety_production = monthly_production.sum()
# Calcola rese di olio con variabilità
min_yield_factor = np.random.uniform(0.95, 1.05)
max_yield_factor = np.random.uniform(0.95, 1.05)
avg_yield_factor = (min_yield_factor + max_yield_factor) / 2
min_oil_production = annual_variety_production * variety_info[
'Min Litri per Tonnellata'] / 1000 * min_yield_factor
max_oil_production = annual_variety_production * variety_info[
'Max Litri per Tonnellata'] / 1000 * max_yield_factor
avg_oil_production = annual_variety_production * variety_info[
'Media Litri per Tonnellata'] / 1000 * avg_yield_factor
# Calcola fabbisogno idrico
base_water_need = (
variety_info['Fabbisogno Acqua Primavera (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Estate (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Autunno (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Inverno (m³/ettaro)']
) / 4
monthly_water_need = zone_weather.apply(
lambda row: calculate_water_need(row, base_water_need, variety_info['Temperatura Ottimale']),
axis=1
)
monthly_water_need *= np.random.uniform(0.95, 1.05, len(monthly_water_need))
annual_variety_water_need = monthly_water_need.sum() * percentage * hectares
# Aggiorna totali annuali
annual_production += annual_variety_production
annual_min_oil += min_oil_production
annual_max_oil += max_oil_production
annual_avg_oil += avg_oil_production
annual_water_need += annual_variety_water_need
# Aggiorna dati varietà
clean_variety = clean_column_name(variety)
variety_data[clean_variety].update({
'tech': clean_column_name(technique),
'pct': percentage,
'prod_t_ha': variety_info['Produzione (tonnellate/ettaro)'] * np.random.uniform(0.95, 1.05),
'oil_prod_t_ha': variety_info['Produzione Olio (tonnellate/ettaro)'] * np.random.uniform(0.95, 1.05),
'oil_prod_l_ha': variety_info['Produzione Olio (litri/ettaro)'] * np.random.uniform(0.95, 1.05),
'min_yield_pct': variety_info['Min % Resa'] * min_yield_factor,
'max_yield_pct': variety_info['Max % Resa'] * max_yield_factor,
'min_oil_prod_l_ha': variety_info['Min Produzione Olio (litri/ettaro)'] * min_yield_factor,
'max_oil_prod_l_ha': variety_info['Max Produzione Olio (litri/ettaro)'] * max_yield_factor,
'avg_oil_prod_l_ha': variety_info['Media Produzione Olio (litri/ettaro)'] * avg_yield_factor,
'l_per_t': variety_info['Litri per Tonnellata'] * np.random.uniform(0.98, 1.02),
'min_l_per_t': variety_info['Min Litri per Tonnellata'] * min_yield_factor,
'max_l_per_t': variety_info['Max Litri per Tonnellata'] * max_yield_factor,
'avg_l_per_t': variety_info['Media Litri per Tonnellata'] * avg_yield_factor,
'olive_prod': annual_variety_production,
'min_oil_prod': min_oil_production,
'max_oil_prod': max_oil_production,
'avg_oil_prod': avg_oil_production,
'water_need': annual_variety_water_need
})
# Appiattisci i dati delle varietà
flattened_variety_data = {
f'{variety}_{key}': value
for variety, data in variety_data.items()
for key, value in data.items()
}
# Restituisci il risultato della zona
return {
'year': year,
'zone_id': zone + 1,
'temp_mean': zone_weather['temp_mean'].mean(),
'precip_sum': zone_weather['precip_sum'].sum(),
'solar_energy_sum': zone_weather['solarenergy_sum'].sum(),
'ha': hectares,
'zone': f"zone_{zone + 1}",
'olive_prod': annual_production,
'min_oil_prod': annual_min_oil,
'max_oil_prod': annual_max_oil,
'avg_oil_prod': annual_avg_oil,
'total_water_need': annual_water_need,
**flattened_variety_data
}
-220
View File
@@ -1,220 +0,0 @@
import pandas as pd
import numpy as np
import joblib
import os
from typing import Dict
def create_technique_mapping(olive_varieties: pd.DataFrame,
mapping_path: str = './kaggle/working/models/technique_mapping.joblib') -> Dict[str, int]:
"""
Crea un mapping numerico per le tecniche di coltivazione.
Parameters
----------
olive_varieties : pd.DataFrame
DataFrame contenente le varietà di olive e le tecniche
mapping_path : str
Percorso dove salvare il mapping
Returns
-------
Dict[str, int]
Dizionario di mapping tecnica -> codice numerico
"""
# Estrai tecniche uniche e convertile in lowercase
all_techniques = olive_varieties['Tecnica di Coltivazione'].str.lower().unique()
# Crea il mapping partendo da 1 (0 è riservato per valori mancanti)
technique_mapping = {tech: i + 1 for i, tech in enumerate(sorted(all_techniques))}
# Salva il mapping
os.makedirs(os.path.dirname(mapping_path), exist_ok=True)
joblib.dump(technique_mapping, mapping_path)
return technique_mapping
def calculate_stress_index(weather_data: pd.DataFrame,
olive_info: pd.Series,
vpd_threshold: float = 2.0) -> float:
"""
Calcola l'indice di stress per le olive basato su condizioni ambientali.
Parameters
----------
weather_data : pd.DataFrame
Dati meteorologici
olive_info : pd.Series
Informazioni sulla varietà di oliva
vpd_threshold : float
Soglia VPD per lo stress
Returns
-------
float
Indice di stress calcolato
"""
# Calcola componenti di stress
temp_stress = np.where(
weather_data['temp'] > olive_info['Temperatura Ottimale'],
(weather_data['temp'] - olive_info['Temperatura Ottimale']) / 10,
0
)
water_stress = np.where(
weather_data['vpd'] > vpd_threshold,
(weather_data['vpd'] - vpd_threshold) / 2,
0
)
# Considera la resistenza alla siccità
resistance_factor = 1.0
if olive_info['Resistenza alla Siccità'] == 'Alta':
resistance_factor = 0.7
elif olive_info['Resistenza alla Siccità'] == 'Media':
resistance_factor = 0.85
# Calcola stress complessivo
total_stress = (temp_stress + water_stress * resistance_factor)
return total_stress.mean()
def calculate_quality_indicators(olive_data: pd.DataFrame,
weather_data: pd.DataFrame) -> pd.DataFrame:
"""
Calcola indicatori di qualità per le olive.
Parameters
----------
olive_data : pd.DataFrame
Dati sulle olive
weather_data : pd.DataFrame
Dati meteorologici
Returns
-------
pd.DataFrame
DataFrame con indicatori di qualità aggiunti
"""
result = olive_data.copy()
# Calcola indicatori base
result['oil_content_index'] = result['Max % Resa'] * (1 - result['stress_index'] * 0.1)
result['fruit_size_index'] = np.clip(
result['Produzione (tonnellate/ettaro)'] * (1 - result['water_stress'] * 0.15),0, None
)
# Calcola indice di maturazione ottimale
optimal_harvest_conditions = (
(weather_data['temp'].between(15, 25)) &
(weather_data['humidity'].between(50, 70)) &
(weather_data['cloudcover'] < 60)
)
result['maturity_index'] = optimal_harvest_conditions.mean()
# Calcola indice di qualità complessivo
result['quality_index'] = (
result['oil_content_index'] * 0.4 +
result['fruit_size_index'] * 0.3 +
result['maturity_index'] * 0.3
)
return result
def add_olive_features(df: pd.DataFrame,
weather_data: pd.DataFrame) -> pd.DataFrame:
"""
Aggiunge feature specifiche per le olive.
Parameters
----------
df : pd.DataFrame
DataFrame delle varietà di olive
weather_data : pd.DataFrame
Dati meteorologici
Returns
-------
pd.DataFrame
DataFrame con feature aggiuntive
"""
result = df.copy()
# Calcola stress index per ogni varietà
result['stress_index'] = result.apply(
lambda row: calculate_stress_index(weather_data, row),
axis=1
)
# Aggiungi indicatori di qualità
result = calculate_quality_indicators(result, weather_data)
# Calcola efficienza produttiva
result['production_efficiency'] = result['Produzione (tonnellate/ettaro)'] / \
result['Fabbisogno Idrico Annuale (m³/ettaro)']
# Calcola indice di adattamento climatico
result['climate_adaptation'] = np.where(
result['Resistenza alla Siccità'] == 'Alta',
0.9,
np.where(result['Resistenza alla Siccità'] == 'Media', 0.7, 0.5)
)
# Aggiungi feature di produzione
result = add_production_features(result, weather_data)
return result
def add_production_features(df: pd.DataFrame,
weather_data: pd.DataFrame) -> pd.DataFrame:
"""
Aggiunge feature relative alla produzione di olive.
Parameters
----------
df : pd.DataFrame
DataFrame delle varietà di olive
weather_data : pd.DataFrame
Dati meteorologici
Returns
-------
pd.DataFrame
DataFrame con feature di produzione
"""
result = df.copy()
# Calcola i rapporti di produzione
result['oil_yield_ratio'] = result['Produzione Olio (tonnellate/ettaro)'] / result['Produzione (tonnellate/ettaro)']
result['water_efficiency'] = result['Produzione (tonnellate/ettaro)'] / result['Fabbisogno Idrico Annuale (m³/ettaro)']
# Calcola indici di produttività
result['productivity_index'] = (
result['oil_yield_ratio'] * 0.4 +
result['water_efficiency'] * 0.3 +
result['climate_adaptation'] * 0.3
)
# Aggiungi indicatori di rendimento
result['yield_stability'] = 1 - (
(result['Max % Resa'] - result['Min % Resa']) / result['Max % Resa']
)
result['oil_quality_potential'] = (
result['Max Litri per Tonnellata'] / 1000 * result['yield_stability'] * (1 - result['stress_index'] * 0.1)
)
# Calcola intervalli di produzione ottimale
result['optimal_production_lower'] = result['Produzione (tonnellate/ettaro)'] * 0.8
result['optimal_production_upper'] = result['Produzione (tonnellate/ettaro)'] * 1.2
# Aggiungi indici economici
result['economic_efficiency'] = (result['Produzione Olio (litri/ettaro)'] / result['Fabbisogno Idrico Annuale (m³/ettaro)']) * result['productivity_index']
return result
-205
View File
@@ -1,205 +0,0 @@
import pandas as pd
import numpy as np
from typing import Union, Optional
from datetime import datetime
def get_season(date: datetime) -> str:
"""
Determina la stagione in base alla data.
Parameters
----------
date : datetime
Data per cui determinare la stagione
Returns
-------
str
Nome della stagione ('Winter', 'Spring', 'Summer', 'Autumn')
"""
month = date.month
day = date.day
if (month == 12 and day >= 21) or (month <= 3 and day < 20):
return 'Winter'
elif (month == 3 and day >= 20) or (month <= 6 and day < 21):
return 'Spring'
elif (month == 6 and day >= 21) or (month <= 9 and day < 23):
return 'Summer'
elif (month == 9 and day >= 23) or (month <= 12 and day < 21):
return 'Autumn'
else:
return 'Unknown'
def get_time_period(hour: int) -> str:
"""
Determina il periodo del giorno in base all'ora.
Parameters
----------
hour : int
Ora del giorno (0-23)
Returns
-------
str
Periodo del giorno ('Morning', 'Afternoon', 'Evening', 'Night')
"""
if 5 <= hour < 12:
return 'Morning'
elif 12 <= hour < 17:
return 'Afternoon'
elif 17 <= hour < 21:
return 'Evening'
else:
return 'Night'
def add_time_features(df: pd.DataFrame) -> pd.DataFrame:
"""
Aggiunge feature temporali al DataFrame.
Parameters
----------
df : pd.DataFrame
DataFrame contenente una colonna 'datetime'
Returns
-------
pd.DataFrame
DataFrame con feature temporali aggiuntive
"""
# Assicurati che datetime sia nel formato corretto
df['datetime'] = pd.to_datetime(df['datetime'])
# Feature temporali di base
df['timestamp'] = df['datetime'].astype(np.int64) // 10 ** 9
df['year'] = df['datetime'].dt.year
df['month'] = df['datetime'].dt.month
df['day'] = df['datetime'].dt.day
df['hour'] = df['datetime'].dt.hour
df['minute'] = df['datetime'].dt.minute
# Feature cicliche
df['hour_sin'] = np.sin(df['hour'] * (2 * np.pi / 24))
df['hour_cos'] = np.cos(df['hour'] * (2 * np.pi / 24))
df['month_sin'] = np.sin(df['month'] * (2 * np.pi / 12))
df['month_cos'] = np.cos(df['month'] * (2 * np.pi / 12))
# Feature calendario
df['day_of_week'] = df['datetime'].dt.dayofweek
df['day_of_year'] = df['datetime'].dt.dayofyear
df['week_of_year'] = df['datetime'].dt.isocalendar().week.astype(int)
df['quarter'] = df['datetime'].dt.quarter
# Feature cicliche giorno dell'anno
df['day_of_year_sin'] = np.sin(df['day_of_year'] * (2 * np.pi / 365.25))
df['day_of_year_cos'] = np.cos(df['day_of_year'] * (2 * np.pi / 365.25))
# Flag speciali
df['is_month_end'] = df['datetime'].dt.is_month_end.astype(int)
df['is_quarter_end'] = df['datetime'].dt.is_quarter_end.astype(int)
df['is_year_end'] = df['datetime'].dt.is_year_end.astype(int)
# Periodi del giorno e stagioni
df['season'] = df['datetime'].apply(get_season)
df['time_period'] = df['hour'].apply(get_time_period)
return df
def create_time_based_features(
df: pd.DataFrame,
datetime_col: str = 'datetime',
add_cyclical: bool = True,
add_time_periods: bool = True,
add_seasons: bool = True,
custom_features: Optional[list] = None
) -> pd.DataFrame:
"""
Crea feature temporali personalizzate.
Parameters
----------
df : pd.DataFrame
DataFrame di input
datetime_col : str
Nome della colonna datetime
add_cyclical : bool
Se True, aggiunge feature cicliche
add_time_periods : bool
Se True, aggiunge periodi del giorno
add_seasons : bool
Se True, aggiunge stagioni
custom_features : list, optional
Lista di feature temporali personalizzate da aggiungere
Returns
-------
pd.DataFrame
DataFrame con le nuove feature temporali
"""
# Crea una copia del DataFrame
result = df.copy()
# Converti la colonna datetime se necessario
if not pd.api.types.is_datetime64_any_dtype(result[datetime_col]):
result[datetime_col] = pd.to_datetime(result[datetime_col])
# Feature temporali di base
result['year'] = result[datetime_col].dt.year
result['month'] = result[datetime_col].dt.month
result['day'] = result[datetime_col].dt.day
result['hour'] = result[datetime_col].dt.hour
result['day_of_week'] = result[datetime_col].dt.dayofweek
result['day_of_year'] = result[datetime_col].dt.dayofyear
# Feature cicliche
if add_cyclical:
# Ora
result['hour_sin'] = np.sin(result['hour'] * (2 * np.pi / 24))
result['hour_cos'] = np.cos(result['hour'] * (2 * np.pi / 24))
# Mese
result['month_sin'] = np.sin((result['month'] - 1) * (2 * np.pi / 12))
result['month_cos'] = np.cos((result['month'] - 1) * (2 * np.pi / 12))
# Giorno dell'anno
result['day_of_year_sin'] = np.sin((result['day_of_year'] - 1) * (2 * np.pi / 365.25))
result['day_of_year_cos'] = np.cos((result['day_of_year'] - 1) * (2 * np.pi / 365.25))
# Giorno della settimana
result['day_of_week_sin'] = np.sin(result['day_of_week'] * (2 * np.pi / 7))
result['day_of_week_cos'] = np.cos(result['day_of_week'] * (2 * np.pi / 7))
# Periodi del giorno
if add_time_periods:
result['time_period'] = result['hour'].apply(get_time_period)
# One-hot encoding del periodo del giorno
time_period_dummies = pd.get_dummies(result['time_period'], prefix='time_period')
result = pd.concat([result, time_period_dummies], axis=1)
# Stagioni
if add_seasons:
result['season'] = result[datetime_col].apply(get_season)
# One-hot encoding delle stagioni
season_dummies = pd.get_dummies(result['season'], prefix='season')
result = pd.concat([result, season_dummies], axis=1)
# Feature personalizzate
if custom_features:
for feature in custom_features:
if feature == 'is_weekend':
result['is_weekend'] = result['day_of_week'].isin([5, 6]).astype(int)
elif feature == 'is_business_hour':
result['is_business_hour'] = ((result['hour'] >= 9) &
(result['hour'] < 18) &
~result['is_weekend']).astype(int)
elif feature == 'season_progress':
result['season_progress'] = result.apply(
lambda x: (x['day_of_year'] % 91) / 91.0, axis=1
)
return result
-186
View File
@@ -1,186 +0,0 @@
import pandas as pd
import numpy as np
from typing import Union
def calculate_vpd(temp: Union[float, np.ndarray], humidity: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
"""
Calcola il Deficit di Pressione di Vapore (VPD).
VPD è una misura della domanda evaporativa dell'aria.
Parameters
----------
temp : float or np.ndarray
Temperatura in Celsius
humidity : float or np.ndarray
Umidità relativa (0-100)
Returns
-------
float or np.ndarray
VPD in kPa
"""
# Pressione di vapore saturo (kPa)
es = 0.6108 * np.exp((17.27 * temp) / (temp + 237.3))
# Pressione di vapore attuale (kPa)
ea = es * (humidity / 100.0)
# VPD (kPa)
vpd = es - ea
return np.maximum(vpd, 0) # VPD non può essere negativo
def add_solar_features(df: pd.DataFrame) -> pd.DataFrame:
"""
Aggiunge feature relative alla radiazione solare.
Parameters
----------
df : pd.DataFrame
DataFrame di input
Returns
-------
pd.DataFrame
DataFrame con feature solari aggiunte
"""
# Calcola angolo solare
df['solar_angle'] = np.sin(df['day_of_year'] * (2 * np.pi / 365.25)) * \
np.sin(df['hour'] * (2 * np.pi / 24))
# Interazioni tra feature rilevanti
df['cloud_temp_interaction'] = df['cloudcover'] * df['temp']
df['visibility_cloud_interaction'] = df['visibility'] * (100 - df['cloudcover'])
# Feature derivate
df['clear_sky_index'] = (100 - df['cloudcover']) / 100
df['temp_gradient'] = df['temp'] - df['tempmin']
# Feature di efficienza solare
df['solar_efficiency'] = df['solarenergy'] / (df['solarradiation'] + 1e-6) # evita divisione per zero
df['solar_temp_ratio'] = df['solarradiation'] / (df['temp'] + 273.15) # temperatura in Kelvin
return df
def add_solar_specific_features(df: pd.DataFrame) -> pd.DataFrame:
"""
Aggiunge feature specifiche per l'analisi solare.
Parameters
----------
df : pd.DataFrame
DataFrame di input
Returns
-------
pd.DataFrame
DataFrame con feature solari specifiche aggiunte
"""
# Angolo solare e durata del giorno
df['day_length'] = 12 + 3 * np.sin(2 * np.pi * (df['day_of_year'] - 81) / 365.25)
df['solar_noon'] = 12 - df['hour']
df['solar_elevation'] = np.sin(2 * np.pi * df['day_of_year'] / 365.25) * \
np.cos(2 * np.pi * df['solar_noon'] / 24)
# Interazioni
df['cloud_elevation'] = df['cloudcover'] * df['solar_elevation']
df['visibility_elevation'] = df['visibility'] * df['solar_elevation']
# Rolling features
df['cloud_rolling_12h'] = df['cloudcover'].rolling(window=12, min_periods=1).mean()
df['temp_rolling_12h'] = df['temp'].rolling(window=12, min_periods=1).mean()
# Feature di efficienza energetica
df['solar_energy_density'] = df['solarenergy'] / df['day_length']
df['cloud_impact'] = df['solarradiation'] * (1 - df['cloudcover'] / 100)
return df
def add_environmental_features(df: pd.DataFrame) -> pd.DataFrame:
"""
Aggiunge feature ambientali derivate.
Parameters
----------
df : pd.DataFrame
DataFrame di input
Returns
-------
pd.DataFrame
DataFrame con feature ambientali aggiunte
"""
# Calcola VPD
df['vpd'] = calculate_vpd(df['temp'], df['humidity'])
# Feature di stress idrico
df['water_stress_index'] = df['vpd'] * (1 - df['humidity'] / 100)
df['dryness_index'] = (df['temp'] - df['dew']) * (100 - df['humidity']) / 100
# Indici di comfort
df['heat_index'] = np.where(
df['temp'] >= 27,
-42.379 + 2.04901523 * df['temp'] + 10.14333127 * df['humidity'] -
0.22475541 * df['temp'] * df['humidity'] - 0.00683783 * df['temp'] ** 2 -
0.05481717 * df['humidity'] ** 2 + 0.00122874 * df['temp'] ** 2 * df['humidity'] +
0.00085282 * df['temp'] * df['humidity'] ** 2 -
0.00000199 * df['temp'] ** 2 * df['humidity'] ** 2,
df['temp']
)
# Rolling means per trend
windows = [3, 6, 12, 24] # ore
for window in windows:
df[f'temp_rolling_mean_{window}h'] = df['temp'].rolling(window=window, min_periods=1).mean()
df[f'humid_rolling_mean_{window}h'] = df['humidity'].rolling(window=window, min_periods=1).mean()
df[f'precip_rolling_sum_{window}h'] = df['precip'].rolling(window=window, min_periods=1).sum()
return df
def add_weather_indicators(df: pd.DataFrame) -> pd.DataFrame:
"""
Aggiunge indicatori meteorologici complessi.
Parameters
----------
df : pd.DataFrame
DataFrame di input
Returns
-------
pd.DataFrame
DataFrame con indicatori meteorologici aggiunti
"""
# Indicatori di stabilità atmosferica
df['temp_stability'] = df['temp_rolling_mean_12h'].std()
df['pressure_tendency'] = df['pressure'].diff()
# Indicatori di precipitazioni
df['rain_intensity'] = np.where(
df['precip'] > 0,
df['precip'] / (df['precip_rolling_sum_24h'] + 1e-6),
0
)
df['dry_spell'] = (df['precip'] == 0).astype(int).groupby(
(df['precip'] != 0).cumsum()
).cumsum()
# Indicatori di comfort termico
df['apparent_temp'] = df['temp'] + 0.33 * df['vpd'] - 0.7 * df['windspeed'] - 4.0
df['frost_risk'] = (df['temp'] < 2).astype(int)
df['heat_stress'] = (df['temp'] > 30).astype(int) * (df['humidity'] > 70).astype(int)
# Indicatori di qualità dell'aria
df['stagnation_index'] = (df['windspeed'] < 5).astype(int) * (df['cloudcover'] > 80).astype(int)
df['visibility_index'] = df['visibility'] * (1 - df['cloudcover'] / 100)
# Indicatori agrometeorologici
df['growing_degree_days'] = np.maximum(0, df['temp'] - 10) # base 10°C
df['chill_hours'] = (df['temp'] < 7).astype(int)
df['evapotranspiration_proxy'] = df['vpd'] * df['solarradiation'] * (1 + 0.536 * df['windspeed'])
return df
View File
Whitespace-only changes.
Binary file not shown.
+240
View File
@@ -0,0 +1,240 @@
import pandas as pd
import numpy as np
from tqdm import tqdm
def generate_training_dataset(weather_data, olive_varieties, num_simulations=1000, random_seed=42):
"""
Genera dataset di training combinando le migliori caratteristiche di entrambi gli approcci.
Args:
weather_data: DataFrame con dati meteorologici
olive_varieties: DataFrame con informazioni sulle varietà
num_simulations: Numero di simulazioni da generare
random_seed: Seme per riproducibilità
"""
np.random.seed(random_seed)
# Prepara dati meteorologici annuali
weather_annual = weather_data.groupby('year').agg({
'temp': ['mean', 'min', 'max', 'std'],
'humidity': ['mean', 'min', 'max'],
'precip': ['sum', 'mean', 'std'],
'solarradiation': ['mean', 'sum', 'std'],
'cloudcover': ['mean']
}).reset_index()
# Appiattisci i nomi delle colonne
weather_annual.columns = ['year'] + [
f'{col[0]}_{col[1]}' for col in weather_annual.columns[1:]
]
all_results = []
all_varieties = olive_varieties['Varietà di Olive'].unique()
with tqdm(total=num_simulations, desc="Generazione dataset") as pbar:
for sim in range(num_simulations):
# Seleziona anno base e applica variazioni
selected_year = np.random.choice(weather_annual['year'])
weather = weather_annual[weather_annual['year'] == selected_year].iloc[0].copy()
# Applica variazioni meteorologiche (±20%)
for col in weather.index:
if col != 'year':
weather[col] *= np.random.uniform(0.8, 1.2)
# Genera caratteristiche dell'oliveto
num_varieties = np.random.randint(1, 4) # 1-3 varietà
selected_varieties = np.random.choice(all_varieties, size=num_varieties, replace=False)
hectares = np.random.uniform(1, 10)
percentages = np.random.dirichlet(np.ones(num_varieties))
# Inizializza contatori per l'anno
annual_results = {
'simulation_id': sim + 1,
'year': selected_year,
'hectares': hectares,
'num_varieties': num_varieties,
'total_olive_production': 0,
'total_oil_production': 0,
'total_water_need': 0,
}
# Aggiungi dati meteorologici
for col in weather.index:
if col != 'year':
annual_results[f'weather_{col}'] = weather[col]
# Simula per ogni varietà
variety_details = []
for i, variety in enumerate(selected_varieties):
# Seleziona tecnica di coltivazione
variety_data = olive_varieties[olive_varieties['Varietà di Olive'] == variety]
technique = np.random.choice(variety_data['Tecnica di Coltivazione'].unique())
percentage = percentages[i]
# Ottieni dati specifici varietà
variety_info = variety_data[
variety_data['Tecnica di Coltivazione'] == technique
].iloc[0]
# Calcola produzione base con variabilità
base_variation = np.random.uniform(0.8, 1.2)
base_production = variety_info['Produzione (tonnellate/ettaro)'] * base_variation
# Applica effetti meteorologici
temp_effect = calculate_temperature_effect(
weather['temp_mean'],
variety_info['Temperatura Ottimale']
)
water_effect = calculate_water_effect(
weather['precip_sum'],
variety_info['Resistenza alla Siccità']
)
solar_effect = calculate_solar_effect(
weather['solarradiation_mean']
)
# Calcola produzione effettiva
actual_production = (
base_production *
temp_effect *
water_effect *
solar_effect *
percentage *
hectares
)
# Calcola resa olio con variabilità
oil_yield = np.random.uniform(
variety_info['Min % Resa'],
variety_info['Max % Resa']
)
oil_production = actual_production * oil_yield
# Calcola fabbisogno idrico
base_water_need = (
variety_info['Fabbisogno Acqua Primavera (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Estate (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Autunno (m³/ettaro)'] +
variety_info['Fabbisogno Acqua Inverno (m³/ettaro)']
) / 4
# Adatta fabbisogno idrico alle condizioni
actual_water_need = (
base_water_need *
(1 + max(0, (weather['temp_mean'] - 20) / 50)) *
max(0.6, 1 - (weather['precip_sum'] / 1000)) *
percentage *
hectares
)
# Salva dettagli varietà
variety_details.append({
'variety': variety,
'technique': technique,
'percentage': percentage,
'production': actual_production,
'oil_production': oil_production,
'water_need': actual_water_need,
'yield': oil_yield,
'base_production': base_production,
'temp_effect': temp_effect,
'water_effect': water_effect,
'solar_effect': solar_effect
})
# Aggiorna totali annuali
annual_results['total_olive_production'] += actual_production
annual_results['total_oil_production'] += oil_production
annual_results['total_water_need'] += actual_water_need
# Calcola metriche per ettaro
annual_results['olive_production_ha'] = annual_results['total_olive_production'] / hectares
annual_results['oil_production_ha'] = annual_results['total_oil_production'] / hectares
annual_results['water_need_ha'] = annual_results['total_water_need'] / hectares
# Aggiungi KPI di efficienza
annual_results['yield_efficiency'] = annual_results['total_oil_production'] / annual_results[
'total_olive_production']
annual_results['water_efficiency'] = annual_results['total_olive_production'] / annual_results[
'total_water_need']
# Aggiungi dettagli varietà al risultato
for i, detail in enumerate(variety_details):
prefix = f'variety_{i + 1}'
for key, value in detail.items():
annual_results[f'{prefix}_{key}'] = value
all_results.append(annual_results)
pbar.update(1)
# Crea DataFrame finale
df = pd.DataFrame(all_results)
return df
def calculate_temperature_effect(temp, optimal_temp):
"""Calcola effetto temperatura con variabilità"""
temp_diff = abs(temp - optimal_temp)
if temp_diff <= 5:
return np.random.uniform(0.95, 1.0)
elif temp_diff <= 10:
return np.random.uniform(0.8, 0.9)
else:
return np.random.uniform(0.6, 0.8)
def calculate_water_effect(precip, drought_resistance):
"""Calcola effetto precipitazioni con variabilità"""
if 'alta' in str(drought_resistance).lower():
min_precip = 20
elif 'media' in str(drought_resistance).lower():
min_precip = 30
else:
min_precip = 40
if precip >= min_precip:
return np.random.uniform(0.95, 1.0)
else:
base_factor = max(0.6, precip / min_precip)
return base_factor * np.random.uniform(0.8, 1.2)
def calculate_solar_effect(radiation):
"""Calcola effetto radiazione solare con variabilità"""
if radiation >= 200:
return np.random.uniform(0.95, 1.0)
else:
base_factor = max(0.7, radiation / 200)
return base_factor * np.random.uniform(0.8, 1.2)
# Test del codice
if __name__ == "__main__":
print("Generazione dataset di training...")
# Carica dati
weather_data = pd.read_parquet('./sources/weather_data_complete.parquet')
olive_varieties = pd.read_parquet('./sources/olive_varieties.parquet')
# Genera dataset
df = generate_training_dataset(weather_data, olive_varieties, 100000)
print("\nShape dataset:", df.shape)
print("\nColonne disponibili:")
print(df.columns.tolist())
print("\nStatistiche di base:")
print(df.describe())
# Analisi variabilità
print("\nAnalisi coefficienti di variazione:")
for col in ['olive_production_ha', 'oil_production_ha', 'water_need_ha']:
cv = df[col].std() / df[col].mean()
print(f"{col}: {cv:.2%}")
# Salva dataset
df.to_parquet('./sources/olive_training_dataset.parquet', index=False)
print("\nDataset salvato come 'olive_training_dataset.parquet'")
-207
View File
@@ -1,207 +0,0 @@
import tensorflow as tf
import numpy as np
from typing import Dict, Optional, List
import os
import json
from datetime import datetime
@tf.keras.saving.register_keras_serializable()
class CustomCallback(tf.keras.callbacks.Callback):
"""
Callback personalizzato per monitorare la non-negatività delle predizioni
e altre metriche durante il training.
"""
def __init__(self, validation_data: Optional[tuple] = None):
super().__init__()
self.validation_data = validation_data
def on_epoch_end(self, epoch: int, logs: Optional[Dict] = None):
try:
if hasattr(self.model, 'validation_data'):
val_x = self.model.validation_data[0]
if isinstance(val_x, list): # Per il modello della radiazione
val_pred = self.model.predict(val_x, verbose=0)
else:
val_pred = self.model.predict(val_x, verbose=0)
# Verifica non-negatività
if np.any(val_pred < 0):
print("\nWarning: Rilevati valori negativi nelle predizioni")
print(f"Min value: {np.min(val_pred)}")
# Statistiche predizioni
print(f"\nStatistiche predizioni epoca {epoch}:")
print(f"Min: {np.min(val_pred):.4f}")
print(f"Max: {np.max(val_pred):.4f}")
print(f"Media: {np.mean(val_pred):.4f}")
# Aggiunge le metriche ai logs
if logs is not None:
logs['val_pred_min'] = np.min(val_pred)
logs['val_pred_max'] = np.max(val_pred)
logs['val_pred_mean'] = np.mean(val_pred)
except Exception as e:
print(f"\nWarning nel CustomCallback: {str(e)}")
@tf.keras.saving.register_keras_serializable()
class WarmUpLearningRateSchedule(tf.keras.optimizers.schedules.LearningRateSchedule):
"""
Schedule del learning rate con warm-up lineare e decay esponenziale.
"""
def __init__(self, initial_learning_rate: float = 1e-3,
warmup_steps: int = 500,
decay_steps: int = 5000):
super().__init__()
self.initial_learning_rate = initial_learning_rate
self.warmup_steps = warmup_steps
self.decay_steps = decay_steps
def __call__(self, step):
warmup_pct = tf.cast(step, tf.float32) / self.warmup_steps
warmup_lr = self.initial_learning_rate * warmup_pct
decay_factor = tf.pow(0.1, tf.cast(step, tf.float32) / self.decay_steps)
decayed_lr = self.initial_learning_rate * decay_factor
return tf.where(step < self.warmup_steps, warmup_lr, decayed_lr)
def get_config(self):
return {
'initial_learning_rate': self.initial_learning_rate,
'warmup_steps': self.warmup_steps,
'decay_steps': self.decay_steps
}
class MetricLogger(tf.keras.callbacks.Callback):
"""
Logger avanzato per metriche di training che salva i risultati in JSON
e crea grafici di progresso.
"""
def __init__(self, log_dir: str = './logs',
metric_list: Optional[List[str]] = None,
save_freq: int = 1):
super().__init__()
self.log_dir = log_dir
os.makedirs(log_dir, exist_ok=True)
self.metric_list = metric_list or ['loss', 'val_loss', 'mae', 'val_mae']
self.save_freq = save_freq
self.history = {metric: [] for metric in self.metric_list}
# Timestamp per il nome del file
self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
self.log_file = os.path.join(log_dir, f'metrics_{self.timestamp}.json')
def on_epoch_end(self, epoch: int, logs: Dict = None):
# Aggiorna lo storico
for metric in self.metric_list:
if metric in logs:
self.history[metric].append(float(logs[metric]))
# Salva i log periodicamente
if (epoch + 1) % self.save_freq == 0:
self._save_logs()
self._create_plots()
def _save_logs(self):
"""Salva i log in formato JSON."""
with open(self.log_file, 'w') as f:
json.dump({
'history': self.history,
'epochs': len(next(iter(self.history.values())))
}, f, indent=4)
def _create_plots(self):
"""Crea grafici delle metriche."""
import matplotlib.pyplot as plt
# Plot per ogni metrica
for metric in self.metric_list:
if metric in self.history and len(self.history[metric]) > 0:
plt.figure(figsize=(10, 6))
plt.plot(self.history[metric])
plt.title(f'Model {metric}')
plt.ylabel(metric)
plt.xlabel('Epoch')
plt.savefig(os.path.join(self.log_dir, f'{metric}_{self.timestamp}.png'))
plt.close()
class EarlyStoppingWithBest(tf.keras.callbacks.EarlyStopping):
"""
Early stopping avanzato che salva il miglior modello e fornisce
analisi dettagliate sulla convergenza.
"""
def __init__(self,
monitor: str = 'val_loss',
min_delta: float = 0,
patience: int = 0,
verbose: int = 0,
mode: str = 'auto',
baseline: Optional[float] = None,
restore_best_weights: bool = True,
start_from_epoch: int = 0):
super().__init__(
monitor=monitor,
min_delta=min_delta,
patience=patience,
verbose=verbose,
mode=mode,
baseline=baseline,
restore_best_weights=restore_best_weights,
start_from_epoch=start_from_epoch
)
self.best_epoch = 0
self.convergence_history = []
def on_epoch_end(self, epoch: int, logs: Optional[Dict] = None):
current = self.get_monitor_value(logs)
if current is None:
return
# Aggiungi il valore corrente alla storia
self.convergence_history.append(float(current))
# Calcola statistiche di convergenza
if len(self.convergence_history) > 1:
improvement = self.convergence_history[-2] - current
pct_improvement = (improvement / self.convergence_history[-2]) * 100
if self.verbose > 0:
print(f"\nEpoch {epoch + 1}: {self.monitor} improved by {pct_improvement:.2f}%")
# Aggiorna best_epoch se necessario
if self.monitor_op(current - self.min_delta, self.best):
self.best = current
self.best_epoch = epoch
self.wait = 0
else:
self.wait += 1
if self.wait >= self.patience:
self.stopped_epoch = epoch
self.model.stop_training = True
if self.restore_best_weights and self.best_weights is not None:
if self.verbose > 0:
print(f"\nRestoring model weights from epoch {self.best_epoch + 1}")
self.model.set_weights(self.best_weights)
def get_convergence_stats(self) -> Dict:
"""
Restituisce statistiche dettagliate sulla convergenza.
"""
if len(self.convergence_history) < 2:
return {}
improvements = np.diff(self.convergence_history)
return {
'best_epoch': self.best_epoch + 1,
'best_value': float(self.best),
'avg_improvement': float(np.mean(improvements)),
'total_improvement': float(self.convergence_history[0] - self.best),
'convergence_rate': float(np.mean(np.abs(improvements[1:] / improvements[:-1]))),
'final_value': float(self.convergence_history[-1])
}
-327
View File
@@ -1,327 +0,0 @@
import tensorflow as tf
from tf.keras import layers
from typing import List, Optional
@tf.keras.saving.register_keras_serializable()
class MultiScaleAttention(layers.Layer):
"""
Layer di attenzione multi-scala per catturare pattern temporali a diverse granularità.
Attributes
----------
num_heads : int
Numero di teste di attenzione
head_dim : int
Dimensionalità per ogni testa
scales : List[int]
Lista delle scale temporali da considerare
"""
def __init__(
self,
num_heads: int = 8,
head_dim: int = 64,
scales: List[int] = [1, 2, 4],
dropout: float = 0.1,
**kwargs
):
super().__init__(**kwargs)
self.num_heads = num_heads
self.head_dim = head_dim
self.scales = scales
self.dropout = dropout
# Creiamo un'attention layer per ogni scala
self.attention_layers = [
layers.MultiHeadAttention(
num_heads=num_heads,
key_dim=head_dim,
dropout=dropout,
name=f'attention_scale_{scale}'
) for scale in scales
]
# Layer per combinare le diverse scale
self.combine = layers.Dense(
head_dim * num_heads,
activation='gelu',
name='scale_combination'
)
def call(self, inputs: tf.Tensor, training: Optional[bool] = None) -> tf.Tensor:
# Lista per salvare gli output delle diverse scale
scale_outputs = []
for scale, attention in zip(self.scales, self.attention_layers):
# Applica max pooling per ridurre la sequenza alla scala corrente
if scale > 1:
pooled = tf.keras.layers.MaxPool1D(
pool_size=scale,
strides=scale
)(inputs)
else:
pooled = inputs
# Applica attenzione alla sequenza ridotta
attended = attention(pooled, pooled)
# Se necessario, riporta alla dimensione originale
if scale > 1:
attended = tf.keras.layers.UpSampling1D(size=scale)(attended)
# Taglia eventuali timestep in eccesso
attended = attended[:, :tf.shape(inputs)[1], :]
scale_outputs.append(attended)
# Concatena e combina gli output delle diverse scale
concatenated = tf.concat(scale_outputs, axis=-1)
output = self.combine(concatenated)
return output
def get_config(self) -> dict:
config = super().get_config()
config.update({
"num_heads": self.num_heads,
"head_dim": self.head_dim,
"scales": self.scales,
"dropout": self.dropout
})
return config
@tf.keras.saving.register_keras_serializable()
class TemporalConvBlock(layers.Layer):
"""
Blocco di convoluzione temporale con residual connection.
Attributes
----------
filters : int
Numero di filtri convoluzionali
kernel_sizes : List[int]
Lista delle dimensioni dei kernel da utilizzare
dilation_rates : List[int]
Lista dei tassi di dilatazione
"""
def __init__(
self,
filters: int = 64,
kernel_sizes: List[int] = [3, 5, 7],
dilation_rates: List[int] = [1, 2, 4],
dropout: float = 0.1,
**kwargs
):
super().__init__(**kwargs)
self.filters = filters
self.kernel_sizes = kernel_sizes
self.dilation_rates = dilation_rates
self.dropout = dropout
# Crea i layer convoluzionali
self.conv_layers = []
for k_size in kernel_sizes:
for d_rate in dilation_rates:
self.conv_layers.append(
layers.Conv1D(
filters=filters // (len(kernel_sizes) * len(dilation_rates)),
kernel_size=k_size,
dilation_rate=d_rate,
padding='same',
activation='gelu'
)
)
# Layer per il processing finale
self.combine = layers.Conv1D(filters, 1)
self.layernorm = layers.LayerNormalization()
self.dropout = layers.Dropout(dropout)
def call(self, inputs: tf.Tensor, training: Optional[bool] = None) -> tf.Tensor:
# Lista per gli output di ogni convoluzione
conv_outputs = []
# Applica ogni combinazione di kernel size e dilation rate
for conv in self.conv_layers:
conv_outputs.append(conv(inputs))
# Concatena tutti gli output
concatenated = tf.concat(conv_outputs, axis=-1)
# Combinazione finale
x = self.combine(concatenated)
x = self.layernorm(x)
x = self.dropout(x, training=training)
# Residual connection
return x + inputs
def get_config(self) -> dict:
config = super().get_config()
config.update({
"filters": self.filters,
"kernel_sizes": self.kernel_sizes,
"dilation_rates": self.dilation_rates,
"dropout": self.dropout
})
return config
@tf.keras.saving.register_keras_serializable()
class WeatherEmbedding(layers.Layer):
"""
Layer per l'embedding di feature meteorologiche.
Combina embedding categorici e numerici.
Attributes
----------
embedding_dim : int
Dimensionalità dell'embedding
num_numerical : int
Numero di feature numeriche
categorical_features : dict
Dizionario con feature categoriche e loro cardinalità
"""
def __init__(
self,
embedding_dim: int = 32,
num_numerical: int = 8,
categorical_features: Optional[dict] = None,
**kwargs
):
super().__init__(**kwargs)
self.embedding_dim = embedding_dim
self.num_numerical = num_numerical
self.categorical_features = categorical_features or {
'season': 4,
'time_period': 4,
'weather_condition': 10
}
# Layer per feature numeriche
self.numerical_projection = layers.Dense(
embedding_dim,
activation='gelu'
)
# Layer per feature categoriche
self.categorical_embeddings = {
name: layers.Embedding(
input_dim=num_categories,
output_dim=embedding_dim
)
for name, num_categories in self.categorical_features.items()
}
# Layer di combinazione finale
self.combine = layers.Dense(embedding_dim, activation='gelu')
def call(self, inputs: dict) -> tf.Tensor:
# Processa feature numeriche
numerical = self.numerical_projection(inputs['numerical'])
# Lista per gli embedding categorici
categorical_outputs = []
# Processa ogni feature categorica
for name, embedding_layer in self.categorical_embeddings.items():
if name in inputs['categorical']:
embedded = embedding_layer(inputs['categorical'][name])
categorical_outputs.append(embedded)
# Combina tutti gli embedding
if categorical_outputs:
categorical = tf.reduce_mean(tf.stack(categorical_outputs, axis=1), axis=1)
combined = tf.concat([numerical, categorical], axis=-1)
else:
combined = numerical
return self.combine(combined)
def get_config(self) -> dict:
config = super().get_config()
config.update({
"embedding_dim": self.embedding_dim,
"num_numerical": self.num_numerical,
"categorical_features": self.categorical_features
})
return config
@tf.keras.saving.register_keras_serializable()
class OliveVarietyEmbedding(layers.Layer):
"""
Layer per l'embedding delle varietà di olive e delle loro caratteristiche.
Attributes
----------
embedding_dim : int
Dimensionalità dell'embedding
num_varieties : int
Numero di varietà di olive
num_techniques : int
Numero di tecniche di coltivazione
"""
def __init__(
self,
embedding_dim: int = 32,
num_varieties: int = 11,
num_techniques: int = 3,
**kwargs
):
super().__init__(**kwargs)
self.embedding_dim = embedding_dim
self.num_varieties = num_varieties
self.num_techniques = num_techniques
# Embedding per varietà e tecniche
self.variety_embedding = layers.Embedding(
input_dim=num_varieties,
output_dim=embedding_dim
)
self.technique_embedding = layers.Embedding(
input_dim=num_techniques,
output_dim=embedding_dim
)
# Layer per feature continue
self.continuous_projection = layers.Dense(
embedding_dim,
activation='gelu'
)
# Layer di combinazione
self.combine = layers.Dense(embedding_dim, activation='gelu')
def call(self, inputs: dict) -> tf.Tensor:
# Embedding varietà
variety_embedded = self.variety_embedding(inputs['variety'])
# Embedding tecniche
technique_embedded = self.technique_embedding(inputs['technique'])
# Proiezione feature continue
continuous_projected = self.continuous_projection(inputs['continuous'])
# Combinazione
combined = tf.concat([
variety_embedded,
technique_embedded,
continuous_projected
], axis=-1)
return self.combine(combined)
def get_config(self) -> dict:
config = super().get_config()
config.update({
"embedding_dim": self.embedding_dim,
"num_varieties": self.num_varieties,
"num_techniques": self.num_techniques
})
return config
-204
View File
@@ -1,204 +0,0 @@
import tensorflow as tf
import tf.keras.layers as layers
def create_radiation_model(input_shape, solar_params_shape=(3,)):
"""
Modello per la radiazione solare con vincoli di non-negatività.
"""
# Input layers
main_input = layers.Input(shape=input_shape, name='main_input')
solar_input = layers.Input(shape=solar_params_shape, name='solar_params')
# Branch CNN
x1 = layers.Conv1D(32, 3, padding='same')(main_input)
x1 = layers.BatchNormalization()(x1)
x1 = layers.Activation('relu')(x1)
x1 = layers.Conv1D(64, 3, padding='same')(x1)
x1 = layers.BatchNormalization()(x1)
x1 = layers.Activation('relu')(x1)
x1 = layers.GlobalAveragePooling1D()(x1)
# Branch LSTM
x2 = layers.Bidirectional(layers.LSTM(64, return_sequences=True))(main_input)
x2 = layers.Bidirectional(layers.LSTM(32))(x2)
x2 = layers.BatchNormalization()(x2)
# Solar parameters processing
x3 = layers.Dense(32)(solar_input)
x3 = layers.BatchNormalization()(x3)
x3 = layers.Activation('relu')(x3)
# Combine all branches
x = layers.concatenate([x1, x2, x3])
# Dense layers with non-negativity constraints
x = layers.Dense(64, kernel_constraint=tf.keras.constraints.NonNeg())(x)
x = layers.BatchNormalization()(x)
x = layers.Activation('relu')(x)
x = layers.Dropout(0.2)(x)
x = layers.Dense(32, kernel_constraint=tf.keras.constraints.NonNeg())(x)
x = layers.BatchNormalization()(x)
x = layers.Activation('relu')(x)
# Output layer con vincoli di non-negatività
output = layers.Dense(1,
kernel_constraint=tf.keras.constraints.NonNeg(),
activation='relu')(x)
model = layers.Model(inputs=[main_input, solar_input], outputs=output, name="SolarRadiation")
return model
def create_energy_model(input_shape):
"""
Modello migliorato per l'energia solare che sfrutta la relazione con la radiazione.
Include vincoli di non-negatività e migliore gestione delle dipendenze temporali.
"""
inputs = layers.Input(shape=input_shape)
# Branch 1: Elaborazione temporale con attention
# Multi-head attention per catturare relazioni temporali
x1 = layers.MultiHeadAttention(num_heads=8, key_dim=32)(inputs, inputs)
x1 = layers.BatchNormalization()(x1)
x1 = layers.Activation('relu')(x1)
# Temporal Convolution branch per catturare pattern locali
x2 = layers.Conv1D(
filters=64,
kernel_size=3,
padding='same',
kernel_constraint=tf.keras.constraints.NonNeg()
)(inputs)
x2 = layers.BatchNormalization()(x2)
x2 = layers.Activation('relu')(x2)
x2 = layers.Conv1D(
filters=32,
kernel_size=3,
padding='same',
kernel_constraint=tf.keras.constraints.NonNeg()
)(x2)
x2 = layers.BatchNormalization()(x2)
x2 = layers.Activation('relu')(x2)
# LSTM branch per memoria a lungo termine
x3 = layers.LSTM(64, return_sequences=True)(inputs)
x3 = layers.LSTM(32, return_sequences=False)(x3)
x3 = layers.BatchNormalization()(x3)
x3 = layers.Activation('relu')(x3)
# Global pooling per ogni branch
x1 = layers.GlobalAveragePooling1D()(x1)
x2 = layers.GlobalAveragePooling1D()(x2)
# Concatena tutti i branch
x = layers.concatenate([x1, x2, x3])
# Dense layers con vincoli di non-negatività
x = layers.Dense(
128,
kernel_constraint=tf.keras.constraints.NonNeg(),
kernel_regularizer=layers.l2(0.01)
)(x)
x = layers.BatchNormalization()(x)
x = layers.Activation('relu')(x)
x = layers.Dropout(0.3)(x)
x = layers.Dense(
64,
kernel_constraint=tf.keras.constraints.NonNeg(),
kernel_regularizer=layers.l2(0.01)
)(x)
x = layers.BatchNormalization()(x)
x = layers.Activation('relu')(x)
x = layers.Dropout(0.2)(x)
# Output layer con vincolo di non-negatività
output = layers.Dense(
1,
kernel_constraint=tf.keras.constraints.NonNeg(),
activation='relu', # Garantisce output non negativo
kernel_regularizer=layers.l2(0.01)
)(x)
model = layers.Model(inputs=inputs, outputs=output, name="SolarEnergy")
return model
def create_uv_model(input_shape):
"""
Modello migliorato per l'indice UV che sfrutta sia radiazione che energia solare.
Include vincoli di non-negatività e considera le relazioni non lineari tra le variabili.
"""
inputs = layers.Input(shape=input_shape)
# CNN branch per pattern locali
x1 = layers.Conv1D(
filters=64,
kernel_size=3,
padding='same',
kernel_constraint=tf.keras.constraints.NonNeg()
)(inputs)
x1 = layers.BatchNormalization()(x1)
x1 = layers.Activation('relu')(x1)
x1 = layers.MaxPooling1D(pool_size=2)(x1)
x1 = layers.Conv1D(
filters=32,
kernel_size=3,
padding='same',
kernel_constraint=tf.keras.constraints.NonNeg()
)(x1)
x1 = layers.BatchNormalization()(x1)
x1 = layers.Activation('relu')(x1)
x1 = layers.GlobalAveragePooling1D()(x1)
# Attention branch per relazioni complesse
# Specialmente utile per le relazioni con radiazione ed energia
x2 = layers.MultiHeadAttention(num_heads=4, key_dim=32)(inputs, inputs)
x2 = layers.BatchNormalization()(x2)
x2 = layers.Activation('relu')(x2)
x2 = layers.GlobalAveragePooling1D()(x2)
# Dense branch per le feature più recenti
x3 = layers.GlobalAveragePooling1D()(inputs)
x3 = layers.Dense(
64,
kernel_constraint=tf.keras.constraints.NonNeg(),
kernel_regularizer=layers.l2(0.01)
)(x3)
x3 = layers.BatchNormalization()(x3)
x3 = layers.Activation('relu')(x3)
# Fusion dei branch
x = layers.concatenate([x1, x2, x3])
# Dense layers con vincoli di non-negatività
x = layers.Dense(
128,
kernel_constraint=tf.keras.constraints.NonNeg(),
kernel_regularizer=layers.l2(0.01)
)(x)
x = layers.BatchNormalization()(x)
x = layers.Activation('relu')(x)
x = layers.Dropout(0.3)(x)
x = layers.Dense(
64,
kernel_constraint=tf.keras.constraints.NonNeg(),
kernel_regularizer=layers.l2(0.01)
)(x)
x = layers.BatchNormalization()(x)
x = layers.Activation('relu')(x)
x = layers.Dropout(0.2)(x)
# Output layer con vincolo di non-negatività
output = layers.Dense(
1,
kernel_constraint=tf.keras.constraints.NonNeg(),
activation='relu', # Garantisce output non negativo
kernel_regularizer=layers.l2(0.01)
)(x)
model = layers.Model(inputs=inputs, outputs=output, name="SolarUV")
return model
-385
View File
@@ -1,385 +0,0 @@
import tensorflow as tf
import numpy as np
from typing import Dict, Tuple, List
import os
import keras
from src.models.transformer import create_olive_oil_transformer
from src.models.callbacks import CustomCallback, WarmUpLearningRateSchedule
def compile_model(model: tf.keras.Model, learning_rate: float = 1e-3) -> tf.keras.Model:
"""
Compila il modello con le impostazioni ottimizzate.
Parameters
----------
model : tf.keras.Model
Modello da compilare
learning_rate : float
Learning rate iniziale
Returns
-------
tf.keras.Model
Modello compilato
"""
lr_schedule = WarmUpLearningRateSchedule(
initial_learning_rate=learning_rate,
warmup_steps=500,
decay_steps=5000
)
model.compile(
optimizer=tf.keras.optimizers.AdamW(
learning_rate=lr_schedule,
weight_decay=0.01
),
loss=tf.keras.losses.Huber(),
metrics=['mae']
)
return model
def create_callbacks(target_names: List[str],
val_data: Dict,
val_targets: np.ndarray) -> List[tf.keras.callbacks.Callback]:
"""
Crea i callbacks per il training del modello.
Parameters
----------
target_names : list
Lista dei nomi dei target
val_data : dict
Dati di validazione
val_targets : np.ndarray
Target di validazione
Returns
-------
list
Lista dei callbacks configurati
"""
class TargetSpecificMetric(tf.keras.callbacks.Callback):
def __init__(self, validation_data, target_names):
super().__init__()
self.validation_data = validation_data
self.target_names = target_names
def on_epoch_end(self, epoch, logs={}):
x_val, y_val = self.validation_data
y_pred = self.model.predict(x_val, verbose=0)
for i, name in enumerate(self.target_names):
mae = np.mean(np.abs(y_val[:, i] - y_pred[:, i]))
logs[f'val_{name}_mae'] = mae
# Crea le cartelle per i checkpoint e i log
os.makedirs('./kaggle/working/models/oil_transformer/checkpoints', exist_ok=True)
os.makedirs('./kaggle/working/models/oil_transformer/logs', exist_ok=True)
callbacks = [
# Early Stopping
tf.keras.callbacks.EarlyStopping(
monitor='val_loss',
patience=20,
restore_best_weights=True,
min_delta=0.0005,
mode='min'
),
# Model Checkpoint
tf.keras.callbacks.ModelCheckpoint(
filepath='./kaggle/working/models/oil_transformer/checkpoints/model_{epoch:02d}_{val_loss:.4f}.h5',
monitor='val_loss',
save_best_only=True,
mode='min',
save_weights_only=True
),
# Target specific metrics
TargetSpecificMetric(
validation_data=(val_data, val_targets),
target_names=target_names
),
# Reduce LR on Plateau
tf.keras.callbacks.ReduceLROnPlateau(
monitor='val_loss',
factor=0.5,
patience=10,
min_lr=1e-6,
verbose=1
),
# TensorBoard logging
tf.keras.callbacks.TensorBoard(
log_dir='./kaggle/working/models/oil_transformer/logs',
histogram_freq=1,
write_graph=True,
update_freq='epoch'
)
]
return callbacks
def setup_transformer_training(train_data: Dict,
train_targets: np.ndarray,
val_data: Dict,
val_targets: np.ndarray) -> Tuple[tf.keras.Model, List, List[str]]:
"""
Configura e prepara il transformer con dimensioni dinamiche.
Parameters
----------
train_data : dict
Dati di training
train_targets : np.ndarray
Target di training
val_data : dict
Dati di validazione
val_targets : np.ndarray
Target di validazione
Returns
-------
tuple
(model, callbacks, target_names)
"""
# Estrai le shape dai dati
temporal_shape = (train_data['temporal'].shape[1], train_data['temporal'].shape[2])
static_shape = (train_data['static'].shape[1],)
num_outputs = train_targets.shape[1]
print(f"Shape rilevate:")
print(f"- Temporal shape: {temporal_shape}")
print(f"- Static shape: {static_shape}")
print(f"- Numero di output: {num_outputs}")
# Target names
target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']
assert len(target_names) == num_outputs, \
f"Il numero di target names ({len(target_names)}) non corrisponde al numero di output ({num_outputs})"
# Crea il modello
model = create_olive_oil_transformer(
temporal_shape=temporal_shape,
static_shape=static_shape,
num_outputs=num_outputs
)
# Compila il modello
model = compile_model(model)
# Crea i callbacks
callbacks = create_callbacks(target_names, val_data, val_targets)
return model, callbacks, target_names
def train_transformer(train_data: Dict,
train_targets: np.ndarray,
val_data: Dict,
val_targets: np.ndarray,
epochs: int = 150,
batch_size: int = 64,
save_name: str = 'final_model') -> Tuple[tf.keras.Model, tf.keras.callbacks.History]:
"""
Funzione principale per l'addestramento del transformer.
Parameters
----------
train_data : dict
Dati di training
train_targets : np.ndarray
Target di training
val_data : dict
Dati di validazione
val_targets : np.ndarray
Target di validazione
epochs : int
Numero di epoche
batch_size : int
Dimensione del batch
save_name : str
Nome per salvare il modello
Returns
-------
tuple
(model, history)
"""
# Setup del modello
model, callbacks, target_names = setup_transformer_training(
train_data, train_targets, val_data, val_targets
)
# Mostra il summary del modello
model.summary()
os.makedirs(f"./kaggle/working/models/oil_transformer/", exist_ok=True)
keras.utils.plot_model(model, f"./kaggle/working/models/oil_transformer/{save_name}.png", show_shapes=True)
# Training
history = model.fit(
x=train_data,
y=train_targets,
validation_data=(val_data, val_targets),
epochs=epochs,
batch_size=batch_size,
callbacks=callbacks,
verbose=1,
shuffle=True
)
# Salva il modello
save_path = f'./kaggle/working/models/oil_transformer/{save_name}.keras'
model.save(save_path, save_format='keras')
os.makedirs(f'./kaggle/working/models/oil_transformer/weights/', exist_ok=True)
model.save_weights(f'./kaggle/working/models/oil_transformer/weights')
print(f"\nModello salvato in: {save_path}")
return model, history
def retrain_model(base_model: tf.keras.Model,
train_data: Dict,
train_targets: np.ndarray,
val_data: Dict,
val_targets: np.ndarray,
test_data: Dict,
test_targets: np.ndarray,
epochs: int = 50,
batch_size: int = 128) -> Tuple[tf.keras.Model, tf.keras.callbacks.History, Dict]:
"""
Implementa il retraining del modello con i dati combinati.
Parameters
----------
base_model : tf.keras.Model
Modello base da riaddestrate
train_data : dict
Dati di training
train_targets : np.ndarray
Target di training
val_data : dict
Dati di validazione
val_targets : np.ndarray
Target di validazione
test_data : dict
Dati di test
test_targets : np.ndarray
Target di test
epochs : int
Numero di epoche
batch_size : int
Dimensione del batch
Returns
-------
tuple
(model, history, final_metrics)
"""
print("Valutazione performance iniziali del modello...")
initial_metrics = {
'train': evaluate_model_performance(base_model, train_data, train_targets, "training"),
'val': evaluate_model_performance(base_model, val_data, val_targets, "validazione"),
'test': evaluate_model_performance(base_model, test_data, test_targets, "test")
}
# Combina i dati
combined_data = {
'temporal': np.concatenate([
train_data['temporal'],
val_data['temporal'],
test_data['temporal']
]),
'static': np.concatenate([
train_data['static'],
val_data['static'],
test_data['static']
])
}
combined_targets = np.concatenate([train_targets, val_targets, test_targets])
# Nuova suddivisione
indices = np.arange(len(combined_targets))
np.random.shuffle(indices)
split_idx = int(len(indices) * 0.9)
train_idx, val_idx = indices[:split_idx], indices[split_idx:]
# Prepara i dati per il retraining
retrain_data = {k: v[train_idx] for k, v in combined_data.items()}
retrain_targets = combined_targets[train_idx]
retrain_val_data = {k: v[val_idx] for k, v in combined_data.items()}
retrain_val_targets = combined_targets[val_idx]
# Callbacks
callbacks = [
tf.keras.callbacks.EarlyStopping(
monitor='val_loss',
patience=10,
restore_best_weights=True,
min_delta=0.0001
),
tf.keras.callbacks.ReduceLROnPlateau(
monitor='val_loss',
factor=0.2,
patience=5,
min_lr=1e-6,
verbose=1
),
tf.keras.callbacks.ModelCheckpoint(
filepath='./kaggle/working/models/oil_transformer/retrain_checkpoints/model_{epoch:02d}_{val_loss:.4f}.keras',
monitor='val_loss',
save_best_only=True,
mode='min',
save_weights_only=True
)
]
# Ricompila il modello
base_model = compile_model(
base_model,
learning_rate=1e-4 # Learning rate più basso per il fine-tuning
)
print("\nAvvio retraining...")
history = base_model.fit(
retrain_data,
retrain_targets,
validation_data=(retrain_val_data, retrain_val_targets),
epochs=epochs,
batch_size=batch_size,
callbacks=callbacks,
verbose=1
)
print("\nValutazione performance finali...")
final_metrics = {
'train': evaluate_model_performance(base_model, train_data, train_targets, "training"),
'val': evaluate_model_performance(base_model, val_data, val_targets, "validazione"),
'test': evaluate_model_performance(base_model, test_data, test_targets, "test")
}
# Salva il modello
save_path = './kaggle/working/models/oil_transformer/retrained_model.keras'
base_model.save(save_path, save_format='keras')
print(f"\nModello riaddestrato salvato in: {save_path}")
# Report miglioramenti
print("\nMiglioramenti delle performance:")
for dataset in ['train', 'val', 'test']:
print(f"\nSet {dataset}:")
for metric in initial_metrics[dataset].keys():
initial = initial_metrics[dataset][metric]
final = final_metrics[dataset][metric]
improvement = ((initial - final) / initial) * 100
print(f"{metric}: {improvement:.2f}% di miglioramento")
return base_model, history, final_metrics
-332
View File
@@ -1,332 +0,0 @@
import tensorflow as tf
from tf.keras import layers
from typing import Tuple, Optional, List
@tf.keras.saving.register_keras_serializable()
class DataAugmentation(layers.Layer):
"""
Layer personalizzato per l'augmentation dei dati temporali.
Attributes
----------
noise_stddev : float
Deviazione standard del rumore gaussiano
"""
def __init__(self, noise_stddev: float = 0.03, **kwargs):
super().__init__(**kwargs)
self.noise_stddev = noise_stddev
def call(self, inputs: tf.Tensor, training: Optional[bool] = None) -> tf.Tensor:
"""
Applica l'augmentation durante il training.
Parameters
----------
inputs : tf.Tensor
Dati di input
training : bool, optional
Flag che indica se siamo in fase di training
Returns
-------
tf.Tensor
Dati aumentati se in training, altrimenti dati originali
"""
if training:
return inputs + tf.random.normal(
shape=tf.shape(inputs),
mean=0.0,
stddev=self.noise_stddev
)
return inputs
def get_config(self) -> dict:
config = super().get_config()
config.update({"noise_stddev": self.noise_stddev})
return config
@tf.keras.saving.register_keras_serializable()
class PositionalEncoding(layers.Layer):
"""
Layer per l'encoding posizionale nel transformer.
Attributes
----------
d_model : int
Dimensionalità del modello
"""
def __init__(self, d_model: int, **kwargs):
super().__init__(**kwargs)
self.d_model = d_model
def build(self, input_shape: tf.TensorShape):
"""
Costruisce la matrice di encoding posizionale.
Parameters
----------
input_shape : tf.TensorShape
Shape dell'input
"""
_, seq_length, _ = input_shape
# Crea la matrice di encoding posizionale
position = tf.range(seq_length, dtype=tf.float32)[:, tf.newaxis]
div_term = tf.exp(
tf.range(0, self.d_model, 2, dtype=tf.float32) *
(-tf.math.log(10000.0) / self.d_model)
)
# Calcola sin e cos
pos_encoding = tf.zeros((1, seq_length, self.d_model))
pos_encoding_even = tf.sin(position * div_term)
pos_encoding_odd = tf.cos(position * div_term)
# Assegna i valori alle posizioni pari e dispari
pos_encoding = tf.concat(
[tf.expand_dims(pos_encoding_even, -1),
tf.expand_dims(pos_encoding_odd, -1)],
axis=-1
)
pos_encoding = tf.reshape(pos_encoding, (1, seq_length, -1))
pos_encoding = pos_encoding[:, :, :self.d_model]
# Salva l'encoding come peso non trainabile
self.pos_encoding = self.add_weight(
shape=(1, seq_length, self.d_model),
initializer=tf.keras.initializers.Constant(pos_encoding),
trainable=False,
name='positional_encoding'
)
super().build(input_shape)
def call(self, inputs: tf.Tensor) -> tf.Tensor:
"""
Applica l'encoding posizionale.
Parameters
----------
inputs : tf.Tensor
Dati di input
Returns
-------
tf.Tensor
Dati con encoding posizionale aggiunto
"""
batch_size = tf.shape(inputs)[0]
return inputs + tf.tile(self.pos_encoding, [batch_size, 1, 1])
def get_config(self) -> dict:
config = super().get_config()
config.update({"d_model": self.d_model})
return config
@tf.keras.saving.register_keras_serializable()
class OliveTransformerBlock(layers.Layer):
"""
Blocco transformer personalizzato per dati di produzione olive.
Attributes
----------
num_heads : int
Numero di teste di attenzione
key_dim : int
Dimensione delle chiavi
ff_dim : int
Dimensione del feed-forward network
dropout : float
Tasso di dropout
"""
def __init__(self, num_heads: int, key_dim: int, ff_dim: int, dropout: float = 0.1, **kwargs):
super().__init__(**kwargs)
self.num_heads = num_heads
self.key_dim = key_dim
self.ff_dim = ff_dim
self.dropout = dropout
# Multi-head attention
self.mha = layers.MultiHeadAttention(
num_heads=num_heads,
key_dim=key_dim,
dropout=dropout
)
# Feed-forward network
self.ffn = tf.keras.Sequential([
layers.Dense(ff_dim, activation="gelu"),
layers.Dropout(dropout),
layers.Dense(key_dim)
])
# Layer normalization
self.layernorm1 = layers.LayerNormalization(epsilon=1e-6)
self.layernorm2 = layers.LayerNormalization(epsilon=1e-6)
# Dropout layers
self.dropout1 = layers.Dropout(dropout)
self.dropout2 = layers.Dropout(dropout)
def call(self, inputs: tf.Tensor, training: Optional[bool] = None) -> tf.Tensor:
"""
Forward pass del blocco transformer.
Parameters
----------
inputs : tf.Tensor
Dati di input
training : bool, optional
Flag di training
Returns
-------
tf.Tensor
Output del blocco transformer
"""
# Multi-head attention
attn_output = self.mha(inputs, inputs)
attn_output = self.dropout1(attn_output, training=training)
out1 = self.layernorm1(inputs + attn_output)
# Feed-forward network
ffn_output = self.ffn(out1)
ffn_output = self.dropout2(ffn_output, training=training)
return self.layernorm2(out1 + ffn_output)
def get_config(self) -> dict:
config = super().get_config()
config.update({
"num_heads": self.num_heads,
"key_dim": self.key_dim,
"ff_dim": self.ff_dim,
"dropout": self.dropout
})
return config
def create_olive_oil_transformer(
temporal_shape: Tuple[int, int],
static_shape: Tuple[int],
num_outputs: int,
d_model: int = 128,
num_heads: int = 8,
ff_dim: int = 256,
num_transformer_blocks: int = 4,
mlp_units: List[int] = [256, 128, 64],
dropout: float = 0.2
) -> tf.keras.Model:
"""
Crea un transformer per la predizione della produzione di olio d'oliva.
Parameters
----------
temporal_shape : tuple
Shape dei dati temporali (timesteps, features)
static_shape : tuple
Shape dei dati statici (features,)
num_outputs : int
Numero di output del modello
d_model : int
Dimensionalità del modello
num_heads : int
Numero di teste di attenzione
ff_dim : int
Dimensione del feed-forward network
num_transformer_blocks : int
Numero di blocchi transformer
mlp_units : list
Unità nei layer MLP
dropout : float
Tasso di dropout
Returns
-------
tf.keras.Model
Modello transformer configurato
"""
# Input layers
temporal_input = layers.Input(shape=temporal_shape, name='temporal')
static_input = layers.Input(shape=static_shape, name='static')
# === TEMPORAL PATH ===
x = layers.LayerNormalization(epsilon=1e-6)(temporal_input)
x = DataAugmentation()(x)
# Temporal projection
x = layers.Dense(d_model // 2, activation='gelu',
kernel_regularizer=tf.keras.regularizers.l2(1e-5))(x)
x = layers.Dropout(dropout)(x)
x = layers.Dense(d_model, activation='gelu',
kernel_regularizer=tf.keras.regularizers.l2(1e-5))(x)
# Positional encoding
x = PositionalEncoding(d_model)(x)
# Transformer blocks
skip_connection = x
for _ in range(num_transformer_blocks):
x = OliveTransformerBlock(num_heads, d_model, ff_dim, dropout)(x)
# Add final skip connection
x = layers.Add()([x, skip_connection])
# Temporal pooling
attention_pooled = layers.MultiHeadAttention(
num_heads=num_heads,
key_dim=d_model // 4
)(x, x)
attention_pooled = layers.GlobalAveragePooling1D()(attention_pooled)
# Additional pooling operations
avg_pooled = layers.GlobalAveragePooling1D()(x)
max_pooled = layers.GlobalMaxPooling1D()(x)
# Combine pooling results
temporal_features = layers.Concatenate()([attention_pooled, avg_pooled, max_pooled])
# === STATIC PATH ===
static_features = layers.LayerNormalization(epsilon=1e-6)(static_input)
for units in [256, 128, 64]:
static_features = layers.Dense(
units,
activation='gelu',
kernel_regularizer=tf.keras.regularizers.l2(1e-5)
)(static_features)
static_features = layers.Dropout(dropout)(static_features)
# === FEATURE FUSION ===
combined = layers.Concatenate()([temporal_features, static_features])
# === MLP HEAD ===
x = combined
for units in mlp_units:
x = layers.BatchNormalization()(x)
x = layers.Dense(
units,
activation="gelu",
kernel_regularizer=tf.keras.regularizers.l2(1e-5)
)(x)
x = layers.Dropout(dropout)(x)
# Output layer
outputs = layers.Dense(
num_outputs,
activation='linear',
kernel_regularizer=tf.keras.regularizers.l2(1e-5)
)(x)
# Create model
model = tf.keras.Model(
inputs={'temporal': temporal_input, 'static': static_input},
outputs=outputs,
name='OliveOilTransformer'
)
return model
+1366 -918
View File
File diff suppressed because it is too large. Load diff
+16 -4
View File
@@ -5,12 +5,17 @@
{ {
"variety": "Nocellara dell'Etna", "variety": "Nocellara dell'Etna",
"technique": "Tradizionale", "technique": "Tradizionale",
"percentage": 70 "percentage": 30
}, },
{ {
"variety": "Frantoio", "variety": "Frantoio",
"technique": "Tradizionale", "technique": "Tradizionale",
"percentage": 30 "percentage": 30
},
{
"variety": "Coratina",
"technique": "Tradizionale",
"percentage": 40
} }
] ]
}, },
@@ -18,12 +23,14 @@
"fixed": { "fixed": {
"ammortamento": 2000, "ammortamento": 2000,
"assicurazione": 500, "assicurazione": 500,
"manutenzione": 800 "manutenzione": 800,
"certificazioni": 3000
}, },
"variable": { "variable": {
"raccolta": 0.35, "raccolta": 0.35,
"potatura": 600, "potatura": 600,
"fertilizzanti": 400 "fertilizzanti": 400,
"irrigazione": 300
}, },
"transformation": { "transformation": {
"molitura": 0.15, "molitura": 0.15,
@@ -31,6 +38,11 @@
"bottiglia": 1.2, "bottiglia": 1.2,
"etichettatura": 0.3 "etichettatura": 0.3
}, },
"selling_price": 12 "marketing": {
"budget_annuale": 15000,
"costi_commerciali": 0.5,
"prezzo_vendita": 12,
"perc_vendita_diretta": 30
}
} }
} }
@@ -0,0 +1,7 @@
Metadata-Version: 2.1
Name: olive_oil_dashboard
Version: 0.1
Requires-Dist: pandas
Requires-Dist: numpy
Requires-Dist: tensorflow
Requires-Dist: scikit-learn
@@ -0,0 +1,11 @@
README.md
setup.py
model_train/__init__.py
model_train/create_train_dataset.py
olive_oil_dashboard.egg-info/PKG-INFO
olive_oil_dashboard.egg-info/SOURCES.txt
olive_oil_dashboard.egg-info/dependency_links.txt
olive_oil_dashboard.egg-info/requires.txt
olive_oil_dashboard.egg-info/top_level.txt
utils/__init__.py
utils/helpers.py
@@ -0,0 +1 @@
@@ -0,0 +1,4 @@
pandas
numpy
tensorflow
scikit-learn
@@ -0,0 +1,2 @@
model_train
utils
+15
View File
@@ -0,0 +1,15 @@
# setup.py
from setuptools import setup, find_packages
setup(
name="olive_oil_dashboard",
version="0.1",
packages=find_packages(),
install_requires=[
"pandas",
"numpy",
"tensorflow",
"scikit-learn",
# aggiungi altre dipendenze necessarie
]
)
Binary file not shown.
File renamed without changes.
Binary file not shown.
-502
View File
@@ -1,502 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Addestramento Modello per Previsione Produzione Olio d'Oliva\n",
"\n",
"Questo notebook utilizza le funzioni modularizzate per:\n",
"1. Caricare e preprocessare i dati meteorologici\n",
"2. Preparare i dati per il training\n",
"3. Configurare e addestrare il modello"
]
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-11-07T17:19:09.011001Z",
"start_time": "2024-11-07T17:18:13.513Z"
}
},
"cell_type": "code",
"source": [
"#!apt-get update\n",
"#!apt-get install graphviz -y\n",
"\n",
"!pip install tensorflow\n",
"!pip install numpy\n",
"!pip install pandas\n",
"\n",
"!pip install keras\n",
"!pip install scikit-learn\n",
"!pip install matplotlib\n",
"!pip install joblib\n",
"!pip install pyarrow\n",
"!pip install fastparquet\n",
"!pip install scipy\n",
"!pip install seaborn\n",
"!pip install tqdm\n",
"!pip install pydot\n",
"!pip install tensorflow-io\n",
"!pip install pvlib"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: tensorflow in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (2.16.2)\r\n",
"Requirement already satisfied: absl-py>=1.0.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (2.1.0)\r\n",
"Requirement already satisfied: astunparse>=1.6.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (1.6.3)\r\n",
"Requirement already satisfied: flatbuffers>=23.5.26 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (24.3.25)\r\n",
"Requirement already satisfied: gast!=0.5.0,!=0.5.1,!=0.5.2,>=0.2.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (0.4.0)\r\n",
"Requirement already satisfied: google-pasta>=0.1.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (0.2.0)\r\n",
"Requirement already satisfied: h5py>=3.10.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (3.11.0)\r\n",
"Requirement already satisfied: libclang>=13.0.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (18.1.1)\r\n",
"Requirement already satisfied: ml-dtypes~=0.3.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (0.3.2)\r\n",
"Requirement already satisfied: opt-einsum>=2.3.2 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (3.3.0)\r\n",
"Requirement already satisfied: packaging in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (24.1)\r\n",
"Requirement already satisfied: protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.20.3 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (3.20.3)\r\n",
"Requirement already satisfied: requests<3,>=2.21.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (2.32.3)\r\n",
"Requirement already satisfied: setuptools in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (72.1.0)\r\n",
"Requirement already satisfied: six>=1.12.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (1.16.0)\r\n",
"Requirement already satisfied: termcolor>=1.1.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (2.1.0)\r\n",
"Requirement already satisfied: typing-extensions>=3.6.6 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (4.11.0)\r\n",
"Requirement already satisfied: wrapt>=1.11.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (1.14.1)\r\n",
"Requirement already satisfied: grpcio<2.0,>=1.24.3 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (1.48.2)\r\n",
"Requirement already satisfied: tensorboard<2.17,>=2.16 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (2.16.2)\r\n",
"Requirement already satisfied: keras>=3.0.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (3.5.0)\r\n",
"Requirement already satisfied: tensorflow-io-gcs-filesystem>=0.23.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (0.37.1)\r\n",
"Requirement already satisfied: numpy<2.0.0,>=1.23.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow) (1.23.5)\r\n",
"Requirement already satisfied: wheel<1.0,>=0.23.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from astunparse>=1.6.0->tensorflow) (0.43.0)\r\n",
"Requirement already satisfied: rich in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras>=3.0.0->tensorflow) (13.8.0)\r\n",
"Requirement already satisfied: namex in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras>=3.0.0->tensorflow) (0.0.8)\r\n",
"Requirement already satisfied: optree in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras>=3.0.0->tensorflow) (0.12.1)\r\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from requests<3,>=2.21.0->tensorflow) (3.3.2)\r\n",
"Requirement already satisfied: idna<4,>=2.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from requests<3,>=2.21.0->tensorflow) (3.7)\r\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from requests<3,>=2.21.0->tensorflow) (2.2.2)\r\n",
"Requirement already satisfied: certifi>=2017.4.17 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from requests<3,>=2.21.0->tensorflow) (2024.8.30)\r\n",
"Requirement already satisfied: markdown>=2.6.8 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorboard<2.17,>=2.16->tensorflow) (3.4.1)\r\n",
"Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorboard<2.17,>=2.16->tensorflow) (0.7.0)\r\n",
"Requirement already satisfied: werkzeug>=1.0.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorboard<2.17,>=2.16->tensorflow) (3.0.3)\r\n",
"Requirement already satisfied: importlib-metadata>=4.4 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from markdown>=2.6.8->tensorboard<2.17,>=2.16->tensorflow) (7.0.1)\r\n",
"Requirement already satisfied: MarkupSafe>=2.1.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from werkzeug>=1.0.1->tensorboard<2.17,>=2.16->tensorflow) (2.1.3)\r\n",
"Requirement already satisfied: markdown-it-py>=2.2.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from rich->keras>=3.0.0->tensorflow) (3.0.0)\r\n",
"Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from rich->keras>=3.0.0->tensorflow) (2.15.1)\r\n",
"Requirement already satisfied: zipp>=0.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from importlib-metadata>=4.4->markdown>=2.6.8->tensorboard<2.17,>=2.16->tensorflow) (3.17.0)\r\n",
"Requirement already satisfied: mdurl~=0.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from markdown-it-py>=2.2.0->rich->keras>=3.0.0->tensorflow) (0.1.2)\r\n",
"Requirement already satisfied: numpy in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (1.23.5)\r\n",
"Requirement already satisfied: pandas in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (2.2.2)\r\n",
"Requirement already satisfied: numpy>=1.22.4 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas) (1.23.5)\r\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas) (2.9.0.post0)\r\n",
"Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas) (2024.1)\r\n",
"Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas) (2023.3)\r\n",
"Requirement already satisfied: six>=1.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\r\n",
"Requirement already satisfied: keras in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (3.5.0)\r\n",
"Requirement already satisfied: absl-py in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras) (2.1.0)\r\n",
"Requirement already satisfied: numpy in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras) (1.23.5)\r\n",
"Requirement already satisfied: rich in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras) (13.8.0)\r\n",
"Requirement already satisfied: namex in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras) (0.0.8)\r\n",
"Requirement already satisfied: h5py in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras) (3.11.0)\r\n",
"Requirement already satisfied: optree in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras) (0.12.1)\r\n",
"Requirement already satisfied: ml-dtypes in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras) (0.3.2)\r\n",
"Requirement already satisfied: packaging in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from keras) (24.1)\r\n",
"Requirement already satisfied: typing-extensions>=4.5.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from optree->keras) (4.11.0)\r\n",
"Requirement already satisfied: markdown-it-py>=2.2.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from rich->keras) (3.0.0)\r\n",
"Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from rich->keras) (2.15.1)\r\n",
"Requirement already satisfied: mdurl~=0.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from markdown-it-py>=2.2.0->rich->keras) (0.1.2)\r\n",
"Requirement already satisfied: scikit-learn in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (1.5.1)\r\n",
"Requirement already satisfied: numpy>=1.19.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from scikit-learn) (1.23.5)\r\n",
"Requirement already satisfied: scipy>=1.6.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from scikit-learn) (1.11.4)\r\n",
"Requirement already satisfied: joblib>=1.2.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from scikit-learn) (1.4.2)\r\n",
"Requirement already satisfied: threadpoolctl>=3.1.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from scikit-learn) (3.5.0)\r\n",
"Requirement already satisfied: matplotlib in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (3.8.4)\r\n",
"Requirement already satisfied: contourpy>=1.0.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (1.2.0)\r\n",
"Requirement already satisfied: cycler>=0.10 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (0.11.0)\r\n",
"Requirement already satisfied: fonttools>=4.22.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (4.51.0)\r\n",
"Requirement already satisfied: kiwisolver>=1.3.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (1.4.4)\r\n",
"Requirement already satisfied: numpy>=1.21 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (1.23.5)\r\n",
"Requirement already satisfied: packaging>=20.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (24.1)\r\n",
"Requirement already satisfied: pillow>=8 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (10.4.0)\r\n",
"Requirement already satisfied: pyparsing>=2.3.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (3.0.9)\r\n",
"Requirement already satisfied: python-dateutil>=2.7 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (2.9.0.post0)\r\n",
"Requirement already satisfied: importlib-resources>=3.2.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib) (6.4.0)\r\n",
"Requirement already satisfied: zipp>=3.1.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from importlib-resources>=3.2.0->matplotlib) (3.17.0)\r\n",
"Requirement already satisfied: six>=1.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\r\n",
"Requirement already satisfied: joblib in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (1.4.2)\r\n",
"Requirement already satisfied: pyarrow in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (17.0.0)\r\n",
"Requirement already satisfied: numpy>=1.16.6 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pyarrow) (1.23.5)\r\n",
"Requirement already satisfied: fastparquet in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (2024.5.0)\r\n",
"Requirement already satisfied: pandas>=1.5.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from fastparquet) (2.2.2)\r\n",
"Requirement already satisfied: numpy in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from fastparquet) (1.23.5)\r\n",
"Requirement already satisfied: cramjam>=2.3 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from fastparquet) (2.8.3)\r\n",
"Requirement already satisfied: fsspec in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from fastparquet) (2024.6.1)\r\n",
"Requirement already satisfied: packaging in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from fastparquet) (24.1)\r\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas>=1.5.0->fastparquet) (2.9.0.post0)\r\n",
"Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas>=1.5.0->fastparquet) (2024.1)\r\n",
"Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas>=1.5.0->fastparquet) (2023.3)\r\n",
"Requirement already satisfied: six>=1.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas>=1.5.0->fastparquet) (1.16.0)\r\n",
"Requirement already satisfied: scipy in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (1.11.4)\r\n",
"Requirement already satisfied: numpy<1.28.0,>=1.21.6 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from scipy) (1.23.5)\r\n",
"Requirement already satisfied: seaborn in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (0.13.2)\r\n",
"Requirement already satisfied: numpy!=1.24.0,>=1.20 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from seaborn) (1.23.5)\r\n",
"Requirement already satisfied: pandas>=1.2 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from seaborn) (2.2.2)\r\n",
"Requirement already satisfied: matplotlib!=3.6.1,>=3.4 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from seaborn) (3.8.4)\r\n",
"Requirement already satisfied: contourpy>=1.0.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.2.0)\r\n",
"Requirement already satisfied: cycler>=0.10 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (0.11.0)\r\n",
"Requirement already satisfied: fonttools>=4.22.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (4.51.0)\r\n",
"Requirement already satisfied: kiwisolver>=1.3.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.4.4)\r\n",
"Requirement already satisfied: packaging>=20.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (24.1)\r\n",
"Requirement already satisfied: pillow>=8 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (10.4.0)\r\n",
"Requirement already satisfied: pyparsing>=2.3.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (3.0.9)\r\n",
"Requirement already satisfied: python-dateutil>=2.7 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.9.0.post0)\r\n",
"Requirement already satisfied: importlib-resources>=3.2.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (6.4.0)\r\n",
"Requirement already satisfied: pytz>=2020.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas>=1.2->seaborn) (2024.1)\r\n",
"Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas>=1.2->seaborn) (2023.3)\r\n",
"Requirement already satisfied: zipp>=3.1.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from importlib-resources>=3.2.0->matplotlib!=3.6.1,>=3.4->seaborn) (3.17.0)\r\n",
"Requirement already satisfied: six>=1.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn) (1.16.0)\r\n",
"Collecting tqdm\r\n",
" Downloading tqdm-4.67.0-py3-none-any.whl.metadata (57 kB)\r\n",
"Downloading tqdm-4.67.0-py3-none-any.whl (78 kB)\r\n",
"Installing collected packages: tqdm\r\n",
"Successfully installed tqdm-4.67.0\r\n",
"Collecting pydot\r\n",
" Downloading pydot-3.0.2-py3-none-any.whl.metadata (10 kB)\r\n",
"Requirement already satisfied: pyparsing>=3.0.9 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pydot) (3.0.9)\r\n",
"Downloading pydot-3.0.2-py3-none-any.whl (35 kB)\r\n",
"Installing collected packages: pydot\r\n",
"Successfully installed pydot-3.0.2\r\n",
"Collecting tensorflow-io\r\n",
" Downloading tensorflow_io-0.37.1-cp39-cp39-macosx_12_0_arm64.whl.metadata (14 kB)\r\n",
"Requirement already satisfied: tensorflow-io-gcs-filesystem==0.37.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from tensorflow-io) (0.37.1)\r\n",
"Downloading tensorflow_io-0.37.1-cp39-cp39-macosx_12_0_arm64.whl (31.8 MB)\r\n",
"\u001B[2K \u001B[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001B[0m \u001B[32m31.8/31.8 MB\u001B[0m \u001B[31m1.2 MB/s\u001B[0m eta \u001B[36m0:00:00\u001B[0m00:01\u001B[0m00:01\u001B[0m0m\r\n",
"\u001B[?25hInstalling collected packages: tensorflow-io\r\n",
"Successfully installed tensorflow-io-0.37.1\r\n",
"Collecting pvlib\r\n",
" Downloading pvlib-0.11.1-py3-none-any.whl.metadata (2.8 kB)\r\n",
"Requirement already satisfied: numpy>=1.19.3 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pvlib) (1.23.5)\r\n",
"Requirement already satisfied: pandas>=1.3.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pvlib) (2.2.2)\r\n",
"Requirement already satisfied: pytz in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pvlib) (2024.1)\r\n",
"Requirement already satisfied: requests in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pvlib) (2.32.3)\r\n",
"Requirement already satisfied: scipy>=1.6.0 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pvlib) (1.11.4)\r\n",
"Requirement already satisfied: h5py in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pvlib) (3.11.0)\r\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas>=1.3.0->pvlib) (2.9.0.post0)\r\n",
"Requirement already satisfied: tzdata>=2022.7 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from pandas>=1.3.0->pvlib) (2023.3)\r\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from requests->pvlib) (3.3.2)\r\n",
"Requirement already satisfied: idna<4,>=2.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from requests->pvlib) (3.7)\r\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from requests->pvlib) (2.2.2)\r\n",
"Requirement already satisfied: certifi>=2017.4.17 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from requests->pvlib) (2024.8.30)\r\n",
"Requirement already satisfied: six>=1.5 in /opt/homebrew/anaconda3/envs/ml_env/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas>=1.3.0->pvlib) (1.16.0)\r\n",
"Downloading pvlib-0.11.1-py3-none-any.whl (29.5 MB)\r\n",
"\u001B[2K \u001B[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001B[0m \u001B[32m29.5/29.5 MB\u001B[0m \u001B[31m2.2 MB/s\u001B[0m eta \u001B[36m0:00:00\u001B[0m00:01\u001B[0m00:01\u001B[0m\r\n",
"\u001B[?25hInstalling collected packages: pvlib\r\n",
"Successfully installed pvlib-0.11.1\r\n"
]
}
],
"execution_count": 1
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": [
"import tensorflow as tf\n",
"import keras\n",
"\n",
"print(f\"Keras version: {keras.__version__}\")\n",
"print(f\"TensorFlow version: {tf.__version__}\")\n",
"print(f\"TensorFlow version: {tf.__version__}\")\n",
"print(f\"CUDA available: {tf.test.is_built_with_cuda()}\")\n",
"print(f\"GPU devices: {tf.config.list_physical_devices('GPU')}\")\n",
"\n",
"# GPU configuration\n",
"gpus = tf.config.experimental.list_physical_devices('GPU')\n",
"if gpus:\n",
" try:\n",
" for gpu in gpus:\n",
" tf.config.experimental.set_memory_growth(gpu, True)\n",
" logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n",
" print(len(gpus), \"Physical GPUs,\", len(logical_gpus), \"Logical GPUs\")\n",
" except RuntimeError as e:\n",
" print(e)"
]
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": [
"# Test semplice per verificare che la GPU funzioni\n",
"def test_gpu():\n",
" print(\"TensorFlow version:\", tf.__version__)\n",
" print(\"\\nDispositivi disponibili:\")\n",
" print(tf.config.list_physical_devices())\n",
"\n",
" # Creiamo e moltiplichiamo due tensori sulla GPU\n",
" with tf.device('/GPU:0'):\n",
" a = tf.random.normal([10000, 10000])\n",
" b = tf.random.normal([10000, 10000])\n",
" c = tf.matmul(a, b)\n",
"\n",
" print(\"\\nShape del risultato:\", c.shape)\n",
" print(\"Device del tensore:\", c.device)\n",
" return \"Test completato con successo!\"\n",
"\n",
"\n",
"test_gpu()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"source": [
"# Imports necessari\n",
"from src.data.data_loader import load_weather_data, load_olive_varieties\n",
"from src.data.data_processor import prepare_solar_data, prepare_transformer_data\n",
"from src.features.weather_features import add_solar_features, add_environmental_features\n",
"from src.features.temporal_features import add_time_features\n",
"from src.models.training import train_transformer, setup_transformer_training\n",
"from src.utils.helpers import get_optimal_workers\n",
"from src.visualization.plots import plot_correlation_matrix\n",
"import pandas as pd\n",
"import os"
],
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. Caricamento e Preparazione Dati"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"source": [
"random_state_value = 42\n",
"\n",
"base_dir = './kaggle'\n",
"input_dir = f'{base_dir}/input'\n",
"working_dir = f'{base_dir}/working'\n",
"working_data_dir = f'{working_dir}/data'\n",
"data_models_dir = f'{working_data_dir}/models'\n",
"\n",
"os.makedirs(working_dir, exist_ok=True)\n",
"os.makedirs(working_data_dir, exist_ok=True)\n",
"os.makedirs(data_models_dir, exist_ok=True)\n",
"\n",
"# Carica i dati meteorologici\n",
"weather_data = load_weather_data(\n",
" f'{input_dir}/olive-oil/weather_data.parquet',\n",
" start_year=2010\n",
")\n",
"\n",
"# Carica i dati delle varietà di olive\n",
"olive_varieties = load_olive_varieties(\n",
" f'{input_dir}/olive-oil/variety_olive_oil_production.csv'\n",
")\n",
"\n",
"print(f\"Shape dati meteo: {weather_data.shape}\")\n",
"print(f\"Shape dati olive: {olive_varieties.shape}\")"
],
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Feature Engineering"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"source": [
"# Aggiungi feature temporali\n",
"weather_data = add_time_features(weather_data)\n",
"\n",
"# Aggiungi feature solari e ambientali\n",
"weather_data = add_solar_features(weather_data)\n",
"weather_data = add_environmental_features(weather_data)\n",
"\n",
"# Definisci le feature da utilizzare\n",
"features = [\n",
" 'temp', 'tempmin', 'tempmax', 'humidity', 'cloudcover',\n",
" 'windspeed', 'pressure', 'visibility',\n",
" 'hour_sin', 'hour_cos', 'month_sin', 'month_cos',\n",
" 'day_of_year_sin', 'day_of_year_cos',\n",
" 'temp_humidity', 'temp_cloudcover', 'visibility_cloudcover',\n",
" 'clear_sky_factor', 'day_length',\n",
" 'temp_1h_lag', 'cloudcover_1h_lag', 'humidity_1h_lag',\n",
" 'temp_rolling_mean_6h', 'cloudcover_rolling_mean_6h'\n",
" ] + [col for col in weather_data.columns if 'season_' in col or 'time_period_' in col]\n",
"\n",
"print(f\"Numero totale di feature: {len(features)}\")"
],
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Analisi delle Correlazioni"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"source": [
"# Analizza correlazioni tra feature\n",
"plot_correlation_matrix(\n",
" weather_data[features + ['solarradiation', 'solarenergy', 'uvindex']],\n",
" title='Correlazioni tra Feature Meteorologiche'\n",
")"
],
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. Preparazione Dati per il Training"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"source": [
"# Prepara i dati per il modello\n",
"X_scaled, scaler_X, y_scaled, scaler_y, data_after_2010 = prepare_solar_data(\n",
" weather_data,\n",
" features\n",
")\n",
"\n",
"# Prepara i dati per il transformer\n",
"(train_data, train_targets), (val_data, val_targets), (test_data, test_targets), scalers = prepare_transformer_data(\n",
" data_after_2010, olive_varieties)\n",
"\n",
"print(\"\\nShape dei dati:\")\n",
"print(f\"Training - Temporal: {train_data['temporal'].shape}, Static: {train_data['static'].shape}\")\n",
"print(f\"Validation - Temporal: {val_data['temporal'].shape}, Static: {val_data['static'].shape}\")\n",
"print(f\"Test - Temporal: {test_data['temporal'].shape}, Static: {test_data['static'].shape}\")"
],
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Training del Modello"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"source": [
"# Training del transformer\n",
"model, history = train_transformer(\n",
" train_data=train_data,\n",
" train_targets=train_targets,\n",
" val_data=val_data,\n",
" val_targets=val_targets,\n",
" epochs=150,\n",
" batch_size=64,\n",
" save_name='weather_transformer'\n",
")"
],
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Valutazione del Modello"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"source": [
"from src.utils.metrics import calculate_real_error, evaluate_model_performance\n",
"\n",
"# Calcola gli errori reali\n",
"percentage_errors, absolute_errors = calculate_real_error(\n",
" model,\n",
" test_data,\n",
" test_targets,\n",
" scaler_y,\n",
" target_names=['solarradiation', 'solarenergy', 'uvindex']\n",
")\n",
"\n",
"# Valuta le performance del modello\n",
"metrics = evaluate_model_performance(\n",
" model,\n",
" test_data,\n",
" test_targets,\n",
" 'test'\n",
")"
],
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7. Visualizzazione dei Risultati"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"source": [
"from src.visualization.plots import (\n",
" plot_production_trends,\n",
" plot_correlation_matrix\n",
")\n",
"\n",
"# Plot dei trend di produzione\n",
"predictions = model.predict(test_data)\n",
"predictions_real = scaler_y.inverse_transform(predictions)\n",
"\n",
"# Crea DataFrame con predizioni\n",
"results_df = pd.DataFrame(\n",
" predictions_real,\n",
" columns=['solarradiation', 'solarenergy', 'uvindex']\n",
")\n",
"\n",
"# Plot delle correlazioni tra predizioni\n",
"plot_correlation_matrix(\n",
" results_df,\n",
" title='Correlazioni tra Predizioni'\n",
")\n",
"\n",
"# Plot dei trend temporali\n",
"plot_production_trends(results_df)"
],
"outputs": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
View File
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
+166 -2
View File
@@ -2,7 +2,8 @@ import psutil
import multiprocessing import multiprocessing
import re import re
import pandas as pd import pandas as pd
from typing import List from typing import List, Dict
import numpy as np
def get_optimal_workers() -> int: def get_optimal_workers() -> int:
@@ -212,4 +213,167 @@ def get_full_data(simulated_data: pd.DataFrame,
full_data[f'{col}_ma3'] = full_data[col].rolling(window=3, min_periods=1).mean() full_data[f'{col}_ma3'] = full_data[col].rolling(window=3, min_periods=1).mean()
full_data[f'{col}_ma5'] = full_data[col].rolling(window=5, min_periods=1).mean() full_data[f'{col}_ma5'] = full_data[col].rolling(window=5, min_periods=1).mean()
return full_data return full_data
import numpy as np
from typing import List, Dict
def prepare_static_features_multiple(varieties_info: List[Dict],
percentages: List[float],
hectares: float,
all_varieties: List[str]) -> np.ndarray:
"""
Prepara le feature statiche per multiple varietà.
Parameters
----------
varieties_info : List[Dict]
Lista di dizionari contenenti le informazioni sulle varietà selezionate
percentages : List[float]
Lista delle percentuali corrispondenti a ciascuna varietà selezionata
hectares : float
Numero di ettari totali
all_varieties : List[str]
Lista di tutte le possibili varietà nel dataset originale
Returns
-------
np.ndarray
Array numpy contenente tutte le feature statiche
"""
# Inizializza un dizionario per tutte le varietà possibili
variety_data = {variety.lower(): {
'pct': 0,
'prod_t_ha': 0,
'tech': '',
'oil_prod_t_ha': 0,
'oil_prod_l_ha': 0,
'min_yield_pct': 0,
'max_yield_pct': 0,
'min_oil_prod_l_ha': 0,
'max_oil_prod_l_ha': 0,
'avg_oil_prod_l_ha': 0,
'l_per_t': 0,
'min_l_per_t': 0,
'max_l_per_t': 0,
'avg_l_per_t': 0,
'water_need_spring': 0,
'water_need_summer': 0,
'water_need_autumn': 0,
'water_need_winter': 0,
'annual_water_need': 0,
'optimal_temp': 0,
'drought_resistance': 0
} for variety in all_varieties}
# Aggiorna i dati per le varietà selezionate
for variety_info, percentage in zip(varieties_info, percentages):
variety_name = clean_column_name(variety_info['variet_di_olive']).lower()
technique = clean_column_name(variety_info['tecnica_di_coltivazione']).lower()
if variety_name not in variety_data:
print(f"Attenzione: La varietà '{variety_name}' non è presente nella lista delle varietà conosciute.")
continue
variety_data[variety_name].update({
'pct': percentage / 100,
'prod_t_ha': variety_info['produzione_tonnellateettaro'],
'tech': technique,
'oil_prod_t_ha': variety_info['produzione_olio_tonnellateettaro'],
'oil_prod_l_ha': variety_info['produzione_olio_litriettaro'],
'min_yield_pct': variety_info['min__resa'],
'max_yield_pct': variety_info['max__resa'],
'min_oil_prod_l_ha': variety_info['min_produzione_olio_litriettaro'],
'max_oil_prod_l_ha': variety_info['max_produzione_olio_litriettaro'],
'avg_oil_prod_l_ha': variety_info['media_produzione_olio_litriettaro'],
'l_per_t': variety_info['litri_per_tonnellata'],
'min_l_per_t': variety_info['min_litri_per_tonnellata'],
'max_l_per_t': variety_info['max_litri_per_tonnellata'],
'avg_l_per_t': variety_info['media_litri_per_tonnellata'],
'water_need_spring': variety_info['fabbisogno_acqua_primavera_mettaro'],
'water_need_summer': variety_info['fabbisogno_acqua_estate_mettaro'],
'water_need_autumn': variety_info['fabbisogno_acqua_autunno_mettaro'],
'water_need_winter': variety_info['fabbisogno_acqua_inverno_mettaro'],
'annual_water_need': variety_info['fabbisogno_idrico_annuale_mettaro'],
'optimal_temp': variety_info['temperatura_ottimale'],
'drought_resistance': variety_info['resistenza_alla_siccit']
})
# Crea il vettore delle feature
static_features = [hectares]
# Lista delle feature per ogni varietà
variety_features = ['pct', 'prod_t_ha', 'oil_prod_t_ha', 'oil_prod_l_ha',
'min_yield_pct', 'max_yield_pct', 'min_oil_prod_l_ha',
'max_oil_prod_l_ha', 'avg_oil_prod_l_ha', 'l_per_t',
'min_l_per_t', 'max_l_per_t', 'avg_l_per_t',
'water_need_spring', 'water_need_summer', 'water_need_autumn',
'water_need_winter', 'annual_water_need', 'optimal_temp',
'drought_resistance']
# Appiattisci i dati delle varietà
for variety in all_varieties:
variety_lower = variety.lower()
# Feature esistenti
for feature in variety_features:
static_features.append(variety_data[variety_lower][feature])
# Feature binarie per le tecniche
for technique in ['tradizionale', 'intensiva', 'superintensiva']:
static_features.append(1 if variety_data[variety_lower]['tech'] == technique else 0)
return np.array(static_features).reshape(1, -1)
def get_feature_names(all_varieties: List[str]) -> List[str]:
"""
Genera i nomi delle feature nell'ordine corretto.
Parameters
----------
all_varieties : List[str]
Lista di tutte le varietà possibili
Returns
-------
List[str]
Lista dei nomi delle feature
"""
feature_names = ['hectares']
variety_features = ['pct', 'prod_t_ha', 'oil_prod_t_ha', 'oil_prod_l_ha',
'min_yield_pct', 'max_yield_pct', 'min_oil_prod_l_ha',
'max_oil_prod_l_ha', 'avg_oil_prod_l_ha', 'l_per_t',
'min_l_per_t', 'max_l_per_t', 'avg_l_per_t']
techniques = ['tradizionale', 'intensiva', 'superintensiva']
for variety in all_varieties:
for feature in variety_features:
feature_names.append(f"{variety}_{feature}")
for technique in techniques:
feature_names.append(f"{variety}_tech_{technique}")
return feature_names
def add_controlled_variation(base_value: float, max_variation_pct: float = 0.20) -> float:
"""
Aggiunge una variazione controllata a un valore base.
Parameters
----------
base_value : float
Valore base da modificare
max_variation_pct : float
Percentuale massima di variazione (default 20%)
Returns
-------
float
Valore con variazione applicata
"""
variation = np.random.uniform(-max_variation_pct, max_variation_pct)
return base_value * (1 + variation)
-282
View File
@@ -1,282 +0,0 @@
import numpy as np
from typing import Dict, Tuple, List, Optional
from scipy import stats
def calculate_real_error(
model,
test_data: Dict,
test_targets: np.ndarray,
scaler_y,
target_names: Optional[List[str]] = None
) -> Tuple[List[float], List[float]]:
"""
Calcola l'errore reale denormalizzando le predizioni.
Parameters
----------
model : tf.keras.Model
Modello addestrato
test_data : dict
Dati di test
test_targets : np.ndarray
Target di test
scaler_y : scaler
Scaler utilizzato per normalizzare i target
target_names : list, optional
Nomi dei target
Returns
-------
tuple
(percentage_errors, absolute_errors)
"""
# Predizioni
predictions = model.predict(test_data)
# Denormalizza predizioni e target
predictions_real = scaler_y.inverse_transform(predictions)
targets_real = scaler_y.inverse_transform(test_targets)
# Calcola errori percentuali e assoluti
percentage_errors = []
absolute_errors = []
if target_names is None:
target_names = [f'target_{i}' for i in range(predictions_real.shape[1])]
# Calcola errori per ogni target
for i in range(predictions_real.shape[1]):
mae = np.mean(np.abs(predictions_real[:, i] - targets_real[:, i]))
mape = np.mean(np.abs((predictions_real[:, i] - targets_real[:, i]) / targets_real[:, i])) * 100
percentage_errors.append(mape)
absolute_errors.append(mae)
print(f"\n{target_names[i]}:")
print(f"MAE assoluto: {mae:.2f}")
print(f"Errore percentuale medio: {mape:.2f}%")
print(f"Precisione: {100 - mape:.2f}%")
print("-" * 50)
return percentage_errors, absolute_errors
def evaluate_model_performance(
model,
data: Dict,
targets: np.ndarray,
set_name: str = "",
threshold: Optional[float] = None
) -> Dict:
"""
Valuta le performance del modello su un set di dati.
Parameters
----------
model : tf.keras.Model
Modello da valutare
data : dict
Dati di input
targets : np.ndarray
Target reali
set_name : str
Nome del set di dati
threshold : float, optional
Soglia per calcolare accuracy binaria
Returns
-------
dict
Dizionario con le metriche calcolate
"""
predictions = model.predict(data, verbose=0)
metrics = {}
target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']
for i, name in enumerate(target_names):
# Metriche di base
mae = np.mean(np.abs(targets[:, i] - predictions[:, i]))
mse = np.mean(np.square(targets[:, i] - predictions[:, i]))
rmse = np.sqrt(mse)
mape = np.mean(np.abs((targets[:, i] - predictions[:, i]) / (targets[:, i] + 1e-7))) * 100
# R2 score
ss_res = np.sum(np.square(targets[:, i] - predictions[:, i]))
ss_tot = np.sum(np.square(targets[:, i] - np.mean(targets[:, i])))
r2 = 1 - (ss_res / (ss_tot + 1e-7))
# Salva le metriche
metrics[f"{name}_mae"] = mae
metrics[f"{name}_rmse"] = rmse
metrics[f"{name}_mape"] = mape
metrics[f"{name}_r2"] = r2
# Calcola accuracy binaria se fornita una soglia
if threshold is not None:
binary_acc = np.mean(
(predictions[:, i] > threshold) == (targets[:, i] > threshold)
)
metrics[f"{name}_binary_acc"] = binary_acc
if set_name:
print(f"\nPerformance sul set {set_name}:")
for metric, value in metrics.items():
print(f"{metric}: {value:.4f}")
return metrics
def calculate_efficiency_metrics(
predictions: np.ndarray,
targets: np.ndarray,
resource_usage: np.ndarray
) -> Dict:
"""
Calcola metriche di efficienza basate sull'utilizzo delle risorse.
Parameters
----------
predictions : np.ndarray
Predizioni del modello
targets : np.ndarray
Target reali
resource_usage : np.ndarray
Dati sull'utilizzo delle risorse
Returns
-------
dict
Metriche di efficienza
"""
metrics = {}
# Efficienza di produzione
production_efficiency = predictions / (resource_usage + 1e-7)
target_efficiency = targets / (resource_usage + 1e-7)
# Calcola metriche
metrics['mean_efficiency'] = np.mean(production_efficiency)
metrics['efficiency_error'] = np.mean(np.abs(production_efficiency - target_efficiency))
metrics['efficiency_std'] = np.std(production_efficiency)
# ROI stimato
estimated_roi = (predictions - resource_usage) / (resource_usage + 1e-7)
actual_roi = (targets - resource_usage) / (resource_usage + 1e-7)
metrics['roi_error'] = np.mean(np.abs(estimated_roi - actual_roi))
# Sostenibilità
metrics['resource_utilization'] = np.mean(predictions / resource_usage)
metrics['efficiency_improvement'] = (
np.mean(production_efficiency) - np.mean(target_efficiency)
) / np.mean(target_efficiency) * 100
return metrics
def calculate_forecast_accuracy(
predictions: np.ndarray,
targets: np.ndarray,
horizons: List[int]
) -> Dict:
"""
Calcola l'accuratezza delle previsioni per diversi orizzonti temporali.
Parameters
----------
predictions : np.ndarray
Predizioni del modello
targets : np.ndarray
Target reali
horizons : list
Lista degli orizzonti temporali da valutare
Returns
-------
dict
Accuratezza per ogni orizzonte
"""
accuracy_metrics = {}
for horizon in horizons:
# Seleziona dati per l'orizzonte corrente
pred_horizon = predictions[:-horizon]
target_horizon = targets[horizon:]
# Calcola metriche
mae = np.mean(np.abs(pred_horizon - target_horizon))
mape = np.mean(np.abs((pred_horizon - target_horizon) / (target_horizon + 1e-7))) * 100
rmse = np.sqrt(np.mean(np.square(pred_horizon - target_horizon)))
# Calcola il coefficiente di correlazione
corr = np.corrcoef(pred_horizon.flatten(), target_horizon.flatten())[0, 1]
# Salva le metriche
accuracy_metrics[f'horizon_{horizon}'] = {
'mae': mae,
'mape': mape,
'rmse': rmse,
'correlation': corr
}
print(f"\nMetriche per orizzonte {horizon}:")
print(f"MAE: {mae:.4f}")
print(f"MAPE: {mape:.2f}%")
print(f"RMSE: {rmse:.4f}")
print(f"Correlazione: {corr:.4f}")
return accuracy_metrics
def compute_confidence_intervals(
predictions: np.ndarray,
alpha: float = 0.05,
n_bootstrap: int = 1000
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Calcola intervalli di confidenza usando bootstrap.
Parameters
----------
predictions : np.ndarray
Predizioni del modello
alpha : float
Livello di significatività
n_bootstrap : int
Numero di campioni bootstrap
Returns
-------
tuple
(lower_bound, upper_bound, mean_predictions)
"""
n_samples, n_targets = predictions.shape
bootstrap_means = np.zeros((n_bootstrap, n_targets))
# Bootstrap sampling
for i in range(n_bootstrap):
indices = np.random.randint(0, n_samples, size=n_samples)
bootstrap_sample = predictions[indices]
bootstrap_means[i] = np.mean(bootstrap_sample, axis=0)
# Calcola intervalli di confidenza
lower_percentile = alpha / 2 * 100
upper_percentile = (1 - alpha / 2) * 100
lower_bound = np.percentile(bootstrap_means, lower_percentile, axis=0)
upper_bound = np.percentile(bootstrap_means, upper_percentile, axis=0)
mean_predictions = np.mean(predictions, axis=0)
# Calcola intervalli usando t-distribution
std_error = np.std(bootstrap_means, axis=0)
t_value = stats.t.ppf(1 - alpha / 2, df=n_samples - 1)
margin_error = t_value * std_error
print("\nIntervalli di Confidenza:")
for i in range(n_targets):
print(f"\nTarget {i + 1}:")
print(f"Media: {mean_predictions[i]:.4f}")
print(f"Intervallo: [{lower_bound[i]:.4f}, {upper_bound[i]:.4f}]")
print(f"Margine di errore: ±{margin_error[i]:.4f}")
return lower_bound, upper_bound, mean_predictions
-255
View File
@@ -1,255 +0,0 @@
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
import os
from typing import Optional
def save_plot(plt: plt, title: str, output_dir: str = './kaggle/working/plots'):
"""
Salva il plot corrente con un nome formattato.
Parameters
----------
plt : matplotlib.pyplot
Riferimento a pyplot
title : str
Titolo del plot
output_dir : str
Directory di output per i plot
"""
os.makedirs(output_dir, exist_ok=True)
# Pulisci il nome del file
filename = "".join(x for x in title if x.isalnum() or x in [' ', '-', '_']).rstrip()
filename = filename.replace(' ', '_').lower()
filepath = os.path.join(output_dir, f"{filename}.png")
plt.savefig(filepath, bbox_inches='tight', dpi=300)
print(f"Plot salvato come: {filepath}")
def plot_variety_comparison(comparison_data: pd.DataFrame, metric: str):
"""
Crea un grafico a barre per confrontare le varietà di olive su una metrica specifica.
Parameters
----------
comparison_data : pd.DataFrame
DataFrame contenente i dati di confronto
metric : str
Nome della metrica da visualizzare
"""
plt.figure(figsize=(12, 6))
bars = plt.bar(comparison_data['Variety'], comparison_data[metric])
plt.title(f'Confronto di {metric} tra Varietà di Olive')
plt.xlabel('Varietà')
plt.ylabel(metric)
plt.xticks(rotation=45, ha='right')
# Aggiungi etichette sopra le barre
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width() / 2., height,
f'{height:.2f}',
ha='center', va='bottom')
plt.tight_layout()
plt.show()
# Salva il plot
save_plot(plt,
f'variety_comparison_{metric.lower().replace(" ", "_").replace("/", "_").replace("(", "").replace(")", "")}')
plt.close()
def plot_efficiency_vs_production(comparison_data: pd.DataFrame):
"""
Crea uno scatter plot dell'efficienza vs produzione.
Parameters
----------
comparison_data : pd.DataFrame
DataFrame contenente i dati di confronto
"""
plt.figure(figsize=(10, 6))
plt.scatter(comparison_data['Avg Olive Production (kg/ha)'],
comparison_data['Oil Efficiency (L/kg)'],
s=100)
# Aggiungi etichette per ogni punto
for i, row in comparison_data.iterrows():
plt.annotate(row['Variety'],
(row['Avg Olive Production (kg/ha)'], row['Oil Efficiency (L/kg)']),
xytext=(5, 5), textcoords='offset points')
plt.title('Efficienza Olio vs Produzione Olive per Varietà')
plt.xlabel('Produzione Media Olive (kg/ha)')
plt.ylabel('Efficienza Olio (L olio / kg olive)')
plt.tight_layout()
# Salva il plot
save_plot(plt, 'efficiency_vs_production')
plt.close()
def plot_water_efficiency_vs_production(comparison_data: pd.DataFrame):
"""
Crea uno scatter plot dell'efficienza idrica vs produzione.
Parameters
----------
comparison_data : pd.DataFrame
DataFrame contenente i dati di confronto
"""
plt.figure(figsize=(10, 6))
plt.scatter(comparison_data['Avg Olive Production (kg/ha)'],
comparison_data['Water Efficiency (L oil/m³ water)'],
s=100)
# Aggiungi etichette per ogni punto
for i, row in comparison_data.iterrows():
plt.annotate(row['Variety'],
(row['Avg Olive Production (kg/ha)'],
row['Water Efficiency (L oil/m³ water)']),
xytext=(5, 5), textcoords='offset points')
plt.title('Efficienza Idrica vs Produzione Olive per Varietà')
plt.xlabel('Produzione Media Olive (kg/ha)')
plt.ylabel('Efficienza Idrica (L olio / m³ acqua)')
plt.tight_layout()
plt.show()
# Salva il plot
save_plot(plt, 'water_efficiency_vs_production')
plt.close()
def plot_water_need_vs_oil_production(comparison_data: pd.DataFrame):
"""
Crea uno scatter plot del fabbisogno idrico vs produzione di olio.
Parameters
----------
comparison_data : pd.DataFrame
DataFrame contenente i dati di confronto
"""
plt.figure(figsize=(10, 6))
plt.scatter(comparison_data['Avg Water Need (m³/ha)'],
comparison_data['Avg Oil Production (L/ha)'],
s=100)
# Aggiungi etichette per ogni punto
for i, row in comparison_data.iterrows():
plt.annotate(row['Variety'],
(row['Avg Water Need (m³/ha)'],
row['Avg Oil Production (L/ha)']),
xytext=(5, 5), textcoords='offset points')
plt.title('Produzione Olio vs Fabbisogno Idrico per Varietà')
plt.xlabel('Fabbisogno Idrico Medio (m³/ha)')
plt.ylabel('Produzione Media Olio (L/ha)')
plt.tight_layout()
plt.show()
# Salva il plot
save_plot(plt, 'water_need_vs_oil_production')
plt.close()
def plot_production_trends(data: pd.DataFrame,
variety: Optional[str] = None,
metrics: Optional[list] = None):
"""
Crea grafici di trend per le metriche di produzione.
Parameters
----------
data : pd.DataFrame
DataFrame con i dati di produzione
variety : str, optional
Varietà specifica da visualizzare
metrics : list, optional
Lista delle metriche da visualizzare
"""
if metrics is None:
metrics = ['olive_prod', 'oil_prod', 'water_need']
# Filtra per varietà se specificata
if variety:
data = data[data['variety'] == variety]
# Crea subplot per ogni metrica
fig, axes = plt.subplots(len(metrics), 1, figsize=(12, 4 * len(metrics)))
if len(metrics) == 1:
axes = [axes]
for ax, metric in zip(axes, metrics):
sns.lineplot(data=data, x='year', y=metric, ax=ax)
if variety:
ax.set_title(f'{metric} per {variety}')
else:
ax.set_title(f'{metric} - Tutte le varietà')
ax.set_xlabel('Anno')
plt.tight_layout()
# Salva il plot
title = f'production_trends{"_" + variety if variety else ""}'
save_plot(plt, title)
plt.close()
def plot_correlation_matrix(data: pd.DataFrame,
variables: Optional[list] = None,
title: str = "Matrice di Correlazione"):
"""
Crea una matrice di correlazione con heatmap.
Parameters
----------
data : pd.DataFrame
DataFrame con i dati
variables : list, optional
Lista delle variabili da includere
title : str
Titolo del plot
"""
if variables:
corr_matrix = data[variables].corr()
else:
corr_matrix = data.select_dtypes(include=[np.number]).corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix,
annot=True,
cmap='coolwarm',
center=0,
fmt='.2f')
plt.title(title)
plt.tight_layout()
# Salva il plot
save_plot(plt, 'correlation_matrix')
plt.close()
def setup_plotting_style():
"""
Configura lo stile dei plot per uniformità.
"""
plt.style.use('seaborn')
sns.set_palette("husl")
# Impostazioni personalizzate
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['font.size'] = 12
plt.rcParams['axes.labelsize'] = 12
plt.rcParams['axes.titlesize'] = 14
plt.rcParams['xtick.labelsize'] = 10
plt.rcParams['ytick.labelsize'] = 10