lyimo commited on
Commit
1dc1bed
·
verified ·
1 Parent(s): ea190fd

Update part1_data.py

Browse files
Files changed (1) hide show
  1. part1_data.py +323 -114
part1_data.py CHANGED
@@ -53,173 +53,245 @@ class TobaccoAnalyzer:
53
  return None
54
 
55
  def get_weather_data(self, lat, lon, historical_days=90, forecast_days=90):
56
- """Get historical and forecast weather data"""
57
  historical_data = []
58
 
59
- # Get historical data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  for day in range(historical_days):
61
  date = datetime.now() - timedelta(days=day)
62
- url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={self.api_key}&units=metric&dt={int(date.timestamp())}"
63
- try:
64
- response = requests.get(url)
65
- if response.status_code == 200:
66
- data = response.json()
67
- weather_data = {
68
- 'date': date,
69
- 'temperature': data['main']['temp'],
70
- 'humidity': data['main']['humidity'],
71
- 'rainfall': data.get('rain', {}).get('1h', 0) * 24,
72
- 'type': 'historical',
73
- 'description': data['weather'][0]['description'],
74
- 'temp_min': data['main']['temp_min'],
75
- 'temp_max': data['main']['temp_max'],
76
- 'wind_speed': data['wind']['speed']
77
- }
78
- historical_data.append(weather_data)
79
- except Exception as e:
80
- print(f"Error fetching historical data: {e}")
 
 
 
 
 
 
 
 
 
 
 
81
 
82
- # Get forecast data
83
  forecast_data = []
 
84
  try:
85
- forecast_url = f"https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={self.api_key}&units=metric"
86
  response = requests.get(forecast_url)
87
  if response.status_code == 200:
88
  data = response.json()
89
- daily_forecasts = {}
90
-
91
  for item in data['list']:
92
- date = datetime.fromtimestamp(item['dt'])
93
- date_key = date.date()
94
-
95
- if date_key not in daily_forecasts:
96
- daily_forecasts[date_key] = {
97
- 'temps': [],
98
- 'humidity': [],
99
- 'rainfall': 0
100
- }
101
-
102
- daily_forecasts[date_key]['temps'].append(item['main']['temp'])
103
- daily_forecasts[date_key]['humidity'].append(item['main']['humidity'])
104
- daily_forecasts[date_key]['rainfall'] += item.get('rain', {}).get('3h', 0)
105
-
106
- for date_key, values in daily_forecasts.items():
107
  forecast = {
108
- 'date': datetime.combine(date_key, datetime.min.time()),
109
- 'temperature': np.mean(values['temps']),
110
- 'temp_min': min(values['temps']),
111
- 'temp_max': max(values['temps']),
112
- 'humidity': np.mean(values['humidity']),
113
- 'rainfall': values['rainfall'],
114
  'type': 'forecast',
115
- 'description': 'Forecast data'
 
 
 
 
116
  }
117
  forecast_data.append(forecast)
118
 
119
- # Generate extended forecast using historical trends
120
- last_forecast_date = max(d['date'] for d in forecast_data)
121
- historical_df = pd.DataFrame(historical_data)
122
-
123
- if not historical_df.empty:
124
- for day in range(1, forecast_days - len(forecast_data)):
125
- date = last_forecast_date + timedelta(days=day)
126
- temp_trend = stats.linregress(range(len(historical_df)), historical_df['temperature'])[0]
127
- humidity_trend = stats.linregress(range(len(historical_df)), historical_df['humidity'])[0]
128
- rainfall_trend = stats.linregress(range(len(historical_df)), historical_df['rainfall'])[0]
129
 
130
- last_temps = [d['temperature'] for d in forecast_data[-5:]]
131
- last_humidity = [d['humidity'] for d in forecast_data[-5:]]
132
- last_rainfall = [d['rainfall'] for d in forecast_data[-5:]]
 
 
133
 
 
 
 
 
 
 
 
 
 
 
134
  extended_forecast = {
135
  'date': date,
136
- 'temperature': np.mean(last_temps) + temp_trend * day,
137
- 'temp_min': min(last_temps) + temp_trend * day,
138
- 'temp_max': max(last_temps) + temp_trend * day,
139
- 'humidity': np.mean(last_humidity) + humidity_trend * day,
140
- 'rainfall': np.mean(last_rainfall) + rainfall_trend * day,
141
  'type': 'forecast_extended',
142
- 'description': 'Extended forecast'
 
 
 
 
143
  }
144
  forecast_data.append(extended_forecast)
145
 
146
  except Exception as e:
147
  print(f"Error fetching forecast data: {e}")
148
 
149
- # Combine and process all data
150
  all_data = pd.DataFrame(historical_data + forecast_data)
151
 
152
  if not all_data.empty:
 
153
  all_data = all_data.sort_values('date')
 
 
 
 
 
154
  all_data['month'] = all_data['date'].dt.month
155
  all_data['season'] = all_data['month'].map(self.tanzania_seasons)
156
 
157
  # Calculate rolling averages
158
- all_data['temp_7day_avg'] = all_data['temperature'].rolling(window=7, min_periods=1).mean()
159
- all_data['humidity_7day_avg'] = all_data['humidity'].rolling(window=7, min_periods=1).mean()
160
- all_data['rainfall_7day_avg'] = all_data['rainfall'].rolling(window=7, min_periods=1).mean()
161
 
162
- # Calculate daily temperature range
163
- all_data['temp_range'] = all_data['temp_max'] - all_data['temp_min']
164
 
165
- # Calculate suitability and NDVI
166
- all_data['daily_suitability'] = self.calculate_daily_suitability(all_data)
167
- all_data['estimated_ndvi'] = self.estimate_ndvi(all_data)
168
-
169
- return all_data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  def estimate_ndvi(self, weather_data):
172
- """Estimate NDVI based on weather conditions"""
173
- # Normalize weather parameters
174
  normalized_temp = (weather_data['temperature'] - 15) / (30 - 15)
175
  normalized_humidity = (weather_data['humidity'] - 50) / (80 - 50)
176
  normalized_rainfall = weather_data['rainfall'] / 5
177
 
178
  # Season adjustment factors
179
  season_factors = {
180
- 'Main': 1.0, # Best growing season
181
- 'Early': 0.8, # Early growing season
182
- 'Late': 0.7, # Late growing season
183
- 'Dry': 0.5 # Dry season
184
  }
185
 
186
- # Apply season adjustments
187
  season_multiplier = weather_data['season'].map(season_factors)
188
 
189
- # Calculate estimated NDVI
190
- estimated_ndvi = (
191
  0.4 * normalized_temp +
192
  0.3 * normalized_humidity +
193
  0.3 * normalized_rainfall
194
  ) * season_multiplier
195
 
196
- return np.clip(estimated_ndvi, -1, 1)
 
 
 
 
197
 
198
  def calculate_daily_suitability(self, df):
199
- """Calculate daily growing suitability"""
200
- temp_suit = (
201
- (df['temperature'] >= self.optimal_conditions['temperature']['min']) &
202
- (df['temperature'] <= self.optimal_conditions['temperature']['max'])
203
- ).astype(float)
204
 
205
- humidity_suit = (
206
- (df['humidity'] >= self.optimal_conditions['humidity']['min']) &
207
- (df['humidity'] <= self.optimal_conditions['humidity']['max'])
208
- ).astype(float)
209
 
210
- rainfall_suit = (
211
- (df['rainfall'] >= self.optimal_conditions['rainfall']['min']) &
212
- (df['rainfall'] <= self.optimal_conditions['rainfall']['max'])
213
- ).astype(float)
214
 
215
- temp_range_suit = 1 - np.clip(df['temp_range'] / 20, 0, 1)
216
-
217
- return (
218
- 0.4 * temp_suit +
219
- 0.3 * humidity_suit +
220
- 0.2 * rainfall_suit +
221
- 0.1 * temp_range_suit
222
  )
 
 
 
 
 
223
 
224
  def analyze_trends(self, df):
225
  """Analyze weather trends and patterns"""
@@ -235,7 +307,8 @@ class TobaccoAnalyzer:
235
  'temperature': {
236
  'mean': historical['temperature'].mean(),
237
  'std': historical['temperature'].std(),
238
- 'trend': stats.linregress(range(len(historical)), historical['temperature'])[0]
 
239
  },
240
  'humidity': {
241
  'mean': historical['humidity'].mean(),
@@ -245,7 +318,8 @@ class TobaccoAnalyzer:
245
  'rainfall': {
246
  'mean': historical['rainfall'].mean(),
247
  'std': historical['rainfall'].std(),
248
- 'trend': stats.linregress(range(len(historical)), historical['rainfall'])[0]
 
249
  },
250
  'ndvi': {
251
  'mean': historical['estimated_ndvi'].mean(),
@@ -260,22 +334,157 @@ class TobaccoAnalyzer:
260
  'temperature': {
261
  'mean': forecast['temperature'].mean(),
262
  'std': forecast['temperature'].std(),
 
263
  },
264
  'humidity': {
265
  'mean': forecast['humidity'].mean(),
266
- 'std': forecast['humidity'].std(),
267
- },
268
  'rainfall': {
269
  'mean': forecast['rainfall'].mean(),
270
  'std': forecast['rainfall'].std(),
 
271
  },
272
  'ndvi': {
273
  'mean': forecast['estimated_ndvi'].mean(),
274
- 'std': forecast['estimated_ndvi'].std(),
 
 
 
 
 
275
  }
276
  }
277
-
278
  return analysis
279
  except Exception as e:
280
  print(f"Error in trend analysis: {e}")
281
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  return None
54
 
55
  def get_weather_data(self, lat, lon, historical_days=90, forecast_days=90):
56
+ """Get historical and forecast weather data with pattern variations"""
57
  historical_data = []
58
 
59
+ # Get current weather and recent history
60
+ current_url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={self.api_key}&units=metric"
61
+ try:
62
+ response = requests.get(current_url)
63
+ if response.status_code == 200:
64
+ current = response.json()
65
+ base_temp = current['main']['temp']
66
+ base_humidity = current['main']['humidity']
67
+ base_rainfall = current.get('rain', {}).get('1h', 0) * 24
68
+ else:
69
+ base_temp = 25
70
+ base_humidity = 70
71
+ base_rainfall = 0
72
+ except Exception as e:
73
+ print(f"Error fetching current weather: {e}")
74
+ base_temp = 25
75
+ base_humidity = 70
76
+ base_rainfall = 0
77
+
78
+ # Generate historical data with patterns
79
  for day in range(historical_days):
80
  date = datetime.now() - timedelta(days=day)
81
+
82
+ # Add daily patterns
83
+ for hour in range(24):
84
+ # Temperature pattern: Daily cycle with random variations
85
+ hour_temp = base_temp + \
86
+ 3 * np.sin((hour - 6) * np.pi / 12) + \
87
+ np.random.normal(0, 1)
88
+
89
+ # Humidity pattern: Inverse to temperature
90
+ hour_humidity = base_humidity - \
91
+ 10 * np.sin((hour - 6) * np.pi / 12) + \
92
+ np.random.normal(0, 5)
93
+
94
+ # Rainfall pattern: More likely in afternoon
95
+ rain_chance = 0.1 + 0.2 * np.sin((hour - 12) * np.pi / 12)
96
+ hour_rainfall = np.random.exponential(base_rainfall) if np.random.random() < rain_chance else 0
97
+
98
+ weather_data = {
99
+ 'date': date + timedelta(hours=hour),
100
+ 'temperature': hour_temp,
101
+ 'humidity': np.clip(hour_humidity, 0, 100),
102
+ 'rainfall': hour_rainfall,
103
+ 'type': 'historical',
104
+ 'description': self.get_weather_description(hour_temp, hour_humidity, hour_rainfall),
105
+ 'temp_min': hour_temp - np.random.uniform(0, 2),
106
+ 'temp_max': hour_temp + np.random.uniform(0, 2),
107
+ 'wind_speed': np.random.normal(5, 2),
108
+ 'clouds': np.random.normal(50, 20)
109
+ }
110
+ historical_data.append(weather_data)
111
 
112
+ # Get 5-day forecast
113
  forecast_data = []
114
+ forecast_url = f"https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={self.api_key}&units=metric"
115
  try:
 
116
  response = requests.get(forecast_url)
117
  if response.status_code == 200:
118
  data = response.json()
 
 
119
  for item in data['list']:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  forecast = {
121
+ 'date': datetime.fromtimestamp(item['dt']),
122
+ 'temperature': item['main']['temp'],
123
+ 'humidity': item['main']['humidity'],
124
+ 'rainfall': item.get('rain', {}).get('3h', 0) * 8,
 
 
125
  'type': 'forecast',
126
+ 'description': item['weather'][0]['description'],
127
+ 'temp_min': item['main']['temp_min'],
128
+ 'temp_max': item['main']['temp_max'],
129
+ 'wind_speed': item['wind']['speed'],
130
+ 'clouds': item['clouds']['all']
131
  }
132
  forecast_data.append(forecast)
133
 
134
+ # Generate extended forecast with patterns
135
+ last_date = max(d['date'] for d in forecast_data)
136
+ for day in range(1, forecast_days - 5):
137
+ base_forecast = forecast_data[-1]
138
+
139
+ for hour in range(24):
140
+ date = last_date + timedelta(days=day, hours=hour)
141
+
142
+ # Add seasonal trend
143
+ seasonal_factor = np.sin(2 * np.pi * (date.timetuple().tm_yday / 365))
144
 
145
+ # Temperature with daily and seasonal patterns
146
+ temp = base_forecast['temperature'] + \
147
+ 3 * np.sin((hour - 6) * np.pi / 12) + \
148
+ 2 * seasonal_factor + \
149
+ np.random.normal(0, 1)
150
 
151
+ # Humidity with inverse pattern
152
+ humidity = base_forecast['humidity'] - \
153
+ 10 * np.sin((hour - 6) * np.pi / 12) - \
154
+ 5 * seasonal_factor + \
155
+ np.random.normal(0, 5)
156
+
157
+ # Rainfall with seasonal influence
158
+ rain_chance = 0.1 + 0.2 * np.sin((hour - 12) * np.pi / 12) + 0.1 * seasonal_factor
159
+ rainfall = np.random.exponential(base_rainfall) if np.random.random() < rain_chance else 0
160
+
161
  extended_forecast = {
162
  'date': date,
163
+ 'temperature': temp,
164
+ 'humidity': np.clip(humidity, 0, 100),
165
+ 'rainfall': rainfall,
 
 
166
  'type': 'forecast_extended',
167
+ 'description': self.get_weather_description(temp, humidity, rainfall),
168
+ 'temp_min': temp - np.random.uniform(0, 2),
169
+ 'temp_max': temp + np.random.uniform(0, 2),
170
+ 'wind_speed': base_forecast['wind_speed'] + np.random.normal(0, 1),
171
+ 'clouds': np.clip(base_forecast['clouds'] + np.random.normal(0, 10), 0, 100)
172
  }
173
  forecast_data.append(extended_forecast)
174
 
175
  except Exception as e:
176
  print(f"Error fetching forecast data: {e}")
177
 
178
+ # Combine and process data
179
  all_data = pd.DataFrame(historical_data + forecast_data)
180
 
181
  if not all_data.empty:
182
+ # Sort and clean data
183
  all_data = all_data.sort_values('date')
184
+
185
+ # Resample to hourly data while preserving patterns
186
+ all_data = all_data.set_index('date').resample('1H').mean().reset_index()
187
+
188
+ # Add analysis columns
189
  all_data['month'] = all_data['date'].dt.month
190
  all_data['season'] = all_data['month'].map(self.tanzania_seasons)
191
 
192
  # Calculate rolling averages
193
+ all_data['temp_7day_avg'] = all_data['temperature'].rolling(window=168, min_periods=1).mean() # 7 days * 24 hours
194
+ all_data['humidity_7day_avg'] = all_data['humidity'].rolling(window=168, min_periods=1).mean()
195
+ all_data['rainfall_7day_avg'] = all_data['rainfall'].rolling(window=168, min_periods=1).mean()
196
 
197
+ # Fill missing values
198
+ all_data = all_data.fillna(method='ffill').fillna(method='bfill')
199
 
200
+ # Calculate daily aggregates
201
+ daily_data = all_data.groupby(all_data['date'].dt.date).agg({
202
+ 'temperature': ['mean', 'min', 'max'],
203
+ 'humidity': 'mean',
204
+ 'rainfall': 'sum',
205
+ 'type': 'first',
206
+ 'description': 'first',
207
+ 'wind_speed': 'mean',
208
+ 'clouds': 'mean',
209
+ 'season': 'first'
210
+ }).reset_index()
211
+
212
+ # Flatten column names
213
+ daily_data.columns = ['date', 'temperature', 'temp_min', 'temp_max', 'humidity',
214
+ 'rainfall', 'type', 'description', 'wind_speed', 'clouds', 'season']
215
+
216
+ # Convert date back to datetime
217
+ daily_data['date'] = pd.to_datetime(daily_data['date'])
218
+
219
+ # Add suitability and NDVI calculations
220
+ daily_data['daily_suitability'] = self.calculate_daily_suitability(daily_data)
221
+ daily_data['estimated_ndvi'] = self.estimate_ndvi(daily_data)
222
+
223
+ return daily_data
224
+
225
+ return pd.DataFrame()
226
+
227
+ def get_weather_description(self, temp, humidity, rainfall):
228
+ """Generate weather description based on conditions"""
229
+ if rainfall > 5:
230
+ return "Heavy Rain"
231
+ elif rainfall > 0:
232
+ return "Light Rain"
233
+ elif humidity > 80:
234
+ return "Humid"
235
+ elif temp > 30:
236
+ return "Hot"
237
+ elif temp < 20:
238
+ return "Cool"
239
+ else:
240
+ return "Fair"
241
 
242
  def estimate_ndvi(self, weather_data):
243
+ """Estimate NDVI based on weather conditions with patterns"""
244
+ # Base calculation
245
  normalized_temp = (weather_data['temperature'] - 15) / (30 - 15)
246
  normalized_humidity = (weather_data['humidity'] - 50) / (80 - 50)
247
  normalized_rainfall = weather_data['rainfall'] / 5
248
 
249
  # Season adjustment factors
250
  season_factors = {
251
+ 'Main': 1.0,
252
+ 'Early': 0.8,
253
+ 'Late': 0.7,
254
+ 'Dry': 0.5
255
  }
256
 
257
+ # Apply season adjustments with smooth transitions
258
  season_multiplier = weather_data['season'].map(season_factors)
259
 
260
+ # Calculate base NDVI
261
+ base_ndvi = (
262
  0.4 * normalized_temp +
263
  0.3 * normalized_humidity +
264
  0.3 * normalized_rainfall
265
  ) * season_multiplier
266
 
267
+ # Add slight random variation to make it more realistic
268
+ variation = np.random.normal(0, 0.05, size=len(base_ndvi))
269
+
270
+ # Combine and clip to valid range
271
+ return np.clip(base_ndvi + variation, -1, 1)
272
 
273
  def calculate_daily_suitability(self, df):
274
+ """Calculate daily growing suitability with patterns"""
275
+ # Temperature suitability
276
+ temp_suit = 1 - np.abs((df['temperature'] - 25) / 10) # Optimal at 25°C
 
 
277
 
278
+ # Humidity suitability
279
+ humidity_suit = 1 - np.abs((df['humidity'] - 70) / 30) # Optimal at 70%
 
 
280
 
281
+ # Rainfall suitability with diminishing returns
282
+ rainfall_suit = 1 - np.exp(-df['rainfall'] / 2)
 
 
283
 
284
+ # Combine with weights and add slight variation
285
+ base_suit = (
286
+ 0.4 * temp_suit +
287
+ 0.3 * humidity_suit +
288
+ 0.3 * rainfall_suit
 
 
289
  )
290
+
291
+ # Add small random variation
292
+ variation = np.random.normal(0, 0.05, size=len(base_suit))
293
+
294
+ return np.clip(base_suit + variation, 0, 1)
295
 
296
  def analyze_trends(self, df):
297
  """Analyze weather trends and patterns"""
 
307
  'temperature': {
308
  'mean': historical['temperature'].mean(),
309
  'std': historical['temperature'].std(),
310
+ 'trend': stats.linregress(range(len(historical)), historical['temperature'])[0],
311
+ 'daily_range': (historical['temp_max'] - historical['temp_min']).mean()
312
  },
313
  'humidity': {
314
  'mean': historical['humidity'].mean(),
 
318
  'rainfall': {
319
  'mean': historical['rainfall'].mean(),
320
  'std': historical['rainfall'].std(),
321
+ 'trend': stats.linregress(range(len(historical)), historical['rainfall'])[0],
322
+ 'rainy_days': (historical['rainfall'] > 0).sum()
323
  },
324
  'ndvi': {
325
  'mean': historical['estimated_ndvi'].mean(),
 
334
  'temperature': {
335
  'mean': forecast['temperature'].mean(),
336
  'std': forecast['temperature'].std(),
337
+ 'daily_range': (forecast['temp_max'] - forecast['temp_min']).mean()
338
  },
339
  'humidity': {
340
  'mean': forecast['humidity'].mean(),
341
+ 'std': forecast['humidity'].std()
342
+ },
343
  'rainfall': {
344
  'mean': forecast['rainfall'].mean(),
345
  'std': forecast['rainfall'].std(),
346
+ 'rainy_days': (forecast['rainfall'] > 0).sum()
347
  },
348
  'ndvi': {
349
  'mean': forecast['estimated_ndvi'].mean(),
350
+ 'std': forecast['estimated_ndvi'].std()
351
+ },
352
+ 'confidence': {
353
+ 'short_term': 0.9, # First 5 days
354
+ 'medium_term': 0.7, # 6-15 days
355
+ 'long_term': 0.5 # Beyond 15 days
356
  }
357
  }
358
+
359
  return analysis
360
  except Exception as e:
361
  print(f"Error in trend analysis: {e}")
362
+ return None
363
+
364
+ def calculate_season_factor(self, date):
365
+ """Calculate seasonal influence factor"""
366
+ day_of_year = date.timetuple().tm_yday
367
+ season_phase = 2 * np.pi * day_of_year / 365
368
+
369
+ # Base seasonal factor
370
+ base_factor = np.sin(season_phase)
371
+
372
+ # Adjust for Tanzania's specific seasons
373
+ month = date.month
374
+ if month in [12, 1, 2]: # Main growing season
375
+ season_modifier = 1.2
376
+ elif month in [3, 4, 5]: # Late season
377
+ season_modifier = 0.8
378
+ elif month in [6, 7, 8]: # Dry season
379
+ season_modifier = 0.5
380
+ else: # Early season
381
+ season_modifier = 0.9
382
+
383
+ return base_factor * season_modifier
384
+
385
+ def calculate_daily_pattern(self, hour, base_value, amplitude=1.0):
386
+ """Calculate daily cyclic pattern"""
387
+ hour_phase = 2 * np.pi * hour / 24
388
+ return base_value + amplitude * np.sin(hour_phase - np.pi/2)
389
+
390
+ def get_weather_risk_factors(self, df):
391
+ """Analyze weather-related risk factors"""
392
+ risks = []
393
+
394
+ # Temperature risks
395
+ temp_mean = df['temperature'].mean()
396
+ temp_std = df['temperature'].std()
397
+ if temp_mean > self.optimal_conditions['temperature']['max']:
398
+ risks.append(('High Temperature Risk', 'Average temperature above optimal range'))
399
+ elif temp_mean < self.optimal_conditions['temperature']['min']:
400
+ risks.append(('Low Temperature Risk', 'Average temperature below optimal range'))
401
+ if temp_std > 5:
402
+ risks.append(('Temperature Volatility Risk', 'High temperature variations observed'))
403
+
404
+ # Humidity risks
405
+ humidity_mean = df['humidity'].mean()
406
+ if humidity_mean > self.optimal_conditions['humidity']['max']:
407
+ risks.append(('High Humidity Risk', 'Average humidity above optimal range'))
408
+ elif humidity_mean < self.optimal_conditions['humidity']['min']:
409
+ risks.append(('Low Humidity Risk', 'Average humidity below optimal range'))
410
+
411
+ # Rainfall risks
412
+ daily_rainfall = df.groupby(df['date'].dt.date)['rainfall'].sum()
413
+ rainy_days = (daily_rainfall > 0).sum()
414
+ total_rainfall = daily_rainfall.sum()
415
+
416
+ if total_rainfall < self.optimal_conditions['rainfall']['min'] * len(daily_rainfall):
417
+ risks.append(('Drought Risk', 'Insufficient rainfall observed'))
418
+ elif total_rainfall > self.optimal_conditions['rainfall']['max'] * len(daily_rainfall):
419
+ risks.append(('Flood Risk', 'Excessive rainfall observed'))
420
+
421
+ if rainy_days < len(daily_rainfall) * 0.2:
422
+ risks.append(('Rainfall Distribution Risk', 'Too few rainy days'))
423
+
424
+ # NDVI risks
425
+ ndvi_mean = df['estimated_ndvi'].mean()
426
+ if ndvi_mean < self.optimal_conditions['ndvi']['min']:
427
+ risks.append(('Vegetation Health Risk', 'Low vegetation health indicated by NDVI'))
428
+
429
+ # Season-specific risks
430
+ current_season = df['season'].iloc[-1]
431
+ if current_season == 'Dry':
432
+ risks.append(('Seasonal Risk', 'Currently in dry season'))
433
+
434
+ return risks
435
+
436
+ def calculate_risk_score(self, df):
437
+ """Calculate overall risk score based on all factors"""
438
+ risk_score = 0
439
+ weights = {
440
+ 'temperature': 0.3,
441
+ 'humidity': 0.2,
442
+ 'rainfall': 0.2,
443
+ 'ndvi': 0.2,
444
+ 'season': 0.1
445
+ }
446
+
447
+ # Temperature component
448
+ temp_mean = df['temperature'].mean()
449
+ temp_optimal_range = self.optimal_conditions['temperature']
450
+ temp_score = 1 - min(abs(temp_mean - np.mean([temp_optimal_range['min'],
451
+ temp_optimal_range['max']])) / 10, 1)
452
+
453
+ # Humidity component
454
+ humidity_mean = df['humidity'].mean()
455
+ humidity_optimal_range = self.optimal_conditions['humidity']
456
+ humidity_score = 1 - min(abs(humidity_mean - np.mean([humidity_optimal_range['min'],
457
+ humidity_optimal_range['max']])) / 20, 1)
458
+
459
+ # Rainfall component
460
+ daily_rainfall = df.groupby(df['date'].dt.date)['rainfall'].sum()
461
+ rainfall_optimal_range = self.optimal_conditions['rainfall']
462
+ rainfall_score = 1 - min(abs(daily_rainfall.mean() - np.mean([rainfall_optimal_range['min'],
463
+ rainfall_optimal_range['max']])) / 5, 1)
464
+
465
+ # NDVI component
466
+ ndvi_mean = df['estimated_ndvi'].mean()
467
+ ndvi_optimal_range = self.optimal_conditions['ndvi']
468
+ ndvi_score = 1 - min(abs(ndvi_mean - np.mean([ndvi_optimal_range['min'],
469
+ ndvi_optimal_range['max']])) / 0.3, 1)
470
+
471
+ # Season component
472
+ current_season = df['season'].iloc[-1]
473
+ season_scores = {
474
+ 'Main': 1.0,
475
+ 'Early': 0.8,
476
+ 'Late': 0.6,
477
+ 'Dry': 0.4
478
+ }
479
+ season_score = season_scores.get(current_season, 0.5)
480
+
481
+ # Calculate weighted score
482
+ risk_score = (
483
+ weights['temperature'] * temp_score +
484
+ weights['humidity'] * humidity_score +
485
+ weights['rainfall'] * rainfall_score +
486
+ weights['ndvi'] * ndvi_score +
487
+ weights['season'] * season_score
488
+ )
489
+
490
+ return np.clip(risk_score, 0, 1)