离群节点算法lof分析

本次主要针对医保数据,按照病种分组后,进行异常数据分析。主要分为两步:

    一、数据预处理

            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);
	}
}

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值