Pandas: Fenêtre roulante avec taille de pas

Créé le 9 févr. 2017  ·  38Commentaires  ·  Source: pandas-dev/pandas

Juste une suggestion - étendez rolling pour prendre en charge une fenêtre déroulante avec une taille de pas, telle que R de rollapply(by=X) .

Exemple de code

Pandas - solution inefficace (appliquer la fonction à chaque fenêtre, puis trancher pour obtenir un résultat sur deux)

import pandas
ts = pandas.Series(range(0, 40, 2))
ts.rolling(5).apply(max).dropna()[::2]

Suggestion:

ts = pandas.Series(range(0, 40, 2))
ts.rolling(window=5, step=2).apply(max).dropna()

Inspiré de R (voir rollapply docs) :

require(zoo)
TS <- zoo(seq(0, 40, 2))
rollapply(TS, 5, FUN=max, by=2)

8 12 16 20 24 28 32 36 40

Enhancement Needs Discussion Numeric Window

Commentaire le plus utile

"Cela pourrait être fait, mais j'aimerais voir un cas d'utilisation où cela compte."

Quel que soit le projet sur lequel j'ai travaillé en utilisant des pandas, j'ai presque toujours raté cette fonctionnalité, elle est utile à chaque fois que vous devez calculer l'application de temps en temps mais que vous avez toujours besoin d'une bonne résolution à l'intérieur de chaque fenêtre.

Tous les 38 commentaires

Si vous utilisez des fonctions 'standard', celles-ci sont vectorisées, et donc rapides ( ts.rolling(5).max().dropna()[::2] ).

IIUC l'économie ici viendrait de l'application de la fonction seulement une fraction du temps (par exemple chaque nième valeur). Mais y a-t-il un cas où cela fait une différence pratique?

cela pourrait être fait, mais j'aimerais voir un cas d'utilisation où cela compte. Cela briserait également l'API « retourner la même taille que l'entrée ». Bien que je ne pense pas que ce soit en fait difficile à mettre en œuvre (bien que cela impliquerait un certain nombre de changements dans la mise en œuvre). Nous utilisons des fenêtres marginales (IOW, calculez la fenêtre et au fur et à mesure que vous avancez, déposez les points qui partent et ajoutez les points que vous gagnez). Il faudrait donc toujours tout calculer, mais vous ne le feriez tout simplement pas sortir.

Merci pour vos réponses !

IIUC l'économie ici viendrait de l'application de la fonction seulement une fraction du temps (par exemple chaque nième valeur). Mais y a-t-il un cas où cela fait une différence pratique?

Mon cas d'utilisation exécute des fonctions d'agrégation (pas seulement max) sur de grandes séries de données temporelles - 400 colonnes, heures de données à 5-25 Hz. J'ai également fait une chose similaire (ingénierie de fonctionnalités sur les données de capteur) dans le passé avec des données jusqu'à 20 kHz. L'exécution de fenêtres de 30 secondes avec un pas de 5 secondes permet d'économiser une grande partie du traitement - par exemple à 25 Hz avec un pas de 5 s, c'est 1/125ème du travail, ce qui fait la différence entre une exécution en 1 minute ou 2 heures.

Je peux évidemment revenir à numpy, mais ce serait bien s'il y avait une API de niveau supérieur pour faire cela. J'ai juste pensé que cela valait la peine d'être suggéré au cas où d'autres le trouveraient également utile - je ne m'attends pas à ce que vous construisiez une fonctionnalité juste pour moi !

vous pouvez d'abord essayer de ré-échantillonner à un intervalle de fréquence plus élevé, puis rouler

quelque chose comme

df = df.resample('30s')
df.rolling(..).max() (ou n'importe quelle fonction)

Salut @jreback , merci pour la suggestion.

Cela fonctionnerait si je n'exécutais que max sur mes données (le rééchantillonnage a besoin d'une fonction de réduction, sinon la valeur par défaut est mean , n'est-ce pas ?) :

df.resample('1s').max().rolling(30).max()

Cependant, j'aimerais exécuter ma fonction de réduction sur 30 secondes de données, puis avancer d'une seconde et l'exécuter sur les 30 secondes suivantes de données, etc. La méthode ci-dessus applique une fonction sur 1 seconde de données, puis une autre fonction sur 30 résultats de la première fonction.

Voici un exemple rapide - exécuter un calcul crête à crête ne fonctionne pas deux fois (évidemment) :

# 10 minutes of data at 5Hz
n = 5 * 60 * 10
rng = pandas.date_range('1/1/2017', periods=n, freq='200ms')
np.random.seed(0)
d = np.cumsum(np.random.randn(n), axis=0)
s = pandas.Series(d, index=rng)

# Peak to peak
def p2p(d):
    return d.max() - d.min()

def p2p_arr(d):
    return d.max(axis=1) - d.min(axis=1)

def rolling_with_step(s, window, step, func):
    # See https://ga7g08.github.io/2015/01/30/Applying-python-functions-in-moving-windows/
    vert_idx_list = np.arange(0, s.size - window, step)
    hori_idx_list = np.arange(window)
    A, B = np.meshgrid(hori_idx_list, vert_idx_list)
    idx_array = A + B
    x_array = s.values[idx_array]
    idx = s.index[vert_idx_list + int(window/2.)]
    d = func(x_array)
    return pandas.Series(d, index=idx)

# Plot data
ax = s.plot(figsize=(12, 8), legend=True, label='Data')

# Plot resample then rolling (obviously does not work)
s.resample('1s').apply(p2p).rolling(window=30, center=True).apply(p2p).plot(ax=ax, label='1s p2p, roll 30 p2p', legend=True)

# Plot rolling window with step
rolling_with_step(s, window=30 * 5, step=5, func=p2p_arr).plot(ax=ax, label='Roll 30, step 1s', legend=True)

rolling window

@alexlouden de votre description originale, je pense quelque chose comme

df.resample('5s').max().rolling('30s').mean() (ou toute autre réduction) est plus conforme à ce que vous voulez

IOW, prenez tout ce qui se trouve dans un bac de 5 secondes, puis réduisez-le à un seul point, puis roulez sur ces bacs. Cette idée générale est que vous avez beaucoup de données qui peuvent être résumées sur une courte échelle de temps, mais vous voulez en fait le faire rouler à un niveau supérieur.

@jreback , je veux en fait exécuter une fonction sur 30 secondes de données, toutes les 5 secondes. Voir la fonction rolling_with_step dans mon exemple précédent. L'étape supplémentaire de max/mean ne fonctionne pas pour mon cas d'utilisation.

@jreback , il existe un réel besoin pour la fonction step qui n'a pas encore été évoqué dans cette discussion. Je soutiens tout ce que

Supposons que nous effectuons une analyse de séries chronologiques avec des données d'entrée échantillonnées en 3 à 10 millisecondes environ. Nous nous intéressons aux caractéristiques du domaine fréquentiel. La première étape de leur construction serait de découvrir la fréquence de Nyquist. Supposons par connaissance du domaine que nous sachions que c'est 10 Hz (une fois toutes les 100 ms). Cela signifie que nous avons besoin que les données aient une fréquence d'au moins 20 Hz (une fois toutes les 50 ms), si les caractéristiques doivent bien capturer le signal d'entrée. Nous ne pouvons pas rééchantillonner à une fréquence inférieure à celle-ci. En fin de compte, voici les calculs que nous effectuons :

df.resample('50ms').mean().rolling(window=32).aggregate(power_spectrum_coeff)

Ici, nous avons choisi une taille de fenêtre par multiples de 8, et en choisissant 32, la taille de la fenêtre est de 1,6 seconde. La fonction d'agrégat renvoie les coefficients du domaine fréquentiel unilatéral et sans la première composante moyenne (la fonction fft est symétrique et avec une valeur moyenne au 0e élément). Voici l'exemple de fonction d'agrégation :

def power_spectrum_coeff():
    def power_spectrum_coeff_(x):
        return np.fft.fft(x)[1 : int(len(x) / 2 + 1)]

    power_spectrum_coeff_.__name__ = 'power_spectrum_coeff'
    return power_spectrum_coeff_

Maintenant, nous aimerions répéter cela dans une fenêtre glissante de, disons, toutes les 0,4 secondes ou toutes les 0,8 secondes. Il ne sert à rien de gaspiller des calculs et de calculer la FFT toutes les 50 ms à la place, puis de trancher plus tard. De plus, le rééchantillonnage jusqu'à 400 ms n'est pas une option, car 400 ms n'est que de 2,5 Hz, ce qui est bien inférieur à la fréquence de Nyquist et cela entraînera la perte de toutes les informations des fonctionnalités.

Il s'agissait de caractéristiques du domaine fréquentiel, qui ont des applications dans de nombreuses expériences scientifiques liées aux séries chronologiques. Cependant, même des fonctions d'agrégation de domaine temporel plus simples telles que l'écart type ne peuvent pas être prises en charge efficacement par le rééchantillonnage.

Bien que je ne pense pas que ce soit en fait difficile à mettre en œuvre (bien que cela impliquerait un certain nombre de changements dans la mise en œuvre). Nous utilisons des fenêtres marginales (IOW, calculez la fenêtre et au fur et à mesure que vous avancez, supprimez les points qui partent et ajoutez les points que vous gagnez). Il faudrait donc toujours tout calculer, mais vous ne le sortiriez tout simplement pas.

Avoir le paramètre "step" et être capable de réduire les calculs réels en l'utilisant doit être l'objectif futur de Pandas. Si le paramètre step ne renvoie que moins de points, cela ne vaut pas la peine de le faire, car nous pouvons de toute façon découper la sortie. Peut-être qu'étant donné le travail que cela implique, nous pourrions simplement recommander à tous les projets ayant ces besoins d'utiliser Numpy.

@Murmuria, vous pouvez soumettre une pull-request pour ce faire. Ce n'est en fait pas si difficile.

Alors que je seconde la demande d'un paramètre step dans rolling() , je tiens à souligner qu'il est possible d'obtenir le résultat souhaité avec le paramètre base dans resample() , si la taille du pas est une fraction entière de la taille de la fenêtre . En utilisant l'exemple de

pandas.concat([
    s.resample('30s', label='left', loffset=pandas.Timedelta(15, unit='s'), base=i).agg(p2p) 
    for i in range(30)
]).sort_index().plot(ax=ax, label='Solution with resample()', legend=True, style='k:')

On obtient le même résultat (notons que la ligne s'étend de 30 s des deux côtés) :
rolling_with_step_using_resample

C'est encore un peu du gaspillage, selon le type d'agrégation. Pour le cas particulier du calcul crête à crête comme dans l'exemple de @alexlouden , p2p_arr() est presque max() et min() .

Le paramètre step dans le roulement permettrait également d'utiliser cette fonctionnalité sans index datetime. Y a-t-il déjà quelqu'un qui travaille dessus ?

@alexlouden ci-dessus a dit ceci :

Je peux évidemment revenir à numpy, mais ce serait bien s'il y avait une API de niveau supérieur pour faire cela.

@alexlouden ou quelqu'un d'autre qui sait s'il vous plaît partager quelques idées sur la façon de faire cela avec numpy? D'après mes recherches jusqu'à présent, il semble que ce ne soit pas anodin de le faire non plus dans numpy. En fait, il y a un problème ouvert à ce sujet ici https://github.com/numpy/numpy/issues/7753

Merci

Salut @tsando - la fonction rolling_with_step j'ai utilisée ci-dessus n'a-t-elle pas fonctionné pour vous ?

@alexlouden merci, je viens de vérifier cette fonction et elle semble toujours dépendre des pandas (prend une série en entrée et utilise également l'index de la série). Je me demandais s'il y avait une approche purement numpy à ce sujet. Dans le fil que j'ai mentionné https://github.com/numpy/numpy/issues/7753, ils proposent une fonction qui utilise des foulées numpy, mais elles sont difficiles à comprendre et à traduire en entrées de fenêtre et d'étape.

@tsando Voici un PDF de l'article de blog auquel j'ai lié ci-dessus - il semble que l'auteur ait changé son nom d'utilisateur Github et n'ait pas réinstallé son site . (Je viens de l'exécuter localement pour le convertir en PDF).

Ma fonction ci-dessus était de convertir son dernier exemple pour travailler avec Pandas - si vous vouliez utiliser directement numpy, vous pourriez faire quelque chose comme ceci : https://gist.github.com/alexlouden/e42f1d96982f7f005e62ebb737dcd987

J'espère que cela t'aides!

@alexlouden merci ! Je viens de l'essayer sur un tableau de forme (13, 1313) mais cela m'a donné cette erreur :

image

"Cela pourrait être fait, mais j'aimerais voir un cas d'utilisation où cela compte."

Quel que soit le projet sur lequel j'ai travaillé en utilisant des pandas, j'ai presque toujours raté cette fonctionnalité, elle est utile à chaque fois que vous devez calculer l'application de temps en temps mais que vous avez toujours besoin d'une bonne résolution à l'intérieur de chaque fenêtre.

Je suis d'accord et supporte cette fonctionnalité aussi

Vous en avez besoin presque à chaque fois lorsque vous traitez des séries chronologiques, la fonctionnalité pourrait donner un bien meilleur contrôle pour générer des fonctionnalités de séries chronologiques à la fois pour la visualisation et l'analyse. Soutenez fortement cette idée !

d'accord et prend en charge cette fonctionnalité aussi

Cela serait très utile pour réduire le temps de calcul tout en gardant une bonne résolution de fenêtre.

Je fournis des codes de solution, qui pourraient être ajustés davantage en fonction de votre cible particulière.

def average_smoothing(signal, kernel_size, stride):
    sample = []
    start = 0
    end = kernel_size
    while end <= len(signal):
        start = start + stride
        end = end + stride
        sample.append(np.mean(signal[start:end]))
    return np.array(sample)

Je suis d'accord et je soutiens cette fonctionnalité. Je vois qu'il est en stop motion en ce moment.

Le calcul puis le sous-échantillonnage ne sont pas une option lorsque vous avez des To de données.

Ce serait aussi très utile dans ce que je fais. J'ai des To de données pour lesquelles j'ai besoin de diverses statistiques de fenêtres qui ne se chevauchent pas pour comprendre les conditions locales. Mon "correctif" actuel consiste simplement à créer un générateur qui découpe les trames de données et les statistiques de rendement. Ce serait très utile d'avoir cette fonctionnalité.

Cette fonctionnalité est en effet indispensable lorsqu'il s'agit de séries temporelles !

D'accord, j'ai certainement besoin de cette fonctionnalité ajoutée. Essayer de faire des corrélations de fenêtre d'exécution entre les cours des actions et créer ma propre fonction pour cela

Je n'arrive pas à croire qu'une fonctionnalité aussi basique n'existe pas encore !
Quand ce problème sera-t-il résolu ?
Merci

Pour contribuer à la « discussion ultérieure » :
Mon cas d'utilisation consiste à calculer une valeur min/max/médiane par heure pour un mois de données avec une résolution de 1 seconde. Ce sont des données de consommation d'énergie et il y a des pics pendant 1 à 2 secondes que je perdrais avec le rééchantillonnage. En dehors de cela, le rééchantillonnage à, par exemple, 5 secondes/1 minute ne changerait pas le fait que je dois toujours calculer 4k/1k fenêtres par jour qui doivent être jetées, plutôt que de simplement pouvoir calculer les 24 fenêtres nécessaires par jour .

Il serait possible de contourner ce problème en utilisant groupby aso mais cela ne semble ni intuitif ni aussi rapide que l'implémentation par roulement (2 secondes pour des fenêtres d'une heure de 2,5 millions avec tri). C'est incroyablement rapide et utile, mais nous avons vraiment besoin d'un argument de foulée pour utiliser pleinement sa puissance.

J'ai jeté un œil au problème. C'est relativement trivial, mais la façon dont le code est implémenté, d'un coup d'œil rapide, je pense qu'il faudra que quelqu'un s'efforce de modifier manuellement toutes les routines de roulement. Aucun d'entre eux ne respecte les limites de fenêtre données par les classes d'indexation. S'ils le faisaient, cette demande ainsi que #11704 seraient très facilement résolvables. En tout cas, je pense que c'est gérable pour quiconque veut passer du temps à rafraîchir les choses. J'ai lancé un PR à moitié cuit (qui devrait être rejeté, juste pour un MVP) pour montrer comment je m'attaquerais au problème.

Fonctionnement:

import numpy as np
import pandas as pd

data = pd.Series(
    np.arange(100),
    index=pd.date_range('2020/05/12 12:00:00', '2020/05/12 12:00:10', periods=100))

print('1s rolling window every 2s')
print(data.rolling('1s', step='2s').apply(np.mean))

data.sort_index(ascending=False, inplace=True)

print('1s rolling window every 500ms (and reversed)')
print(data.rolling('1s', step='500ms').apply(np.mean))

rendements

1s rolling window every 2s
2020-05-12 12:00:00.000000000     4.5
2020-05-12 12:00:02.020202020    24.5
2020-05-12 12:00:04.040404040    44.5
2020-05-12 12:00:06.060606060    64.5
2020-05-12 12:00:08.080808080    84.5
dtype: float64
1s rolling window every 500ms (and reversed)
2020-05-12 12:00:10.000000000    94.5
2020-05-12 12:00:09.494949494    89.5
2020-05-12 12:00:08.989898989    84.5
2020-05-12 12:00:08.484848484    79.5
2020-05-12 12:00:07.979797979    74.5
2020-05-12 12:00:07.474747474    69.5
2020-05-12 12:00:06.969696969    64.5
2020-05-12 12:00:06.464646464    59.5
2020-05-12 12:00:05.959595959    54.5
2020-05-12 12:00:05.454545454    49.5
2020-05-12 12:00:04.949494949    44.5
2020-05-12 12:00:04.444444444    39.5
2020-05-12 12:00:03.939393939    34.5
2020-05-12 12:00:03.434343434    29.5
2020-05-12 12:00:02.929292929    24.5
2020-05-12 12:00:02.424242424    19.5
2020-05-12 12:00:01.919191919    14.5
2020-05-12 12:00:01.414141414     9.5
2020-05-12 12:00:00.909090909     4.5
dtype: float64

Pour plus de détails sur la mise en œuvre, consultez le PR (ou ici : https://github.com/anthonytw/pandas/tree/rolling-window-step)

Bien que j'aurais aimé passer plus de temps à le terminer, je n'en ai malheureusement plus pour m'attaquer au gros travail de retravailler toutes les fonctions de roulement. Ma recommandation à tous ceux qui souhaitent s'attaquer à ce problème serait d'appliquer les limites de fenêtre générées par les classes d'indexation et d'unifier les fonctions rolling_*_fixed/variable. Avec les limites de début et de fin, je ne vois aucune raison pour laquelle elles devraient être différentes, à moins que vous n'ayez une fonction qui fasse quelque chose de spécial avec des données échantillonnées de manière non uniforme (auquel cas cette fonction spécifique serait mieux à même de gérer la nuance, alors peut-être mettre un drapeau ou quelque chose).

Cela fonctionnera-t-il également pour une fenêtre personnalisée utilisant l'approche get_window_bounds() ?

Bonjour, je seconde également la suggestion s'il vous plaît. Ce serait une fonctionnalité vraiment utile.

Si vous utilisez des fonctions 'standard', celles-ci sont vectorisées, et donc rapides ( ts.rolling(5).max().dropna()[::2] ).

IIUC l'économie ici viendrait de l'application de la fonction seulement une fraction du temps (par exemple chaque nième valeur). Mais y a-t-il un cas où cela fait une différence pratique?

J'ai juste un exemple ici: https://stackoverflow.com/questions/63729190/pandas-resample-daily-data-to-annual-data-with-overlap-and-offset

Chaque Nième serait chaque 365ième. La taille de la fenêtre est variable tout au long de la durée de vie du programme et il n'est pas garanti que l'étape soit une fraction entière de la taille de la fenêtre.

J'ai essentiellement besoin d'une taille de fenêtre définie qui passe par "nombre de jours dans l'année à laquelle il regarde", ce qui est impossible avec toutes les solutions que j'ai trouvées pour ce problème jusqu'à présent.

J'ai également un besoin similaire avec le contexte suivant (adapté d'un besoin réel et professionnel) :

  • J'ai une trame de données chronologique avec une colonne d'horodatage et une colonne de valeur, qui représente des événements irréguliers. Comme l'horodatage du moment où un chien est passé sous ma fenêtre et le nombre de secondes qu'il lui a fallu pour passer. Je peux avoir 6 événements pour un jour donné puis aucun événement du tout pour les 2 jours suivants
  • Je voudrais calculer une métrique (disons le temps moyen passé par les chiens devant ma fenêtre) avec une fenêtre mobile de 365 jours, qui roulerait tous les 30 jours

Pour autant que je sache, l'API dataframe.rolling () me permet de spécifier la durée de 365 jours, mais pas la nécessité de sauter 30 jours de valeurs (ce qui est un nombre non constant de lignes) pour calculer la prochaine moyenne sur une autre sélection de 365 jours de valeurs.

De toute évidence, la trame de données résultante que j'attends aura un nombre (beaucoup) plus petit de lignes que la trame de données initiale « événements de chien ».

Juste pour avoir plus de clarté sur cette demande avec un exemple simple.

Si nous avons cette série :

In [1]: s = pd.Series(range(5))

In [2]: s
Out[2]:
0    0
1    1
2    2
3    3
4    4
dtype: int64

et nous avons une taille de fenêtre de 2 et une taille de pas de 1 . Cette première fenêtre à l'index 0 serait évaluée, passerait par dessus la fenêtre à l'index 1 , évaluerait la fenêtre à l'index 2 , etc ?

In [3]: s.rolling(2, step=1, min_periods=0).max()

Out[3]:
0    0.0
1    NaN # step over this observation
2    2.0
3    NaN # step over this observation
4    4.0
dtype: float64

De même si nous avons cette série basée sur le temps

In [1]: s = pd.Series(range(5), index=pd.DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06', '2020-01-09']))

In [2]: s
Out[2]:
2020-01-01    0
2020-01-02    1
2020-01-03    2
2020-01-06    3
2020-01-09    4
dtype: int64

et nous avons une taille de fenêtre de '3D' et une taille de pas de '3D' . Serait-ce le bon résultat ?

In [3]: s.rolling('3D', step='3D', min_periods=0).max()

Out[3]:
2020-01-01    0.0       # evaluate this window
2020-01-02    NaN    # step over this observation (2020-01-01 + 3 days > 2020-01-02)
2020-01-03    NaN    # step over this observation (2020-01-01 + 3 days > 2020-01-03)
2020-01-06    3.0      # evaluate this window ("snap back" to this observation)
2020-01-09    4.0      # evaluate this window (2020-01-06 + 3 days = 2020-01-09)
dtype: float64

@mroeschke par rapport au premier exemple ([3]), les résultats ne sont pas ce à quoi je m'attendrais. Je suppose qu'il s'agit d'une fenêtre de fin (par exemple, à index=0, ce serait le maximum d'éléments à -1 et 0, donc juste max([0]), alors il devrait avancer l'index "1", à index=0 +step=1, et le prochain calcul serait max([0,1]), puis max([1,2]), etc. Il semble que vous vouliez avoir une taille de pas de deux, donc vous voudriez passer de l'index=0 à l'index=0+2=2 (en sautant l'index 1), et continuer comme ça. Dans ce cas, c'est presque correct, mais il ne devrait pas y avoir de NaN. Bien qu'il puisse être "seulement" le double de la taille dans ce cas, dans d'autres cas, c'est important. Par exemple, j'ai environ une heure de données ECG à 500 Hz pour un patient, c'est 1,8 million d'échantillons. Si je voulais une moyenne mobile de 5 minutes toutes les deux minutes, ce serait un tableau de 1,8 million d'éléments avec 30 calculs valides et un peu moins de 1,8 million de NaNs. :-)

Pour l'indexation, step size = 1 est le comportement actuel, c'est-à-dire calculer la caractéristique d'intérêt à l'aide des données de la fenêtre, décaler la fenêtre d'une unité, puis répéter. Dans cet exemple, je souhaite calculer la caractéristique d'intérêt à l'aide des données de la fenêtre, puis décaler de 60 000 indices, puis répéter.

Remarques similaires pour l'époque. Dans ce cas, il peut y avoir un certain désaccord quant à la bonne façon d'implémenter ce type de fenêtre, mais à mon avis la "meilleure" (TM) façon est de commencer à partir de l'instant t0, de trouver tous les éléments de la plage (t0-window , t0], calculez la caractéristique, puis déplacez-vous par la taille du pas. Jetez toutes les fenêtres qui ont moins que le nombre minimum d'éléments (peut être configurable, valeur par défaut à 1). Cet exemple est pour une fenêtre de fin, mais vous pouvez modifier pour s'adapter à n'importe quelle configuration de fenêtre. Cela a l'inconvénient de perdre du temps dans de grands écarts, mais les écarts peuvent être gérés intelligemment et même si vous calculez de manière naïve (parce que vous êtes paresseux comme moi), je n'ai pas encore vu cette question en pratique , car les écarts ne sont généralement pas assez importants pour avoir de l'importance dans les données réelles. YMMV.

C'est peut-être plus clair ? Jetez un œil à mon exemple + code ci-dessus, cela pourrait mieux l'expliquer.

Merci pour la précision @anthonytw. En effet, il semble que j'avais besoin d'interpréter step comme "pas à pas".

En ce qui concerne les NaN, je comprends le sentiment de supprimer automatiquement les NaN dans le résultat de sortie, mais comme mentionné dans https://github.com/pandas-dev/pandas/issues/15354#issuecomment -278676420 par @jreback , il y a une considération de cohérence API pour que la sortie ait la même longueur que l'entrée. Il se peut qu'un utilisateur souhaite également conserver les NaN (peut-être ?), et dropna serait toujours disponible après l'opération rolling(..., step=...).func() .

@mroeschke Je pense que des exceptions devraient être faites. Tant que vous mettez une note explicite dans la documentation et que le comportement n'est pas par défaut, personne ne sera affecté par le fait de ne pas renvoyer un vecteur plein de déchets. Garder NaNs va à l'encontre de la moitié du but. Un objectif est de limiter le nombre de fois que nous effectuons un calcul coûteux. L'autre objectif est de minimiser l'ensemble de fonctionnalités à quelque chose de gérable. L'exemple que je vous ai donné est réel, et pas autant de données que l'on doit vraiment traiter dans une application de surveillance des patients. Est-il vraiment nécessaire d'allouer 60000x l'espace nécessaire, puis de rechercher dans le tableau pour supprimer les NaN ? Pour chaque caractéristique que nous voulons calculer ?

Notez qu'un calcul peut produire un tableau de valeurs. Qu'est-ce que je veux faire avec une forme d'onde ECG ? Eh bien, calculez le spectre de puissance, bien sûr ! Je dois donc allouer suffisamment d'espace pour 1 vecteur PSD complet (150 000 éléments) 1,8 million de fois (2 To de données) puis filtrer pour obtenir les éléments qui m'intéressent (34 Mo). Pour toutes les séries. Pour tous les patients. Je suppose que je dois acheter plus de RAM !

Il convient également de mentionner que NaN, pour certaines fonctionnalités, peut être une sortie significative. Dans ce cas, je ne peux plus faire la différence entre un NaN significatif et les NaN indésirables remplissant les données.

Bien que je comprenne le désir de maintenir l'API, ce n'est pas une fonctionnalité qui cassera un code existant (parce que c'est une nouvelle fonctionnalité qui n'existait pas auparavant), et étant donné la fonctionnalité, il n'y a aucune raison pour que quiconque s'attende à ce qu'elle génère un sortie de même taille. Et même s'ils le faisaient, une note dans la documentation pour la taille du pas serait suffisante. Les inconvénients dépassent de loin tout avantage d'avoir une API "cohérente" (pour une fonctionnalité qui n'existait pas auparavant, remarquez). Ne pas procéder de cette manière paralysera la fonctionnalité, cela ne vaut même presque pas la peine de l'implémenter dans ce cas (d'après mon expérience, le coût de l'espace est presque toujours le facteur le plus important).

Cette page vous a été utile?
0 / 5 - 0 notes