目的
从snap上下载的Brightkite数据集已经是图结构,但它并不满足实验的要求,在进行社区挖掘前要先对数据进行规范化处理,使数据按照我们设定的格式进行存储。
首先是原数据集中有一些id对应的点不存在,但边数据集中有以此点为端点的边。在处理过程中应删除这些边。
其次是我们使用一个id对应的一条签到信息来构造一个点,此id的其它签到信息应删除。
然后是我们在程序中使用图结构保存数据,在处理数据时最好建立一个图数据结构。此数据结构只用于处理数据。
在进行社区挖掘时需要用到点的度,原数据中没有直接给出每个点的度信息,需要我们在处理数据时计算。
原数据集中有些数据还有可能是不正确的,我们应剔除那些不正确的信息。
处理过程及代码
舍去无用签到信息
介绍
在本部分对Brightkite数据集中的签到信息进行处理,即对Brightkite_totalCheckins.txt进行处理。一个id对应的用户会有多条签到信息,即一个人可能会多次签到,我们只需要一个id的一条信息即可。
Brightkite_totalCheckins.txt文件中每条签到信息是一行数据,每个id对应的多条签到信息是相邻的,id从前到后是递增的。我们取每个id对应的签到信息的第一条信息。
在此步骤中,会对所取的信息进行判断,如果此id对应的第一条签到信息不正确,则会再选择此id对应的下一条签到信息。如果此id对应的所有签到信息都不正确,则舍弃此id对应的信息。
处理代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* 处理Brightkite中的签到数据
* Brightkite数据构建的网络由由58228个节点和214078个边缘组成。
* @author dxt
*
*/
public class getDatas {
/**
* 从签到数据文件src中获取所需数据,并复制到文件dest中
* 获取的数据:
* id--唯一标识签到的人
* 坐标--位置信息
* 由于同一个人会存在多次签到信息,我们只取第一条签到信息。
* @param src
* @param dest
*/
public static int copyData(File src, File dest){
//使用缓冲字节流来进行读取写入操作
BufferedReader br;
BufferedWriter bw;
//计数器:记录复制了多少条数据
int count = 0;
try {
br = new BufferedReader(new FileReader(src));
bw = new BufferedWriter(new FileWriter(dest, true));
//进行读取,信息组合,写入操作操作
String line = null;
String message = null;
String id = null;
String x = null;
String y = null;
String temp_id = null;
while((line=br.readLine()) != null){
//分格数据
String[] parts = line.split(" ");
//获取数据
temp_id = parts[0]; //将当前行的id赋给temp_id
if(temp_id.equals(id)){ //保证只读取相同id的第一次签到数据
continue;
}
id = parts[0];
x = parts[2];
y = parts[3];
//对数据进行判断使其合法
if(x.equals("0.0") || y.equals("0.0")){ //如果位置信息不合法,则此条信息废除
id = null; //重新读取对应id的数据
continue;
}
//将数据按照对应格式拼接
if(id != null && x != null && y != null){
message = id + "\t" + x + "\t" + y;
}else{
System.out.println("出现错误");
}
//将数据写入目标文件
bw.write(message);
bw.newLine();
count++; //写一条数据,计数器+1
}
bw.flush();
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
return count;
}
public static void main(String[] args){
//源文件
File src = new File("F://brightkite数据集//loc-brightkite_totalCheckins//Brightkite_totalCheckins.txt");
//目的文件
File dest = new File("F://brightkite数据集//loc-brightkite_totalCheckins//brightkite.txt");
//获取数据
int count = copyData(src, dest);
System.out.println(count);
}
}
结果
经过处理后,我们获得的brightkite.txt文件中有50686条数据。
创建数据结构
介绍
建立点结构,边结构和图结构,便于之后的数据处理。采用面向对象的方式,建立点类、边类。处理数据只需要点类和边类就足够了,不需要再建立图类。
点类:其属性有id,经度x,纬度y,还有点的度degree。其中id,经度,维度都可以直接从签到信息中获取,度需要我们计算。
边类:我们通过保存两点的方式保存一条边,因为两点确定一条边。同时还记录此边的长度。
代码
点类:
/**
* 空间网络节点类
* 每个节点由三个属性:id 、 位置 、度
* @author dxt
*
*/
public class Node {
private int id; //唯一确定空间中的一个节点
private double x; //位置 x--纬度
private double y; //位置y--经度
private int degree; //度:邻接点的个数
//构造方法
public Node(){}
public Node(int id){
super();
this.id = id;
}
public Node(int id, double x, double y){
super();
this.id = id;
this.x = x;
this.y = y;
}
public Node(int id, double x, double y, int degree){
super();
this.id = id;
this.x = x;
this.y = y;
this.degree = degree;
}
//javabean
public void setId(int id){
this.id = id;
}
public int getId(){
return this.id;
}
public void setX(double x){
this.x = x;
}
public double getX(){
return this.x;
}
public void setY(double y){
this.y = y;
}
public double getY(){
return this.y;
}
public void setDegree(int degree){
this.degree = degree;
}
public int getDegree(){
return this.degree;
}
}
边类:
/**
* 边类
* 一条边有两个端点,边有长度
* 边的长度可由两点的位置来计算
* @author dxt
*
*/
public class Edge {
private Node n1; //边的一个端点
private Node n2; //边的另一个端点
private double length; //边的长度
//构造方法
public Edge(){}
public Edge(int id1, int id2){
this.n1 = new Node(id1);
this.n2 = new Node(id2);
}
public Edge(Node n1, Node n2){
this.n1 = n1;
this.n2 = n2;
//this.length = this.computeLength(); //调用自身方法来计算length
}
//javabean
public void setLength(double length){
this.length = length;
}
public double getLength(){
return this.length;
}
public Node getFirstNode(){
return n1;
}
public Node getSecondNode(){
return n2;
}
/**
* 计算两点间的距离
* 因为数据是基于位置的
* 可以依据经纬度计算出两点间的距离
* @return
*/
public double computeLength(){
double earth_radius = 6378.137; //将地球看作圆,地球的平均半径,单位km
double lat1 = n1.getX(); //获取位置1的纬度,北纬为正,南纬为负
double lon1 = n1.getY(); //获取位置1的经度,东经为正,西经为负
double lat2 = n2.getX(); //获取位置2的纬度
double lon2 = n2.getY(); //获取位置2的经度
double radLon1 = lon1 * (float)(Math.PI /180.0); //转换为弧度
double radLon2 = lon2 * (float)(Math.PI /180.0);
double a = radLon1 - radLon2;
double radLat1 = lat1 * (float)(Math.PI /180.0); //转换为弧度
double radLat2 = lat2 * (float)(Math.PI /180.0);
double b = radLat1 - radLat2;
double dis = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)+
Math.cos(radLon1) * Math.cos(radLon2)* Math.pow(Math.sin(b / 2), 2)));
dis = dis * earth_radius;
dis = Math.round(dis * 1000) / 1000.0;//保留三位小数,精确到米
return dis;
}
/**
* 测试computeDistance()方法
* @param args
*/
public static void main(String[] args){
Node n1 = new Node(1, 0.0, -90.0);
Node n2 = new Node(2, 0.0, 0.0);
Edge e = new Edge(n1, n2);
System.out.println(e.computeLength());
}
}
解释
在边类中我们使用了一种基于经纬度计算距离的方法,这种计算方法参考了谷歌地图中的距离计算方法。
固定格式存储数据
介绍
我们处理Brightkite数据集的点文件和边文件,是为了得到符合我们使用要求的点集文件和边集文件。
其中点集文件要求:每行数据表示一个点;每个点具有四个属性:id,经度x,纬度y,度degree。
其中边集文件要求:每行数据表示一条边;每行数据有两个id构成,表示id对应的两个点之间有此边。
代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import algorithm.Edge;
import algorithm.Node;
/**
* 处理brightkite.txt和Brightkite_edges.txt
* 1. 如果点集中没有点u,但边集中有以u为顶点的边,则去除此边
* 2. 依据边集文件计算点的度
* @author dxt
*
*/
public class getAvailableDatas {
//点集
private List<Node> nodeList;
//边集
private List<Edge> edgeList;
//图的点的个数
private int size;
//空构造方法
public getAvailableDatas(){}
//javabean
public int getSize(){
return this.size;
}
/**
* 从nodeList中找出对应id的点
* 返回一个顶点 或 null
* @param id
* @return
*/
public Node getNode(int id){
for(int i=0; i<this.nodeList.size(); i++){
if(id == this.nodeList.get(i).getId()){
return this.nodeList.get(i);
}
}
return null;
}
/**
* 对点数据文件(nodeSrc)和边数据文件(edgeSrc)进行处理
* 1. 如果点集中没有点u,但边集中有以u为顶点的边,则去除此边
* 2. 依据边集文件计算点的度
* 然后将处理后的数据保存到nodeDest和edgeDest文件中
* @param nodeSrc
* @param edgeSrc
* @param nodeDest
* @param edgeDest
*/
public void getData(File nodeSrc, File edgeSrc, File nodeDest, File edgeDest){
//1. 首先获取有哪些节点(以id唯一标识)
nodeList = new ArrayList<Node>();
//1.1 选择流
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(nodeSrc));
//1.2进行读取操作
String line = null; //保存读到的每一行信息
Node temp_node = null;
int id;
double x;
double y;
while((line = br.readLine()) != null){
String[] datas = line.split("\t");
id = Integer.valueOf(datas[0]);
x = Double.valueOf(datas[1]);
y = Double.valueOf(datas[2]);
//开始构造图的点集
temp_node = new Node(id, x, y); //建立节点
nodeList.add(temp_node); //添加到点集中
}
//释放资源
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
//2. 处理边集, 删除不符合的边,同时计算对应点的度
edgeList = new ArrayList<Edge>();
//2.1 选择处理流
BufferedReader br_edge = null;
try {
br_edge = new BufferedReader(new FileReader(edgeSrc));
//2.2 进行读取
String line = null;
int id_one, id_two;
Node n_one = null, n_two = null;
int id_one_temp = -1; //初始设为-1, 用于在查找时提高效率
int degree = 0; //节点的度
while((line=br_edge.readLine()) != null){
String[] datas = line.split("\t");
id_one = Integer.valueOf(datas[0]);
id_two = Integer.valueOf(datas[1]);
//从nodeList中找出对应的点,依据数据格式进行判断,提高其效率
if(id_one != id_one_temp){ //如果id_one改变,则表示n_one改变,否则一直为之前的
//首先要将id_one_temp对应点的度初始化
if(this.getNode(id_one_temp) != null){
this.getNode(id_one_temp).setDegree(degree);
}
degree = 0; //度的计数从零开始
n_one = this.getNode(id_one); //找到对应id的点
n_two = this.getNode(id_two);
}else{ //否则id_one不变
n_two = this.getNode(id_two); //n_two是经常改变的
}
//如果已经找出两点,则构造边,并添加到边集中
if(n_one != null && n_two != null){
//构造边
Edge e = new Edge(n_one, n_two);
e.setLength(e.computeLength());
//将边添加到边集中
this.edgeList.add(e);
if(id_one != id_one_temp){
//id_one的度为1
degree = 1;
}else{
//修改n_one的度
degree++;
}
//保存此次的id_one,用于下一次查找
id_one_temp = id_one;
}
else{ //否则(可能点集中没有对应的点),则舍去此行信息,读取下一行信息
//保存此次的id_one,用于下一次查找
id_one_temp = id_one;
continue;
}
}
this.getNode(id_one_temp).setDegree(degree); //对最后一个id_one设置度
//关闭字节流
br_edge.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
//3. 将点集写入点集文件
//3.1 选择流
BufferedWriter bw_node = null;
try {
bw_node = new BufferedWriter(new FileWriter(nodeDest, true));
//3.2 获取信息,并写入文件
for(int i=0; i<this.nodeList.size(); i++){
int id = this.nodeList.get(i).getId();
double x = this.nodeList.get(i).getX();
double y = this.nodeList.get(i).getY();
int degree = this.nodeList.get(i).getDegree();
String datas = id + "\t" + x + "\t" + y + "\t" + degree;
bw_node.write(datas);
bw_node.newLine();
}
bw_node.flush();
} catch (IOException e) {
e.printStackTrace();
}
//4. 将边集写入边集文件
BufferedWriter bw_edge = null;
try {
bw_edge = new BufferedWriter(new FileWriter(edgeDest, true));
for(int i=0; i<this.edgeList.size(); i++){
int id_one = this.edgeList.get(i).getFirstNode().getId();
int id_two = this.edgeList.get(i).getSecondNode().getId();
String datas = id_one + "\t" + id_two;
bw_edge.write(datas);
bw_edge.newLine();
}
bw_edge.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args){
File nodeSrc = new File("src//source//brightkite.txt");
File edgeSrc = new File("src//source//Brightkite_edges.txt");
File nodeDest = new File("src//source//brightkite_nodes_available.txt");
File edgeDest = new File("src//source//brightkite_edges_available.txt");
getAvailableDatas g = new getAvailableDatas();
g.getData(nodeSrc, edgeSrc, nodeDest, edgeDest);
}
}
解释
在此步骤中输入的点集文件为brightkite.txt,这是我们在 舍去无用签到信息 步骤中获得的点集文件,不是原数据文件。输入的边集文件为原数据文件。
结果
brightkite_nodes_available.txt文件为处理后的点集文件,有50686条数据,说明有50686个点。数据具体格式如下:
0 39.747652 -104.99251 119
1 37.579963 -122.343908 39
2 39.747652 -104.99251 81
3 38.94511 -77.451706 229
4 37.824562 -122.368844 26
5 37.797691 -122.430553 47
6 42.057777 -87.749627 68
7 37.591126 -122.383322 217
8 39.74832 -105.000522 3
9 37.579001 -122.344753 23
10 49.613146 6.12731 35
11 37.580304 -122.343679 71
brightkite_edges_available.txt文件为处理后的边集文件,有388180条数据,说明有388180条边。数据具体格式如下:
0 118
0 119
0 120
0 121
0 122
1 0
1 3
1 4
1 5
1 7
1 9
处理结果
经过 舍去无用签到信息 和 固定格式存储数据 两个步骤,我们已得到用于构建空间网络图的数据。所需的点集和边集就是固定格式存储数据中获得的点集和边集。