Is there a better way to accomplish this? The function determines the Category
for each row based on some conditions. Another set of conditions determines the ChangeGroup
for the row. Not all the conditions are written but it more or less gets the point across. The determination for a ChangeGroup
is made based on a previous row where ChangeGroup == 'BECMG'
.
Data:
MOCK_DATA = [{'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 12:00:00', 'Ending': '2022-02-19 13:00:00', 'WindDirection': 200, 'WindSpeed': 7, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510111', 'Icing': None, 'Altimeter': 30.09, 'Temperature': -2}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 13:00:00', 'Ending': '2022-02-19 14:00:00', 'WindDirection': 200, 'WindSpeed': 8, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510111', 'Icing': None, 'Altimeter': 30.08, 'Temperature': -1}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 14:00:00', 'Ending': '2022-02-19 15:00:00', 'WindDirection': 210, 'WindSpeed': 10, 'WindGust': 15, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510111', 'Icing': None, 'Altimeter': 30.06, 'Temperature': 0}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 15:00:00', 'Ending': '2022-02-19 16:00:00', 'WindDirection': 220, 'WindSpeed': 11, 'WindGust': 20, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '520111', 'Icing': None, 'Altimeter': 30.02, 'Temperature': 2}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 16:00:00', 'Ending': '2022-02-19 17:00:00', 'WindDirection': 230, 'WindSpeed': 11, 'WindGust': 20, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 15500.0, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '520113', 'Icing': None, 'Altimeter': 30.01, 'Temperature': 4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 17:00:00', 'Ending': '2022-02-19 18:00:00', 'WindDirection': 260, 'WindSpeed': 10, 'WindGust': 23, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SCT', 'CloudBase1': 4500.0, 'CloudCover2': 'BKN', 'CloudBase2': 6000.0, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510111', 'Icing': '620540', 'Altimeter': 30.01, 'Temperature': 6}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 18:00:00', 'Ending': '2022-02-19 19:00:00', 'WindDirection': 290, 'WindSpeed': 15, 'WindGust': 38, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 5000.0, 'CloudCover2': 'BKN', 'CloudBase2': 7500.0, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510118', 'Icing': '620621', 'Altimeter': 29.99, 'Temperature': 7}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 19:00:00', 'Ending': '2022-02-19 20:00:00', 'WindDirection': 290, 'WindSpeed': 18, 'WindGust': 39, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SCT', 'CloudBase1': 6000.0, 'CloudCover2': 'BKN', 'CloudBase2': 8500.0, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '520118', 'Icing': '620700', 'Altimeter': 30.0, 'Temperature': 7}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 20:00:00', 'Ending': '2022-02-19 21:00:00', 'WindDirection': 300, 'WindSpeed': 20, 'WindGust': 42, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '520111', 'Icing': None, 'Altimeter': 30.06, 'Temperature': 5}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 21:00:00', 'Ending': '2022-02-19 22:00:00', 'WindDirection': 310, 'WindSpeed': 18, 'WindGust': 40, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 6500.0, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '520128', 'Icing': None, 'Altimeter': 30.13, 'Temperature': 3}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 22:00:00', 'Ending': '2022-02-19 23:00:00', 'WindDirection': 310, 'WindSpeed': 17, 'WindGust': 39, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '520128', 'Icing': None, 'Altimeter': 30.18, 'Temperature': 1}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-19 23:00:00', 'Ending': '2022-02-20 00:00:00', 'WindDirection': 320, 'WindSpeed': 15, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '520121', 'Icing': None, 'Altimeter': 30.24, 'Temperature': 0}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 00:00:00', 'Ending': '2022-02-20 01:00:00', 'WindDirection': 320, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510130', 'Icing': None, 'Altimeter': 30.3, 'Temperature': -1}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 01:00:00', 'Ending': '2022-02-20 02:00:00', 'WindDirection': 320, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '520131', 'Icing': None, 'Altimeter': 30.34, 'Temperature': -2}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 02:00:00', 'Ending': '2022-02-20 03:00:00', 'WindDirection': 320, 'WindSpeed': 12, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510130', 'Icing': None, 'Altimeter': 30.38, 'Temperature': -3}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 03:00:00', 'Ending': '2022-02-20 04:00:00', 'WindDirection': 320, 'WindSpeed': 11, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False,'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510140', 'Icing': None, 'Altimeter': 30.4, 'Temperature': -3}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 04:00:00', 'Ending': '2022-02-20 05:00:00', 'WindDirection': 320, 'WindSpeed': 9, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510140', 'Icing': None, 'Altimeter': 30.42, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 05:00:00', 'Ending': '2022-02-20 06:00:00', 'WindDirection': 320, 'WindSpeed': 7, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510140', 'Icing': None, 'Altimeter': 30.44, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 06:00:00', 'Ending': '2022-02-20 07:00:00', 'WindDirection': 320, 'WindSpeed': 6, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510140', 'Icing': None, 'Altimeter': 30.45, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 07:00:00', 'Ending': '2022-02-20 08:00:00', 'WindDirection': 320, 'WindSpeed': 6, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510140', 'Icing': None, 'Altimeter': 30.46, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 08:00:00', 'Ending': '2022-02-20 09:00:00', 'WindDirection': 320, 'WindSpeed': 5, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510140', 'Icing': None, 'Altimeter': 30.45, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 09:00:00', 'Ending': '2022-02-20 10:00:00', 'WindDirection': 320, 'WindSpeed': 4, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.45, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 10:00:00', 'Ending': '2022-02-20 11:00:00', 'WindDirection': 310, 'WindSpeed': 2, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.46, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 11:00:00', 'Ending': '2022-02-20 12:00:00', 'WindDirection': 280, 'WindSpeed': 1, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.48, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 12:00:00', 'Ending': '2022-02-20 13:00:00', 'WindDirection': 170, 'WindSpeed': 1, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.49, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 13:00:00', 'Ending': '2022-02-20 14:00:00', 'WindDirection': 130, 'WindSpeed': 2, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.5, 'Temperature': -4}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 14:00:00', 'Ending': '2022-02-20 15:00:00', 'WindDirection': 110, 'WindSpeed': 3, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.5, 'Temperature': -3}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 15:00:00', 'Ending': '2022-02-20 16:00:00', 'WindDirection': 170, 'WindSpeed': 2, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.52, 'Temperature': -2}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 16:00:00', 'Ending': '2022-02-20 17:00:00', 'WindDirection': 200, 'WindSpeed': 5, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.51, 'Temperature': 0}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 17:00:00', 'Ending': '2022-02-20 18:00:00', 'WindDirection': 200, 'WindSpeed': 7, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.49, 'Temperature': 1}, {'ChangeGroup': None, 'Category': None, 'Beginning': '2022-02-20 18:00:00', 'Ending': '2022-02-20 19:00:00', 'WindDirection': 190, 'WindSpeed': 8, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SKC', 'CloudBase1': None, 'CloudCover2': None, 'CloudBase2': None, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': None, 'Altimeter': 30.44, 'Temperature': 2}]
Data2
Another data with more changes to Category
due to cloud cover. Sorry I'm working in slightly different environments. This set does not have the ChangeGroup
and Category
included
MOCK_DATA2 = [{'Beginning': '2022-02-03 06:00:00', 'Ending': '2022-02-03 07:00:00', 'WindDirection': 10, 'WindSpeed': 12, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 1500, 'CloudCover2': 'OVC', 'CloudBase2': 2100, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '600197', 'Altimeter': 30.25, 'Temperature': -7}, {'Beginning': '2022-02-03 07:00:00', 'Ending': '2022-02-03 08:00:00', 'WindDirection': 10, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '650138', 'Altimeter': 30.26, 'Temperature': -8}, {'Beginning': '2022-02-03 08:00:00', 'Ending': '2022-02-03 09:00:00', 'WindDirection': 0, 'WindSpeed': 12, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '650138', 'Altimeter': 30.27, 'Temperature': -8}, {'Beginning': '2022-02-03 09:00:00', 'Ending': '2022-02-03 10:00:00', 'WindDirection': 10, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '650138', 'Altimeter': 30.25, 'Temperature': -8}, {'Beginning': '2022-02-03 10:00:00', 'Ending': '2022-02-03 11:00:00', 'WindDirection': 0, 'WindSpeed': 14, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '650138', 'Altimeter': 30.25, 'Temperature': -8}, {'Beginning': '2022-02-03 11:00:00', 'Ending': '2022-02-03 12:00:00', 'WindDirection': 10, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '650138', 'Altimeter': 30.25, 'Temperature': -8}, {'Beginning': '2022-02-03 12:00:00', 'Ending': '2022-02-03 13:00:00', 'WindDirection': 10, 'WindSpeed': 12, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '650138', 'Altimeter': 30.28, 'Temperature': -7}, {'Beginning': '2022-02-03 13:00:00', 'Ending': '2022-02-03 14:00:00', 'WindDirection': 10, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '650138', 'Altimeter': 30.28, 'Temperature': -7}, {'Beginning': '2022-02-03 14:00:00', 'Ending': '2022-02-03 15:00:00', 'WindDirection': 20, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '650138', 'Altimeter': 30.28, 'Temperature': -7}, {'Beginning': '2022-02-03 15:00:00', 'Ending': '2022-02-03 16:00:00', 'WindDirection': 10, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '650138', 'Altimeter': 30.28, 'Temperature': -6}, {'Beginning': '2022-02-03 16:00:00', 'Ending': '2022-02-03 17:00:00', 'WindDirection': 20, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 1500, 'CloudCover2': 'OVC', 'CloudBase2': 2200, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '650197', 'Altimeter': 30.27, 'Temperature': -6}, {'Beginning': '2022-02-03 17:00:00', 'Ending': '2022-02-03 18:00:00', 'WindDirection': 20, 'WindSpeed': 14, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1500, 'CloudCover3': 'OVC', 'CloudBase3': 2200.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '650197', 'Altimeter': 30.26, 'Temperature': -5}, {'Beginning': '2022-02-03 18:00:00', 'Ending': '2022-02-03 19:00:00', 'WindDirection': 20, 'WindSpeed': 14, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1500, 'CloudCover3': 'OVC', 'CloudBase3': 2100.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '650197', 'Altimeter': 30.24, 'Temperature': -5}, {'Beginning': '2022-02-03 19:00:00', 'Ending': '2022-02-03 20:00:00', 'WindDirection': 20, 'WindSpeed': 15, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1400, 'CloudCover3': 'OVC', 'CloudBase3': 2100.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510121', 'Icing': '650197', 'Altimeter': 30.22, 'Temperature': -5}, {'Beginning': '2022-02-03 20:00:00', 'Ending': '2022-02-03 21:00:00', 'WindDirection': 20, 'WindSpeed': 15, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1400, 'CloudCover3': 'OVC', 'CloudBase3': 2100.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510121', 'Icing': '650197', 'Altimeter': 30.21, 'Temperature': -5}, {'Beginning': '2022-02-03 21:00:00', 'Ending': '2022-02-03 22:00:00', 'WindDirection': 20, 'WindSpeed': 14, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1400, 'CloudCover3': 'OVC', 'CloudBase3': 2100.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510121', 'Icing': '650197', 'Altimeter': 30.22, 'Temperature': -5}, {'Beginning': '2022-02-03 22:00:00', 'Ending': '2022-02-03 23:00:00', 'WindDirection': 20, 'WindSpeed': 14, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1400, 'CloudCover3': 'OVC', 'CloudBase3': 2100.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '650197', 'Altimeter': 30.23, 'Temperature': -5}, {'Beginning': '2022-02-03 23:00:00', 'Ending': '2022-02-04 00:00:00', 'WindDirection': 20, 'WindSpeed': 14, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1500, 'CloudCover3': 'OVC', 'CloudBase3': 2100.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '650197', 'Altimeter': 30.24, 'Temperature': -5}, {'Beginning': '2022-02-04 00:00:00', 'Ending': '2022-02-04 01:00:00', 'WindDirection': 20, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1500, 'CloudCover3': 'OVC', 'CloudBase3': 2100.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '620197', 'Altimeter': 30.26, 'Temperature': -5}, {'Beginning': '2022-02-04 01:00:00', 'Ending': '2022-02-04 02:00:00', 'WindDirection': 20, 'WindSpeed': 13, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1500, 'CloudCover3': 'OVC', 'CloudBase3': 2200.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '620197', 'Altimeter': 30.27, 'Temperature': -5}, {'Beginning': '2022-02-04 02:00:00', 'Ending': '2022-02-04 03:00:00', 'WindDirection': 20, 'WindSpeed': 12, 'WindGust': None, 'Visibility': 9999, 'Precipitation': '-SN', 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1500, 'CloudCover3': 'OVC', 'CloudBase3': 2200.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '620197', 'Altimeter': 30.27, 'Temperature': -6}, {'Beginning': '2022-02-04 03:00:00', 'Ending': '2022-02-04 04:00:00', 'WindDirection': 20, 'WindSpeed': 12, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 1500, 'CloudCover2': 'OVC', 'CloudBase2': 2200, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': '510131', 'Icing': '620207', 'Altimeter': 30.28, 'Temperature': -6}, {'Beginning': '2022-02-04 04:00:00', 'Ending': '2022-02-04 05:00:00', 'WindDirection': 10, 'WindSpeed': 11, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 1500, 'CloudCover2': 'OVC', 'CloudBase2': 2200, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620207', 'Altimeter': 30.29, 'Temperature': -6}, {'Beginning': '2022-02-04 05:00:00', 'Ending': '2022-02-04 06:00:00', 'WindDirection': 10, 'WindSpeed': 11, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1500, 'CloudCover3': 'OVC', 'CloudBase3': 2200.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620207', 'Altimeter': 30.29, 'Temperature': -6}, {'Beginning': '2022-02-04 06:00:00', 'Ending': '2022-02-04 07:00:00', 'WindDirection': 10, 'WindSpeed': 10, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'FEW', 'CloudBase1': 800, 'CloudCover2': 'BKN', 'CloudBase2': 1500, 'CloudCover3': 'OVC', 'CloudBase3': 2200.0, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620207', 'Altimeter': 30.28, 'Temperature': -7}, {'Beginning': '2022-02-04 07:00:00', 'Ending': '2022-02-04 08:00:00', 'WindDirection': 0, 'WindSpeed': 9, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SCT', 'CloudBase1': 800, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620207', 'Altimeter': 30.29, 'Temperature': -7}, {'Beginning': '2022-02-04 08:00:00', 'Ending': '2022-02-04 09:00:00', 'WindDirection': 350, 'WindSpeed': 10, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 900, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620138', 'Altimeter': 30.31, 'Temperature': -7}, {'Beginning': '2022-02-04 09:00:00', 'Ending': '2022-02-04 10:00:00', 'WindDirection': 350, 'WindSpeed': 10, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 900, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620138', 'Altimeter': 30.32, 'Temperature': -8}, {'Beginning': '2022-02-04 10:00:00', 'Ending': '2022-02-04 11:00:00', 'WindDirection': 350, 'WindSpeed': 9, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 900, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620138', 'Altimeter': 30.33, 'Temperature': -8}, {'Beginning': '2022-02-04 11:00:00', 'Ending': '2022-02-04 12:00:00', 'WindDirection': 340, 'WindSpeed': 8, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'BKN', 'CloudBase1': 900, 'CloudCover2': 'OVC', 'CloudBase2': 1500, 'CloudCover3': None, 'CloudBase3': None, 'CloudCover4': None, 'CloudBase4': None, 'CloudCover5': None, 'CloudBase5': None, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620138', 'Altimeter': 30.35, 'Temperature': -8}, {'Beginning': '2022-02-04 12:00:00', 'Ending': '2022-02-04 13:00:00', 'WindDirection': 350, 'WindSpeed': 8, 'WindGust': None, 'Visibility': 9999, 'Precipitation': None, 'Obscuration': None, 'Other': None, 'TSFlag': False, 'CloudCover1': 'SCT', 'CloudBase1': 900, 'CloudCover2': 'SCT', 'CloudBase2': 1500, 'CloudCover3': 'BKN', 'CloudBase3': 2900.0, 'CloudCover4': 'BKN', 'CloudBase4': 6500.0, 'CloudCover5': 'OVC', 'CloudBase5': 9000.0, 'WSHeight': None, 'WSDirection': None, 'WSSpeed': None, 'Turbulence': None, 'Icing': '620801', 'Altimeter': 30.35, 'Temperature': -9}]
Edit 1:
I've started to move some of the conditions out of the for loop and into more vectorized / list comprehension solutions
def dev_mock(taf: pd.DataFrame):
"""\
applies the ammendment & specification criteria
"""
...
taf.loc[0, 'ChangeGroup'] = 'BECMG'
taf['MaxWind'] = taf[['WindSpeed', 'WindGust']].max(axis=1)
cover, bases = taf[pd.Index(CLOUD_COVER)], taf[pd.Index(CLOUD_BASE)]
cover = ((cover == 'BKN') | (cover == 'OVC'))
cover.columns = bases.columns
taf['MinCig'] = bases[cover].min(axis=1)
taf['Category'] = [criteria.loc[
(cig >= criteria['ceiling']) & (vis >= criteria['visibility'])
].index.max() for cig, vis in zip(taf.MinCig, taf.Visibility)]
...
Edit 2:
My attempt to implement the (削除) itertools.accumulate
(削除ここまで)functools.reduce
function. In the first instance I generate a new DataFrame
as the accumulator from rows 0 & 1 and return that DataFrame
as the accumulator
.
...
def accumulator_funk(acc, row) -> pd.DataFrame:
if not isinstance(acc, pd.DataFrame): # first instance
df = pd.DataFrame(
[acc, row]).set_index('Index')
df.loc[row.Index, 'ChangeGroup'] = 'BECMG' if acc.Category != row.Category else None
return df
acc.loc[row.Index, 'Category'] = row.Category
predom = acc.loc[acc.ChangeGroup == 'BECMG'].iloc[-1]
acc.loc[row.Index, 'ChangeGroup'] = (
'BECMG' if predom.Category != row.Category else None)
return acc
# dfs = itertools.accumulate(
# taf[['Category', 'ChangeGroup']].itertuples(), accumulator_funk)
# fcst = ([df for df in dfs][-1])
# more appropriate solution
fcst = functools.reduce(
accumulator_funk, taf[['Category', 'ChangeGroup']].itertuples())
print(fcst)
Out: MOCK_DATA2
Category ChangeGroup
Index
0 D BECMG
1 C BECMG
2 C None
3 C None
4 C None
5 C None
6 C None
7 C None
8 C None
9 C None
10 D BECMG
11 D None
12 D None
13 D None
14 D None
15 D None
16 D None
17 D None
18 D None
19 D None
20 D None
21 D None
22 D None
23 D None
24 D None
25 D None
26 C BECMG
27 C None
28 C None
29 C None
30 E BECMG
Original Solution:
import pandas as pd
import numpy as np
CRITERIA = {
'ceiling': [2000, 1000, 500, 200, 100],
'visibility': [4800, 3200, 3200, 1600, 800],
'index': ['E', 'D', 'C', 'B', 'A']
}
CLOUD_COVER = (
'CloudCover1',
'CloudCover2',
'CloudCover3',
'CloudCover4',
'CloudCover5'
)
CLOUD_BASE = (
'CloudBase1',
'CloudBase2',
'CloudBase3',
'CloudBase4',
'CloudBase5'
)
CLOUD_GROUP = np.stack((CLOUD_COVER, CLOUD_BASE), axis=1).flatten()
def dev_mock(taf: pd.DataFrame):
"""\
applies the ammendment & specification criteria
"""
criteria = pd.DataFrame(CRITERIA).set_index(
'index').rename_axis('Category')
def lowest_bkn_or_ovc_height(taf_row: pd.Series) -> int:
ceiling = ((taf_row[pd.Index(CLOUD_COVER)] == 'OVC') | (
taf_row[pd.Index(CLOUD_COVER)] == 'BKN'))
if ceiling.any():
return np.nanmin(np.where(
ceiling, taf_row[pd.Index(CLOUD_BASE)], np.nan)
)
return 2000
def max_wind(taf_series: pd.Series) -> int:
return np.max(taf_series[['WindSpeed', 'WindGust']])
for index, current_line in taf.iterrows():
# minimum_flight_category
taf.loc[index, 'Category'] = criteria.loc[
(current_line['Visibility'] >= criteria['visibility']) &
(lowest_bkn_or_ovc_height(current_line) >= criteria['ceiling'])
].index.max()
if index == 0:
taf.loc[index, 'ChangeGroup'] = 'BECMG'
continue
# -------------------------------------------------------
predominate_line = (
taf.loc[taf.loc[:, 'ChangeGroup'] == 'BECMG']).iloc[-1, ]
predom_max_speed = max_wind(predominate_line)
current_max_speed = max_wind(current_line)
becoming_conditions = [
((np.abs(predom_max_speed - current_max_speed) >= 10) |
# NOTE Wind Direction: A change > 30 degrees when the predominant
# wind speed or gusts are expected to be 15 knots or greater
((np.max([predom_max_speed, current_max_speed]) > 15) & (
np.abs(predominate_line['WindDirection'] -
current_line['WindDirection']) > 30))) |
# if the flight category has crossed a threshold
(taf.loc[index, 'Category'] != predominate_line['Category'])
]
taf.loc[index, 'ChangeGroup'] = (
np.where(becoming_conditions, 'BECMG', None))
return taf
if __name__ == '__main__':
dev_mock(pd.DataFrame.from_records(MOCK_DATA))
1 Answer 1
Your second data set is more useful for testing since it outputs different categories instead of all E
.
I am ignoring all of your edited code and am comparing for equivalence based on your original solution: aside from your "edit 2" which has a spelling mistake; funk
should be func
.
In your CRITERIA
, rename index
to category
directly in the literal dictionary so that you don't have to do it later with rename_axis
.
Delete your CLOUD_GROUP
.
Delete the backslash in your docstring for dev_mock
.
Your two inner functions - lowest_bkn_or_ovc_height
and max_wind
- should not be inner since they don't have any closure references; they should be moved to the global scope.
lowest_bkn_or_ovc_height
needs to be rephrased in a vectorised manner. The comparison to OVC
and BKN
should be performed using an isin
call. Don't call any
. Apply your two-dimensional boolean predicate to a slice of the dataframe replacing ignored values with inf
, and then apply min
on axis 1. Take the default of 2000 using nan_to_num
.
Your iterrows
probably can't be avoided due to a convoluted, recursive reliance on a changing predominate_line
(which should be called predominant_line
).
Your becoming_conditions
should not be in a list literal, and your subsequent application of np.where
is incorrect. This is difficult or impossible to vectorise, so revert to a basic Python if
. The binary operators |
, &
should be replaced with Python logical operators or
, and
because you're dealing with scalars.
In your becoming_conditions
, do not call np.max
with a list literal, nor np.abs
, because in both cases there are only two values to compare so you should just use the Python built-ins.
The category assignment can be expressed in a vectorised manner by doing a cross-join (cartesian product) of the taf
and criteria
dataframes; filtering where the minimum predicates are true; doing a group by the original index; and then taking the maximum of each group.
Your dataframe should be loaded such that the timestamps are parsed from strings to actual timestamps. I have not shown this in my suggested code.
Spelling: ammendment
-> amendment
.
You should not, on every loop iteration, repeat your search for the last BECMG
row. This can be stored in a variable that updates whenever a new BECMG
assignment is performed.
Suggested
import pandas as pd
import numpy as np
from io import StringIO
MOCK_DATA = [...]
MOCK_DATA2 = [...]
CRITERIA = {
'ceiling': [2000, 1000, 500, 200, 100],
'visibility': [4800, 3200, 3200, 1600, 800],
'category': ['E', 'D', 'C', 'B', 'A']
}
CLOUD_COVER = [
'CloudCover1',
'CloudCover2',
'CloudCover3',
'CloudCover4',
'CloudCover5',
]
CLOUD_BASE = [
'CloudBase1',
'CloudBase2',
'CloudBase3',
'CloudBase4',
'CloudBase5',
]
def lowest_bkn_or_ovc_height(taf: pd.DataFrame) -> np.ndarray:
ceiling = taf[CLOUD_COVER].isin(('OVC', 'BKN'))
# The cloud base types are screwed up and contain a bunch of None
# that need to be coerced to floats.
min_ceil = taf[CLOUD_BASE].astype(float).values
min_ceil[~ceiling] = np.inf
return np.nan_to_num(
x=min_ceil.min(axis=1),
posinf=2000,
)
def get_category(taf: pd.DataFrame) -> pd.Series:
taf = taf.assign(Index=taf.index)
criteria = pd.DataFrame(CRITERIA)
ceil_choice = (
taf[['Index', 'Ceiling', 'Visibility']]
.join(other=criteria, how='cross')
)
return (
ceil_choice[
(ceil_choice.Visibility >= ceil_choice.visibility) &
(ceil_choice.Ceiling >= ceil_choice.ceiling)
]
.groupby('Index').category.max()
)
def dev_mock(taf: pd.DataFrame):
"""
applies the amendment & specification criteria
"""
taf['Ceiling'] = lowest_bkn_or_ovc_height(taf)
taf['Category'] = get_category(taf)
taf['MaxWind'] = taf[['WindSpeed', 'WindGust']].max(axis=1)
taf.loc[0, 'ChangeGroup'] = 'BECMG'
predominant_line = taf.loc[0]
for index, current_line in taf.iloc[1:, :].iterrows():
becoming_conditions = (
abs(
predominant_line.MaxWind - current_line.MaxWind
) >= 10
# Wind Direction: A change > 30 degrees when the predominant
# wind speed or gusts are expected to be 15 knots or greater
or (
max(predominant_line.MaxWind, current_line.MaxWind) > 15
and abs(
predominant_line.WindDirection - current_line.WindDirection
) > 30
)
# if the flight category has crossed a threshold
or current_line.Category != predominant_line.Category
)
if becoming_conditions:
predominant_line = current_line
group = 'BECMG'
else:
group = None
taf.loc[index, 'ChangeGroup'] = group
return taf
def main() -> None:
df = pd.DataFrame.from_records(MOCK_DATA2)
taf = dev_mock(df.copy())
with StringIO() as f:
taf.to_csv(f)
print(f.getvalue())
if __name__ == '__main__':
main()
-
1\$\begingroup\$ Thanks again for your input, it really helps me learn. Tomorrow I’ll set aside some time and break down your solution. You have a few methods that I haven’t really used before. \$\endgroup\$Jason Leaver– Jason Leaver2022年02月23日 00:41:34 +00:00Commented Feb 23, 2022 at 0:41
MOCK_DATA
is cat E. I'm working from a couple different environments. The output of the update is from a different data source which I'll post as well. \$\endgroup\$ChangeGroup
columns in the input always start off asNone
? \$\endgroup\$TEMPO
that I have yet to really start working on. ATEMPO
condition is a condition which would only be expected to only occur for an intermittent amount of time and potentially several other factors. After this function, rows withChangeGroup == None
get dropped. Valuesdf.fillna("")
df.astype(str)
formatted to the terminal aerodrome forecast specification. Then the entire dataframe is joined as a single stringrow\nrow\n...
. \$\endgroup\$