Skip to content

Commit 3e0e0d2

Browse files
authored
Merge pull request jOOQ#387 from Lu-Jichen/issue360
implementation of issue 360
2 parents b69c5b7 + 4aa2ec2 commit 3e0e0d2

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

jOOL-java-8/src/main/java/org/jooq/lambda/Agg.java

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323

2424
import java.util.*;
2525
import java.util.Map.Entry;
26+
import java.util.Objects;
27+
import java.util.Optional;
28+
import java.util.TreeSet;
2629
import java.util.function.*;
2730
import java.util.stream.Collector;
2831
import java.util.stream.Stream;
@@ -1076,4 +1079,149 @@ private static int percentileIndex(double percentile, int size) {
10761079
s -> s.map(Objects::toString).orElse("")
10771080
);
10781081
}
1082+
1083+
1084+
//CS304 Issue link: https://github.com/jOOQ/jOOL/issues/360
1085+
1086+
/**
1087+
* Calculate the variance of objects with mapping function from object to double value,
1088+
* with given list, size and mapping function.
1089+
*
1090+
* <p><pre>
1091+
* This function is based on the definition of standard deviation:
1092+
* First, calculate the sum of value of all objects and the average value;
1093+
* Second, calculate the sum of square of difference between each objects and average;
1094+
* Third, use sum of variance divide size to obtain variance of all objects.
1095+
* </pre>
1096+
*
1097+
* @param function mapping function from object to double value
1098+
* @param l a list containing all the objects
1099+
* @param size the size of the list in collector
1100+
* @return the variance value
1101+
* @version 1.0
1102+
* @author Jichen Lu
1103+
* @date 2021-04-25
1104+
*/
1105+
private static <T> double getVariance(
1106+
Function<? super T, Double> function,
1107+
ArrayList<T> l, int size) {
1108+
double sum = 0.0;
1109+
double average;
1110+
double sumVariance = 0.0;
1111+
double variance;
1112+
for (T o : l) {
1113+
double temp = function.apply(o);
1114+
sum += temp;
1115+
}
1116+
average = sum / size;
1117+
for (T o : l) {
1118+
double temp = Math.pow(function.apply(o) - average, 2);
1119+
sumVariance += temp;
1120+
}
1121+
variance = sumVariance / size;
1122+
return variance;
1123+
}
1124+
1125+
1126+
//CS304 Issue link: https://github.com/jOOQ/jOOL/issues/360
1127+
1128+
/**
1129+
* Calculate the variance of the given object collectors,
1130+
* based on the mapping function from object to double number.
1131+
*
1132+
* <p><pre> Usage of aggregation function stddevBy():
1133+
* The mapping function is a function mapping the objects to a double value, for instance:
1134+
* {@code
1135+
* Function<Integer, Double> mapping = e -> Double.valueOf(e);
1136+
* }
1137+
* The specific usage of stddevBy is like:
1138+
* {@code
1139+
* Seq.of(1, 1, 1, 1).collect(Agg.stddevBy(mapping));
1140+
* }
1141+
* Besides, self defined class is also allowed with mapping function:
1142+
* {@code
1143+
* Seq.of(new Node(1), new Node(1), new Node(1), new Node(1)).collect(Agg.stddevBy(mapping1));
1144+
* }
1145+
* </pre>
1146+
*
1147+
* @param function mapping function from object to double value
1148+
* @return the stddev value
1149+
* @version 1.0
1150+
* @author Jichen Lu
1151+
* @date 2021-04-25
1152+
*/
1153+
public static <T> Collector<T, ?, Optional<Double>> stddevBy(
1154+
Function<? super T,
1155+
Double> function) {
1156+
return Collector.of(
1157+
(Supplier<ArrayList<T>>) ArrayList::new,
1158+
ArrayList::add,
1159+
(l1, l2) -> {
1160+
l1.addAll(l2);
1161+
return l1;
1162+
},
1163+
l -> {
1164+
int size = l.size();
1165+
if (size == 0) {
1166+
return Optional.empty();
1167+
}
1168+
double variance = getVariance(function, l, size);
1169+
double stddev;
1170+
1171+
1172+
stddev = Math.sqrt(variance);
1173+
1174+
return Optional.of(stddev);
1175+
}
1176+
);
1177+
}
1178+
1179+
1180+
//CS304 Issue link: https://github.com/jOOQ/jOOL/issues/360
1181+
1182+
/**
1183+
* Calculate the variance of the given object collectors,
1184+
* based on the mapping function from object to double number.
1185+
*
1186+
* <p><pre> Usage of aggregation function varianceBy():
1187+
* The mapping function is a function mapping the objects to a double value, for instance:
1188+
* {@code
1189+
* Function<Integer, Double> mapping = e -> Double.valueOf(e);
1190+
* }
1191+
* The specific usage of varianceBy is like:
1192+
* {@code
1193+
* Seq.of(1, 1, 1, 1).collect(Agg.varianceBy(mapping));
1194+
* }
1195+
* Besides, self defined class is also allowed with mapping function:
1196+
* {@code
1197+
* Seq.of(new Node(1), new Node(1), new Node(1), new Node(1)).collect(Agg.varianceBy(mapping1));
1198+
* }
1199+
* </pre>
1200+
*
1201+
* @param function mapping function from object to double value
1202+
* @return the stddev value
1203+
* @version 1.0
1204+
* @author Jichen Lu
1205+
* @date 2021-04-25
1206+
*/
1207+
public static <T> Collector<T, ?, Optional<Double>> varianceBy(
1208+
Function<? super T,
1209+
Double> function) {
1210+
return Collector.of(
1211+
(Supplier<ArrayList<T>>) ArrayList::new,
1212+
ArrayList::add,
1213+
(l1, l2) -> {
1214+
l1.addAll(l2);
1215+
return l1;
1216+
},
1217+
l -> {
1218+
int size = l.size();
1219+
if (size == 0) {
1220+
return Optional.empty();
1221+
}
1222+
double variance = getVariance(function, l, size);
1223+
return Optional.of(variance);
1224+
}
1225+
);
1226+
}
10791227
}

jOOL-java-8/src/test/java/org/jooq/lambda/CollectorTests.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import static org.jooq.lambda.tuple.Tuple.tuple;
2323
import static org.junit.Assert.assertEquals;
2424

25+
import java.text.DecimalFormat;
26+
import java.util.Optional;
27+
import java.util.function.Function;
2528
import java.util.Comparator;
2629
import java.util.Optional;
2730
import java.util.function.Supplier;
@@ -1309,4 +1312,74 @@ public void testDropping() {
13091312
assertEquals(Seq.of().toList(), Seq.of(1, 2, 3, 4, 5).collect(Agg.dropping(6)).toList());
13101313
assertEquals(Seq.of("c", "d").toList(), Seq.of("a", "b", "c", "d").collect(Agg.dropping(2)).toList());
13111314
}
1315+
1316+
//CS304 (manually written) Issue link: https://github.com/jOOQ/jOOL/issues/360
1317+
1318+
/**
1319+
* Test the Seq with numbers.
1320+
*
1321+
* @result The result will be the standard deviation and variance.
1322+
*/
1323+
@Test
1324+
public void testStddevAndVarianceWithNumber() {
1325+
DecimalFormat df = new java.text.DecimalFormat("#.000");
1326+
1327+
Function<Integer, Double> mapping = Double::valueOf;
1328+
1329+
assertEquals(Optional.empty(), Seq.<Integer>of().collect(Agg.varianceBy(mapping)));
1330+
assertEquals(Optional.empty(), Seq.<Integer>of().collect(Agg.stddevBy(mapping)));
1331+
assertEquals(Optional.of(0.0), Seq.of(1).collect(Agg.varianceBy(mapping)));
1332+
assertEquals(Optional.of(0.0), Seq.of(1).collect(Agg.stddevBy(mapping)));
1333+
assertEquals(Optional.of(0.0), Seq.of(1, 1, 1, 1).collect(Agg.varianceBy(mapping)));
1334+
assertEquals(Optional.of(0.0), Seq.of(1, 1, 1, 1).collect(Agg.stddevBy(mapping)));
1335+
assertEquals(Optional.of(1.0), Seq.of(1, 1, 3, 3).collect(Agg.varianceBy(mapping)));
1336+
assertEquals(Optional.of(1.0), Seq.of(1, 1, 3, 3).collect(Agg.stddevBy(mapping)));
1337+
assertEquals(Optional.of(1.250), Seq.of(1, 2, 3, 4).collect(Agg.varianceBy(mapping)));
1338+
assertEquals(Optional.of(1.118), Optional.of(Double.parseDouble(df.format(Seq.of(1, 2, 3, 4).collect(Agg.stddevBy(mapping)).get()))));
1339+
1340+
1341+
}
1342+
1343+
//CS304 (manually written) Issue link: https://github.com/jOOQ/jOOL/issues/360
1344+
1345+
/**
1346+
* Test the Seq with numbers.
1347+
*
1348+
* @result The result will be the standard deviation and variance.
1349+
*/
1350+
@Test
1351+
public void testStddevAndVarianceWithObject() {
1352+
DecimalFormat df = new java.text.DecimalFormat("#.000");
1353+
1354+
class Node {
1355+
final int value;
1356+
1357+
Node(int value) {
1358+
this.value = value;
1359+
}
1360+
1361+
public Double function() {
1362+
return (double) value;
1363+
}
1364+
1365+
public int getValue() {
1366+
return this.value;
1367+
}
1368+
}
1369+
Function<Node, Double> mapping = e -> (double) e.getValue();
1370+
1371+
assertEquals(Optional.empty(), Seq.<Node>of().collect(Agg.varianceBy(mapping)));
1372+
assertEquals(Optional.empty(), Seq.<Node>of().collect(Agg.stddevBy(mapping)));
1373+
assertEquals(Optional.of(0.0), Seq.of(new Node(1)).collect(Agg.varianceBy(mapping)));
1374+
assertEquals(Optional.of(0.0), Seq.of(new Node(1)).collect(Agg.stddevBy(mapping)));
1375+
assertEquals(Optional.of(0.0), Seq.of(new Node(1), new Node(1), new Node(1), new Node(1)).collect(Agg.varianceBy(mapping)));
1376+
assertEquals(Optional.of(0.0), Seq.of(new Node(1), new Node(1), new Node(1), new Node(1)).collect(Agg.stddevBy(mapping)));
1377+
assertEquals(Optional.of(1.0), Seq.of(new Node(1), new Node(1), new Node(3), new Node(3)).collect(Agg.varianceBy(mapping)));
1378+
assertEquals(Optional.of(1.0), Seq.of(new Node(1), new Node(1), new Node(3), new Node(3)).collect(Agg.stddevBy(mapping)));
1379+
assertEquals(Optional.of(1.250), Seq.of(new Node(1), new Node(2), new Node(3), new Node(4)).collect(Agg.varianceBy(mapping)));
1380+
assertEquals(Optional.of(1.118), Optional.of(Double.parseDouble(df.format(Seq.of(new Node(1), new Node(2), new Node(3), new Node(4)).collect(Agg.stddevBy(mapping)).get()))));
1381+
1382+
}
1383+
1384+
13121385
}

0 commit comments

Comments
 (0)