Changed gathered ping average to be median instead #691

(For each datapoint a median is calculated, which is then used in calculation for mean values, averages)
This commit is contained in:
Rsl1122 2018-08-21 10:23:13 +03:00
parent 268474d5ca
commit 3afaad9731
4 changed files with 230 additions and 9 deletions

View File

@ -9,10 +9,12 @@ import com.djrapitops.plan.data.store.objects.DateObj;
import com.djrapitops.plan.system.database.databases.Database; import com.djrapitops.plan.system.database.databases.Database;
import com.djrapitops.plan.system.info.server.ServerInfo; import com.djrapitops.plan.system.info.server.ServerInfo;
import com.djrapitops.plan.system.processing.CriticalRunnable; import com.djrapitops.plan.system.processing.CriticalRunnable;
import com.djrapitops.plan.utilities.analysis.Median;
import java.util.List; import java.util.List;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
/** /**
* Processes 60s values of a Ping list. * Processes 60s values of a Ping list.
@ -44,23 +46,28 @@ public class PingInsertProcessor implements CriticalRunnable {
return; return;
} }
int minValue = history.stream() int minValue = getMinValue(history);
.mapToInt(DateObj::getValue)
.filter(i -> i > 0 && i < 4000)
.min().orElse(-1);
double avgValue = history.stream() int meanValue = getMeanValue(history);
.mapToInt(DateObj::getValue)
.filter(i -> i > 0 && i < 4000)
.average().orElse(-1);
int maxValue = max.getAsInt(); int maxValue = max.getAsInt();
Ping ping = new Ping(lastDate, ServerInfo.getServerUUID(), Ping ping = new Ping(lastDate, ServerInfo.getServerUUID(),
minValue, minValue,
maxValue, maxValue,
avgValue); meanValue);
Database.getActive().save().ping(uuid, ping); Database.getActive().save().ping(uuid, ping);
} }
int getMinValue(List<DateObj<Integer>> history) {
return history.stream()
.mapToInt(DateObj::getValue)
.filter(i -> i > 0 && i < 4000)
.min().orElse(-1);
}
int getMeanValue(List<DateObj<Integer>> history) {
return (int) Median.forInt(history.stream().map(DateObj::getValue).collect(Collectors.toList())).calculate();
}
} }

View File

@ -0,0 +1,58 @@
package com.djrapitops.plan.utilities.analysis;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* Math utility for calculating the median from Integer values.
*
* @author Rsl1122
*/
public class Median {
private final List<Long> values;
private int size;
private Median(Collection<Integer> values, int b) {
this(values.stream().map(i -> (long) i).collect(Collectors.toList()));
}
private Median(List<Long> values) {
this.values = values;
Collections.sort(values);
size = values.size();
}
public static Median forInt(Collection<Integer> integers) {
return new Median(integers, 0);
}
public static Median forLong(List<Long> longs) {
return new Median(longs);
}
public double calculate() {
if (values.isEmpty()) {
return -1;
}
if (size % 2 == 0) {
return calculateEven();
} else {
return calculateOdd();
}
}
private double calculateEven() {
int half = size / 2;
double x1 = values.get(half);
double x2 = values.get(half - 1);
return (x1 + x2) / 2;
}
private double calculateOdd() {
int half = size / 2;
return (double) values.get(half);
}
}

View File

@ -0,0 +1,66 @@
package com.djrapitops.plan.system.processing.processors.player;
import com.djrapitops.plan.data.store.objects.DateObj;
import com.djrapitops.plan.utilities.analysis.Median;
import com.djrapitops.plugin.api.TimeAmount;
import org.junit.Before;
import org.junit.Test;
import utilities.RandomData;
import utilities.TestConstants;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
/**
* Test for {@link PingInsertProcessor}.
*
* @author Rsl1122
*/
public class PingInsertProcessorTest {
private List<DateObj<Integer>> testPing;
@Before
public void setUp() {
testPing = new ArrayList<>();
for (int i = 0; i < TimeAmount.MINUTE.ms(); i += TimeAmount.SECOND.ms() * 2L) {
testPing.add(new DateObj<>(i, RandomData.randomInt(1, 4000)));
}
}
@Test
public void testMedian() {
List<Integer> collect = testPing.stream().map(DateObj::getValue).sorted().collect(Collectors.toList());
System.out.println(collect);
int expected = (int) Median.forInt(collect).calculate();
int result = new PingInsertProcessor(TestConstants.PLAYER_ONE_UUID, new ArrayList<>()).getMeanValue(testPing);
System.out.println(result);
assertEquals(expected, result);
}
@Test
public void testMedianSingleEntry() {
int expected = 50;
int result = new PingInsertProcessor(TestConstants.PLAYER_ONE_UUID, new ArrayList<>()).getMeanValue(
Collections.singletonList(new DateObj<>(0, expected))
);
assertEquals(expected, result);
}
@Test
public void testMedianEmpty() {
int expected = -1;
int result = new PingInsertProcessor(TestConstants.PLAYER_ONE_UUID, new ArrayList<>()).getMeanValue(
Collections.emptyList()
);
assertEquals(expected, result);
}
}

View File

@ -0,0 +1,90 @@
package com.djrapitops.plan.utilities.analysis;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link Median}.
*
* @author Rsl1122
*/
public class MedianTest {
@Test
public void simpleOdd() {
List<Integer> testValues = Arrays.asList(1, 3, 3, 6, 7, 8, 9);
Collections.shuffle(testValues);
double expected = 6;
double result = Median.forInt(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@Test
public void simpleEven() {
List<Integer> testValues = Arrays.asList(1, 2, 3, 4, 5, 6, 8, 9);
Collections.shuffle(testValues);
double expected = 4.5;
double result = Median.forInt(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@Test
public void empty() {
double expected = -1;
double result = Median.forInt(Collections.emptyList()).calculate();
assertEquals(expected, result, 0.01);
}
@Test
public void singleValue() {
double expected = 50;
double result = Median.forInt(Collections.singletonList((int) expected)).calculate();
assertEquals(expected, result, 0.01);
}
@Test
public void twoValues() {
List<Integer> testValues = Arrays.asList(1, 2);
double expected = 1.5;
double result = Median.forInt(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@Test
public void overflowOdd() {
List<Integer> testValues = Arrays.asList(Integer.MIN_VALUE, 2, Integer.MAX_VALUE);
double expected = 2;
double result = Median.forInt(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@Test
public void overflowEven() {
List<Integer> testValues = Arrays.asList(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
double expected = -0.5;
double result = Median.forInt(testValues).calculate();
assertEquals(expected, result, 0.01);
}
@Test
public void overflowEven2() {
List<Integer> testValues = Arrays.asList(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
double expected = Integer.MAX_VALUE;
double result = Median.forInt(testValues).calculate();
assertEquals(expected, result, 0.01);
}
}