本次主要针对医保数据,按照病种分组后,进行异常数据分析。主要分为两步:
一、数据预处理
1.处理9个费用指标
次均费用、日均住院费用、药品费用占比、自付药品费用占比、护理费用占比、检查费用占比、高值耗材费用占比、
医保报销比例、医保材料费用报销比例
2.数据过滤
病种选取规则:采用病种分组器,对病例进行分组。得到相应的病种
1、三级医院2016-2017年病例数>=500的病种(大约60个病种)
2、二级医院2016-2017年病例数>=1000的病种(大约80多个病种)
3、一级医院2016-2017年病例数>=1000的病种(大约60多个病种)
二、模型处理
1. 数据空间切片
a. 将9个指标做笛卡尔积组合(排除单个指标组合)==502种
b.对每种组合取对应的数据做计算(每种组合重复计算500次,每次随机组合中的一列,再针对组合其他每列排序依次截取数据---根据切片比例α,再扩展随机列截取的数据,最后返回一个极值)
c.根据每种组合返回对应的极值,取极值最大的前18种组合(差异性越大,数据具有高对比性),若前18种组合未涵盖所有列,则增加缺失列,单独一列作为一种组合,并删除18种排名靠后的组合,使用缺失列组合代替。
d.组合完成后,依次循环组合,取对应的数据,对每条数据做lof运算,求取离群节点因子。
e.最后每条数据通过18种组合就会有18个不同的离群节点因子,保存数据:
每条数据病例号--病种名称--9个指标---18个离群节点因子。
本文主要针对lof算法进行分析。
概念:
k-distance:第k距离 ,距离节点o第k远的点,两者之间的距离----d(o,p)
第k距离领域 :距离节点o的距离<=d(o,p)的点,都成为o点的第k邻域点。
邻域点相对于o的可达距离:max{k_distance(p),d(p,o)},如果邻域p的第k距离>dis(p,p1),则p的第k距离为k_distance(p),否则为d(o,p)--op的真实距离
局部可达密度:o点的局部可达密度=k / o点第k领域所有点的可达距离之和
局部离群点因子:o点的局部离群节点因子=∑ (每个邻域点的局部可达密度 / Oi点的自身局部可达密度的比值) / k
局部离群点因子越接近1,说明o的其邻域点密度差不多,o可能和邻域同属一簇;
如果这个值越小于1,说明o的密度高于其邻域点密度,o为密集点;
如果这个比值越大于1,说明o的密度小于其邻域点密度,o越可能是异常点。
数据节点类
public class DataNode {
private String nodeName; // 样本点名
private double[] dimensioin; // 样本点的维度
private double kDistance; // k-距离
private List<DataNode> kNeighbor = new ArrayList<DataNode>();// k-领域
private double distance; // 到给定点的欧几里得距离
private double reachDensity;// 可达密度
private double reachDis;// 可达距离
private double lof;// 局部离群因子
public DataNode() {
}
public DataNode(String nodeName, double[] dimensioin) {
this.nodeName = nodeName;
this.dimensioin = dimensioin;
}
public String getNodeName() {
return nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
public double[] getDimensioin() {
return dimensioin;
}
public void setDimensioin(double[] dimensioin) {
this.dimensioin = dimensioin;
}
public double getkDistance() {
return kDistance;
}
public void setkDistance(double kDistance) {
this.kDistance = kDistance;
}
public List<DataNode> getkNeighbor() {
return kNeighbor;
}
public void setkNeighbor(List<DataNode> kNeighbor) {
this.kNeighbor = kNeighbor;
}
public double getDistance() {
return distance;
}
public void setDistance(double distance) {
this.distance = distance;
}
public double getReachDensity() {
return reachDensity;
}
public void setReachDensity(double reachDensity) {
this.reachDensity = reachDensity;
}
public double getReachDis() {
return reachDis;
}
public void setReachDis(double reachDis) {
this.reachDis = reachDis;
}
public double getLof() {
return lof;
}
public void setLof(double lof) {
this.lof = lof;
}
}
lof运算程序类
/**
* 离群点分析
*
* @author 算法:基于密度的局部离群点检测(lof算法) 输入:样本集合D,正整数K(用于计算第K距离) 输出:各样本点的局部离群点因子 过程:
* 1)计算每个对象与其他对象的欧几里得距离 2)对欧几里得距离进行排序,计算第k距离以及第K领域 3)计算每个对象的可达密度
* 4)计算每个对象的局部离群点因子 5)对每个点的局部离群点因子进行排序,输出。
**/
public class OutlierNodeDetect {
public static int INT_K = 5;// 正整数K
// 1.找到给定点与其他点的欧几里得距离
// 2.对欧几里得距离进行排序,找到前5位的点,并同时记下k距离
// 3.计算每个点的可达密度
// 4.计算每个点的局部离群点因子
// 5.对每个点的局部离群点因子进行排序,输出。
public List<DataNode> getOutlierNode(List<DataNode> allNodes) {
List<DataNode> kdAndKnList = getKDAndKN(allNodes);
calReachDis(kdAndKnList);//获得每个邻域的第k可达距离
calReachDensity(kdAndKnList);//计算每个点的可达距离密度---根据邻域的可达距离之和的倒数k
calLof(kdAndKnList);
// 降序排序
// Collections.sort(kdAndKnList, new LofComparator());
return kdAndKnList;
}
/**
* 计算每个点的局部离群点因子
*
* @param kdAndKnList
*/
private void calLof(List<DataNode> kdAndKnList) {
for (DataNode node : kdAndKnList) {
List<DataNode> tempNodes = node.getkNeighbor();
double sum = 0.0;
for (DataNode tempNode : tempNodes) {
//获取每个邻域点的可达密度
double rd = getRD(tempNode.getNodeName(), kdAndKnList);//每个邻域点的可达距离密度
sum = rd / node.getReachDensity() + sum;//每个邻域点的可达距离密度 与 node点的自身可达距离密度的比值 之和
}
sum = sum / (double) INT_K;
node.setLof(sum);
}
}
/**
* 计算每个点的可达密度
*
* @param kdAndKnList
*/
private void calReachDensity(List<DataNode> kdAndKnList) {
for (DataNode node : kdAndKnList) {
List<DataNode> tempNodes = node.getkNeighbor();
double sum = 0.0;
double rd = 0.0;
for (DataNode tempNode : tempNodes) {
sum = tempNode.getReachDis() + sum;//每个邻域点的可达距离之和
}
rd = (double) INT_K / sum;
node.setReachDensity(rd);
}
}
/**
* 计算每个邻域点的可达距离,reachdis(p,o)=max{ k-distance(o),d(p,o)}
*
* @param kdAndKnList
*/
private void calReachDis(List<DataNode> kdAndKnList) {
for (DataNode node : kdAndKnList) {
List<DataNode> tempNodes = node.getkNeighbor();
for (DataNode tempNode : tempNodes) {
// 获取tempNode点的k-距离
double kDis = getKDis(tempNode.getNodeName(), kdAndKnList);
// reachdis(p,o)=max{ k-distance(o),d(p,o)}
if (kDis < tempNode.getDistance()) {
tempNode.setReachDis(tempNode.getDistance());
} else {
tempNode.setReachDis(kDis);
}
}
}
}
/**
* 获取某个点的k-距离(kDistance)
*
* @param nodeName
* @param nodeList
* @return
*/
private double getKDis(String nodeName, List<DataNode> nodeList) {
double kDis = 0;
for (DataNode node : nodeList) {
if (nodeName.trim().equals(node.getNodeName().trim())) {
kDis = node.getkDistance();
break;
}
}
return kDis;
}
/**
* 获取某个点的可达距离
*
* @param nodeName
* @param nodeList
* @return
*/
private double getRD(String nodeName, List<DataNode> nodeList) {
double kDis = 0;
for (DataNode node : nodeList) {
if (nodeName.trim().equals(node.getNodeName().trim())) {
kDis = node.getReachDensity();
break;
}
}
return kDis;
}
/**
* 计算给定点NodeA与其他点NodeB的欧几里得距离(distance),并找到NodeA点的前5位NodeB,然后记录到NodeA的k-领域(
* kNeighbor)变量。 同时找到NodeA的k距离,然后记录到NodeA的k-距离(kDistance)变量中。 处理步骤如下:
* 1,计算给定点NodeA与其他点NodeB的欧几里得距离,并记录在NodeB点的distance变量中。
* 2,对所有NodeB点中的distance进行升序排序。
* 3,找到NodeB点的前5位的欧几里得距离点,并记录到到NodeA的kNeighbor变量中。
* 4,找到NodeB点的第5位距离,并记录到NodeA点的kDistance变量中。
*
* @param allNodes
* @return List<Node>
*/
private List<DataNode> getKDAndKN(List<DataNode> allNodes) {
List<DataNode> kdAndKnList = new ArrayList<DataNode>();
for (int i = 0; i < allNodes.size(); i++) {
List<DataNode> tempNodeList = new ArrayList<DataNode>();
DataNode nodeA = new DataNode(allNodes.get(i).getNodeName(),
allNodes.get(i).getDimensioin());
// 1,找到给定点NodeA与其他点NodeB的欧几里得距离,并记录在NodeB点的distance变量中。
for (int j = 0; j < allNodes.size(); j++) {
DataNode nodeB = new DataNode(allNodes.get(j).getNodeName(),
allNodes.get(j).getDimensioin());
// 计算NodeA与NodeB的欧几里得距离(distance)
double tempDis = getDis(nodeA, nodeB);
nodeB.setDistance(tempDis);
tempNodeList.add(nodeB);
}
// 2,对所有NodeB点中的欧几里得距离(distance)进行升序排序。
Collections.sort(tempNodeList, new DistComparator());
for (int k = 1; k < INT_K; k++) {
// 3,找到NodeB点的前5位的欧几里得距离点,并记录到到NodeA的kNeighbor变量中。
nodeA.getkNeighbor().add(tempNodeList.get(k));
if (k == INT_K - 1) {
// 4,找到NodeB点的第5位距离,并记录到NodeA点的kDistance变量中。
nodeA.setkDistance(tempNodeList.get(k).getDistance());
}
}
kdAndKnList.add(nodeA);
}
return kdAndKnList;
}
/**
* 计算给定点A与其他点B之间的欧几里得距离。 欧氏距离的公式: d=sqrt( ∑(xi1-xi2)^2 ) 这里i=1,2..n
* xi1表示第一个点的第i维坐标,xi2表示第二个点的第i维坐标
* n维欧氏空间是一个点集,它的每个点可以表示为(x(1),x(2),...x(n)),
* 其中x(i)(i=1,2...n)是实数,称为x的第i个坐标,
* 两个点x和y=(y(1),y(2)...y(n))之间的距离d(x,y)定义为上面的公式.
*
* @param A
* @param B
* @return
*/
private double getDis(DataNode A, DataNode B) {
double dis = 0.0;
double[] dimA = A.getDimensioin();
double[] dimB = B.getDimensioin();
if (dimA.length == dimB.length) {
for (int i = 0; i < dimA.length; i++) {
double temp = Math.pow(dimA[i] - dimB[i], 2);
dis = dis + temp;
}
dis = Math.pow(dis, 0.5);
}
return dis;
}
/**
* 升序排序
*
* @author
*
*/
class DistComparator implements Comparator<DataNode> {
public int compare(DataNode A, DataNode B) {
// return A.getDistance() - B.getDistance() < 0 ? -1 : 1;
if ((A.getDistance() - B.getDistance()) < 0)
return -1;
else if ((A.getDistance() - B.getDistance()) > 0)
return 1;
else
return 0;
}
}
/**
* 降序排序
*
* @author
*
*/
class LofComparator implements Comparator<DataNode> {
public int compare(DataNode A, DataNode B) {
// return A.getLof() - B.getLof() < 0 ? 1 : -1;
try{
if ((A.getLof() - B.getLof()) < 0)
return 1;
else if ((A.getLof() - B.getLof()) > 0)
return -1;
else
return 0;
}catch (Exception e){
String str = "a:"+A.getLof()+" b:"+B.getLof();
try {
FileOutputStream out = new FileOutputStream("/a_error.log");
out.write(str.getBytes("utf-8"));
out.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
return 0;
}
}
public static void main(String[] args) throws IOException {
//
java.text.DecimalFormat df = new java.text.DecimalFormat("#.####");
ArrayList<DataNode> dpoints = new ArrayList<DataNode>();
BufferedReader br = new BufferedReader(new FileReader("10000data.txt"));
String s = null;
int j =0;
List<String> lines = new ArrayList<String>();
while ((s = br.readLine()) != null) {// 使用readLine方法,一次读一行
lines.add(s);
}
long st = System.currentTimeMillis();
br.close();
long et = System.currentTimeMillis();
System.out.println((et-st)/1000);
}
}