1 // SPDX-License-Identifier: Apache-2.0
2 //
3 // Copyright 2008-2016 Conrad Sanderson (http://conradsanderson.id.au)
4 // Copyright 2008-2016 National ICT Australia (NICTA)
5 //
6 // Licensed under the Apache License, Version 2.0 (the "License");
7 // you may not use this file except in compliance with the License.
8 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 // ------------------------------------------------------------------------
17
18
19 //! \addtogroup op_median
20 //! @{
21
22
23
24 //! \brief
25 //! For each row or for each column, find the median value.
26 //! The result is stored in a dense matrix that has either one column or one row.
27 //! The dimension, for which the medians are found, is set via the median() function.
28 template<typename eT, typename T1>
29 inline
30 void
apply(Mat<eT> & out,const Op<T1,op_median> & in,const typename arma_not_cx<eT>::result * junk)31 op_median::apply(Mat<eT>& out, const Op<T1,op_median>& in, const typename arma_not_cx<eT>::result* junk)
32 {
33 arma_extra_debug_sigprint();
34 arma_ignore(junk);
35
36 // typedef typename T1::elem_type eT;
37
38 const uword dim = in.aux_uword_a;
39 arma_debug_check( (dim > 1), "median(): parameter 'dim' must be 0 or 1" );
40
41 const Proxy<T1> P(in.m);
42
43 typedef typename Proxy<T1>::stored_type P_stored_type;
44
45 const bool is_alias = P.is_alias(out);
46
47 if(is_Mat<P_stored_type>::value || is_alias)
48 {
49 const unwrap_check<P_stored_type> tmp(P.Q, is_alias);
50
51 const typename unwrap_check<P_stored_type>::stored_type& X = tmp.M;
52
53 const uword X_n_rows = X.n_rows;
54 const uword X_n_cols = X.n_cols;
55
56 if(dim == 0) // in each column
57 {
58 arma_extra_debug_print("op_median::apply(): dim = 0");
59
60 out.set_size((X_n_rows > 0) ? 1 : 0, X_n_cols);
61
62 if(X_n_rows > 0)
63 {
64 std::vector<eT> tmp_vec(X_n_rows);
65
66 for(uword col=0; col < X_n_cols; ++col)
67 {
68 arrayops::copy( &(tmp_vec[0]), X.colptr(col), X_n_rows );
69
70 out[col] = op_median::direct_median(tmp_vec);
71 }
72 }
73 }
74 else // in each row
75 {
76 arma_extra_debug_print("op_median::apply(): dim = 1");
77
78 out.set_size(X_n_rows, (X_n_cols > 0) ? 1 : 0);
79
80 if(X_n_cols > 0)
81 {
82 std::vector<eT> tmp_vec(X_n_cols);
83
84 for(uword row=0; row < X_n_rows; ++row)
85 {
86 for(uword col=0; col < X_n_cols; ++col) { tmp_vec[col] = X.at(row,col); }
87
88 out[row] = op_median::direct_median(tmp_vec);
89 }
90 }
91 }
92 }
93 else
94 {
95 const uword P_n_rows = P.get_n_rows();
96 const uword P_n_cols = P.get_n_cols();
97
98 if(dim == 0) // in each column
99 {
100 arma_extra_debug_print("op_median::apply(): dim = 0");
101
102 out.set_size((P_n_rows > 0) ? 1 : 0, P_n_cols);
103
104 if(P_n_rows > 0)
105 {
106 std::vector<eT> tmp_vec(P_n_rows);
107
108 for(uword col=0; col < P_n_cols; ++col)
109 {
110 for(uword row=0; row < P_n_rows; ++row) { tmp_vec[row] = P.at(row,col); }
111
112 out[col] = op_median::direct_median(tmp_vec);
113 }
114 }
115 }
116 else // in each row
117 {
118 arma_extra_debug_print("op_median::apply(): dim = 1");
119
120 out.set_size(P_n_rows, (P_n_cols > 0) ? 1 : 0);
121
122 if(P_n_cols > 0)
123 {
124 std::vector<eT> tmp_vec(P_n_cols);
125
126 for(uword row=0; row < P_n_rows; ++row)
127 {
128 for(uword col=0; col < P_n_cols; ++col) { tmp_vec[col] = P.at(row,col); }
129
130 out[row] = op_median::direct_median(tmp_vec);
131 }
132 }
133 }
134 }
135 }
136
137
138
139 //! Implementation for complex numbers
140 template<typename eT, typename T1>
141 inline
142 void
apply(Mat<eT> & out,const Op<T1,op_median> & in,const typename arma_cx_only<eT>::result * junk)143 op_median::apply(Mat<eT>& out, const Op<T1,op_median>& in, const typename arma_cx_only<eT>::result* junk)
144 {
145 arma_extra_debug_sigprint();
146 arma_ignore(junk);
147
148 // typedef typename std::complex<T> eT;
149 typedef typename get_pod_type<eT>::result T;
150
151 arma_type_check(( is_same_type<eT, typename T1::elem_type>::no ));
152
153 const unwrap_check<T1> tmp(in.m, out);
154 const Mat<eT>& X = tmp.M;
155
156 const uword X_n_rows = X.n_rows;
157 const uword X_n_cols = X.n_cols;
158
159 const uword dim = in.aux_uword_a;
160 arma_debug_check( (dim > 1), "median(): parameter 'dim' must be 0 or 1" );
161
162 if(dim == 0) // in each column
163 {
164 arma_extra_debug_print("op_median::apply(): dim = 0");
165
166 out.set_size((X_n_rows > 0) ? 1 : 0, X_n_cols);
167
168 if(X_n_rows > 0)
169 {
170 std::vector< arma_cx_median_packet<T> > tmp_vec(X_n_rows);
171
172 for(uword col=0; col<X_n_cols; ++col)
173 {
174 const eT* colmem = X.colptr(col);
175
176 for(uword row=0; row<X_n_rows; ++row)
177 {
178 tmp_vec[row].val = std::abs(colmem[row]);
179 tmp_vec[row].index = row;
180 }
181
182 uword index1;
183 uword index2;
184 op_median::direct_cx_median_index(index1, index2, tmp_vec);
185
186 out[col] = op_mean::robust_mean(colmem[index1], colmem[index2]);
187 }
188 }
189 }
190 else
191 if(dim == 1) // in each row
192 {
193 arma_extra_debug_print("op_median::apply(): dim = 1");
194
195 out.set_size(X_n_rows, (X_n_cols > 0) ? 1 : 0);
196
197 if(X_n_cols > 0)
198 {
199 std::vector< arma_cx_median_packet<T> > tmp_vec(X_n_cols);
200
201 for(uword row=0; row<X_n_rows; ++row)
202 {
203 for(uword col=0; col<X_n_cols; ++col)
204 {
205 tmp_vec[col].val = std::abs(X.at(row,col));
206 tmp_vec[col].index = col;
207 }
208
209 uword index1;
210 uword index2;
211 op_median::direct_cx_median_index(index1, index2, tmp_vec);
212
213 out[row] = op_mean::robust_mean( X.at(row,index1), X.at(row,index2) );
214 }
215 }
216 }
217 }
218
219
220
221 template<typename T1>
222 inline
223 typename T1::elem_type
median_vec(const T1 & X,const typename arma_not_cx<typename T1::elem_type>::result * junk)224 op_median::median_vec
225 (
226 const T1& X,
227 const typename arma_not_cx<typename T1::elem_type>::result* junk
228 )
229 {
230 arma_extra_debug_sigprint();
231 arma_ignore(junk);
232
233 typedef typename T1::elem_type eT;
234
235 typedef typename Proxy<T1>::stored_type P_stored_type;
236
237 const Proxy<T1> P(X);
238
239 const uword n_elem = P.get_n_elem();
240
241 if(n_elem == 0)
242 {
243 arma_debug_check(true, "median(): object has no elements");
244
245 return Datum<eT>::nan;
246 }
247
248 std::vector<eT> tmp_vec(n_elem);
249
250 if(is_Mat<P_stored_type>::value)
251 {
252 const unwrap<P_stored_type> tmp(P.Q);
253
254 const typename unwrap<P_stored_type>::stored_type& Y = tmp.M;
255
256 arrayops::copy( &(tmp_vec[0]), Y.memptr(), n_elem );
257 }
258 else
259 {
260 if(Proxy<T1>::use_at == false)
261 {
262 typedef typename Proxy<T1>::ea_type ea_type;
263
264 ea_type A = P.get_ea();
265
266 for(uword i=0; i<n_elem; ++i) { tmp_vec[i] = A[i]; }
267 }
268 else
269 {
270 const uword n_rows = P.get_n_rows();
271 const uword n_cols = P.get_n_cols();
272
273 if(n_cols == 1)
274 {
275 for(uword row=0; row < n_rows; ++row) { tmp_vec[row] = P.at(row,0); }
276 }
277 else
278 if(n_rows == 1)
279 {
280 for(uword col=0; col < n_cols; ++col) { tmp_vec[col] = P.at(0,col); }
281 }
282 else
283 {
284 arma_stop_logic_error("op_median::median_vec(): expected a vector" );
285 }
286 }
287 }
288
289 return op_median::direct_median(tmp_vec);
290 }
291
292
293
294 template<typename T1>
295 inline
296 typename T1::elem_type
median_vec(const T1 & X,const typename arma_cx_only<typename T1::elem_type>::result * junk)297 op_median::median_vec
298 (
299 const T1& X,
300 const typename arma_cx_only<typename T1::elem_type>::result* junk
301 )
302 {
303 arma_extra_debug_sigprint();
304 arma_ignore(junk);
305
306 typedef typename T1::elem_type eT;
307 typedef typename T1::pod_type T;
308
309 const Proxy<T1> P(X);
310
311 const uword n_elem = P.get_n_elem();
312
313 if(n_elem == 0)
314 {
315 arma_debug_check(true, "median(): object has no elements");
316
317 return Datum<eT>::nan;
318 }
319
320 std::vector< arma_cx_median_packet<T> > tmp_vec(n_elem);
321
322 if(Proxy<T1>::use_at == false)
323 {
324 typedef typename Proxy<T1>::ea_type ea_type;
325
326 ea_type A = P.get_ea();
327
328 for(uword i=0; i<n_elem; ++i)
329 {
330 tmp_vec[i].val = std::abs( A[i] );
331 tmp_vec[i].index = i;
332 }
333
334 uword index1;
335 uword index2;
336 op_median::direct_cx_median_index(index1, index2, tmp_vec);
337
338 return op_mean::robust_mean( A[index1], A[index2] );
339 }
340 else
341 {
342 const uword n_rows = P.get_n_rows();
343 const uword n_cols = P.get_n_cols();
344
345 if(n_cols == 1)
346 {
347 for(uword row=0; row < n_rows; ++row)
348 {
349 tmp_vec[row].val = std::abs( P.at(row,0) );
350 tmp_vec[row].index = row;
351 }
352
353 uword index1;
354 uword index2;
355 op_median::direct_cx_median_index(index1, index2, tmp_vec);
356
357 return op_mean::robust_mean( P.at(index1,0), P.at(index2,0) );
358 }
359 else
360 if(n_rows == 1)
361 {
362 for(uword col=0; col < n_cols; ++col)
363 {
364 tmp_vec[col].val = std::abs( P.at(0,col) );
365 tmp_vec[col].index = col;
366 }
367
368 uword index1;
369 uword index2;
370 op_median::direct_cx_median_index(index1, index2, tmp_vec);
371
372 return op_mean::robust_mean( P.at(0,index1), P.at(0,index2) );
373 }
374 else
375 {
376 arma_stop_logic_error("op_median::median_vec(): expected a vector" );
377
378 return eT(0);
379 }
380 }
381 }
382
383
384
385 //! find the median value of a std::vector (contents is modified)
386 template<typename eT>
387 inline
388 eT
direct_median(std::vector<eT> & X)389 op_median::direct_median(std::vector<eT>& X)
390 {
391 arma_extra_debug_sigprint();
392
393 // TODO: if NaN is detected, return NaN
394
395 const uword n_elem = uword(X.size());
396 const uword half = n_elem/2;
397
398 typename std::vector<eT>::iterator first = X.begin();
399 typename std::vector<eT>::iterator nth = first + half;
400 typename std::vector<eT>::iterator pastlast = X.end();
401
402 std::nth_element(first, nth, pastlast);
403
404 if((n_elem % 2) == 0) // even number of elements
405 {
406 typename std::vector<eT>::iterator start = X.begin();
407 typename std::vector<eT>::iterator pastend = start + half;
408
409 const eT val1 = (*nth);
410 const eT val2 = (*(std::max_element(start, pastend)));
411
412 return op_mean::robust_mean(val1, val2);
413 }
414 else // odd number of elements
415 {
416 return (*nth);
417 }
418 }
419
420
421
422 template<typename T>
423 inline
424 void
direct_cx_median_index(uword & out_index1,uword & out_index2,std::vector<arma_cx_median_packet<T>> & X)425 op_median::direct_cx_median_index
426 (
427 uword& out_index1,
428 uword& out_index2,
429 std::vector< arma_cx_median_packet<T> >& X
430 )
431 {
432 arma_extra_debug_sigprint();
433
434 typedef arma_cx_median_packet<T> eT;
435
436 // TODO: if NaN is detected, return bool set to false, indicating presence of NaN
437
438 const uword n_elem = uword(X.size());
439 const uword half = n_elem/2;
440
441 typename std::vector<eT>::iterator first = X.begin();
442 typename std::vector<eT>::iterator nth = first + half;
443 typename std::vector<eT>::iterator pastlast = X.end();
444
445 std::nth_element(first, nth, pastlast);
446
447 out_index1 = (*nth).index;
448
449 if((n_elem % 2) == 0) // even number of elements
450 {
451 typename std::vector<eT>::iterator start = X.begin();
452 typename std::vector<eT>::iterator pastend = start + half;
453
454 out_index2 = (*(std::max_element(start, pastend))).index;
455 }
456 else // odd number of elements
457 {
458 out_index2 = out_index1;
459 }
460 }
461
462
463
464 //! @}
465
466