分位值在数据分析中是一个比较常见的需求,计算一组数据的分位值,例如P10、P50等,有很多现成的Java实现,例如Guava的Quantiles
类,以及org.apache.commons.math.stat
下的相关实现,在此不赘述。
计算某个数据,在一组数中的所处分位,即Excel中PERCENTRANK,分为两种情况:
- 若目标数包含在数组中,则
所处分位 = 小于该数的数据量 / (数据总量 - 1)
- 若目标数不在数组中,则它的所处分位,由与他相领的两个数的所处分位计算而来,具体为(以目标数据为b,与他相领的数据为a,c为例):
Pb = Pa + ((b-a)/(c-a))*(Pc-Pa)
以下是Java实现:
public static String getPosition(List<Number> data, double target) {
double[] doubles = Doubles.toArray(data);
// 需要对数据进行排序
Arrays.sort(doubles);
double result;
// 小于目标值的数量
int lt = 0;
// 如果是第二种情况,则用lt2来记录比a小的数据量,它等于比c小的数据量减去a出现的次数。
int lt2 = 1;
int pos = -1;
double a = NaN;
double c = NaN;
for (int i = 0; i < doubles.length; i++) {
if (doubles[i] == target) {
pos = i;
break;
} else if (doubles[i] < target) {
lt++;
if (doubles[i] == a) {
lt2++;
} else {
lt2 = 1;
a = doubles[i];
}
} else {
c = doubles[i];
break;
}
}
// 若目标b不在数组中,则需要找到目标值临近的两个数(例如ac之于b)的位置,通过Pb = Pa + ((b-a)/(c-a))*(Pc-Pa)
Pc: result = (double) lt / (doubles.length - 1);
if (pos < 0) {
double Pa = (double) (lt - lt2) / (doubles.length - 1);
result = Pa + ((target - a)/ (c - a)) * (result - Pa);
}
return String.format("P%.0f", result * 100);
}