Menu

[r4678]: / trunk / py4science / examples / pyrex / trailstats / ringbuf.pyx  Maximize  Restore  History

Download this file

209 lines (162 with data), 5.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
"""
ringbuf.pyx Pyrex interface to ringbuf.c It defines ring buffer class,
Ringbuf, on which various statistics are calculated as each entry is
added and a method runstats for computing a host of trailing
statistics over a numpy array
"""
include "c_ringbuf.pxi"
# ringbuf is a C library, but we can give it a pythonic object
# oriented API by managing the ringbuf pointers and function calls in
# a python class
cdef class Ringbuf:
cdef ringbuf_t *rb_ptr
def __new__(self, N):
self.rb_ptr = new_ringbuf(N)
def __dealloc__(self):
delete_ringbuf(self.rb_ptr)
def __getitem__(self, i):
ret = ringbuf_getitem(self.rb_ptr, i)
if ret == 1e38:
raise IndexError("hit bad value marker")
return ret
def __len__(self):
return ringbuf_N_filled(self.rb_ptr)
def __getslice__(self, i0, i1):
xx = list()
i0 = max(0, i0)
i1 = min(ringbuf_N_filled(self.rb_ptr), i1)
for i in range(i0, i1):
x = ringbuf_getitem(self.rb_ptr, i)
if x == 1e38:
raise IndexError("hit bad value marker")
xx.append(x)
return xx
def empty(self):
zero_ringbuf(self.rb_ptr)
def add(self, d):
ringbuf_add(self.rb_ptr, d)
def min(self):
return ringbuf_min(self.rb_ptr)
def max(self):
return ringbuf_max(self.rb_ptr)
def median(self):
return ringbuf_median(self.rb_ptr)
def N_added(self):
return ringbuf_N_added(self.rb_ptr)
def N_good(self):
return ringbuf_N_good(self.rb_ptr)
def mean(self):
return ringbuf_mean(self.rb_ptr)
def sd(self):
return ringbuf_sd(self.rb_ptr)
cdef class RingbufRecords:
cdef ringbuf_t *rb_ptr
cdef object records
def __new__(self, N):
self.rb_ptr = new_ringbuf(N)
self.records = []
def __dealloc__(self):
delete_ringbuf(self.rb_ptr)
del self.records[:]
def __getitem__(self, i):
r = self.records[i]
ret = ringbuf_getitem(self.rb_ptr, i)
if ret == 1e38:
raise IndexError("hit bad value marker")
return ret, r
def __len__(self):
return ringbuf_N_filled(self.rb_ptr)
def __getslice__(self, i0, i1):
xx = list()
i0 = max(0, i0)
i1 = min(ringbuf_N_filled(self.rb_ptr), i1)
for i in range(i0, i1):
x = ringbuf_getitem(self.rb_ptr, i)
if x == 1e38:
raise IndexError("hit bad value marker")
xx.append(x)
return xx, self.records[i0:i1]
def empty(self):
zero_ringbuf(self.rb_ptr)
self.records = []
def add(self, d, r):
ringbuf_add(self.rb_ptr, d)
self.records.append(r)
if len(self.records) > self.rb_ptr.N_size:
del self.records[0]
def min(self):
return ringbuf_min(self.rb_ptr)
def max(self):
return ringbuf_max(self.rb_ptr)
def median(self):
return ringbuf_median(self.rb_ptr)
def N_added(self):
return ringbuf_N_added(self.rb_ptr)
def N_good(self):
return ringbuf_N_good(self.rb_ptr)
def mean(self):
return ringbuf_mean(self.rb_ptr)
def sd(self):
return ringbuf_sd(self.rb_ptr)
# now we'll provide a numpy interface to the runstats function, which
# fills predimensioned arrays with the trailing mean, std, max, etc,
# for a data array
cimport c_numpy
import numpy
c_numpy.import_array()
def runstats(data, nrb):
"""
Compute running stats on 1D array data over window length nrb
"""
# we will be calling the ringbuf C function runstats, which
# expects a bunch of c data pointers to the data arrays as well as
# the output arrays for the mean, std, etc... Create a array for
# each C float array pointer the C function expects. The type of
# the array is c_numpy.ndarray
cdef c_numpy.ndarray c_data
cdef c_numpy.ndarray c_dmean
cdef c_numpy.ndarray c_dstd
cdef c_numpy.ndarray c_dmin
cdef c_numpy.ndarray c_dmax
cdef c_numpy.ndarray c_dmedian
cdef c_numpy.ndarray c_ng
# make sure that the input array is a 1D numpy array of floats.
# asarray is used to copy and cast a python sequence or array to
# the approriate type, with the advantage that if the data is
# already the right type, no copy is performed
data = numpy.asarray(data, dtype=numpy.float_)
if data.ndim != 1:
raise ValueError("data must be 1-D for now")
nd = data.shape[0]
# use numpy.empty_like to create and empty array of the same type
# and shape as data for each of the return stats (mean, std, ...)
# These are genuine numpy arrays we will be passing back to the
# python level. Remember the ng (the number of good elements) is
# an int, and everything else is a float
dmean = numpy.empty_like(data)
dstd = numpy.empty_like(data)
dmin = numpy.empty_like(data)
dmax = numpy.empty_like(data)
dmedian = numpy.empty_like(data)
ng = numpy.empty(data.shape, dtype=numpy.int_)
# now we have to assign the c_data structures and friends to their
# corresponding numpy arrays
c_data = data
c_dmean = dmean
c_dstd = dstd
c_dmin = dmin
c_dmax = dmax
c_dmedian = dmedian
c_ng = ng
# now we call the function and pass in the c data pointers to the
# arrays. The syntax <double *>c_data.data tells pyrex to pass
# the numpy data memory block as a pointer to a float array.
c_runstats(nrb, nd, <double *>c_data.data,
<double *>c_dmean.data,
<double *>c_dstd.data,
<double *>c_dmin.data,
<double *>c_dmax.data,
<double *>c_dmedian.data,
<int *>c_ng.data)
# all done, return the arrays
return dmean, dstd, dmin, dmax, dmedian, ng
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.