@@ -1473,66 +1473,9 @@ def roll_weighted_var(const float64_t[:] values, const float64_t[:] weights,
14731473# ----------------------------------------------------------------------
14741474# Exponentially weighted moving average
14751475
1476- def ewma_time (const float64_t[:] vals , int64_t[:] start , int64_t[:] end ,
1477- int minp , ndarray[int64_t] times , int64_t halflife ):
1478- """
1479- Compute exponentially-weighted moving average using halflife and time
1480- distances.
1481-
1482- Parameters
1483- ----------
1484- vals : ndarray[float_64]
1485- start: ndarray[int_64]
1486- end: ndarray[int_64]
1487- minp : int
1488- times : ndarray[int64]
1489- halflife : int64
1490-
1491- Returns
1492- -------
1493- ndarray
1494- """
1495- cdef:
1496- Py_ssize_t i, j, num_not_nan = 0 , N = len (vals)
1497- bint is_not_nan
1498- float64_t last_result, weights_dot, weights_sum, weight, halflife_float
1499- float64_t[:] times_float
1500- float64_t[:] observations = np.zeros(N, dtype = float )
1501- float64_t[:] times_masked = np.zeros(N, dtype = float )
1502- ndarray[float64_t] output = np.empty(N, dtype = float )
1503-
1504- if N == 0 :
1505- return output
1506-
1507- halflife_float = < float64_t> halflife
1508- times_float = times.astype(float )
1509- last_result = vals[0 ]
1510-
1511- with nogil:
1512- for i in range (N):
1513- is_not_nan = vals[i] == vals[i]
1514- num_not_nan += is_not_nan
1515- if is_not_nan:
1516- times_masked[num_not_nan- 1 ] = times_float[i]
1517- observations[num_not_nan- 1 ] = vals[i]
1518-
1519- weights_sum = 0
1520- weights_dot = 0
1521- for j in range (num_not_nan):
1522- weight = 0.5 ** (
1523- (times_float[i] - times_masked[j]) / halflife_float)
1524- weights_sum += weight
1525- weights_dot += weight * observations[j]
1526-
1527- last_result = weights_dot / weights_sum
1528-
1529- output[i] = last_result if num_not_nan >= minp else NaN
1530-
1531- return output
1532-
1533-
15341476def ewma (float64_t[:] vals , int64_t[:] start , int64_t[:] end , int minp ,
1535- float64_t com , bint adjust , bint ignore_na ):
1477+ float64_t com , bint adjust , bint ignore_na , float64_t[:] times ,
1478+ float64_t halflife ):
15361479 """
15371480 Compute exponentially-weighted moving average using center-of-mass.
15381481
@@ -1555,13 +1498,15 @@ def ewma(float64_t[:] vals, int64_t[:] start, int64_t[:] end, int minp,
15551498 Py_ssize_t i, j, s, e, nobs, win_size, N = len (vals), M = len (start)
15561499 float64_t[:] sub_vals
15571500 ndarray[float64_t] sub_output, output = np.empty(N, dtype = float )
1558- float64_t alpha, old_wt_factor, new_wt, weighted_avg, old_wt, cur
1501+ float64_t alpha, old_wt_factor, new_wt, weighted_avg, old_wt, cur, delta
15591502 bint is_observation
15601503
15611504 if N == 0 :
15621505 return output
15631506
15641507 alpha = 1. / (1. + com)
1508+ old_wt_factor = 1. - alpha
1509+ new_wt = 1. if adjust else alpha
15651510
15661511 for j in range (M):
15671512 s = start[j]
@@ -1570,9 +1515,6 @@ def ewma(float64_t[:] vals, int64_t[:] start, int64_t[:] end, int minp,
15701515 win_size = len (sub_vals)
15711516 sub_output = np.empty(win_size, dtype = float )
15721517
1573- old_wt_factor = 1. - alpha
1574- new_wt = 1. if adjust else alpha
1575-
15761518 weighted_avg = sub_vals[0 ]
15771519 is_observation = weighted_avg == weighted_avg
15781520 nobs = int (is_observation)
@@ -1587,8 +1529,8 @@ def ewma(float64_t[:] vals, int64_t[:] start, int64_t[:] end, int minp,
15871529 if weighted_avg == weighted_avg:
15881530
15891531 if is_observation or not ignore_na:
1590-
1591- old_wt *= old_wt_factor
1532+ delta = times[i] - times[i - 1 ]
1533+ old_wt *= old_wt_factor ** (delta / halflife)
15921534 if is_observation:
15931535
15941536 # avoid numerical errors on constant series
0 commit comments