1 """
2 1-D and 2-D rebinning code.
3 """
4 import numpy
5
6 from reflectometry.reduction import _reduction
7
8 -def rebin(x,I,xo,Io=None,dtype=numpy.float64):
9 """
10 Rebin a vector.
11
12 x are the existing bin edges
13 xo are the new bin edges
14 I are the existing counts (one fewer than edges)
15
16 Io will be used if present, but be sure that it is a contiguous
17 array of the correct shape and size.
18
19 dtype is the type to use for the intensity vectors. This can be
20 integer (uint8, uint16, uint32) or real (float32 or f, float64 or d).
21 The edge vectors are all coerced to doubles.
22
23 Note that total intensity is not preserved for integer rebinning.
24 The algorithm uses truncation so total intensity will be down on
25 average by half the total number of bins.
26 """
27
28 x,xo = _input(x,dtype='d'),_input(xo,dtype='d')
29 shape_in = numpy.array([x.shape[0]-1])
30 shape_out = numpy.array([xo.shape[0]-1])
31
32
33 if dtype is None:
34 try:
35 dtype = I.dtype
36 except AttributeError:
37 dtype = numpy.float64
38 I = _input(I, dtype=dtype)
39 if shape_in != I.shape:
40 raise TypeError("input array incorrect shape %s"%I.shape)
41
42
43 Io = _output(Io,shape_out,dtype=dtype)
44
45
46 try:
47 rebincore = getattr(_reduction,'rebin_'+I.dtype.name)
48 except AttributeError:
49 raise TypeError("rebin supports uint8 uint16 uint32 float32 float64, not "
50 + I.dtype.name)
51 rebincore(x,I,xo,Io)
52 return Io
53
54 -def rebin2d(x,y,I,xo,yo,Io=None,dtype=None):
55 """
56 Rebin a matrix.
57
58 x,y are the existing bin edges
59 xo,yo are the new bin edges
60 I is the existing counts (one fewer than edges in each direction)
61
62 For example, with x representing the column edges in each row and
63 y representing the row edges in each column, the following
64 represents a uniform field::
65
66 >>> from reflectometry.reduction.rebin import rebin2d
67 >>> x,y = [0,2,4,5], [0,1,3]
68 >>> z = [[2,2,1],[4,4,2]]
69
70 We can check this by rebinning with uniform size bins::
71
72 >>> xo,yo = range(6), range(4)
73 >>> rebin2d(y,x,z,yo,xo)
74 array([[ 1., 1., 1., 1., 1.],
75 [ 1., 1., 1., 1., 1.],
76 [ 1., 1., 1., 1., 1.]])
77
78 dtype is the type to use for the intensity vectors. This can be
79 integer (uint8, uint16, uint32) or real (float32 or f, float64 or d).
80 The edge vectors are all coerced to doubles.
81
82 Note that total intensity is not preserved for integer rebinning.
83 The algorithm uses truncation so total intensity will be down on
84 average by half the total number of bins.
85
86 Io will be used if present, if it is contiguous and if it has the
87 correct shape and type for the input. Otherwise it will raise a
88 TypeError. This will allow you to rebin the slices of an appropriately
89 ordered matrix without making copies.
90 """
91
92 x,y,xo,yo = [_input(v, dtype='d') for v in (x,y,xo,yo)]
93 shape_in = numpy.array([x.shape[0]-1,y.shape[0]-1])
94 shape_out = numpy.array([xo.shape[0]-1,yo.shape[0]-1])
95
96
97 if dtype is None:
98 try: dtype = I.dtype
99 except AttributeError: dtype = numpy.float64
100 I = _input(I, dtype=dtype)
101 if (shape_in != I.shape).any():
102 raise TypeError("input array incorrect shape %s"%str(I.shape))
103
104
105 Io = _output(Io,shape_out,dtype=dtype)
106
107
108 try:
109 rebincore = getattr(_reduction,'rebin2d_'+I.dtype.name)
110 except AttributeError:
111 raise TypeError("rebin2d supports uint8 uint16 uint32 float32 float64, not "
112 + I.dtype.name)
113 rebincore(x,y,I,xo,yo,Io)
114 return Io
115
122
123 -def _output(v, shape, dtype=numpy.float64):
124 """
125 Create a contiguous array of the correct shape and type to hold a
126 returned array, reusing an existing array if possible.
127 """
128 if v is None:
129 return numpy.empty(shape,dtype=dtype)
130 if not (isinstance(v,numpy.ndarray)
131 and v.dtype == numpy.dtype(dtype)
132 and (v.shape == shape).all()
133 and v.flags.contiguous):
134 raise TypeError("output vector must be contiguous %s of size %s"
135 %(dtype,shape))
136 return v
137
138
139
140
141 -def _check1d(from_bins,val,to_bins,target):
142 target = _input(target)
143 for (f,F) in [(from_bins,val), (from_bins[::-1],val[::-1])]:
144 for (t,T) in [(to_bins,target), (to_bins[::-1],target[::-1])]:
145 result = rebin(f,F,t)
146 assert numpy.linalg.norm(T-result) < 1e-14, \
147 "rebin failed for %s->%s %s"%(f,t,result)
148
150
151 _check1d([1,2,3,4],[10,20,30],[1,2.5,4],[20,40])
152
153
154 _check1d([0,1,2,3,4],[5,10,20,30],[1,2.5,3],[20,10]);
155
156
157 _check1d([ 1, 2, 3, 4, 5, 6],
158 [ 10, 20, 30, 40, 50],
159 [ 2.5, 3.5], [25])
160
161
162 _check1d([1, 2, 3, 4, 5, 6],
163 [10, 20, 30, 40, 50],
164 [2.1, 2.2, 2.3, 2.4 ],
165 [2, 2, 2]);
166
167
168 _check1d([1, 2, 3, 4, 5, 6],
169 [10, 20, 30, 40, 50],
170 [ 2.5, 4.5 ],
171 [60])
172
174 result = rebin2d(x,y,z,xo,yo)
175 target = numpy.array(zo,dtype=result.dtype)
176 assert numpy.linalg.norm(target-result) < 1e-14, \
177 "rebin2d failed for %s,%s->%s,%s\n%s\n%s\n%s"%(x,y,z,xo,yo,zo)
178
189
191 x,y,I = [0,3,5,7], [0,1,3], [[3,6],[2,4],[2,4]]
192 xo,yo,Io = range(8), range(4), [[1]*3]*7
193 x,y,I,xo,yo,Io = [numpy.array(A,'d') for A in [x,y,I,xo,yo,Io]]
194
195
196 _check2d(x,y,I,xo,yo,Io)
197 _check2d(x[::-1],y,I[::-1,:],xo,yo,Io)
198 _check2d(x,y[::-1],I[:,::-1],xo,yo,Io)
199 _check2d(x,y,I,[7,3,0],yo,[[4]*3,[3]*3])
200 _check2d(x,y,I,xo,[3,2,0],[[1,2]]*7)
201 _check2d(y,x,I.T,yo,xo,Io.T)
202
203
204 _check2d([-1,2,4], [0,1,3], [[3,6],[2,4]],
205 [1,2], [1,2], [1])
206
207 _check2d([0,1,2,3], [0,1,2,3], [[1]*3]*3,
208 [0.5,1.5,2.5], [0.5,1.5,2.5], [[1]*2]*2)
209 for dtype in ['uint8','uint16','uint32','float32','float64']:
210 _check2d([0,1,2,3,4], [0,1,2,3,4],
211 numpy.array([[1]*4]*4, dtype=dtype),
212 [-2,-1,2,5,6], [-2,-1,2,5,6],
213 numpy.array([[0,0,0,0],[0,4,4,0],[0,4,4,0],[0,0,0,0]],
214 dtype=dtype)
215 )
216
217 _uniform_test([1,2.5,4,0.5],[3,1,2.5,1,3.5])
218 _uniform_test([3,2],[1,2])
219
221 _test1d()
222 _test2d()
223
224 if __name__ == "__main__": test()
225