Skip to content

Commit 477936f

Browse files
committed
[TMath] Add moving mean, moving median and mode
1 parent 075aa8e commit 477936f

File tree

2 files changed

+260
-1
lines changed

2 files changed

+260
-1
lines changed

math/mathcore/inc/TMath.h

+222-1
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,13 @@ struct Limits {
517517
template <typename Iterator> Double_t Mean(Iterator first, Iterator last);
518518
template <typename Iterator, typename WeightIterator> Double_t Mean(Iterator first, Iterator last, WeightIterator wfirst);
519519

520+
template <typename T>
521+
T *MovMedian(const Long64_t n, T *a, const Long64_t k, Double_t *w = nullptr, Long64_t *work = nullptr);
522+
template <typename T>
523+
T *MovMean(const Long64_t n, T *a, const Long64_t k, Double_t *w = nullptr);
524+
template <typename T>
525+
Double_t Mode(const Long64_t n, T *a);
526+
520527
template <typename T> Double_t GeomMean(Long64_t n, const T *a);
521528
template <typename Iterator> Double_t GeomMean(Iterator first, Iterator last);
522529

@@ -1083,7 +1090,7 @@ Double_t TMath::Mean(Iterator first, Iterator last, WeightIterator w)
10831090
}
10841091

10851092
////////////////////////////////////////////////////////////////////////////////
1086-
/// Returns the weighted mean of an array a with length n.
1093+
/// Returns the weighted mean of an array with a length n.
10871094

10881095
template <typename T>
10891096
Double_t TMath::Mean(Long64_t n, const T *a, const Double_t *w)
@@ -1095,6 +1102,220 @@ Double_t TMath::Mean(Long64_t n, const T *a, const Double_t *w)
10951102
}
10961103
}
10971104

1105+
////////////////////////////////////////////////////////////////////////////////
1106+
/// Returns the moving mean of an array.
1107+
///
1108+
/// \param[in] n number of array elements
1109+
/// \param[in] *a array
1110+
/// \param[in] k window size
1111+
/// \param[in] *w weight of the array values
1112+
///
1113+
/// The mean values will be calculated over the window value
1114+
/// for each entry a[i] of the array. The function will return
1115+
/// weighted moving mean if the weight argument is provided.
1116+
1117+
template <typename T>
1118+
class Moving {
1119+
public:
1120+
T *a0;
1121+
Double_t *w0;
1122+
Long64_t *work0;
1123+
Long64_t k0;
1124+
Long64_t n0;
1125+
1126+
virtual Double_t GetValue(const Long64_t n, T *a, Double_t *w = nullptr, Long64_t *work = nullptr) = 0;
1127+
virtual ~Moving(){};
1128+
1129+
Moving(const Long64_t n, T *a, const Long64_t k, Double_t *w = nullptr, Long64_t *work = nullptr)
1130+
{
1131+
n0 = n;
1132+
a0 = a;
1133+
k0 = k;
1134+
w0 = w;
1135+
work0 = work;
1136+
}
1137+
1138+
template <typename U>
1139+
void SplitArray(const U *v, U *v1, Long64_t first, const Long64_t last)
1140+
{
1141+
Long64_t i = 0;
1142+
1143+
while (first <= last) {
1144+
v1[i] = v[first];
1145+
i++;
1146+
first++;
1147+
}
1148+
}
1149+
1150+
Double_t GetMoving(const Long64_t n, const Long64_t first, const Long64_t last, Moving *sp)
1151+
{
1152+
Double_t result;
1153+
T *a1 = new T[n + 1];
1154+
SplitArray(a0, a1, first, last);
1155+
1156+
if (w0 != nullptr) {
1157+
Double_t *w1 = new Double_t[n + 1];
1158+
SplitArray(w0, w1, first, last);
1159+
1160+
if (work0 != nullptr) {
1161+
Long64_t *wrk1 = new Long64_t[n + 1];
1162+
SplitArray(work0, wrk1, first, last);
1163+
result = sp->GetValue(n, a1, w1, wrk1);
1164+
delete[] wrk1;
1165+
} else {
1166+
result = sp->GetValue(n, a1, w1);
1167+
}
1168+
delete[] w1;
1169+
} else {
1170+
result = sp->GetValue(n, a1);
1171+
}
1172+
delete[] a1;
1173+
return result;
1174+
}
1175+
1176+
T *GetResult(Moving *sp)
1177+
{
1178+
Long64_t kl = k0 / 2;
1179+
Long64_t kr = (k0 % 2) ? k0 / 2 : k0 / 2 - 1;
1180+
T *mov = new T[n0 + 1];
1181+
1182+
for (Long64_t i = 0; i != n0; i++) {
1183+
kl = (i < k0 / 2 and i > 0) ? i : k0 / 2;
1184+
if (i + kr > n0 - 1) {
1185+
kr--;
1186+
}
1187+
1188+
if (i == 0) {
1189+
if (k0 > 2) {
1190+
mov[i] = sp->GetMoving(kr + 1, 0, kr + 1, sp);
1191+
} else {
1192+
mov[i] = sp->GetMoving(1, 0, 1, sp);
1193+
}
1194+
} else if (i == n0 - 1) {
1195+
mov[i] = sp->GetMoving(kl + 1, i - kl, i, sp);
1196+
} else {
1197+
mov[i] = sp->GetMoving(kl + kr + 1, i - kl, i + kr, sp);
1198+
}
1199+
}
1200+
return mov;
1201+
}
1202+
};
1203+
1204+
template <typename T>
1205+
class MovingMean : public Moving<T> {
1206+
public:
1207+
MovingMean(const Long64_t n, T *a, const Long64_t k, Double_t *w) : Moving<T>(n, a, k, w) {}
1208+
1209+
Double_t GetValue(const Long64_t n, T *a, Double_t *w = nullptr, Long64_t *work = nullptr)
1210+
{
1211+
(void)work;
1212+
Double_t mean = TMath::Mean(n, a, w);
1213+
return mean;
1214+
}
1215+
};
1216+
1217+
template <typename T>
1218+
T *TMath::MovMean(const Long64_t n, T *a, const Long64_t k, Double_t *w)
1219+
{
1220+
if (!a) {
1221+
Fatal("MovMean", "the array is empty");
1222+
}
1223+
if (n <= 1) {
1224+
Fatal("MovMean", "the length of the array must be >1");
1225+
}
1226+
if (k > n) {
1227+
Fatal("MovMean", "the window length k is larger than the length of the array");
1228+
}
1229+
if (k == 1) {
1230+
Fatal("MovMean", "the window length k must be >1");
1231+
}
1232+
1233+
MovingMean<T> *movmean = new MovingMean(n, a, k, w);
1234+
T *result = movmean->GetResult(movmean);
1235+
delete movmean;
1236+
return result;
1237+
}
1238+
1239+
////////////////////////////////////////////////////////////////////////////////
1240+
/// Returns the moving median of an array.
1241+
///
1242+
/// \param[in] n number of array elements
1243+
/// \param[in] *a array
1244+
/// \param[in] k window size
1245+
/// \param[in] *w weight of the array values
1246+
/// \param[in] *work work
1247+
///
1248+
/// The median values will be calculated over the window
1249+
/// value for each entry a[i] of the array. The function will
1250+
/// return weighted moving median if the weight argument
1251+
/// is provided.
1252+
1253+
template <typename T>
1254+
class MovingMedian : public Moving<T> {
1255+
public:
1256+
MovingMedian(const Long64_t n, T *a, const Long64_t k, Double_t *w, Long64_t *work) : Moving<T>(n, a, k, w, work) {}
1257+
1258+
Double_t GetValue(const Long64_t n, T *a, Double_t *w, Long64_t *work)
1259+
{
1260+
Double_t median = TMath::Median(n, a, w, work);
1261+
return median;
1262+
}
1263+
};
1264+
1265+
template <typename T>
1266+
T *TMath::MovMedian(const Long64_t n, T *a, const Long64_t k, Double_t *w, Long64_t *work)
1267+
{
1268+
if (!a) {
1269+
Fatal("MovMedian", "the array is empty");
1270+
}
1271+
if (n <= 1) {
1272+
Fatal("MovMedian", "the length of the array must be >1");
1273+
}
1274+
if (k > n) {
1275+
Fatal("MovMedian", "the window length k is larger than the length of the array");
1276+
}
1277+
if (k == 1) {
1278+
Fatal("MovMedian", "the window length k must be >1");
1279+
}
1280+
1281+
MovingMedian<T> *movmedian = new MovingMedian(n, a, k, w, work);
1282+
T *result = movmedian->GetResult(movmedian);
1283+
delete movmedian;
1284+
return result;
1285+
}
1286+
1287+
////////////////////////////////////////////////////////////////////////////////
1288+
/// Returns the mode of an array. If there are an equal amount of
1289+
/// different values, the smallest value will be returned.
1290+
///
1291+
/// \param[in] n number of array elements
1292+
/// \param[in] *a array
1293+
1294+
template <typename T>
1295+
Double_t TMath::Mode(const Long64_t n, T *a)
1296+
{
1297+
if (!a) {
1298+
Fatal("Mode", "the array is empty");
1299+
}
1300+
if (n < 1) {
1301+
Fatal("Mode", "the length of the array must be >0");
1302+
}
1303+
1304+
Long64_t N1 = 0;
1305+
Long64_t N2 = 0;
1306+
Double_t mode = 0;
1307+
std::sort(a, a + n);
1308+
1309+
for (Long64_t i = 0; i < n; i++) {
1310+
N2 = std::count(a, a + n, a[i]);
1311+
if (N2 > N1 && mode != a[i]) {
1312+
mode = a[i];
1313+
}
1314+
N1 = N2;
1315+
}
1316+
return mode;
1317+
}
1318+
10981319
////////////////////////////////////////////////////////////////////////////////
10991320
/// Returns the geometric mean of an array defined by the iterators.
11001321
/// \f[ GeomMean = (\prod_{i=0}^{n-1} |a[i]|)^{1/n} \f]

math/mathcore/test/testTMath.cxx

+38
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,34 @@ void testNormCross()
3636
<< endl;
3737
}
3838

39+
template <typename T>
40+
void testMovingFunctions()
41+
{
42+
const Long64_t n = 10;
43+
const Long64_t k1 = 5;
44+
const Long64_t k2 = 6;
45+
T sa[n] = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20};
46+
47+
const T meanresulta[n] = {4, 5, 6, 7, 9, 11, 13, 15, 16, 17};
48+
const T medianresulta[n] = {4, 5, 6, 8, 10, 12, 14, 16, 17, 18};
49+
50+
Double_t w[n] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
51+
Long64_t work[n] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
52+
53+
T *movmedian = TMath::MovMedian(n, sa, k1, w, work);
54+
T *movmean = TMath::MovMean(n, sa, k2, w);
55+
56+
for (Long64_t i = 0; i < n; i++) {
57+
if (meanresulta[i] != movmean[i])
58+
Error("testMovingFunctions", "For MovMean, different values found at i = %lld", i);
59+
60+
if (medianresulta[i] != movmedian[i])
61+
Error("testMovingFunctions", "For MovMedian, different values found at i = %lld", i);
62+
}
63+
64+
delete[] movmedian;
65+
delete[] movmean;
66+
}
3967

4068
template <typename T, typename U>
4169
void testArrayFunctions()
@@ -63,6 +91,7 @@ void testArrayFunctions()
6391
<< " RMS: " << RMS(n, sa)
6492
<< " Median: " << Median(n, sa)
6593
<< " KOrdStat(3): " << KOrdStat(n, sa, k)
94+
<< " Mode: " << Mode(n, sa)
6695
<< endl;
6796

6897
Sort(n, sa, index, kFALSE);
@@ -183,6 +212,15 @@ void testTMath()
183212
testIteratorFunctions<Long_t>();
184213
testIteratorFunctions<Long64_t>();
185214

215+
cout << "\nMoving functions tests: " << endl;
216+
217+
testMovingFunctions<Short_t>();
218+
testMovingFunctions<Int_t>();
219+
testMovingFunctions<Float_t>();
220+
testMovingFunctions<Double_t>();
221+
testMovingFunctions<Long_t>();
222+
testMovingFunctions<Long64_t>();
223+
186224
cout << "\nPoint functions tests: " << endl;
187225

188226
testPoints<Double_t>(1.3, 0.5);

0 commit comments

Comments
 (0)