3 votes

Moyenne glissante sur la diagonale inverse en Python

J'ai un tableau croisé dynamique pandas qui était précédemment décalé et qui ressemble maintenant à ceci :

pivot
    A    B    C    D    E
0  5.3  5.1  3.5  4.2  4.5
1  5.3  4.1  3.5  4.2  NaN
2  4.3  4.1  3.5  NaN  NaN
3  4.3  4.1  NaN  NaN  NaN
4  4.3  NaN  NaN  NaN  NaN

J'essaie de calculer une moyenne mobile avec une fenêtre variable (dans ce cas, 3 et 4 périodes) sur la diagonale inverse en itérant sur chaque colonne et en essayant de stocker cette valeur dans un nouveau cadre de données, qui ressemblerait à ceci :

expected_df with a 3 periods window
    A    B    C    D    E
0  4.3  4.1  3.5  4.2  4.5

expected_df with a 4 periods window
    A    B    C    D    E
0  4.5  4.3  3.5  4.2  4.5

Jusqu'à présent, j'ai essayé de sous-titrer le tableau croisé dynamique d'origine et de créer un cadre de données différent qui ne contient que les valeurs de la fenêtre spécifiée pour chaque colonne, pour ensuite calculer la moyenne, comme ceci :

subset
    A    B    C    D    E
0  4.3  4.1  3.5  4.2  4.5
1  4.3  4.1  3.5  4.2  NaN
2  4.3  4.1  3.5  NaN  NaN

Pour cela, j'ai essayé de construire la boucle for suivante :

df2 = pd.DataFrame()
size = pivot.shape[0]
window = 3

for i in range(size): 
    df2[i] = pivot.iloc[size-window-i:size-i,i]

Ce qui ne fonctionne pas même lorsque cette pivot.iloc[size-window-i:size-i,i] renvoie bien les valeurs dont j'ai besoin lorsque je passe manuellement les index, mais dans la boucle for, il manque la première valeur de la deuxième colonne et ainsi de suite :

df2
    A    B    C    D    E
0  4.3  NaN  NaN  NaN  NaN
1  4.3  4.1  NaN  NaN  NaN
2  4.3  4.1  3.5  NaN  NaN

Est-ce que quelqu'un a une bonne idée sur la façon de calculer la moyenne mobile ou sur la façon de corriger la partie de la boucle for ? Merci d'avance pour vos commentaires.

5voto

piRSquared Points 159

IIUC :

shift tout revenir

shifted = pd.concat([df.iloc[:, i].shift(i) for i in range(df.shape[1])], axis=1)
shifted

     A    B    C    D    E
0  5.3  NaN  NaN  NaN  NaN
1  5.3  5.1  NaN  NaN  NaN
2  4.3  4.1  3.5  NaN  NaN
3  4.3  4.1  3.5  4.2  NaN
4  4.3  4.1  3.5  4.2  4.5

Ensuite, vous pouvez obtenir votre moyen.

# Change this  to get the last n number of rows
shifted.iloc[-3:].mean()

A    4.3
B    4.1
C    3.5
D    4.2
E    4.5
dtype: float64

Ou la moyenne mobile

#   Change this  to get the last n number of rows
shifted.rolling(3, min_periods=1).mean()

          A         B    C    D    E
0  5.300000       NaN  NaN  NaN  NaN
1  5.300000  5.100000  NaN  NaN  NaN
2  4.966667  4.600000  3.5  NaN  NaN
3  4.633333  4.433333  3.5  4.2  NaN
4  4.300000  4.100000  3.5  4.2  4.5

Foulées de Numpy

Je vais utiliser les strides pour construire un tableau 3D et faire la moyenne sur l'un des axes. C'est plus rapide mais confus car tous les ...

Aussi, je n'utiliserais pas ça. Je voulais juste apprendre à saisir les éléments diagonaux via les strides. C'était plus pratique pour moi et je voulais partager.

from numpy.lib.stride_tricks import as_strided as strided

a = df.values

roll = 3
r_ = roll - 1  # one less than roll

h, w = a.shape
w_ = w - 1  # one less than width

b = np.empty((h + 2 * w_ + r_, w), dtype=a.dtype)
b.fill(np.nan)
b[w_ + r_:-w_] = a

s0, s1 = b.strides
a_ = np.nanmean(strided(b, (h + w_, roll, w), (s0, s0, s1 - s0))[w_:], axis=1)

pd.DataFrame(a_, df.index, df.columns)

          A         B    C    D    E
0  5.300000       NaN  NaN  NaN  NaN
1  5.300000  5.100000  NaN  NaN  NaN
2  4.966667  4.600000  3.5  NaN  NaN
3  4.633333  4.433333  3.5  4.2  NaN
4  4.300000  4.100000  3.5  4.2  4.5

Numba

Je me sens mieux avec ça qu'avec les foulées.

import numpy as np
from numba import njit
import warnings

@njit
def dshift(a, roll):
  h, w = a.shape
  b = np.empty((h, roll, w), dtype=np.float64)
  b.fill(np.nan)

  for r in range(roll):
    for i in range(h):
      for j in range(w):
        k = i - j - r
        if k >= 0:
          b[i, r, j] = a[k, j]

  return b

with warnings.catch_warnings():
  warnings.simplefilter('ignore', category=RuntimeWarning)

  df_ = pd.DataFrame(np.nanmean(dshift(a, 3), axis=1, ), df.index, df.columns)

df_

          A         B    C    D    E
0  5.300000       NaN  NaN  NaN  NaN
1  5.300000  5.100000  NaN  NaN  NaN
2  4.966667  4.600000  3.5  NaN  NaN
3  4.633333  4.433333  3.5  4.2  NaN
4  4.300000  4.100000  3.5  4.2  4.5

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X