4
votes

Est-il possible d'utiliser pandas.DataFrame.rolling avec un pas supérieur à 1?

En R, vous pouvez calculer une moyenne glissante avec une fenêtre spécifiée qui peut se décaler d'une quantité spécifiée à chaque fois.

Cependant, peut-être que je ne l'ai simplement trouvé nulle part, mais il ne semble pas que vous puissiez le faire dans pandas ou une autre bibliothèque Python?

Est-ce que quelqu'un connaît un moyen de contourner cela? Je vais vous donner un exemple de ce que je veux dire:

example

Ici, nous avons des données bihebdomadaires, et je calcule les deux mois moyenne mobile qui se décale d'un mois, soit 2 lignes.

Donc, dans RI ferait quelque chose comme: two_month__movavg = rollapply (mydata, 4, mean, by = 2, na.pad = FALSE) N'y a-t-il pas d'équivalent en Python?

EDIT1:

DATE  A DEMAND   ...     AA DEMAND  A Price
    0  2006/01/01 00:30:00  8013.27833   ...     5657.67500    20.03
    1  2006/01/01 01:00:00  7726.89167   ...     5460.39500    18.66
    2  2006/01/01 01:30:00  7372.85833   ...     5766.02500    20.38
    3  2006/01/01 02:00:00  7071.83333   ...     5503.25167    18.59
    4  2006/01/01 02:30:00  6865.44000   ...     5214.01500    17.53


3 commentaires

Pouvez-vous nous montrer la trame de données plutôt qu'une image?


Oui, il s'agit donc de données d'une demi-heure, et je veux que la moyenne sur un an (17520) se déplace chaque semaine (de 336). (J'ai édité le message pour le montrer)


L'exemple mensuel ci-dessus était juste pour illustrer mon problème un peu plus simple


3 Réponses :


1
votes

Maintenant, c'est un peu exagéré pour un tableau 1D de données, mais vous pouvez le simplifier et extraire ce dont vous avez besoin. Puisque les pandas peuvent compter sur numpy, vous voudrez peut-être vérifier comment leur fonction de roulement / strided est implémentée. Résultats pour 20 numéros séquentiels. Une fenêtre de 7 jours, marchant / glissant de 2

nans = np.full_like(z, np.nan, dtype='float')  # z is the 20 number sequence
means = np.mean(s, axis=1)   # results from the strided mean
# assign the means to the output array skipping the first and last 3 and striding by 2

nans[3:-3:2] = means        

nans # array([nan, nan, nan,  3., nan,  5., nan,  7., nan,  9., nan, 11., nan, 13., nan, 15., nan, nan, nan, nan])

Voici le code que j'utilise sans la majeure partie de la documentation. Il est dérivé de nombreuses implémentations de fonction strided dans numpy que l'on peut trouver sur ce site. Il y a des variantes et des incarnations, c'est juste une autre.

def stride(a, win=(3, 3), stepby=(1, 1)):
    """Provide a 2D sliding/moving view of an array.
    There is no edge correction for outputs. Use the `pad_` function first."""
    err = """Array shape, window and/or step size error.
    Use win=(3,) with stepby=(1,) for 1D array
    or win=(3,3) with stepby=(1,1) for 2D array
    or win=(1,3,3) with stepby=(1,1,1) for 3D
    ----    a.ndim != len(win) != len(stepby) ----
    """
    from numpy.lib.stride_tricks import as_strided
    a_ndim = a.ndim
    if isinstance(win, int):
        win = (win,) * a_ndim
    if isinstance(stepby, int):
        stepby = (stepby,) * a_ndim
    assert (a_ndim == len(win)) and (len(win) == len(stepby)), err
    shp = np.array(a.shape)    # array shape (r, c) or (d, r, c)
    win_shp = np.array(win)    # window      (3, 3) or (1, 3, 3)
    ss = np.array(stepby)      # step by     (1, 1) or (1, 1, 1)
    newshape = tuple(((shp - win_shp) // ss) + 1) + tuple(win_shp)
    newstrides = tuple(np.array(a.strides) * ss) + a.strides
    a_s = as_strided(a, shape=newshape, strides=newstrides, subok=True).squeeze()
    return a_s

J'ai omis de souligner que vous pouvez créer une sortie que vous pourriez ajouter en tant que colonne aux pandas. Revenons aux définitions originales utilisées ci-dessus

    z = np.arange(20)
    z   #array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
    s = stride(z, (7,), (2,))

np.mean(s, axis=1)  # array([ 3.,  5.,  7.,  9., 11., 13., 15.])


0 commentaires

4
votes

Vous pouvez utiliser à nouveau le roulement, il suffit juste de travailler un peu avec vous attribuez un index

Ici by = 2

by = 2

df.loc[df.index[np.arange(len(df))%by==1],'New']=df.Price.rolling(window=4).mean()
df
    Price    New
0      63    NaN
1      92    NaN
2      92    NaN
3       5  63.00
4      90    NaN
5       3  47.50
6      81    NaN
7      98  68.00
8     100    NaN
9      58  84.25
10     38    NaN
11     15  52.75
12     75    NaN
13     19  36.75


5 commentaires

Pouvez-vous fournir une explication à ce sujet? df.loc [df.index [np.arange (len (df))% by == 1], 'Nouveau'


@newtoR c'est pour obtenir le mod de la taille des fenêtres, donc par exemple, si nous avons 6 lignes, 0,1,2,3,4,5 sera 0,1,0,1,0,1 et nous coupons le mod égal à 1, puis nous coupons à partir de l'index d'origine de df, puis nous attribuons la valeur, puisque les pandas sont sensibles à l'index, l'index non mentionné à gauche sera renvoyé comme NaN


D'accord, et si vous changez votre by = 2 en by = 3 , votre moyenne mobile sera mise à jour?


@newtoR si vous augmentez de == 3, je pense qu'il renverra le même résultat lorsque vous entrez par = 3 dans votre rollapply dans R


Ok c'est super



2
votes

Si la taille des données n'est pas trop grande, voici un moyen simple:

by = 2
win = 4
start = 3 ## it is the index of your 1st valid value.
df.rolling(win).mean()[start::by] ## calculate all, choose what you need.


1 commentaires

Il peut être plus sûr de couper avec iloc, ie. df.rolling (win) .mean (). iloc [start :: by] au cas où l'index DF ne serait pas une séquence numérique commençant à 0.