Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error on rolling_apply when returning multiple values #11

Closed
PedroLacerdaELTE opened this issue Sep 6, 2021 · 5 comments
Closed

Error on rolling_apply when returning multiple values #11

PedroLacerdaELTE opened this issue Sep 6, 2021 · 5 comments

Comments

@PedroLacerdaELTE
Copy link

Hi

This is somewhat related to issue #9

I'm using rolling_apply on a function that returns multiple values.

Something like this:

import numpy as np
import numpy_ext as npext

def min_max(arr):
    return np.min(arr), np.max(arr)

a = np.arange(1000)

b = npext.rolling_apply(min_max, 5, a)

However I'm getting this error:
ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 1 dimension(s) and the array at index 1 has 2 dimension(s)

The problem is that, when the NaN are prepended, a 1D vector of NaN is prepended.

Here:

return np.hstack(
        (
            nans(n, array[0].dtype) if len(array) and hasattr(array[0], 'dtype') else nans(n),
            array
        )
    )

I noticed that the nans function is ready to get the shape argument but the prepend only send n, instead of n and len(array[0])

I'd propose making this change on prepend_na:

    return np.vstack(
        (
            nans((n, len(array[0])), array[0].dtype) if len(array) and hasattr(array[0], 'dtype') else nans((n, len(array[0]))),
            array
        )
    )

Note also the change from hstack to vstack

IMPORTANT: this breaks the implementation for 1D arrays if array[0] is a number and doesn't have len()

Regards

@saninstein
Copy link
Contributor

Hi @PedroLacerdaELTE
I will review your suggestion and look what we could do.
Thank you!

@miko1ann
Copy link

Hi, All! I've got same issue.
May be solution could be something like this:

`
def prepend_na(array: np.ndarray, n: int) -> np.ndarray:
"""
Return a copy of array with nans inserted at the beginning.

Parameters
----------
array : np.ndarray
    Input array.
n : int
    Number of elements to insert.

Returns
-------
np.ndarray
    New array with nans added at the beginning.

Examples
--------
>>> prepend_na(np.array([1, 2]), 2)
array([nan, nan,  1.,  2.])
"""

if len(array.shape) == 1:
    return np.hstack(
    (
        nans(n),
        array
    ))
else:
    return np.vstack(
        (
            nans((n, array.shape[1]), array[0].dtype),
            array
        )

)
`

Test:
`
prepend_na(np.ones(4), 2)

array([nan, nan, 1., 1., 1., 1.]

prepend_na(np.ones(4), 20)

array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
nan, nan, nan, nan, nan, nan, nan, 1., 1., 1., 1.])

prepend_na(np.ones((4,2)), 2)

array([[nan, nan],
[nan, nan],
[ 1., 1.],
[ 1., 1.],
[ 1., 1.],
[ 1., 1.]]),

prepend_na(np.ones((4,3)), 2)

array([[nan, nan, nan],
[nan, nan, nan],
[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])

prepend_na(np.ones((4,4)), 2)

array([[nan, nan, nan, nan],
[nan, nan, nan, nan],
[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.]]),

prepend_na(np.ones((4,1)), 2)

array([[nan],
[nan],
[ 1.],
[ 1.],
[ 1.],
[ 1.]])

`

@miko1ann
Copy link

miko1ann commented Sep 11, 2021

And

def rolling_apply(func: Callable, window: int, *arrays: np.ndarray, n_jobs: int = 1, **kwargs) -> np.ndarray:
    """
    Roll a fixed-width window over an array or a group of arrays, producing slices.
    Apply a function to each slice / group of slices, transforming them into a value.
    Perform computations in parallel, optionally.
    Return a new np.ndarray with the resulting values.

    Parameters
    ----------
    func : Callable
        The function to apply to each slice or a group of slices.
    window : int
        Window size.
    *arrays : list
        List of input arrays.
    n_jobs : int, optional
        Parallel tasks count for joblib. If 1, joblib won't be used. Default is 1.
    **kwargs : dict
        Input parameters (passed to func, must be named).

    Returns
    -------
    np.ndarray

    Examples
    --------
    >>> arr = np.array([1, 2, 3, 4, 5])
    >>> rolling_apply(sum, 2, arr)
    array([nan,  3.,  5.,  7.,  9.])
    >>> arr2 = np.array([1.5, 2.5, 3.5, 4.5, 5.5])
    >>> func = lambda a1, a2, k: (sum(a1) + max(a2)) * k
    >>> rolling_apply(func, 2, arr, arr2, k=-1)
    array([  nan,  -5.5,  -8.5, -11.5, -14.5])
    """
    if not any(isinstance(window, t) for t in [int, np.integer]):
        raise TypeError(f'Wrong window type ({type(window)}) int expected')

    window = int(window)

    if max(len(x.shape) for x in arrays) != 1:
        raise ValueError('Wrong array shape. Supported only 1D arrays')

    if len({array.size for array in arrays}) != 1:
        raise ValueError('Arrays must be the same length')

    def _apply_func_to_arrays(idxs):
        return func(*[array[idxs[0]:idxs[-1] + 1] for array in arrays], **kwargs)

    array = arrays[0]
    rolls = rolling(
        array if len(arrays) == n_jobs == 1 else np.arange(len(array)),
        window=window,
        skip_na=True
    )

    if n_jobs == 1:
        if len(arrays) == 1:
            arr = list(map(partial(func, **kwargs), rolls))
        else:
            arr = list(map(_apply_func_to_arrays, rolls))
    else:
        f = delayed(_apply_func_to_arrays)
        arr = Parallel(n_jobs=n_jobs)(f(idxs[[0, -1]]) for idxs in rolls)

    arr = np.array(arr)  # <- here 
    return prepend_na(arr, n=window - 1)   

@emiliobasualdo
Copy link
Contributor

emiliobasualdo commented Jul 14, 2022

I had the same problem
#16 This pull request would solve it

@saninstein
Copy link
Contributor

Version 0.9.8 brings a support of the funcs with multiple output for the rolling_apply

@emiliobasualdo @PedroLacerdaELTE @miko1ann
Thank you for your contribution. Also, please, take my apologies for waiting!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants