wip refactor
This commit is contained in:
1 parent
549ced1aea
commit
d092c0967c
74 files changed
+2008
-1095015
No files matched your search
@@ -1 +0,0 @@
|
||||
data/simulated_data.parquet
|
||||
Generated
+1
@@ -2,6 +2,7 @@
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/models" />
|
||||
</content>
|
||||
<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
@@ -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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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
|
||||
|
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": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
File renamed without changes.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
@@ -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'")
|
||||
@@ -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])
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
+16
-4
@@ -5,12 +5,17 @@
|
||||
{
|
||||
"variety": "Nocellara dell'Etna",
|
||||
"technique": "Tradizionale",
|
||||
"percentage": 70
|
||||
"percentage": 30
|
||||
},
|
||||
{
|
||||
"variety": "Frantoio",
|
||||
"technique": "Tradizionale",
|
||||
"percentage": 30
|
||||
},
|
||||
{
|
||||
"variety": "Coratina",
|
||||
"technique": "Tradizionale",
|
||||
"percentage": 40
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -18,12 +23,14 @@
|
||||
"fixed": {
|
||||
"ammortamento": 2000,
|
||||
"assicurazione": 500,
|
||||
"manutenzione": 800
|
||||
"manutenzione": 800,
|
||||
"certificazioni": 3000
|
||||
},
|
||||
"variable": {
|
||||
"raccolta": 0.35,
|
||||
"potatura": 600,
|
||||
"fertilizzanti": 400
|
||||
"fertilizzanti": 400,
|
||||
"irrigazione": 300
|
||||
},
|
||||
"transformation": {
|
||||
"molitura": 0.15,
|
||||
@@ -31,6 +38,11 @@
|
||||
"bottiglia": 1.2,
|
||||
"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
|
||||
@@ -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.
@@ -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
|
||||
}
|
||||
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
+166
-2
@@ -2,7 +2,8 @@ import psutil
|
||||
import multiprocessing
|
||||
import re
|
||||
import pandas as pd
|
||||
from typing import List
|
||||
from typing import List, Dict
|
||||
import numpy as np
|
||||
|
||||
|
||||
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}_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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user