j'ai une trame de données de pandas de mesures et de poids correspondants:Lissage une série de valeurs pondérées en numpy/pandas
df = pd.DataFrame({'x': np.random.randn(1000), 'w': np.random.rand(1000)})
Je souhaite lisser les valeurs de mesure (x
) tout en prenant l'élément sage poids (w
) en compte. Ceci est indépendant des poids de la fenêtre coulissante, que je voudrais aussi appliquer (par exemple une fenêtre triangulaire, ou quelque chose de colombophile). Ainsi, pour calculer la valeur lissée dans chaque fenêtre, la fonction doit pondérer les éléments tranchés de x
non seulement par la fonction de fenêtre (par exemple triangle), mais aussi par les éléments correspondants dans w
.
Pour autant que je peux dire, pd.rolling_apply
ne sera pas le faire, car elle applique la fonction donnée sur x
et w
séparément. De même, pd.rolling_window
ne prend pas en compte les poids élément par élément du DataFrame source; la fenêtre pondérée (par exemple «triangle») peut être définie par l'utilisateur, mais elle est fixée à l'avant.
Voici mon implémentation ish lente:
def rolling_weighted_triangle(x, w, window_size):
"""Smooth with triangle window, also using per-element weights."""
# Simplify slicing
wing = window_size // 2
# Pad both arrays with mirror-image values at edges
xp = np.r_[x[wing-1::-1], x, x[:-wing-1:-1]]
wp = np.r_[w[wing-1::-1], w, w[:-wing-1:-1]]
# Generate a (triangular) window of weights to slide
incr = 1./(wing + 1)
ramp = np.arange(incr, 1, incr)
triangle = np.r_[ramp, 1.0, ramp[::-1]]
# Apply both sets of weights over each window
slices = (slice(i - wing, i + wing + 1) for i in xrange(wing, len(x) + wing))
out = (np.average(xp[slc], weights=triangle * wp[slc]) for slc in slices)
return np.fromiter(out, x.dtype)
Comment puis-je accélérer ce avec numpy/scipy/pandas géants?
La trame de données peut déjà prendre une partie non triviale de la RAM (lignes 10k à 200M), par ex. l'allocation d'un tableau 2D de poids de fenêtre par élément à l'avant est trop importante. J'essaie de minimiser l'utilisation de tableaux temporaires, peut-être en utilisant np.lib.stride_tricks.as_strided
et np.apply_along_axis
ou np.convolve
, mais n'ont rien trouvé pour répliquer complètement ce qui précède.
est ici l'équivalent d'une fenêtre uniforme, au lieu d'un triangle (en utilisant le get_sliding_window trick from here) - proche, mais pas tout à fait:
def get_sliding_window(a, width):
"""Sliding window over a 2D array.
Source: https://stackoverflow.com/questions/37447347/dataframe-representation-of-a-rolling-window/41406783#41406783
"""
# NB: a = df.values or np.vstack([x, y]).T
s0, s1 = a.strides
m, n = a.shape
return as_strided(a,
shape=(m-width+1, width, n),
strides=(s0, s0, s1))
def rolling_weighted_average(x, w, window_size):
"""Rolling weighted average with a uniform 'boxcar' window."""
wing = window_size // 2
window_size = 2 * wing + 1
xp = np.r_[x[wing-1::-1], x, x[:-wing-1:-1]]
wp = np.r_[w[wing-1::-1], w, w[:-wing-1:-1]]
x_w = np.vstack([xp, wp]).T
wins = get_sliding_window(x_w, window_size)
# TODO - apply triangle window weights - multiply over wins[,:,1]?
result = np.average(wins[:,:,0], axis=1, weights=wins[:,:,1])
return result
N'est-ce pas équivalent à l'application de la fenêtre sur 'w * x'? Peut-être que vous pouvez générer cette colonne en premier? – VBB
Cela ne semble pas être le cas. La moyenne dans une tranche de fenêtre donnée n'est pas nécessairement 0. –