I have some data that represents the area, speed(meters per second), and azimuth(rads) of a polygon. The objective is to determine the mean for each set of parameters. With that mean value, apply a threshold to remove data points that fell outside of the mean deviation threshold. Then recalculate the means for the parameters set.
I am using match
statements so you will need python 3.10
data
DATA = [
[0.045750000000000984, 0.04939999999999974, 0.05244999999999857, 0.054399999999998436, 0.056099999999999456, 0.05375000000000011, 0.047700000000000985, 0.053700000000000594, 0.053249999999999686, 16.81209831859011, 27.983008063348617, 20.301826794339316, 13.421484359287598,
17.10557180223468, 14.686987606192902, 41.18082589372154, 13.065486605460656, 4.608155337880411, 0.7571951341718915, 0.3457777398771206, 0.46056961460740087, 0.2819901554949584, 0.7205708724903156, -0.00040991561554303056, 0.3860776453927442, 0.09629955035400072, 0.2468722764784534],
[0.03290000000000123, 0.033550000000000045, 0.03949999999999949, 0.035550000000001615, 0.03725000000000067, 0.03515000000000022, 0.03714999999999999, 0.041350000000001316, 0.03869999999999882, 14.902912040542054, 15.452401255651447, 12.5484544315597, 10.83783196920491,
13.246734412134916, 12.636336066844674, 19.61093585673077, 8.898327945757917, 26.237157593452878, 0.36589566404637536, 0.5140598344685876, 0.06092353057665044, 0.5433592643289422, 0.3300217799943245, 0.1295695630661193, 0.5813772760582753, 0.97888742111902, 0.2144469684077374],
[0.15945000000000153, 0.1419000000000014, 0.1346, 0.14314999999999908, 0.14485000000000084, 0.1293000000000014, 0.14264999999999992, 0.1434499999999994, 0.08944999999999861, 39.54982055905117, 67.05598966736439, 14.170464300161013, 25.57103344352307, 22.224605807376317,
28.901343579758898, 19.05130673155152, 25.61289901736464, 122.31441473369556, 0.5250514668415044, 0.3119136953061388, 0.3818093125132205, 0.2669557624118119, -0.4876722558321518, 0.03814067199937899, -0.07502082862542193, 0.15371372713178985, -1.1026290280269917],
[0.00340000000000014, 0.0066000000000002515, 0.008499999999999603, 0.007999999999999518, 0.0065500000000002015, 0.008100000000000543, 0.0032500000000004834, 0.018350000000000196, 0.020150000000000542, 22.364276562983136, 20.506557739907425, 6.243036570120132, 19.3458717607394,
10.456833536986126, 19.65879720239808, 14.307243539019602, 9.091403905263792, 18.47420699835527, 0.8485690702159555, 0.40094252533477115, 0.1157765828210181, 0.6944022345849206, 0.46766736064272035, 1.153657712332056, 0.7600461290670872, 0.4170470750740715, 0.49920935232659946],
[None, None, 0.003549999999999955, 0.005799999999999788, 0.005799999999999788, 0.006900000000000275, 0.007049999999999682, 0.006800000000000173, 0.005200000000000045, None, None, 8.06110268044255, 13.695219329907925, 0.0,
12.81872890572318, 6.190861120089245, 3.9861242549543703, 4.403761437912741, None, None, -0.8342338501323102, -0.35990880371032785, 0.0, -2.9708131816772876, -0.12378167714772335, -1.4646911328887278, -2.475146432921943]
]
main.py
import functools
from typing import Iterable, Dict
import pandas as pd
import numpy as np
DATA = ...
INDEX = ['89234', '89467', '89519', '89680', '90104']
#COLUMN = np.repeat(['area', 'MPS', 'azimuth'], 9)
# added MULTI_COLUMN per request
DATE_RANGE = pd.to_datetime(range(9), unit='m', origin=pd.Timestamp('1960-01-01'))
MULTI_COLUMN = pd.MultiIndex.from_tuples((
(date, param) for param in ('area', 'MPS', 'azimuth')
for date in DATE_RANGE), names=['validTime', 'parameters'])
def normalize(
frame_cache: pd.DataFrame, thresh) -> pd.DataFrame:
"""normalize values in the dataframe"""
frame_cache[['area', 'MPS', 'azimuth']] = np.block(
list(outliers2nan(frame_cache, thresh=thresh)))
return frame_cache
def outliers2nan(
frame_cache: pd.DataFrame, thresh: Dict[str, float]) -> Iterable[np.ndarray]:
"""
where a parameter is greater than the minimum mean threshold set the value to NaN
"""
for param, frame in frame_cache.groupby('parameters', axis=1, sort=False):
mean = mean_stack(frame, kind=param)
match param:
case 'azimuth':
deg_diff = (
np.rad2deg(frame_cache['azimuth']) - np.rad2deg(mean))
yield frame.where(abs((deg_diff + 180) % 360 - 180) < thresh[param])
case _:
yield frame.where(abs(frame-mean) < mean*thresh[param])
def mean_stack(
frame: pd.DataFrame, axis=1, kind=None) -> np.ndarray:
"""DataFrame.mean(1) stacked as a np.ndarray"""
match kind:
case 'azimuth':
return (
functools.reduce(_mean_azimuth_reduction, frame.to_numpy().T)[:, np.newaxis])
case _:
return (
frame.mean(axis=axis).to_numpy()[:, np.newaxis])
def _mean_azimuth_reduction(col1: np.ndarray, col2: np.ndarray) -> np.ndarray:
np.nan_to_num(x=col1, nan=col2, copy=False)
mean_rads = (
np.arctan2(np.sin(col1)+np.sin(col2), np.cos(col1)+np.cos(col2)))
return mean_rads
def get_means(df: pd.DataFrame) -> pd.DataFrame:
"""start subroutine"""
return pd.DataFrame({
param: mean_stack(frame, kind=param).squeeze()
for param, frame in df.groupby('parameters', axis=1, sort=False)
}, index=INDEX)
def start():
"""construct df and display means"""
bad_df = pd.DataFrame(
DATA, index=INDEX, columns=MULTI_COLUMN)
bad_means = get_means(bad_df)
good_df = normalize(
bad_df.droplevel('validTime', axis=1).copy(), thresh={'area': .65, 'MPS': .65, 'azimuth': 60.0})
good_means = get_means(good_df)
good_df.columns = MULTI_COLUMN
print(f"""
BAD
{bad_df.stack()}
GOOD
{good_df.stack()}
BAD
{bad_means}
GOOD
{good_means}
DIF
{abs(bad_means-good_means)}
""")
if __name__ == '__main__':
start()
result
A good example of the use case is on the last MPS
column of index 89519
which reported a speed of 122 meters per second. That value gets omitted from the final return value.
BAD
validTime 1960-01-01 00:00:00 1960-01-01 00:01:00 1960-01-01 00:02:00 1960-01-01 00:03:00 1960-01-01 00:04:00 1960-01-01 00:05:00 1960-01-01 00:06:00 1960-01-01 00:07:00 1960-01-01 00:08:00
parameters
89234 MPS 16.812098 27.983008 20.301827 13.421484 17.105572 14.686988 41.180826 13.065487 4.608155
area 0.045750 0.049400 0.052450 0.054400 0.056100 0.053750 0.047700 0.053700 0.053250
azimuth 0.757195 0.345778 0.460570 0.281990 0.720571 -0.000410 0.386078 0.096300 0.246872
89467 MPS 14.902912 15.452401 12.548454 10.837832 13.246734 12.636336 19.610936 8.898328 26.237158
area 0.032900 0.033550 0.039500 0.035550 0.037250 0.035150 0.037150 0.041350 0.038700
azimuth 0.365896 0.514060 0.060924 0.543359 0.330022 0.129570 0.581377 0.978887 0.214447
89519 MPS 39.549821 67.055990 14.170464 25.571033 22.224606 28.901344 19.051307 25.612899 122.314415
area 0.159450 0.141900 0.134600 0.143150 0.144850 0.129300 0.142650 0.143450 0.089450
azimuth 0.525051 0.311914 0.381809 0.266956 -0.487672 0.038141 -0.075021 0.153714 -1.102629
89680 MPS 22.364277 20.506558 6.243037 19.345872 10.456834 19.658797 14.307244 9.091404 18.474207
area 0.003400 0.006600 0.008500 0.008000 0.006550 0.008100 0.003250 0.018350 0.020150
azimuth 0.848569 0.400943 0.115777 0.694402 0.467667 1.153658 0.760046 0.417047 0.499209
90104 MPS NaN NaN 8.061103 13.695219 0.000000 12.818729 6.190861 3.986124 4.403761
area NaN NaN 0.003550 0.005800 0.005800 0.006900 0.007050 0.006800 0.005200
azimuth NaN NaN -0.834234 -0.359909 0.000000 -2.970813 -0.123782 -1.464691 -2.475146
GOOD
1960-01-01 00:00:00 1960-01-01 00:01:00 1960-01-01 00:02:00 1960-01-01 00:03:00 1960-01-01 00:04:00 1960-01-01 00:05:00 1960-01-01 00:06:00 1960-01-01 00:07:00 1960-01-01 00:08:00
89234 MPS 16.812098 27.983008 20.301827 13.421484 17.105572 14.686988 NaN 13.065487 NaN
area 0.045750 0.049400 0.052450 0.054400 0.056100 0.053750 0.047700 0.053700 0.053250
azimuth 0.757195 0.345778 0.460570 0.281990 0.720571 -0.000410 0.386078 0.096300 0.246872
89467 MPS 14.902912 15.452401 12.548454 10.837832 13.246734 12.636336 19.610936 8.898328 NaN
area 0.032900 0.033550 0.039500 0.035550 0.037250 0.035150 0.037150 0.041350 0.038700
azimuth 0.365896 0.514060 0.060924 0.543359 0.330022 0.129570 0.581377 0.978887 0.214447
89519 MPS 39.549821 NaN NaN 25.571033 22.224606 28.901344 19.051307 25.612899 NaN
area 0.159450 0.141900 0.134600 0.143150 0.144850 0.129300 0.142650 0.143450 0.089450
azimuth NaN 0.311914 0.381809 0.266956 -0.487672 0.038141 -0.075021 0.153714 -1.102629
89680 MPS 22.364277 20.506558 6.243037 19.345872 10.456834 19.658797 14.307244 9.091404 18.474207
area 0.003400 0.006600 0.008500 0.008000 0.006550 0.008100 0.003250 NaN NaN
azimuth 0.848569 0.400943 0.115777 0.694402 0.467667 1.153658 0.760046 0.417047 0.499209
90104 MPS NaN NaN 8.061103 NaN NaN NaN 6.190861 3.986124 4.403761
area NaN NaN 0.003550 0.005800 0.005800 0.006900 0.007050 0.006800 0.005200
azimuth NaN NaN -0.834234 NaN NaN NaN NaN -1.464691 -2.475146
BAD
area MPS azimuth
89234 0.051833 18.796161 0.230576
89467 0.036789 14.930121 0.455432
89519 0.136533 40.494653 -0.524696
89680 0.009211 15.605359 0.552226
90104 0.005871 7.022257 -1.823553
GOOD
area MPS azimuth
89234 0.051833 17.625209 0.230576
89467 0.036789 13.516742 0.455432
89519 0.136533 26.818502 -0.525529
89680 0.006343 15.605359 0.552226
90104 0.005871 5.660462 -1.969919
DIF
area MPS azimuth
89234 0.000000 1.170951 0.000000
89467 0.000000 1.413380 0.000000
89519 0.000000 13.676152 0.000833
89680 0.002868 0.000000 0.000000
90104 0.000000 1.361794 0.146366
area
and multipleazimuth
. What is the actual semantic difference from column to column for those areas and azimuths? \$\endgroup\$Timestamp
that was dropped from aMultiIndex
\$\endgroup\$param[N]
column is associated to aTimestamp
. \$\endgroup\$repeat()
ed columns. \$\endgroup\$MultiIndex
. That being said Is there a preferred/more common axis to use timeseries data? \$\endgroup\$