Skip to main content
fix to multicolumn
Source Link

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).rename_axis(['validTime', 'parameters'], axis=1)

    bad_means = get_means(bad_df.droplevel('validTime', axis=1))

    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()


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))

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).rename_axis(['validTime', 'parameters'], axis=1)

    bad_means = get_means(bad_df.droplevel('validTime', axis=1))

    good_df = normalize(
        bad_df.droplevel('validTime', axis=1).copy(), thresh={'area': .65, 'MPS': .65, 'azimuth': 60.0})

    good_means = get_means(good_df)

    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()


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()

updated results with multiindex output
Source Link

import functools
from typing import Iterable, Dict
import pandas as pd
import numpy as np

DATA = ...
INDEX = ['89234', '89467', '89519', '89680', '90104']
COLUMN#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))

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=COLUMNcolumns=MULTI_COLUMN).rename_axis('parameters'['validTime', 'parameters'], axis=1)

    bad_means = get_means(bad_df.droplevel('validTime', axis=1))

    good_df = normalize(
        bad_df.droplevel('validTime', axis=1).copy(), thresh={'area': .65, 'MPS': .65, 'azimuth': 60.0})

    good_means = get_means(good_df)
 

    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()

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     area     area     area     area     area     area     area     area     area        MPS        MPS        MPS        MPS        MPS        MPS        MPS        MPS         MPS   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth
89234       0.04575  0.04940  0.05245  0.05440  0.05610  0.05375  0.04770  0.05370  0.05325                     
89234 MPS                   16.812098            27.983008            20.301827            13.421484            17.105572            14.686988            41.180826            13.065487             4.608155
      area                   0.757195045750             0.345778049400             0.460570052450             0.281990054400             0.720571056100 -            0.000410053750             0.386078047700             0.096300053700             0.246872053250
89467      azimuth                0.03290757195             0.03355345778             0.03950460570             0.03555281990             0.03725720571            -0.03515000410             0.03715386078             0.04135096300             0.03870246872
89467 MPS                   14.902912            15.452401            12.548454            10.837832            13.246734            12.636336            19.610936             8.898328            26.237158
      area                   0.365896032900             0.514060033550             0.060924039500             0.543359035550             0.330022037250             0.129570035150             0.581377037150             0.978887041350             0.214447038700
89519      azimuth                0.15945365896             0.14190514060             0.13460060924             0.14315543359             0.14485330022             0.12930129570             0.14265581377             0.14345978887             0.08945214447
89519 MPS                   39.549821            67.055990            14.170464            25.571033            22.224606            28.901344            19.051307            25.612899           122.314415
      area                   0.525051159450             0.311914141900             0.381809134600             0.266956143150 -            0.487672144850             0.038141129300 -            0.075021142650             0.153714143450 -1            0.102629089450
89680      azimuth                0.00340525051             0.00660311914             0.00850381809             0.00800266956            -0.00655487672             0.00810038141            -0.00325075021             0.01835153714  0          -1.02015102629
89680 MPS                   22.364277            20.506558             6.243037            19.345872            10.456834            19.658797            14.307244             9.091404            18.474207
      area                   0.848569003400             0.400943006600             0.115777008500             0.694402008000             0.467667006550  1           0.153658008100             0.760046003250             0.417047018350             0.499209020150
90104      azimuth     NaN      NaN     0.00355848569             0.00580400943             0.00580115777             0.00690694402             0.00705467667             1.153658             0.00680760046             0.00520417047             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
parameters     area     area     area1960-01-01 00:00:00  1960-01-01 00:01:00 area 1960-01-01 00:02:00  1960-01-01 area00:03:00  1960-01-01 00:04:00  area1960-01-01 00:05:00  1960-01-01 00:06:00 area 1960-01-01 00:07:00  1960-01-01 area00:08:00
89234 MPS    area        MPS    16.812098    MPS        MPS27.983008        MPS    20.301827    MPS        MPS13.421484        MPS    17.105572    MPS        MPS14.686988   azimuth   azimuth   azimuth   azimuth   azimuth   azimuthNaN   azimuth   azimuth   azimuth   13.065487                  NaN
89234      area 0.04575  0.04940             0.05245045750             0.05440049400             0.05610052450             0.05375054400             0.04770056100             0.05370053750             0.05325047700  16.812098  27.983008  20.301827  13.421484  17.105572  14 0.686988053700        NaN  13   0.065487053250
      azimuth   NaN          0.757195             0.345778             0.460570             0.281990             0.720571            -0.000410             0.386078             0.096300             0.246872
89467 MPS      0.03290  0.03355  0.03950  0.03555  0.03725  014.03515902912  0.03715  0.04135  0.03870  14.902912    15.452401            12.548454            10.837832            13.246734            12.636336            19.610936             8.898328                  NaN
      area                0.365896032900             0.514060033550             0.060924039500             0.543359035550             0.330022037250             0.129570035150             0.581377037150             0.978887041350             0.214447038700
89519      azimuth             0.15945365896             0.14190514060             0.13460060924             0.14315543359             0.14485330022             0.12930129570             0.14265581377             0.14345978887             0.08945214447
89519 MPS                39.549821                  NaN                  NaN            25.571033            22.224606            28.901344            19.051307            25.612899        NaN          NaN
      area                0.311914159450             0.381809141900             0.266956134600 -            0.487672143150             0.038141144850 -            0.075021129300             0.153714142650 -1            0.102629143450             0.089450
89680      azimuth                  NaN             0.00340311914             0.00660381809             0.00850266956            -0.00800487672             0.00655038141            -0.00810075021             0.00325153714      NaN      NaN-1.102629
89680 MPS                22.364277            20.506558             6.243037            19.345872            10.456834            19.658797            14.307244             9.091404            18.474207 
  0.848569    area                0.400943003400             0.115777006600             0.694402008500             0.467667008000  1.153658           0.760046006550             0.417047008100             0.499209
90104003250                  NaN                  NaN
      azimuth             0.00355848569             0.00580400943             0.00580115777             0.00690694402             0.00705467667             1.153658             0.00680760046             0.00520417047             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

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)

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=COLUMN).rename_axis('parameters', axis=1)

    bad_means = get_means(bad_df)

    good_df = normalize(
        bad_df.copy(), thresh={'area': .65, 'MPS': .65, 'azimuth': 60.0})

    good_means = get_means(good_df)
 

    print(f"""
BAD
{bad_df}

GOOD
{good_df}

BAD
{bad_means}

GOOD
{good_means}

DIF
{abs(bad_means-good_means)}
    """)
 


if __name__ == '__main__':
    start()

BAD
parameters     area     area     area     area     area     area     area     area     area        MPS        MPS        MPS        MPS        MPS        MPS        MPS        MPS         MPS   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth
89234       0.04575  0.04940  0.05245  0.05440  0.05610  0.05375  0.04770  0.05370  0.05325  16.812098  27.983008  20.301827  13.421484  17.105572  14.686988  41.180826  13.065487    4.608155  0.757195  0.345778  0.460570  0.281990  0.720571 -0.000410  0.386078  0.096300  0.246872
89467       0.03290  0.03355  0.03950  0.03555  0.03725  0.03515  0.03715  0.04135  0.03870  14.902912  15.452401  12.548454  10.837832  13.246734  12.636336  19.610936   8.898328   26.237158  0.365896  0.514060  0.060924  0.543359  0.330022  0.129570  0.581377  0.978887  0.214447
89519       0.15945  0.14190  0.13460  0.14315  0.14485  0.12930  0.14265  0.14345  0.08945  39.549821  67.055990  14.170464  25.571033  22.224606  28.901344  19.051307  25.612899  122.314415  0.525051  0.311914  0.381809  0.266956 -0.487672  0.038141 -0.075021  0.153714 -1.102629
89680       0.00340  0.00660  0.00850  0.00800  0.00655  0.00810  0.00325  0.01835  0.02015  22.364277  20.506558   6.243037  19.345872  10.456834  19.658797  14.307244   9.091404   18.474207  0.848569  0.400943  0.115777  0.694402  0.467667  1.153658  0.760046  0.417047  0.499209
90104           NaN      NaN  0.00355  0.00580  0.00580  0.00690  0.00705  0.00680  0.00520        NaN        NaN   8.061103  13.695219   0.000000  12.818729   6.190861   3.986124    4.403761       NaN       NaN -0.834234 -0.359909  0.000000 -2.970813 -0.123782 -1.464691 -2.475146

GOOD
parameters     area     area     area     area     area     area     area     area     area        MPS        MPS        MPS        MPS        MPS        MPS        MPS        MPS        MPS   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth   azimuth
89234       0.04575  0.04940  0.05245  0.05440  0.05610  0.05375  0.04770  0.05370  0.05325  16.812098  27.983008  20.301827  13.421484  17.105572  14.686988        NaN  13.065487        NaN  0.757195  0.345778  0.460570  0.281990  0.720571 -0.000410  0.386078  0.096300  0.246872
89467       0.03290  0.03355  0.03950  0.03555  0.03725  0.03515  0.03715  0.04135  0.03870  14.902912  15.452401  12.548454  10.837832  13.246734  12.636336  19.610936   8.898328        NaN  0.365896  0.514060  0.060924  0.543359  0.330022  0.129570  0.581377  0.978887  0.214447
89519       0.15945  0.14190  0.13460  0.14315  0.14485  0.12930  0.14265  0.14345  0.08945  39.549821        NaN        NaN  25.571033  22.224606  28.901344  19.051307  25.612899        NaN       NaN  0.311914  0.381809  0.266956 -0.487672  0.038141 -0.075021  0.153714 -1.102629
89680       0.00340  0.00660  0.00850  0.00800  0.00655  0.00810  0.00325      NaN      NaN  22.364277  20.506558   6.243037  19.345872  10.456834  19.658797  14.307244   9.091404  18.474207  0.848569  0.400943  0.115777  0.694402  0.467667  1.153658  0.760046  0.417047  0.499209
90104           NaN      NaN  0.00355  0.00580  0.00580  0.00690  0.00705  0.00680  0.00520        NaN        NaN   8.061103        NaN        NaN        NaN   6.190861   3.986124   4.403761       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

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))

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).rename_axis(['validTime', 'parameters'], axis=1)

    bad_means = get_means(bad_df.droplevel('validTime', axis=1))

    good_df = normalize(
        bad_df.droplevel('validTime', axis=1).copy(), thresh={'area': .65, 'MPS': .65, 'azimuth': 60.0})

    good_means = get_means(good_df)

    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()

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
update formatting, add context from comment
Source Link

I am using matchmatch statements so you will need python 3.10

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.

I am using match statements so you will need python 3.10

I am using match statements so you will need python 3.10

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.

changed `col` to `param` it makes more sense
Source Link
Loading
Source Link
Loading