Skip to content

Commit 2c241ef

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

File tree

2 files changed

+252
-1
lines changed

2 files changed

+252
-1
lines changed

math/mathcore/inc/TMath.h

+206-1
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,10 @@ 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> T *MovMedian(Long64_t n, T *a, Long64_t k, Double_t *w = nullptr, Long64_t *work = nullptr);
521+
template <typename T> T *MovMean(Long64_t n, T *a, Long64_t k, Double_t *w = nullptr);
522+
template <typename T> Double_t Mode(const Long64_t n, T *a);
523+
520524
template <typename T> Double_t GeomMean(Long64_t n, const T *a);
521525
template <typename Iterator> Double_t GeomMean(Iterator first, Iterator last);
522526

@@ -1083,7 +1087,7 @@ Double_t TMath::Mean(Iterator first, Iterator last, WeightIterator w)
10831087
}
10841088

10851089
////////////////////////////////////////////////////////////////////////////////
1086-
/// Returns the weighted mean of an array a with length n.
1090+
/// Returns the weighted mean of an array with a length n.
10871091

10881092
template <typename T>
10891093
Double_t TMath::Mean(Long64_t n, const T *a, const Double_t *w)
@@ -1095,6 +1099,207 @@ Double_t TMath::Mean(Long64_t n, const T *a, const Double_t *w)
10951099
}
10961100
}
10971101

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

math/mathcore/test/testTMath.cxx

+46
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,42 @@ 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 *movmediana = TMath::MovMedian(n, sa, k1, w, work);
54+
T *movmeana = TMath::MovMean(n, sa, k2, w);
55+
56+
// test results
57+
bool ok1 = true;
58+
bool ok2 = true;
59+
60+
for (Int_t i = 0; i < n && ok1 && ok2; i++) {
61+
ok1 &= (meanresulta[i] == movmeana[i]);
62+
63+
if (!ok1)
64+
Error("testMovingFunctions", "For MovMean, different values found at i = %d", i);
65+
66+
ok2 &= (medianresulta[i] == movmediana[i]);
67+
68+
if (!ok2)
69+
Error("testMovingFunctions", "For MovMedian, different values found at i = %d", i);
70+
}
71+
72+
delete[] movmediana;
73+
delete[] movmeana;
74+
}
3975

4076
template <typename T, typename U>
4177
void testArrayFunctions()
@@ -63,6 +99,7 @@ void testArrayFunctions()
6399
<< " RMS: " << RMS(n, sa)
64100
<< " Median: " << Median(n, sa)
65101
<< " KOrdStat(3): " << KOrdStat(n, sa, k)
102+
<< " Mode: " << Mode(n, sa)
66103
<< endl;
67104

68105
Sort(n, sa, index, kFALSE);
@@ -183,6 +220,15 @@ void testTMath()
183220
testIteratorFunctions<Long_t>();
184221
testIteratorFunctions<Long64_t>();
185222

223+
cout << "\nMoving functions tests: " << endl;
224+
225+
testMovingFunctions<Short_t>();
226+
testMovingFunctions<Int_t>();
227+
testMovingFunctions<Float_t>();
228+
testMovingFunctions<Double_t>();
229+
testMovingFunctions<Long_t>();
230+
testMovingFunctions<Long64_t>();
231+
186232
cout << "\nPoint functions tests: " << endl;
187233

188234
testPoints<Double_t>(1.3, 0.5);

0 commit comments

Comments
 (0)